From cd26f1bd6bf3c73cc5afe848677b430ab342a909 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Aug 2014 15:29:23 +0800 Subject: greybus: Initial commit --- drivers/staging/greybus/.gitignore | 23 +++ drivers/staging/greybus/LICENSE | 339 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/README.md | 4 + 3 files changed, 366 insertions(+) create mode 100644 drivers/staging/greybus/.gitignore create mode 100644 drivers/staging/greybus/LICENSE create mode 100644 drivers/staging/greybus/README.md diff --git a/drivers/staging/greybus/.gitignore b/drivers/staging/greybus/.gitignore new file mode 100644 index 000000000000..4d40434d6421 --- /dev/null +++ b/drivers/staging/greybus/.gitignore @@ -0,0 +1,23 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Libraries +*.lib +*.a + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex diff --git a/drivers/staging/greybus/LICENSE b/drivers/staging/greybus/LICENSE new file mode 100644 index 000000000000..d7f105139782 --- /dev/null +++ b/drivers/staging/greybus/LICENSE @@ -0,0 +1,339 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/drivers/staging/greybus/README.md b/drivers/staging/greybus/README.md new file mode 100644 index 000000000000..57e8490f6300 --- /dev/null +++ b/drivers/staging/greybus/README.md @@ -0,0 +1,4 @@ +greybus +======= + +greybus kernel code -- cgit v1.2.3-59-g8ed1b From c8a797a98cb63afd620d3ae448e8ee3e45f47088 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Aug 2014 15:30:45 +0800 Subject: greybus: Import most recent greybus code to new repo. --- drivers/staging/greybus/Makefile | 22 +++++ drivers/staging/greybus/core.c | 153 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/greybus.h | 97 ++++++++++++++++++++++ drivers/staging/greybus/greybus_id.h | 27 +++++++ drivers/staging/greybus/i2c-gb.c | 122 ++++++++++++++++++++++++++++ 5 files changed, 421 insertions(+) create mode 100644 drivers/staging/greybus/Makefile create mode 100644 drivers/staging/greybus/core.c create mode 100644 drivers/staging/greybus/greybus.h create mode 100644 drivers/staging/greybus/greybus_id.h create mode 100644 drivers/staging/greybus/i2c-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile new file mode 100644 index 000000000000..432ad4c72863 --- /dev/null +++ b/drivers/staging/greybus/Makefile @@ -0,0 +1,22 @@ +greybus-y := core.o + +obj-m += greybus.o +obj-m += i2c-gb.o + +KERNELVER ?= $(shell uname -r) +KERNELDIR ?= /lib/modules/$(KERNELVER)/build +PWD := $(shell pwd) + +all: module + +module: + $(MAKE) -C $(KERNELDIR) M=$(PWD) + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers + +coccicheck: + $(MAKE) -C $(KERNELDIR) M=$(PWD) coccicheck + diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c new file mode 100644 index 000000000000..87148cd38b56 --- /dev/null +++ b/drivers/staging/greybus/core.c @@ -0,0 +1,153 @@ +/* + * Greybus "Core" + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "greybus.h" + +/* Allow greybus to be disabled at boot if needed */ +static bool nogreybus; +#ifdef MODULE +module_param(nogreybus, bool, 0444); +#else +core_param(nogreybus, bool, 0444); +#endif +int greybus_disabled(void) +{ + return nogreybus; +} +EXPORT_SYMBOL_GPL(greybus_disabled); + +static int greybus_match_one_id(struct greybus_device *gdev, + const struct greybus_device_id *id) +{ + struct greybus_descriptor *des = &gdev->descriptor; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && + (des->wVendor != id->wVendor)) + return 0; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && + (des->wProduct != id->wProduct)) + return 0; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && + (des->lSerialNumber != id->lSerialNumber)) + return 0; + + return 1; +} + +static const struct greybus_device_id *greybus_match_id( + struct greybus_device *gdev, + const struct greybus_device_id *id) +{ + if (id == NULL) + return NULL; + + for (; id->wVendor || id->wProduct || id->lSerialNumber || + id->driver_info ; id++) { + if (greybus_match_one_id(gdev, id)) + return id; + } + + return NULL; +} + +static int greybus_device_match(struct device *dev, struct device_driver *drv) +{ + struct greybus_driver *driver = to_greybus_driver(dev->driver); + struct greybus_device *gdev = to_greybus_device(dev); + const struct greybus_device_id *id; + + id = greybus_match_id(gdev, driver->id_table); + if (id) + return 1; + /* FIXME - Dyanmic ids? */ + return 0; +} + +static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + /* struct greybus_device *gdev = to_greybus_device(dev); */ + + /* FIXME - add some uevents here... */ + return 0; +} + +struct bus_type greybus_bus_type = { + .name = "greybus", + .match = greybus_device_match, + .uevent = greybus_uevent, +}; + +static int greybus_probe(struct device *dev) +{ + struct greybus_driver *driver = to_greybus_driver(dev->driver); + struct greybus_device *gdev = to_greybus_device(dev); + const struct greybus_device_id *id; + int retval; + + /* match id */ + id = greybus_match_id(gdev, driver->id_table); + if (!id) + return -ENODEV; + + retval = driver->probe(gdev, id); + if (retval) + return retval; + + return 0; +} + +static int greybus_remove(struct device *dev) +{ + struct greybus_driver *driver = to_greybus_driver(dev->driver); + struct greybus_device *gdev = to_greybus_device(dev); + + driver->disconnect(gdev); + return 0; +} + +int greybus_register_driver(struct greybus_driver *driver, struct module *owner, + const char *mod_name) +{ + int retval; + + if (greybus_disabled()) + return -ENODEV; + + driver->driver.name = driver->name; + driver->driver.probe = greybus_probe; + driver->driver.remove = greybus_remove; + driver->driver.owner = owner; + driver->driver.mod_name = mod_name; + + retval = driver_register(&driver->driver); + if (retval) + return retval; + + pr_info("registered new driver %s\n", driver->name); + return 0; +} +EXPORT_SYMBOL_GPL(greybus_register_driver); + +void greybus_deregister(struct greybus_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(greybus_deregister); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h new file mode 100644 index 000000000000..8158e45393a9 --- /dev/null +++ b/drivers/staging/greybus/greybus.h @@ -0,0 +1,97 @@ +/* + * Greybus driver and device API + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __LINUX_GREYBUS_H +#define __LINUX_GREYBUS_H + +#ifdef __KERNEL__ + +#include +#include +#include +#include "greybus_id.h" + + +#define GREYBUS_DEVICE_ID_MATCH_DEVICE \ + (GREYBUS_DEVICE_ID_MATCH_VENDOR | GREYBUS_DEVICE_ID_MATCH_PRODUCT) + +#define GREYBUS_DEVICE(vendor, product) \ + .match_flags = GREYBUS_DEVICE_ID_MATCH_DEVICE, \ + .wVendor = (vendor), \ + .wProduct = (product), + +#define GREYBUS_DEVICE_SERIAL(serial) \ + .match_flags = GREYBUS_DEVICE_ID_MATCH_SERIAL, \ + .lSerial = (serial), + + +struct greybus_descriptor { + __u16 wVendor; + __u16 wProduct; + __u64 lSerialNumber; +}; + +struct greybus_device { + struct device dev; + struct greybus_descriptor descriptor; +}; +#define to_greybus_device(d) container_of(d, struct greybus_device, dev) + +struct greybus_driver { + const char *name; + + int (*probe) (struct greybus_device *gdev, + const struct greybus_device_id *id); + void (*disconnect) (struct greybus_device *gdev); + + int (*suspend) (struct greybus_device *gdev, pm_message_t message); + int (*resume) (struct greybus_device *gdev); + + const struct greybus_device_id *id_table; + + struct device_driver driver; +}; +#define to_greybus_driver(d) container_of(d, struct greybus_driver, driver) + +static inline void greybus_set_drvdata(struct greybus_device *gdev, void *data) +{ + dev_set_drvdata(&gdev->dev, data); +} + +static inline void *greybus_get_drvdata(struct greybus_device *gdev) +{ + return dev_get_drvdata(&gdev->dev); +} + +/* Don't call these directly, use the module_greybus_driver() macro instead */ +int greybus_register_driver(struct greybus_driver *driver, + struct module *module, const char *mod_name); +void greybus_deregister(struct greybus_driver *driver); + +/* define to get proper THIS_MODULE and KBUILD_MODNAME values */ +#define greybus_register(driver) \ + greybus_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) + +/** + * module_greybus_driver() - Helper macro for registering a Greybus driver + * @__greybus_driver: greybus_driver structure + * + * Helper macro for Greybus drivers to set up proper module init / exit + * functions. Replaces module_init() and module_exit() and keeps people from + * printing pointless things to the kernel log when their driver is loaded. + */ +#define module_greybus_driver(__greybus_driver) \ + module_driver(__greybus_driver, greybus_register, greybus_deregister) + +extern struct bus_type greybus_bus_type; + +int greybus_disabled(void); + + +#endif /* __KERNEL__ */ +#endif /* __LINUX_GREYBUS_H */ diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h new file mode 100644 index 000000000000..4afbfe2d5cb9 --- /dev/null +++ b/drivers/staging/greybus/greybus_id.h @@ -0,0 +1,27 @@ +/* FIXME + * move this to include/linux/mod_devicetable.h when merging + */ + +#ifndef __LINUX_GREYBUS_ID_H +#define __LINUX_GREYBUS_ID_H + +#include +#include + + +struct greybus_device_id { + __u16 match_flags; + __u16 wVendor; + __u16 wProduct; + __u64 lSerialNumber; + + kernel_ulong_t driver_info + __attribute__((aligned(sizeof(kernel_ulong_t)))); +}; + +/* Used to match the greybus_device_id */ +#define GREYBUS_DEVICE_ID_MATCH_VENDOR BIT(0) +#define GREYBUS_DEVICE_ID_MATCH_PRODUCT BIT(1) +#define GREYBUS_DEVICE_ID_MATCH_SERIAL BIT(2) + +#endif /* __LINUX_GREYBUS_H */ diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c new file mode 100644 index 000000000000..a481e06c8b67 --- /dev/null +++ b/drivers/staging/greybus/i2c-gb.c @@ -0,0 +1,122 @@ +/* + * I2C bridge driver for the Greybus "generic" I2C module. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct i2c_gb_data { + struct i2c_adapter *adapter; + struct greybus_device *gdev; +}; + +static const struct greybus_device_id id_table[] = { + { GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */ + { }, /* terminating NULL entry */ +}; + +/* We BETTER be able to do SMBUS protocl calls, otherwise we are bit-banging the + * slowest thing possible over the fastest bus possible, crazy... + * FIXME - research this, for now just assume we can + */ + + +static s32 i2c_gb_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_gb_data *i2c_gb_data; + struct greybus_device *gdev; + + i2c_gb_data = i2c_get_adapdata(adap); + gdev = i2c_gb_data->gdev; + + // FIXME - do the actual work of sending a i2c message here... + switch (size) { + case I2C_SMBUS_QUICK: + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_BROKEN: + case I2C_SMBUS_BLOCK_PROC_CALL: + case I2C_SMBUS_I2C_BLOCK_DATA: + default: + dev_err(&gdev->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + return 0; +} + +static u32 i2c_gb_func(struct i2c_adapter *adapter) +{ + // FIXME - someone figure out what we really can support, for now just guess... + return I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | + I2C_FUNC_SMBUS_PEC | + I2C_FUNC_SMBUS_READ_I2C_BLOCK; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = i2c_gb_access, + .functionality = i2c_gb_func, +}; + +static int i2c_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) +{ + struct i2c_gb_data *i2c_gb_data; + struct i2c_adapter *adapter; + + i2c_gb_data = kzalloc(sizeof(*i2c_gb_data), GFP_KERNEL); + if (!i2c_gb_data) + return -ENOMEM; + adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); + if (!adapter) { + kfree(i2c_gb_data); + return -ENOMEM; + } + + i2c_set_adapdata(adapter, i2c_gb_data); + adapter->owner = THIS_MODULE; + adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adapter->algo = &smbus_algorithm; + + i2c_gb_data->gdev = gdev; + i2c_gb_data->adapter = adapter; + + greybus_set_drvdata(gdev, i2c_gb_data); + return 0; +} + +static void i2c_gb_disconnect(struct greybus_device *gdev) +{ + struct i2c_gb_data *i2c_gb_data; + + i2c_gb_data = greybus_get_drvdata(gdev); + i2c_del_adapter(i2c_gb_data->adapter); + kfree(i2c_gb_data->adapter); + kfree(i2c_gb_data); +} + +static struct greybus_driver i2c_gb_driver = { + .probe = i2c_gb_probe, + .disconnect = i2c_gb_disconnect, + .id_table = id_table, +}; + +module_greybus_driver(i2c_gb_driver); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From 06823c3eb9c4069f899b0c71d35fd17af945627c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Aug 2014 15:32:12 +0800 Subject: greybus: README and .gitignore updates --- drivers/staging/greybus/.gitignore | 30 ++++++++---------------------- drivers/staging/greybus/README | 1 + drivers/staging/greybus/README.md | 4 ---- 3 files changed, 9 insertions(+), 26 deletions(-) create mode 100644 drivers/staging/greybus/README delete mode 100644 drivers/staging/greybus/README.md diff --git a/drivers/staging/greybus/.gitignore b/drivers/staging/greybus/.gitignore index 4d40434d6421..a07ab9d2eb46 100644 --- a/drivers/staging/greybus/.gitignore +++ b/drivers/staging/greybus/.gitignore @@ -1,23 +1,9 @@ -# Object files -*.o +*.cmd *.ko -*.obj -*.elf - -# Libraries -*.lib -*.a - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex +*.mod.c +modules.order +Module.symvers +*.o +*.swp +.tmp_versions +tags diff --git a/drivers/staging/greybus/README b/drivers/staging/greybus/README new file mode 100644 index 000000000000..50953b1cbd36 --- /dev/null +++ b/drivers/staging/greybus/README @@ -0,0 +1 @@ +Greybus kernel code diff --git a/drivers/staging/greybus/README.md b/drivers/staging/greybus/README.md deleted file mode 100644 index 57e8490f6300..000000000000 --- a/drivers/staging/greybus/README.md +++ /dev/null @@ -1,4 +0,0 @@ -greybus -======= - -greybus kernel code -- cgit v1.2.3-59-g8ed1b From 4c009fada183bbca4dfa40e187268d3ee1040119 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Aug 2014 15:44:51 +0800 Subject: greybus: update README with info on how to build and contact me. --- drivers/staging/greybus/README | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/greybus/README b/drivers/staging/greybus/README index 50953b1cbd36..b0745d37265b 100644 --- a/drivers/staging/greybus/README +++ b/drivers/staging/greybus/README @@ -1 +1,10 @@ Greybus kernel code + +To build against the running kernel (odds are you don't want this): + make + +To build against a specific kernel source tree (odds are you want this): + KERNELDIR=/home/some/random/place make + +Any questions / concerns about this code base, please email: + Greg Kroah-Hartman -- cgit v1.2.3-59-g8ed1b From 53419e07cc94c89307c7b1e698e84f7f9340f37d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Aug 2014 17:01:15 +0800 Subject: greybus: i2c-gb: actually add the i2c adapter properly... --- drivers/staging/greybus/i2c-gb.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index a481e06c8b67..7fcbcef641a5 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -79,6 +79,7 @@ static int i2c_gb_probe(struct greybus_device *gdev, const struct greybus_device { struct i2c_gb_data *i2c_gb_data; struct i2c_adapter *adapter; + int retval; i2c_gb_data = kzalloc(sizeof(*i2c_gb_data), GFP_KERNEL); if (!i2c_gb_data) @@ -93,12 +94,24 @@ static int i2c_gb_probe(struct greybus_device *gdev, const struct greybus_device adapter->owner = THIS_MODULE; adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adapter->algo = &smbus_algorithm; + adapter->dev.parent = &gdev->dev; + adapter->retries = 3; /* we have to pick something... */ + snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); + retval = i2c_add_adapter(adapter); + if (retval) { + dev_err(&gdev->dev, "Can not add SMBus adapter\n"); + goto error; + } i2c_gb_data->gdev = gdev; i2c_gb_data->adapter = adapter; greybus_set_drvdata(gdev, i2c_gb_data); return 0; +error: + kfree(adapter); + kfree(i2c_gb_data); + return retval; } static void i2c_gb_disconnect(struct greybus_device *gdev) -- cgit v1.2.3-59-g8ed1b From 776f136c75f2c2346a8fca698ef81d15245f5a00 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Aug 2014 17:27:07 +0800 Subject: greybus: greybus.h: tiny coding style cleanups --- drivers/staging/greybus/greybus.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 8158e45393a9..28b3148fb1da 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -45,12 +45,12 @@ struct greybus_device { struct greybus_driver { const char *name; - int (*probe) (struct greybus_device *gdev, - const struct greybus_device_id *id); - void (*disconnect) (struct greybus_device *gdev); + int (*probe)(struct greybus_device *gdev, + const struct greybus_device_id *id); + void (*disconnect)(struct greybus_device *gdev); - int (*suspend) (struct greybus_device *gdev, pm_message_t message); - int (*resume) (struct greybus_device *gdev); + int (*suspend)(struct greybus_device *gdev, pm_message_t message); + int (*resume)(struct greybus_device *gdev); const struct greybus_device_id *id_table; @@ -60,12 +60,12 @@ struct greybus_driver { static inline void greybus_set_drvdata(struct greybus_device *gdev, void *data) { - dev_set_drvdata(&gdev->dev, data); + dev_set_drvdata(&gdev->dev, data); } static inline void *greybus_get_drvdata(struct greybus_device *gdev) { - return dev_get_drvdata(&gdev->dev); + return dev_get_drvdata(&gdev->dev); } /* Don't call these directly, use the module_greybus_driver() macro instead */ -- cgit v1.2.3-59-g8ed1b From 83ddaaab01c2ababbcaa507a3ecaa80499cf000f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Aug 2014 17:27:22 +0800 Subject: greybus: Greybus SD/MMC host driver Need to add specifics, but this should be enough to hook up to the mmc framework. --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/sdio-gb.c | 88 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 drivers/staging/greybus/sdio-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 432ad4c72863..753436d94f1f 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -2,6 +2,7 @@ greybus-y := core.o obj-m += greybus.o obj-m += i2c-gb.o +obj-m += sdio-gb.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c new file mode 100644 index 000000000000..f92ab1b7438a --- /dev/null +++ b/drivers/staging/greybus/sdio-gb.c @@ -0,0 +1,88 @@ +/* + * SD/MMC Greybus driver. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct sd_gb_host { + struct mmc_host *mmc; + struct mmc_request *mrq; + // FIXME - some lock? +}; + +static const struct greybus_device_id id_table[] = { + { GREYBUS_DEVICE(0x43, 0x43) }, /* make shit up */ + { }, /* terminating NULL entry */ +}; + +static void gb_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + // FIXME - do something here... +} + +static void gb_sd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + // FIXME - do something here... +} + +static int gb_sd_get_ro(struct mmc_host *mmc) +{ + // FIXME - do something here... + return 0; +} + +static const struct mmc_host_ops gb_sd_ops = { + .request = gb_sd_request, + .set_ios = gb_sd_set_ios, + .get_ro = gb_sd_get_ro, +}; + +static int sd_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) +{ + struct mmc_host *mmc; + struct sd_gb_host *host; + + mmc = mmc_alloc_host(sizeof(struct sd_gb_host), &gdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->mmc = mmc; + + mmc->ops = &gb_sd_ops; + // FIXME - set up size limits we can handle. + + greybus_set_drvdata(gdev, host); + return 0; +} + +static void sd_gb_disconnect(struct greybus_device *gdev) +{ + struct mmc_host *mmc; + struct sd_gb_host *host; + + host = greybus_get_drvdata(gdev); + mmc = host->mmc; + + mmc_remove_host(mmc); + mmc_free_host(mmc); +} + +static struct greybus_driver sd_gb_driver = { + .probe = sd_gb_probe, + .disconnect = sd_gb_disconnect, + .id_table = id_table, +}; + +module_greybus_driver(sd_gb_driver); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Greybus SD/MMC Host driver"); +MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From d5d1903dcd158e220275a64f9f4df6cd06addb82 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Aug 2014 19:03:20 +0800 Subject: greybus: add framework for 'struct gbuf' This is the equlivant of sk_buf or urbs for Greybus. --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/gbuf.c | 42 ++++++++++++++++++++++++++++++++ drivers/staging/greybus/greybus.h | 51 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/greybus/gbuf.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 753436d94f1f..cc9c3a7d17b8 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,4 +1,4 @@ -greybus-y := core.o +greybus-y := core.o gbuf.o obj-m += greybus.o obj-m += i2c-gb.o diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c new file mode 100644 index 000000000000..de31da8165de --- /dev/null +++ b/drivers/staging/greybus/gbuf.c @@ -0,0 +1,42 @@ +/* + * Greybus gbuf handling + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "greybus.h" + + +struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, + struct cport *cport, + gfp_t mem_flags) +{ + return NULL; +} + +void greybus_free_gbuf(struct gbuf *gbuf) +{ +} + +int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags) +{ + return -ENOMEM; +} + +int greybus_kill_gbuf(struct gbuf *gbuf) +{ + return -ENOMEM; +} + + + diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 28b3148fb1da..51b9f5b69979 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -29,19 +29,68 @@ .match_flags = GREYBUS_DEVICE_ID_MATCH_SERIAL, \ .lSerial = (serial), - struct greybus_descriptor { __u16 wVendor; __u16 wProduct; __u64 lSerialNumber; }; + +struct gbuf; + +struct cport { + u16 number; + // FIXME, what else? +}; + +typedef void (*gbuf_complete_t)(struct gbuf *gbuf); + +struct gbuf { + struct kref kref; + void *hcpriv; + + struct list_head anchor_list; + struct gbuf_anchor *anchor; // FIXME do we need? + + struct greybus_device *gdev; + struct cport *cport; + int status; + void *transfer_buffer; + u32 transfer_flags; /* flags for the transfer buffer */ + u32 transfer_buffer_length; + u32 actual_length; + + struct scatterlist *sg; // FIXME do we need? + int num_sgs; + + void *context; + gbuf_complete_t complete; +}; + +/* + * gbuf->transfer_flags + */ +#define GBUF_FREE_BUFFER BIT(0) /* Free the transfer buffer with the gbuf */ + + struct greybus_device { struct device dev; struct greybus_descriptor descriptor; + int num_cport; + struct cport cport[0]; }; #define to_greybus_device(d) container_of(d, struct greybus_device, dev) + +struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, + struct cport *cport, + gfp_t mem_flags); +void greybus_free_gbuf(struct gbuf *gbuf); + +int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags); +int greybus_kill_gbuf(struct gbuf *gbuf); + + struct greybus_driver { const char *name; -- cgit v1.2.3-59-g8ed1b From c16854c3bf562a1f7d1dea92309f35fe97e26889 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 12 Aug 2014 12:00:16 +0800 Subject: greybus: gpio driver --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/gpio-gb.c | 108 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 drivers/staging/greybus/gpio-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index cc9c3a7d17b8..12705b0543a3 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -3,6 +3,7 @@ greybus-y := core.o gbuf.o obj-m += greybus.o obj-m += i2c-gb.o obj-m += sdio-gb.o +obj-m += gpio-gb.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c new file mode 100644 index 000000000000..98b599b462c5 --- /dev/null +++ b/drivers/staging/greybus/gpio-gb.c @@ -0,0 +1,108 @@ +/* + * GPIO Greybus driver. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_gpio { + struct gpio_chip chip; + struct greybus_device *gdev; + // FIXME - some lock? +}; + +static const struct greybus_device_id id_table[] = { + { GREYBUS_DEVICE(0x44, 0x44) }, /* make shit up */ + { }, /* terminating NULL entry */ +}; + +static int direction_input(struct gpio_chip *gpio, unsigned nr) +{ + struct gp_gpio *gp_gpio = container_of(gpio, struct gp_gpio, chip); + + // FIXME - do something there + return 0; +} + +static int direction_output(struct gpio_chip *gpio, unsigned nr, int val) +{ + // FIXME - do something there + return 0; +} + +static int gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + // FIXME - do something there + return 0; +} + +static int gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +{ + // FIXME - do something there + return 0; +} + +static int gpio_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) +{ + struct gp_gpio *gp_gpio; + struct gpio_chip *gpio; + struct device *dev = &gdev->dev; + + gp_gpio = devm_kzalloc(dev, sizeof(*gp_gpio), GFP_KERNEL); + if (!gp_gpio) + return -ENOMEM; + gp_gpio->gdev = gdev; + + gpio = &gp_gpio->gpio; + + gpio->label = "greybus_gpio"; + gpio->owner = THIS_MODULE; + gpio->direction_input = direction_input; + gpio->direction_output = direction_output; + gpio->get = gpio_get; + gpio->set = gpio_set; + gpio->dbg_show = NULL; + gpio->base = 0; // FIXME!!! + gpio->ngpio = 42; // FIXME!!! + gpio->can_sleep = false; // FIXME!!! + + greybus_set_drvdata(gdev, gp_gpio); + + retval = gpio_chip_add(gpio); + if (retval) { + dev_err(dev, "Failed to register GPIO\n"); + return retval; + } + + return 0; +} + +static void gpio_gb_disconnect(struct greybus_device *gdev) +{ + struct mmc_host *mmc; + struct sd_gb_host *host; + + host = greybus_get_drvdata(gdev); + mmc = host->mmc; + + mmc_remove_host(mmc); + mmc_free_host(mmc); +} + +static struct greybus_driver gpio_gb_driver = { + .probe = gpio_gb_probe, + .disconnect = gpio_gb_disconnect, + .id_table = id_table, +}; + +module_greybus_driver(gpio_gb_driver); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Greybus GPIO driver"); +MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From e9023d227ab854c9ea3019c226b2c15cb3f3c2ea Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 12 Aug 2014 14:41:49 +0800 Subject: greybus: gpio-gb.c: it now builds properly --- drivers/staging/greybus/gpio-gb.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 98b599b462c5..0cbabb5606ac 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -10,11 +10,13 @@ #include #include #include +#include #include "greybus.h" struct gb_gpio { struct gpio_chip chip; struct greybus_device *gdev; + struct gpio_chip *gpio; // FIXME - some lock? }; @@ -25,7 +27,7 @@ static const struct greybus_device_id id_table[] = { static int direction_input(struct gpio_chip *gpio, unsigned nr) { - struct gp_gpio *gp_gpio = container_of(gpio, struct gp_gpio, chip); +// struct gb_gpio *gb_gpio = container_of(gpio, struct gb_gpio, chip); // FIXME - do something there return 0; @@ -43,24 +45,25 @@ static int gpio_get(struct gpio_chip *gpio, unsigned nr) return 0; } -static int gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +static void gpio_set(struct gpio_chip *gpio, unsigned nr, int val) { // FIXME - do something there - return 0; + return; } static int gpio_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) { - struct gp_gpio *gp_gpio; + struct gb_gpio *gb_gpio; struct gpio_chip *gpio; struct device *dev = &gdev->dev; + int retval; - gp_gpio = devm_kzalloc(dev, sizeof(*gp_gpio), GFP_KERNEL); - if (!gp_gpio) + gb_gpio = devm_kzalloc(dev, sizeof(*gb_gpio), GFP_KERNEL); + if (!gb_gpio) return -ENOMEM; - gp_gpio->gdev = gdev; + gb_gpio->gdev = gdev; - gpio = &gp_gpio->gpio; + gpio = &gb_gpio->chip; gpio->label = "greybus_gpio"; gpio->owner = THIS_MODULE; @@ -73,9 +76,9 @@ static int gpio_gb_probe(struct greybus_device *gdev, const struct greybus_devic gpio->ngpio = 42; // FIXME!!! gpio->can_sleep = false; // FIXME!!! - greybus_set_drvdata(gdev, gp_gpio); + greybus_set_drvdata(gdev, gb_gpio); - retval = gpio_chip_add(gpio); + retval = gpiochip_add(gpio); if (retval) { dev_err(dev, "Failed to register GPIO\n"); return retval; @@ -86,14 +89,11 @@ static int gpio_gb_probe(struct greybus_device *gdev, const struct greybus_devic static void gpio_gb_disconnect(struct greybus_device *gdev) { - struct mmc_host *mmc; - struct sd_gb_host *host; + struct gb_gpio *gb_gpio; - host = greybus_get_drvdata(gdev); - mmc = host->mmc; + gb_gpio = greybus_get_drvdata(gdev); - mmc_remove_host(mmc); - mmc_free_host(mmc); + gpiochip_remove(&gb_gpio->chip); } static struct greybus_driver gpio_gb_driver = { -- cgit v1.2.3-59-g8ed1b From 79c822be7b85c8bfc682c011d06a02fe960773ad Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 15 Aug 2014 16:01:23 +0800 Subject: greybus: uart framework added, doesn't build --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/uart-gb.c | 118 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 drivers/staging/greybus/uart-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 12705b0543a3..b84ff769ea4f 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -4,6 +4,7 @@ obj-m += greybus.o obj-m += i2c-gb.o obj-m += sdio-gb.o obj-m += gpio-gb.o +obj-m += uart-gb.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c new file mode 100644 index 000000000000..95273073a4af --- /dev/null +++ b/drivers/staging/greybus/uart-gb.c @@ -0,0 +1,118 @@ +/* + * I2C bridge driver for the Greybus "generic" I2C module. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "greybus.h" + +#define GB_TTY_MAJOR 180 /* FIXME use a real number!!! */ + +struct gb_tty { + struct tty_port port; + struct greybus_device *gdev; + int cport; + unsigned int minor; + unsigned char clocal; + // FIXME locking!!! +}; + +static const struct greybus_device_id id_table[] = { + { GREYBUS_DEVICE(0x45, 0x45) }, /* make shit up */ + { }, /* terminating NULL entry */ +}; + + +static const struct tty_operations gb_ops = { + .install = gb_tty_install, + .open = gb_tty_open, + .close = gb_tty_close, + .cleanup = gb_tty_cleanup, + .hangup = gb_tty_hangup, + .write = gb_tty_write, + .write_room = gb_tty_write_room, + .ioctl = gb_tty_ioctl, + .throttle = gb_tty_throttle, + .unthrottle = gb_tty_unthrottle, + .chars_in_buffer = gb_tty_chars_in_buffer, + .break_ctl = gb_tty_break_ctl, + .set_termios = gb_tty_set_termios, + .tiocmget = gb_tty_tiocmget, + .tiocmset = gb_tty_tiocmset, +}; + +static struct tty_driver *gb_tty_driver; +static struct gb_tty *gb_tty_table[255]; + +static int tty_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) +{ + int retval; + + //greybus_set_drvdata(gdev, i2c_gb_data); + return 0; +} + +static void tty_gb_disconnect(struct greybus_device *gdev) +{ +} + +static struct greybus_driver tty_gb_driver = { + .probe = tty_gb_probe, + .disconnect = tty_gb_disconnect, + .id_table = id_table, +}; + + +static int __init gb_tty_init(void) +{ + int retval; + + gb_tty_driver = alloc_tty_driver(255); + if (!gb_tty_driver) + return -ENOMEM; + + gb_tty_driver->driver_name = "gb"; + gb_tty_driver->name = "ttyGB"; + gb_tty_driver->major = GB_TTY_MAJOR; + gb_tty_driver->minor_start = 0; + gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + gb_tty_driver->init_termios = tty_std_termios; + gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty_set_operations(gb_tty_driver, &gb_ops); + + retval = tty_register_driver(gb_tty_driver); + if (retval) { + put_tty_driver(gb_tty_driver); + return retval; + } + + retval = greybus_register(&tty_gb_driver); + if (retval) { + tty_unregister_driver(gb_tty_driver); + put_tty_driver(gb_tty_driver); + } + return retval; +} + +static void __exit gb_tty_exit(void) +{ + greybus_deregister(&tty_gb_driver); + tty_unregister_driver(gb_tty_driver); + put_tty_driver(gb_tty_driver); +} + +module_init(gb_tty_init); +module_exit(gb_tty_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From ff45c265f849ce42e8d1eb48e49e8176019ec5af Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 15 Aug 2014 18:33:33 +0800 Subject: greybus: uart-gb: more work on tty functions --- drivers/staging/greybus/uart-gb.c | 86 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 95273073a4af..3eaada99951f 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "greybus.h" #define GB_TTY_MAJOR 180 /* FIXME use a real number!!! */ @@ -31,6 +32,89 @@ static const struct greybus_device_id id_table[] = { { }, /* terminating NULL entry */ }; +static struct tty_driver *gb_tty_driver; +static DEFINE_IDR(tty_minors); +static DEFINE_MUTEX(table_lock); + +static struct gb_tty *get_gb_by_minor(unsigned minor) +{ + struct gb_tty *gb_tty; + + mutex_lock(&table_lock); + gb_tty = idr_find(&tty_minors, minor); + mutex_unlock(&table_lock); + return gb_tty; +} + +static int alloc_minor(struct gb_tty *gb_tty) +{ + int minor; + + mutex_lock(&table_lock); + minor = idr_alloc(&tty_minors, gb_tty, 0, 0, GFP_KERNEL); + if (minor < 0) + goto error; + gb_tty->minor = minor; +error: + mutex_unlock(&table_lock); + return minor; +} + +static void release_minor(struct gb_tty *gb_tty) +{ + mutex_lock(&table_lock); + idr_remove(&tty_minors, gb_tty->minor); + mutex_unlock(&table_lock); +} + +static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct gb_tty *gb_tty; + int retval; + + gb_tty = get_gb_by_minor(tty->index); + if (!gb_tty) + return -ENODEV; + + retval = tty_standard_install(driver, tty); + if (retval) + goto error; + + tty->driver_data = gb_tty; + return 0; +error: + tty_port_put(&gb_tty->port); + return retval; +} + +static int gb_tty_open(struct tty_struct *tty, struct file *file) +{ + struct gb_tty *gb_tty = tty->driver_data; + + return tty_port_open(&gb_tty->port, tty, file); +} + +static void gb_tty_close(struct tty_struct *tty, struct file *file) +{ + struct gb_tty *gb_tty = tty->driver_data; + + tty_port_close(&gb_tty->port, tty, file); +} + +static void gb_tty_cleanup(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + tty_port_put(&gb_tty->port); +} + +static void gb_tty_hangup(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + tty_port_hangup(&gb_tty->port); +} + static const struct tty_operations gb_ops = { .install = gb_tty_install, @@ -50,8 +134,6 @@ static const struct tty_operations gb_ops = { .tiocmset = gb_tty_tiocmset, }; -static struct tty_driver *gb_tty_driver; -static struct gb_tty *gb_tty_table[255]; static int tty_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) { -- cgit v1.2.3-59-g8ed1b From a18e15175708d39abbe9746ddc3479466b7800c3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 15 Aug 2014 18:54:11 +0800 Subject: greybus: more uart work --- drivers/staging/greybus/uart-gb.c | 106 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 3eaada99951f..792536c1e77e 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -16,7 +16,8 @@ #include #include "greybus.h" -#define GB_TTY_MAJOR 180 /* FIXME use a real number!!! */ +#define GB_TTY_MAJOR 180 /* FIXME use a real number!!! */ +#define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ struct gb_tty { struct tty_port port; @@ -24,6 +25,10 @@ struct gb_tty { int cport; unsigned int minor; unsigned char clocal; + unsigned int throttled:1; + unsigned int throttle_req:1; + spinlock_t read_lock; + spinlock_t write_lock; // FIXME locking!!! }; @@ -115,6 +120,57 @@ static void gb_tty_hangup(struct tty_struct *tty) tty_port_hangup(&gb_tty->port); } +static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - actually implement... + + return 0; +} + +static int gb_tty_write_room(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - how much do we want to say we have room for? + return 0; +} + +static int gb_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - how many left to send? + return 0; +} + +static void gb_tty_throttle(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + spin_lock_irq(&gb_tty->read_lock); + gb_tty->throttle_req = 1; + spin_unlock_irq(&gb_tty->read_lock); +} + +static void gb_tty_unthrottle(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + unsigned int was_throttled; + + spin_lock_irq(&gb_tty->read_lock); + was_throttled = gb_tty->throttled; + gb_tty->throttle_req = 0; + gb_tty->throttled = 0; + spin_unlock_irq(&gb_tty->read_lock); + + if (was_throttled) { + // FIXME - send more data + } +} + static const struct tty_operations gb_ops = { .install = gb_tty_install, @@ -137,14 +193,58 @@ static const struct tty_operations gb_ops = { static int tty_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) { + struct gb_tty *gb_tty; + struct device *tty_dev; int retval; + int minor; + + gb_tty = devm_kzalloc(&gdev->dev, sizeof(*gb_tty), GFP_KERNEL); + if (!gb_tty) + return -ENOMEM; + + minor = alloc_minor(gb_tty); + if (minor == GB_NUM_MINORS) { + dev_err(&gdev->dev, "no more free minor numbers\n"); + return -ENODEV; + } + + gb_tty->minor = minor; + gb_tty->gdev = gdev; + spin_lock_init(&gb_tty->write_lock); + spin_lock_init(&gb_tty->read_lock); + + /* FIXME - allocate gb buffers */ + + greybus_set_drvdata(gdev, gb_tty); + + tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, + &gdev->dev); + if (IS_ERR(tty_dev)) { + retval = PTR_ERR(tty_dev); + goto error; + } - //greybus_set_drvdata(gdev, i2c_gb_data); return 0; +error: + release_minor(gb_tty); + return retval; } static void tty_gb_disconnect(struct greybus_device *gdev) { + struct gb_tty *gb_tty = greybus_get_drvdata(gdev); + struct tty_struct *tty; + + tty = tty_port_tty_get(&gb_tty->port); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + /* FIXME - stop all traffic */ + + tty_unregister_device(gb_tty_driver, gb_tty->minor); + + tty_port_put(&gb_tty->port); } static struct greybus_driver tty_gb_driver = { @@ -158,7 +258,7 @@ static int __init gb_tty_init(void) { int retval; - gb_tty_driver = alloc_tty_driver(255); + gb_tty_driver = alloc_tty_driver(GB_NUM_MINORS); if (!gb_tty_driver) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From e68453ed28c550c448709473ab89cebafa0ee34e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 15 Aug 2014 19:44:32 +0800 Subject: greybus: uart-gb: now builds, more framework added --- drivers/staging/greybus/uart-gb.c | 221 +++++++++++++++++++++++++++++++++++++- 1 file changed, 216 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 792536c1e77e..13a23cde71fb 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -1,18 +1,27 @@ /* - * I2C bridge driver for the Greybus "generic" I2C module. + * UART driver for the Greybus "generic" UART module. * * Copyright 2014 Google Inc. * * Released under the GPLv2 only. + * + * Heavily based on drivers/usb/class/cdc-acm.c and + * drivers/usb/serial/usb-serial.c. */ #include +#include #include +#include +#include #include +#include +#include #include #include #include #include +#include #include #include "greybus.h" @@ -27,9 +36,14 @@ struct gb_tty { unsigned char clocal; unsigned int throttled:1; unsigned int throttle_req:1; + bool disconnected; + int writesize; // FIXME - set this somehow. spinlock_t read_lock; spinlock_t write_lock; - // FIXME locking!!! + struct async_icount iocount; + struct async_icount oldcount; + wait_queue_head_t wioctl; + struct mutex mutex; }; static const struct greybus_device_id id_table[] = { @@ -47,6 +61,16 @@ static struct gb_tty *get_gb_by_minor(unsigned minor) mutex_lock(&table_lock); gb_tty = idr_find(&tty_minors, minor); + if (gb_tty) { + mutex_lock(&gb_tty->mutex); + if (gb_tty->disconnected) { + mutex_unlock(&gb_tty->mutex); + gb_tty = NULL; + } else { + tty_port_get(&gb_tty->port); + mutex_unlock(&gb_tty->mutex); + } + } mutex_unlock(&table_lock); return gb_tty; } @@ -123,7 +147,7 @@ static void gb_tty_hangup(struct tty_struct *tty) static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { - struct gb_tty *gb_tty = tty->driver_data; +// struct gb_tty *gb_tty = tty->driver_data; // FIXME - actually implement... @@ -132,7 +156,7 @@ static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, static int gb_tty_write_room(struct tty_struct *tty) { - struct gb_tty *gb_tty = tty->driver_data; +// struct gb_tty *gb_tty = tty->driver_data; // FIXME - how much do we want to say we have room for? return 0; @@ -140,12 +164,43 @@ static int gb_tty_write_room(struct tty_struct *tty) static int gb_tty_chars_in_buffer(struct tty_struct *tty) { - struct gb_tty *gb_tty = tty->driver_data; +// struct gb_tty *gb_tty = tty->driver_data; // FIXME - how many left to send? return 0; } +static int gb_tty_break_ctl(struct tty_struct *tty, int state) +{ +// struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - send a break, if asked to... + return 0; +} + +static void gb_tty_set_termios(struct tty_struct *tty, struct ktermios *old) +{ + // FIXME - is this it??? + tty_termios_copy_hw(&tty->termios, old); +} + +static int gb_tty_tiocmget(struct tty_struct *tty) +{ +// struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - get some tiocms! + return 0; +} + +static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, + unsigned int clear) +{ +// struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - set some tiocms! + return 0; +} + static void gb_tty_throttle(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; @@ -171,6 +226,148 @@ static void gb_tty_unthrottle(struct tty_struct *tty) } } +static int get_serial_info(struct gb_tty *gb_tty, + struct serial_struct __user *info) +{ + struct serial_struct tmp; + + if (!info) + return -EINVAL; + + memset(&tmp, 0, sizeof(tmp)); + tmp.flags = ASYNC_LOW_LATENCY; + tmp.xmit_fifo_size = gb_tty->writesize; + tmp.baud_base = 0; // FIXME + tmp.close_delay = gb_tty->port.close_delay / 10; + tmp.closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; + + if (copy_to_user(info, &tmp, sizeof(tmp))) + return -EFAULT; + else + return 0; +} + +static int set_serial_info(struct gb_tty *gb_tty, + struct serial_struct __user *newinfo) +{ + struct serial_struct new_serial; + unsigned int closing_wait; + unsigned int close_delay; + int retval; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + close_delay = new_serial.close_delay * 10; + closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; + + mutex_lock(&gb_tty->port.mutex); + if (!capable(CAP_SYS_ADMIN)) { + if ((close_delay != gb_tty->port.close_delay) || + (closing_wait != gb_tty->port.closing_wait)) + retval = -EPERM; + else + retval = -EOPNOTSUPP; + } else { + gb_tty->port.close_delay = close_delay; + gb_tty->port.closing_wait = closing_wait; + } + mutex_unlock(&gb_tty->port.mutex); + return retval; +} + +static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) +{ + int retval = 0; + DECLARE_WAITQUEUE(wait, current); + struct async_icount old; + struct async_icount new; + + if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD)) + return -EINVAL; + + do { + spin_lock_irq(&gb_tty->read_lock); + old = gb_tty->oldcount; + new = gb_tty->iocount; + gb_tty->oldcount = new; + spin_lock_irq(&gb_tty->read_lock); + + if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) + break; + if ((arg & TIOCM_CD) && (old.dcd != new.dcd)) + break; + if ((arg & TIOCM_RI) && (old.rng != new.rng)) + break; + + add_wait_queue(&gb_tty->wioctl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + remove_wait_queue(&gb_tty->wioctl, &wait); + if (gb_tty->disconnected) { + if (arg & TIOCM_CD) + break; + else + retval = -ENODEV; + } else { + if (signal_pending(current)) + retval = -ERESTARTSYS; + } + } while (!retval); + + return retval; +} + +static int get_serial_usage(struct gb_tty *gb_tty, + struct serial_icounter_struct __user *count) +{ + struct serial_icounter_struct icount; + int retval = 0; + + memset(&icount, 0, sizeof(icount)); + icount.dsr = gb_tty->iocount.dsr; + icount.rng = gb_tty->iocount.rng; + icount.dcd = gb_tty->iocount.dcd; + icount.frame = gb_tty->iocount.frame; + icount.overrun = gb_tty->iocount.overrun; + icount.parity = gb_tty->iocount.parity; + icount.brk = gb_tty->iocount.brk; + + if (copy_to_user(count, &icount, sizeof(icount)) > 0) + retval = -EFAULT; + + return retval; +} + +static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg) +{ + struct gb_tty *gb_tty = tty->driver_data; + int retval = -ENOIOCTLCMD; + + switch (cmd) { + case TIOCGSERIAL: + retval = get_serial_info(gb_tty, + (struct serial_struct __user *)arg); + break; + case TIOCSSERIAL: + retval = set_serial_info(gb_tty, + (struct serial_struct __user *)arg); + break; + case TIOCMIWAIT: + retval = wait_serial_change(gb_tty, arg); + break; + case TIOCGICOUNT: + retval = get_serial_usage(gb_tty, + (struct serial_icounter_struct __user *)arg); + break; + } + + return retval; +} + static const struct tty_operations gb_ops = { .install = gb_tty_install, @@ -212,6 +409,8 @@ static int tty_gb_probe(struct greybus_device *gdev, const struct greybus_device gb_tty->gdev = gdev; spin_lock_init(&gb_tty->write_lock); spin_lock_init(&gb_tty->read_lock); + init_waitqueue_head(&gb_tty->wioctl); + mutex_init(&gb_tty->mutex); /* FIXME - allocate gb buffers */ @@ -235,6 +434,16 @@ static void tty_gb_disconnect(struct greybus_device *gdev) struct gb_tty *gb_tty = greybus_get_drvdata(gdev); struct tty_struct *tty; + if (!gb_tty) + return; + + mutex_lock(&gb_tty->mutex); + gb_tty->disconnected = true; + + wake_up_all(&gb_tty->wioctl); + greybus_set_drvdata(gdev, NULL); + mutex_unlock(&gb_tty->mutex); + tty = tty_port_tty_get(&gb_tty->port); if (tty) { tty_vhangup(tty); @@ -244,6 +453,8 @@ static void tty_gb_disconnect(struct greybus_device *gdev) tty_unregister_device(gb_tty_driver, gb_tty->minor); + /* FIXME - free transmit / recieve buffers */ + tty_port_put(&gb_tty->port); } -- cgit v1.2.3-59-g8ed1b From ff5f0b38228fbf10ff58a530a8a462b3b90cc7ef Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 18 Aug 2014 18:25:11 -0500 Subject: greybus: uart-gb: improve minor device number error checking When alloc_minor() finds an available minor device number it does not constrain the highest number desired. Instead, it relies on its caller, tty_gb_probe() to see if the returned number indicates all minor numbers have been exhausted. There are a couple problems with this--or rather with this code. First, if an allocation is attempted *after* GB_NUM_MINORS is returned, a new number greater than (but not equal to) GB_NUM_MINORS will be allocated, and that won't produce any error condition. Second, alloc_minor() can return an error code (like -ENOMEM). And its caller is only checking for GB_NUM_MINORS. If an error code is returned, tty_gb_probe() simply uses it. Change alloc_minor() so it requests minor device numbers in the range 0..(GB_NUM_MINORS-1), and use an error return to detect when the minor device numbers have been exhausted. If alloc_minor() returns -ENOSPC (from idr_alloc()), translate that to -ENODEV. The only other error we might see is -ENOMEM, and if we get that, return it. Finally, zero gb_tty->minor when it's released. (If this is actually important a reserved value like GB_NUM_MINORS should be used instead to signify a gb_tty with no minor assigned.) Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 13a23cde71fb..0e2507cdcd5a 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -80,19 +80,20 @@ static int alloc_minor(struct gb_tty *gb_tty) int minor; mutex_lock(&table_lock); - minor = idr_alloc(&tty_minors, gb_tty, 0, 0, GFP_KERNEL); - if (minor < 0) - goto error; - gb_tty->minor = minor; -error: + minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL); mutex_unlock(&table_lock); + if (minor >= 0) + gb_tty->minor = minor; return minor; } static void release_minor(struct gb_tty *gb_tty) { + int minor = gb_tty->minor; + + gb_tty->minor = 0; /* Maybe should use an invalid value instead */ mutex_lock(&table_lock); - idr_remove(&tty_minors, gb_tty->minor); + idr_remove(&tty_minors, minor); mutex_unlock(&table_lock); } @@ -400,9 +401,12 @@ static int tty_gb_probe(struct greybus_device *gdev, const struct greybus_device return -ENOMEM; minor = alloc_minor(gb_tty); - if (minor == GB_NUM_MINORS) { - dev_err(&gdev->dev, "no more free minor numbers\n"); - return -ENODEV; + if (minor < 0) { + if (minor == -ENOSPC) { + dev_err(&gdev->dev, "no more free minor numbers\n"); + return -ENODEV; + } + return minor; } gb_tty->minor = minor; -- cgit v1.2.3-59-g8ed1b From caaa8a838dd098fc72f393b871c3f882bfac9e77 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 18 Aug 2014 18:25:12 -0500 Subject: greybus: uart-gb: a few minor bug fixes Here are a few small bug fixes in uart-gb.c: - In wait_serial_change(): - Return -EINVAL if *none* of the relevant flags are set in the "arg" parameter. - Balance the spin_lock_irq() with an unlock call (not another lock). - Rearrange a nested if structure (not a bug fix). - In tty_gb_probe(): - Reset the greybus_device driver data in case of error. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 0e2507cdcd5a..4a54e9e27b25 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -286,7 +286,7 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) struct async_icount old; struct async_icount new; - if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD)) + if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) return -EINVAL; do { @@ -294,7 +294,7 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) old = gb_tty->oldcount; new = gb_tty->iocount; gb_tty->oldcount = new; - spin_lock_irq(&gb_tty->read_lock); + spin_unlock_irq(&gb_tty->read_lock); if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) break; @@ -310,11 +310,9 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) if (gb_tty->disconnected) { if (arg & TIOCM_CD) break; - else - retval = -ENODEV; - } else { - if (signal_pending(current)) - retval = -ERESTARTSYS; + retval = -ENODEV; + } else if (signal_pending(current)) { + retval = -ERESTARTSYS; } } while (!retval); @@ -429,6 +427,7 @@ static int tty_gb_probe(struct greybus_device *gdev, const struct greybus_device return 0; error: + greybus_set_drvdata(gdev, NULL); release_minor(gb_tty); return retval; } -- cgit v1.2.3-59-g8ed1b From 199d68d4a8cb3d2318d62a29f74d1838e1069232 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 16:20:22 -0700 Subject: greybus: start moving the function types into the greybus core --- drivers/staging/greybus/Makefile | 3 +-- drivers/staging/greybus/core.c | 24 ++++++++++++++++++++ drivers/staging/greybus/gpio-gb.c | 12 +++++----- drivers/staging/greybus/greybus.h | 29 ++++++++++++++++++++++++ drivers/staging/greybus/i2c-gb.c | 47 +++++++++++++++++++++------------------ drivers/staging/greybus/sdio-gb.c | 8 +++---- drivers/staging/greybus/uart-gb.c | 21 +++++++---------- 7 files changed, 97 insertions(+), 47 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index b84ff769ea4f..2e3212b7301e 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,7 +1,6 @@ -greybus-y := core.o gbuf.o +greybus-y := core.o gbuf.o i2c-gb.o obj-m += greybus.o -obj-m += i2c-gb.o obj-m += sdio-gb.o obj-m += gpio-gb.o obj-m += uart-gb.o diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 87148cd38b56..ccc4e353fdb9 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -149,5 +149,29 @@ void greybus_deregister(struct greybus_driver *driver) } EXPORT_SYMBOL_GPL(greybus_deregister); + +static int new_device(struct greybus_device *gdev, + const struct greybus_device_id *id) +{ + int retval; + + /* Allocate all of the different "sub device types" for this device */ + retval = gb_i2c_probe(gdev, id); + return 0; +} + + +int __init gb_init(void) +{ + return 0; +} + +void __exit gb_exit(void) +{ +} + +module_init(gb_init); +module_exit(gb_exit); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 0cbabb5606ac..990a74d53577 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -13,7 +13,7 @@ #include #include "greybus.h" -struct gb_gpio { +struct gb_gpio_device { struct gpio_chip chip; struct greybus_device *gdev; struct gpio_chip *gpio; @@ -27,7 +27,7 @@ static const struct greybus_device_id id_table[] = { static int direction_input(struct gpio_chip *gpio, unsigned nr) { -// struct gb_gpio *gb_gpio = container_of(gpio, struct gb_gpio, chip); + //struct gb_gpio_device *gb_gpio_dev = container_of(gpio, struct gb_gpio_device, chip); // FIXME - do something there return 0; @@ -53,7 +53,7 @@ static void gpio_set(struct gpio_chip *gpio, unsigned nr, int val) static int gpio_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) { - struct gb_gpio *gb_gpio; + struct gb_gpio_device *gb_gpio; struct gpio_chip *gpio; struct device *dev = &gdev->dev; int retval; @@ -89,11 +89,11 @@ static int gpio_gb_probe(struct greybus_device *gdev, const struct greybus_devic static void gpio_gb_disconnect(struct greybus_device *gdev) { - struct gb_gpio *gb_gpio; + struct gb_gpio_device *gb_gpio_dev; - gb_gpio = greybus_get_drvdata(gdev); + gb_gpio_dev = greybus_get_drvdata(gdev); - gpiochip_remove(&gb_gpio->chip); + gpiochip_remove(&gb_gpio_dev->chip); } static struct greybus_driver gpio_gb_driver = { diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 51b9f5b69979..3137668f25d2 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -72,15 +72,44 @@ struct gbuf { */ #define GBUF_FREE_BUFFER BIT(0) /* Free the transfer buffer with the gbuf */ +/* For SP1 hardware, we are going to "hardcode" each device to have all logical + * blocks in order to be able to address them as one unified "unit". Then + * higher up layers will then be able to talk to them as one logical block and + * properly know how they are hooked together (i.e. which i2c port is on the + * same module as the gpio pins, etc.) + * + * So, put the "private" data structures here in greybus.h and link to them off + * of the "main" greybus_device structure. + */ + +struct gb_i2c_device; +struct gb_gpio_device; +struct gb_sdio_host; +struct gb_tty; +struct gb_usb_device; struct greybus_device { struct device dev; struct greybus_descriptor descriptor; int num_cport; struct cport cport[0]; + + struct gb_i2c_device *gb_i2c_dev; + struct gb_gpio_device *gb_gpio_dev; + struct gb_sdio_host *gb_sdio_host; + struct gb_tty *gb_tty; + struct gb_usb_device *gb_usb_dev; }; #define to_greybus_device(d) container_of(d, struct greybus_device, dev) +/* + * Because we are allocating a data structure per "type" in the greybus device, + * we have static functions for this, not "dynamic" drivers like we really + * should in the end. + */ +int gb_i2c_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +void gb_i2c_disconnect(struct greybus_device *gdev); + struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, struct cport *cport, diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 7fcbcef641a5..929243e15954 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -12,7 +12,7 @@ #include #include "greybus.h" -struct i2c_gb_data { +struct gb_i2c_device { struct i2c_adapter *adapter; struct greybus_device *gdev; }; @@ -32,11 +32,11 @@ static s32 i2c_gb_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { - struct i2c_gb_data *i2c_gb_data; + struct gb_i2c_device *i2c_gb_dev; struct greybus_device *gdev; - i2c_gb_data = i2c_get_adapdata(adap); - gdev = i2c_gb_data->gdev; + i2c_gb_dev = i2c_get_adapdata(adap); + gdev = i2c_gb_dev->gdev; // FIXME - do the actual work of sending a i2c message here... switch (size) { @@ -75,22 +75,23 @@ static const struct i2c_algorithm smbus_algorithm = { .functionality = i2c_gb_func, }; -static int i2c_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) +int gb_i2c_probe(struct greybus_device *gdev, + const struct greybus_device_id *id) { - struct i2c_gb_data *i2c_gb_data; + struct gb_i2c_device *i2c_gb_dev; struct i2c_adapter *adapter; int retval; - i2c_gb_data = kzalloc(sizeof(*i2c_gb_data), GFP_KERNEL); - if (!i2c_gb_data) + i2c_gb_dev = kzalloc(sizeof(*i2c_gb_dev), GFP_KERNEL); + if (!i2c_gb_dev) return -ENOMEM; adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); if (!adapter) { - kfree(i2c_gb_data); + kfree(i2c_gb_dev); return -ENOMEM; } - i2c_set_adapdata(adapter, i2c_gb_data); + i2c_set_adapdata(adapter, i2c_gb_dev); adapter->owner = THIS_MODULE; adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adapter->algo = &smbus_algorithm; @@ -103,33 +104,35 @@ static int i2c_gb_probe(struct greybus_device *gdev, const struct greybus_device goto error; } - i2c_gb_data->gdev = gdev; - i2c_gb_data->adapter = adapter; + i2c_gb_dev->gdev = gdev; + i2c_gb_dev->adapter = adapter; - greybus_set_drvdata(gdev, i2c_gb_data); + greybus_set_drvdata(gdev, i2c_gb_dev); return 0; error: kfree(adapter); - kfree(i2c_gb_data); + kfree(i2c_gb_dev); return retval; } -static void i2c_gb_disconnect(struct greybus_device *gdev) +void gb_i2c_disconnect(struct greybus_device *gdev) { - struct i2c_gb_data *i2c_gb_data; + struct gb_i2c_device *i2c_gb_dev; - i2c_gb_data = greybus_get_drvdata(gdev); - i2c_del_adapter(i2c_gb_data->adapter); - kfree(i2c_gb_data->adapter); - kfree(i2c_gb_data); + i2c_gb_dev = greybus_get_drvdata(gdev); + i2c_del_adapter(i2c_gb_dev->adapter); + kfree(i2c_gb_dev->adapter); + kfree(i2c_gb_dev); } +#if 0 static struct greybus_driver i2c_gb_driver = { - .probe = i2c_gb_probe, - .disconnect = i2c_gb_disconnect, + .probe = gb_i2c_probe, + .disconnect = gb_i2c_disconnect, .id_table = id_table, }; module_greybus_driver(i2c_gb_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Greg Kroah-Hartman "); +#endif diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index f92ab1b7438a..8521b060878b 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -12,7 +12,7 @@ #include #include "greybus.h" -struct sd_gb_host { +struct gb_sdio_host { struct mmc_host *mmc; struct mmc_request *mrq; // FIXME - some lock? @@ -48,9 +48,9 @@ static const struct mmc_host_ops gb_sd_ops = { static int sd_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) { struct mmc_host *mmc; - struct sd_gb_host *host; + struct gb_sdio_host *host; - mmc = mmc_alloc_host(sizeof(struct sd_gb_host), &gdev->dev); + mmc = mmc_alloc_host(sizeof(struct gb_sdio_host), &gdev->dev); if (!mmc) return -ENOMEM; @@ -67,7 +67,7 @@ static int sd_gb_probe(struct greybus_device *gdev, const struct greybus_device_ static void sd_gb_disconnect(struct greybus_device *gdev) { struct mmc_host *mmc; - struct sd_gb_host *host; + struct gb_sdio_host *host; host = greybus_get_drvdata(gdev); mmc = host->mmc; diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 4a54e9e27b25..839ea2d045e0 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -344,27 +344,22 @@ static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct gb_tty *gb_tty = tty->driver_data; - int retval = -ENOIOCTLCMD; switch (cmd) { case TIOCGSERIAL: - retval = get_serial_info(gb_tty, - (struct serial_struct __user *)arg); - break; + return get_serial_info(gb_tty, + (struct serial_struct __user *)arg); case TIOCSSERIAL: - retval = set_serial_info(gb_tty, - (struct serial_struct __user *)arg); - break; + return set_serial_info(gb_tty, + (struct serial_struct __user *)arg); case TIOCMIWAIT: - retval = wait_serial_change(gb_tty, arg); - break; + return wait_serial_change(gb_tty, arg); case TIOCGICOUNT: - retval = get_serial_usage(gb_tty, - (struct serial_icounter_struct __user *)arg); - break; + return get_serial_usage(gb_tty, + (struct serial_icounter_struct __user *)arg); } - return retval; + return -ENOIOCTLCMD; } -- cgit v1.2.3-59-g8ed1b From 503c1cdbfb5c7570bf64d7b04847861dfd6328fa Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 16:21:03 -0700 Subject: greybus: static module_init/exit functions --- drivers/staging/greybus/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index ccc4e353fdb9..f8332b4d5db0 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -161,12 +161,12 @@ static int new_device(struct greybus_device *gdev, } -int __init gb_init(void) +static int __init gb_init(void) { return 0; } -void __exit gb_exit(void) +static void __exit gb_exit(void) { } -- cgit v1.2.3-59-g8ed1b From db6e1fd264ac6716ad9778e9b6cd6bae5a527f5e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 16:47:26 -0700 Subject: greybus: hook up sdio, gpio, and tty into the greybus core. --- drivers/staging/greybus/Makefile | 5 +---- drivers/staging/greybus/core.c | 41 +++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/gpio-gb.c | 11 +++++++---- drivers/staging/greybus/greybus.h | 10 +++++++++- drivers/staging/greybus/sdio-gb.c | 11 +++++++---- drivers/staging/greybus/uart-gb.c | 15 ++++++++------ 6 files changed, 74 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 2e3212b7301e..74d4124a5c63 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,9 +1,6 @@ -greybus-y := core.o gbuf.o i2c-gb.o +greybus-y := core.o gbuf.o i2c-gb.o gpio-gb.o sdio-gb.o uart-gb.o obj-m += greybus.o -obj-m += sdio-gb.o -obj-m += gpio-gb.o -obj-m += uart-gb.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index f8332b4d5db0..b1a5b887e9ee 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -157,17 +157,58 @@ static int new_device(struct greybus_device *gdev, /* Allocate all of the different "sub device types" for this device */ retval = gb_i2c_probe(gdev, id); + if (retval) + goto error_i2c; + + retval = gb_gpio_probe(gdev, id); + if (retval) + goto error_gpio; + + retval = gb_sdio_probe(gdev, id); + if (retval) + goto error_sdio; + + retval = gb_tty_probe(gdev, id); + if (retval) + goto error_tty; return 0; + +error_tty: + gb_sdio_disconnect(gdev); + +error_sdio: + gb_gpio_disconnect(gdev); + +error_gpio: + gb_i2c_disconnect(gdev); + +error_i2c: + return retval; } +static void remove_device(struct greybus_device *gdev) +{ + /* tear down all of the "sub device types" for this device */ + gb_i2c_disconnect(gdev); + gb_gpio_disconnect(gdev); + gb_sdio_disconnect(gdev); + gb_tty_disconnect(gdev); +} static int __init gb_init(void) { + int retval; + + retval = gb_tty_init(); + if (retval) + return retval; + return 0; } static void __exit gb_exit(void) { + gb_tty_exit(); } module_init(gb_init); diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 990a74d53577..30b15ed32e12 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -51,7 +51,8 @@ static void gpio_set(struct gpio_chip *gpio, unsigned nr, int val) return; } -static int gpio_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) +int gb_gpio_probe(struct greybus_device *gdev, + const struct greybus_device_id *id) { struct gb_gpio_device *gb_gpio; struct gpio_chip *gpio; @@ -87,7 +88,7 @@ static int gpio_gb_probe(struct greybus_device *gdev, const struct greybus_devic return 0; } -static void gpio_gb_disconnect(struct greybus_device *gdev) +void gb_gpio_disconnect(struct greybus_device *gdev) { struct gb_gpio_device *gb_gpio_dev; @@ -96,9 +97,10 @@ static void gpio_gb_disconnect(struct greybus_device *gdev) gpiochip_remove(&gb_gpio_dev->chip); } +#if 0 static struct greybus_driver gpio_gb_driver = { - .probe = gpio_gb_probe, - .disconnect = gpio_gb_disconnect, + .probe = gb_gpio_probe, + .disconnect = gb_gpio_disconnect, .id_table = id_table, }; @@ -106,3 +108,4 @@ module_greybus_driver(gpio_gb_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Greybus GPIO driver"); MODULE_AUTHOR("Greg Kroah-Hartman "); +#endif diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 3137668f25d2..2440342aaca9 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -109,7 +109,15 @@ struct greybus_device { */ int gb_i2c_probe(struct greybus_device *gdev, const struct greybus_device_id *id); void gb_i2c_disconnect(struct greybus_device *gdev); - +int gb_gpio_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +void gb_gpio_disconnect(struct greybus_device *gdev); +int gb_sdio_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +void gb_sdio_disconnect(struct greybus_device *gdev); +int gb_tty_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +void gb_tty_disconnect(struct greybus_device *gdev); + +int gb_tty_init(void); +void gb_tty_exit(void); struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, struct cport *cport, diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 8521b060878b..9f7b44d63b67 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -45,7 +45,8 @@ static const struct mmc_host_ops gb_sd_ops = { .get_ro = gb_sd_get_ro, }; -static int sd_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) +int gb_sdio_probe(struct greybus_device *gdev, + const struct greybus_device_id *id) { struct mmc_host *mmc; struct gb_sdio_host *host; @@ -64,7 +65,7 @@ static int sd_gb_probe(struct greybus_device *gdev, const struct greybus_device_ return 0; } -static void sd_gb_disconnect(struct greybus_device *gdev) +void gb_sdio_disconnect(struct greybus_device *gdev) { struct mmc_host *mmc; struct gb_sdio_host *host; @@ -76,9 +77,10 @@ static void sd_gb_disconnect(struct greybus_device *gdev) mmc_free_host(mmc); } +#if 0 static struct greybus_driver sd_gb_driver = { - .probe = sd_gb_probe, - .disconnect = sd_gb_disconnect, + .probe = gb_sdio_probe, + .disconnect = gb_sdio_disconnect, .id_table = id_table, }; @@ -86,3 +88,4 @@ module_greybus_driver(sd_gb_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Greybus SD/MMC Host driver"); MODULE_AUTHOR("Greg Kroah-Hartman "); +#endif diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 839ea2d045e0..cdb4a824f253 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -382,7 +382,8 @@ static const struct tty_operations gb_ops = { }; -static int tty_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id) +int gb_tty_probe(struct greybus_device *gdev, + const struct greybus_device_id *id) { struct gb_tty *gb_tty; struct device *tty_dev; @@ -427,7 +428,7 @@ error: return retval; } -static void tty_gb_disconnect(struct greybus_device *gdev) +void gb_tty_disconnect(struct greybus_device *gdev) { struct gb_tty *gb_tty = greybus_get_drvdata(gdev); struct tty_struct *tty; @@ -457,13 +458,13 @@ static void tty_gb_disconnect(struct greybus_device *gdev) } static struct greybus_driver tty_gb_driver = { - .probe = tty_gb_probe, - .disconnect = tty_gb_disconnect, + .probe = gb_tty_probe, + .disconnect = gb_tty_disconnect, .id_table = id_table, }; -static int __init gb_tty_init(void) +int __init gb_tty_init(void) { int retval; @@ -496,14 +497,16 @@ static int __init gb_tty_init(void) return retval; } -static void __exit gb_tty_exit(void) +void __exit gb_tty_exit(void) { greybus_deregister(&tty_gb_driver); tty_unregister_driver(gb_tty_driver); put_tty_driver(gb_tty_driver); } +#if 0 module_init(gb_tty_init); module_exit(gb_tty_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Greg Kroah-Hartman "); +#endif -- cgit v1.2.3-59-g8ed1b From e7e0782c41681856740fbdd63972fba6ec08f943 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 16:49:05 -0700 Subject: greybus: i2c: tie to the proper place on the greybus_device --- drivers/staging/greybus/i2c-gb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 929243e15954..8b99e8a083a3 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -107,7 +107,7 @@ int gb_i2c_probe(struct greybus_device *gdev, i2c_gb_dev->gdev = gdev; i2c_gb_dev->adapter = adapter; - greybus_set_drvdata(gdev, i2c_gb_dev); + gdev->gb_i2c_dev = i2c_gb_dev; return 0; error: kfree(adapter); @@ -119,7 +119,7 @@ void gb_i2c_disconnect(struct greybus_device *gdev) { struct gb_i2c_device *i2c_gb_dev; - i2c_gb_dev = greybus_get_drvdata(gdev); + i2c_gb_dev = gdev->gb_i2c_dev; i2c_del_adapter(i2c_gb_dev->adapter); kfree(i2c_gb_dev->adapter); kfree(i2c_gb_dev); -- cgit v1.2.3-59-g8ed1b From 3d9efaaea3bd49ddd6d06b079812f4af8103d824 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 16:49:59 -0700 Subject: greybus: i2c: use same naming convention everywhere --- drivers/staging/greybus/i2c-gb.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 8b99e8a083a3..bb107f8ce490 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -32,11 +32,11 @@ static s32 i2c_gb_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { - struct gb_i2c_device *i2c_gb_dev; + struct gb_i2c_device *gb_i2c_dev; struct greybus_device *gdev; - i2c_gb_dev = i2c_get_adapdata(adap); - gdev = i2c_gb_dev->gdev; + gb_i2c_dev = i2c_get_adapdata(adap); + gdev = gb_i2c_dev->gdev; // FIXME - do the actual work of sending a i2c message here... switch (size) { @@ -78,20 +78,20 @@ static const struct i2c_algorithm smbus_algorithm = { int gb_i2c_probe(struct greybus_device *gdev, const struct greybus_device_id *id) { - struct gb_i2c_device *i2c_gb_dev; + struct gb_i2c_device *gb_i2c_dev; struct i2c_adapter *adapter; int retval; - i2c_gb_dev = kzalloc(sizeof(*i2c_gb_dev), GFP_KERNEL); - if (!i2c_gb_dev) + gb_i2c_dev = kzalloc(sizeof(*gb_i2c_dev), GFP_KERNEL); + if (!gb_i2c_dev) return -ENOMEM; adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); if (!adapter) { - kfree(i2c_gb_dev); + kfree(gb_i2c_dev); return -ENOMEM; } - i2c_set_adapdata(adapter, i2c_gb_dev); + i2c_set_adapdata(adapter, gb_i2c_dev); adapter->owner = THIS_MODULE; adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adapter->algo = &smbus_algorithm; @@ -104,25 +104,25 @@ int gb_i2c_probe(struct greybus_device *gdev, goto error; } - i2c_gb_dev->gdev = gdev; - i2c_gb_dev->adapter = adapter; + gb_i2c_dev->gdev = gdev; + gb_i2c_dev->adapter = adapter; - gdev->gb_i2c_dev = i2c_gb_dev; + gdev->gb_i2c_dev = gb_i2c_dev; return 0; error: kfree(adapter); - kfree(i2c_gb_dev); + kfree(gb_i2c_dev); return retval; } void gb_i2c_disconnect(struct greybus_device *gdev) { - struct gb_i2c_device *i2c_gb_dev; + struct gb_i2c_device *gb_i2c_dev; - i2c_gb_dev = gdev->gb_i2c_dev; - i2c_del_adapter(i2c_gb_dev->adapter); - kfree(i2c_gb_dev->adapter); - kfree(i2c_gb_dev); + gb_i2c_dev = gdev->gb_i2c_dev; + i2c_del_adapter(gb_i2c_dev->adapter); + kfree(gb_i2c_dev->adapter); + kfree(gb_i2c_dev); } #if 0 -- cgit v1.2.3-59-g8ed1b From 426f29d6be1272dc3e7678144d2ef2a89a1fdd1d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 16:51:21 -0700 Subject: greybus: gpio: tie into gb core properly --- drivers/staging/greybus/gpio-gb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 30b15ed32e12..7b8b85f6f9e5 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -77,7 +77,7 @@ int gb_gpio_probe(struct greybus_device *gdev, gpio->ngpio = 42; // FIXME!!! gpio->can_sleep = false; // FIXME!!! - greybus_set_drvdata(gdev, gb_gpio); + gdev->gb_gpio_dev= gb_gpio; retval = gpiochip_add(gpio); if (retval) { @@ -92,7 +92,7 @@ void gb_gpio_disconnect(struct greybus_device *gdev) { struct gb_gpio_device *gb_gpio_dev; - gb_gpio_dev = greybus_get_drvdata(gdev); + gb_gpio_dev = gdev->gb_gpio_dev; gpiochip_remove(&gb_gpio_dev->chip); } -- cgit v1.2.3-59-g8ed1b From 56f10573be491212dd46ec44789f7a3ffc6e3352 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 16:52:18 -0700 Subject: greybus: sdio: tie into gb core properly --- drivers/staging/greybus/sdio-gb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 9f7b44d63b67..81eb33d2c887 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -61,7 +61,7 @@ int gb_sdio_probe(struct greybus_device *gdev, mmc->ops = &gb_sd_ops; // FIXME - set up size limits we can handle. - greybus_set_drvdata(gdev, host); + gdev->gb_sdio_host = host; return 0; } @@ -70,7 +70,7 @@ void gb_sdio_disconnect(struct greybus_device *gdev) struct mmc_host *mmc; struct gb_sdio_host *host; - host = greybus_get_drvdata(gdev); + host = gdev->gb_sdio_host; mmc = host->mmc; mmc_remove_host(mmc); -- cgit v1.2.3-59-g8ed1b From eca17c52038f974d406768a6f0ab171b9e81a7c8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 16:54:05 -0700 Subject: greybus: uart: tie into gb core properly --- drivers/staging/greybus/uart-gb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index cdb4a824f253..377ad3846020 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -412,7 +412,7 @@ int gb_tty_probe(struct greybus_device *gdev, /* FIXME - allocate gb buffers */ - greybus_set_drvdata(gdev, gb_tty); + gdev->gb_tty = gb_tty; tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, &gdev->dev); @@ -423,14 +423,14 @@ int gb_tty_probe(struct greybus_device *gdev, return 0; error: - greybus_set_drvdata(gdev, NULL); + gdev->gb_tty = NULL; release_minor(gb_tty); return retval; } void gb_tty_disconnect(struct greybus_device *gdev) { - struct gb_tty *gb_tty = greybus_get_drvdata(gdev); + struct gb_tty *gb_tty = gdev->gb_tty; struct tty_struct *tty; if (!gb_tty) @@ -440,7 +440,7 @@ void gb_tty_disconnect(struct greybus_device *gdev) gb_tty->disconnected = true; wake_up_all(&gb_tty->wioctl); - greybus_set_drvdata(gdev, NULL); + gdev->gb_tty = NULL; mutex_unlock(&gb_tty->mutex); tty = tty_port_tty_get(&gb_tty->port); -- cgit v1.2.3-59-g8ed1b From ba4468d4641d959350d69d9ccaab41ffb92355cc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 17:06:54 -0700 Subject: greybus: initial framework for ES1 usb AP driver --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/es1-ap-usb.c | 45 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 drivers/staging/greybus/es1-ap-usb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 74d4124a5c63..29ba1da3aac2 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,6 +1,7 @@ greybus-y := core.o gbuf.o i2c-gb.o gpio-gb.o sdio-gb.o uart-gb.o obj-m += greybus.o +obj-m += es1-ap-usb.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c new file mode 100644 index 000000000000..05c724881b07 --- /dev/null +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -0,0 +1,45 @@ +/* + * Greybus "AP" USB driver + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ +#include +#include +#include +#include +#include + + +static const struct usb_device_id id_table[] = { + { USB_DEVICE(0x0000, 0x0000) }, // FIXME + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + + +static int ap_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + + return 0; +} + +static void ap_disconnect(struct usb_interface *interface) +{ + + +} + +static struct usb_driver es1_ap_driver = { + .name = "es1_ap_driver", + .probe = ap_probe, + .disconnect = ap_disconnect, + .id_table = id_table, +}; + +module_usb_driver(es1_ap_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From e5f167f1df8c0975ab90489724e7f542cd72dc45 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 17:11:41 -0700 Subject: greybus: can't use devm anymore, we aren't tieing into the driver model lifecycle :( --- drivers/staging/greybus/gpio-gb.c | 3 ++- drivers/staging/greybus/uart-gb.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 7b8b85f6f9e5..43fd0d350723 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -59,7 +59,7 @@ int gb_gpio_probe(struct greybus_device *gdev, struct device *dev = &gdev->dev; int retval; - gb_gpio = devm_kzalloc(dev, sizeof(*gb_gpio), GFP_KERNEL); + gb_gpio = kzalloc(dev, sizeof(*gb_gpio), GFP_KERNEL); if (!gb_gpio) return -ENOMEM; gb_gpio->gdev = gdev; @@ -95,6 +95,7 @@ void gb_gpio_disconnect(struct greybus_device *gdev) gb_gpio_dev = gdev->gb_gpio_dev; gpiochip_remove(&gb_gpio_dev->chip); + kfree(gb_gpio_dev); } #if 0 diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 377ad3846020..298b9ff7841b 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -390,7 +390,7 @@ int gb_tty_probe(struct greybus_device *gdev, int retval; int minor; - gb_tty = devm_kzalloc(&gdev->dev, sizeof(*gb_tty), GFP_KERNEL); + gb_tty = kzalloc(&gdev->dev, sizeof(*gb_tty), GFP_KERNEL); if (!gb_tty) return -ENOMEM; @@ -455,6 +455,8 @@ void gb_tty_disconnect(struct greybus_device *gdev) /* FIXME - free transmit / recieve buffers */ tty_port_put(&gb_tty->port); + + kfree(gb_tty); } static struct greybus_driver tty_gb_driver = { -- cgit v1.2.3-59-g8ed1b From 8bf23e84d7cc2d63cfe433ded084e1052fc6d487 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 17:18:04 -0700 Subject: greybus: actually get the devm() change to build... --- drivers/staging/greybus/gpio-gb.c | 2 +- drivers/staging/greybus/uart-gb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 43fd0d350723..204779426b64 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -59,7 +59,7 @@ int gb_gpio_probe(struct greybus_device *gdev, struct device *dev = &gdev->dev; int retval; - gb_gpio = kzalloc(dev, sizeof(*gb_gpio), GFP_KERNEL); + gb_gpio = kzalloc(sizeof(*gb_gpio), GFP_KERNEL); if (!gb_gpio) return -ENOMEM; gb_gpio->gdev = gdev; diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 298b9ff7841b..f667b5c0f393 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -390,7 +390,7 @@ int gb_tty_probe(struct greybus_device *gdev, int retval; int minor; - gb_tty = kzalloc(&gdev->dev, sizeof(*gb_tty), GFP_KERNEL); + gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); if (!gb_tty) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From f1eec30ac83cf95d0607a1c736cf42b44846bad8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 17:18:14 -0700 Subject: greybus: first framework for the es1 ap controller --- drivers/staging/greybus/es1-ap-usb.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 05c724881b07..991a53846e79 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -18,17 +18,40 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); +/* + * Hack, we "know" we will only have one of these at any one time, so only + * create one static structure pointer. + */ +struct es1_ap_dev { + struct usb_interface *usb_intf; + +} *es1_ap_dev; + static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) { + if (es1_ap_dev) { + dev_err(&interface->dev, "Already have a es1_ap_dev???\n"); + return -ENODEV; + } + es1_ap_dev = kzalloc(sizeof(*es1_ap_dev), GFP_KERNEL); + if (!es1_ap_dev) + return -ENOMEM; + es1_ap_dev->usb_intf = interface; + usb_set_intfdata(interface, es1_ap_dev); return 0; } static void ap_disconnect(struct usb_interface *interface) { + es1_ap_dev = usb_get_intfdata(interface); + + /* Tear down everything! */ + kfree(es1_ap_dev); + es1_ap_dev = NULL; } -- cgit v1.2.3-59-g8ed1b From 6f83ab76b8e6ed3d9b8187ffd97bfd68c8a9a045 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 Aug 2014 17:30:04 -0700 Subject: greybus: es1-ap-usb: more init framework added. --- drivers/staging/greybus/es1-ap-usb.c | 45 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 991a53846e79..bebef6dbae7b 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -18,19 +18,31 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); +struct es1_ap_dev { + struct usb_device *usb_dev; + struct usb_interface *usb_intf; + + __u8 ap_in_endpoint; + __u8 ap_out_endpoint; + u8 *ap_buffer; + +}; + /* * Hack, we "know" we will only have one of these at any one time, so only * create one static structure pointer. */ -struct es1_ap_dev { - struct usb_interface *usb_intf; - -} *es1_ap_dev; +static struct es1_ap_dev *es1_ap_dev; static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) { + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + size_t buffer_size; + int i; + if (es1_ap_dev) { dev_err(&interface->dev, "Already have a es1_ap_dev???\n"); return -ENODEV; @@ -39,7 +51,30 @@ static int ap_probe(struct usb_interface *interface, if (!es1_ap_dev) return -ENOMEM; + // FIXME + // figure out endpoint for talking to the AP. + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(endpoint)) { + buffer_size = usb_endpoint_maxp(endpoint); + // FIXME - Save buffer_size? + es1_ap_dev->ap_in_endpoint = endpoint->bEndpointAddress; + } + if (usb_endpoint_is_bulk_out(endpoint)) { + // FIXME - anything else about this we need? + es1_ap_dev->ap_out_endpoint = endpoint->bEndpointAddress; + } + // FIXME - properly exit once found the AP endpoint + // FIXME - set up cport endpoints + } + + // FIXME - allocate buffer + // FIXME = start up talking, then create the gb "devices" based on what the AP tells us. + es1_ap_dev->usb_intf = interface; + es1_ap_dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); usb_set_intfdata(interface, es1_ap_dev); return 0; } @@ -50,6 +85,8 @@ static void ap_disconnect(struct usb_interface *interface) /* Tear down everything! */ + usb_put_dev(es1_ap_dev->usb_dev); + kfree(es1_ap_dev->ap_buffer); kfree(es1_ap_dev); es1_ap_dev = NULL; -- cgit v1.2.3-59-g8ed1b From 27fb83109a3901767cbabafeba617d74f70fcbdc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 31 Aug 2014 13:54:59 -0700 Subject: greybus: register the bus with the driver core and add framework for debugfs files. --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/core.c | 24 ++++++++++++++++++++++-- drivers/staging/greybus/debugfs.c | 34 ++++++++++++++++++++++++++++++++++ drivers/staging/greybus/greybus.h | 4 ++-- 4 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 drivers/staging/greybus/debugfs.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 29ba1da3aac2..3badfefd237d 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,4 +1,4 @@ -greybus-y := core.o gbuf.o i2c-gb.o gpio-gb.o sdio-gb.o uart-gb.o +greybus-y := core.o gbuf.o debugfs.o i2c-gb.o gpio-gb.o sdio-gb.o uart-gb.o obj-m += greybus.o obj-m += es1-ap-usb.o diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index b1a5b887e9ee..143882a523e3 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -86,7 +86,7 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } -struct bus_type greybus_bus_type = { +static struct bus_type greybus_bus_type = { .name = "greybus", .match = greybus_device_match, .uevent = greybus_uevent, @@ -199,16 +199,36 @@ static int __init gb_init(void) { int retval; - retval = gb_tty_init(); + retval = greybus_debugfs_init(); if (retval) return retval; + retval = bus_register(&greybus_bus_type); + if (retval) + goto error_bus; + + // FIXME - more gb core init goes here + + retval = gb_tty_init(); + if (retval) + goto error_tty; + return 0; + +error_tty: + bus_unregister(&greybus_bus_type); + +error_bus: + greybus_debugfs_cleanup(); + + return retval; } static void __exit gb_exit(void) { gb_tty_exit(); + bus_unregister(&greybus_bus_type); + greybus_debugfs_cleanup(); } module_init(gb_init); diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c new file mode 100644 index 000000000000..097b32d2bb56 --- /dev/null +++ b/drivers/staging/greybus/debugfs.c @@ -0,0 +1,34 @@ +/* + * Greybus debugfs code + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "greybus.h" + +static struct dentry *gb_debug_root; + +int greybus_debugfs_init(void) +{ + gb_debug_root = debugfs_create_dir("greybus", NULL); + if (!gb_debug_root) + return -ENOENT; + + return 0; +} + +void greybus_debugfs_cleanup(void) +{ + debugfs_remove_recursive(gb_debug_root); +} diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2440342aaca9..7ce8c6ef0287 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -174,10 +174,10 @@ void greybus_deregister(struct greybus_driver *driver); #define module_greybus_driver(__greybus_driver) \ module_driver(__greybus_driver, greybus_register, greybus_deregister) -extern struct bus_type greybus_bus_type; - int greybus_disabled(void); +int greybus_debugfs_init(void); +void greybus_debugfs_cleanup(void); #endif /* __KERNEL__ */ #endif /* __LINUX_GREYBUS_H */ -- cgit v1.2.3-59-g8ed1b From de536e3094760880569e23166854c99b7c66135c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 31 Aug 2014 16:17:04 -0700 Subject: greybus: ap message loop added. --- drivers/staging/greybus/Makefile | 9 ++- drivers/staging/greybus/ap.c | 125 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/core.c | 14 +++- drivers/staging/greybus/debugfs.c | 4 +- drivers/staging/greybus/es1-ap-usb.c | 64 +++++++++++++++++- drivers/staging/greybus/greybus.h | 12 +++- 6 files changed, 219 insertions(+), 9 deletions(-) create mode 100644 drivers/staging/greybus/ap.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 3badfefd237d..9815e8abac56 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,4 +1,11 @@ -greybus-y := core.o gbuf.o debugfs.o i2c-gb.o gpio-gb.o sdio-gb.o uart-gb.o +greybus-y := core.o \ + gbuf.o \ + debugfs.o \ + ap.o \ + i2c-gb.o \ + gpio-gb.o \ + sdio-gb.o \ + uart-gb.o obj-m += greybus.o obj-m += es1-ap-usb.o diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c new file mode 100644 index 000000000000..5189e8e05ad2 --- /dev/null +++ b/drivers/staging/greybus/ap.c @@ -0,0 +1,125 @@ +/* + * Greybus "AP" message loop handling + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "greybus.h" + +struct ap_msg { + u8 *data; + int size; + struct list_head list; +}; + +static LIST_HEAD(ap_msg_list); +static spinlock_t ap_msg_list_lock; +static struct task_struct *ap_thread; +static wait_queue_head_t ap_wait; + +static struct ap_msg *get_ap_msg(void) +{ + struct ap_msg *ap_msg; + unsigned long flags; + + spin_lock_irqsave(&ap_msg_list_lock, flags); + + ap_msg = list_first_entry_or_null(&ap_msg_list, struct ap_msg, list); + if (ap_msg != NULL) + list_del(&ap_msg->list); + spin_unlock_irqrestore(&ap_msg_list_lock, flags); + + return ap_msg; +} + +static int ap_process_loop(void *data) +{ + struct ap_msg *ap_msg; + + while (!kthread_should_stop()) { + wait_event_interruptible(ap_wait, kthread_should_stop()); + + if (kthread_should_stop()) + break; + + /* Get some data off of the ap list and process it */ + ap_msg = get_ap_msg(); + if (!ap_msg) + continue; + + // FIXME - process the message + + /* clean the message up */ + kfree(ap_msg->data); + kfree(ap_msg); + } + return 0; +} + +int gb_new_ap_msg(u8 *data, int size) +{ + struct ap_msg *ap_msg; + unsigned long flags; + + /* + * Totally naive copy the message into a new structure that we slowly + * create and add it to the list. Let's get this working, the odds of + * this being any "slow path" for AP messages is really low at this + * point in time, but you never know, so this comment is here to point + * out that maybe we should use a slab allocator, or even just not copy + * the data, but use it directly and force the urbs to be "new" each + * time. + */ + + /* Note - this can, and will, be called in interrupt context. */ + ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC); + if (!ap_msg) + return -ENOMEM; + ap_msg->data = kmalloc(size, GFP_ATOMIC); + if (!ap_msg->data) { + kfree(ap_msg); + return -ENOMEM; + } + memcpy(ap_msg->data, data, size); + ap_msg->size = size; + + spin_lock_irqsave(&ap_msg_list_lock, flags); + list_add(&ap_msg->list, &ap_msg_list); + spin_unlock_irqrestore(&ap_msg_list_lock, flags); + + /* kick our thread to handle the message */ + wake_up_interruptible(&ap_wait); + + return 0; +} + +int gb_thread_init(void) +{ + init_waitqueue_head(&ap_wait); + spin_lock_init(&ap_msg_list_lock); + + ap_thread = kthread_run(ap_process_loop, NULL, "greybus_ap"); + if (IS_ERR(ap_thread)) + return PTR_ERR(ap_thread); + + return 0; +} + +void gb_thread_destroy(void) +{ + kthread_stop(ap_thread); +} + + diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 143882a523e3..887aa601fd22 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "greybus.h" @@ -199,7 +200,7 @@ static int __init gb_init(void) { int retval; - retval = greybus_debugfs_init(); + retval = gb_debugfs_init(); if (retval) return retval; @@ -207,6 +208,10 @@ static int __init gb_init(void) if (retval) goto error_bus; + retval = gb_thread_init(); + if (retval) + goto error_thread; + // FIXME - more gb core init goes here retval = gb_tty_init(); @@ -216,10 +221,13 @@ static int __init gb_init(void) return 0; error_tty: + gb_thread_destroy(); + +error_thread: bus_unregister(&greybus_bus_type); error_bus: - greybus_debugfs_cleanup(); + gb_debugfs_cleanup(); return retval; } @@ -228,7 +236,7 @@ static void __exit gb_exit(void) { gb_tty_exit(); bus_unregister(&greybus_bus_type); - greybus_debugfs_cleanup(); + gb_debugfs_cleanup(); } module_init(gb_init); diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c index 097b32d2bb56..4e313f1a5143 100644 --- a/drivers/staging/greybus/debugfs.c +++ b/drivers/staging/greybus/debugfs.c @@ -19,7 +19,7 @@ static struct dentry *gb_debug_root; -int greybus_debugfs_init(void) +int gb_debugfs_init(void) { gb_debug_root = debugfs_create_dir("greybus", NULL); if (!gb_debug_root) @@ -28,7 +28,7 @@ int greybus_debugfs_init(void) return 0; } -void greybus_debugfs_cleanup(void) +void gb_debugfs_cleanup(void) { debugfs_remove_recursive(gb_debug_root); } diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index bebef6dbae7b..abf9dfe66450 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -10,7 +10,7 @@ #include #include #include - +#include "greybus.h" static const struct usb_device_id id_table[] = { { USB_DEVICE(0x0000, 0x0000) }, // FIXME @@ -34,6 +34,68 @@ struct es1_ap_dev { */ static struct es1_ap_dev *es1_ap_dev; +static void ap_in_callback(struct urb *urb) +{ + struct device *dev = &urb->dev->dev; + int status = urb->status; + int retval; + + switch (status) { + case 0: + break; + case -EOVERFLOW: + dev_err(dev, "%s: overflow actual length is %d\n", + __func__, urb->actual_length); + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EILSEQ: + /* device is gone, stop sending */ + return; + default: + dev_err(dev, "%s: unknown status %d\n", __func__, status); + goto exit; + } + + /* We have a message, create a new message structure, add it to the + * list, and wake up our thread that will process the messages. + */ + gb_new_ap_msg(urb->transfer_buffer, urb->actual_length); + +exit: + /* resubmit the urb to get more messages */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "Can not submit urb for AP data: %d\n", retval); +} + +static void ap_out_callback(struct urb *urb) +{ + struct device *dev = &urb->dev->dev; + int status = urb->status; + + switch (status) { + case 0: + break; + case -EOVERFLOW: + dev_err(dev, "%s: overflow actual length is %d\n", + __func__, urb->actual_length); + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EILSEQ: + /* device is gone, stop sending */ + return; + default: + dev_err(dev, "%s: unknown status %d\n", __func__, status); + goto exit; + } + + // FIXME - queue up next AP message to send??? +exit: + return; +} + static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 7ce8c6ef0287..9b0b41bb2a65 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -176,8 +176,16 @@ void greybus_deregister(struct greybus_driver *driver); int greybus_disabled(void); -int greybus_debugfs_init(void); -void greybus_debugfs_cleanup(void); + +/* Internal functions to gb module, move to internal .h file eventually. */ + +int gb_new_ap_msg(u8 *data, int length); +int gb_thread_init(void); +void gb_thread_destroy(void); +int gb_debugfs_init(void); +void gb_debugfs_cleanup(void); + + #endif /* __KERNEL__ */ #endif /* __LINUX_GREYBUS_H */ -- cgit v1.2.3-59-g8ed1b From be1e2e9cd12de6611939d37b468c2ef1939225b2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 31 Aug 2014 16:21:33 -0700 Subject: greybus: structures added --- drivers/staging/greybus/ap.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 5189e8e05ad2..39b83c5fb117 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -18,6 +18,21 @@ #include #include "greybus.h" +/* + * AP <-> SVC message structure format: + * + * + * + */ +struct svc_msg { + u8 function; + u8 type; + u8 version_major; + u8 version_minor; + u16 payload_length; +}; + + struct ap_msg { u8 *data; int size; -- cgit v1.2.3-59-g8ed1b From 2ecd536de70997a1975fa5547185f0c4e70db203 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 31 Aug 2014 17:25:22 -0700 Subject: greybus: more structure definitions added --- drivers/staging/greybus/ap.c | 125 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 39b83c5fb117..3741428558d4 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -24,14 +24,135 @@ * * */ -struct svc_msg { +enum svc_function_type { + SVC_FUNCTION_HANDSHAKE = 0x00, + SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, + SVC_FUNCTION_HOTPLUG = 0x02, + SVC_FUNCTION_DDB = 0x03, + SVC_FUNCTION_POWER = 0x04, + SVC_FUNCTION_EPM = 0x05, + SVC_FUNCTION_SUSPEND = 0x06, +}; + +struct svc_msg_header { u8 function; - u8 type; + u8 type; /* enum svc_function_type */ u8 version_major; u8 version_minor; u16 payload_length; }; +enum svc_function_handshake_type { + SVC_HANDSHAKE_SVC_HELLO = 0x00, + SVC_HANDSHAKE_AP_HELLO = 0x01, + SVC_HANDSHAKE_MODULE_HELLO = 0x02, +}; + +struct svc_function_handshake { + u8 handshake_type; /* enum svc_function_handshake_type */ +}; + +struct svc_function_unipro_set_route { + u8 source_device_id; + u8 source_cport_id; + u8 destination_device_id; + u8 destination_cport_id; +}; + +struct svc_function_unipro_link_up { + u8 device_id; +}; + +enum svc_function_management_event { + SVC_MANAGEMENT_SET_ROUTE = 0x00, + SVC_MANAGEMENT_LINK_UP = 0x01, +}; + +struct svc_function_unipro_management { + u8 management_packet_type; /* enum svc_function_management_event */ + union { + struct svc_function_unipro_set_route set_route; + struct svc_function_unipro_link_up link_up; + }; +}; + +enum svc_function_hotplug_event { + SVC_HOTPLUG_EVENT = 0x00, + SVC_HOTUNPLUG_EVENT = 0x01, +}; + +struct svc_function_hotplug { + u8 hotplug_event; /* enum svc_function_hotplug_event */ + u8 device_id; +}; + +enum svc_function_ddb_type { + SVC_DDB_GET = 0x00, + SVC_DDB_RESPONSE = 0x01, +}; + +struct svc_function_ddb_get { + u8 device_id; + u8 message_id; +}; + +struct svc_function_ddb_response { + u8 device_id; + u8 message_id; + u16 descriptor_length; + u8 ddb[0]; +}; + +struct svc_function_ddb { + u8 ddb_type; /* enum svc_function_ddb_type */ + union { + struct svc_function_ddb_get ddb_get; + struct svc_function_ddb_response ddb_response; + }; +}; + +enum svc_function_power_type { + SVC_POWER_BATTERY_STATUS = 0x00, + SVC_POWER_BATTERY_STATUS_REQUEST = 0x01, +}; + +enum svc_function_battery_status { + SVC_BATTERY_UNKNOWN = 0x00, + SVC_BATTERY_CHARGING = 0x01, + SVC_BATTERY_DISCHARGING = 0x02, + SVC_BATTERY_NOT_CHARGING = 0x03, + SVC_BATTERY_FULL = 0x04, +}; + +struct svc_function_power_battery_status { + u16 charge_full; + u16 charge_now; + u8 status; /* enum svc_function_battery_status */ +}; + +struct svc_function_power_battery_status_request { + +}; + +struct svc_function_power { + u8 power_type; /* enum svc_function_power_type */ + union { + struct svc_function_power_battery_status status; + struct svc_function_power_battery_status_request request; + }; +}; + +struct svc_msg { + struct svc_msg_header header; + union { + struct svc_function_handshake handshake; + struct svc_function_unipro_management management; + struct svc_function_hotplug hotplug; + struct svc_function_ddb ddb; + u8 data[0]; + }; +}; + struct ap_msg { u8 *data; -- cgit v1.2.3-59-g8ed1b From b9b2a46265a16702dd77a55f7b7434d27823e702 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 31 Aug 2014 17:43:38 -0700 Subject: greybus: split svc msg out into separate header file --- drivers/staging/greybus/ap.c | 136 +------------------------------- drivers/staging/greybus/svc_msg.h | 162 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 135 deletions(-) create mode 100644 drivers/staging/greybus/svc_msg.h diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 3741428558d4..2d63b5c6f97c 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -16,143 +16,9 @@ #include #include #include +#include "svc_msg.h" #include "greybus.h" -/* - * AP <-> SVC message structure format: - * - * - * - */ -enum svc_function_type { - SVC_FUNCTION_HANDSHAKE = 0x00, - SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, - SVC_FUNCTION_HOTPLUG = 0x02, - SVC_FUNCTION_DDB = 0x03, - SVC_FUNCTION_POWER = 0x04, - SVC_FUNCTION_EPM = 0x05, - SVC_FUNCTION_SUSPEND = 0x06, -}; - -struct svc_msg_header { - u8 function; - u8 type; /* enum svc_function_type */ - u8 version_major; - u8 version_minor; - u16 payload_length; -}; - -enum svc_function_handshake_type { - SVC_HANDSHAKE_SVC_HELLO = 0x00, - SVC_HANDSHAKE_AP_HELLO = 0x01, - SVC_HANDSHAKE_MODULE_HELLO = 0x02, -}; - -struct svc_function_handshake { - u8 handshake_type; /* enum svc_function_handshake_type */ -}; - -struct svc_function_unipro_set_route { - u8 source_device_id; - u8 source_cport_id; - u8 destination_device_id; - u8 destination_cport_id; -}; - -struct svc_function_unipro_link_up { - u8 device_id; -}; - -enum svc_function_management_event { - SVC_MANAGEMENT_SET_ROUTE = 0x00, - SVC_MANAGEMENT_LINK_UP = 0x01, -}; - -struct svc_function_unipro_management { - u8 management_packet_type; /* enum svc_function_management_event */ - union { - struct svc_function_unipro_set_route set_route; - struct svc_function_unipro_link_up link_up; - }; -}; - -enum svc_function_hotplug_event { - SVC_HOTPLUG_EVENT = 0x00, - SVC_HOTUNPLUG_EVENT = 0x01, -}; - -struct svc_function_hotplug { - u8 hotplug_event; /* enum svc_function_hotplug_event */ - u8 device_id; -}; - -enum svc_function_ddb_type { - SVC_DDB_GET = 0x00, - SVC_DDB_RESPONSE = 0x01, -}; - -struct svc_function_ddb_get { - u8 device_id; - u8 message_id; -}; - -struct svc_function_ddb_response { - u8 device_id; - u8 message_id; - u16 descriptor_length; - u8 ddb[0]; -}; - -struct svc_function_ddb { - u8 ddb_type; /* enum svc_function_ddb_type */ - union { - struct svc_function_ddb_get ddb_get; - struct svc_function_ddb_response ddb_response; - }; -}; - -enum svc_function_power_type { - SVC_POWER_BATTERY_STATUS = 0x00, - SVC_POWER_BATTERY_STATUS_REQUEST = 0x01, -}; - -enum svc_function_battery_status { - SVC_BATTERY_UNKNOWN = 0x00, - SVC_BATTERY_CHARGING = 0x01, - SVC_BATTERY_DISCHARGING = 0x02, - SVC_BATTERY_NOT_CHARGING = 0x03, - SVC_BATTERY_FULL = 0x04, -}; - -struct svc_function_power_battery_status { - u16 charge_full; - u16 charge_now; - u8 status; /* enum svc_function_battery_status */ -}; - -struct svc_function_power_battery_status_request { - -}; - -struct svc_function_power { - u8 power_type; /* enum svc_function_power_type */ - union { - struct svc_function_power_battery_status status; - struct svc_function_power_battery_status_request request; - }; -}; - -struct svc_msg { - struct svc_msg_header header; - union { - struct svc_function_handshake handshake; - struct svc_function_unipro_management management; - struct svc_function_hotplug hotplug; - struct svc_function_ddb ddb; - u8 data[0]; - }; -}; - struct ap_msg { u8 *data; diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h new file mode 100644 index 000000000000..3f97a5e39c27 --- /dev/null +++ b/drivers/staging/greybus/svc_msg.h @@ -0,0 +1,162 @@ +/* + * Greybus AP <-> SVC message structure format. + * + * Defined in the "Greybus Application Protocol" document. + * See that document for any details on these values and structures. + * + * Copyright 2014 Google Inc. + */ +enum svc_function_type { + SVC_FUNCTION_HANDSHAKE = 0x00, + SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, + SVC_FUNCTION_HOTPLUG = 0x02, + SVC_FUNCTION_DDB = 0x03, + SVC_FUNCTION_POWER = 0x04, + SVC_FUNCTION_EPM = 0x05, + SVC_FUNCTION_SUSPEND = 0x06, +}; + +struct svc_msg_header { + u8 function; + u8 type; /* enum svc_function_type */ + u8 version_major; + u8 version_minor; + u16 payload_length; +}; + +enum svc_function_handshake_type { + SVC_HANDSHAKE_SVC_HELLO = 0x00, + SVC_HANDSHAKE_AP_HELLO = 0x01, + SVC_HANDSHAKE_MODULE_HELLO = 0x02, +}; + +struct svc_function_handshake { + u8 handshake_type; /* enum svc_function_handshake_type */ +}; + +struct svc_function_unipro_set_route { + u8 source_device_id; + u8 source_cport_id; + u8 destination_device_id; + u8 destination_cport_id; +}; + +struct svc_function_unipro_link_up { + u8 device_id; +}; + +enum svc_function_management_event { + SVC_MANAGEMENT_SET_ROUTE = 0x00, + SVC_MANAGEMENT_LINK_UP = 0x01, +}; + +struct svc_function_unipro_management { + u8 management_packet_type; /* enum svc_function_management_event */ + union { + struct svc_function_unipro_set_route set_route; + struct svc_function_unipro_link_up link_up; + }; +}; + +enum svc_function_hotplug_event { + SVC_HOTPLUG_EVENT = 0x00, + SVC_HOTUNPLUG_EVENT = 0x01, +}; + +struct svc_function_hotplug { + u8 hotplug_event; /* enum svc_function_hotplug_event */ + u8 device_id; +}; + +enum svc_function_ddb_type { + SVC_DDB_GET = 0x00, + SVC_DDB_RESPONSE = 0x01, +}; + +struct svc_function_ddb_get { + u8 device_id; + u8 message_id; +}; + +struct svc_function_ddb_response { + u8 device_id; + u8 message_id; + u16 descriptor_length; + u8 ddb[0]; +}; + +struct svc_function_ddb { + u8 ddb_type; /* enum svc_function_ddb_type */ + union { + struct svc_function_ddb_get ddb_get; + struct svc_function_ddb_response ddb_response; + }; +}; + +enum svc_function_power_type { + SVC_POWER_BATTERY_STATUS = 0x00, + SVC_POWER_BATTERY_STATUS_REQUEST = 0x01, +}; + +enum svc_function_battery_status { + SVC_BATTERY_UNKNOWN = 0x00, + SVC_BATTERY_CHARGING = 0x01, + SVC_BATTERY_DISCHARGING = 0x02, + SVC_BATTERY_NOT_CHARGING = 0x03, + SVC_BATTERY_FULL = 0x04, +}; + +struct svc_function_power_battery_status { + u16 charge_full; + u16 charge_now; + u8 status; /* enum svc_function_battery_status */ +}; + +struct svc_function_power_battery_status_request { + u8 epm_command_type; /* enum svc_function_epm_command_type */ + u8 device_id; +}; + +struct svc_function_power { + u8 power_type; /* enum svc_function_power_type */ + union { + struct svc_function_power_battery_status status; + struct svc_function_power_battery_status_request request; + }; +}; + +enum svc_function_epm_command_type { + SVC_EPM_ENABLE = 0x00, + SVC_EPM_DISABLE = 0x01, +}; + +struct svc_function_epm { + u8 epm_command_type; /* enum svc_function_epm_command_type */ + u8 device_id; +}; + +enum svc_function_suspend_command_type { + SVC_SUSPEND_FIXME_1 = 0x00, // FIXME + SVC_SUSPEND_FIXME_2 = 0x01, +}; + +struct svc_function_suspend { + u8 suspend_command_type; /* enum function_suspend_command_type */ + u8 device_id; +}; + +struct svc_msg { + struct svc_msg_header header; + union { + struct svc_function_handshake handshake; + struct svc_function_unipro_management management; + struct svc_function_hotplug hotplug; + struct svc_function_ddb ddb; + struct svc_function_power power; + struct svc_function_epm epm; + struct svc_function_suspend suspend; + }; +}; + + + -- cgit v1.2.3-59-g8ed1b From 80ebe8a63134f4418765d6ae9ddcba7427094da8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 31 Aug 2014 18:08:52 -0700 Subject: greybus: greybus_desc.h created --- drivers/staging/greybus/ap.c | 2 +- drivers/staging/greybus/greybus_desc.h | 95 ++++++++++++++++++++++++++++++++++ drivers/staging/greybus/svc_msg.h | 15 +++--- 3 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 drivers/staging/greybus/greybus_desc.h diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 2d63b5c6f97c..e2b60632f1db 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -17,9 +17,9 @@ #include #include #include "svc_msg.h" +#include "greybus_desc.h" #include "greybus.h" - struct ap_msg { u8 *data; int size; diff --git a/drivers/staging/greybus/greybus_desc.h b/drivers/staging/greybus/greybus_desc.h new file mode 100644 index 000000000000..6a3674dcc5d7 --- /dev/null +++ b/drivers/staging/greybus/greybus_desc.h @@ -0,0 +1,95 @@ +/* + * Greybus device descriptor definition + * + * Defined in the "Greybus Application Protocol" document. + * See that document for any details on these values and structures. + * + * Copyright 2014 Google Inc. + */ + +#ifndef __GREYBUS_DESC_H +#define __GREYBUS_DESC_H + +struct greybus_decriptor_block_header { + __le16 size; + u8 version_major; + u8 version_minor; +}; + +enum greybus_descriptor_type { + GREYBUS_TYPE_INVALID = 0x0000, + GREYBUS_TYPE_DEVICE_ID = 0x0001, + GREYBUS_TYPE_SERIAL_NUMBER = 0x0002, + GREYBUS_TYPE_DEVICE_STRING = 0x0003, + GREYBUS_TYPE_CPORT = 0x0004, + GREYBUS_TYPE_FUNCTION = 0x0005, +}; + +struct greybus_descriptor_header { + __le16 size; + __le16 type; /* enum greybus_descriptor_type */ +}; + + +struct greybus_descriptor_deviceid { + __le16 vendor; + __le16 product; + __le16 version; + u8 vendor_stringid; + u8 product_stringid; +}; + +struct greybus_descriptor_serial_number { + __le64 serial_number; +}; + +struct greybus_descriptor_string { + u8 id; + __le16 length; + u8 string[0]; +}; + +struct greybus_descriptor_cport { + __le16 number; + u8 speed; // FIXME + u8 reserved; +}; + +enum greybus_function_class { + GREYBUS_FUNCTION_CONTROL = 0x00, + GREYBUS_FUNCTION_USB = 0x01, + GREYBUS_FUNCTION_GPIO = 0x02, + GREYBUS_FUNCTION_SPI = 0x03, + GREYBUS_FUNCTION_UART = 0x04, + GREYBUS_FUNCTION_PWM = 0x05, + GREYBUS_FUNCTION_I2S = 0x06, + GREYBUS_FUNCTION_I2C = 0x07, + GREYBUS_FUNCTION_SDIO = 0x08, + GREYBUS_FUNCTION_HID = 0x09, + GREYBUS_FUNCTION_DISPLAY = 0x0a, + GREYBUS_FUNCTION_CAMERA = 0x0b, + GREYBUS_FUNCTION_SENSOR = 0x0c, + GREYBUS_FUNCTION_VENDOR = 0xff, +}; + +struct greybus_descriptor_function { + __le16 number; + __le16 cport; + u8 function_class; /* enum greybus_function_class */ + u8 function_subclass; + u8 function_protocol; + u8 reserved; +}; + +struct greybus_msg_descriptor { + struct greybus_descriptor_header header; + union { + struct greybus_descriptor_deviceid device_id; + struct greybus_descriptor_serial_number serial_number; + struct greybus_descriptor_string string; + struct greybus_descriptor_cport cport; + struct greybus_descriptor_function function; + }; +}; + +#endif /* __GREYBUS_DESC_H */ diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 3f97a5e39c27..f97b22c0ab6c 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -6,6 +6,10 @@ * * Copyright 2014 Google Inc. */ + +#ifndef __SVC_MSG_H +#define __SVC_MSG_H + enum svc_function_type { SVC_FUNCTION_HANDSHAKE = 0x00, SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, @@ -21,7 +25,7 @@ struct svc_msg_header { u8 type; /* enum svc_function_type */ u8 version_major; u8 version_minor; - u16 payload_length; + __le16 payload_length; }; enum svc_function_handshake_type { @@ -81,7 +85,7 @@ struct svc_function_ddb_get { struct svc_function_ddb_response { u8 device_id; u8 message_id; - u16 descriptor_length; + __le16 descriptor_length; u8 ddb[0]; }; @@ -107,8 +111,8 @@ enum svc_function_battery_status { }; struct svc_function_power_battery_status { - u16 charge_full; - u16 charge_now; + __le16 charge_full; + __le16 charge_now; u8 status; /* enum svc_function_battery_status */ }; @@ -158,5 +162,4 @@ struct svc_msg { }; }; - - +#endif /* __SVC_MSG_H */ -- cgit v1.2.3-59-g8ed1b From 3772f1610f4f61023019c5639e4e74fd03806f68 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 09:51:33 -0700 Subject: greybus: header file s/u8/__u8/g --- drivers/staging/greybus/greybus_desc.h | 24 +++++++-------- drivers/staging/greybus/svc_msg.h | 54 +++++++++++++++++----------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/drivers/staging/greybus/greybus_desc.h b/drivers/staging/greybus/greybus_desc.h index 6a3674dcc5d7..30995e57772d 100644 --- a/drivers/staging/greybus/greybus_desc.h +++ b/drivers/staging/greybus/greybus_desc.h @@ -12,8 +12,8 @@ struct greybus_decriptor_block_header { __le16 size; - u8 version_major; - u8 version_minor; + __u8 version_major; + __u8 version_minor; }; enum greybus_descriptor_type { @@ -35,8 +35,8 @@ struct greybus_descriptor_deviceid { __le16 vendor; __le16 product; __le16 version; - u8 vendor_stringid; - u8 product_stringid; + __u8 vendor_stringid; + __u8 product_stringid; }; struct greybus_descriptor_serial_number { @@ -44,15 +44,15 @@ struct greybus_descriptor_serial_number { }; struct greybus_descriptor_string { - u8 id; + __u8 id; __le16 length; - u8 string[0]; + __u8 string[0]; }; struct greybus_descriptor_cport { __le16 number; - u8 speed; // FIXME - u8 reserved; + __u8 speed; // FIXME + __u8 reserved; }; enum greybus_function_class { @@ -75,10 +75,10 @@ enum greybus_function_class { struct greybus_descriptor_function { __le16 number; __le16 cport; - u8 function_class; /* enum greybus_function_class */ - u8 function_subclass; - u8 function_protocol; - u8 reserved; + __u8 function_class; /* enum greybus_function_class */ + __u8 function_subclass; + __u8 function_protocol; + __u8 reserved; }; struct greybus_msg_descriptor { diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index f97b22c0ab6c..3b4fd0d31c14 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -21,10 +21,10 @@ enum svc_function_type { }; struct svc_msg_header { - u8 function; - u8 type; /* enum svc_function_type */ - u8 version_major; - u8 version_minor; + __u8 function; + __u8 type; /* enum svc_function_type */ + __u8 version_major; + __u8 version_minor; __le16 payload_length; }; @@ -35,18 +35,18 @@ enum svc_function_handshake_type { }; struct svc_function_handshake { - u8 handshake_type; /* enum svc_function_handshake_type */ + __u8 handshake_type; /* enum svc_function_handshake_type */ }; struct svc_function_unipro_set_route { - u8 source_device_id; - u8 source_cport_id; - u8 destination_device_id; - u8 destination_cport_id; + __u8 source_device_id; + __u8 source_cport_id; + __u8 destination_device_id; + __u8 destination_cport_id; }; struct svc_function_unipro_link_up { - u8 device_id; + __u8 device_id; }; enum svc_function_management_event { @@ -55,7 +55,7 @@ enum svc_function_management_event { }; struct svc_function_unipro_management { - u8 management_packet_type; /* enum svc_function_management_event */ + __u8 management_packet_type; /* enum svc_function_management_event */ union { struct svc_function_unipro_set_route set_route; struct svc_function_unipro_link_up link_up; @@ -68,8 +68,8 @@ enum svc_function_hotplug_event { }; struct svc_function_hotplug { - u8 hotplug_event; /* enum svc_function_hotplug_event */ - u8 device_id; + __u8 hotplug_event; /* enum svc_function_hotplug_event */ + __u8 device_id; }; enum svc_function_ddb_type { @@ -78,19 +78,19 @@ enum svc_function_ddb_type { }; struct svc_function_ddb_get { - u8 device_id; - u8 message_id; + __u8 device_id; + __u8 message_id; }; struct svc_function_ddb_response { - u8 device_id; - u8 message_id; + __u8 device_id; + __u8 message_id; __le16 descriptor_length; - u8 ddb[0]; + __u8 ddb[0]; }; struct svc_function_ddb { - u8 ddb_type; /* enum svc_function_ddb_type */ + __u8 ddb_type; /* enum svc_function_ddb_type */ union { struct svc_function_ddb_get ddb_get; struct svc_function_ddb_response ddb_response; @@ -113,16 +113,16 @@ enum svc_function_battery_status { struct svc_function_power_battery_status { __le16 charge_full; __le16 charge_now; - u8 status; /* enum svc_function_battery_status */ + __u8 status; /* enum svc_function_battery_status */ }; struct svc_function_power_battery_status_request { - u8 epm_command_type; /* enum svc_function_epm_command_type */ - u8 device_id; + __u8 epm_command_type; /* enum svc_function_epm_command_type */ + __u8 device_id; }; struct svc_function_power { - u8 power_type; /* enum svc_function_power_type */ + __u8 power_type; /* enum svc_function_power_type */ union { struct svc_function_power_battery_status status; struct svc_function_power_battery_status_request request; @@ -135,8 +135,8 @@ enum svc_function_epm_command_type { }; struct svc_function_epm { - u8 epm_command_type; /* enum svc_function_epm_command_type */ - u8 device_id; + __u8 epm_command_type; /* enum svc_function_epm_command_type */ + __u8 device_id; }; enum svc_function_suspend_command_type { @@ -145,8 +145,8 @@ enum svc_function_suspend_command_type { }; struct svc_function_suspend { - u8 suspend_command_type; /* enum function_suspend_command_type */ - u8 device_id; + __u8 suspend_command_type; /* enum function_suspend_command_type */ + __u8 device_id; }; struct svc_msg { -- cgit v1.2.3-59-g8ed1b From 712d65915a3ad5bffaa8cd97e6dd951e1d10eabd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 09:51:51 -0700 Subject: greybus: greybus.h: tiny movement around --- drivers/staging/greybus/greybus.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 9b0b41bb2a65..f878453b31c9 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -102,23 +102,6 @@ struct greybus_device { }; #define to_greybus_device(d) container_of(d, struct greybus_device, dev) -/* - * Because we are allocating a data structure per "type" in the greybus device, - * we have static functions for this, not "dynamic" drivers like we really - * should in the end. - */ -int gb_i2c_probe(struct greybus_device *gdev, const struct greybus_device_id *id); -void gb_i2c_disconnect(struct greybus_device *gdev); -int gb_gpio_probe(struct greybus_device *gdev, const struct greybus_device_id *id); -void gb_gpio_disconnect(struct greybus_device *gdev); -int gb_sdio_probe(struct greybus_device *gdev, const struct greybus_device_id *id); -void gb_sdio_disconnect(struct greybus_device *gdev); -int gb_tty_probe(struct greybus_device *gdev, const struct greybus_device_id *id); -void gb_tty_disconnect(struct greybus_device *gdev); - -int gb_tty_init(void); -void gb_tty_exit(void); - struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, struct cport *cport, gfp_t mem_flags); @@ -185,6 +168,23 @@ void gb_thread_destroy(void); int gb_debugfs_init(void); void gb_debugfs_cleanup(void); +/* + * Because we are allocating a data structure per "type" in the greybus device, + * we have static functions for this, not "dynamic" drivers like we really + * should in the end. + */ +int gb_i2c_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +void gb_i2c_disconnect(struct greybus_device *gdev); +int gb_gpio_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +void gb_gpio_disconnect(struct greybus_device *gdev); +int gb_sdio_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +void gb_sdio_disconnect(struct greybus_device *gdev); +int gb_tty_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +void gb_tty_disconnect(struct greybus_device *gdev); + +int gb_tty_init(void); +void gb_tty_exit(void); + #endif /* __KERNEL__ */ -- cgit v1.2.3-59-g8ed1b From d58778002b303bd1915b7e38084feb9df1ea3361 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 10:59:08 -0700 Subject: greybus: more changes due to name changes in the greybus document --- drivers/staging/greybus/greybus_desc.h | 64 +++++++++++++++++----------------- drivers/staging/greybus/svc_msg.h | 19 +++++----- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/drivers/staging/greybus/greybus_desc.h b/drivers/staging/greybus/greybus_desc.h index 30995e57772d..ae962d519ee1 100644 --- a/drivers/staging/greybus/greybus_desc.h +++ b/drivers/staging/greybus/greybus_desc.h @@ -18,11 +18,11 @@ struct greybus_decriptor_block_header { enum greybus_descriptor_type { GREYBUS_TYPE_INVALID = 0x0000, - GREYBUS_TYPE_DEVICE_ID = 0x0001, - GREYBUS_TYPE_SERIAL_NUMBER = 0x0002, - GREYBUS_TYPE_DEVICE_STRING = 0x0003, - GREYBUS_TYPE_CPORT = 0x0004, - GREYBUS_TYPE_FUNCTION = 0x0005, + GREYBUS_TYPE_FUNCTION = 0x0001, + GREYBUS_TYPE_MODULE_ID = 0x0002, + GREYBUS_TYPE_SERIAL_NUMBER = 0x0003, + GREYBUS_TYPE_DEVICE_STRING = 0x0004, + GREYBUS_TYPE_CPORT = 0x0005, }; struct greybus_descriptor_header { @@ -30,31 +30,6 @@ struct greybus_descriptor_header { __le16 type; /* enum greybus_descriptor_type */ }; - -struct greybus_descriptor_deviceid { - __le16 vendor; - __le16 product; - __le16 version; - __u8 vendor_stringid; - __u8 product_stringid; -}; - -struct greybus_descriptor_serial_number { - __le64 serial_number; -}; - -struct greybus_descriptor_string { - __u8 id; - __le16 length; - __u8 string[0]; -}; - -struct greybus_descriptor_cport { - __le16 number; - __u8 speed; // FIXME - __u8 reserved; -}; - enum greybus_function_class { GREYBUS_FUNCTION_CONTROL = 0x00, GREYBUS_FUNCTION_USB = 0x01, @@ -81,14 +56,39 @@ struct greybus_descriptor_function { __u8 reserved; }; +struct greybus_descriptor_module_id { + __le16 vendor; + __le16 product; + __le16 version; + __u8 vendor_stringid; + __u8 product_stringid; +}; + +struct greybus_descriptor_serial_number { + __le64 serial_number; +}; + +struct greybus_descriptor_string { + __le16 length; + __u8 id; + __u8 string[0]; +}; + +struct greybus_descriptor_cport { + __le16 number; + __le16 size; + __u8 speed; // FIXME + __u8 reserved; +}; + struct greybus_msg_descriptor { struct greybus_descriptor_header header; union { - struct greybus_descriptor_deviceid device_id; + struct greybus_descriptor_function function; + struct greybus_descriptor_module_id module_id; struct greybus_descriptor_serial_number serial_number; struct greybus_descriptor_string string; struct greybus_descriptor_cport cport; - struct greybus_descriptor_function function; }; }; diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 3b4fd0d31c14..d992f3cebcef 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -39,14 +39,14 @@ struct svc_function_handshake { }; struct svc_function_unipro_set_route { - __u8 source_device_id; + __u8 source_module_id; __u8 source_cport_id; - __u8 destination_device_id; + __u8 destination_module_id; __u8 destination_cport_id; }; struct svc_function_unipro_link_up { - __u8 device_id; + __u8 module_id; }; enum svc_function_management_event { @@ -69,7 +69,7 @@ enum svc_function_hotplug_event { struct svc_function_hotplug { __u8 hotplug_event; /* enum svc_function_hotplug_event */ - __u8 device_id; + __u8 module_id; }; enum svc_function_ddb_type { @@ -78,12 +78,12 @@ enum svc_function_ddb_type { }; struct svc_function_ddb_get { - __u8 device_id; + __u8 module_id; __u8 message_id; }; struct svc_function_ddb_response { - __u8 device_id; + __u8 module_id; __u8 message_id; __le16 descriptor_length; __u8 ddb[0]; @@ -117,12 +117,11 @@ struct svc_function_power_battery_status { }; struct svc_function_power_battery_status_request { - __u8 epm_command_type; /* enum svc_function_epm_command_type */ - __u8 device_id; }; struct svc_function_power { __u8 power_type; /* enum svc_function_power_type */ + __u8 module_id; union { struct svc_function_power_battery_status status; struct svc_function_power_battery_status_request request; @@ -136,7 +135,7 @@ enum svc_function_epm_command_type { struct svc_function_epm { __u8 epm_command_type; /* enum svc_function_epm_command_type */ - __u8 device_id; + __u8 module_id; }; enum svc_function_suspend_command_type { @@ -146,7 +145,7 @@ enum svc_function_suspend_command_type { struct svc_function_suspend { __u8 suspend_command_type; /* enum function_suspend_command_type */ - __u8 device_id; + __u8 module_id; }; struct svc_msg { -- cgit v1.2.3-59-g8ed1b From 6584c8af7045d12249ebbe34b1e370d972abb0ff Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 13:31:31 -0700 Subject: greybus: s/greybus_device_id/greybus_module_id/g --- drivers/staging/greybus/core.c | 12 ++++++------ drivers/staging/greybus/gpio-gb.c | 4 ++-- drivers/staging/greybus/greybus.h | 12 ++++++------ drivers/staging/greybus/greybus_id.h | 2 +- drivers/staging/greybus/i2c-gb.c | 4 ++-- drivers/staging/greybus/sdio-gb.c | 4 ++-- drivers/staging/greybus/uart-gb.c | 4 ++-- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 887aa601fd22..e41689f5562c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -31,7 +31,7 @@ int greybus_disabled(void) EXPORT_SYMBOL_GPL(greybus_disabled); static int greybus_match_one_id(struct greybus_device *gdev, - const struct greybus_device_id *id) + const struct greybus_module_id *id) { struct greybus_descriptor *des = &gdev->descriptor; @@ -50,9 +50,9 @@ static int greybus_match_one_id(struct greybus_device *gdev, return 1; } -static const struct greybus_device_id *greybus_match_id( +static const struct greybus_module_id *greybus_match_id( struct greybus_device *gdev, - const struct greybus_device_id *id) + const struct greybus_module_id *id) { if (id == NULL) return NULL; @@ -70,7 +70,7 @@ static int greybus_device_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(dev->driver); struct greybus_device *gdev = to_greybus_device(dev); - const struct greybus_device_id *id; + const struct greybus_module_id *id; id = greybus_match_id(gdev, driver->id_table); if (id) @@ -97,7 +97,7 @@ static int greybus_probe(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); struct greybus_device *gdev = to_greybus_device(dev); - const struct greybus_device_id *id; + const struct greybus_module_id *id; int retval; /* match id */ @@ -152,7 +152,7 @@ EXPORT_SYMBOL_GPL(greybus_deregister); static int new_device(struct greybus_device *gdev, - const struct greybus_device_id *id) + const struct greybus_module_id *id) { int retval; diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 204779426b64..c4cb6a2f34c9 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -20,7 +20,7 @@ struct gb_gpio_device { // FIXME - some lock? }; -static const struct greybus_device_id id_table[] = { +static const struct greybus_module_id id_table[] = { { GREYBUS_DEVICE(0x44, 0x44) }, /* make shit up */ { }, /* terminating NULL entry */ }; @@ -52,7 +52,7 @@ static void gpio_set(struct gpio_chip *gpio, unsigned nr, int val) } int gb_gpio_probe(struct greybus_device *gdev, - const struct greybus_device_id *id) + const struct greybus_module_id *id) { struct gb_gpio_device *gb_gpio; struct gpio_chip *gpio; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f878453b31c9..45720ce74d78 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -115,13 +115,13 @@ struct greybus_driver { const char *name; int (*probe)(struct greybus_device *gdev, - const struct greybus_device_id *id); + const struct greybus_module_id *id); void (*disconnect)(struct greybus_device *gdev); int (*suspend)(struct greybus_device *gdev, pm_message_t message); int (*resume)(struct greybus_device *gdev); - const struct greybus_device_id *id_table; + const struct greybus_module_id *id_table; struct device_driver driver; }; @@ -173,13 +173,13 @@ void gb_debugfs_cleanup(void); * we have static functions for this, not "dynamic" drivers like we really * should in the end. */ -int gb_i2c_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +int gb_i2c_probe(struct greybus_device *gdev, const struct greybus_module_id *id); void gb_i2c_disconnect(struct greybus_device *gdev); -int gb_gpio_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +int gb_gpio_probe(struct greybus_device *gdev, const struct greybus_module_id *id); void gb_gpio_disconnect(struct greybus_device *gdev); -int gb_sdio_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +int gb_sdio_probe(struct greybus_device *gdev, const struct greybus_module_id *id); void gb_sdio_disconnect(struct greybus_device *gdev); -int gb_tty_probe(struct greybus_device *gdev, const struct greybus_device_id *id); +int gb_tty_probe(struct greybus_device *gdev, const struct greybus_module_id *id); void gb_tty_disconnect(struct greybus_device *gdev); int gb_tty_init(void); diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index 4afbfe2d5cb9..b6fe26a44ea8 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -9,7 +9,7 @@ #include -struct greybus_device_id { +struct greybus_module_id { __u16 match_flags; __u16 wVendor; __u16 wProduct; diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index bb107f8ce490..3c1d947208c9 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -17,7 +17,7 @@ struct gb_i2c_device { struct greybus_device *gdev; }; -static const struct greybus_device_id id_table[] = { +static const struct greybus_module_id id_table[] = { { GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */ { }, /* terminating NULL entry */ }; @@ -76,7 +76,7 @@ static const struct i2c_algorithm smbus_algorithm = { }; int gb_i2c_probe(struct greybus_device *gdev, - const struct greybus_device_id *id) + const struct greybus_module_id *id) { struct gb_i2c_device *gb_i2c_dev; struct i2c_adapter *adapter; diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 81eb33d2c887..665767d31f8f 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -18,7 +18,7 @@ struct gb_sdio_host { // FIXME - some lock? }; -static const struct greybus_device_id id_table[] = { +static const struct greybus_module_id id_table[] = { { GREYBUS_DEVICE(0x43, 0x43) }, /* make shit up */ { }, /* terminating NULL entry */ }; @@ -46,7 +46,7 @@ static const struct mmc_host_ops gb_sd_ops = { }; int gb_sdio_probe(struct greybus_device *gdev, - const struct greybus_device_id *id) + const struct greybus_module_id *id) { struct mmc_host *mmc; struct gb_sdio_host *host; diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index f667b5c0f393..d104d9cc1221 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -46,7 +46,7 @@ struct gb_tty { struct mutex mutex; }; -static const struct greybus_device_id id_table[] = { +static const struct greybus_module_id id_table[] = { { GREYBUS_DEVICE(0x45, 0x45) }, /* make shit up */ { }, /* terminating NULL entry */ }; @@ -383,7 +383,7 @@ static const struct tty_operations gb_ops = { int gb_tty_probe(struct greybus_device *gdev, - const struct greybus_device_id *id) + const struct greybus_module_id *id) { struct gb_tty *gb_tty; struct device *tty_dev; -- cgit v1.2.3-59-g8ed1b From 6dca7b97c7e0d4ef71c0dad9a3ab939fb6a27033 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 13:42:43 -0700 Subject: greybus: get field names right for descriptors --- drivers/staging/greybus/core.c | 18 +++++++++++------- drivers/staging/greybus/greybus.h | 21 ++++++++++----------- drivers/staging/greybus/greybus_desc.h | 2 +- drivers/staging/greybus/greybus_id.h | 6 +++--- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index e41689f5562c..c73b3ac61691 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -33,18 +33,22 @@ EXPORT_SYMBOL_GPL(greybus_disabled); static int greybus_match_one_id(struct greybus_device *gdev, const struct greybus_module_id *id) { - struct greybus_descriptor *des = &gdev->descriptor; + struct greybus_descriptor_module_id *module_id; + struct greybus_descriptor_serial_number *serial_num; + + module_id = &gdev->module_id; + serial_num = &gdev->serial_number; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && - (des->wVendor != id->wVendor)) + (id->vendor != le16_to_cpu(module_id->vendor))) return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && - (des->wProduct != id->wProduct)) + (id->product != le16_to_cpu(module_id->product))) return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && - (des->lSerialNumber != id->lSerialNumber)) + (id->serial_number != le64_to_cpu(serial_num->serial_number))) return 0; return 1; @@ -57,7 +61,7 @@ static const struct greybus_module_id *greybus_match_id( if (id == NULL) return NULL; - for (; id->wVendor || id->wProduct || id->lSerialNumber || + for (; id->vendor || id->product || id->serial_number || id->driver_info ; id++) { if (greybus_match_one_id(gdev, id)) return id; @@ -151,7 +155,7 @@ void greybus_deregister(struct greybus_driver *driver) EXPORT_SYMBOL_GPL(greybus_deregister); -static int new_device(struct greybus_device *gdev, +int new_device(struct greybus_device *gdev, const struct greybus_module_id *id) { int retval; @@ -187,7 +191,7 @@ error_i2c: return retval; } -static void remove_device(struct greybus_device *gdev) +void remove_device(struct greybus_device *gdev) { /* tear down all of the "sub device types" for this device */ gb_i2c_disconnect(gdev); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 45720ce74d78..b511c6370aea 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -11,29 +11,26 @@ #ifdef __KERNEL__ +#include #include #include #include #include "greybus_id.h" +#include "greybus_desc.h" #define GREYBUS_DEVICE_ID_MATCH_DEVICE \ (GREYBUS_DEVICE_ID_MATCH_VENDOR | GREYBUS_DEVICE_ID_MATCH_PRODUCT) -#define GREYBUS_DEVICE(vendor, product) \ +#define GREYBUS_DEVICE(v, p) \ .match_flags = GREYBUS_DEVICE_ID_MATCH_DEVICE, \ - .wVendor = (vendor), \ - .wProduct = (product), + .vendor = (v), \ + .product = (p), -#define GREYBUS_DEVICE_SERIAL(serial) \ +#define GREYBUS_DEVICE_SERIAL(s) \ .match_flags = GREYBUS_DEVICE_ID_MATCH_SERIAL, \ - .lSerial = (serial), + .serial_number = (s), -struct greybus_descriptor { - __u16 wVendor; - __u16 wProduct; - __u64 lSerialNumber; -}; struct gbuf; @@ -90,7 +87,9 @@ struct gb_usb_device; struct greybus_device { struct device dev; - struct greybus_descriptor descriptor; + struct greybus_descriptor_function function; + struct greybus_descriptor_module_id module_id; + struct greybus_descriptor_serial_number serial_number; int num_cport; struct cport cport[0]; diff --git a/drivers/staging/greybus/greybus_desc.h b/drivers/staging/greybus/greybus_desc.h index ae962d519ee1..48e2ec92b1d6 100644 --- a/drivers/staging/greybus/greybus_desc.h +++ b/drivers/staging/greybus/greybus_desc.h @@ -81,7 +81,7 @@ struct greybus_descriptor_cport { __u8 reserved; }; -struct greybus_msg_descriptor { +struct greybus_descriptor { struct greybus_descriptor_header header; union { struct greybus_descriptor_function function; diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index b6fe26a44ea8..83c4d057cfb0 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -11,9 +11,9 @@ struct greybus_module_id { __u16 match_flags; - __u16 wVendor; - __u16 wProduct; - __u64 lSerialNumber; + __u16 vendor; + __u16 product; + __u64 serial_number; kernel_ulong_t driver_info __attribute__((aligned(sizeof(kernel_ulong_t)))); -- cgit v1.2.3-59-g8ed1b From ec909874c7e0cfe81b8fd1c2a93c97f8a1086afc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 14:39:14 -0700 Subject: greybus: turn off warnings for es1-ap-usb.c to make it easier to build for now... --- drivers/staging/greybus/es1-ap-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index abf9dfe66450..e7042d569695 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -34,7 +34,7 @@ struct es1_ap_dev { */ static struct es1_ap_dev *es1_ap_dev; -static void ap_in_callback(struct urb *urb) +void ap_in_callback(struct urb *urb) { struct device *dev = &urb->dev->dev; int status = urb->status; @@ -69,7 +69,7 @@ exit: dev_err(dev, "Can not submit urb for AP data: %d\n", retval); } -static void ap_out_callback(struct urb *urb) +void ap_out_callback(struct urb *urb) { struct device *dev = &urb->dev->dev; int status = urb->status; -- cgit v1.2.3-59-g8ed1b From d94a44a54e9b2df3716e919763763e7095526e4b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 14:39:34 -0700 Subject: greybus: export gb_new_ap_msg so that the es1 module can use it --- drivers/staging/greybus/ap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index e2b60632f1db..5b06b1919153 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -106,6 +106,8 @@ int gb_new_ap_msg(u8 *data, int size) return 0; } +EXPORT_SYMBOL_GPL(gb_new_ap_msg); + int gb_thread_init(void) { -- cgit v1.2.3-59-g8ed1b From a239f67c5d3b443597e8dde92bd2d80dc59418a1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 14:39:49 -0700 Subject: greybus: start parsing descriptor structures --- drivers/staging/greybus/core.c | 117 ++++++++++++++++++++++++++++++++- drivers/staging/greybus/greybus.h | 4 +- drivers/staging/greybus/greybus_desc.h | 2 +- 3 files changed, 119 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index c73b3ac61691..f4b562c709d9 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -155,8 +156,8 @@ void greybus_deregister(struct greybus_driver *driver) EXPORT_SYMBOL_GPL(greybus_deregister); -int new_device(struct greybus_device *gdev, - const struct greybus_module_id *id) +static int gb_init_subdevs(struct greybus_device *gdev, + const struct greybus_module_id *id) { int retval; @@ -191,6 +192,118 @@ error_i2c: return retval; } +static const struct greybus_module_id fake_gb_id = + { GREYBUS_DEVICE(0x42, 0x42) }; + +/** + * greybus_new_device: + * + * Pass in a buffer that _should_ be a set of greybus descriptor fields and spit + * out a greybus device structure. + */ +struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) +{ + struct greybus_device *gdev; + struct greybus_descriptor_block_header *block; + struct greybus_descriptor *desc; + int retval; + int overall_size; + int header_size; + int desc_size; + u8 version_major; + u8 version_minor; + + /* we have to have at _least_ the block header */ + if (size <= sizeof(struct greybus_descriptor_block_header)) + return NULL; + + gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); + if (!gdev) + return NULL; + + gdev->module_number = module_number; + + block = (struct greybus_descriptor_block_header *)data; + overall_size = le16_to_cpu(block->size); + if (overall_size != size) { + pr_err("size != block header size, %d != %d\n", size, + overall_size); + goto error; + } + + version_major = block->version_major; + version_minor = block->version_minor; + + // FIXME - check version major/minor here! + + size -= sizeof(struct greybus_descriptor_block_header); + data += sizeof(struct greybus_descriptor_block_header); + while (size > 0) { + desc = (struct greybus_descriptor *)data; + desc_size = le16_to_cpu(desc->header.size); + + switch (desc->header.type) { + case GREYBUS_TYPE_FUNCTION: + header_size = + sizeof(struct greybus_descriptor_function); + if (desc_size != header_size) { + pr_err("invalid function header size %d\n", + desc_size); + goto error; + } + memcpy(&gdev->function, &desc->function, header_size); + size -= header_size; + data += header_size; + break; + + case GREYBUS_TYPE_MODULE_ID: + header_size = + sizeof(struct greybus_descriptor_module_id); + if (desc_size != header_size) { + pr_err("invalid module header size %d\n", + desc_size); + goto error; + } + memcpy(&gdev->module_id, &desc->module_id, header_size); + size -= header_size; + data += header_size; + break; + + case GREYBUS_TYPE_SERIAL_NUMBER: + header_size = + sizeof(struct greybus_descriptor_serial_number); + if (desc_size != header_size) { + pr_err("invalid serial number header size %d\n", + desc_size); + goto error; + } + memcpy(&gdev->serial_number, &desc->serial_number, + header_size); + size -= header_size; + data += header_size; + break; + + case GREYBUS_TYPE_DEVICE_STRING: + case GREYBUS_TYPE_CPORT: + case GREYBUS_TYPE_INVALID: + default: + pr_err("invalid descriptor type %d\n", desc->header.type); + goto error; + } +#if 0 + struct greybus_descriptor_string string; + struct greybus_descriptor_cport cport; +#endif + } + retval = gb_init_subdevs(gdev, &fake_gb_id); + if (retval) + goto error; + return gdev; +error: + kfree(gdev); + return NULL; +} + void remove_device(struct greybus_device *gdev) { /* tear down all of the "sub device types" for this device */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index b511c6370aea..93d7dae61012 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -37,6 +37,7 @@ struct gbuf; struct cport { u16 number; + u16 size; // FIXME, what else? }; @@ -87,11 +88,12 @@ struct gb_usb_device; struct greybus_device { struct device dev; + u16 module_number; struct greybus_descriptor_function function; struct greybus_descriptor_module_id module_id; struct greybus_descriptor_serial_number serial_number; int num_cport; - struct cport cport[0]; + struct cport *cport[10]; // FIXME - no more than 10 cports per device... struct gb_i2c_device *gb_i2c_dev; struct gb_gpio_device *gb_gpio_dev; diff --git a/drivers/staging/greybus/greybus_desc.h b/drivers/staging/greybus/greybus_desc.h index 48e2ec92b1d6..e7355a365c92 100644 --- a/drivers/staging/greybus/greybus_desc.h +++ b/drivers/staging/greybus/greybus_desc.h @@ -10,7 +10,7 @@ #ifndef __GREYBUS_DESC_H #define __GREYBUS_DESC_H -struct greybus_decriptor_block_header { +struct greybus_descriptor_block_header { __le16 size; __u8 version_major; __u8 version_minor; -- cgit v1.2.3-59-g8ed1b From 526c5c8d2393da2b74655f12687c30538c469825 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 16:03:31 -0700 Subject: greybus: start parsing descriptor fields --- drivers/staging/greybus/core.c | 139 +++++++++++++++++++++++++-------- drivers/staging/greybus/gbuf.c | 2 +- drivers/staging/greybus/greybus.h | 23 ++++-- drivers/staging/greybus/greybus_desc.h | 2 +- 4 files changed, 126 insertions(+), 40 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index f4b562c709d9..9e9ffa0420e4 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -195,6 +195,76 @@ error_i2c: static const struct greybus_module_id fake_gb_id = { GREYBUS_DEVICE(0x42, 0x42) }; +static int create_function(struct greybus_device *gdev, + struct greybus_descriptor *desc, int desc_size) +{ + int header_size = sizeof(struct greybus_descriptor_function); + + if (desc_size != header_size) { + pr_err("invalid function header size %d\n", desc_size); + return -EINVAL; + } + memcpy(&gdev->function, &desc->function, header_size); + return 0; +} + +static int create_module_id(struct greybus_device *gdev, + struct greybus_descriptor *desc, int desc_size) +{ + int header_size = sizeof(struct greybus_descriptor_module_id); + + if (desc_size != header_size) { + pr_err("invalid module header size %d\n", desc_size); + return -EINVAL; + } + memcpy(&gdev->module_id, &desc->module_id, header_size); + return 0; +} + +static int create_serial_number(struct greybus_device *gdev, + struct greybus_descriptor *desc, int desc_size) +{ + int header_size = sizeof(struct greybus_descriptor_serial_number); + + if (desc_size != header_size) { + pr_err("invalid serial number header size %d\n", desc_size); + return -EINVAL; + } + memcpy(&gdev->serial_number, &desc->serial_number, header_size); + return 0; +} + +static int create_string(struct greybus_device *gdev, + struct greybus_descriptor *desc, int desc_size) +{ + int string_size; + struct gdev_string *string; + int header_size = sizeof(struct greybus_descriptor_string); + + if ((gdev->num_strings + 1) >= MAX_STRINGS_PER_MODULE) { + pr_err("too many strings for this module!\n"); + return -EINVAL; + } + + if (desc_size < header_size) { + pr_err("invalid string header size %d\n", desc_size); + return -EINVAL; + } + + string_size = le16_to_cpu(desc->string.length); + string = kzalloc(sizeof(*string) + string_size + 1, GFP_KERNEL); + if (!string) + return -ENOMEM; + + string->length = string_size; + string->id = desc->string.id; + memcpy(&string->string, &desc->string.string, string_size); + gdev->string[gdev->num_strings] = string; + gdev->num_strings++; + + return 0; +} + /** * greybus_new_device: * @@ -210,6 +280,7 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) int overall_size; int header_size; int desc_size; + int i; u8 version_major; u8 version_minor; @@ -244,62 +315,64 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) switch (desc->header.type) { case GREYBUS_TYPE_FUNCTION: - header_size = - sizeof(struct greybus_descriptor_function); - if (desc_size != header_size) { - pr_err("invalid function header size %d\n", - desc_size); - goto error; - } - memcpy(&gdev->function, &desc->function, header_size); - size -= header_size; - data += header_size; + retval = create_function(gdev, desc, desc_size); break; case GREYBUS_TYPE_MODULE_ID: - header_size = - sizeof(struct greybus_descriptor_module_id); - if (desc_size != header_size) { - pr_err("invalid module header size %d\n", - desc_size); - goto error; - } - memcpy(&gdev->module_id, &desc->module_id, header_size); - size -= header_size; - data += header_size; + retval = create_module_id(gdev, desc, desc_size); break; case GREYBUS_TYPE_SERIAL_NUMBER: - header_size = - sizeof(struct greybus_descriptor_serial_number); + retval = create_serial_number(gdev, desc, desc_size); + break; + + case GREYBUS_TYPE_STRING: + retval = create_string(gdev, desc, desc_size); + break; + + case GREYBUS_TYPE_CPORT: { + struct gdev_cport *cport; + + header_size = sizeof(struct greybus_descriptor_cport); if (desc_size != header_size) { pr_err("invalid serial number header size %d\n", desc_size); goto error; } - memcpy(&gdev->serial_number, &desc->serial_number, - header_size); - size -= header_size; - data += header_size; + cport = kzalloc(sizeof(*cport), GFP_KERNEL); + if (!cport) + goto error; + cport->number = le16_to_cpu(desc->cport.number); + cport->size = le16_to_cpu(desc->cport.size); + cport->speed = desc->cport.speed; + gdev->cport[gdev->num_cports] = cport; + gdev->num_cports++; + // FIXME - check for too many cports... + + size -= desc_size; + data += desc_size; break; - - case GREYBUS_TYPE_DEVICE_STRING: - case GREYBUS_TYPE_CPORT: + } case GREYBUS_TYPE_INVALID: default: pr_err("invalid descriptor type %d\n", desc->header.type); goto error; } -#if 0 - struct greybus_descriptor_string string; - struct greybus_descriptor_cport cport; -#endif + if (retval) + goto error; + size -= desc_size; + data += desc_size; } + retval = gb_init_subdevs(gdev, &fake_gb_id); if (retval) goto error; return gdev; error: + for (i = 0; i < gdev->num_strings; ++i) + kfree(gdev->string[i]); + for (i = 0; i < gdev->num_cports; ++i) + kfree(gdev->cport[i]); kfree(gdev); return NULL; } diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index de31da8165de..9f37fc6e31cf 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -18,7 +18,7 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, - struct cport *cport, + struct gdev_cport *cport, gfp_t mem_flags) { return NULL; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 93d7dae61012..6d82806e3e00 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -35,10 +35,17 @@ struct gbuf; -struct cport { +struct gdev_cport { u16 number; u16 size; // FIXME, what else? + u8 speed; // valid??? +}; + +struct gdev_string { + u16 length; + u8 id; + u8 string[0]; }; typedef void (*gbuf_complete_t)(struct gbuf *gbuf); @@ -51,7 +58,7 @@ struct gbuf { struct gbuf_anchor *anchor; // FIXME do we need? struct greybus_device *gdev; - struct cport *cport; + struct gdev_cport *cport; int status; void *transfer_buffer; u32 transfer_flags; /* flags for the transfer buffer */ @@ -86,14 +93,20 @@ struct gb_sdio_host; struct gb_tty; struct gb_usb_device; +/* Increase these values if needed */ +#define MAX_CPORTS_PER_MODULE 10 +#define MAX_STRINGS_PER_MODULE 10 + struct greybus_device { struct device dev; u16 module_number; struct greybus_descriptor_function function; struct greybus_descriptor_module_id module_id; struct greybus_descriptor_serial_number serial_number; - int num_cport; - struct cport *cport[10]; // FIXME - no more than 10 cports per device... + int num_cports; + int num_strings; + struct gdev_cport *cport[MAX_CPORTS_PER_MODULE]; + struct gdev_string *string[MAX_STRINGS_PER_MODULE]; struct gb_i2c_device *gb_i2c_dev; struct gb_gpio_device *gb_gpio_dev; @@ -104,7 +117,7 @@ struct greybus_device { #define to_greybus_device(d) container_of(d, struct greybus_device, dev) struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, - struct cport *cport, + struct gdev_cport *cport, gfp_t mem_flags); void greybus_free_gbuf(struct gbuf *gbuf); diff --git a/drivers/staging/greybus/greybus_desc.h b/drivers/staging/greybus/greybus_desc.h index e7355a365c92..37cc99ace3bf 100644 --- a/drivers/staging/greybus/greybus_desc.h +++ b/drivers/staging/greybus/greybus_desc.h @@ -21,7 +21,7 @@ enum greybus_descriptor_type { GREYBUS_TYPE_FUNCTION = 0x0001, GREYBUS_TYPE_MODULE_ID = 0x0002, GREYBUS_TYPE_SERIAL_NUMBER = 0x0003, - GREYBUS_TYPE_DEVICE_STRING = 0x0004, + GREYBUS_TYPE_STRING = 0x0004, GREYBUS_TYPE_CPORT = 0x0005, }; -- cgit v1.2.3-59-g8ed1b From b94295e0502dbe7f0c0d2b572c8b7334b05c279c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 18:34:28 -0700 Subject: greybus: sysfs attributes for functions and more driver core integration. --- drivers/staging/greybus/core.c | 178 +++++++++++++++++++++++++-------- drivers/staging/greybus/greybus.h | 4 + drivers/staging/greybus/greybus_desc.h | 6 +- 3 files changed, 146 insertions(+), 42 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9e9ffa0420e4..c05495bc34b0 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -156,6 +156,78 @@ void greybus_deregister(struct greybus_driver *driver) EXPORT_SYMBOL_GPL(greybus_deregister); +static void greybus_module_release(struct device *dev) +{ + struct greybus_device *gdev = to_greybus_device(dev); + int i; + + for (i = 0; i < gdev->num_strings; ++i) + kfree(gdev->string[i]); + for (i = 0; i < gdev->num_cports; ++i) + kfree(gdev->cport[i]); + kfree(gdev); +} + + + +static struct device_type greybus_module_type = { + .name = "greybus_module", + .release = greybus_module_release, +}; + +/* Function fields */ +#define greybus_function_attr(field) \ +static ssize_t function_##field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct greybus_device *gdev = to_greybus_device(dev); \ + return sprintf(buf, "%d\n", gdev->function.field); \ +} \ +static DEVICE_ATTR_RO(function_##field) + +greybus_function_attr(number); +greybus_function_attr(cport); +greybus_function_attr(class); +greybus_function_attr(subclass); +greybus_function_attr(protocol); + +static struct attribute *function_attrs[] = { + &dev_attr_function_number.attr, + &dev_attr_function_cport.attr, + &dev_attr_function_class.attr, + &dev_attr_function_subclass.attr, + &dev_attr_function_protocol.attr, + NULL, +}; + +static umode_t function_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct greybus_device *gdev = to_greybus_device(kobj_to_dev(kobj)); + + // FIXME - make this a dynamic structure to "know" if it really is here + // or not easier? + if (gdev->function.number || + gdev->function.cport || + gdev->function.class || + gdev->function.subclass || + gdev->function.protocol) + return a->mode; + return 0; +} + +static struct attribute_group function_addr_grp = { + .attrs = function_attrs, + .is_visible = function_attrs_are_visible, +}; + +static const struct attribute_group *greybus_module_groups[] = { + &function_addr_grp, + NULL, +}; + + static int gb_init_subdevs(struct greybus_device *gdev, const struct greybus_module_id *id) { @@ -201,7 +273,8 @@ static int create_function(struct greybus_device *gdev, int header_size = sizeof(struct greybus_descriptor_function); if (desc_size != header_size) { - pr_err("invalid function header size %d\n", desc_size); + dev_err(gdev->dev.parent, "invalid function header size %d\n", + desc_size); return -EINVAL; } memcpy(&gdev->function, &desc->function, header_size); @@ -214,7 +287,8 @@ static int create_module_id(struct greybus_device *gdev, int header_size = sizeof(struct greybus_descriptor_module_id); if (desc_size != header_size) { - pr_err("invalid module header size %d\n", desc_size); + dev_err(gdev->dev.parent, "invalid module header size %d\n", + desc_size); return -EINVAL; } memcpy(&gdev->module_id, &desc->module_id, header_size); @@ -227,7 +301,8 @@ static int create_serial_number(struct greybus_device *gdev, int header_size = sizeof(struct greybus_descriptor_serial_number); if (desc_size != header_size) { - pr_err("invalid serial number header size %d\n", desc_size); + dev_err(gdev->dev.parent, "invalid serial number header size %d\n", + desc_size); return -EINVAL; } memcpy(&gdev->serial_number, &desc->serial_number, header_size); @@ -242,12 +317,14 @@ static int create_string(struct greybus_device *gdev, int header_size = sizeof(struct greybus_descriptor_string); if ((gdev->num_strings + 1) >= MAX_STRINGS_PER_MODULE) { - pr_err("too many strings for this module!\n"); + dev_err(gdev->dev.parent, + "too many strings for this module!\n"); return -EINVAL; } if (desc_size < header_size) { - pr_err("invalid string header size %d\n", desc_size); + dev_err(gdev->dev.parent, "invalid string header size %d\n", + desc_size); return -EINVAL; } @@ -259,28 +336,59 @@ static int create_string(struct greybus_device *gdev, string->length = string_size; string->id = desc->string.id; memcpy(&string->string, &desc->string.string, string_size); + gdev->string[gdev->num_strings] = string; gdev->num_strings++; return 0; } +static int create_cport(struct greybus_device *gdev, + struct greybus_descriptor *desc, int desc_size) +{ + struct gdev_cport *cport; + int header_size = sizeof(struct greybus_descriptor_cport); + + if ((gdev->num_cports + 1) >= MAX_CPORTS_PER_MODULE) { + dev_err(gdev->dev.parent, "too many cports for this module!\n"); + return -EINVAL; + } + + if (desc_size != header_size) { + dev_err(gdev->dev.parent, + "invalid serial number header size %d\n", desc_size); + return -EINVAL; + } + + cport = kzalloc(sizeof(*cport), GFP_KERNEL); + if (!cport) + return -ENOMEM; + + cport->number = le16_to_cpu(desc->cport.number); + cport->size = le16_to_cpu(desc->cport.size); + cport->speed = desc->cport.speed; + + gdev->cport[gdev->num_cports] = cport; + gdev->num_cports++; + + return 0; +} + /** * greybus_new_device: * * Pass in a buffer that _should_ be a set of greybus descriptor fields and spit * out a greybus device structure. */ -struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) +struct greybus_device *greybus_new_device(struct device *parent, + int module_number, u8 *data, int size) { struct greybus_device *gdev; struct greybus_descriptor_block_header *block; struct greybus_descriptor *desc; int retval; int overall_size; - int header_size; int desc_size; - int i; u8 version_major; u8 version_minor; @@ -293,12 +401,20 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) return NULL; gdev->module_number = module_number; + gdev->dev.parent = parent; + gdev->dev.driver = NULL; + gdev->dev.bus = &greybus_bus_type; + gdev->dev.type = &greybus_module_type; + gdev->dev.groups = greybus_module_groups; + gdev->dev.dma_mask = parent->dma_mask; + device_initialize(&gdev->dev); + dev_set_name(&gdev->dev, "%d", module_number); block = (struct greybus_descriptor_block_header *)data; overall_size = le16_to_cpu(block->size); if (overall_size != size) { - pr_err("size != block header size, %d != %d\n", size, - overall_size); + dev_err(parent, "size != block header size, %d != %d\n", size, + overall_size); goto error; } @@ -330,32 +446,14 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) retval = create_string(gdev, desc, desc_size); break; - case GREYBUS_TYPE_CPORT: { - struct gdev_cport *cport; - - header_size = sizeof(struct greybus_descriptor_cport); - if (desc_size != header_size) { - pr_err("invalid serial number header size %d\n", - desc_size); - goto error; - } - cport = kzalloc(sizeof(*cport), GFP_KERNEL); - if (!cport) - goto error; - cport->number = le16_to_cpu(desc->cport.number); - cport->size = le16_to_cpu(desc->cport.size); - cport->speed = desc->cport.speed; - gdev->cport[gdev->num_cports] = cport; - gdev->num_cports++; - // FIXME - check for too many cports... - - size -= desc_size; - data += desc_size; + case GREYBUS_TYPE_CPORT: + retval = create_cport(gdev, desc, desc_size); break; - } + case GREYBUS_TYPE_INVALID: default: - pr_err("invalid descriptor type %d\n", desc->header.type); + dev_err(parent, "invalid descriptor type %d\n", + desc->header.type); goto error; } if (retval) @@ -367,23 +465,25 @@ struct greybus_device *greybus_new_device(int module_number, u8 *data, int size) retval = gb_init_subdevs(gdev, &fake_gb_id); if (retval) goto error; + + // FIXME device_add(&gdev->dev); + + return gdev; error: - for (i = 0; i < gdev->num_strings; ++i) - kfree(gdev->string[i]); - for (i = 0; i < gdev->num_cports; ++i) - kfree(gdev->cport[i]); - kfree(gdev); + greybus_module_release(&gdev->dev); return NULL; } -void remove_device(struct greybus_device *gdev) +void greybus_remove_device(struct greybus_device *gdev) { /* tear down all of the "sub device types" for this device */ gb_i2c_disconnect(gdev); gb_gpio_disconnect(gdev); gb_sdio_disconnect(gdev); gb_tty_disconnect(gdev); + + // FIXME - device_remove(&gdev->dev); } static int __init gb_init(void) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 6d82806e3e00..1a0dc4c7254c 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -173,6 +173,10 @@ void greybus_deregister(struct greybus_driver *driver); int greybus_disabled(void); +struct greybus_device *greybus_new_device(struct device *parent, + int module_number, u8 *data, + int size); +void greybus_remove_device(struct greybus_device *gdev); /* Internal functions to gb module, move to internal .h file eventually. */ diff --git a/drivers/staging/greybus/greybus_desc.h b/drivers/staging/greybus/greybus_desc.h index 37cc99ace3bf..0fd7230f63f9 100644 --- a/drivers/staging/greybus/greybus_desc.h +++ b/drivers/staging/greybus/greybus_desc.h @@ -50,9 +50,9 @@ enum greybus_function_class { struct greybus_descriptor_function { __le16 number; __le16 cport; - __u8 function_class; /* enum greybus_function_class */ - __u8 function_subclass; - __u8 function_protocol; + __u8 class; /* enum greybus_function_class */ + __u8 subclass; + __u8 protocol; __u8 reserved; }; -- cgit v1.2.3-59-g8ed1b From 291f3b9e61eb4d4f3cc43f93b8862fbe74c588ae Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 18:41:39 -0700 Subject: greybus: serial number attribute added --- drivers/staging/greybus/core.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index c05495bc34b0..68fb2a5fe187 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -169,7 +169,6 @@ static void greybus_module_release(struct device *dev) } - static struct device_type greybus_module_type = { .name = "greybus_module", .release = greybus_module_release, @@ -222,8 +221,40 @@ static struct attribute_group function_addr_grp = { .is_visible = function_attrs_are_visible, }; +#if 0 +struct greybus_descriptor_module_id { + __le16 vendor; + __le16 product; + __le16 version; + __u8 vendor_stringid; + __u8 product_stringid; +}; +#endif + +/* Serial Number */ +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct greybus_device *gdev = to_greybus_device(dev); + return sprintf(buf, "%llX\n", + (unsigned long long)gdev->serial_number.serial_number); +} +static DEVICE_ATTR_RO(serial_number); + +static struct attribute *serial_number_attrs[] = { + &dev_attr_serial_number.attr, + NULL, +}; + +static struct attribute_group serial_number_grp = { + .attrs = serial_number_attrs, + .is_visible = function_attrs_are_visible, +}; + + static const struct attribute_group *greybus_module_groups[] = { &function_addr_grp, + &serial_number_grp, NULL, }; -- cgit v1.2.3-59-g8ed1b From 21ee4116fdf251b26a0fa6921f771f714f3bcd65 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 18:57:42 -0700 Subject: greybus: module id attributes --- drivers/staging/greybus/core.c | 86 +++++++++++++++++++++++++++++++++------ drivers/staging/greybus/greybus.h | 2 + 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 68fb2a5fe187..37ece6d03c1e 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -216,20 +216,81 @@ static umode_t function_attrs_are_visible(struct kobject *kobj, return 0; } -static struct attribute_group function_addr_grp = { +static struct attribute_group function_attr_grp = { .attrs = function_attrs, .is_visible = function_attrs_are_visible, }; -#if 0 -struct greybus_descriptor_module_id { - __le16 vendor; - __le16 product; - __le16 version; - __u8 vendor_stringid; - __u8 product_stringid; +/* Module fields */ +#define greybus_module_attr(field) \ +static ssize_t module_##field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct greybus_device *gdev = to_greybus_device(dev); \ + return sprintf(buf, "%x\n", gdev->module_id.field); \ +} \ +static DEVICE_ATTR_RO(module_##field) + +greybus_module_attr(vendor); +greybus_module_attr(product); +greybus_module_attr(version); + +static ssize_t module_vendor_string_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct greybus_device *gdev = to_greybus_device(dev); + return sprintf(buf, "%s", + greybus_string(gdev->module_id.vendor_stringid)); +} +static DEVICE_ATTR_RO(module_vendor_string); + +static ssize_t module_product_string_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct greybus_device *gdev = to_greybus_device(dev); + return sprintf(buf, "%s", + greybus_string(gdev->module_id.product_stringid)); +} +static DEVICE_ATTR_RO(module_product_string); + +static struct attribute *module_attrs[] = { + &dev_attr_module_vendor.attr, + &dev_attr_module_product.attr, + &dev_attr_module_version.attr, + &dev_attr_module_vendor_string.attr, + &dev_attr_module_product_string.attr, + NULL, }; -#endif + +static umode_t module_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct greybus_device *gdev = to_greybus_device(kobj_to_dev(kobj)); + + if ((a == &dev_attr_module_vendor_string.attr) && + (gdev->module_id.vendor_stringid)) + return a->mode; + if ((a == &dev_attr_module_product_string.attr) && + (gdev->module_id.product_stringid)) + return a->mode; + + // FIXME - make this a dynamic structure to "know" if it really is here + // or not easier? + if (gdev->module_id.vendor || + gdev->module_id.product || + gdev->module_id.version) + return a->mode; + return 0; +} + +static struct attribute_group module_attr_grp = { + .attrs = module_attrs, + .is_visible = module_attrs_are_visible, +}; + /* Serial Number */ static ssize_t serial_number_show(struct device *dev, @@ -246,15 +307,16 @@ static struct attribute *serial_number_attrs[] = { NULL, }; -static struct attribute_group serial_number_grp = { +static struct attribute_group serial_number_attr_grp = { .attrs = serial_number_attrs, .is_visible = function_attrs_are_visible, }; static const struct attribute_group *greybus_module_groups[] = { - &function_addr_grp, - &serial_number_grp, + &function_attr_grp, + &module_attr_grp, + &serial_number_attr_grp, NULL, }; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 1a0dc4c7254c..1955fea116d1 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -178,6 +178,8 @@ struct greybus_device *greybus_new_device(struct device *parent, int size); void greybus_remove_device(struct greybus_device *gdev); +const u8 *greybus_string(int id); + /* Internal functions to gb module, move to internal .h file eventually. */ int gb_new_ap_msg(u8 *data, int length); -- cgit v1.2.3-59-g8ed1b From e24e7257b12d409d1ef0a24307b9525093845f1e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 19:01:14 -0700 Subject: greybus: greybus_string() --- drivers/staging/greybus/core.c | 20 ++++++++++++++++++-- drivers/staging/greybus/greybus.h | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 37ece6d03c1e..235a9c410d41 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -169,6 +169,22 @@ static void greybus_module_release(struct device *dev) } +const u8 *greybus_string(struct greybus_device *gdev, int id) +{ + int i; + struct gdev_string *string; + + if (!gdev) + return NULL; + + for (i = 0; i < gdev->num_strings; ++i) { + string = gdev->string[i]; + if (string->id == id) + return &string->string[0]; + } + return NULL; +} + static struct device_type greybus_module_type = { .name = "greybus_module", .release = greybus_module_release, @@ -242,7 +258,7 @@ static ssize_t module_vendor_string_show(struct device *dev, { struct greybus_device *gdev = to_greybus_device(dev); return sprintf(buf, "%s", - greybus_string(gdev->module_id.vendor_stringid)); + greybus_string(gdev, gdev->module_id.vendor_stringid)); } static DEVICE_ATTR_RO(module_vendor_string); @@ -252,7 +268,7 @@ static ssize_t module_product_string_show(struct device *dev, { struct greybus_device *gdev = to_greybus_device(dev); return sprintf(buf, "%s", - greybus_string(gdev->module_id.product_stringid)); + greybus_string(gdev, gdev->module_id.product_stringid)); } static DEVICE_ATTR_RO(module_product_string); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 1955fea116d1..50bbf181106b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -178,7 +178,7 @@ struct greybus_device *greybus_new_device(struct device *parent, int size); void greybus_remove_device(struct greybus_device *gdev); -const u8 *greybus_string(int id); +const u8 *greybus_string(struct greybus_device *gdev, int id); /* Internal functions to gb module, move to internal .h file eventually. */ -- cgit v1.2.3-59-g8ed1b From 06340efb7cc38fb7184cae6d3705383f468cefbe Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 19:05:54 -0700 Subject: greybus: split sysfs functions out to separate file. --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/core.c | 147 --------------------------------- drivers/staging/greybus/greybus.h | 2 + drivers/staging/greybus/sysfs.c | 166 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 147 deletions(-) create mode 100644 drivers/staging/greybus/sysfs.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 9815e8abac56..c1e4e12b8b4a 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,5 +1,6 @@ greybus-y := core.o \ gbuf.o \ + sysfs.o \ debugfs.o \ ap.o \ i2c-gb.o \ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 235a9c410d41..bbe9ef96a4f8 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -190,153 +190,6 @@ static struct device_type greybus_module_type = { .release = greybus_module_release, }; -/* Function fields */ -#define greybus_function_attr(field) \ -static ssize_t function_##field##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - struct greybus_device *gdev = to_greybus_device(dev); \ - return sprintf(buf, "%d\n", gdev->function.field); \ -} \ -static DEVICE_ATTR_RO(function_##field) - -greybus_function_attr(number); -greybus_function_attr(cport); -greybus_function_attr(class); -greybus_function_attr(subclass); -greybus_function_attr(protocol); - -static struct attribute *function_attrs[] = { - &dev_attr_function_number.attr, - &dev_attr_function_cport.attr, - &dev_attr_function_class.attr, - &dev_attr_function_subclass.attr, - &dev_attr_function_protocol.attr, - NULL, -}; - -static umode_t function_attrs_are_visible(struct kobject *kobj, - struct attribute *a, int n) -{ - struct greybus_device *gdev = to_greybus_device(kobj_to_dev(kobj)); - - // FIXME - make this a dynamic structure to "know" if it really is here - // or not easier? - if (gdev->function.number || - gdev->function.cport || - gdev->function.class || - gdev->function.subclass || - gdev->function.protocol) - return a->mode; - return 0; -} - -static struct attribute_group function_attr_grp = { - .attrs = function_attrs, - .is_visible = function_attrs_are_visible, -}; - -/* Module fields */ -#define greybus_module_attr(field) \ -static ssize_t module_##field##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - struct greybus_device *gdev = to_greybus_device(dev); \ - return sprintf(buf, "%x\n", gdev->module_id.field); \ -} \ -static DEVICE_ATTR_RO(module_##field) - -greybus_module_attr(vendor); -greybus_module_attr(product); -greybus_module_attr(version); - -static ssize_t module_vendor_string_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct greybus_device *gdev = to_greybus_device(dev); - return sprintf(buf, "%s", - greybus_string(gdev, gdev->module_id.vendor_stringid)); -} -static DEVICE_ATTR_RO(module_vendor_string); - -static ssize_t module_product_string_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct greybus_device *gdev = to_greybus_device(dev); - return sprintf(buf, "%s", - greybus_string(gdev, gdev->module_id.product_stringid)); -} -static DEVICE_ATTR_RO(module_product_string); - -static struct attribute *module_attrs[] = { - &dev_attr_module_vendor.attr, - &dev_attr_module_product.attr, - &dev_attr_module_version.attr, - &dev_attr_module_vendor_string.attr, - &dev_attr_module_product_string.attr, - NULL, -}; - -static umode_t module_attrs_are_visible(struct kobject *kobj, - struct attribute *a, int n) -{ - struct greybus_device *gdev = to_greybus_device(kobj_to_dev(kobj)); - - if ((a == &dev_attr_module_vendor_string.attr) && - (gdev->module_id.vendor_stringid)) - return a->mode; - if ((a == &dev_attr_module_product_string.attr) && - (gdev->module_id.product_stringid)) - return a->mode; - - // FIXME - make this a dynamic structure to "know" if it really is here - // or not easier? - if (gdev->module_id.vendor || - gdev->module_id.product || - gdev->module_id.version) - return a->mode; - return 0; -} - -static struct attribute_group module_attr_grp = { - .attrs = module_attrs, - .is_visible = module_attrs_are_visible, -}; - - -/* Serial Number */ -static ssize_t serial_number_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct greybus_device *gdev = to_greybus_device(dev); - return sprintf(buf, "%llX\n", - (unsigned long long)gdev->serial_number.serial_number); -} -static DEVICE_ATTR_RO(serial_number); - -static struct attribute *serial_number_attrs[] = { - &dev_attr_serial_number.attr, - NULL, -}; - -static struct attribute_group serial_number_attr_grp = { - .attrs = serial_number_attrs, - .is_visible = function_attrs_are_visible, -}; - - -static const struct attribute_group *greybus_module_groups[] = { - &function_attr_grp, - &module_attr_grp, - &serial_number_attr_grp, - NULL, -}; - - static int gb_init_subdevs(struct greybus_device *gdev, const struct greybus_module_id *id) { diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 50bbf181106b..d9cfc3ba3001 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -188,6 +188,8 @@ void gb_thread_destroy(void); int gb_debugfs_init(void); void gb_debugfs_cleanup(void); +extern const struct attribute_group *greybus_module_groups[]; + /* * Because we are allocating a data structure per "type" in the greybus device, * we have static functions for this, not "dynamic" drivers like we really diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c new file mode 100644 index 000000000000..16e6ca4f9200 --- /dev/null +++ b/drivers/staging/greybus/sysfs.c @@ -0,0 +1,166 @@ +/* + * Greybus sysfs file functions + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "greybus.h" + +/* Function fields */ +#define greybus_function_attr(field) \ +static ssize_t function_##field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct greybus_device *gdev = to_greybus_device(dev); \ + return sprintf(buf, "%d\n", gdev->function.field); \ +} \ +static DEVICE_ATTR_RO(function_##field) + +greybus_function_attr(number); +greybus_function_attr(cport); +greybus_function_attr(class); +greybus_function_attr(subclass); +greybus_function_attr(protocol); + +static struct attribute *function_attrs[] = { + &dev_attr_function_number.attr, + &dev_attr_function_cport.attr, + &dev_attr_function_class.attr, + &dev_attr_function_subclass.attr, + &dev_attr_function_protocol.attr, + NULL, +}; + +static umode_t function_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct greybus_device *gdev = to_greybus_device(kobj_to_dev(kobj)); + + // FIXME - make this a dynamic structure to "know" if it really is here + // or not easier? + if (gdev->function.number || + gdev->function.cport || + gdev->function.class || + gdev->function.subclass || + gdev->function.protocol) + return a->mode; + return 0; +} + +static struct attribute_group function_attr_grp = { + .attrs = function_attrs, + .is_visible = function_attrs_are_visible, +}; + +/* Module fields */ +#define greybus_module_attr(field) \ +static ssize_t module_##field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct greybus_device *gdev = to_greybus_device(dev); \ + return sprintf(buf, "%x\n", gdev->module_id.field); \ +} \ +static DEVICE_ATTR_RO(module_##field) + +greybus_module_attr(vendor); +greybus_module_attr(product); +greybus_module_attr(version); + +static ssize_t module_vendor_string_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct greybus_device *gdev = to_greybus_device(dev); + return sprintf(buf, "%s", + greybus_string(gdev, gdev->module_id.vendor_stringid)); +} +static DEVICE_ATTR_RO(module_vendor_string); + +static ssize_t module_product_string_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct greybus_device *gdev = to_greybus_device(dev); + return sprintf(buf, "%s", + greybus_string(gdev, gdev->module_id.product_stringid)); +} +static DEVICE_ATTR_RO(module_product_string); + +static struct attribute *module_attrs[] = { + &dev_attr_module_vendor.attr, + &dev_attr_module_product.attr, + &dev_attr_module_version.attr, + &dev_attr_module_vendor_string.attr, + &dev_attr_module_product_string.attr, + NULL, +}; + +static umode_t module_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct greybus_device *gdev = to_greybus_device(kobj_to_dev(kobj)); + + if ((a == &dev_attr_module_vendor_string.attr) && + (gdev->module_id.vendor_stringid)) + return a->mode; + if ((a == &dev_attr_module_product_string.attr) && + (gdev->module_id.product_stringid)) + return a->mode; + + // FIXME - make this a dynamic structure to "know" if it really is here + // or not easier? + if (gdev->module_id.vendor || + gdev->module_id.product || + gdev->module_id.version) + return a->mode; + return 0; +} + +static struct attribute_group module_attr_grp = { + .attrs = module_attrs, + .is_visible = module_attrs_are_visible, +}; + + +/* Serial Number */ +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct greybus_device *gdev = to_greybus_device(dev); + return sprintf(buf, "%llX\n", + (unsigned long long)gdev->serial_number.serial_number); +} +static DEVICE_ATTR_RO(serial_number); + +static struct attribute *serial_number_attrs[] = { + &dev_attr_serial_number.attr, + NULL, +}; + +static struct attribute_group serial_number_attr_grp = { + .attrs = serial_number_attrs, + .is_visible = function_attrs_are_visible, +}; + + +const struct attribute_group *greybus_module_groups[] = { + &function_attr_grp, + &module_attr_grp, + &serial_number_attr_grp, + NULL, +}; + -- cgit v1.2.3-59-g8ed1b From 3be03d42cd02402a5aa41d8a55ae3597ef302667 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Sep 2014 19:10:06 -0700 Subject: greybus: minor checkpatch cleanups --- drivers/staging/greybus/core.c | 5 +++-- drivers/staging/greybus/gpio-gb.c | 3 +-- drivers/staging/greybus/sysfs.c | 3 +++ drivers/staging/greybus/uart-gb.c | 3 +-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index bbe9ef96a4f8..da62baf764a2 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -226,8 +226,9 @@ error_i2c: return retval; } -static const struct greybus_module_id fake_gb_id = - { GREYBUS_DEVICE(0x42, 0x42) }; +static const struct greybus_module_id fake_gb_id = { + GREYBUS_DEVICE(0x42, 0x42) +}; static int create_function(struct greybus_device *gdev, struct greybus_descriptor *desc, int desc_size) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index c4cb6a2f34c9..3428220c5158 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -48,7 +48,6 @@ static int gpio_get(struct gpio_chip *gpio, unsigned nr) static void gpio_set(struct gpio_chip *gpio, unsigned nr, int val) { // FIXME - do something there - return; } int gb_gpio_probe(struct greybus_device *gdev, @@ -77,7 +76,7 @@ int gb_gpio_probe(struct greybus_device *gdev, gpio->ngpio = 42; // FIXME!!! gpio->can_sleep = false; // FIXME!!! - gdev->gb_gpio_dev= gb_gpio; + gdev->gb_gpio_dev = gb_gpio; retval = gpiochip_add(gpio); if (retval) { diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 16e6ca4f9200..2393a6a0b678 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -85,6 +85,7 @@ static ssize_t module_vendor_string_show(struct device *dev, char *buf) { struct greybus_device *gdev = to_greybus_device(dev); + return sprintf(buf, "%s", greybus_string(gdev, gdev->module_id.vendor_stringid)); } @@ -95,6 +96,7 @@ static ssize_t module_product_string_show(struct device *dev, char *buf) { struct greybus_device *gdev = to_greybus_device(dev); + return sprintf(buf, "%s", greybus_string(gdev, gdev->module_id.product_stringid)); } @@ -141,6 +143,7 @@ static ssize_t serial_number_show(struct device *dev, struct device_attribute *attr, char *buf) { struct greybus_device *gdev = to_greybus_device(dev); + return sprintf(buf, "%llX\n", (unsigned long long)gdev->serial_number.serial_number); } diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index d104d9cc1221..896d50124a9a 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -245,8 +245,7 @@ static int get_serial_info(struct gb_tty *gb_tty, if (copy_to_user(info, &tmp, sizeof(tmp))) return -EFAULT; - else - return 0; + return 0; } static int set_serial_info(struct gb_tty *gb_tty, -- cgit v1.2.3-59-g8ed1b From 48123e0e1eb917c577fb48ef25f41823a7b080f9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Sep 2014 10:51:56 -0700 Subject: greybus: add proper packing to all greybus message types --- drivers/staging/greybus/greybus_desc.h | 4 ++++ drivers/staging/greybus/svc_msg.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/staging/greybus/greybus_desc.h b/drivers/staging/greybus/greybus_desc.h index 0fd7230f63f9..592b6517d43b 100644 --- a/drivers/staging/greybus/greybus_desc.h +++ b/drivers/staging/greybus/greybus_desc.h @@ -10,6 +10,8 @@ #ifndef __GREYBUS_DESC_H #define __GREYBUS_DESC_H +#pragma pack(push, 1) + struct greybus_descriptor_block_header { __le16 size; __u8 version_major; @@ -92,4 +94,6 @@ struct greybus_descriptor { }; }; +#pragma pack(pop) + #endif /* __GREYBUS_DESC_H */ diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index d992f3cebcef..bbc7ef0b6517 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -10,6 +10,8 @@ #ifndef __SVC_MSG_H #define __SVC_MSG_H +#pragma pack(push, 1) + enum svc_function_type { SVC_FUNCTION_HANDSHAKE = 0x00, SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, @@ -161,4 +163,6 @@ struct svc_msg { }; }; +#pragma pack(pop) + #endif /* __SVC_MSG_H */ -- cgit v1.2.3-59-g8ed1b From f8089c0c6e6572b4c1f3483d93e0ec4edff20f53 Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Fri, 5 Sep 2014 23:56:09 -0400 Subject: greybus: uart-gb.c: replace alloc_tty_driver with tty_alloc_driver alloc_tty_driver() is deprecated. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 896d50124a9a..013936513eac 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -469,8 +469,8 @@ int __init gb_tty_init(void) { int retval; - gb_tty_driver = alloc_tty_driver(GB_NUM_MINORS); - if (!gb_tty_driver) + gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); + if (IS_ERR(gb_tty_driver)) return -ENOMEM; gb_tty_driver->driver_name = "gb"; -- cgit v1.2.3-59-g8ed1b From 7fabc884f9be6ad0828c955bfd28d067bbf6a999 Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Fri, 5 Sep 2014 23:56:10 -0400 Subject: greybus: uart-gb.c: dynamically allocate device numbers --- drivers/staging/greybus/uart-gb.c | 47 +++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 013936513eac..f351046bd79b 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -23,10 +23,13 @@ #include #include #include +#include +#include #include "greybus.h" #define GB_TTY_MAJOR 180 /* FIXME use a real number!!! */ #define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ +#define GB_NAME "ttyGB" struct gb_tty { struct tty_port port; @@ -467,16 +470,24 @@ static struct greybus_driver tty_gb_driver = { int __init gb_tty_init(void) { - int retval; + int retval = 0; + dev_t dev; + + retval = alloc_chrdev_region(&dev, 0, GB_NUM_MINORS, GB_NAME); + if (retval) + return retval; gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); - if (IS_ERR(gb_tty_driver)) - return -ENOMEM; + if (IS_ERR(gb_tty_driver)) { + retval = -ENOMEM; + goto fail_unregister_dev; + } + gb_tty_driver->owner = THIS_MODULE; gb_tty_driver->driver_name = "gb"; - gb_tty_driver->name = "ttyGB"; - gb_tty_driver->major = GB_TTY_MAJOR; - gb_tty_driver->minor_start = 0; + gb_tty_driver->name = GB_NAME; + gb_tty_driver->major = MAJOR(dev); + gb_tty_driver->minor_start = MINOR(dev); gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; @@ -485,24 +496,32 @@ int __init gb_tty_init(void) tty_set_operations(gb_tty_driver, &gb_ops); retval = tty_register_driver(gb_tty_driver); - if (retval) { - put_tty_driver(gb_tty_driver); - return retval; - } + if (retval) + goto fail_put_gb_tty; retval = greybus_register(&tty_gb_driver); - if (retval) { - tty_unregister_driver(gb_tty_driver); - put_tty_driver(gb_tty_driver); - } + if (retval) + goto fail_unregister_gb_tty; + + return 0; + + fail_unregister_gb_tty: + tty_unregister_driver(gb_tty_driver); + fail_put_gb_tty: + put_tty_driver(gb_tty_driver); + fail_unregister_dev: + unregister_chrdev_region(dev, GB_NUM_MINORS); return retval; } void __exit gb_tty_exit(void) { + int major = MAJOR(gb_tty_driver->major); + int minor = gb_tty_driver->minor_start; greybus_deregister(&tty_gb_driver); tty_unregister_driver(gb_tty_driver); put_tty_driver(gb_tty_driver); + unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); } #if 0 -- cgit v1.2.3-59-g8ed1b From f0e49eb05997d9a835a9537e8c466f1ce01a93cb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 6 Sep 2014 11:42:25 -0700 Subject: greybus: uart-gb: remove unneeded THIS_MODULE setting --- drivers/staging/greybus/uart-gb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index f351046bd79b..852dd0c513d5 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -483,7 +483,6 @@ int __init gb_tty_init(void) goto fail_unregister_dev; } - gb_tty_driver->owner = THIS_MODULE; gb_tty_driver->driver_name = "gb"; gb_tty_driver->name = GB_NAME; gb_tty_driver->major = MAJOR(dev); -- cgit v1.2.3-59-g8ed1b From d6e0e1c552fe83f6694acebf3a34e4b66a143e05 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 6 Sep 2014 13:13:13 -0700 Subject: greybus: add es1_ap_desc.c to describe the ES1 USB device descriptors --- drivers/staging/greybus/es1_ap_desc.c | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 drivers/staging/greybus/es1_ap_desc.c diff --git a/drivers/staging/greybus/es1_ap_desc.c b/drivers/staging/greybus/es1_ap_desc.c new file mode 100644 index 000000000000..eec734996df3 --- /dev/null +++ b/drivers/staging/greybus/es1_ap_desc.c @@ -0,0 +1,63 @@ + +static const u8 es1_dev_descriptor[] = { + 0x12, /* __u8 bLength */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, 0x02 /* __le16 bcdUSB v2.0 */ + 0x00, /* __u8 bDeviceClass */ + 0x00, /* __u8 bDeviceClass */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x40, /* __u8 bMaxPacketSize0; 2^64 = 512 Bytes */ + + 0xff, 0xff, /* __le16 idVendor; 0xffff made up for now */ + 0x01, 0x00, /* __le16 idProduct; 0x0001 made up for now */ + 0x01, 0x00, /* __le16 bcdDevice; ES1 */ + + 0x03, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +static const u8 es1_config_descriptor[] = { + /* one configuration */ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, 0x00, /* __le16 wTotalLength; */ + 0x01, /* __u8 bNumInterfaces; (1) */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0xc0, /* __u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* one interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x02, /* __u8 if_bNumEndpoints; */ + 0xff, /* __u8 if_bInterfaceClass; Vendor-specific */ + 0xff, /* __u8 if_bInterfaceSubClass; Vendor-specific */ + 0xff, /* __u8 if_bInterfaceProtocol; Vendor-specific */ + 0x00, /* __u8 if_iInterface; */ + + /* two endpoints */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x02, /* __u8 ep_bmAttributes; Bulk */ + 0x40, 0x00, /* __le16 ep_wMaxPacketSize; 64??? */ + 0x00 /* __u8 ep_bInterval; */ + + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x02, /* __u8 ep_bEndpointAddress; Out Endpoint 2 */ + 0x02, /* __u8 ep_bmAttributes; Bulk */ + 0x40, 0x00, /* __le16 ep_wMaxPacketSize; 64??? */ + 0x00 /* __u8 ep_bInterval; */ + +}; -- cgit v1.2.3-59-g8ed1b From a39879fc089c9db1be9d2aee2daf2991424f9a04 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 6 Sep 2014 16:57:36 -0700 Subject: greybus: host controller additions Also some gbuf functions starting to get fleshed out. --- drivers/staging/greybus/core.c | 16 +++++ drivers/staging/greybus/es1-ap-usb.c | 113 +++++++++++++++++++++++++++-------- drivers/staging/greybus/gbuf.c | 76 ++++++++++++++++++++++- drivers/staging/greybus/greybus.h | 41 +++++++++++-- 4 files changed, 213 insertions(+), 33 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index da62baf764a2..517b260159c9 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -449,6 +449,22 @@ void greybus_remove_device(struct greybus_device *gdev) // FIXME - device_remove(&gdev->dev); } +struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver, + struct device *parent) +{ + struct greybus_host_device *hd; + + hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL); + if (!hd) + return NULL; + + kref_init(&hd->kref); + + return hd; +} +EXPORT_SYMBOL_GPL(greybus_create_hd); + + static int __init gb_init(void) { int retval; diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index e7042d569695..a08e4ddc4f20 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -13,7 +13,7 @@ #include "greybus.h" static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x0000, 0x0000) }, // FIXME + { USB_DEVICE(0xffff, 0x0001) }, // FIXME { }, }; MODULE_DEVICE_TABLE(usb, id_table); @@ -21,18 +21,73 @@ MODULE_DEVICE_TABLE(usb, id_table); struct es1_ap_dev { struct usb_device *usb_dev; struct usb_interface *usb_intf; + struct greybus_host_device *hd; - __u8 ap_in_endpoint; - __u8 ap_out_endpoint; + __u8 ap_comm_endpoint; /* endpoint to talk to the AP */ + __u8 ap_in_endpoint; /* bulk in for CPort data */ + __u8 ap_out_endpoint; /* bulk out for CPort data */ u8 *ap_buffer; }; +static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) +{ + return (struct es1_ap_dev *)(hd->hd_priv); +} + /* - * Hack, we "know" we will only have one of these at any one time, so only - * create one static structure pointer. + * Allocate the actual buffer for this gbuf and device and cport + * + * We are responsible for setting the following fields in a struct gbuf: + * void *hcpriv; + * void *transfer_buffer; + * u32 transfer_buffer_length; */ -static struct es1_ap_dev *es1_ap_dev; +static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) +{ + struct es1_ap_dev *es1 = hd_to_es1(gbuf->gdev->hd); + u8 *buffer; + + /* For ES2 we need to figure out what cport is going to what endpoint, + * but for ES1, it's so dirt simple, we don't have a choice... + * + * Also, do a "slow" allocation now, if we need speed, use a cache + */ + buffer = kmalloc(size + 1, gfp_mask); + if (!buffer) + return -ENOMEM; + + /* + * we will encode the cport number in the first byte of the buffer, so + * set the second byte to be the "transfer buffer" + */ + buffer[0] = gbuf->cport->number; + gbuf->transfer_buffer = &buffer[1]; + gbuf->transfer_buffer_length = size; + + gbuf->hdpriv = es1; /* really, we could do something else here... */ + + return 0; +} + +/* Free the memory we allocated with a gbuf */ +static void free_gbuf(struct gbuf *gbuf) +{ + u8 *transfer_buffer; + u8 *buffer; + + transfer_buffer = gbuf->transfer_buffer; + buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ + kfree(buffer); +} + + +static struct greybus_host_driver es1_driver = { + .hd_priv_size = sizeof(struct es1_ap_dev), + .alloc_gbuf = alloc_gbuf, + .free_gbuf = free_gbuf, +}; + void ap_in_callback(struct urb *urb) { @@ -100,19 +155,26 @@ exit: static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) { + struct es1_ap_dev *es1; + struct greybus_host_device *hd; + struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - size_t buffer_size; int i; - if (es1_ap_dev) { - dev_err(&interface->dev, "Already have a es1_ap_dev???\n"); - return -ENODEV; - } - es1_ap_dev = kzalloc(sizeof(*es1_ap_dev), GFP_KERNEL); - if (!es1_ap_dev) + udev = usb_get_dev(interface_to_usbdev(interface)); + + hd = greybus_create_hd(&es1_driver, &udev->dev); + if (!hd) return -ENOMEM; + es1 = hd_to_es1(hd); + es1->hd = hd; + + /* Control endpoint is the pipe to talk to this AP, so save it off */ + endpoint = &udev->ep0.desc; + es1->ap_comm_endpoint = endpoint->bEndpointAddress; + // FIXME // figure out endpoint for talking to the AP. iface_desc = interface->cur_altsetting; @@ -120,13 +182,10 @@ static int ap_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[i].desc; if (usb_endpoint_is_bulk_in(endpoint)) { - buffer_size = usb_endpoint_maxp(endpoint); - // FIXME - Save buffer_size? - es1_ap_dev->ap_in_endpoint = endpoint->bEndpointAddress; + es1->ap_in_endpoint = endpoint->bEndpointAddress; } if (usb_endpoint_is_bulk_out(endpoint)) { - // FIXME - anything else about this we need? - es1_ap_dev->ap_out_endpoint = endpoint->bEndpointAddress; + es1->ap_out_endpoint = endpoint->bEndpointAddress; } // FIXME - properly exit once found the AP endpoint // FIXME - set up cport endpoints @@ -135,23 +194,25 @@ static int ap_probe(struct usb_interface *interface, // FIXME - allocate buffer // FIXME = start up talking, then create the gb "devices" based on what the AP tells us. - es1_ap_dev->usb_intf = interface; - es1_ap_dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); - usb_set_intfdata(interface, es1_ap_dev); + es1->usb_intf = interface; + es1->usb_dev = udev; + usb_set_intfdata(interface, es1); return 0; } static void ap_disconnect(struct usb_interface *interface) { - es1_ap_dev = usb_get_intfdata(interface); + struct es1_ap_dev *es1; + + es1 = usb_get_intfdata(interface); /* Tear down everything! */ - usb_put_dev(es1_ap_dev->usb_dev); - kfree(es1_ap_dev->ap_buffer); - kfree(es1_ap_dev); - es1_ap_dev = NULL; + usb_put_dev(es1->usb_dev); + kfree(es1->ap_buffer); + // FIXME + //greybus_destroy_hd(es1->hd); } static struct usb_driver es1_ap_driver = { diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 9f37fc6e31cf..1444b3ffa174 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -12,21 +12,93 @@ #include #include #include +#include #include +#include #include "greybus.h" +/** + * greybus_alloc_gbuf - allocate a greybus buffer + * + * @gdev: greybus device that wants to allocate this + * @cport: cport to send the data to + * @complete: callback when the gbuf is finished with + * @size: size of the buffer + * @gfp_mask: allocation mask + * @context: context added to the gbuf by the driver + * + * TODO: someday it will be nice to handle DMA, but for now, due to the + * architecture we are stuck with, the greybus core has to allocate the buffer + * that the driver can then fill up with the data to be sent out. Curse + * hardware designers for this issue... + */ struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, struct gdev_cport *cport, - gfp_t mem_flags) + gbuf_complete_t complete, + unsigned int size, + gfp_t gfp_mask, + void *context) { - return NULL; + struct gbuf *gbuf; + int retval; + + /* + * change this to a slab allocation if it's too slow, but for now, let's + * be dumb and simple. + */ + gbuf = kzalloc(sizeof(*gbuf), gfp_mask); + if (!gbuf) + return NULL; + + kref_init(&gbuf->kref); + gbuf->gdev = gdev; + gbuf->cport = cport; + gbuf->complete = complete; + gbuf->context = context; + + /* Host controller specific allocation for the actual buffer */ + retval = gbuf->gdev->hd->driver->alloc_gbuf(gbuf, size, gfp_mask); + if (retval) { + kfree(gbuf); + return NULL; + } + + return gbuf; +} +EXPORT_SYMBOL_GPL(greybus_alloc_gbuf); + +static DEFINE_MUTEX(gbuf_mutex); + +static void free_gbuf(struct kref *kref) +{ + struct gbuf *gbuf = container_of(kref, struct gbuf, kref); + + /* let the host controller free what it wants to */ + gbuf->gdev->hd->driver->free_gbuf(gbuf); + + kfree(gbuf); } void greybus_free_gbuf(struct gbuf *gbuf) { + /* drop the reference count and get out of here */ + kref_put_mutex(&gbuf->kref, free_gbuf, &gbuf_mutex); + } +EXPORT_SYMBOL_GPL(greybus_free_gbuf); + +struct gbuf *greybus_get_gbuf(struct gbuf *gbuf) +{ + mutex_lock(&gbuf_mutex); + kref_get(&gbuf->kref); + mutex_unlock(&gbuf_mutex); + return gbuf; +} +EXPORT_SYMBOL_GPL(greybus_get_gbuf); + + int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags) { diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index d9cfc3ba3001..95225f22783d 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -52,10 +52,7 @@ typedef void (*gbuf_complete_t)(struct gbuf *gbuf); struct gbuf { struct kref kref; - void *hcpriv; - - struct list_head anchor_list; - struct gbuf_anchor *anchor; // FIXME do we need? + void *hdpriv; struct greybus_device *gdev; struct gdev_cport *cport; @@ -65,8 +62,10 @@ struct gbuf { u32 transfer_buffer_length; u32 actual_length; +#if 0 struct scatterlist *sg; // FIXME do we need? int num_sgs; +#endif void *context; gbuf_complete_t complete; @@ -92,6 +91,31 @@ struct gb_gpio_device; struct gb_sdio_host; struct gb_tty; struct gb_usb_device; +struct greybus_host_device; + +/* Greybus "Host driver" structure, needed by a host controller driver to be + * able to handle both SVC control as well as "real" greybus messages + */ +struct greybus_host_driver { + size_t hd_priv_size; + + int (*start)(struct greybus_host_device *hd); + int (*alloc_gbuf)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); + void (*free_gbuf)(struct gbuf *gbuf); +}; + +struct greybus_host_device { + struct kref kref; + const struct greybus_host_driver *driver; + unsigned long hd_priv_size; + + /* Private data for the host driver */ + unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64)))); +}; + +struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver, + struct device *parent); + /* Increase these values if needed */ #define MAX_CPORTS_PER_MODULE 10 @@ -108,6 +132,8 @@ struct greybus_device { struct gdev_cport *cport[MAX_CPORTS_PER_MODULE]; struct gdev_string *string[MAX_STRINGS_PER_MODULE]; + struct greybus_host_device *hd; + struct gb_i2c_device *gb_i2c_dev; struct gb_gpio_device *gb_gpio_dev; struct gb_sdio_host *gb_sdio_host; @@ -118,8 +144,13 @@ struct greybus_device { struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, struct gdev_cport *cport, - gfp_t mem_flags); + gbuf_complete_t complete, + unsigned int size, + gfp_t gfp_mask, + void *context); void greybus_free_gbuf(struct gbuf *gbuf); +struct gbuf *greybus_get_gbuf(struct gbuf *gbuf); +#define greybus_put_gbuf greybus_free_gbuf int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags); int greybus_kill_gbuf(struct gbuf *gbuf); -- cgit v1.2.3-59-g8ed1b From 68f1fc4d2c9c908b1a6f00847743545082353e99 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 7 Sep 2014 13:12:11 -0700 Subject: greybus: more hd work --- drivers/staging/greybus/ap.c | 29 ++++++++++++++++++++++++++--- drivers/staging/greybus/core.c | 18 +++++++++++++++++- drivers/staging/greybus/es1-ap-usb.c | 20 +++++++++++++++++--- drivers/staging/greybus/greybus.h | 5 ++++- 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 5b06b1919153..2fb2cc1416a7 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -22,7 +22,8 @@ struct ap_msg { u8 *data; - int size; + size_t size; + struct greybus_host_device *hd; struct list_head list; }; @@ -31,6 +32,21 @@ static spinlock_t ap_msg_list_lock; static struct task_struct *ap_thread; static wait_queue_head_t ap_wait; + +static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) +{ + struct svc_msg *svc_msg; + + // FIXME - validate message, right now we are trusting the size and data + // from the AP, what could go wrong? :) + // for now, just cast the pointer and run away... + + svc_msg = (struct svc_msg *)ap_msg->data; + return svc_msg; +} + + + static struct ap_msg *get_ap_msg(void) { struct ap_msg *ap_msg; @@ -49,6 +65,7 @@ static struct ap_msg *get_ap_msg(void) static int ap_process_loop(void *data) { struct ap_msg *ap_msg; + struct svc_msg *svc_msg; while (!kthread_should_stop()) { wait_event_interruptible(ap_wait, kthread_should_stop()); @@ -61,7 +78,12 @@ static int ap_process_loop(void *data) if (!ap_msg) continue; - // FIXME - process the message + /* Turn the "raw" data into a real message */ + svc_msg = convert_ap_message(ap_msg); + if (svc_msg) { + /* Pass the message to the host controller */ + ap_msg->hd->driver->ap_msg(svc_msg, ap_msg->hd); + } /* clean the message up */ kfree(ap_msg->data); @@ -70,7 +92,7 @@ static int ap_process_loop(void *data) return 0; } -int gb_new_ap_msg(u8 *data, int size) +int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd) { struct ap_msg *ap_msg; unsigned long flags; @@ -96,6 +118,7 @@ int gb_new_ap_msg(u8 *data, int size) } memcpy(ap_msg->data, data, size); ap_msg->size = size; + ap_msg->hd = hd; spin_lock_irqsave(&ap_msg_list_lock, flags); list_add(&ap_msg->list, &ap_msg_list); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 517b260159c9..5c633a3945c1 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include "greybus.h" @@ -449,6 +448,17 @@ void greybus_remove_device(struct greybus_device *gdev) // FIXME - device_remove(&gdev->dev); } +static DEFINE_MUTEX(hd_mutex); + +static void free_hd(struct kref *kref) +{ + struct greybus_host_device *hd; + + hd = container_of(kref, struct greybus_host_device, kref); + + kfree(hd); +} + struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver, struct device *parent) { @@ -464,6 +474,12 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver } EXPORT_SYMBOL_GPL(greybus_create_hd); +void greybus_remove_hd(struct greybus_host_device *hd) +{ + kref_put_mutex(&hd->kref, free_hd, &hd_mutex); +} +EXPORT_SYMBOL_GPL(greybus_remove_hd); + static int __init gb_init(void) { diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index a08e4ddc4f20..8589273f35a5 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -82,15 +82,30 @@ static void free_gbuf(struct gbuf *gbuf) } +/* Main message loop for ap messages */ +/* Odds are, most of this logic can move to core.c someday, but as we only have + * one host controller driver for now, let's leave it here */ +static void ap_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + + /* Look at the message to figure out what to do with it */ + + +} + + static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .alloc_gbuf = alloc_gbuf, .free_gbuf = free_gbuf, + .ap_msg = ap_msg, }; void ap_in_callback(struct urb *urb) { + struct es1_ap_dev *es1 = urb->context; struct device *dev = &urb->dev->dev; int status = urb->status; int retval; @@ -115,7 +130,7 @@ void ap_in_callback(struct urb *urb) /* We have a message, create a new message structure, add it to the * list, and wake up our thread that will process the messages. */ - gb_new_ap_msg(urb->transfer_buffer, urb->actual_length); + gb_new_ap_msg(urb->transfer_buffer, urb->actual_length, es1->hd); exit: /* resubmit the urb to get more messages */ @@ -211,8 +226,7 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(es1->usb_dev); kfree(es1->ap_buffer); - // FIXME - //greybus_destroy_hd(es1->hd); + greybus_remove_hd(es1->hd); } static struct usb_driver es1_ap_driver = { diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 95225f22783d..2f67df5dd5e3 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -92,6 +92,7 @@ struct gb_sdio_host; struct gb_tty; struct gb_usb_device; struct greybus_host_device; +struct svc_msg; /* Greybus "Host driver" structure, needed by a host controller driver to be * able to handle both SVC control as well as "real" greybus messages @@ -102,6 +103,7 @@ struct greybus_host_driver { int (*start)(struct greybus_host_device *hd); int (*alloc_gbuf)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); void (*free_gbuf)(struct gbuf *gbuf); + void (*ap_msg)(struct svc_msg *svc_msg, struct greybus_host_device *hd); }; struct greybus_host_device { @@ -115,6 +117,7 @@ struct greybus_host_device { struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver, struct device *parent); +void greybus_remove_hd(struct greybus_host_device *hd); /* Increase these values if needed */ @@ -213,7 +216,7 @@ const u8 *greybus_string(struct greybus_device *gdev, int id); /* Internal functions to gb module, move to internal .h file eventually. */ -int gb_new_ap_msg(u8 *data, int length); +int gb_new_ap_msg(u8 *data, int length, struct greybus_host_device *hd); int gb_thread_init(void); void gb_thread_destroy(void); int gb_debugfs_init(void); -- cgit v1.2.3-59-g8ed1b From 43cc32a2ab9ba004fa2fec1893ca5bd3e13f64d2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 7 Sep 2014 13:51:12 -0700 Subject: greybus: first cut at parsing svc messages sent to the AP --- drivers/staging/greybus/ap.c | 6 ++ drivers/staging/greybus/es1-ap-usb.c | 147 ++++++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 2fb2cc1416a7..a70404d7c1f1 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -42,6 +42,12 @@ static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) // for now, just cast the pointer and run away... svc_msg = (struct svc_msg *)ap_msg->data; + + // FIXME - put in correct version numbers + if ((svc_msg->header.version_major != 0x00) && + (svc_msg->header.version_minor != 0x00)) + return NULL; + return svc_msg; } diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 8589273f35a5..12a5d0cf76ba 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -11,6 +11,7 @@ #include #include #include "greybus.h" +#include "svc_msg.h" static const struct usb_device_id id_table[] = { { USB_DEVICE(0xffff, 0x0001) }, // FIXME @@ -81,6 +82,124 @@ static void free_gbuf(struct gbuf *gbuf) kfree(buffer); } +static struct svc_msg *svc_msg_alloc(enum svc_function_type type) +{ + struct svc_msg *svc_msg; + + svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL); + if (!svc_msg) + return NULL; + + // FIXME - verify we are only sending message types we should be + svc_msg->header.type = type; + return svc_msg; +} + +static void svc_msg_free(struct svc_msg *svc_msg) +{ + kfree(svc_msg); +} + +static int svc_msg_send(struct svc_msg *svc_msg) +{ + // FIXME - Do something with this message! + + + svc_msg_free(svc_msg); + return 0; +} + + +static void svc_handshake(struct svc_function_handshake *handshake, + struct es1_ap_dev *es1) +{ + struct svc_msg *svc_msg; + + /* A new SVC communication channel, let's verify it was for us */ + if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) { + /* we don't know what to do with this, log it and return */ + dev_dbg(&es1->usb_intf->dev, + "received invalid handshake type %d\n", + handshake->handshake_type); + return; + } + + /* Send back a AP_HELLO message */ + svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE); + if (!svc_msg) + return; + + svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; + svc_msg_send(svc_msg); +} + +static void svc_management(struct svc_function_unipro_management *management, + struct es1_ap_dev *es1) +{ + /* What? An AP should not get this message */ + dev_err(&es1->usb_intf->dev, "Got an svc management message???\n"); +} + +static void svc_hotplug(struct svc_function_hotplug *hotplug, + struct es1_ap_dev *es1) +{ + u8 module_id = hotplug->module_id; + + switch (hotplug->hotplug_event) { + case SVC_HOTPLUG_EVENT: + dev_dbg(&es1->usb_intf->dev, "module id %d added\n", + module_id); + // FIXME - add the module to the system + break; + + case SVC_HOTUNPLUG_EVENT: + dev_dbg(&es1->usb_intf->dev, "module id %d removed\n", + module_id); + // FIXME - remove the module from the system + break; + + default: + dev_err(&es1->usb_intf->dev, "received invalid hotplug message type %d\n", + hotplug->hotplug_event); + break; + } +} + +static void svc_ddb(struct svc_function_ddb *ddb, struct es1_ap_dev *es1) +{ + /* What? An AP should not get this message */ + dev_err(&es1->usb_intf->dev, "Got an svc DDB message???\n"); +} + +static void svc_power(struct svc_function_power *power, struct es1_ap_dev *es1) +{ + u8 module_id = power->module_id; + + if (power->power_type != SVC_POWER_BATTERY_STATUS) { + dev_err(&es1->usb_intf->dev, "received invalid power type %d\n", + power->power_type); + return; + } + + dev_dbg(&es1->usb_intf->dev, "power status for module id %d is %d\n", + module_id, power->status.status); + + // FIXME - do something with the power information, like update our + // battery information... +} + +static void svc_epm(struct svc_function_epm *epm, struct es1_ap_dev *es1) +{ + /* What? An AP should not get this message */ + dev_err(&es1->usb_intf->dev, "Got an EPM message???\n"); +} + +static void svc_suspend(struct svc_function_suspend *suspend, + struct es1_ap_dev *es1) +{ + /* What? An AP should not get this message */ + dev_err(&es1->usb_intf->dev, "Got an suspend message???\n"); +} /* Main message loop for ap messages */ /* Odds are, most of this logic can move to core.c someday, but as we only have @@ -90,8 +209,32 @@ static void ap_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd) struct es1_ap_dev *es1 = hd_to_es1(hd); /* Look at the message to figure out what to do with it */ - - + switch (svc_msg->header.type) { + case SVC_FUNCTION_HANDSHAKE: + svc_handshake(&svc_msg->handshake, es1); + break; + case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT: + svc_management(&svc_msg->management, es1); + break; + case SVC_FUNCTION_HOTPLUG: + svc_hotplug(&svc_msg->hotplug, es1); + break; + case SVC_FUNCTION_DDB: + svc_ddb(&svc_msg->ddb, es1); + break; + case SVC_FUNCTION_POWER: + svc_power(&svc_msg->power, es1); + break; + case SVC_FUNCTION_EPM: + svc_epm(&svc_msg->epm, es1); + break; + case SVC_FUNCTION_SUSPEND: + svc_suspend(&svc_msg->suspend, es1); + break; + default: + dev_err(&es1->usb_intf->dev, "received invalid SVC message type %d\n", + svc_msg->header.type); + } } -- cgit v1.2.3-59-g8ed1b From 33ea3a3f56d26f10e07ffe16fcaccaad904056d3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 7 Sep 2014 15:39:34 -0700 Subject: greybus: add battery module --- drivers/staging/greybus/Makefile | 3 +- drivers/staging/greybus/battery-gb.c | 153 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/core.c | 8 ++ drivers/staging/greybus/greybus.h | 4 + 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/greybus/battery-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index c1e4e12b8b4a..114089ada9f7 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -6,7 +6,8 @@ greybus-y := core.o \ i2c-gb.o \ gpio-gb.o \ sdio-gb.o \ - uart-gb.o + uart-gb.o \ + battery-gb.o obj-m += greybus.o obj-m += es1-ap-usb.o diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c new file mode 100644 index 000000000000..123b2af56635 --- /dev/null +++ b/drivers/staging/greybus/battery-gb.c @@ -0,0 +1,153 @@ +/* + * Battery driver for a Greybus module. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_battery { + struct power_supply bat; + struct greybus_device *gdev; +}; +#define to_gb_battery(x) container_of(x, struct gb_battery, bat) + +static const struct greybus_module_id id_table[] = { + { GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */ + { }, /* terminating NULL entry */ +}; + +static int get_status(struct gb_battery *gb) +{ + // FIXME!!! + return 0; +} + +static int get_capacity(struct gb_battery *gb) +{ + // FIXME!!! + return 0; +} + +static int get_temp(struct gb_battery *gb) +{ + // FIXME!!! + return 0; +} + +static int get_voltage(struct gb_battery *gb) +{ + // FIXME!!! + return 0; +} + + + +static int get_property(struct power_supply *b, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct gb_battery *gb = to_gb_battery(b); + + switch (psp) { + case POWER_SUPPLY_PROP_TECHNOLOGY: + // FIXME - guess! + val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH; + break; + + case POWER_SUPPLY_PROP_STATUS: + val->intval = get_status(gb); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = 4700000; // FIXME - guess??? + break; + + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = get_capacity(gb); + break; + + case POWER_SUPPLY_PROP_TEMP: + val->intval = get_temp(gb); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = get_voltage(gb); + break; + + default: + return -EINVAL; + } + + return 0; +} + + +// FIXME - verify this list, odds are some can be removed and others added. +static enum power_supply_property battery_props[] = { + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +int gb_battery_probe(struct greybus_device *gdev, + const struct greybus_module_id *id) +{ + struct gb_battery *gb; + struct power_supply *b; + int retval; + + gb = kzalloc(sizeof(*gb), GFP_KERNEL); + if (!gb) + return -ENOMEM; + + b = &gb->bat; + // FIXME - get a better (i.e. unique) name + // FIXME - anything else needs to be set? + b->name = "gb_battery"; + b->type = POWER_SUPPLY_TYPE_BATTERY, + b->properties = battery_props, + b->num_properties = ARRAY_SIZE(battery_props), + b->get_property = get_property, + + retval = power_supply_register(&gdev->dev, b); + if (retval) { + kfree(gb); + return retval; + } + gdev->gb_battery = gb; + + return 0; +} + +void gb_battery_disconnect(struct greybus_device *gdev) +{ + struct gb_battery *gb; + + gb = gdev->gb_battery; + + power_supply_unregister(&gb->bat); + + kfree(gb); +} + +#if 0 +static struct greybus_driver battery_gb_driver = { + .probe = gb_battery_probe, + .disconnect = gb_battery_disconnect, + .id_table = id_table, +}; + +module_greybus_driver(battery_gb_driver); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); +#endif diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 5c633a3945c1..76938cd00288 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -210,8 +210,15 @@ static int gb_init_subdevs(struct greybus_device *gdev, retval = gb_tty_probe(gdev, id); if (retval) goto error_tty; + + retval = gb_battery_probe(gdev, id); + if (retval) + goto error_battery; return 0; +error_battery: + gb_tty_disconnect(gdev); + error_tty: gb_sdio_disconnect(gdev); @@ -444,6 +451,7 @@ void greybus_remove_device(struct greybus_device *gdev) gb_gpio_disconnect(gdev); gb_sdio_disconnect(gdev); gb_tty_disconnect(gdev); + gb_battery_disconnect(gdev); // FIXME - device_remove(&gdev->dev); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2f67df5dd5e3..2b96917c8730 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -91,6 +91,7 @@ struct gb_gpio_device; struct gb_sdio_host; struct gb_tty; struct gb_usb_device; +struct gb_battery; struct greybus_host_device; struct svc_msg; @@ -142,6 +143,7 @@ struct greybus_device { struct gb_sdio_host *gb_sdio_host; struct gb_tty *gb_tty; struct gb_usb_device *gb_usb_dev; + struct gb_battery *gb_battery; }; #define to_greybus_device(d) container_of(d, struct greybus_device, dev) @@ -237,6 +239,8 @@ int gb_sdio_probe(struct greybus_device *gdev, const struct greybus_module_id *i void gb_sdio_disconnect(struct greybus_device *gdev); int gb_tty_probe(struct greybus_device *gdev, const struct greybus_module_id *id); void gb_tty_disconnect(struct greybus_device *gdev); +int gb_battery_probe(struct greybus_device *gdev, const struct greybus_module_id *id); +void gb_battery_disconnect(struct greybus_device *gdev); int gb_tty_init(void); void gb_tty_exit(void); -- cgit v1.2.3-59-g8ed1b From d47aa761612c9bc2174718889cdab185cb20b723 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 7 Sep 2014 15:54:24 -0700 Subject: greybus: battery FIXME added --- drivers/staging/greybus/battery-gb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 123b2af56635..242b34f637d8 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -14,6 +14,10 @@ struct gb_battery { struct power_supply bat; + // FIXME + // we will want to keep the battery stats in here as we will be getting + // updates from the SVC "on the fly" so we don't have to always go ask + // the battery for some information. Hopefully... struct greybus_device *gdev; }; #define to_gb_battery(x) container_of(x, struct gb_battery, bat) @@ -47,8 +51,6 @@ static int get_voltage(struct gb_battery *gb) return 0; } - - static int get_property(struct power_supply *b, enum power_supply_property psp, union power_supply_propval *val) -- cgit v1.2.3-59-g8ed1b From fe3270425f5555bf382178f27327752f3f2c9c4a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 7 Sep 2014 15:57:07 -0700 Subject: greybus: minor whitespace cleanups to make checkpatch.pl happy --- drivers/staging/greybus/battery-gb.c | 3 +-- drivers/staging/greybus/gbuf.c | 7 ------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 242b34f637d8..7d124331f22f 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -90,7 +90,6 @@ static int get_property(struct power_supply *b, return 0; } - // FIXME - verify this list, odds are some can be removed and others added. static enum power_supply_property battery_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, @@ -102,7 +101,7 @@ static enum power_supply_property battery_props[] = { }; int gb_battery_probe(struct greybus_device *gdev, - const struct greybus_module_id *id) + const struct greybus_module_id *id) { struct gb_battery *gb; struct power_supply *b; diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 1444b3ffa174..1bf92262a629 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -18,7 +18,6 @@ #include "greybus.h" - /** * greybus_alloc_gbuf - allocate a greybus buffer * @@ -85,7 +84,6 @@ void greybus_free_gbuf(struct gbuf *gbuf) { /* drop the reference count and get out of here */ kref_put_mutex(&gbuf->kref, free_gbuf, &gbuf_mutex); - } EXPORT_SYMBOL_GPL(greybus_free_gbuf); @@ -98,8 +96,6 @@ struct gbuf *greybus_get_gbuf(struct gbuf *gbuf) } EXPORT_SYMBOL_GPL(greybus_get_gbuf); - - int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags) { return -ENOMEM; @@ -109,6 +105,3 @@ int greybus_kill_gbuf(struct gbuf *gbuf) { return -ENOMEM; } - - - -- cgit v1.2.3-59-g8ed1b From 8b9951480b996eac4699860042bf3d6a0ef4d064 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 8 Sep 2014 19:34:30 -0700 Subject: greybus: es1_ap_desc.c: updated ES1 USB device descriptor Interrupt IN endpoint added. --- drivers/staging/greybus/es1_ap_desc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es1_ap_desc.c b/drivers/staging/greybus/es1_ap_desc.c index eec734996df3..8c558a4755a3 100644 --- a/drivers/staging/greybus/es1_ap_desc.c +++ b/drivers/staging/greybus/es1_ap_desc.c @@ -1,3 +1,4 @@ +/* ES1 AP Bridge Chip USB descriptor definitions */ static const u8 es1_dev_descriptor[] = { 0x12, /* __u8 bLength */ @@ -45,19 +46,25 @@ static const u8 es1_config_descriptor[] = { 0xff, /* __u8 if_bInterfaceProtocol; Vendor-specific */ 0x00, /* __u8 if_iInterface; */ - /* two endpoints */ + /* three endpoints */ 0x07, /* __u8 ep_bLength; */ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Bulk */ + 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ + 0x00 /* __u8 ep_bInterval; */ + + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x82, /* __u8 ep_bEndpointAddress; IN Endpoint 2 */ 0x02, /* __u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* __le16 ep_wMaxPacketSize; 64??? */ + 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ 0x00 /* __u8 ep_bInterval; */ 0x07, /* __u8 ep_bLength; */ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x02, /* __u8 ep_bEndpointAddress; Out Endpoint 2 */ 0x02, /* __u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* __le16 ep_wMaxPacketSize; 64??? */ + 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ 0x00 /* __u8 ep_bInterval; */ - }; -- cgit v1.2.3-59-g8ed1b From 47f6ef12fe6dfc4107597af4a44b045324c53c02 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 8 Sep 2014 20:09:08 -0700 Subject: greybus: es1: finialized USB device structure Set up device properly and start up the SVC interrupt in endpoint for processing data --- drivers/staging/greybus/es1-ap-usb.c | 129 +++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 12a5d0cf76ba..da3523488165 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -24,11 +24,12 @@ struct es1_ap_dev { struct usb_interface *usb_intf; struct greybus_host_device *hd; - __u8 ap_comm_endpoint; /* endpoint to talk to the AP */ - __u8 ap_in_endpoint; /* bulk in for CPort data */ - __u8 ap_out_endpoint; /* bulk out for CPort data */ - u8 *ap_buffer; - + __u8 control_endpoint; /* endpoint to send data to SVC */ + __u8 svc_endpoint; /* endpoint for SVC data */ + __u8 cport_in_endpoint; /* bulk in for CPort data */ + __u8 cport_out_endpoint; /* bulk out for CPort data */ + u8 *svc_buffer; /* buffer for SVC messages coming in */ + struct urb *svc_urb; /* urb for SVC messages coming in */ }; static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) @@ -245,8 +246,8 @@ static struct greybus_host_driver es1_driver = { .ap_msg = ap_msg, }; - -void ap_in_callback(struct urb *urb) +/* Callback for when we get a SVC message */ +static void svc_callback(struct urb *urb) { struct es1_ap_dev *es1 = urb->context; struct device *dev = &urb->dev->dev; @@ -282,7 +283,7 @@ exit: dev_err(dev, "Can not submit urb for AP data: %d\n", retval); } -void ap_out_callback(struct urb *urb) +void cport_in_callback(struct urb *urb) { struct device *dev = &urb->dev->dev; int status = urb->status; @@ -304,12 +305,45 @@ void ap_out_callback(struct urb *urb) goto exit; } - // FIXME - queue up next AP message to send??? + // FIXME - handle the CPort in data exit: return; } +void cport_out_callback(struct urb *urb) +{ + struct device *dev = &urb->dev->dev; + int status = urb->status; + + switch (status) { + case 0: + break; + case -EOVERFLOW: + dev_err(dev, "%s: overflow actual length is %d\n", + __func__, urb->actual_length); + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EILSEQ: + /* device is gone, stop sending */ + return; + default: + dev_err(dev, "%s: unknown status %d\n", __func__, status); + goto exit; + } + // FIXME - handle the CPort out data callback +exit: + return; +} + +/* + * The ES1 USB Bridge device contains 4 endpoints + * 1 Control - usual USB stuff + AP -> SVC messages + * 1 Interrupt IN - SVC -> AP messages + * 1 Bulk IN - CPort data in + * 1 Bulk OUT - CPorta data out + */ static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -318,7 +352,13 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; + bool int_in_found = false; + bool bulk_in_found = false; + bool bulk_out_found = false; + int retval = -ENOMEM; int i; + int buffer_size = 0; + u8 svc_interval = 0; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -328,34 +368,69 @@ static int ap_probe(struct usb_interface *interface, es1 = hd_to_es1(hd); es1->hd = hd; + es1->usb_intf = interface; + es1->usb_dev = udev; + usb_set_intfdata(interface, es1); /* Control endpoint is the pipe to talk to this AP, so save it off */ endpoint = &udev->ep0.desc; - es1->ap_comm_endpoint = endpoint->bEndpointAddress; + es1->control_endpoint = endpoint->bEndpointAddress; - // FIXME - // figure out endpoint for talking to the AP. + /* find all 3 of our endpoints */ iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { - es1->ap_in_endpoint = endpoint->bEndpointAddress; + if (usb_endpoint_is_int_in(endpoint)) { + es1->svc_endpoint = endpoint->bEndpointAddress; + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + svc_interval = endpoint->bInterval; + int_in_found = true; + } else if (usb_endpoint_is_bulk_in(endpoint)) { + es1->cport_in_endpoint = endpoint->bEndpointAddress; + bulk_in_found = true; + } else if (usb_endpoint_is_bulk_out(endpoint)) { + es1->cport_out_endpoint = endpoint->bEndpointAddress; + bulk_out_found = true; + } else { + dev_err(&udev->dev, + "Unknown endpoint type found, address %x\n", + endpoint->bEndpointAddress); } - if (usb_endpoint_is_bulk_out(endpoint)) { - es1->ap_out_endpoint = endpoint->bEndpointAddress; - } - // FIXME - properly exit once found the AP endpoint - // FIXME - set up cport endpoints + } + if ((int_in_found == false) || + (bulk_in_found == false) || + (bulk_out_found == false)) { + dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); + goto error; } - // FIXME - allocate buffer - // FIXME = start up talking, then create the gb "devices" based on what the AP tells us. + /* Create our buffer and URB to get SVC messages, and start it up */ + es1->svc_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!es1->svc_buffer) + goto error; + + es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!es1->svc_urb) + goto error_urb; + + usb_fill_int_urb(es1->svc_urb, udev, + usb_rcvintpipe(udev, es1->svc_endpoint), + es1->svc_buffer, buffer_size, svc_callback, + es1, svc_interval); + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error_submit_urb; - es1->usb_intf = interface; - es1->usb_dev = udev; - usb_set_intfdata(interface, es1); return 0; + +error_submit_urb: + usb_free_urb(es1->svc_urb); +error_urb: + kfree(es1->svc_buffer); +error: + greybus_remove_hd(es1->hd); + return retval; } static void ap_disconnect(struct usb_interface *interface) @@ -365,11 +440,11 @@ static void ap_disconnect(struct usb_interface *interface) es1 = usb_get_intfdata(interface); /* Tear down everything! */ - + usb_kill_urb(es1->svc_urb); usb_put_dev(es1->usb_dev); - kfree(es1->ap_buffer); - + kfree(es1->svc_buffer); greybus_remove_hd(es1->hd); + usb_set_intfdata(interface, NULL); } static struct usb_driver es1_ap_driver = { -- cgit v1.2.3-59-g8ed1b From d9d077fdbc474150b9494fc45855a9cec781e778 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 8 Sep 2014 20:11:18 -0700 Subject: greybus: es1: forgot to free our urb on disconnect --- drivers/staging/greybus/es1-ap-usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index da3523488165..767981a248c8 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -441,6 +441,7 @@ static void ap_disconnect(struct usb_interface *interface) /* Tear down everything! */ usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); usb_put_dev(es1->usb_dev); kfree(es1->svc_buffer); greybus_remove_hd(es1->hd); -- cgit v1.2.3-59-g8ed1b From 05ad189c23ad278c5586936bbb08147ac6f479be Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Sep 2014 13:55:03 -0500 Subject: greybus: switch to the term "manifest" We agreed to rename a few things to improve clarity. This patch implements one of those changes. The blob of data that describes what's relevant to Greybus within an Ara module will now be called the "module manifest." In addition, in the context of Greybus we'll also be calling what's in an Ara module a "module" or "Greybus module." So this patch renames some structures and updates some comments. It also renames "greybus_desc.h" to be "greybus_manifest.h", and renames greybus_new_device() to be greybus_new_module(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 2 +- drivers/staging/greybus/core.c | 30 ++++----- drivers/staging/greybus/greybus.h | 4 +- drivers/staging/greybus/greybus_desc.h | 99 ------------------------------ drivers/staging/greybus/greybus_manifest.h | 99 ++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 117 deletions(-) delete mode 100644 drivers/staging/greybus/greybus_desc.h create mode 100644 drivers/staging/greybus/greybus_manifest.h diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index a70404d7c1f1..aee47f50274d 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -17,7 +17,7 @@ #include #include #include "svc_msg.h" -#include "greybus_desc.h" +#include "greybus_manifest.h" #include "greybus.h" struct ap_msg { diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 76938cd00288..107e1fb75b8d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -344,16 +344,16 @@ static int create_cport(struct greybus_device *gdev, } /** - * greybus_new_device: + * greybus_new_module: * - * Pass in a buffer that _should_ be a set of greybus descriptor fields and spit - * out a greybus device structure. + * Pass in a buffer that _should_ contain a Greybus module manifest + * and spit out a greybus device structure. */ -struct greybus_device *greybus_new_device(struct device *parent, +struct greybus_device *greybus_new_module(struct device *parent, int module_number, u8 *data, int size) { struct greybus_device *gdev; - struct greybus_descriptor_block_header *block; + struct greybus_manifest_header *header; struct greybus_descriptor *desc; int retval; int overall_size; @@ -361,8 +361,8 @@ struct greybus_device *greybus_new_device(struct device *parent, u8 version_major; u8 version_minor; - /* we have to have at _least_ the block header */ - if (size <= sizeof(struct greybus_descriptor_block_header)) + /* we have to have at _least_ the manifest header */ + if (size <= sizeof(struct greybus_manifest_header)) return NULL; gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); @@ -379,21 +379,21 @@ struct greybus_device *greybus_new_device(struct device *parent, device_initialize(&gdev->dev); dev_set_name(&gdev->dev, "%d", module_number); - block = (struct greybus_descriptor_block_header *)data; - overall_size = le16_to_cpu(block->size); + header = (struct greybus_manifest_header *)data; + overall_size = le16_to_cpu(header->size); if (overall_size != size) { - dev_err(parent, "size != block header size, %d != %d\n", size, - overall_size); + dev_err(parent, "size != manifest header size, %d != %d\n", + size, overall_size); goto error; } - version_major = block->version_major; - version_minor = block->version_minor; + version_major = header->version_major; + version_minor = header->version_minor; // FIXME - check version major/minor here! - size -= sizeof(struct greybus_descriptor_block_header); - data += sizeof(struct greybus_descriptor_block_header); + size -= sizeof(struct greybus_manifest_header); + data += sizeof(struct greybus_manifest_header); while (size > 0) { desc = (struct greybus_descriptor *)data; desc_size = le16_to_cpu(desc->header.size); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2b96917c8730..a8d13b55de18 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -16,7 +16,7 @@ #include #include #include "greybus_id.h" -#include "greybus_desc.h" +#include "greybus_manifest.h" #define GREYBUS_DEVICE_ID_MATCH_DEVICE \ @@ -209,7 +209,7 @@ void greybus_deregister(struct greybus_driver *driver); int greybus_disabled(void); -struct greybus_device *greybus_new_device(struct device *parent, +struct greybus_device *greybus_new_module(struct device *parent, int module_number, u8 *data, int size); void greybus_remove_device(struct greybus_device *gdev); diff --git a/drivers/staging/greybus/greybus_desc.h b/drivers/staging/greybus/greybus_desc.h deleted file mode 100644 index 592b6517d43b..000000000000 --- a/drivers/staging/greybus/greybus_desc.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Greybus device descriptor definition - * - * Defined in the "Greybus Application Protocol" document. - * See that document for any details on these values and structures. - * - * Copyright 2014 Google Inc. - */ - -#ifndef __GREYBUS_DESC_H -#define __GREYBUS_DESC_H - -#pragma pack(push, 1) - -struct greybus_descriptor_block_header { - __le16 size; - __u8 version_major; - __u8 version_minor; -}; - -enum greybus_descriptor_type { - GREYBUS_TYPE_INVALID = 0x0000, - GREYBUS_TYPE_FUNCTION = 0x0001, - GREYBUS_TYPE_MODULE_ID = 0x0002, - GREYBUS_TYPE_SERIAL_NUMBER = 0x0003, - GREYBUS_TYPE_STRING = 0x0004, - GREYBUS_TYPE_CPORT = 0x0005, -}; - -struct greybus_descriptor_header { - __le16 size; - __le16 type; /* enum greybus_descriptor_type */ -}; - -enum greybus_function_class { - GREYBUS_FUNCTION_CONTROL = 0x00, - GREYBUS_FUNCTION_USB = 0x01, - GREYBUS_FUNCTION_GPIO = 0x02, - GREYBUS_FUNCTION_SPI = 0x03, - GREYBUS_FUNCTION_UART = 0x04, - GREYBUS_FUNCTION_PWM = 0x05, - GREYBUS_FUNCTION_I2S = 0x06, - GREYBUS_FUNCTION_I2C = 0x07, - GREYBUS_FUNCTION_SDIO = 0x08, - GREYBUS_FUNCTION_HID = 0x09, - GREYBUS_FUNCTION_DISPLAY = 0x0a, - GREYBUS_FUNCTION_CAMERA = 0x0b, - GREYBUS_FUNCTION_SENSOR = 0x0c, - GREYBUS_FUNCTION_VENDOR = 0xff, -}; - -struct greybus_descriptor_function { - __le16 number; - __le16 cport; - __u8 class; /* enum greybus_function_class */ - __u8 subclass; - __u8 protocol; - __u8 reserved; -}; - -struct greybus_descriptor_module_id { - __le16 vendor; - __le16 product; - __le16 version; - __u8 vendor_stringid; - __u8 product_stringid; -}; - -struct greybus_descriptor_serial_number { - __le64 serial_number; -}; - -struct greybus_descriptor_string { - __le16 length; - __u8 id; - __u8 string[0]; -}; - -struct greybus_descriptor_cport { - __le16 number; - __le16 size; - __u8 speed; // FIXME - __u8 reserved; -}; - -struct greybus_descriptor { - struct greybus_descriptor_header header; - union { - struct greybus_descriptor_function function; - struct greybus_descriptor_module_id module_id; - struct greybus_descriptor_serial_number serial_number; - struct greybus_descriptor_string string; - struct greybus_descriptor_cport cport; - }; -}; - -#pragma pack(pop) - -#endif /* __GREYBUS_DESC_H */ diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h new file mode 100644 index 000000000000..293451eee4d9 --- /dev/null +++ b/drivers/staging/greybus/greybus_manifest.h @@ -0,0 +1,99 @@ +/* + * Greybus device descriptor definition + * + * Defined in the "Greybus Application Protocol" document. + * See that document for any details on these values and structures. + * + * Copyright 2014 Google Inc. + */ + +#ifndef __GREYBUS_DESC_H +#define __GREYBUS_DESC_H + +#pragma pack(push, 1) + +struct greybus_manifest_header { + __le16 size; + __u8 version_major; + __u8 version_minor; +}; + +enum greybus_descriptor_type { + GREYBUS_TYPE_INVALID = 0x0000, + GREYBUS_TYPE_FUNCTION = 0x0001, + GREYBUS_TYPE_MODULE_ID = 0x0002, + GREYBUS_TYPE_SERIAL_NUMBER = 0x0003, + GREYBUS_TYPE_STRING = 0x0004, + GREYBUS_TYPE_CPORT = 0x0005, +}; + +struct greybus_descriptor_header { + __le16 size; + __le16 type; /* enum greybus_descriptor_type */ +}; + +enum greybus_function_class { + GREYBUS_FUNCTION_CONTROL = 0x00, + GREYBUS_FUNCTION_USB = 0x01, + GREYBUS_FUNCTION_GPIO = 0x02, + GREYBUS_FUNCTION_SPI = 0x03, + GREYBUS_FUNCTION_UART = 0x04, + GREYBUS_FUNCTION_PWM = 0x05, + GREYBUS_FUNCTION_I2S = 0x06, + GREYBUS_FUNCTION_I2C = 0x07, + GREYBUS_FUNCTION_SDIO = 0x08, + GREYBUS_FUNCTION_HID = 0x09, + GREYBUS_FUNCTION_DISPLAY = 0x0a, + GREYBUS_FUNCTION_CAMERA = 0x0b, + GREYBUS_FUNCTION_SENSOR = 0x0c, + GREYBUS_FUNCTION_VENDOR = 0xff, +}; + +struct greybus_descriptor_function { + __le16 number; + __le16 cport; + __u8 class; /* enum greybus_function_class */ + __u8 subclass; + __u8 protocol; + __u8 reserved; +}; + +struct greybus_descriptor_module_id { + __le16 vendor; + __le16 product; + __le16 version; + __u8 vendor_stringid; + __u8 product_stringid; +}; + +struct greybus_descriptor_serial_number { + __le64 serial_number; +}; + +struct greybus_descriptor_string { + __le16 length; + __u8 id; + __u8 string[0]; +}; + +struct greybus_descriptor_cport { + __le16 number; + __le16 size; + __u8 speed; // FIXME + __u8 reserved; +}; + +struct greybus_descriptor { + struct greybus_descriptor_header header; + union { + struct greybus_descriptor_function function; + struct greybus_descriptor_module_id module_id; + struct greybus_descriptor_serial_number serial_number; + struct greybus_descriptor_string string; + struct greybus_descriptor_cport cport; + }; +}; + +#pragma pack(pop) + +#endif /* __GREYBUS_DESC_H */ -- cgit v1.2.3-59-g8ed1b From badad68e3adc3d0b7cbfe66902cb21c97833e242 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Sep 2014 13:55:04 -0500 Subject: greybus: define struct greybus_manifest Define a structure that describes the entire greybus manifest. Adjust greybus_new_module() to use that, making it explicit that it's not just a header that's being provided to that function. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 16 ++++++++-------- drivers/staging/greybus/greybus_manifest.h | 5 +++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 107e1fb75b8d..95655e258e84 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -353,7 +353,7 @@ struct greybus_device *greybus_new_module(struct device *parent, int module_number, u8 *data, int size) { struct greybus_device *gdev; - struct greybus_manifest_header *header; + struct greybus_manifest *manifest; struct greybus_descriptor *desc; int retval; int overall_size; @@ -362,7 +362,7 @@ struct greybus_device *greybus_new_module(struct device *parent, u8 version_minor; /* we have to have at _least_ the manifest header */ - if (size <= sizeof(struct greybus_manifest_header)) + if (size <= sizeof(manifest->header)) return NULL; gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); @@ -379,21 +379,21 @@ struct greybus_device *greybus_new_module(struct device *parent, device_initialize(&gdev->dev); dev_set_name(&gdev->dev, "%d", module_number); - header = (struct greybus_manifest_header *)data; - overall_size = le16_to_cpu(header->size); + manifest = (struct greybus_manifest *)data; + overall_size = le16_to_cpu(manifest->header.size); if (overall_size != size) { dev_err(parent, "size != manifest header size, %d != %d\n", size, overall_size); goto error; } - version_major = header->version_major; - version_minor = header->version_minor; + version_major = manifest->header.version_major; + version_minor = manifest->header.version_minor; // FIXME - check version major/minor here! - size -= sizeof(struct greybus_manifest_header); - data += sizeof(struct greybus_manifest_header); + size -= sizeof(manifest->header); + data += sizeof(manifest->header); while (size > 0) { desc = (struct greybus_descriptor *)data; desc_size = le16_to_cpu(desc->header.size); diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 293451eee4d9..8b4acd3c2fc1 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -94,6 +94,11 @@ struct greybus_descriptor { }; }; +struct greybus_manifest { + struct greybus_manifest_header header; + struct greybus_descriptor descriptors[0]; +}; + #pragma pack(pop) #endif /* __GREYBUS_DESC_H */ -- cgit v1.2.3-59-g8ed1b From a5808add9a6cae290af60bb3b4efd2c9d3208588 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Sep 2014 13:55:06 -0500 Subject: greybus: call put_device() on error As soon as we've called device_initialize() we're required to call put_device() in order to drop our reference to the device structure. This was missed in the error path in greybus_new_module(). Fix that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 95655e258e84..3bb8f9cb55f8 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -440,6 +440,7 @@ struct greybus_device *greybus_new_module(struct device *parent, return gdev; error: + put_device(&gdev->dev); greybus_module_release(&gdev->dev); return NULL; } -- cgit v1.2.3-59-g8ed1b From a22e15a1fc8d46139c6ca4b17b81c14783d3f5a2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Sep 2014 13:55:07 -0500 Subject: greybus: interpret descriptor type properly The type field in a manifest descriptor header is in little endian format. Make sure we interpret it that way. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 3bb8f9cb55f8..61a4bc6687e6 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -398,7 +398,7 @@ struct greybus_device *greybus_new_module(struct device *parent, desc = (struct greybus_descriptor *)data; desc_size = le16_to_cpu(desc->header.size); - switch (desc->header.type) { + switch (le16_to_cpu(desc->header.type)) { case GREYBUS_TYPE_FUNCTION: retval = create_function(gdev, desc, desc_size); break; -- cgit v1.2.3-59-g8ed1b From 57fc0a110405ba305b525bede8cdf2e1b00b69a0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Sep 2014 13:55:08 -0500 Subject: greybus: validate descriptor sizes When interpreting a manifest descriptor header, don't assume there is enough space in the buffer to hold a descriptor header. Also, verify the remaining buffer is at least as big as the reported descriptor size. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 61a4bc6687e6..4b7034dc8558 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -395,8 +395,17 @@ struct greybus_device *greybus_new_module(struct device *parent, size -= sizeof(manifest->header); data += sizeof(manifest->header); while (size > 0) { + if (size < sizeof(desc->header)) { + dev_err(parent, "remaining size %d too small\n", size); + goto error; + } desc = (struct greybus_descriptor *)data; desc_size = le16_to_cpu(desc->header.size); + if (size < desc_size) { + dev_err(parent, "descriptor size %d too big\n", + desc_size); + goto error; + } switch (le16_to_cpu(desc->header.type)) { case GREYBUS_TYPE_FUNCTION: -- cgit v1.2.3-59-g8ed1b From e82bef42fdacd3434bd58738545a98c791fd64d1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Sep 2014 13:55:09 -0500 Subject: greybus: fix manifest parsing size bug The type-specific "create" routines that get called while parsing the descriptor entries in the module manifest assume the size they are provided is the size of their data portion only--not including the descriptor header. Compute this value in greybus_new_module(), and pass it to those functions rather than the full descriptor size. Move a few declarations to the innermost block that uses them. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 4b7034dc8558..f239b9694fb9 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -354,10 +354,8 @@ struct greybus_device *greybus_new_module(struct device *parent, { struct greybus_device *gdev; struct greybus_manifest *manifest; - struct greybus_descriptor *desc; int retval; int overall_size; - int desc_size; u8 version_major; u8 version_minor; @@ -395,6 +393,10 @@ struct greybus_device *greybus_new_module(struct device *parent, size -= sizeof(manifest->header); data += sizeof(manifest->header); while (size > 0) { + struct greybus_descriptor *desc; + u16 desc_size; + size_t data_size; + if (size < sizeof(desc->header)) { dev_err(parent, "remaining size %d too small\n", size); goto error; @@ -406,26 +408,27 @@ struct greybus_device *greybus_new_module(struct device *parent, desc_size); goto error; } + data_size = (size_t)desc_size - sizeof(desc->header); switch (le16_to_cpu(desc->header.type)) { case GREYBUS_TYPE_FUNCTION: - retval = create_function(gdev, desc, desc_size); + retval = create_function(gdev, desc, data_size); break; case GREYBUS_TYPE_MODULE_ID: - retval = create_module_id(gdev, desc, desc_size); + retval = create_module_id(gdev, desc, data_size); break; case GREYBUS_TYPE_SERIAL_NUMBER: - retval = create_serial_number(gdev, desc, desc_size); + retval = create_serial_number(gdev, desc, data_size); break; case GREYBUS_TYPE_STRING: - retval = create_string(gdev, desc, desc_size); + retval = create_string(gdev, desc, data_size); break; case GREYBUS_TYPE_CPORT: - retval = create_cport(gdev, desc, desc_size); + retval = create_cport(gdev, desc, data_size); break; case GREYBUS_TYPE_INVALID: -- cgit v1.2.3-59-g8ed1b From 3d5453261b6e03b670c408c4139bbcc9076896a8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 9 Sep 2014 17:16:54 -0700 Subject: greybus: pass appropriate type to create function Based on a patch from Alex Elder . Alex's original description: Every descriptor in a manifest is interpreted by greybus_new_module(). We call a function to do initialization based on descriptor's type. Since we know the type of the descriptor at that point, we can pass to the called function the actual sub-type it needs (i.e., the union member associated with the type). This allows those functions to be slightly simplified, and more focused. Also change some size variables to have size_t type, and simplify a few spots further by using sizeof(object) in place of sizeof(type). --- drivers/staging/greybus/core.c | 93 +++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index f239b9694fb9..647bc5baaef1 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -237,53 +237,50 @@ static const struct greybus_module_id fake_gb_id = { }; static int create_function(struct greybus_device *gdev, - struct greybus_descriptor *desc, int desc_size) + struct greybus_descriptor_function *function, + size_t desc_size) { - int header_size = sizeof(struct greybus_descriptor_function); - - if (desc_size != header_size) { - dev_err(gdev->dev.parent, "invalid function header size %d\n", + if (desc_size != sizeof(*function)) { + dev_err(gdev->dev.parent, "invalid function header size %zu\n", desc_size); return -EINVAL; } - memcpy(&gdev->function, &desc->function, header_size); + memcpy(&gdev->function, function, desc_size); return 0; } static int create_module_id(struct greybus_device *gdev, - struct greybus_descriptor *desc, int desc_size) + struct greybus_descriptor_module_id *module_id, + size_t desc_size) { - int header_size = sizeof(struct greybus_descriptor_module_id); - - if (desc_size != header_size) { - dev_err(gdev->dev.parent, "invalid module header size %d\n", + if (desc_size != sizeof(*module_id)) { + dev_err(gdev->dev.parent, "invalid module header size %zu\n", desc_size); return -EINVAL; } - memcpy(&gdev->module_id, &desc->module_id, header_size); + memcpy(&gdev->module_id, module_id, desc_size); return 0; } static int create_serial_number(struct greybus_device *gdev, - struct greybus_descriptor *desc, int desc_size) + struct greybus_descriptor_serial_number *serial_num, + size_t desc_size) { - int header_size = sizeof(struct greybus_descriptor_serial_number); - - if (desc_size != header_size) { - dev_err(gdev->dev.parent, "invalid serial number header size %d\n", + if (desc_size != sizeof(*serial_num)) { + dev_err(gdev->dev.parent, "invalid serial number header size %zu\n", desc_size); return -EINVAL; } - memcpy(&gdev->serial_number, &desc->serial_number, header_size); + memcpy(&gdev->serial_number, serial_num, desc_size); return 0; } static int create_string(struct greybus_device *gdev, - struct greybus_descriptor *desc, int desc_size) + struct greybus_descriptor_string *string, + size_t desc_size) { int string_size; - struct gdev_string *string; - int header_size = sizeof(struct greybus_descriptor_string); + struct gdev_string *gdev_string; if ((gdev->num_strings + 1) >= MAX_STRINGS_PER_MODULE) { dev_err(gdev->dev.parent, @@ -291,53 +288,53 @@ static int create_string(struct greybus_device *gdev, return -EINVAL; } - if (desc_size < header_size) { - dev_err(gdev->dev.parent, "invalid string header size %d\n", + if (desc_size < sizeof(*string)) { + dev_err(gdev->dev.parent, "invalid string header size %zu\n", desc_size); return -EINVAL; } - string_size = le16_to_cpu(desc->string.length); - string = kzalloc(sizeof(*string) + string_size + 1, GFP_KERNEL); - if (!string) + string_size = le16_to_cpu(string->length); + gdev_string = kzalloc(sizeof(*gdev_string) + string_size + 1, GFP_KERNEL); + if (!gdev_string) return -ENOMEM; - string->length = string_size; - string->id = desc->string.id; - memcpy(&string->string, &desc->string.string, string_size); + gdev_string->length = string_size; + gdev_string->id = string->id; + memcpy(&gdev_string->string, &string->string, string_size); - gdev->string[gdev->num_strings] = string; + gdev->string[gdev->num_strings] = gdev_string; gdev->num_strings++; return 0; } static int create_cport(struct greybus_device *gdev, - struct greybus_descriptor *desc, int desc_size) + struct greybus_descriptor_cport *cport, + size_t desc_size) { - struct gdev_cport *cport; - int header_size = sizeof(struct greybus_descriptor_cport); + struct gdev_cport *gdev_cport; if ((gdev->num_cports + 1) >= MAX_CPORTS_PER_MODULE) { dev_err(gdev->dev.parent, "too many cports for this module!\n"); return -EINVAL; } - if (desc_size != header_size) { + if (desc_size != sizeof(*cport)) { dev_err(gdev->dev.parent, - "invalid serial number header size %d\n", desc_size); + "invalid serial number header size %zu\n", desc_size); return -EINVAL; } - cport = kzalloc(sizeof(*cport), GFP_KERNEL); - if (!cport) + gdev_cport = kzalloc(sizeof(*gdev_cport), GFP_KERNEL); + if (!gdev_cport) return -ENOMEM; - cport->number = le16_to_cpu(desc->cport.number); - cport->size = le16_to_cpu(desc->cport.size); - cport->speed = desc->cport.speed; + gdev_cport->number = le16_to_cpu(cport->number); + gdev_cport->size = le16_to_cpu(cport->size); + gdev_cport->speed = cport->speed; - gdev->cport[gdev->num_cports] = cport; + gdev->cport[gdev->num_cports] = gdev_cport; gdev->num_cports++; return 0; @@ -412,23 +409,27 @@ struct greybus_device *greybus_new_module(struct device *parent, switch (le16_to_cpu(desc->header.type)) { case GREYBUS_TYPE_FUNCTION: - retval = create_function(gdev, desc, data_size); + retval = create_function(gdev, &desc->function, + data_size); break; case GREYBUS_TYPE_MODULE_ID: - retval = create_module_id(gdev, desc, data_size); + retval = create_module_id(gdev, &desc->module_id, + data_size); break; case GREYBUS_TYPE_SERIAL_NUMBER: - retval = create_serial_number(gdev, desc, data_size); + retval = create_serial_number(gdev, + &desc->serial_number, + data_size); break; case GREYBUS_TYPE_STRING: - retval = create_string(gdev, desc, data_size); + retval = create_string(gdev, &desc->string, data_size); break; case GREYBUS_TYPE_CPORT: - retval = create_cport(gdev, desc, data_size); + retval = create_cport(gdev, &desc->cport, data_size); break; case GREYBUS_TYPE_INVALID: -- cgit v1.2.3-59-g8ed1b From a6294fe8493cf0c98480f42d3057da2f98cc4ec1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 10 Sep 2014 17:12:20 -0700 Subject: greybus: fix endian issue in sysfs.c --- drivers/staging/greybus/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 2393a6a0b678..a24a9edb9d77 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -145,7 +145,7 @@ static ssize_t serial_number_show(struct device *dev, struct greybus_device *gdev = to_greybus_device(dev); return sprintf(buf, "%llX\n", - (unsigned long long)gdev->serial_number.serial_number); + (unsigned long long)le64_to_cpu(gdev->serial_number.serial_number)); } static DEVICE_ATTR_RO(serial_number); -- cgit v1.2.3-59-g8ed1b From f91121b48fda4bba7eb298c51e9a871ec9c538d7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Sep 2014 08:22:06 -0700 Subject: greybus: Fix build errors on older kernels. Thanks to Marti for pointing out the code didn't build properly on 3.10. Added kernel_ver.h to handle any api mis-matches between the code and older kernel versions. --- drivers/staging/greybus/gpio-gb.c | 5 +++-- drivers/staging/greybus/kernel_ver.h | 22 ++++++++++++++++++++++ drivers/staging/greybus/sysfs.c | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/greybus/kernel_ver.h diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 3428220c5158..bcf65087e8e5 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include "greybus.h" struct gb_gpio_device { @@ -90,10 +90,11 @@ int gb_gpio_probe(struct greybus_device *gdev, void gb_gpio_disconnect(struct greybus_device *gdev) { struct gb_gpio_device *gb_gpio_dev; + int retval; gb_gpio_dev = gdev->gb_gpio_dev; - gpiochip_remove(&gb_gpio_dev->chip); + retval = gpiochip_remove(&gb_gpio_dev->chip); kfree(gb_gpio_dev); } diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h new file mode 100644 index 000000000000..40cc2e8af525 --- /dev/null +++ b/drivers/staging/greybus/kernel_ver.h @@ -0,0 +1,22 @@ +/* + * Greybus kernel "version" glue logic. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + * + * Backports of newer kernel apis to allow the code to build properly on older + * kernel versions. Remove this file when merging to upstream, it should not be + * needed at all + */ + +#ifndef __GREYBUS_KERNEL_VER_H +#define __GREYBUS_KERNEL_VER_H + +#ifndef DEVICE_ATTR_RO +#define DEVICE_ATTR_RO(_name) \ + struct device_attribute dev_attr_##_name = __ATTR_RO(_name) +#endif + + +#endif /* __GREYBUS_KERNEL_VER_H */ diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 2393a6a0b678..c02ca552c1a0 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -18,6 +18,8 @@ #include "greybus.h" +#include "kernel_ver.h" + /* Function fields */ #define greybus_function_attr(field) \ static ssize_t function_##field##_show(struct device *dev, \ -- cgit v1.2.3-59-g8ed1b From 8c53e073f723cec46270cbc98dd690dd209f4fa2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 12 Sep 2014 20:47:11 -0700 Subject: greybus: AP: move a bunch of svc message handling logic into ap.c Add a send_svc_msg() callback to the host driver. hook up ES1 driver to send control USB messages as it's SVC transport. --- drivers/staging/greybus/ap.c | 181 +++++++++++++++++++++++++++++++++-- drivers/staging/greybus/es1-ap-usb.c | 168 ++++---------------------------- drivers/staging/greybus/greybus.h | 9 +- 3 files changed, 195 insertions(+), 163 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index aee47f50274d..53ad66c6c3d7 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -32,6 +32,127 @@ static spinlock_t ap_msg_list_lock; static struct task_struct *ap_thread; static wait_queue_head_t ap_wait; +static struct svc_msg *svc_msg_alloc(enum svc_function_type type) +{ + struct svc_msg *svc_msg; + + svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL); + if (!svc_msg) + return NULL; + + // FIXME - verify we are only sending message types we should be + svc_msg->header.type = type; + return svc_msg; +} + +static void svc_msg_free(struct svc_msg *svc_msg) +{ + kfree(svc_msg); +} + +static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + int retval; + + // FIXME - Do we need to do more than just pass it to the hd and then + // free it? + retval = hd->driver->send_svc_msg(svc_msg, hd); + + svc_msg_free(svc_msg); + return retval; +} + + +static void svc_handshake(struct svc_function_handshake *handshake, + struct greybus_host_device *hd) +{ + struct svc_msg *svc_msg; + + /* A new SVC communication channel, let's verify it was for us */ + if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) { + /* we don't know what to do with this, log it and return */ + dev_dbg(&hd->dev, "received invalid handshake type %d\n", + handshake->handshake_type); + return; + } + + /* Send back a AP_HELLO message */ + svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE); + if (!svc_msg) + return; + + svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; + svc_msg_send(svc_msg, hd); +} + +static void svc_management(struct svc_function_unipro_management *management, + struct greybus_host_device *hd) +{ + /* What? An AP should not get this message */ + dev_err(&hd->dev, "Got an svc management message???\n"); +} + +static void svc_hotplug(struct svc_function_hotplug *hotplug, + struct greybus_host_device *hd) +{ + u8 module_id = hotplug->module_id; + + switch (hotplug->hotplug_event) { + case SVC_HOTPLUG_EVENT: + dev_dbg(&hd->dev, "module id %d added\n", module_id); + // FIXME - add the module to the system + break; + + case SVC_HOTUNPLUG_EVENT: + dev_dbg(&hd->dev, "module id %d removed\n", module_id); + // FIXME - remove the module from the system + break; + + default: + dev_err(&hd->dev, "received invalid hotplug message type %d\n", + hotplug->hotplug_event); + break; + } +} + +static void svc_ddb(struct svc_function_ddb *ddb, + struct greybus_host_device *hd) +{ + /* What? An AP should not get this message */ + dev_err(&hd->dev, "Got an svc DDB message???\n"); +} + +static void svc_power(struct svc_function_power *power, + struct greybus_host_device *hd) +{ + u8 module_id = power->module_id; + + if (power->power_type != SVC_POWER_BATTERY_STATUS) { + dev_err(&hd->dev, "received invalid power type %d\n", + power->power_type); + return; + } + + dev_dbg(&hd->dev, "power status for module id %d is %d\n", + module_id, power->status.status); + + // FIXME - do something with the power information, like update our + // battery information... +} + +static void svc_epm(struct svc_function_epm *epm, + struct greybus_host_device *hd) +{ + /* What? An AP should not get this message */ + dev_err(&hd->dev, "Got an EPM message???\n"); +} + +static void svc_suspend(struct svc_function_suspend *suspend, + struct greybus_host_device *hd) +{ + /* What? An AP should not get this message */ + dev_err(&hd->dev, "Got an suspend message???\n"); +} static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) { @@ -43,16 +164,62 @@ static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) svc_msg = (struct svc_msg *)ap_msg->data; - // FIXME - put in correct version numbers - if ((svc_msg->header.version_major != 0x00) && - (svc_msg->header.version_minor != 0x00)) + /* Verify the version is something we can handle with this code */ + if ((svc_msg->header.version_major != GREYBUS_VERSION_MAJOR) && + (svc_msg->header.version_minor != GREYBUS_VERSION_MINOR)) return NULL; return svc_msg; } +static void process_ap_message(struct ap_msg *ap_msg) +{ + struct svc_msg *svc_msg; + struct greybus_host_device *hd; + + /* Turn the "raw" data into a real message */ + svc_msg = convert_ap_message(ap_msg); + if (!svc_msg) { + // FIXME log an error??? + return; + } + + hd = ap_msg->hd; + + /* Pass the message to the host controller */ +// ap_msg->hd->driver->ap_msg(svc_msg, ap_msg->hd); + + /* Look at the message to figure out what to do with it */ + switch (svc_msg->header.type) { + case SVC_FUNCTION_HANDSHAKE: + svc_handshake(&svc_msg->handshake, hd); + break; + case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT: + svc_management(&svc_msg->management, hd); + break; + case SVC_FUNCTION_HOTPLUG: + svc_hotplug(&svc_msg->hotplug, hd); + break; + case SVC_FUNCTION_DDB: + svc_ddb(&svc_msg->ddb, hd); + break; + case SVC_FUNCTION_POWER: + svc_power(&svc_msg->power, hd); + break; + case SVC_FUNCTION_EPM: + svc_epm(&svc_msg->epm, hd); + break; + case SVC_FUNCTION_SUSPEND: + svc_suspend(&svc_msg->suspend, hd); + break; + default: + dev_err(&hd->dev, "received invalid SVC message type %d\n", + svc_msg->header.type); + } +} + static struct ap_msg *get_ap_msg(void) { struct ap_msg *ap_msg; @@ -71,7 +238,6 @@ static struct ap_msg *get_ap_msg(void) static int ap_process_loop(void *data) { struct ap_msg *ap_msg; - struct svc_msg *svc_msg; while (!kthread_should_stop()) { wait_event_interruptible(ap_wait, kthread_should_stop()); @@ -84,12 +250,7 @@ static int ap_process_loop(void *data) if (!ap_msg) continue; - /* Turn the "raw" data into a real message */ - svc_msg = convert_ap_message(ap_msg); - if (svc_msg) { - /* Pass the message to the host controller */ - ap_msg->hd->driver->ap_msg(svc_msg, ap_msg->hd); - } + process_ap_message(ap_msg); /* clean the message up */ kfree(ap_msg->data); diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 767981a248c8..f32023e7f22e 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -83,167 +83,33 @@ static void free_gbuf(struct gbuf *gbuf) kfree(buffer); } -static struct svc_msg *svc_msg_alloc(enum svc_function_type type) +#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +static int send_svc_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd) { - struct svc_msg *svc_msg; - - svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL); - if (!svc_msg) - return NULL; - - // FIXME - verify we are only sending message types we should be - svc_msg->header.type = type; - return svc_msg; -} - -static void svc_msg_free(struct svc_msg *svc_msg) -{ - kfree(svc_msg); -} - -static int svc_msg_send(struct svc_msg *svc_msg) -{ - // FIXME - Do something with this message! + struct es1_ap_dev *es1 = hd_to_es1(hd); + int retval; + /* SVC messages go down our control pipe */ + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + 0x01, /* vendor request AP message */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER, + 0x00, 0x00, + (char *)svc_msg, + sizeof(*svc_msg), + ES1_TIMEOUT); + if (retval != sizeof(*svc_msg)) + return retval; - svc_msg_free(svc_msg); return 0; } - -static void svc_handshake(struct svc_function_handshake *handshake, - struct es1_ap_dev *es1) -{ - struct svc_msg *svc_msg; - - /* A new SVC communication channel, let's verify it was for us */ - if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) { - /* we don't know what to do with this, log it and return */ - dev_dbg(&es1->usb_intf->dev, - "received invalid handshake type %d\n", - handshake->handshake_type); - return; - } - - /* Send back a AP_HELLO message */ - svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE); - if (!svc_msg) - return; - - svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; - svc_msg_send(svc_msg); -} - -static void svc_management(struct svc_function_unipro_management *management, - struct es1_ap_dev *es1) -{ - /* What? An AP should not get this message */ - dev_err(&es1->usb_intf->dev, "Got an svc management message???\n"); -} - -static void svc_hotplug(struct svc_function_hotplug *hotplug, - struct es1_ap_dev *es1) -{ - u8 module_id = hotplug->module_id; - - switch (hotplug->hotplug_event) { - case SVC_HOTPLUG_EVENT: - dev_dbg(&es1->usb_intf->dev, "module id %d added\n", - module_id); - // FIXME - add the module to the system - break; - - case SVC_HOTUNPLUG_EVENT: - dev_dbg(&es1->usb_intf->dev, "module id %d removed\n", - module_id); - // FIXME - remove the module from the system - break; - - default: - dev_err(&es1->usb_intf->dev, "received invalid hotplug message type %d\n", - hotplug->hotplug_event); - break; - } -} - -static void svc_ddb(struct svc_function_ddb *ddb, struct es1_ap_dev *es1) -{ - /* What? An AP should not get this message */ - dev_err(&es1->usb_intf->dev, "Got an svc DDB message???\n"); -} - -static void svc_power(struct svc_function_power *power, struct es1_ap_dev *es1) -{ - u8 module_id = power->module_id; - - if (power->power_type != SVC_POWER_BATTERY_STATUS) { - dev_err(&es1->usb_intf->dev, "received invalid power type %d\n", - power->power_type); - return; - } - - dev_dbg(&es1->usb_intf->dev, "power status for module id %d is %d\n", - module_id, power->status.status); - - // FIXME - do something with the power information, like update our - // battery information... -} - -static void svc_epm(struct svc_function_epm *epm, struct es1_ap_dev *es1) -{ - /* What? An AP should not get this message */ - dev_err(&es1->usb_intf->dev, "Got an EPM message???\n"); -} - -static void svc_suspend(struct svc_function_suspend *suspend, - struct es1_ap_dev *es1) -{ - /* What? An AP should not get this message */ - dev_err(&es1->usb_intf->dev, "Got an suspend message???\n"); -} - -/* Main message loop for ap messages */ -/* Odds are, most of this logic can move to core.c someday, but as we only have - * one host controller driver for now, let's leave it here */ -static void ap_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - - /* Look at the message to figure out what to do with it */ - switch (svc_msg->header.type) { - case SVC_FUNCTION_HANDSHAKE: - svc_handshake(&svc_msg->handshake, es1); - break; - case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT: - svc_management(&svc_msg->management, es1); - break; - case SVC_FUNCTION_HOTPLUG: - svc_hotplug(&svc_msg->hotplug, es1); - break; - case SVC_FUNCTION_DDB: - svc_ddb(&svc_msg->ddb, es1); - break; - case SVC_FUNCTION_POWER: - svc_power(&svc_msg->power, es1); - break; - case SVC_FUNCTION_EPM: - svc_epm(&svc_msg->epm, es1); - break; - case SVC_FUNCTION_SUSPEND: - svc_suspend(&svc_msg->suspend, es1); - break; - default: - dev_err(&es1->usb_intf->dev, "received invalid SVC message type %d\n", - svc_msg->header.type); - } -} - - static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .alloc_gbuf = alloc_gbuf, .free_gbuf = free_gbuf, - .ap_msg = ap_msg, + .send_svc_msg = send_svc_msg, }; /* Callback for when we get a SVC message */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index a8d13b55de18..2d7972f29118 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -19,6 +19,10 @@ #include "greybus_manifest.h" +/* Matches up with the Greybus Protocol specification document */ +#define GREYBUS_VERSION_MAJOR 0x01 +#define GREYBUS_VERSION_MINOR 0x00 + #define GREYBUS_DEVICE_ID_MATCH_DEVICE \ (GREYBUS_DEVICE_ID_MATCH_VENDOR | GREYBUS_DEVICE_ID_MATCH_PRODUCT) @@ -104,11 +108,12 @@ struct greybus_host_driver { int (*start)(struct greybus_host_device *hd); int (*alloc_gbuf)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); void (*free_gbuf)(struct gbuf *gbuf); - void (*ap_msg)(struct svc_msg *svc_msg, struct greybus_host_device *hd); + int (*send_svc_msg)(struct svc_msg *svc_msg, struct greybus_host_device *hd); }; struct greybus_host_device { - struct kref kref; + struct device dev; + struct kref kref; const struct greybus_host_driver *driver; unsigned long hd_priv_size; -- cgit v1.2.3-59-g8ed1b From 112997324db001eed32ec67909d5c8eed74a554d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 12 Sep 2014 21:17:37 -0700 Subject: greybus: es1: add the start of cport urb handling. --- drivers/staging/greybus/es1-ap-usb.c | 122 +++++++++++++++++++++++++++++++++-- drivers/staging/greybus/greybus.h | 2 + 2 files changed, 119 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index f32023e7f22e..c37deb1c6515 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -19,6 +19,19 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); +/* + * Number of CPort IN urbs in flight at any point in time. + * Adjust if we are having stalls in the USB buffer due to not enough urbs in + * flight. + */ +#define NUM_CPORT_IN_URB 4 + +/* Number of CPort OUT urbs in flight at any point in time. + * Adjust if we get messages saying we are out of urbs in the system log. + */ +#define NUM_CPORT_OUT_URB 8 + + struct es1_ap_dev { struct usb_device *usb_dev; struct usb_interface *usb_intf; @@ -30,6 +43,10 @@ struct es1_ap_dev { __u8 cport_out_endpoint; /* bulk out for CPort data */ u8 *svc_buffer; /* buffer for SVC messages coming in */ struct urb *svc_urb; /* urb for SVC messages coming in */ + struct urb *cport_in_urb[NUM_CPORT_IN_URB]; /* CPort IN urbs */ + u8 *cport_in_buffer[NUM_CPORT_IN_URB]; /* CPort IN buffers */ + struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; /* CPort OUT urbs */ + u8 cport_out_urb_busy[NUM_CPORT_OUT_URB]; /* CPort OUT urb busy marker */ }; static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) @@ -37,6 +54,8 @@ static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) return (struct es1_ap_dev *)(hd->hd_priv); } +static void cport_out_callback(struct urb *urb); + /* * Allocate the actual buffer for this gbuf and device and cport * @@ -67,7 +86,8 @@ static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) gbuf->transfer_buffer = &buffer[1]; gbuf->transfer_buffer_length = size; - gbuf->hdpriv = es1; /* really, we could do something else here... */ + /* When we send the gbuf, we need this pointer to be here */ + gbuf->hdpriv = es1; return 0; } @@ -105,11 +125,44 @@ static int send_svc_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd) return 0; } +static struct urb *next_free_urb(struct es1_ap_dev *es1) +{ + // FIXME + return NULL; +} + +static int send_gbuf(struct gbuf *gbuf, struct greybus_host_device *hd, + gfp_t gfp_mask) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + int retval; + u8 *transfer_buffer; + u8 *buffer; + struct urb *urb; + + transfer_buffer = gbuf->transfer_buffer; + buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ + + /* Find a free urb */ + urb = next_free_urb(es1); + if (!urb) + return -ENOMEM; + + usb_fill_bulk_urb(urb, udev, + usb_sndbulkpipe(udev, es1->cport_out_endpoint), + buffer, gbuf->transfer_buffer_length + 1, + cport_out_callback, gbuf); + retval = usb_submit_urb(urb, gfp_mask); + return retval; +} + static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .alloc_gbuf = alloc_gbuf, .free_gbuf = free_gbuf, .send_svc_msg = send_svc_msg, + .send_gbuf = send_gbuf, }; /* Callback for when we get a SVC message */ @@ -149,7 +202,7 @@ exit: dev_err(dev, "Can not submit urb for AP data: %d\n", retval); } -void cport_in_callback(struct urb *urb) +static void cport_in_callback(struct urb *urb) { struct device *dev = &urb->dev->dev; int status = urb->status; @@ -176,7 +229,7 @@ exit: return; } -void cport_out_callback(struct urb *urb) +static void cport_out_callback(struct urb *urb) { struct device *dev = &urb->dev->dev; int status = urb->status; @@ -278,7 +331,7 @@ static int ap_probe(struct usb_interface *interface, es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); if (!es1->svc_urb) - goto error_urb; + goto error_int_urb; usb_fill_int_urb(es1->svc_urb, udev, usb_rcvintpipe(udev, es1->svc_endpoint), @@ -288,11 +341,56 @@ static int ap_probe(struct usb_interface *interface, if (retval) goto error_submit_urb; + /* Allocate buffers for our cport in messages and start them up */ + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb; + u8 *buffer; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error_bulk_in_urb; + buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buffer) + goto error_bulk_in_urb; + + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, es1->cport_in_endpoint), + buffer, PAGE_SIZE, cport_in_callback, es1); + es1->cport_in_urb[i] = urb; + es1->cport_in_buffer[i] = buffer; + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) + goto error_bulk_in_urb; + } + + /* Allocate urbs for our CPort OUT messages */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + struct urb *urb; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error_bulk_out_urb; + + es1->cport_out_urb[i] = urb; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ + } + return 0; +error_bulk_out_urb: + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) + usb_free_urb(es1->cport_out_urb[i]); + +error_bulk_in_urb: + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + usb_kill_urb(es1->cport_in_urb[i]); + usb_free_urb(es1->cport_in_urb[i]); + kfree(es1->cport_in_buffer[i]); + } + error_submit_urb: usb_free_urb(es1->svc_urb); -error_urb: +error_int_urb: kfree(es1->svc_buffer); error: greybus_remove_hd(es1->hd); @@ -302,10 +400,24 @@ error: static void ap_disconnect(struct usb_interface *interface) { struct es1_ap_dev *es1; + int i; es1 = usb_get_intfdata(interface); + if (!es1) + return; /* Tear down everything! */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + usb_kill_urb(es1->cport_out_urb[i]); + usb_free_urb(es1->cport_out_urb[i]); + } + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + usb_kill_urb(es1->cport_in_urb[i]); + usb_free_urb(es1->cport_in_urb[i]); + kfree(es1->cport_in_buffer[i]); + } + usb_kill_urb(es1->svc_urb); usb_free_urb(es1->svc_urb); usb_put_dev(es1->usb_dev); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2d7972f29118..573274b6267d 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -109,6 +109,8 @@ struct greybus_host_driver { int (*alloc_gbuf)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); void (*free_gbuf)(struct gbuf *gbuf); int (*send_svc_msg)(struct svc_msg *svc_msg, struct greybus_host_device *hd); + int (*send_gbuf)(struct gbuf *gbuf, struct greybus_host_device *hd, + gfp_t gfp_mask); }; struct greybus_host_device { -- cgit v1.2.3-59-g8ed1b From 0dad95dc372057e628f8aced70fa0b8493896b1a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 09:54:35 -0700 Subject: greybus: es1: allocate cport out urbs properly --- drivers/staging/greybus/es1-ap-usb.c | 39 +++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index c37deb1c6515..cf2987ef94e9 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -46,7 +46,8 @@ struct es1_ap_dev { struct urb *cport_in_urb[NUM_CPORT_IN_URB]; /* CPort IN urbs */ u8 *cport_in_buffer[NUM_CPORT_IN_URB]; /* CPort IN buffers */ struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; /* CPort OUT urbs */ - u8 cport_out_urb_busy[NUM_CPORT_OUT_URB]; /* CPort OUT urb busy marker */ + bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; /* CPort OUT urb busy marker */ + spinlock_t cport_out_urb_lock; /* locks list of cport out urbs */ }; static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) @@ -125,10 +126,37 @@ static int send_svc_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd) return 0; } -static struct urb *next_free_urb(struct es1_ap_dev *es1) +static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) { - // FIXME - return NULL; + struct urb *urb = NULL; + unsigned long flags; + int i; + + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + + /* Look in our pool of allocated urbs first, as that's the "fastest" */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (es1->cport_out_urb_busy[i] == false) { + es1->cport_out_urb_busy[i] = true; + urb = es1->cport_out_urb[i]; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + if (urb) + return urb; + + /* + * Crap, pool is empty, complain to the syslog and go allocate one + * dynamically as we have to succeed. + */ + dev_err(&es1->usb_dev->dev, + "No free CPort OUT urbs, having to dynamically allocate one!\n"); + urb = usb_alloc_urb(0, gfp_mask); + if (!urb) + return NULL; + + return urb; } static int send_gbuf(struct gbuf *gbuf, struct greybus_host_device *hd, @@ -145,7 +173,7 @@ static int send_gbuf(struct gbuf *gbuf, struct greybus_host_device *hd, buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ /* Find a free urb */ - urb = next_free_urb(es1); + urb = next_free_urb(es1, gfp_mask); if (!urb) return -ENOMEM; @@ -289,6 +317,7 @@ static int ap_probe(struct usb_interface *interface, es1->hd = hd; es1->usb_intf = interface; es1->usb_dev = udev; + spin_lock_init(&es1->cport_out_urb_lock); usb_set_intfdata(interface, es1); /* Control endpoint is the pipe to talk to this AP, so save it off */ -- cgit v1.2.3-59-g8ed1b From 9c8d3afdb58cf96a0e784e2e0a62923fe8855e4d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 11:09:35 -0700 Subject: greybus: es1: handle cport data in and out --- drivers/staging/greybus/ap.c | 6 ++++ drivers/staging/greybus/es1-ap-usb.c | 58 ++++++++++++++++++++++++++++++++---- drivers/staging/greybus/gbuf.c | 9 ++++++ drivers/staging/greybus/greybus.h | 3 ++ 4 files changed, 71 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 53ad66c6c3d7..e606a4557ab2 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -298,6 +298,12 @@ int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd) } EXPORT_SYMBOL_GPL(gb_new_ap_msg); +void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, + size_t length) +{ + // FIXME - implement... +} +EXPORT_SYMBOL_GPL(greybus_cport_in_data); int gb_thread_init(void) { diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index cf2987ef94e9..6635194e4a9a 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -233,7 +233,11 @@ exit: static void cport_in_callback(struct urb *urb) { struct device *dev = &urb->dev->dev; + struct es1_ap_dev *es1 = urb->context; int status = urb->status; + int retval; + u8 cport; + u8 *data; switch (status) { case 0: @@ -252,15 +256,40 @@ static void cport_in_callback(struct urb *urb) goto exit; } - // FIXME - handle the CPort in data + /* The size has to be more then just an "empty" transfer */ + if (urb->actual_length <= 2) { + dev_err(dev, "%s: \"short\" cport in transfer of %d bytes?\n", + __func__, urb->actual_length); + goto exit; + } + + /* + * The CPort number is the first byte of the data stream, the rest of + * the stream is "real" data + */ + data = urb->transfer_buffer; + cport = data[0]; + data = &data[1]; + + /* Pass this data to the greybus core */ + greybus_cport_in_data(es1->hd, cport, data, urb->actual_length - 1); + exit: - return; + /* put our urb back in the request pool */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "%s: error %d in submitting urb.\n", + __func__, retval); } static void cport_out_callback(struct urb *urb) { struct device *dev = &urb->dev->dev; + struct gbuf *gbuf = urb->context; + struct es1_ap_dev *es1 = gbuf->hdpriv; + unsigned long flags; int status = urb->status; + int i; switch (status) { case 0: @@ -273,15 +302,34 @@ static void cport_out_callback(struct urb *urb) case -ESHUTDOWN: case -EILSEQ: /* device is gone, stop sending */ - return; + goto exit; default: dev_err(dev, "%s: unknown status %d\n", __func__, status); goto exit; } - // FIXME - handle the CPort out data callback + // FIXME - do we care about errors going back up? + + /* Tell the core the gbuf is properly sent */ + greybus_gbuf_finished(gbuf); + exit: - return; + /* + * See if this was an urb in our pool, if so mark it "free", otherwise we + * need to free it ourselves. + */ + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (urb == es1->cport_out_urb[i]) { + es1->cport_out_urb_busy[i] = false; + urb = NULL; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + if (urb) + usb_free_urb(urb); + } /* diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 1bf92262a629..e12a625131de 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -98,10 +98,19 @@ EXPORT_SYMBOL_GPL(greybus_get_gbuf); int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags) { + // FIXME - implement return -ENOMEM; } int greybus_kill_gbuf(struct gbuf *gbuf) { + // FIXME - implement return -ENOMEM; } + +/* Can be called in interrupt context, do the work and get out of here */ +void greybus_gbuf_finished(struct gbuf *gbuf) +{ + // FIXME - implement +} +EXPORT_SYMBOL_GPL(greybus_gbuf_finished); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 573274b6267d..1badfa8cfd36 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -126,6 +126,9 @@ struct greybus_host_device { struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver, struct device *parent); void greybus_remove_hd(struct greybus_host_device *hd); +void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, + size_t length); +void greybus_gbuf_finished(struct gbuf *gbuf); /* Increase these values if needed */ -- cgit v1.2.3-59-g8ed1b From 88929c593d38c0855baddc5192acb08f244b59e2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 11:35:02 -0700 Subject: greybus: ap: convert to workqueue from thread --- drivers/staging/greybus/ap.c | 65 ++++++++++---------------------------------- 1 file changed, 14 insertions(+), 51 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index e606a4557ab2..e9aa701ad988 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -24,13 +24,10 @@ struct ap_msg { u8 *data; size_t size; struct greybus_host_device *hd; - struct list_head list; + struct work_struct event; }; -static LIST_HEAD(ap_msg_list); -static spinlock_t ap_msg_list_lock; -static struct task_struct *ap_thread; -static wait_queue_head_t ap_wait; +static struct workqueue_struct *ap_workqueue; static struct svc_msg *svc_msg_alloc(enum svc_function_type type) { @@ -220,49 +217,22 @@ static void process_ap_message(struct ap_msg *ap_msg) } -static struct ap_msg *get_ap_msg(void) +static void ap_process_event(struct work_struct *work) { struct ap_msg *ap_msg; - unsigned long flags; - spin_lock_irqsave(&ap_msg_list_lock, flags); + ap_msg = container_of(work, struct ap_msg, event); - ap_msg = list_first_entry_or_null(&ap_msg_list, struct ap_msg, list); - if (ap_msg != NULL) - list_del(&ap_msg->list); - spin_unlock_irqrestore(&ap_msg_list_lock, flags); + process_ap_message(ap_msg); - return ap_msg; -} - -static int ap_process_loop(void *data) -{ - struct ap_msg *ap_msg; - - while (!kthread_should_stop()) { - wait_event_interruptible(ap_wait, kthread_should_stop()); - - if (kthread_should_stop()) - break; - - /* Get some data off of the ap list and process it */ - ap_msg = get_ap_msg(); - if (!ap_msg) - continue; - - process_ap_message(ap_msg); - - /* clean the message up */ - kfree(ap_msg->data); - kfree(ap_msg); - } - return 0; + /* clean the message up */ + kfree(ap_msg->data); + kfree(ap_msg); } int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd) { struct ap_msg *ap_msg; - unsigned long flags; /* * Totally naive copy the message into a new structure that we slowly @@ -287,12 +257,8 @@ int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd) ap_msg->size = size; ap_msg->hd = hd; - spin_lock_irqsave(&ap_msg_list_lock, flags); - list_add(&ap_msg->list, &ap_msg_list); - spin_unlock_irqrestore(&ap_msg_list_lock, flags); - - /* kick our thread to handle the message */ - wake_up_interruptible(&ap_wait); + INIT_WORK(&ap_msg->event, ap_process_event); + queue_work(ap_workqueue, &ap_msg->event); return 0; } @@ -307,19 +273,16 @@ EXPORT_SYMBOL_GPL(greybus_cport_in_data); int gb_thread_init(void) { - init_waitqueue_head(&ap_wait); - spin_lock_init(&ap_msg_list_lock); - - ap_thread = kthread_run(ap_process_loop, NULL, "greybus_ap"); - if (IS_ERR(ap_thread)) - return PTR_ERR(ap_thread); + ap_workqueue = alloc_workqueue("greybus_ap", 0, 1); + if (!ap_workqueue) + return -ENOMEM; return 0; } void gb_thread_destroy(void) { - kthread_stop(ap_thread); + destroy_workqueue(ap_workqueue); } -- cgit v1.2.3-59-g8ed1b From b57b06241b0177a36a1c951b25fdead3bc1d87ba Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 12:18:09 -0700 Subject: greybus: ap: cleanup of process ap message loop --- drivers/staging/greybus/ap.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index e9aa701ad988..f92066625c1e 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -169,10 +169,14 @@ static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) return svc_msg; } -static void process_ap_message(struct ap_msg *ap_msg) +static void ap_process_event(struct work_struct *work) { struct svc_msg *svc_msg; struct greybus_host_device *hd; + struct ap_msg *ap_msg; + + ap_msg = container_of(work, struct ap_msg, event); + hd = ap_msg->hd; /* Turn the "raw" data into a real message */ svc_msg = convert_ap_message(ap_msg); @@ -181,11 +185,6 @@ static void process_ap_message(struct ap_msg *ap_msg) return; } - hd = ap_msg->hd; - - /* Pass the message to the host controller */ -// ap_msg->hd->driver->ap_msg(svc_msg, ap_msg->hd); - /* Look at the message to figure out what to do with it */ switch (svc_msg->header.type) { case SVC_FUNCTION_HANDSHAKE: @@ -214,17 +213,6 @@ static void process_ap_message(struct ap_msg *ap_msg) svc_msg->header.type); } - -} - -static void ap_process_event(struct work_struct *work) -{ - struct ap_msg *ap_msg; - - ap_msg = container_of(work, struct ap_msg, event); - - process_ap_message(ap_msg); - /* clean the message up */ kfree(ap_msg->data); kfree(ap_msg); -- cgit v1.2.3-59-g8ed1b From ac7171ea266d6978bc9eeb4db5e60847237cd1a5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 12:39:23 -0700 Subject: greybus: Makefile: add 'check' option to run sparse with endian checks enabled --- drivers/staging/greybus/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 114089ada9f7..0550e219af3d 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -21,6 +21,9 @@ all: module module: $(MAKE) -C $(KERNELDIR) M=$(PWD) +check: + $(MAKE) -C $(KERNELDIR) M=$(PWD) C=2 CF="-D__CHECK_ENDIAN__" + clean: rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c rm -f Module.markers Module.symvers modules.order -- cgit v1.2.3-59-g8ed1b From 082570b0eebdd108c6b50b6c1299cf6d02a6ad82 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 16:15:07 -0700 Subject: greybus: es1 endpoint descriptor: minor fixes to get the config right --- drivers/staging/greybus/es1_ap_desc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es1_ap_desc.c b/drivers/staging/greybus/es1_ap_desc.c index 8c558a4755a3..1502089ec29c 100644 --- a/drivers/staging/greybus/es1_ap_desc.c +++ b/drivers/staging/greybus/es1_ap_desc.c @@ -40,7 +40,7 @@ static const u8 es1_config_descriptor[] = { 0x04, /* __u8 if_bDescriptorType; Interface */ 0x00, /* __u8 if_bInterfaceNumber; */ 0x00, /* __u8 if_bAlternateSetting; */ - 0x02, /* __u8 if_bNumEndpoints; */ + 0x03, /* __u8 if_bNumEndpoints; */ 0xff, /* __u8 if_bInterfaceClass; Vendor-specific */ 0xff, /* __u8 if_bInterfaceSubClass; Vendor-specific */ 0xff, /* __u8 if_bInterfaceProtocol; Vendor-specific */ @@ -50,21 +50,21 @@ static const u8 es1_config_descriptor[] = { 0x07, /* __u8 ep_bLength; */ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Bulk */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ - 0x00 /* __u8 ep_bInterval; */ + 0x40, /* __u8 ep_bInterval; 64ms */ 0x07, /* __u8 ep_bLength; */ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x82, /* __u8 ep_bEndpointAddress; IN Endpoint 2 */ 0x02, /* __u8 ep_bmAttributes; Bulk */ 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ - 0x00 /* __u8 ep_bInterval; */ + 0x40 /* __u8 ep_bInterval; */ 0x07, /* __u8 ep_bLength; */ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x02, /* __u8 ep_bEndpointAddress; Out Endpoint 2 */ 0x02, /* __u8 ep_bmAttributes; Bulk */ 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ - 0x00 /* __u8 ep_bInterval; */ + 0x40 /* __u8 ep_bInterval; */ }; -- cgit v1.2.3-59-g8ed1b From 168db1cd2977f29c1181482aec69c22c20da7d72 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 16:15:52 -0700 Subject: greybus: tty driver fixes to get init working properly --- drivers/staging/greybus/core.c | 16 ++++++++++++---- drivers/staging/greybus/uart-gb.c | 30 ++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 647bc5baaef1..1d9efd4b5f59 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -508,22 +508,30 @@ static int __init gb_init(void) int retval; retval = gb_debugfs_init(); - if (retval) + if (retval) { + pr_err("debugfs failed\n"); return retval; + } retval = bus_register(&greybus_bus_type); - if (retval) + if (retval) { + pr_err("bus_register failed\n"); goto error_bus; + } retval = gb_thread_init(); - if (retval) + if (retval) { + pr_err("gb_thread_init failed\n"); goto error_thread; + } // FIXME - more gb core init goes here retval = gb_tty_init(); - if (retval) + if (retval) { + pr_err("gb_tty_init failed\n"); goto error_tty; + } return 0; diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 852dd0c513d5..5c01b0a9c3f5 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -8,6 +8,7 @@ * Heavily based on drivers/usb/class/cdc-acm.c and * drivers/usb/serial/usb-serial.c. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include @@ -27,7 +28,7 @@ #include #include "greybus.h" -#define GB_TTY_MAJOR 180 /* FIXME use a real number!!! */ +#define GB_TTY_MAJOR 230 /* FIXME use a real number!!! */ #define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ #define GB_NAME "ttyGB" @@ -473,20 +474,27 @@ int __init gb_tty_init(void) int retval = 0; dev_t dev; +#if 0 + retval = alloc_chrdev_region(&dev, 0, GB_NUM_MINORS, GB_NAME); - if (retval) + if (retval) { + pr_err("Can not allocate minors\n"); return retval; + } +#endif + gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); if (IS_ERR(gb_tty_driver)) { + pr_err("Can not allocate tty driver\n"); retval = -ENOMEM; goto fail_unregister_dev; } gb_tty_driver->driver_name = "gb"; gb_tty_driver->name = GB_NAME; - gb_tty_driver->major = MAJOR(dev); - gb_tty_driver->minor_start = MINOR(dev); + gb_tty_driver->major = 0; + gb_tty_driver->minor_start = 0; gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; @@ -495,12 +503,18 @@ int __init gb_tty_init(void) tty_set_operations(gb_tty_driver, &gb_ops); retval = tty_register_driver(gb_tty_driver); - if (retval) + if (retval) { + pr_err("Can not register tty driver: %d\n", retval); goto fail_put_gb_tty; + } +#if 0 retval = greybus_register(&tty_gb_driver); - if (retval) + if (retval) { + pr_err("Can not register greybus driver.\n"); goto fail_unregister_gb_tty; + } +#endif return 0; @@ -509,7 +523,7 @@ int __init gb_tty_init(void) fail_put_gb_tty: put_tty_driver(gb_tty_driver); fail_unregister_dev: - unregister_chrdev_region(dev, GB_NUM_MINORS); +// unregister_chrdev_region(dev, GB_NUM_MINORS); return retval; } @@ -517,7 +531,7 @@ void __exit gb_tty_exit(void) { int major = MAJOR(gb_tty_driver->major); int minor = gb_tty_driver->minor_start; - greybus_deregister(&tty_gb_driver); +// greybus_deregister(&tty_gb_driver); tty_unregister_driver(gb_tty_driver); put_tty_driver(gb_tty_driver); unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); -- cgit v1.2.3-59-g8ed1b From 543b8ed2fee047ae8f598a0c8b763aff59139044 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 17:02:47 -0700 Subject: greybus: uart-gb: let the core dynamically allocate the major number Don't register the tty_gb_driver, the gb core is not ready for the for ES1 devices. --- drivers/staging/greybus/uart-gb.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 5c01b0a9c3f5..39a6e0191434 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -28,7 +28,6 @@ #include #include "greybus.h" -#define GB_TTY_MAJOR 230 /* FIXME use a real number!!! */ #define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ #define GB_NAME "ttyGB" @@ -462,27 +461,17 @@ void gb_tty_disconnect(struct greybus_device *gdev) kfree(gb_tty); } +#if 0 static struct greybus_driver tty_gb_driver = { .probe = gb_tty_probe, .disconnect = gb_tty_disconnect, .id_table = id_table, }; - +#endif int __init gb_tty_init(void) { int retval = 0; - dev_t dev; - -#if 0 - - retval = alloc_chrdev_region(&dev, 0, GB_NUM_MINORS, GB_NAME); - if (retval) { - pr_err("Can not allocate minors\n"); - return retval; - } -#endif - gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); if (IS_ERR(gb_tty_driver)) { @@ -518,12 +507,11 @@ int __init gb_tty_init(void) return 0; - fail_unregister_gb_tty: +/* fail_unregister_gb_tty: */ tty_unregister_driver(gb_tty_driver); - fail_put_gb_tty: +fail_put_gb_tty: put_tty_driver(gb_tty_driver); - fail_unregister_dev: -// unregister_chrdev_region(dev, GB_NUM_MINORS); +fail_unregister_dev: return retval; } @@ -531,7 +519,10 @@ void __exit gb_tty_exit(void) { int major = MAJOR(gb_tty_driver->major); int minor = gb_tty_driver->minor_start; -// greybus_deregister(&tty_gb_driver); + +#if 0 + greybus_deregister(&tty_gb_driver); +#endif tty_unregister_driver(gb_tty_driver); put_tty_driver(gb_tty_driver); unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); -- cgit v1.2.3-59-g8ed1b From a1dc62b0c8efe79f27d048d03daac4764cb7c46c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 17:28:19 -0700 Subject: greybus: core: verify major/minor number of greybus protocol --- drivers/staging/greybus/core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 1d9efd4b5f59..bad40bafae4c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -385,7 +385,15 @@ struct greybus_device *greybus_new_module(struct device *parent, version_major = manifest->header.version_major; version_minor = manifest->header.version_minor; - // FIXME - check version major/minor here! + /* Validate major/minor number */ + if ((version_major != GREYBUS_VERSION_MAJOR) || + (version_minor != GREYBUS_VERSION_MINOR)) { + dev_err(parent, + "Invalid greybus versions, expected %d.%d, got %d.%d\n", + GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR, + version_major, version_minor); + goto error; + } size -= sizeof(manifest->header); data += sizeof(manifest->header); -- cgit v1.2.3-59-g8ed1b From 7f9e05e15b4e0a252a26122ac3762a9caa80a65d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 17:28:33 -0700 Subject: greybus: es1: functionally complete Have only tested USB device add/remove, the urbs seem to all be queued up, no data has been tested to flow through yet. Odds are the hc interface will have to change, but this is a good first start to build on. --- drivers/staging/greybus/es1-ap-usb.c | 57 ++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 6635194e4a9a..e6cfd6ce8b98 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -14,7 +14,8 @@ #include "svc_msg.h" static const struct usb_device_id id_table[] = { - { USB_DEVICE(0xffff, 0x0001) }, // FIXME + /* Made up numbers for the SVC USB Bridge in ES1 */ + { USB_DEVICE(0xffff, 0x0001) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); @@ -31,23 +32,42 @@ MODULE_DEVICE_TABLE(usb, id_table); */ #define NUM_CPORT_OUT_URB 8 - +/** + * es1_ap_dev - ES1 USB Bridge to AP structure + * @usb_dev: pointer to the USB device we are. + * @usb_intf: pointer to the USB interface we are bound to. + * @hd: pointer to our greybus_host_device structure + * @control_endpoint: endpoint to send data to SVC + * @svc_endpoint: endpoint for SVC data in + * @cport_in_endpoint: bulk in endpoint for CPort data + * @cport-out_endpoint: bulk out endpoint for CPort data + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint + * @svc_urb: urb for SVC messages coming in on @svc_endpoint + * @cport_in_urb: array of urbs for the CPort in messages + * @cport_in_buffer: array of buffers for the @cport_in_urb urbs + * @cport_out_urb: array of urbs for the CPort out messages + * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or + * not. + * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" + */ struct es1_ap_dev { struct usb_device *usb_dev; struct usb_interface *usb_intf; struct greybus_host_device *hd; - __u8 control_endpoint; /* endpoint to send data to SVC */ - __u8 svc_endpoint; /* endpoint for SVC data */ - __u8 cport_in_endpoint; /* bulk in for CPort data */ - __u8 cport_out_endpoint; /* bulk out for CPort data */ - u8 *svc_buffer; /* buffer for SVC messages coming in */ - struct urb *svc_urb; /* urb for SVC messages coming in */ - struct urb *cport_in_urb[NUM_CPORT_IN_URB]; /* CPort IN urbs */ - u8 *cport_in_buffer[NUM_CPORT_IN_URB]; /* CPort IN buffers */ - struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; /* CPort OUT urbs */ - bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; /* CPort OUT urb busy marker */ - spinlock_t cport_out_urb_lock; /* locks list of cport out urbs */ + __u8 control_endpoint; + __u8 svc_endpoint; + __u8 cport_in_endpoint; + __u8 cport_out_endpoint; + + u8 *svc_buffer; + struct urb *svc_urb; + + struct urb *cport_in_urb[NUM_CPORT_IN_URB]; + u8 *cport_in_buffer[NUM_CPORT_IN_URB]; + struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; + bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; + spinlock_t cport_out_urb_lock; }; static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) @@ -291,6 +311,7 @@ static void cport_out_callback(struct urb *urb) int status = urb->status; int i; + /* do we care about errors going back up? */ switch (status) { case 0: break; @@ -308,15 +329,13 @@ static void cport_out_callback(struct urb *urb) goto exit; } - // FIXME - do we care about errors going back up? - /* Tell the core the gbuf is properly sent */ greybus_gbuf_finished(gbuf); exit: /* - * See if this was an urb in our pool, if so mark it "free", otherwise we - * need to free it ourselves. + * See if this was an urb in our pool, if so mark it "free", otherwise + * we need to free it ourselves. */ spin_lock_irqsave(&es1->cport_out_urb_lock, flags); for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { @@ -327,9 +346,9 @@ exit: } } spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - if (urb) - usb_free_urb(urb); + /* If urb is not NULL, then we need to free this urb */ + usb_free_urb(urb); } /* -- cgit v1.2.3-59-g8ed1b From ee6fb799021290e226e0559eb8048b86747cb045 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 17:31:27 -0700 Subject: greybus: devices: endpoint description of device --- drivers/staging/greybus/devices | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 drivers/staging/greybus/devices diff --git a/drivers/staging/greybus/devices b/drivers/staging/greybus/devices new file mode 100644 index 000000000000..486bba8e5de0 --- /dev/null +++ b/drivers/staging/greybus/devices @@ -0,0 +1,11 @@ +T: Bus=01 Lev=03 Prnt=07 Port=02 Cnt=03 Dev#= 12 Spd=12 MxCh= 0 +D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 +P: Vendor=ffff ProdID=0001 Rev= 1.00 +S: Manufacturer=Greybus +S: Product=SVC Bridge +S: SerialNumber=12239 +C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr=100mA +I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=es1_ap_driver +E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=64ms +E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms +E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms -- cgit v1.2.3-59-g8ed1b From 80e04f09942e3641a5745e22b270e91f65c9107c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Sep 2014 18:20:54 -0700 Subject: greybus: gbuf recieve path work, not done, dinner time... --- drivers/staging/greybus/ap.c | 7 --- drivers/staging/greybus/gbuf.c | 105 ++++++++++++++++++++++++++++++++++---- drivers/staging/greybus/greybus.h | 7 ++- 3 files changed, 100 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index f92066625c1e..0fc3ff70f317 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -252,13 +252,6 @@ int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd) } EXPORT_SYMBOL_GPL(gb_new_ap_msg); -void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, - size_t length) -{ - // FIXME - implement... -} -EXPORT_SYMBOL_GPL(greybus_cport_in_data); - int gb_thread_init(void) { ap_workqueue = alloc_workqueue("greybus_ap", 0, 1); diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index e12a625131de..b06d779254c5 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -18,6 +18,31 @@ #include "greybus.h" +static struct gbuf *__alloc_gbuf(struct greybus_device *gdev, + struct gdev_cport *cport, + gbuf_complete_t complete, + gfp_t gfp_mask, + void *context) +{ + struct gbuf *gbuf; + + /* + * change this to a slab allocation if it's too slow, but for now, let's + * be dumb and simple. + */ + gbuf = kzalloc(sizeof(*gbuf), gfp_mask); + if (!gbuf) + return NULL; + + kref_init(&gbuf->kref); + gbuf->gdev = gdev; + gbuf->cport = cport; + gbuf->complete = complete; + gbuf->context = context; + + return gbuf; +} + /** * greybus_alloc_gbuf - allocate a greybus buffer * @@ -43,20 +68,10 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, struct gbuf *gbuf; int retval; - /* - * change this to a slab allocation if it's too slow, but for now, let's - * be dumb and simple. - */ - gbuf = kzalloc(sizeof(*gbuf), gfp_mask); + gbuf = __alloc_gbuf(gdev, cport, complete, gfp_mask, context); if (!gbuf) return NULL; - kref_init(&gbuf->kref); - gbuf->gdev = gdev; - gbuf->cport = cport; - gbuf->complete = complete; - gbuf->context = context; - /* Host controller specific allocation for the actual buffer */ retval = gbuf->gdev->hd->driver->alloc_gbuf(gbuf, size, gfp_mask); if (retval) { @@ -114,3 +129,71 @@ void greybus_gbuf_finished(struct gbuf *gbuf) // FIXME - implement } EXPORT_SYMBOL_GPL(greybus_gbuf_finished); + +#define MAX_CPORTS 1024 +struct gb_cport_handler { + gbuf_complete_t handler; + struct gdev_cport cport; + struct greybus_device *gdev; + void *context; +}; + +static struct gb_cport_handler cport_handler[MAX_CPORTS]; +// FIXME - use a lock for this list of handlers, but really, for now we don't +// need it, we don't have a dynamic system... + +int gb_register_cport_complete(struct greybus_device *gdev, + gbuf_complete_t handler, int cport, + void *context) +{ + if (cport_handler[cport].handler) + return -EINVAL; + cport_handler[cport].context = context; + cport_handler[cport].gdev = gdev; + cport_handler[cport].cport.number = cport; + cport_handler[cport].handler = handler; + return 0; +} + +void gb_deregister_cport_handler(int cport) +{ + cport_handler[cport].handler = NULL; +} + +void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, + size_t length) +{ + struct gb_cport_handler *ch; + struct gbuf *gbuf; + + /* first check to see if we have a cport handler for this cport */ + ch = &cport_handler[cport]; + if (!ch->handler) { + /* Ugh, drop the data on the floor, after logging it... */ + dev_err(&hd->dev, + "Received data for cport %d, but no handler!\n", + cport); + return; + } + + gbuf = __alloc_gbuf(ch->gdev, &ch->cport, ch->handler, GFP_ATOMIC, + ch->context); + if (!gbuf) { + /* Again, something bad went wrong, log it... */ + pr_err("can't allocate gbuf???\n"); + return; + } + /* Set the data pointers */ + + // FIXME - implement... +} +EXPORT_SYMBOL_GPL(greybus_cport_in_data); + +int greybus_gbuf_init(void) +{ + return 0; +} + +void greybus_gbuf_exit(void) +{ +} diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 1badfa8cfd36..55cb48f264ac 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -42,8 +42,8 @@ struct gbuf; struct gdev_cport { u16 number; u16 size; - // FIXME, what else? u8 speed; // valid??? + // FIXME, what else? }; struct gdev_string { @@ -234,6 +234,11 @@ void gb_thread_destroy(void); int gb_debugfs_init(void); void gb_debugfs_cleanup(void); +int gb_register_cport_complete(struct greybus_device *gdev, + gbuf_complete_t handler, int cport, + void *context); +void gb_deregister_cport_complete(int cport); + extern const struct attribute_group *greybus_module_groups[]; /* -- cgit v1.2.3-59-g8ed1b From 45f3678bcff37889b8fe81a7b1ea21b09a25803d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 14 Sep 2014 11:40:35 -0700 Subject: greybus: gbuf: cport in buffer stream logic --- drivers/staging/greybus/ap.c | 6 ++-- drivers/staging/greybus/core.c | 21 +++++++++---- drivers/staging/greybus/gbuf.c | 62 +++++++++++++++++++++++++++++++++++---- drivers/staging/greybus/greybus.h | 6 ++-- 4 files changed, 79 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 0fc3ff70f317..4c1065030745 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include "svc_msg.h" #include "greybus_manifest.h" @@ -252,7 +252,7 @@ int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd) } EXPORT_SYMBOL_GPL(gb_new_ap_msg); -int gb_thread_init(void) +int gb_ap_init(void) { ap_workqueue = alloc_workqueue("greybus_ap", 0, 1); if (!ap_workqueue) @@ -261,7 +261,7 @@ int gb_thread_init(void) return 0; } -void gb_thread_destroy(void) +void gb_ap_exit(void) { destroy_workqueue(ap_workqueue); } diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index bad40bafae4c..f987086ae6ec 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -527,13 +527,17 @@ static int __init gb_init(void) goto error_bus; } - retval = gb_thread_init(); + retval = gb_ap_init(); if (retval) { - pr_err("gb_thread_init failed\n"); - goto error_thread; + pr_err("gb_ap_init failed\n"); + goto error_ap; } - // FIXME - more gb core init goes here + retval = gb_gbuf_init(); + if (retval) { + pr_err("gb_gbuf_init failed\n"); + goto error_gbuf; + } retval = gb_tty_init(); if (retval) { @@ -544,9 +548,12 @@ static int __init gb_init(void) return 0; error_tty: - gb_thread_destroy(); + gb_gbuf_exit(); + +error_gbuf: + gb_ap_exit(); -error_thread: +error_ap: bus_unregister(&greybus_bus_type); error_bus: @@ -558,6 +565,8 @@ error_bus: static void __exit gb_exit(void) { gb_tty_exit(); + gb_gbuf_exit(); + gb_ap_exit(); bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); } diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index b06d779254c5..3affdbfa0479 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "greybus.h" @@ -155,16 +156,42 @@ int gb_register_cport_complete(struct greybus_device *gdev, return 0; } -void gb_deregister_cport_handler(int cport) +void gb_deregister_cport_complete(int cport) { cport_handler[cport].handler = NULL; } +struct cport_msg { + struct gbuf *gbuf; + struct work_struct event; +}; + +static struct workqueue_struct *cport_workqueue; + +static void cport_process_event(struct work_struct *work) +{ + struct cport_msg *cm; + struct gbuf *gbuf; + + cm = container_of(work, struct cport_msg, event); + + gbuf = cm->gbuf; + + /* call the gbuf handler */ + gbuf->complete(gbuf); + + /* free all the memory */ + kfree(gbuf->transfer_buffer); + kfree(gbuf); + kfree(cm); +} + void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, size_t length) { struct gb_cport_handler *ch; struct gbuf *gbuf; + struct cport_msg *cm; /* first check to see if we have a cport handler for this cport */ ch = &cport_handler[cport]; @@ -183,17 +210,42 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, pr_err("can't allocate gbuf???\n"); return; } - /* Set the data pointers */ + gbuf->hdpriv = hd; + + /* + * FIXME: + * Very dumb copy data method for now, if this is slow (odds are it will + * be, we should move to a model where the hd "owns" all buffers, but we + * want something up and working first for now. + */ + gbuf->transfer_buffer = kmalloc(length, GFP_ATOMIC); + if (!gbuf->transfer_buffer) { + kfree(gbuf); + return; + } + memcpy(gbuf->transfer_buffer, data, length); + gbuf->transfer_buffer_length = length; - // FIXME - implement... + /* Again with the slow allocate... */ + cm = kmalloc(sizeof(*cm), GFP_ATOMIC); + + /* Queue up the cport message to be handled in user context */ + cm->gbuf = gbuf; + INIT_WORK(&cm->event, cport_process_event); + queue_work(cport_workqueue, &cm->event); } EXPORT_SYMBOL_GPL(greybus_cport_in_data); -int greybus_gbuf_init(void) +int gb_gbuf_init(void) { + cport_workqueue = alloc_workqueue("greybus_gbuf", 0, 1); + if (!cport_workqueue) + return -ENOMEM; + return 0; } -void greybus_gbuf_exit(void) +void gb_gbuf_exit(void) { + destroy_workqueue(cport_workqueue); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 55cb48f264ac..ecda30dd308a 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -229,10 +229,12 @@ const u8 *greybus_string(struct greybus_device *gdev, int id); /* Internal functions to gb module, move to internal .h file eventually. */ int gb_new_ap_msg(u8 *data, int length, struct greybus_host_device *hd); -int gb_thread_init(void); -void gb_thread_destroy(void); +int gb_ap_init(void); +void gb_ap_exit(void); int gb_debugfs_init(void); void gb_debugfs_cleanup(void); +int gb_gbuf_init(void); +void gb_gbuf_exit(void); int gb_register_cport_complete(struct greybus_device *gdev, gbuf_complete_t handler, int cport, -- cgit v1.2.3-59-g8ed1b From 13c8d9cd8a5381a68eb39bfebf2004835ffc0251 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 14 Sep 2014 12:27:19 -0700 Subject: greybus: add test_sink driver --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/test_sink.c | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 drivers/staging/greybus/test_sink.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 0550e219af3d..2e048a010a23 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -11,6 +11,7 @@ greybus-y := core.o \ obj-m += greybus.o obj-m += es1-ap-usb.o +obj-m += test_sink.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/test_sink.c b/drivers/staging/greybus/test_sink.c new file mode 100644 index 000000000000..932c1692a13b --- /dev/null +++ b/drivers/staging/greybus/test_sink.c @@ -0,0 +1,38 @@ +/* + * Test "sink" Greybus driver. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include "greybus.h" + +struct test_device { + struct greybus_device *gdev; +}; + +int gb_register_cport_complete(struct greybus_device *gdev, + gbuf_complete_t handler, int cport, + void *context); +void gb_deregister_cport_complete(int cport); + + + +static int test_init(void) +{ + return 0; +} + +static void test_exit(void) +{ +} + +module_init(test_init); +module_exit(test_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From 772149b6df4648333c033d6b68e7a0b860dc22da Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 14 Sep 2014 12:27:28 -0700 Subject: greybus: fix hd init sequence of setting up parent and driver pointers properly --- drivers/staging/greybus/ap.c | 23 ++++++++++++----------- drivers/staging/greybus/core.c | 2 ++ drivers/staging/greybus/gbuf.c | 2 +- drivers/staging/greybus/greybus.h | 4 +--- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 4c1065030745..4293073efe41 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -68,7 +68,7 @@ static void svc_handshake(struct svc_function_handshake *handshake, /* A new SVC communication channel, let's verify it was for us */ if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) { /* we don't know what to do with this, log it and return */ - dev_dbg(&hd->dev, "received invalid handshake type %d\n", + dev_dbg(hd->parent, "received invalid handshake type %d\n", handshake->handshake_type); return; } @@ -86,7 +86,7 @@ static void svc_management(struct svc_function_unipro_management *management, struct greybus_host_device *hd) { /* What? An AP should not get this message */ - dev_err(&hd->dev, "Got an svc management message???\n"); + dev_err(hd->parent, "Got an svc management message???\n"); } static void svc_hotplug(struct svc_function_hotplug *hotplug, @@ -96,17 +96,18 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, switch (hotplug->hotplug_event) { case SVC_HOTPLUG_EVENT: - dev_dbg(&hd->dev, "module id %d added\n", module_id); + dev_dbg(hd->parent, "module id %d added\n", module_id); // FIXME - add the module to the system break; case SVC_HOTUNPLUG_EVENT: - dev_dbg(&hd->dev, "module id %d removed\n", module_id); + dev_dbg(hd->parent, "module id %d removed\n", module_id); // FIXME - remove the module from the system break; default: - dev_err(&hd->dev, "received invalid hotplug message type %d\n", + dev_err(hd->parent, + "received invalid hotplug message type %d\n", hotplug->hotplug_event); break; } @@ -116,7 +117,7 @@ static void svc_ddb(struct svc_function_ddb *ddb, struct greybus_host_device *hd) { /* What? An AP should not get this message */ - dev_err(&hd->dev, "Got an svc DDB message???\n"); + dev_err(hd->parent, "Got an svc DDB message???\n"); } static void svc_power(struct svc_function_power *power, @@ -125,12 +126,12 @@ static void svc_power(struct svc_function_power *power, u8 module_id = power->module_id; if (power->power_type != SVC_POWER_BATTERY_STATUS) { - dev_err(&hd->dev, "received invalid power type %d\n", + dev_err(hd->parent, "received invalid power type %d\n", power->power_type); return; } - dev_dbg(&hd->dev, "power status for module id %d is %d\n", + dev_dbg(hd->parent, "power status for module id %d is %d\n", module_id, power->status.status); // FIXME - do something with the power information, like update our @@ -141,14 +142,14 @@ static void svc_epm(struct svc_function_epm *epm, struct greybus_host_device *hd) { /* What? An AP should not get this message */ - dev_err(&hd->dev, "Got an EPM message???\n"); + dev_err(hd->parent, "Got an EPM message???\n"); } static void svc_suspend(struct svc_function_suspend *suspend, struct greybus_host_device *hd) { /* What? An AP should not get this message */ - dev_err(&hd->dev, "Got an suspend message???\n"); + dev_err(hd->parent, "Got an suspend message???\n"); } static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) @@ -209,7 +210,7 @@ static void ap_process_event(struct work_struct *work) svc_suspend(&svc_msg->suspend, hd); break; default: - dev_err(&hd->dev, "received invalid SVC message type %d\n", + dev_err(hd->parent, "received invalid SVC message type %d\n", svc_msg->header.type); } diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index f987086ae6ec..a13cf9f05059 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -499,6 +499,8 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver return NULL; kref_init(&hd->kref); + hd->parent = parent; + hd->driver = driver; return hd; } diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 3affdbfa0479..2bdf485d1051 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -197,7 +197,7 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, ch = &cport_handler[cport]; if (!ch->handler) { /* Ugh, drop the data on the floor, after logging it... */ - dev_err(&hd->dev, + dev_err(hd->parent, "Received data for cport %d, but no handler!\n", cport); return; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index ecda30dd308a..2838e44ce6fb 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -105,7 +105,6 @@ struct svc_msg; struct greybus_host_driver { size_t hd_priv_size; - int (*start)(struct greybus_host_device *hd); int (*alloc_gbuf)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); void (*free_gbuf)(struct gbuf *gbuf); int (*send_svc_msg)(struct svc_msg *svc_msg, struct greybus_host_device *hd); @@ -114,10 +113,9 @@ struct greybus_host_driver { }; struct greybus_host_device { - struct device dev; struct kref kref; + struct device *parent; const struct greybus_host_driver *driver; - unsigned long hd_priv_size; /* Private data for the host driver */ unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64)))); -- cgit v1.2.3-59-g8ed1b From 710ecb0605350391e14a2d470397afb798b1fbf7 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 18 Sep 2014 15:25:41 -0400 Subject: greybus: update svc_msg_header fields and users to match spec The Greybus spec has been updated to clarify some of the original intent of the SVC message definition. The svc_msg_header was: struct svc_msg_header { __u8 function; __u8 message_type; ... } and is now struct svc_msg_header { __u8 function_id; __u8 message_type; ... } to match the spec. The function_id carries enum svc_function_id values and message_type is now clarified to be a session layer level field that is simply "data" or "error". Change all references of function type to function id. For now, don't parse the message_type field but add the two allowable svc_msg_type enums. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 12 ++++++------ drivers/staging/greybus/svc_msg.h | 11 ++++++++--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 4293073efe41..c2c5aa654e7e 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -29,7 +29,7 @@ struct ap_msg { static struct workqueue_struct *ap_workqueue; -static struct svc_msg *svc_msg_alloc(enum svc_function_type type) +static struct svc_msg *svc_msg_alloc(enum svc_function_id id) { struct svc_msg *svc_msg; @@ -37,8 +37,8 @@ static struct svc_msg *svc_msg_alloc(enum svc_function_type type) if (!svc_msg) return NULL; - // FIXME - verify we are only sending message types we should be - svc_msg->header.type = type; + // FIXME - verify we are only sending function IDs we should be + svc_msg->header.function_id = id; return svc_msg; } @@ -187,7 +187,7 @@ static void ap_process_event(struct work_struct *work) } /* Look at the message to figure out what to do with it */ - switch (svc_msg->header.type) { + switch (svc_msg->header.function_id) { case SVC_FUNCTION_HANDSHAKE: svc_handshake(&svc_msg->handshake, hd); break; @@ -210,8 +210,8 @@ static void ap_process_event(struct work_struct *work) svc_suspend(&svc_msg->suspend, hd); break; default: - dev_err(hd->parent, "received invalid SVC message type %d\n", - svc_msg->header.type); + dev_err(hd->parent, "received invalid SVC function ID %d\n", + svc_msg->header.function_id); } /* clean the message up */ diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index bbc7ef0b6517..3f1a93499aee 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -12,7 +12,7 @@ #pragma pack(push, 1) -enum svc_function_type { +enum svc_function_id { SVC_FUNCTION_HANDSHAKE = 0x00, SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, SVC_FUNCTION_HOTPLUG = 0x02, @@ -22,9 +22,14 @@ enum svc_function_type { SVC_FUNCTION_SUSPEND = 0x06, }; +enum svc_msg_type { + SVC_MSG_DATA = 0x00, + SVC_MSG_ERROR = 0xff, +}; + struct svc_msg_header { - __u8 function; - __u8 type; /* enum svc_function_type */ + __u8 function_id; /* enum svc_function_id */ + __u8 message_type; __u8 version_major; __u8 version_minor; __le16 payload_length; -- cgit v1.2.3-59-g8ed1b From e94e17143ea61f08488e55a8b8dfa025ab5fee44 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 18 Sep 2014 15:25:42 -0400 Subject: greybus: move versioning from svc message header to handshake function The Greybus spec has been updated to improve the efficiency of the version major/minor information that had been previously carried in every SVC message header. The version major/minor is now provided as part of the handshake function. Update the SVC msg header and handshake function payload definitions and move the version major/minor validation into the SVC handshake handling routine. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 15 +++++++++------ drivers/staging/greybus/svc_msg.h | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index c2c5aa654e7e..2a60de0cec89 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -65,7 +65,15 @@ static void svc_handshake(struct svc_function_handshake *handshake, { struct svc_msg *svc_msg; - /* A new SVC communication channel, let's verify it was for us */ + /* A new SVC communication channel, let's verify a supported version */ + if ((handshake->version_major != GREYBUS_VERSION_MAJOR) && + (handshake->version_minor != GREYBUS_VERSION_MINOR)) { + dev_dbg(hd->parent, "received invalid greybus version %d:%d\n", + handshake->version_major, handshake->version_minor); + return; + } + + /* Validate that the handshake came from the SVC */ if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) { /* we don't know what to do with this, log it and return */ dev_dbg(hd->parent, "received invalid handshake type %d\n", @@ -162,11 +170,6 @@ static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) svc_msg = (struct svc_msg *)ap_msg->data; - /* Verify the version is something we can handle with this code */ - if ((svc_msg->header.version_major != GREYBUS_VERSION_MAJOR) && - (svc_msg->header.version_minor != GREYBUS_VERSION_MINOR)) - return NULL; - return svc_msg; } diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 3f1a93499aee..e84e01cf3cce 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -30,8 +30,6 @@ enum svc_msg_type { struct svc_msg_header { __u8 function_id; /* enum svc_function_id */ __u8 message_type; - __u8 version_major; - __u8 version_minor; __le16 payload_length; }; @@ -42,6 +40,8 @@ enum svc_function_handshake_type { }; struct svc_function_handshake { + __u8 version_major; + __u8 version_minor; __u8 handshake_type; /* enum svc_function_handshake_type */ }; -- cgit v1.2.3-59-g8ed1b From 52adb56340b6f83d62d65e96b0f925ec8797b802 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 18 Sep 2014 15:25:43 -0400 Subject: greybus: update GREYBUS_VERSION_[MAJOR|MINOR] to match spec The Greybus spec was updated to have major=0 and minor=1 so update this in the code. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2838e44ce6fb..c9f516ab7612 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -20,8 +20,8 @@ /* Matches up with the Greybus Protocol specification document */ -#define GREYBUS_VERSION_MAJOR 0x01 -#define GREYBUS_VERSION_MINOR 0x00 +#define GREYBUS_VERSION_MAJOR 0x00 +#define GREYBUS_VERSION_MINOR 0x01 #define GREYBUS_DEVICE_ID_MATCH_DEVICE \ (GREYBUS_DEVICE_ID_MATCH_VENDOR | GREYBUS_DEVICE_ID_MATCH_PRODUCT) -- cgit v1.2.3-59-g8ed1b From 29f000f434afbdf136714aabaaab8f8f27025a36 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Sep 2014 18:37:44 -0700 Subject: greybus: es1: set buffer sizes for messages based on email discussions The gbuf sizes are 4k (i.e. PAGE_SIZE) and svc interrupt urb is 2k --- drivers/staging/greybus/es1-ap-usb.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index e6cfd6ce8b98..c385a0be3b6b 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -13,6 +13,11 @@ #include "greybus.h" #include "svc_msg.h" +/* Memory sizes for the buffers sent to/from the ES1 controller */ +#define ES1_SVC_MSG_SIZE 2048 +#define ES1_GBUF_MSG_SIZE PAGE_SIZE + + static const struct usb_device_id id_table[] = { /* Made up numbers for the SVC USB Bridge in ES1 */ { USB_DEVICE(0xffff, 0x0001) }, @@ -90,6 +95,11 @@ static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) struct es1_ap_dev *es1 = hd_to_es1(gbuf->gdev->hd); u8 *buffer; + if (size > ES1_GBUF_MSG_SIZE) { + pr_err("guf was asked to be bigger than %d!\n", + ES1_GBUF_MSG_SIZE); + } + /* For ES2 we need to figure out what cport is going to what endpoint, * but for ES1, it's so dirt simple, we don't have a choice... * @@ -371,7 +381,6 @@ static int ap_probe(struct usb_interface *interface, bool bulk_out_found = false; int retval = -ENOMEM; int i; - int buffer_size = 0; u8 svc_interval = 0; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -398,7 +407,6 @@ static int ap_probe(struct usb_interface *interface, if (usb_endpoint_is_int_in(endpoint)) { es1->svc_endpoint = endpoint->bEndpointAddress; - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); svc_interval = endpoint->bInterval; int_in_found = true; } else if (usb_endpoint_is_bulk_in(endpoint)) { @@ -421,7 +429,7 @@ static int ap_probe(struct usb_interface *interface, } /* Create our buffer and URB to get SVC messages, and start it up */ - es1->svc_buffer = kmalloc(buffer_size, GFP_KERNEL); + es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); if (!es1->svc_buffer) goto error; @@ -431,7 +439,7 @@ static int ap_probe(struct usb_interface *interface, usb_fill_int_urb(es1->svc_urb, udev, usb_rcvintpipe(udev, es1->svc_endpoint), - es1->svc_buffer, buffer_size, svc_callback, + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_callback, es1, svc_interval); retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); if (retval) @@ -445,7 +453,7 @@ static int ap_probe(struct usb_interface *interface, urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) goto error_bulk_in_urb; - buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + buffer = kmalloc(ES1_GBUF_MSG_SIZE, GFP_KERNEL); if (!buffer) goto error_bulk_in_urb; -- cgit v1.2.3-59-g8ed1b From f036e05600bd3901ee5d24d52cbfde93b71a5751 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Sep 2014 19:13:33 -0700 Subject: greybus: gbuf: implement submission logic --- drivers/staging/greybus/es1-ap-usb.c | 16 ++++++++-------- drivers/staging/greybus/gbuf.c | 27 +++++++++++++++++---------- drivers/staging/greybus/greybus.h | 7 ++++--- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index c385a0be3b6b..961d6b1a6a21 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -96,7 +96,7 @@ static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) u8 *buffer; if (size > ES1_GBUF_MSG_SIZE) { - pr_err("guf was asked to be bigger than %d!\n", + pr_err("guf was asked to be bigger than %ld!\n", ES1_GBUF_MSG_SIZE); } @@ -189,8 +189,8 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) return urb; } -static int send_gbuf(struct gbuf *gbuf, struct greybus_host_device *hd, - gfp_t gfp_mask) +static int submit_gbuf(struct gbuf *gbuf, struct greybus_host_device *hd, + gfp_t gfp_mask) { struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; @@ -216,11 +216,11 @@ static int send_gbuf(struct gbuf *gbuf, struct greybus_host_device *hd, } static struct greybus_host_driver es1_driver = { - .hd_priv_size = sizeof(struct es1_ap_dev), - .alloc_gbuf = alloc_gbuf, - .free_gbuf = free_gbuf, - .send_svc_msg = send_svc_msg, - .send_gbuf = send_gbuf, + .hd_priv_size = sizeof(struct es1_ap_dev), + .alloc_gbuf = alloc_gbuf, + .free_gbuf = free_gbuf, + .send_svc_msg = send_svc_msg, + .submit_gbuf = submit_gbuf, }; /* Callback for when we get a SVC message */ diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 2bdf485d1051..6cc752cdd1db 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -112,10 +112,9 @@ struct gbuf *greybus_get_gbuf(struct gbuf *gbuf) } EXPORT_SYMBOL_GPL(greybus_get_gbuf); -int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags) +int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { - // FIXME - implement - return -ENOMEM; + return gbuf->gdev->hd->driver->submit_gbuf(gbuf, gbuf->gdev->hd, gfp_mask); } int greybus_kill_gbuf(struct gbuf *gbuf) @@ -124,13 +123,6 @@ int greybus_kill_gbuf(struct gbuf *gbuf) return -ENOMEM; } -/* Can be called in interrupt context, do the work and get out of here */ -void greybus_gbuf_finished(struct gbuf *gbuf) -{ - // FIXME - implement -} -EXPORT_SYMBOL_GPL(greybus_gbuf_finished); - #define MAX_CPORTS 1024 struct gb_cport_handler { gbuf_complete_t handler; @@ -236,6 +228,21 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, } EXPORT_SYMBOL_GPL(greybus_cport_in_data); +/* Can be called in interrupt context, do the work and get out of here */ +void greybus_gbuf_finished(struct gbuf *gbuf) +{ + struct cport_msg *cm; + + /* Again with the slow allocate... */ + cm = kmalloc(sizeof(*cm), GFP_ATOMIC); + cm->gbuf = gbuf; + INIT_WORK(&cm->event, cport_process_event); + queue_work(cport_workqueue, &cm->event); + + // FIXME - implement +} +EXPORT_SYMBOL_GPL(greybus_gbuf_finished); + int gb_gbuf_init(void) { cport_workqueue = alloc_workqueue("greybus_gbuf", 0, 1); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index c9f516ab7612..17a01bd41ee5 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -107,9 +107,10 @@ struct greybus_host_driver { int (*alloc_gbuf)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); void (*free_gbuf)(struct gbuf *gbuf); - int (*send_svc_msg)(struct svc_msg *svc_msg, struct greybus_host_device *hd); - int (*send_gbuf)(struct gbuf *gbuf, struct greybus_host_device *hd, - gfp_t gfp_mask); + int (*send_svc_msg)(struct svc_msg *svc_msg, + struct greybus_host_device *hd); + int (*submit_gbuf)(struct gbuf *gbuf, struct greybus_host_device *hd, + gfp_t gfp_mask); }; struct greybus_host_device { -- cgit v1.2.3-59-g8ed1b From 3e7736e5c17801e15c0355d905988c03bbc0ba22 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 21 Sep 2014 17:34:28 -0700 Subject: greybus: gbuf: clean up logic of who owns what "part" of the gbuf Started documenting the gbuf and how a greybus driver and a host controller driver needs to interact with it, and the rest of the greybus system. It's crude documentation, but better than nothing for now... --- drivers/staging/greybus/es1-ap-usb.c | 22 ++++--- drivers/staging/greybus/gbuf.c | 112 +++++++++++++++++++---------------- drivers/staging/greybus/greybus.h | 73 +++++++++++++++++++++-- 3 files changed, 140 insertions(+), 67 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 961d6b1a6a21..1a47da6fb933 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -90,7 +90,7 @@ static void cport_out_callback(struct urb *urb); * void *transfer_buffer; * u32 transfer_buffer_length; */ -static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) +static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) { struct es1_ap_dev *es1 = hd_to_es1(gbuf->gdev->hd); u8 *buffer; @@ -116,6 +116,7 @@ static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) buffer[0] = gbuf->cport->number; gbuf->transfer_buffer = &buffer[1]; gbuf->transfer_buffer_length = size; + gbuf->actual_length = size; /* When we send the gbuf, we need this pointer to be here */ gbuf->hdpriv = es1; @@ -124,14 +125,17 @@ static int alloc_gbuf(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) } /* Free the memory we allocated with a gbuf */ -static void free_gbuf(struct gbuf *gbuf) +static void free_gbuf_data(struct gbuf *gbuf) { u8 *transfer_buffer; u8 *buffer; transfer_buffer = gbuf->transfer_buffer; - buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ - kfree(buffer); + /* Can be called with a NULL transfer_buffer on some error paths */ + if (transfer_buffer) { + buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ + kfree(buffer); + } } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ @@ -216,11 +220,11 @@ static int submit_gbuf(struct gbuf *gbuf, struct greybus_host_device *hd, } static struct greybus_host_driver es1_driver = { - .hd_priv_size = sizeof(struct es1_ap_dev), - .alloc_gbuf = alloc_gbuf, - .free_gbuf = free_gbuf, - .send_svc_msg = send_svc_msg, - .submit_gbuf = submit_gbuf, + .hd_priv_size = sizeof(struct es1_ap_dev), + .alloc_gbuf_data = alloc_gbuf_data, + .free_gbuf_data = free_gbuf_data, + .send_svc_msg = send_svc_msg, + .submit_gbuf = submit_gbuf, }; /* Callback for when we get a SVC message */ diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 6cc752cdd1db..40174b8f63f4 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -19,6 +19,9 @@ #include "greybus.h" + +static struct kmem_cache *gbuf_head_cache; + static struct gbuf *__alloc_gbuf(struct greybus_device *gdev, struct gdev_cport *cport, gbuf_complete_t complete, @@ -27,11 +30,7 @@ static struct gbuf *__alloc_gbuf(struct greybus_device *gdev, { struct gbuf *gbuf; - /* - * change this to a slab allocation if it's too slow, but for now, let's - * be dumb and simple. - */ - gbuf = kzalloc(sizeof(*gbuf), gfp_mask); + gbuf = kmem_cache_zalloc(gbuf_head_cache, gfp_mask); if (!gbuf) return NULL; @@ -73,10 +72,12 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, if (!gbuf) return NULL; + gbuf->direction = GBUF_DIRECTION_OUT; + /* Host controller specific allocation for the actual buffer */ - retval = gbuf->gdev->hd->driver->alloc_gbuf(gbuf, size, gfp_mask); + retval = gbuf->gdev->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); if (retval) { - kfree(gbuf); + greybus_free_gbuf(gbuf); return NULL; } @@ -90,10 +91,15 @@ static void free_gbuf(struct kref *kref) { struct gbuf *gbuf = container_of(kref, struct gbuf, kref); - /* let the host controller free what it wants to */ - gbuf->gdev->hd->driver->free_gbuf(gbuf); + /* If the direction is "out" then the host controller frees the data */ + if (gbuf->direction == GBUF_DIRECTION_OUT) { + gbuf->gdev->hd->driver->free_gbuf_data(gbuf); + } else { + /* we "own" this in data, so free it ourselves */ + kfree(gbuf->transfer_buffer); + } - kfree(gbuf); + kmem_cache_free(gbuf_head_cache, gbuf); } void greybus_free_gbuf(struct gbuf *gbuf) @@ -123,6 +129,43 @@ int greybus_kill_gbuf(struct gbuf *gbuf) return -ENOMEM; } +struct cport_msg { + struct gbuf *gbuf; + struct work_struct event; +}; + +static struct workqueue_struct *cport_workqueue; + +static void cport_process_event(struct work_struct *work) +{ + struct cport_msg *cm; + struct gbuf *gbuf; + + cm = container_of(work, struct cport_msg, event); + + gbuf = cm->gbuf; + + /* call the gbuf handler */ + gbuf->complete(gbuf); + + /* free all the memory */ + greybus_free_gbuf(gbuf); + kfree(cm); +} + +static void cport_create_event(struct gbuf *gbuf) +{ + struct cport_msg *cm; + + /* Slow alloc, does it matter??? */ + cm = kmalloc(sizeof(*cm), GFP_ATOMIC); + + /* Queue up the cport message to be handled in user context */ + cm->gbuf = gbuf; + INIT_WORK(&cm->event, cport_process_event); + queue_work(cport_workqueue, &cm->event); +} + #define MAX_CPORTS 1024 struct gb_cport_handler { gbuf_complete_t handler; @@ -153,37 +196,11 @@ void gb_deregister_cport_complete(int cport) cport_handler[cport].handler = NULL; } -struct cport_msg { - struct gbuf *gbuf; - struct work_struct event; -}; - -static struct workqueue_struct *cport_workqueue; - -static void cport_process_event(struct work_struct *work) -{ - struct cport_msg *cm; - struct gbuf *gbuf; - - cm = container_of(work, struct cport_msg, event); - - gbuf = cm->gbuf; - - /* call the gbuf handler */ - gbuf->complete(gbuf); - - /* free all the memory */ - kfree(gbuf->transfer_buffer); - kfree(gbuf); - kfree(cm); -} - void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, size_t length) { struct gb_cport_handler *ch; struct gbuf *gbuf; - struct cport_msg *cm; /* first check to see if we have a cport handler for this cport */ ch = &cport_handler[cport]; @@ -203,6 +220,7 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, return; } gbuf->hdpriv = hd; + gbuf->direction = GBUF_DIRECTION_IN; /* * FIXME: @@ -217,29 +235,16 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, } memcpy(gbuf->transfer_buffer, data, length); gbuf->transfer_buffer_length = length; + gbuf->actual_length = length; - /* Again with the slow allocate... */ - cm = kmalloc(sizeof(*cm), GFP_ATOMIC); - - /* Queue up the cport message to be handled in user context */ - cm->gbuf = gbuf; - INIT_WORK(&cm->event, cport_process_event); - queue_work(cport_workqueue, &cm->event); + cport_create_event(gbuf); } EXPORT_SYMBOL_GPL(greybus_cport_in_data); /* Can be called in interrupt context, do the work and get out of here */ void greybus_gbuf_finished(struct gbuf *gbuf) { - struct cport_msg *cm; - - /* Again with the slow allocate... */ - cm = kmalloc(sizeof(*cm), GFP_ATOMIC); - cm->gbuf = gbuf; - INIT_WORK(&cm->event, cport_process_event); - queue_work(cport_workqueue, &cm->event); - - // FIXME - implement + cport_create_event(gbuf); } EXPORT_SYMBOL_GPL(greybus_gbuf_finished); @@ -249,10 +254,13 @@ int gb_gbuf_init(void) if (!cport_workqueue) return -ENOMEM; + gbuf_head_cache = kmem_cache_create("gbuf_head_cache", + sizeof(struct gbuf), 0, 0, NULL); return 0; } void gb_gbuf_exit(void) { destroy_workqueue(cport_workqueue); + kmem_cache_destroy(gbuf_head_cache); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 17a01bd41ee5..9802ccec79ea 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -36,6 +36,68 @@ .serial_number = (s), +/* + gbuf + + This is the "main" data structure to send / receive Greybus messages + + There are two different "views" of a gbuf structure: + - a greybus driver + - a greybus host controller + + A Greybus driver needs to worry about the following: + - creating a gbuf + - putting data into a gbuf + - sending a gbuf to a device + - receiving a gbuf from a device + + Creating a gbuf: + A greybus driver calls greybus_alloc_gbuf() + Putting data into a gbuf: + copy data into gbuf->transfer_buffer + Send a gbuf: + A greybus driver calls greybus_submit_gbuf() + The completion function in a gbuf will be called if the gbuf is successful + or not. That completion function runs in user context. After the + completion function is called, the gbuf must not be touched again as the + greybus core "owns" it. But, if a greybus driver wants to "hold on" to a + gbuf after the completion function has been called, a reference must be + grabbed on the gbuf with a call to greybus_get_gbuf(). When finished with + the gbuf, call greybus_free_gbuf() and when the last reference count is + dropped, it will be removed from the system. + Receive a gbuf: + A greybus driver calls gb_register_cport_complete() with a pointer to the + callback function to be called for when a gbuf is received from a specific + cport and device. That callback will be made in user context with a gbuf + when it is received. To stop receiving messages, call + gb_deregister_cport_complete() for a specific cport. + + + Greybus Host controller drivers need to provide + - a way to allocate the transfer buffer for a gbuf + - a way to free the transfer buffer for a gbuf when it is "finished" + - a way to submit gbuf for transmissions + - notify the core the gbuf is complete + - receive gbuf from the wire and submit them to the core + - a way to send and receive svc messages + Allocate a transfer buffer + the host controller function alloc_gbuf_data is called + Free a transfer buffer + the host controller function free_gbuf_data is called + Submit a gbuf to the hardware + the host controller function submit_gbuf is called + Notify the gbuf is complete + the host controller driver must call greybus_gbuf_finished() + Submit a SVC message to the hardware + the host controller function send_svc_msg is called + Receive gbuf messages + the host controller driver must call greybus_cport_in_data() with the data + Reveive SVC messages from the hardware + The host controller driver must call gb_new_ap_msg + + +*/ + struct gbuf; @@ -66,10 +128,9 @@ struct gbuf { u32 transfer_buffer_length; u32 actual_length; -#if 0 - struct scatterlist *sg; // FIXME do we need? - int num_sgs; -#endif +#define GBUF_DIRECTION_OUT 0 +#define GBUF_DIRECTION_IN 1 + unsigned int direction : 1; /* 0 is out, 1 is in */ void *context; gbuf_complete_t complete; @@ -105,8 +166,8 @@ struct svc_msg; struct greybus_host_driver { size_t hd_priv_size; - int (*alloc_gbuf)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); - void (*free_gbuf)(struct gbuf *gbuf); + int (*alloc_gbuf_data)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); + void (*free_gbuf_data)(struct gbuf *gbuf); int (*send_svc_msg)(struct svc_msg *svc_msg, struct greybus_host_device *hd); int (*submit_gbuf)(struct gbuf *gbuf, struct greybus_host_device *hd, -- cgit v1.2.3-59-g8ed1b From 798ea88abec88852e6dae2afb265560f9ec56432 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 21 Sep 2014 18:16:41 -0700 Subject: greybus: svc_msg.h: add data for hotplug message --- drivers/staging/greybus/svc_msg.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index e84e01cf3cce..c290547305ac 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -77,6 +77,7 @@ enum svc_function_hotplug_event { struct svc_function_hotplug { __u8 hotplug_event; /* enum svc_function_hotplug_event */ __u8 module_id; + __u8 data[0]; }; enum svc_function_ddb_type { -- cgit v1.2.3-59-g8ed1b From 85e0066c597c5fd9bc278eac79065251aa507c65 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 21 Sep 2014 18:17:12 -0700 Subject: greybus: greybus.h: add function prototype for add/remove a module --- drivers/staging/greybus/greybus.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 9802ccec79ea..f804b198254d 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -288,6 +288,9 @@ const u8 *greybus_string(struct greybus_device *gdev, int id); /* Internal functions to gb module, move to internal .h file eventually. */ +void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data); +void gb_remove_module(struct greybus_host_device *hd, u8 module_id); + int gb_new_ap_msg(u8 *data, int length, struct greybus_host_device *hd); int gb_ap_init(void); void gb_ap_exit(void); -- cgit v1.2.3-59-g8ed1b From 6779997d78bf4c98857c397ed3d1dfede8949835 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 21 Sep 2014 18:17:36 -0700 Subject: greybus: core.c: create empty functions to keep linking working for hotplug/remove --- drivers/staging/greybus/core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index a13cf9f05059..9a232a0a9227 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -346,6 +346,11 @@ static int create_cport(struct greybus_device *gdev, * Pass in a buffer that _should_ contain a Greybus module manifest * and spit out a greybus device structure. */ +void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data) +{ + // FIXME - should be the new module call... +} + struct greybus_device *greybus_new_module(struct device *parent, int module_number, u8 *data, int size) { @@ -466,6 +471,11 @@ error: return NULL; } +void gb_remove_module(struct greybus_host_device *hd, u8 module_id) +{ + // FIXME should be the remove_device call... +} + void greybus_remove_device(struct greybus_device *gdev) { /* tear down all of the "sub device types" for this device */ -- cgit v1.2.3-59-g8ed1b From 00c52e4d3e37f3cd56c2159c7827f6d08e198262 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 21 Sep 2014 18:19:54 -0700 Subject: greybus: ap: start validating the message better We check the type of the message now. Start to check the size of the payload to match the size of the message type. Still more work to do needed here. Also "hooked up" the hotplug message, but doesn't call anything as the core doesn't implement that yet... --- drivers/staging/greybus/ap.c | 59 +++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 2a60de0cec89..267d8b51fd4b 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -61,10 +61,17 @@ static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) static void svc_handshake(struct svc_function_handshake *handshake, - struct greybus_host_device *hd) + int payload_length, struct greybus_host_device *hd) { struct svc_msg *svc_msg; + if (payload_length != sizeof(struct svc_function_handshake)) { + dev_err(hd->parent, + "Illegal size of svc handshake message %d\n", + payload_length); + return; + } + /* A new SVC communication channel, let's verify a supported version */ if ((handshake->version_major != GREYBUS_VERSION_MAJOR) && (handshake->version_minor != GREYBUS_VERSION_MINOR)) { @@ -91,8 +98,15 @@ static void svc_handshake(struct svc_function_handshake *handshake, } static void svc_management(struct svc_function_unipro_management *management, - struct greybus_host_device *hd) + int payload_length, struct greybus_host_device *hd) { + if (payload_length != sizeof(struct svc_function_unipro_management)) { + dev_err(hd->parent, + "Illegal size of svc management message %d\n", + payload_length); + return; + } + /* What? An AP should not get this message */ dev_err(hd->parent, "Got an svc management message???\n"); } @@ -104,13 +118,15 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, switch (hotplug->hotplug_event) { case SVC_HOTPLUG_EVENT: + /* Add a new module to the system */ dev_dbg(hd->parent, "module id %d added\n", module_id); - // FIXME - add the module to the system + gb_add_module(hd, module_id, hotplug->data); break; case SVC_HOTUNPLUG_EVENT: + /* Remove a module from the system */ dev_dbg(hd->parent, "module id %d removed\n", module_id); - // FIXME - remove the module from the system + gb_remove_module(hd, module_id); break; default: @@ -160,15 +176,27 @@ static void svc_suspend(struct svc_function_suspend *suspend, dev_err(hd->parent, "Got an suspend message???\n"); } -static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) +static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg, + struct greybus_host_device *hd) { struct svc_msg *svc_msg; - - // FIXME - validate message, right now we are trusting the size and data - // from the AP, what could go wrong? :) - // for now, just cast the pointer and run away... + struct svc_msg_header *header; svc_msg = (struct svc_msg *)ap_msg->data; + header = &svc_msg->header; + + /* Validate the message type */ + if (header->message_type != SVC_MSG_DATA) { + dev_err(hd->parent, "message type %d received?\n", + header->message_type); + return NULL; + } + + /* + * The validation of the size of the message buffer happens in each + * svc_* function, due to the different types of messages, keeping the + * logic for each message only in one place. + */ return svc_msg; } @@ -178,24 +206,25 @@ static void ap_process_event(struct work_struct *work) struct svc_msg *svc_msg; struct greybus_host_device *hd; struct ap_msg *ap_msg; + int payload_length; ap_msg = container_of(work, struct ap_msg, event); hd = ap_msg->hd; /* Turn the "raw" data into a real message */ - svc_msg = convert_ap_message(ap_msg); - if (!svc_msg) { - // FIXME log an error??? + svc_msg = convert_ap_message(ap_msg, hd); + if (!svc_msg) return; - } + + payload_length = le16_to_cpu(svc_msg->header.payload_length); /* Look at the message to figure out what to do with it */ switch (svc_msg->header.function_id) { case SVC_FUNCTION_HANDSHAKE: - svc_handshake(&svc_msg->handshake, hd); + svc_handshake(&svc_msg->handshake, payload_length, hd); break; case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT: - svc_management(&svc_msg->management, hd); + svc_management(&svc_msg->management, payload_length, hd); break; case SVC_FUNCTION_HOTPLUG: svc_hotplug(&svc_msg->hotplug, hd); -- cgit v1.2.3-59-g8ed1b From d0cfd109c7eb5df548dd98bfa7f2dba370c68e1c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 21 Sep 2014 19:10:39 -0700 Subject: greybus: ap: validate the rest of the svc message buffer sizes --- drivers/staging/greybus/ap.c | 63 +++++++++++++++++++++++++++++++-------- drivers/staging/greybus/core.c | 3 +- drivers/staging/greybus/greybus.h | 3 +- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 267d8b51fd4b..21f2e3327f12 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -112,49 +112,86 @@ static void svc_management(struct svc_function_unipro_management *management, } static void svc_hotplug(struct svc_function_hotplug *hotplug, - struct greybus_host_device *hd) + int payload_length, struct greybus_host_device *hd) { u8 module_id = hotplug->module_id; switch (hotplug->hotplug_event) { case SVC_HOTPLUG_EVENT: /* Add a new module to the system */ + if (payload_length < 0x03) { + /* Hotplug message is at lest 3 bytes big */ + dev_err(hd->parent, + "Illegal size of svc hotplug message %d\n", + payload_length); + return; + } dev_dbg(hd->parent, "module id %d added\n", module_id); - gb_add_module(hd, module_id, hotplug->data); + gb_add_module(hd, module_id, hotplug->data, + payload_length - 0x02); break; case SVC_HOTUNPLUG_EVENT: /* Remove a module from the system */ + if (payload_length != 0x02) { + /* Hotunplug message is only 2 bytes big */ + dev_err(hd->parent, + "Illegal size of svc hotunplug message %d\n", + payload_length); + return; + } dev_dbg(hd->parent, "module id %d removed\n", module_id); gb_remove_module(hd, module_id); break; default: dev_err(hd->parent, - "received invalid hotplug message type %d\n", + "Received invalid hotplug message type %d\n", hotplug->hotplug_event); break; } } static void svc_ddb(struct svc_function_ddb *ddb, - struct greybus_host_device *hd) + int payload_length, struct greybus_host_device *hd) { + /* + * Need to properly validate payload_length once we start + * to handle ddb messages, but for now, we don't, so no need to check + * anything. + */ + /* What? An AP should not get this message */ dev_err(hd->parent, "Got an svc DDB message???\n"); } static void svc_power(struct svc_function_power *power, - struct greybus_host_device *hd) + int payload_length, struct greybus_host_device *hd) { u8 module_id = power->module_id; + /* + * The AP is only allowed to get a Battery Status message, not a Battery + * Status Request + */ if (power->power_type != SVC_POWER_BATTERY_STATUS) { - dev_err(hd->parent, "received invalid power type %d\n", + dev_err(hd->parent, "Received invalid power type %d\n", power->power_type); return; } + /* + * As struct struct svc_function_power_battery_status_request is 0 bytes + * big, we can just check the union of the whole structure to validate + * the size of this message. + */ + if (payload_length != sizeof(struct svc_function_power)) { + dev_err(hd->parent, + "Illegal size of svc power message %d\n", + payload_length); + return; + } + dev_dbg(hd->parent, "power status for module id %d is %d\n", module_id, power->status.status); @@ -163,14 +200,14 @@ static void svc_power(struct svc_function_power *power, } static void svc_epm(struct svc_function_epm *epm, - struct greybus_host_device *hd) + int payload_length, struct greybus_host_device *hd) { /* What? An AP should not get this message */ dev_err(hd->parent, "Got an EPM message???\n"); } static void svc_suspend(struct svc_function_suspend *suspend, - struct greybus_host_device *hd) + int payload_length, struct greybus_host_device *hd) { /* What? An AP should not get this message */ dev_err(hd->parent, "Got an suspend message???\n"); @@ -227,19 +264,19 @@ static void ap_process_event(struct work_struct *work) svc_management(&svc_msg->management, payload_length, hd); break; case SVC_FUNCTION_HOTPLUG: - svc_hotplug(&svc_msg->hotplug, hd); + svc_hotplug(&svc_msg->hotplug, payload_length, hd); break; case SVC_FUNCTION_DDB: - svc_ddb(&svc_msg->ddb, hd); + svc_ddb(&svc_msg->ddb, payload_length, hd); break; case SVC_FUNCTION_POWER: - svc_power(&svc_msg->power, hd); + svc_power(&svc_msg->power, payload_length, hd); break; case SVC_FUNCTION_EPM: - svc_epm(&svc_msg->epm, hd); + svc_epm(&svc_msg->epm, payload_length, hd); break; case SVC_FUNCTION_SUSPEND: - svc_suspend(&svc_msg->suspend, hd); + svc_suspend(&svc_msg->suspend, payload_length, hd); break; default: dev_err(hd->parent, "received invalid SVC function ID %d\n", diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9a232a0a9227..b4e3093e3899 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -346,7 +346,8 @@ static int create_cport(struct greybus_device *gdev, * Pass in a buffer that _should_ contain a Greybus module manifest * and spit out a greybus device structure. */ -void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data) +void gb_add_module(struct greybus_host_device *hd, u8 module_id, + u8 *data, int size) { // FIXME - should be the new module call... } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f804b198254d..855cb0e02bb7 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -288,7 +288,8 @@ const u8 *greybus_string(struct greybus_device *gdev, int id); /* Internal functions to gb module, move to internal .h file eventually. */ -void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data); +void gb_add_module(struct greybus_host_device *hd, u8 module_id, + u8 *data, int size); void gb_remove_module(struct greybus_host_device *hd, u8 module_id); int gb_new_ap_msg(u8 *data, int length, struct greybus_host_device *hd); -- cgit v1.2.3-59-g8ed1b From 4a833fdb7fc73039b06c1d879d896ebab0df7bd9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 21 Sep 2014 19:17:55 -0700 Subject: greybus: core: hook up the hotplug message We should now try to parse the manifest and create a device based on the manifest. Not hooked up to the driver core yet, so removing it isn't going to do anything except cause problems... --- drivers/staging/greybus/core.c | 38 ++++++++++++++++---------------------- drivers/staging/greybus/greybus.h | 3 --- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index b4e3093e3899..e4a78b4f9bd3 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -341,19 +341,13 @@ static int create_cport(struct greybus_device *gdev, } /** - * greybus_new_module: + * gb_add_module * * Pass in a buffer that _should_ contain a Greybus module manifest - * and spit out a greybus device structure. + * and register a greybus device structure with the kernel core. */ void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size) -{ - // FIXME - should be the new module call... -} - -struct greybus_device *greybus_new_module(struct device *parent, - int module_number, u8 *data, int size) { struct greybus_device *gdev; struct greybus_manifest *manifest; @@ -364,26 +358,26 @@ struct greybus_device *greybus_new_module(struct device *parent, /* we have to have at _least_ the manifest header */ if (size <= sizeof(manifest->header)) - return NULL; + return; gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); if (!gdev) - return NULL; + return; - gdev->module_number = module_number; - gdev->dev.parent = parent; + gdev->module_number = module_id; + gdev->dev.parent = hd->parent; gdev->dev.driver = NULL; gdev->dev.bus = &greybus_bus_type; gdev->dev.type = &greybus_module_type; gdev->dev.groups = greybus_module_groups; - gdev->dev.dma_mask = parent->dma_mask; + gdev->dev.dma_mask = hd->parent->dma_mask; device_initialize(&gdev->dev); - dev_set_name(&gdev->dev, "%d", module_number); + dev_set_name(&gdev->dev, "%d", module_id); manifest = (struct greybus_manifest *)data; overall_size = le16_to_cpu(manifest->header.size); if (overall_size != size) { - dev_err(parent, "size != manifest header size, %d != %d\n", + dev_err(hd->parent, "size != manifest header size, %d != %d\n", size, overall_size); goto error; } @@ -394,7 +388,7 @@ struct greybus_device *greybus_new_module(struct device *parent, /* Validate major/minor number */ if ((version_major != GREYBUS_VERSION_MAJOR) || (version_minor != GREYBUS_VERSION_MINOR)) { - dev_err(parent, + dev_err(hd->parent, "Invalid greybus versions, expected %d.%d, got %d.%d\n", GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR, version_major, version_minor); @@ -409,13 +403,14 @@ struct greybus_device *greybus_new_module(struct device *parent, size_t data_size; if (size < sizeof(desc->header)) { - dev_err(parent, "remaining size %d too small\n", size); + dev_err(hd->parent, "remaining size %d too small\n", + size); goto error; } desc = (struct greybus_descriptor *)data; desc_size = le16_to_cpu(desc->header.size); if (size < desc_size) { - dev_err(parent, "descriptor size %d too big\n", + dev_err(hd->parent, "descriptor size %d too big\n", desc_size); goto error; } @@ -448,7 +443,7 @@ struct greybus_device *greybus_new_module(struct device *parent, case GREYBUS_TYPE_INVALID: default: - dev_err(parent, "invalid descriptor type %d\n", + dev_err(hd->parent, "invalid descriptor type %d\n", desc->header.type); goto error; } @@ -464,12 +459,11 @@ struct greybus_device *greybus_new_module(struct device *parent, // FIXME device_add(&gdev->dev); - - return gdev; + //return gdev; + return; error: put_device(&gdev->dev); greybus_module_release(&gdev->dev); - return NULL; } void gb_remove_module(struct greybus_host_device *hd, u8 module_id) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 855cb0e02bb7..1f996b97a072 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -279,9 +279,6 @@ void greybus_deregister(struct greybus_driver *driver); int greybus_disabled(void); -struct greybus_device *greybus_new_module(struct device *parent, - int module_number, u8 *data, - int size); void greybus_remove_device(struct greybus_device *gdev); const u8 *greybus_string(struct greybus_device *gdev, int id); -- cgit v1.2.3-59-g8ed1b From 648cb6cb75a1ef09200a24b280a2932caef88597 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Mon, 22 Sep 2014 15:51:48 -0400 Subject: greybus: es1-ap-usb: fix svc control pipe flags The control message flags currently indicate USB_DIR_IN, which doesn't allow the data phase carrying the SVC message to be send to the device. Change this to USB_DIR_OUT so our SVC message buffer reaches the device. Also, the recipient is USB_RECIP_OTHER but almost all real devices that handle vendor setup requests seem to set this as USB_RECIP_INTERFACE. As a result, functionfs-based gadgets don't handle vendor setup requests with a recipient of OTHER. Change this to USB_RECIP_INTERFACE to work with the functionfs-based emulator and this should be no issue for the firmware to implement to match. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 1a47da6fb933..47c4524d6f5e 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -149,7 +149,7 @@ static int send_svc_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd) usb_sndctrlpipe(es1->usb_dev, es1->control_endpoint), 0x01, /* vendor request AP message */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, (char *)svc_msg, sizeof(*svc_msg), -- cgit v1.2.3-59-g8ed1b From be5064c75bb9e513698142584f4bb64de73e9f18 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Mon, 22 Sep 2014 15:51:49 -0400 Subject: greybus: initialize all fields in an SVC handshake message Currently only the handshake_type is being initialized when responding to an SVC handshake request. Update this to explicitly set all header/payload fields appropriately. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 21f2e3327f12..78e9e4a1db2e 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -93,6 +93,12 @@ static void svc_handshake(struct svc_function_handshake *handshake, if (!svc_msg) return; + svc_msg->header.function_id = SVC_FUNCTION_HANDSHAKE; + svc_msg->header.message_type = SVC_MSG_DATA; + svc_msg->header.payload_length = + cpu_to_le16(sizeof(struct svc_function_handshake)); + svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR; + svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR; svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; svc_msg_send(svc_msg, hd); } -- cgit v1.2.3-59-g8ed1b From 69f93abf1181082c7a3dfa24b2ba4a2933ce2c1c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 22 Sep 2014 18:53:02 -0500 Subject: greybus: simple fixes A few silly little fixes. - Clear out some unnecessary #includes in "debugfs.c" - Drop some unneeded parentheses in hd_to_es1() - Use &hd->hd_priv in hd_to_es1() to emphasize we are working with an embedded array, not a pointer - Fix a comment in the header for ap_probe() - Drop a duplicate #include in "gpio-gb.c" - Fix a use-before-set problem in set_serial_info() Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/debugfs.c | 5 ----- drivers/staging/greybus/es1-ap-usb.c | 4 ++-- drivers/staging/greybus/gpio-gb.c | 1 - drivers/staging/greybus/uart-gb.c | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c index 4e313f1a5143..ef292f43db45 100644 --- a/drivers/staging/greybus/debugfs.c +++ b/drivers/staging/greybus/debugfs.c @@ -8,11 +8,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include #include #include "greybus.h" diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 47c4524d6f5e..eadbfc80cb2b 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -77,7 +77,7 @@ struct es1_ap_dev { static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) { - return (struct es1_ap_dev *)(hd->hd_priv); + return (struct es1_ap_dev *)&hd->hd_priv; } static void cport_out_callback(struct urb *urb); @@ -370,7 +370,7 @@ exit: * 1 Control - usual USB stuff + AP -> SVC messages * 1 Interrupt IN - SVC -> AP messages * 1 Bulk IN - CPort data in - * 1 Bulk OUT - CPorta data out + * 1 Bulk OUT - CPort data out */ static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index bcf65087e8e5..485480dc7233 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -10,7 +10,6 @@ #include #include #include -#include #include "greybus.h" struct gb_gpio_device { diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 39a6e0191434..75ddd18792ce 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -257,7 +257,7 @@ static int set_serial_info(struct gb_tty *gb_tty, struct serial_struct new_serial; unsigned int closing_wait; unsigned int close_delay; - int retval; + int retval = 0; if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) return -EFAULT; -- cgit v1.2.3-59-g8ed1b From 778c69c9e2fbb7462e5ebffe42845c9cdf75b1e2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 22 Sep 2014 19:19:03 -0500 Subject: greybus: rename struct greybus_device The greybus_device structure represents an Ara phone module. It does *not* (necessarily) represent a UniPro device, nor any device (like an i2c adapter) that might reside on an Ara module. As such, rename struct greybus_device to be struct greybus_module. Rename all symbols having that type to be "gmod" rather than "gdev". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 12 +-- drivers/staging/greybus/core.c | 196 +++++++++++++++++------------------ drivers/staging/greybus/es1-ap-usb.c | 2 +- drivers/staging/greybus/gbuf.c | 30 +++--- drivers/staging/greybus/gpio-gb.c | 14 +-- drivers/staging/greybus/greybus.h | 64 ++++++------ drivers/staging/greybus/greybus_id.h | 2 +- drivers/staging/greybus/i2c-gb.c | 22 ++-- drivers/staging/greybus/sdio-gb.c | 10 +- drivers/staging/greybus/sysfs.c | 44 ++++---- drivers/staging/greybus/test_sink.c | 4 +- drivers/staging/greybus/uart-gb.c | 20 ++-- 12 files changed, 210 insertions(+), 210 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 7d124331f22f..5f1bf004dfbc 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -18,7 +18,7 @@ struct gb_battery { // we will want to keep the battery stats in here as we will be getting // updates from the SVC "on the fly" so we don't have to always go ask // the battery for some information. Hopefully... - struct greybus_device *gdev; + struct greybus_module *gmod; }; #define to_gb_battery(x) container_of(x, struct gb_battery, bat) @@ -100,7 +100,7 @@ static enum power_supply_property battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, }; -int gb_battery_probe(struct greybus_device *gdev, +int gb_battery_probe(struct greybus_module *gmod, const struct greybus_module_id *id) { struct gb_battery *gb; @@ -120,21 +120,21 @@ int gb_battery_probe(struct greybus_device *gdev, b->num_properties = ARRAY_SIZE(battery_props), b->get_property = get_property, - retval = power_supply_register(&gdev->dev, b); + retval = power_supply_register(&gmod->dev, b); if (retval) { kfree(gb); return retval; } - gdev->gb_battery = gb; + gmod->gb_battery = gb; return 0; } -void gb_battery_disconnect(struct greybus_device *gdev) +void gb_battery_disconnect(struct greybus_module *gmod) { struct gb_battery *gb; - gb = gdev->gb_battery; + gb = gmod->gb_battery; power_supply_unregister(&gb->bat); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index e4a78b4f9bd3..296ff61aaefb 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -30,14 +30,14 @@ int greybus_disabled(void) } EXPORT_SYMBOL_GPL(greybus_disabled); -static int greybus_match_one_id(struct greybus_device *gdev, +static int greybus_match_one_id(struct greybus_module *gmod, const struct greybus_module_id *id) { struct greybus_descriptor_module_id *module_id; struct greybus_descriptor_serial_number *serial_num; - module_id = &gdev->module_id; - serial_num = &gdev->serial_number; + module_id = &gmod->module_id; + serial_num = &gmod->serial_number; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && (id->vendor != le16_to_cpu(module_id->vendor))) @@ -55,7 +55,7 @@ static int greybus_match_one_id(struct greybus_device *gdev, } static const struct greybus_module_id *greybus_match_id( - struct greybus_device *gdev, + struct greybus_module *gmod, const struct greybus_module_id *id) { if (id == NULL) @@ -63,20 +63,20 @@ static const struct greybus_module_id *greybus_match_id( for (; id->vendor || id->product || id->serial_number || id->driver_info ; id++) { - if (greybus_match_one_id(gdev, id)) + if (greybus_match_one_id(gmod, id)) return id; } return NULL; } -static int greybus_device_match(struct device *dev, struct device_driver *drv) +static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct greybus_device *gdev = to_greybus_device(dev); + struct greybus_module *gmod = to_greybus_module(dev); const struct greybus_module_id *id; - id = greybus_match_id(gdev, driver->id_table); + id = greybus_match_id(gmod, driver->id_table); if (id) return 1; /* FIXME - Dyanmic ids? */ @@ -85,7 +85,7 @@ static int greybus_device_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { - /* struct greybus_device *gdev = to_greybus_device(dev); */ + /* struct greybus_module *gmod = to_greybus_module(dev); */ /* FIXME - add some uevents here... */ return 0; @@ -93,23 +93,23 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) static struct bus_type greybus_bus_type = { .name = "greybus", - .match = greybus_device_match, + .match = greybus_module_match, .uevent = greybus_uevent, }; static int greybus_probe(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct greybus_device *gdev = to_greybus_device(dev); + struct greybus_module *gmod = to_greybus_module(dev); const struct greybus_module_id *id; int retval; /* match id */ - id = greybus_match_id(gdev, driver->id_table); + id = greybus_match_id(gmod, driver->id_table); if (!id) return -ENODEV; - retval = driver->probe(gdev, id); + retval = driver->probe(gmod, id); if (retval) return retval; @@ -119,9 +119,9 @@ static int greybus_probe(struct device *dev) static int greybus_remove(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct greybus_device *gdev = to_greybus_device(dev); + struct greybus_module *gmod = to_greybus_module(dev); - driver->disconnect(gdev); + driver->disconnect(gmod); return 0; } @@ -157,27 +157,27 @@ EXPORT_SYMBOL_GPL(greybus_deregister); static void greybus_module_release(struct device *dev) { - struct greybus_device *gdev = to_greybus_device(dev); + struct greybus_module *gmod = to_greybus_module(dev); int i; - for (i = 0; i < gdev->num_strings; ++i) - kfree(gdev->string[i]); - for (i = 0; i < gdev->num_cports; ++i) - kfree(gdev->cport[i]); - kfree(gdev); + for (i = 0; i < gmod->num_strings; ++i) + kfree(gmod->string[i]); + for (i = 0; i < gmod->num_cports; ++i) + kfree(gmod->cport[i]); + kfree(gmod); } -const u8 *greybus_string(struct greybus_device *gdev, int id) +const u8 *greybus_string(struct greybus_module *gmod, int id) { int i; - struct gdev_string *string; + struct gmod_string *string; - if (!gdev) + if (!gmod) return NULL; - for (i = 0; i < gdev->num_strings; ++i) { - string = gdev->string[i]; + for (i = 0; i < gmod->num_strings; ++i) { + string = gmod->string[i]; if (string->id == id) return &string->string[0]; } @@ -189,44 +189,44 @@ static struct device_type greybus_module_type = { .release = greybus_module_release, }; -static int gb_init_subdevs(struct greybus_device *gdev, +static int gb_init_subdevs(struct greybus_module *gmod, const struct greybus_module_id *id) { int retval; /* Allocate all of the different "sub device types" for this device */ - retval = gb_i2c_probe(gdev, id); + retval = gb_i2c_probe(gmod, id); if (retval) goto error_i2c; - retval = gb_gpio_probe(gdev, id); + retval = gb_gpio_probe(gmod, id); if (retval) goto error_gpio; - retval = gb_sdio_probe(gdev, id); + retval = gb_sdio_probe(gmod, id); if (retval) goto error_sdio; - retval = gb_tty_probe(gdev, id); + retval = gb_tty_probe(gmod, id); if (retval) goto error_tty; - retval = gb_battery_probe(gdev, id); + retval = gb_battery_probe(gmod, id); if (retval) goto error_battery; return 0; error_battery: - gb_tty_disconnect(gdev); + gb_tty_disconnect(gmod); error_tty: - gb_sdio_disconnect(gdev); + gb_sdio_disconnect(gmod); error_sdio: - gb_gpio_disconnect(gdev); + gb_gpio_disconnect(gmod); error_gpio: - gb_i2c_disconnect(gdev); + gb_i2c_disconnect(gmod); error_i2c: return retval; @@ -236,106 +236,106 @@ static const struct greybus_module_id fake_gb_id = { GREYBUS_DEVICE(0x42, 0x42) }; -static int create_function(struct greybus_device *gdev, +static int create_function(struct greybus_module *gmod, struct greybus_descriptor_function *function, size_t desc_size) { if (desc_size != sizeof(*function)) { - dev_err(gdev->dev.parent, "invalid function header size %zu\n", + dev_err(gmod->dev.parent, "invalid function header size %zu\n", desc_size); return -EINVAL; } - memcpy(&gdev->function, function, desc_size); + memcpy(&gmod->function, function, desc_size); return 0; } -static int create_module_id(struct greybus_device *gdev, +static int create_module_id(struct greybus_module *gmod, struct greybus_descriptor_module_id *module_id, size_t desc_size) { if (desc_size != sizeof(*module_id)) { - dev_err(gdev->dev.parent, "invalid module header size %zu\n", + dev_err(gmod->dev.parent, "invalid module header size %zu\n", desc_size); return -EINVAL; } - memcpy(&gdev->module_id, module_id, desc_size); + memcpy(&gmod->module_id, module_id, desc_size); return 0; } -static int create_serial_number(struct greybus_device *gdev, +static int create_serial_number(struct greybus_module *gmod, struct greybus_descriptor_serial_number *serial_num, size_t desc_size) { if (desc_size != sizeof(*serial_num)) { - dev_err(gdev->dev.parent, "invalid serial number header size %zu\n", + dev_err(gmod->dev.parent, "invalid serial number header size %zu\n", desc_size); return -EINVAL; } - memcpy(&gdev->serial_number, serial_num, desc_size); + memcpy(&gmod->serial_number, serial_num, desc_size); return 0; } -static int create_string(struct greybus_device *gdev, +static int create_string(struct greybus_module *gmod, struct greybus_descriptor_string *string, size_t desc_size) { int string_size; - struct gdev_string *gdev_string; + struct gmod_string *gmod_string; - if ((gdev->num_strings + 1) >= MAX_STRINGS_PER_MODULE) { - dev_err(gdev->dev.parent, + if ((gmod->num_strings + 1) >= MAX_STRINGS_PER_MODULE) { + dev_err(gmod->dev.parent, "too many strings for this module!\n"); return -EINVAL; } if (desc_size < sizeof(*string)) { - dev_err(gdev->dev.parent, "invalid string header size %zu\n", + dev_err(gmod->dev.parent, "invalid string header size %zu\n", desc_size); return -EINVAL; } string_size = le16_to_cpu(string->length); - gdev_string = kzalloc(sizeof(*gdev_string) + string_size + 1, GFP_KERNEL); - if (!gdev_string) + gmod_string = kzalloc(sizeof(*gmod_string) + string_size + 1, GFP_KERNEL); + if (!gmod_string) return -ENOMEM; - gdev_string->length = string_size; - gdev_string->id = string->id; - memcpy(&gdev_string->string, &string->string, string_size); + gmod_string->length = string_size; + gmod_string->id = string->id; + memcpy(&gmod_string->string, &string->string, string_size); - gdev->string[gdev->num_strings] = gdev_string; - gdev->num_strings++; + gmod->string[gmod->num_strings] = gmod_string; + gmod->num_strings++; return 0; } -static int create_cport(struct greybus_device *gdev, +static int create_cport(struct greybus_module *gmod, struct greybus_descriptor_cport *cport, size_t desc_size) { - struct gdev_cport *gdev_cport; + struct gmod_cport *gmod_cport; - if ((gdev->num_cports + 1) >= MAX_CPORTS_PER_MODULE) { - dev_err(gdev->dev.parent, "too many cports for this module!\n"); + if ((gmod->num_cports + 1) >= MAX_CPORTS_PER_MODULE) { + dev_err(gmod->dev.parent, "too many cports for this module!\n"); return -EINVAL; } if (desc_size != sizeof(*cport)) { - dev_err(gdev->dev.parent, + dev_err(gmod->dev.parent, "invalid serial number header size %zu\n", desc_size); return -EINVAL; } - gdev_cport = kzalloc(sizeof(*gdev_cport), GFP_KERNEL); - if (!gdev_cport) + gmod_cport = kzalloc(sizeof(*gmod_cport), GFP_KERNEL); + if (!gmod_cport) return -ENOMEM; - gdev_cport->number = le16_to_cpu(cport->number); - gdev_cport->size = le16_to_cpu(cport->size); - gdev_cport->speed = cport->speed; + gmod_cport->number = le16_to_cpu(cport->number); + gmod_cport->size = le16_to_cpu(cport->size); + gmod_cport->speed = cport->speed; - gdev->cport[gdev->num_cports] = gdev_cport; - gdev->num_cports++; + gmod->cport[gmod->num_cports] = gmod_cport; + gmod->num_cports++; return 0; } @@ -349,7 +349,7 @@ static int create_cport(struct greybus_device *gdev, void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size) { - struct greybus_device *gdev; + struct greybus_module *gmod; struct greybus_manifest *manifest; int retval; int overall_size; @@ -360,19 +360,19 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, if (size <= sizeof(manifest->header)) return; - gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); - if (!gdev) + gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); + if (!gmod) return; - gdev->module_number = module_id; - gdev->dev.parent = hd->parent; - gdev->dev.driver = NULL; - gdev->dev.bus = &greybus_bus_type; - gdev->dev.type = &greybus_module_type; - gdev->dev.groups = greybus_module_groups; - gdev->dev.dma_mask = hd->parent->dma_mask; - device_initialize(&gdev->dev); - dev_set_name(&gdev->dev, "%d", module_id); + gmod->module_number = module_id; + gmod->dev.parent = hd->parent; + gmod->dev.driver = NULL; + gmod->dev.bus = &greybus_bus_type; + gmod->dev.type = &greybus_module_type; + gmod->dev.groups = greybus_module_groups; + gmod->dev.dma_mask = hd->parent->dma_mask; + device_initialize(&gmod->dev); + dev_set_name(&gmod->dev, "%d", module_id); manifest = (struct greybus_manifest *)data; overall_size = le16_to_cpu(manifest->header.size); @@ -418,27 +418,27 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, switch (le16_to_cpu(desc->header.type)) { case GREYBUS_TYPE_FUNCTION: - retval = create_function(gdev, &desc->function, + retval = create_function(gmod, &desc->function, data_size); break; case GREYBUS_TYPE_MODULE_ID: - retval = create_module_id(gdev, &desc->module_id, + retval = create_module_id(gmod, &desc->module_id, data_size); break; case GREYBUS_TYPE_SERIAL_NUMBER: - retval = create_serial_number(gdev, + retval = create_serial_number(gmod, &desc->serial_number, data_size); break; case GREYBUS_TYPE_STRING: - retval = create_string(gdev, &desc->string, data_size); + retval = create_string(gmod, &desc->string, data_size); break; case GREYBUS_TYPE_CPORT: - retval = create_cport(gdev, &desc->cport, data_size); + retval = create_cport(gmod, &desc->cport, data_size); break; case GREYBUS_TYPE_INVALID: @@ -453,17 +453,17 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, data += desc_size; } - retval = gb_init_subdevs(gdev, &fake_gb_id); + retval = gb_init_subdevs(gmod, &fake_gb_id); if (retval) goto error; - // FIXME device_add(&gdev->dev); + // FIXME device_add(&gmod->dev); - //return gdev; + //return gmod; return; error: - put_device(&gdev->dev); - greybus_module_release(&gdev->dev); + put_device(&gmod->dev); + greybus_module_release(&gmod->dev); } void gb_remove_module(struct greybus_host_device *hd, u8 module_id) @@ -471,16 +471,16 @@ void gb_remove_module(struct greybus_host_device *hd, u8 module_id) // FIXME should be the remove_device call... } -void greybus_remove_device(struct greybus_device *gdev) +void greybus_remove_device(struct greybus_module *gmod) { /* tear down all of the "sub device types" for this device */ - gb_i2c_disconnect(gdev); - gb_gpio_disconnect(gdev); - gb_sdio_disconnect(gdev); - gb_tty_disconnect(gdev); - gb_battery_disconnect(gdev); + gb_i2c_disconnect(gmod); + gb_gpio_disconnect(gmod); + gb_sdio_disconnect(gmod); + gb_tty_disconnect(gmod); + gb_battery_disconnect(gmod); - // FIXME - device_remove(&gdev->dev); + // FIXME - device_remove(&gmod->dev); } static DEFINE_MUTEX(hd_mutex); diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index eadbfc80cb2b..dfe96cb36a4e 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -92,7 +92,7 @@ static void cport_out_callback(struct urb *urb); */ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) { - struct es1_ap_dev *es1 = hd_to_es1(gbuf->gdev->hd); + struct es1_ap_dev *es1 = hd_to_es1(gbuf->gmod->hd); u8 *buffer; if (size > ES1_GBUF_MSG_SIZE) { diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 40174b8f63f4..5655721bb397 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -22,8 +22,8 @@ static struct kmem_cache *gbuf_head_cache; -static struct gbuf *__alloc_gbuf(struct greybus_device *gdev, - struct gdev_cport *cport, +static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, + struct gmod_cport *cport, gbuf_complete_t complete, gfp_t gfp_mask, void *context) @@ -35,7 +35,7 @@ static struct gbuf *__alloc_gbuf(struct greybus_device *gdev, return NULL; kref_init(&gbuf->kref); - gbuf->gdev = gdev; + gbuf->gmod = gmod; gbuf->cport = cport; gbuf->complete = complete; gbuf->context = context; @@ -46,7 +46,7 @@ static struct gbuf *__alloc_gbuf(struct greybus_device *gdev, /** * greybus_alloc_gbuf - allocate a greybus buffer * - * @gdev: greybus device that wants to allocate this + * @gmod: greybus device that wants to allocate this * @cport: cport to send the data to * @complete: callback when the gbuf is finished with * @size: size of the buffer @@ -58,8 +58,8 @@ static struct gbuf *__alloc_gbuf(struct greybus_device *gdev, * that the driver can then fill up with the data to be sent out. Curse * hardware designers for this issue... */ -struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, - struct gdev_cport *cport, +struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, + struct gmod_cport *cport, gbuf_complete_t complete, unsigned int size, gfp_t gfp_mask, @@ -68,14 +68,14 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, struct gbuf *gbuf; int retval; - gbuf = __alloc_gbuf(gdev, cport, complete, gfp_mask, context); + gbuf = __alloc_gbuf(gmod, cport, complete, gfp_mask, context); if (!gbuf) return NULL; gbuf->direction = GBUF_DIRECTION_OUT; /* Host controller specific allocation for the actual buffer */ - retval = gbuf->gdev->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); + retval = gbuf->gmod->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); if (retval) { greybus_free_gbuf(gbuf); return NULL; @@ -93,7 +93,7 @@ static void free_gbuf(struct kref *kref) /* If the direction is "out" then the host controller frees the data */ if (gbuf->direction == GBUF_DIRECTION_OUT) { - gbuf->gdev->hd->driver->free_gbuf_data(gbuf); + gbuf->gmod->hd->driver->free_gbuf_data(gbuf); } else { /* we "own" this in data, so free it ourselves */ kfree(gbuf->transfer_buffer); @@ -120,7 +120,7 @@ EXPORT_SYMBOL_GPL(greybus_get_gbuf); int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { - return gbuf->gdev->hd->driver->submit_gbuf(gbuf, gbuf->gdev->hd, gfp_mask); + return gbuf->gmod->hd->driver->submit_gbuf(gbuf, gbuf->gmod->hd, gfp_mask); } int greybus_kill_gbuf(struct gbuf *gbuf) @@ -169,8 +169,8 @@ static void cport_create_event(struct gbuf *gbuf) #define MAX_CPORTS 1024 struct gb_cport_handler { gbuf_complete_t handler; - struct gdev_cport cport; - struct greybus_device *gdev; + struct gmod_cport cport; + struct greybus_module *gmod; void *context; }; @@ -178,14 +178,14 @@ static struct gb_cport_handler cport_handler[MAX_CPORTS]; // FIXME - use a lock for this list of handlers, but really, for now we don't // need it, we don't have a dynamic system... -int gb_register_cport_complete(struct greybus_device *gdev, +int gb_register_cport_complete(struct greybus_module *gmod, gbuf_complete_t handler, int cport, void *context) { if (cport_handler[cport].handler) return -EINVAL; cport_handler[cport].context = context; - cport_handler[cport].gdev = gdev; + cport_handler[cport].gmod = gmod; cport_handler[cport].cport.number = cport; cport_handler[cport].handler = handler; return 0; @@ -212,7 +212,7 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, return; } - gbuf = __alloc_gbuf(ch->gdev, &ch->cport, ch->handler, GFP_ATOMIC, + gbuf = __alloc_gbuf(ch->gmod, &ch->cport, ch->handler, GFP_ATOMIC, ch->context); if (!gbuf) { /* Again, something bad went wrong, log it... */ diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 485480dc7233..4b7186651db5 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -14,7 +14,7 @@ struct gb_gpio_device { struct gpio_chip chip; - struct greybus_device *gdev; + struct greybus_module *gmod; struct gpio_chip *gpio; // FIXME - some lock? }; @@ -49,18 +49,18 @@ static void gpio_set(struct gpio_chip *gpio, unsigned nr, int val) // FIXME - do something there } -int gb_gpio_probe(struct greybus_device *gdev, +int gb_gpio_probe(struct greybus_module *gmod, const struct greybus_module_id *id) { struct gb_gpio_device *gb_gpio; struct gpio_chip *gpio; - struct device *dev = &gdev->dev; + struct device *dev = &gmod->dev; int retval; gb_gpio = kzalloc(sizeof(*gb_gpio), GFP_KERNEL); if (!gb_gpio) return -ENOMEM; - gb_gpio->gdev = gdev; + gb_gpio->gmod = gmod; gpio = &gb_gpio->chip; @@ -75,7 +75,7 @@ int gb_gpio_probe(struct greybus_device *gdev, gpio->ngpio = 42; // FIXME!!! gpio->can_sleep = false; // FIXME!!! - gdev->gb_gpio_dev = gb_gpio; + gmod->gb_gpio_dev = gb_gpio; retval = gpiochip_add(gpio); if (retval) { @@ -86,12 +86,12 @@ int gb_gpio_probe(struct greybus_device *gdev, return 0; } -void gb_gpio_disconnect(struct greybus_device *gdev) +void gb_gpio_disconnect(struct greybus_module *gmod) { struct gb_gpio_device *gb_gpio_dev; int retval; - gb_gpio_dev = gdev->gb_gpio_dev; + gb_gpio_dev = gmod->gb_gpio_dev; retval = gpiochip_remove(&gb_gpio_dev->chip); kfree(gb_gpio_dev); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 1f996b97a072..59f9b980c6fc 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -101,14 +101,14 @@ struct gbuf; -struct gdev_cport { +struct gmod_cport { u16 number; u16 size; u8 speed; // valid??? // FIXME, what else? }; -struct gdev_string { +struct gmod_string { u16 length; u8 id; u8 string[0]; @@ -120,8 +120,8 @@ struct gbuf { struct kref kref; void *hdpriv; - struct greybus_device *gdev; - struct gdev_cport *cport; + struct greybus_module *gmod; + struct gmod_cport *cport; int status; void *transfer_buffer; u32 transfer_flags; /* flags for the transfer buffer */ @@ -148,7 +148,7 @@ struct gbuf { * same module as the gpio pins, etc.) * * So, put the "private" data structures here in greybus.h and link to them off - * of the "main" greybus_device structure. + * of the "main" greybus_module structure. */ struct gb_i2c_device; @@ -195,7 +195,7 @@ void greybus_gbuf_finished(struct gbuf *gbuf); #define MAX_CPORTS_PER_MODULE 10 #define MAX_STRINGS_PER_MODULE 10 -struct greybus_device { +struct greybus_module { struct device dev; u16 module_number; struct greybus_descriptor_function function; @@ -203,8 +203,8 @@ struct greybus_device { struct greybus_descriptor_serial_number serial_number; int num_cports; int num_strings; - struct gdev_cport *cport[MAX_CPORTS_PER_MODULE]; - struct gdev_string *string[MAX_STRINGS_PER_MODULE]; + struct gmod_cport *cport[MAX_CPORTS_PER_MODULE]; + struct gmod_string *string[MAX_STRINGS_PER_MODULE]; struct greybus_host_device *hd; @@ -215,10 +215,10 @@ struct greybus_device { struct gb_usb_device *gb_usb_dev; struct gb_battery *gb_battery; }; -#define to_greybus_device(d) container_of(d, struct greybus_device, dev) +#define to_greybus_module(d) container_of(d, struct greybus_module, dev) -struct gbuf *greybus_alloc_gbuf(struct greybus_device *gdev, - struct gdev_cport *cport, +struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, + struct gmod_cport *cport, gbuf_complete_t complete, unsigned int size, gfp_t gfp_mask, @@ -234,12 +234,12 @@ int greybus_kill_gbuf(struct gbuf *gbuf); struct greybus_driver { const char *name; - int (*probe)(struct greybus_device *gdev, + int (*probe)(struct greybus_module *gmod, const struct greybus_module_id *id); - void (*disconnect)(struct greybus_device *gdev); + void (*disconnect)(struct greybus_module *gmod); - int (*suspend)(struct greybus_device *gdev, pm_message_t message); - int (*resume)(struct greybus_device *gdev); + int (*suspend)(struct greybus_module *gmod, pm_message_t message); + int (*resume)(struct greybus_module *gmod); const struct greybus_module_id *id_table; @@ -247,14 +247,14 @@ struct greybus_driver { }; #define to_greybus_driver(d) container_of(d, struct greybus_driver, driver) -static inline void greybus_set_drvdata(struct greybus_device *gdev, void *data) +static inline void greybus_set_drvdata(struct greybus_module *gmod, void *data) { - dev_set_drvdata(&gdev->dev, data); + dev_set_drvdata(&gmod->dev, data); } -static inline void *greybus_get_drvdata(struct greybus_device *gdev) +static inline void *greybus_get_drvdata(struct greybus_module *gmod) { - return dev_get_drvdata(&gdev->dev); + return dev_get_drvdata(&gmod->dev); } /* Don't call these directly, use the module_greybus_driver() macro instead */ @@ -279,9 +279,9 @@ void greybus_deregister(struct greybus_driver *driver); int greybus_disabled(void); -void greybus_remove_device(struct greybus_device *gdev); +void greybus_remove_device(struct greybus_module *gmod); -const u8 *greybus_string(struct greybus_device *gdev, int id); +const u8 *greybus_string(struct greybus_module *gmod, int id); /* Internal functions to gb module, move to internal .h file eventually. */ @@ -297,7 +297,7 @@ void gb_debugfs_cleanup(void); int gb_gbuf_init(void); void gb_gbuf_exit(void); -int gb_register_cport_complete(struct greybus_device *gdev, +int gb_register_cport_complete(struct greybus_module *gmod, gbuf_complete_t handler, int cport, void *context); void gb_deregister_cport_complete(int cport); @@ -309,16 +309,16 @@ extern const struct attribute_group *greybus_module_groups[]; * we have static functions for this, not "dynamic" drivers like we really * should in the end. */ -int gb_i2c_probe(struct greybus_device *gdev, const struct greybus_module_id *id); -void gb_i2c_disconnect(struct greybus_device *gdev); -int gb_gpio_probe(struct greybus_device *gdev, const struct greybus_module_id *id); -void gb_gpio_disconnect(struct greybus_device *gdev); -int gb_sdio_probe(struct greybus_device *gdev, const struct greybus_module_id *id); -void gb_sdio_disconnect(struct greybus_device *gdev); -int gb_tty_probe(struct greybus_device *gdev, const struct greybus_module_id *id); -void gb_tty_disconnect(struct greybus_device *gdev); -int gb_battery_probe(struct greybus_device *gdev, const struct greybus_module_id *id); -void gb_battery_disconnect(struct greybus_device *gdev); +int gb_i2c_probe(struct greybus_module *gmod, const struct greybus_module_id *id); +void gb_i2c_disconnect(struct greybus_module *gmod); +int gb_gpio_probe(struct greybus_module *gmod, const struct greybus_module_id *id); +void gb_gpio_disconnect(struct greybus_module *gmod); +int gb_sdio_probe(struct greybus_module *gmod, const struct greybus_module_id *id); +void gb_sdio_disconnect(struct greybus_module *gmod); +int gb_tty_probe(struct greybus_module *gmod, const struct greybus_module_id *id); +void gb_tty_disconnect(struct greybus_module *gmod); +int gb_battery_probe(struct greybus_module *gmod, const struct greybus_module_id *id); +void gb_battery_disconnect(struct greybus_module *gmod); int gb_tty_init(void); void gb_tty_exit(void); diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index 83c4d057cfb0..8b3bce6a9d0b 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -19,7 +19,7 @@ struct greybus_module_id { __attribute__((aligned(sizeof(kernel_ulong_t)))); }; -/* Used to match the greybus_device_id */ +/* Used to match the greybus_module_id */ #define GREYBUS_DEVICE_ID_MATCH_VENDOR BIT(0) #define GREYBUS_DEVICE_ID_MATCH_PRODUCT BIT(1) #define GREYBUS_DEVICE_ID_MATCH_SERIAL BIT(2) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 3c1d947208c9..bd03f19a3568 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -14,7 +14,7 @@ struct gb_i2c_device { struct i2c_adapter *adapter; - struct greybus_device *gdev; + struct greybus_module *gmod; }; static const struct greybus_module_id id_table[] = { @@ -33,10 +33,10 @@ static s32 i2c_gb_access(struct i2c_adapter *adap, u16 addr, int size, union i2c_smbus_data *data) { struct gb_i2c_device *gb_i2c_dev; - struct greybus_device *gdev; + struct greybus_module *gmod; gb_i2c_dev = i2c_get_adapdata(adap); - gdev = gb_i2c_dev->gdev; + gmod = gb_i2c_dev->gmod; // FIXME - do the actual work of sending a i2c message here... switch (size) { @@ -50,7 +50,7 @@ static s32 i2c_gb_access(struct i2c_adapter *adap, u16 addr, case I2C_SMBUS_BLOCK_PROC_CALL: case I2C_SMBUS_I2C_BLOCK_DATA: default: - dev_err(&gdev->dev, "Unsupported transaction %d\n", size); + dev_err(&gmod->dev, "Unsupported transaction %d\n", size); return -EOPNOTSUPP; } @@ -75,7 +75,7 @@ static const struct i2c_algorithm smbus_algorithm = { .functionality = i2c_gb_func, }; -int gb_i2c_probe(struct greybus_device *gdev, +int gb_i2c_probe(struct greybus_module *gmod, const struct greybus_module_id *id) { struct gb_i2c_device *gb_i2c_dev; @@ -95,19 +95,19 @@ int gb_i2c_probe(struct greybus_device *gdev, adapter->owner = THIS_MODULE; adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adapter->algo = &smbus_algorithm; - adapter->dev.parent = &gdev->dev; + adapter->dev.parent = &gmod->dev; adapter->retries = 3; /* we have to pick something... */ snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); retval = i2c_add_adapter(adapter); if (retval) { - dev_err(&gdev->dev, "Can not add SMBus adapter\n"); + dev_err(&gmod->dev, "Can not add SMBus adapter\n"); goto error; } - gb_i2c_dev->gdev = gdev; + gb_i2c_dev->gmod = gmod; gb_i2c_dev->adapter = adapter; - gdev->gb_i2c_dev = gb_i2c_dev; + gmod->gb_i2c_dev = gb_i2c_dev; return 0; error: kfree(adapter); @@ -115,11 +115,11 @@ error: return retval; } -void gb_i2c_disconnect(struct greybus_device *gdev) +void gb_i2c_disconnect(struct greybus_module *gmod) { struct gb_i2c_device *gb_i2c_dev; - gb_i2c_dev = gdev->gb_i2c_dev; + gb_i2c_dev = gmod->gb_i2c_dev; i2c_del_adapter(gb_i2c_dev->adapter); kfree(gb_i2c_dev->adapter); kfree(gb_i2c_dev); diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 665767d31f8f..3cf258aedc11 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -45,13 +45,13 @@ static const struct mmc_host_ops gb_sd_ops = { .get_ro = gb_sd_get_ro, }; -int gb_sdio_probe(struct greybus_device *gdev, +int gb_sdio_probe(struct greybus_module *gmod, const struct greybus_module_id *id) { struct mmc_host *mmc; struct gb_sdio_host *host; - mmc = mmc_alloc_host(sizeof(struct gb_sdio_host), &gdev->dev); + mmc = mmc_alloc_host(sizeof(struct gb_sdio_host), &gmod->dev); if (!mmc) return -ENOMEM; @@ -61,16 +61,16 @@ int gb_sdio_probe(struct greybus_device *gdev, mmc->ops = &gb_sd_ops; // FIXME - set up size limits we can handle. - gdev->gb_sdio_host = host; + gmod->gb_sdio_host = host; return 0; } -void gb_sdio_disconnect(struct greybus_device *gdev) +void gb_sdio_disconnect(struct greybus_module *gmod) { struct mmc_host *mmc; struct gb_sdio_host *host; - host = gdev->gb_sdio_host; + host = gmod->gb_sdio_host; mmc = host->mmc; mmc_remove_host(mmc); diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 074fcfe6a1ea..b503d2c4ef28 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -26,8 +26,8 @@ static ssize_t function_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct greybus_device *gdev = to_greybus_device(dev); \ - return sprintf(buf, "%d\n", gdev->function.field); \ + struct greybus_module *gmod = to_greybus_module(dev); \ + return sprintf(buf, "%d\n", gmod->function.field); \ } \ static DEVICE_ATTR_RO(function_##field) @@ -49,15 +49,15 @@ static struct attribute *function_attrs[] = { static umode_t function_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { - struct greybus_device *gdev = to_greybus_device(kobj_to_dev(kobj)); + struct greybus_module *gmod = to_greybus_module(kobj_to_dev(kobj)); // FIXME - make this a dynamic structure to "know" if it really is here // or not easier? - if (gdev->function.number || - gdev->function.cport || - gdev->function.class || - gdev->function.subclass || - gdev->function.protocol) + if (gmod->function.number || + gmod->function.cport || + gmod->function.class || + gmod->function.subclass || + gmod->function.protocol) return a->mode; return 0; } @@ -73,8 +73,8 @@ static ssize_t module_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct greybus_device *gdev = to_greybus_device(dev); \ - return sprintf(buf, "%x\n", gdev->module_id.field); \ + struct greybus_module *gmod = to_greybus_module(dev); \ + return sprintf(buf, "%x\n", gmod->module_id.field); \ } \ static DEVICE_ATTR_RO(module_##field) @@ -86,10 +86,10 @@ static ssize_t module_vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct greybus_device *gdev = to_greybus_device(dev); + struct greybus_module *gmod = to_greybus_module(dev); return sprintf(buf, "%s", - greybus_string(gdev, gdev->module_id.vendor_stringid)); + greybus_string(gmod, gmod->module_id.vendor_stringid)); } static DEVICE_ATTR_RO(module_vendor_string); @@ -97,10 +97,10 @@ static ssize_t module_product_string_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct greybus_device *gdev = to_greybus_device(dev); + struct greybus_module *gmod = to_greybus_module(dev); return sprintf(buf, "%s", - greybus_string(gdev, gdev->module_id.product_stringid)); + greybus_string(gmod, gmod->module_id.product_stringid)); } static DEVICE_ATTR_RO(module_product_string); @@ -116,20 +116,20 @@ static struct attribute *module_attrs[] = { static umode_t module_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { - struct greybus_device *gdev = to_greybus_device(kobj_to_dev(kobj)); + struct greybus_module *gmod = to_greybus_module(kobj_to_dev(kobj)); if ((a == &dev_attr_module_vendor_string.attr) && - (gdev->module_id.vendor_stringid)) + (gmod->module_id.vendor_stringid)) return a->mode; if ((a == &dev_attr_module_product_string.attr) && - (gdev->module_id.product_stringid)) + (gmod->module_id.product_stringid)) return a->mode; // FIXME - make this a dynamic structure to "know" if it really is here // or not easier? - if (gdev->module_id.vendor || - gdev->module_id.product || - gdev->module_id.version) + if (gmod->module_id.vendor || + gmod->module_id.product || + gmod->module_id.version) return a->mode; return 0; } @@ -144,10 +144,10 @@ static struct attribute_group module_attr_grp = { static ssize_t serial_number_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct greybus_device *gdev = to_greybus_device(dev); + struct greybus_module *gmod = to_greybus_module(dev); return sprintf(buf, "%llX\n", - (unsigned long long)le64_to_cpu(gdev->serial_number.serial_number)); + (unsigned long long)le64_to_cpu(gmod->serial_number.serial_number)); } static DEVICE_ATTR_RO(serial_number); diff --git a/drivers/staging/greybus/test_sink.c b/drivers/staging/greybus/test_sink.c index 932c1692a13b..9dbf6eeba8a6 100644 --- a/drivers/staging/greybus/test_sink.c +++ b/drivers/staging/greybus/test_sink.c @@ -12,10 +12,10 @@ #include "greybus.h" struct test_device { - struct greybus_device *gdev; + struct greybus_module *gmod; }; -int gb_register_cport_complete(struct greybus_device *gdev, +int gb_register_cport_complete(struct greybus_module *gmod, gbuf_complete_t handler, int cport, void *context); void gb_deregister_cport_complete(int cport); diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 75ddd18792ce..2021f2775e22 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -33,7 +33,7 @@ struct gb_tty { struct tty_port port; - struct greybus_device *gdev; + struct greybus_module *gmod; int cport; unsigned int minor; unsigned char clocal; @@ -384,7 +384,7 @@ static const struct tty_operations gb_ops = { }; -int gb_tty_probe(struct greybus_device *gdev, +int gb_tty_probe(struct greybus_module *gmod, const struct greybus_module_id *id) { struct gb_tty *gb_tty; @@ -399,14 +399,14 @@ int gb_tty_probe(struct greybus_device *gdev, minor = alloc_minor(gb_tty); if (minor < 0) { if (minor == -ENOSPC) { - dev_err(&gdev->dev, "no more free minor numbers\n"); + dev_err(&gmod->dev, "no more free minor numbers\n"); return -ENODEV; } return minor; } gb_tty->minor = minor; - gb_tty->gdev = gdev; + gb_tty->gmod = gmod; spin_lock_init(&gb_tty->write_lock); spin_lock_init(&gb_tty->read_lock); init_waitqueue_head(&gb_tty->wioctl); @@ -414,10 +414,10 @@ int gb_tty_probe(struct greybus_device *gdev, /* FIXME - allocate gb buffers */ - gdev->gb_tty = gb_tty; + gmod->gb_tty = gb_tty; tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, - &gdev->dev); + &gmod->dev); if (IS_ERR(tty_dev)) { retval = PTR_ERR(tty_dev); goto error; @@ -425,14 +425,14 @@ int gb_tty_probe(struct greybus_device *gdev, return 0; error: - gdev->gb_tty = NULL; + gmod->gb_tty = NULL; release_minor(gb_tty); return retval; } -void gb_tty_disconnect(struct greybus_device *gdev) +void gb_tty_disconnect(struct greybus_module *gmod) { - struct gb_tty *gb_tty = gdev->gb_tty; + struct gb_tty *gb_tty = gmod->gb_tty; struct tty_struct *tty; if (!gb_tty) @@ -442,7 +442,7 @@ void gb_tty_disconnect(struct greybus_device *gdev) gb_tty->disconnected = true; wake_up_all(&gb_tty->wioctl); - gdev->gb_tty = NULL; + gmod->gb_tty = NULL; mutex_unlock(&gb_tty->mutex); tty = tty_port_tty_get(&gb_tty->port); -- cgit v1.2.3-59-g8ed1b From 380f6bddc900b9baba8163e5c5f60eabe712f7d0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 23 Sep 2014 12:46:32 -0500 Subject: greybus: quick fix for sysfs serial number Let the serial number attribute have its own is_visible function. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sysfs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index b503d2c4ef28..792a2381c289 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -156,9 +156,15 @@ static struct attribute *serial_number_attrs[] = { NULL, }; +static umode_t serial_number_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + return a->mode; +} + static struct attribute_group serial_number_attr_grp = { .attrs = serial_number_attrs, - .is_visible = function_attrs_are_visible, + .is_visible = serial_number_is_visible, }; -- cgit v1.2.3-59-g8ed1b From 217b870e99ce68b868db08f4257c6f463abddd0c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 23 Sep 2014 12:46:33 -0500 Subject: greybus: get rid of a weird idiom It strikes me as strange to add one to a value while checking to see if it exceeds a maximum. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 296ff61aaefb..33d7a334c9b1 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -282,7 +282,7 @@ static int create_string(struct greybus_module *gmod, int string_size; struct gmod_string *gmod_string; - if ((gmod->num_strings + 1) >= MAX_STRINGS_PER_MODULE) { + if (gmod->num_strings == MAX_STRINGS_PER_MODULE) { dev_err(gmod->dev.parent, "too many strings for this module!\n"); return -EINVAL; @@ -315,7 +315,7 @@ static int create_cport(struct greybus_module *gmod, { struct gmod_cport *gmod_cport; - if ((gmod->num_cports + 1) >= MAX_CPORTS_PER_MODULE) { + if (gmod->num_cports == MAX_CPORTS_PER_MODULE) { dev_err(gmod->dev.parent, "too many cports for this module!\n"); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 2e353685bf0e523505695f8339316b0536474ea4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 23 Sep 2014 12:46:36 -0500 Subject: greybus: embed workqueue structure in struct gbuf A Greybus buffer containing outbound data is submitted to to the underlying driver to be sent over a CPort. Sending that data could be deferred, so the submit operation completes asynchronously. When the send is done, a callback occurs, and the buffer is "completed", and the buffer's completion routine is called. The buffer is then freed. If data arrives on the CPort, greybus_cport_in_data() is called to allocate a Greybus buffer and copy the received data into it. Once that's done the buffer is completed, again allowing the buffer's completion routine to finish any final tasks before freeing the buffer. We use a workqueue to schedule calling the buffer's completion function. This patch does two things related to the work queue: - Renames the work queue "gbuf_workqueue" so its name more directly describes its purpose - Moves the work_struct needed for scheduling completions into the struct greybuf. Previously a separate type was used, and dynamically allocated *at interrupt time* to hold this work_struct. We can now do away with that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 51 +++++++++++---------------------------- drivers/staging/greybus/greybus.h | 1 + 2 files changed, 15 insertions(+), 37 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 5655721bb397..b51bd2ad66ec 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -19,9 +19,13 @@ #include "greybus.h" +static void cport_process_event(struct work_struct *work); static struct kmem_cache *gbuf_head_cache; +/* Workqueue to handle Greybus buffer completions. */ +static struct workqueue_struct *gbuf_workqueue; + static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, struct gmod_cport *cport, gbuf_complete_t complete, @@ -37,6 +41,7 @@ static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, kref_init(&gbuf->kref); gbuf->gmod = gmod; gbuf->cport = cport; + INIT_WORK(&gbuf->event, cport_process_event); gbuf->complete = complete; gbuf->context = context; @@ -129,41 +134,13 @@ int greybus_kill_gbuf(struct gbuf *gbuf) return -ENOMEM; } -struct cport_msg { - struct gbuf *gbuf; - struct work_struct event; -}; - -static struct workqueue_struct *cport_workqueue; - static void cport_process_event(struct work_struct *work) { - struct cport_msg *cm; - struct gbuf *gbuf; - - cm = container_of(work, struct cport_msg, event); - - gbuf = cm->gbuf; - - /* call the gbuf handler */ - gbuf->complete(gbuf); - - /* free all the memory */ - greybus_free_gbuf(gbuf); - kfree(cm); -} - -static void cport_create_event(struct gbuf *gbuf) -{ - struct cport_msg *cm; - - /* Slow alloc, does it matter??? */ - cm = kmalloc(sizeof(*cm), GFP_ATOMIC); + struct gbuf *gbuf = container_of(work, struct gbuf, event); - /* Queue up the cport message to be handled in user context */ - cm->gbuf = gbuf; - INIT_WORK(&cm->event, cport_process_event); - queue_work(cport_workqueue, &cm->event); + /* Call the completion handler, then drop our reference */ + gbuf->complete(gbuf); + greybus_put_gbuf(gbuf); } #define MAX_CPORTS 1024 @@ -237,21 +214,21 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, gbuf->transfer_buffer_length = length; gbuf->actual_length = length; - cport_create_event(gbuf); + queue_work(gbuf_workqueue, &gbuf->event); } EXPORT_SYMBOL_GPL(greybus_cport_in_data); /* Can be called in interrupt context, do the work and get out of here */ void greybus_gbuf_finished(struct gbuf *gbuf) { - cport_create_event(gbuf); + queue_work(gbuf_workqueue, &gbuf->event); } EXPORT_SYMBOL_GPL(greybus_gbuf_finished); int gb_gbuf_init(void) { - cport_workqueue = alloc_workqueue("greybus_gbuf", 0, 1); - if (!cport_workqueue) + gbuf_workqueue = alloc_workqueue("greybus_gbuf", 0, 1); + if (!gbuf_workqueue) return -ENOMEM; gbuf_head_cache = kmem_cache_create("gbuf_head_cache", @@ -261,6 +238,6 @@ int gb_gbuf_init(void) void gb_gbuf_exit(void) { - destroy_workqueue(cport_workqueue); + destroy_workqueue(gbuf_workqueue); kmem_cache_destroy(gbuf_head_cache); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 59f9b980c6fc..692a5b9bf797 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -133,6 +133,7 @@ struct gbuf { unsigned int direction : 1; /* 0 is out, 1 is in */ void *context; + struct work_struct event; gbuf_complete_t complete; }; -- cgit v1.2.3-59-g8ed1b From dce745af21884c9e27665a333d3b4fc9fcf643e7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Sep 2014 20:58:58 -0700 Subject: greybus: fix up coding style issue I caused with the last patch... --- drivers/staging/greybus/gbuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index b51bd2ad66ec..46896e829af0 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -139,8 +139,8 @@ static void cport_process_event(struct work_struct *work) struct gbuf *gbuf = container_of(work, struct gbuf, event); /* Call the completion handler, then drop our reference */ - gbuf->complete(gbuf); - greybus_put_gbuf(gbuf); + gbuf->complete(gbuf); + greybus_put_gbuf(gbuf); } #define MAX_CPORTS 1024 -- cgit v1.2.3-59-g8ed1b From 6d63ff7a2d40b8865eb00857cdb910944c17f0b2 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Fri, 26 Sep 2014 20:49:48 -0500 Subject: greybus: update descriptor module_id->module to match spec Greybus spec was updated to change the name of the Module ID descriptor to simply Module descriptor. Change everything accordingly. Signed-off-by: Matt Porter Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 20 ++++++++++---------- drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/greybus_manifest.h | 6 +++--- drivers/staging/greybus/sysfs.c | 16 ++++++++-------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 33d7a334c9b1..37bc7803db91 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -33,18 +33,18 @@ EXPORT_SYMBOL_GPL(greybus_disabled); static int greybus_match_one_id(struct greybus_module *gmod, const struct greybus_module_id *id) { - struct greybus_descriptor_module_id *module_id; + struct greybus_descriptor_module *module; struct greybus_descriptor_serial_number *serial_num; - module_id = &gmod->module_id; + module = &gmod->module; serial_num = &gmod->serial_number; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && - (id->vendor != le16_to_cpu(module_id->vendor))) + (id->vendor != le16_to_cpu(module->vendor))) return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && - (id->product != le16_to_cpu(module_id->product))) + (id->product != le16_to_cpu(module->product))) return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && @@ -249,16 +249,16 @@ static int create_function(struct greybus_module *gmod, return 0; } -static int create_module_id(struct greybus_module *gmod, - struct greybus_descriptor_module_id *module_id, +static int create_module(struct greybus_module *gmod, + struct greybus_descriptor_module *module, size_t desc_size) { - if (desc_size != sizeof(*module_id)) { + if (desc_size != sizeof(*module)) { dev_err(gmod->dev.parent, "invalid module header size %zu\n", desc_size); return -EINVAL; } - memcpy(&gmod->module_id, module_id, desc_size); + memcpy(&gmod->module, module, desc_size); return 0; } @@ -422,8 +422,8 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, data_size); break; - case GREYBUS_TYPE_MODULE_ID: - retval = create_module_id(gmod, &desc->module_id, + case GREYBUS_TYPE_MODULE: + retval = create_module(gmod, &desc->module, data_size); break; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 692a5b9bf797..0a0b0a591355 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -200,7 +200,7 @@ struct greybus_module { struct device dev; u16 module_number; struct greybus_descriptor_function function; - struct greybus_descriptor_module_id module_id; + struct greybus_descriptor_module module; struct greybus_descriptor_serial_number serial_number; int num_cports; int num_strings; diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 8b4acd3c2fc1..037d7a4fd8f2 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -21,7 +21,7 @@ struct greybus_manifest_header { enum greybus_descriptor_type { GREYBUS_TYPE_INVALID = 0x0000, GREYBUS_TYPE_FUNCTION = 0x0001, - GREYBUS_TYPE_MODULE_ID = 0x0002, + GREYBUS_TYPE_MODULE = 0x0002, GREYBUS_TYPE_SERIAL_NUMBER = 0x0003, GREYBUS_TYPE_STRING = 0x0004, GREYBUS_TYPE_CPORT = 0x0005, @@ -58,7 +58,7 @@ struct greybus_descriptor_function { __u8 reserved; }; -struct greybus_descriptor_module_id { +struct greybus_descriptor_module { __le16 vendor; __le16 product; __le16 version; @@ -87,7 +87,7 @@ struct greybus_descriptor { struct greybus_descriptor_header header; union { struct greybus_descriptor_function function; - struct greybus_descriptor_module_id module_id; + struct greybus_descriptor_module module; struct greybus_descriptor_serial_number serial_number; struct greybus_descriptor_string string; struct greybus_descriptor_cport cport; diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 792a2381c289..8cedd4b0e468 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -74,7 +74,7 @@ static ssize_t module_##field##_show(struct device *dev, \ char *buf) \ { \ struct greybus_module *gmod = to_greybus_module(dev); \ - return sprintf(buf, "%x\n", gmod->module_id.field); \ + return sprintf(buf, "%x\n", gmod->module.field); \ } \ static DEVICE_ATTR_RO(module_##field) @@ -89,7 +89,7 @@ static ssize_t module_vendor_string_show(struct device *dev, struct greybus_module *gmod = to_greybus_module(dev); return sprintf(buf, "%s", - greybus_string(gmod, gmod->module_id.vendor_stringid)); + greybus_string(gmod, gmod->module.vendor_stringid)); } static DEVICE_ATTR_RO(module_vendor_string); @@ -100,7 +100,7 @@ static ssize_t module_product_string_show(struct device *dev, struct greybus_module *gmod = to_greybus_module(dev); return sprintf(buf, "%s", - greybus_string(gmod, gmod->module_id.product_stringid)); + greybus_string(gmod, gmod->module.product_stringid)); } static DEVICE_ATTR_RO(module_product_string); @@ -119,17 +119,17 @@ static umode_t module_attrs_are_visible(struct kobject *kobj, struct greybus_module *gmod = to_greybus_module(kobj_to_dev(kobj)); if ((a == &dev_attr_module_vendor_string.attr) && - (gmod->module_id.vendor_stringid)) + (gmod->module.vendor_stringid)) return a->mode; if ((a == &dev_attr_module_product_string.attr) && - (gmod->module_id.product_stringid)) + (gmod->module.product_stringid)) return a->mode; // FIXME - make this a dynamic structure to "know" if it really is here // or not easier? - if (gmod->module_id.vendor || - gmod->module_id.product || - gmod->module_id.version) + if (gmod->module.vendor || + gmod->module.product || + gmod->module.version) return a->mode; return 0; } -- cgit v1.2.3-59-g8ed1b From 4fc645494f7a363f0aa8a997b38ac73a63306e61 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Fri, 26 Sep 2014 20:49:49 -0500 Subject: greybus: remove serial number descriptor to match spec Greybus spec was updated to remove the serial number descriptor and move the serial number field to the, now mandatory, module descriptor. Change everything accordingly. Signed-off-by: Matt Porter Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 23 +--------------- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/greybus_manifest.h | 7 +---- drivers/staging/greybus/sysfs.c | 42 ++++++++++-------------------- 4 files changed, 16 insertions(+), 57 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 37bc7803db91..fc54ac9f5c91 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -34,10 +34,8 @@ static int greybus_match_one_id(struct greybus_module *gmod, const struct greybus_module_id *id) { struct greybus_descriptor_module *module; - struct greybus_descriptor_serial_number *serial_num; module = &gmod->module; - serial_num = &gmod->serial_number; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && (id->vendor != le16_to_cpu(module->vendor))) @@ -48,7 +46,7 @@ static int greybus_match_one_id(struct greybus_module *gmod, return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && - (id->serial_number != le64_to_cpu(serial_num->serial_number))) + (id->serial_number != le64_to_cpu(module->serial_number))) return 0; return 1; @@ -262,19 +260,6 @@ static int create_module(struct greybus_module *gmod, return 0; } -static int create_serial_number(struct greybus_module *gmod, - struct greybus_descriptor_serial_number *serial_num, - size_t desc_size) -{ - if (desc_size != sizeof(*serial_num)) { - dev_err(gmod->dev.parent, "invalid serial number header size %zu\n", - desc_size); - return -EINVAL; - } - memcpy(&gmod->serial_number, serial_num, desc_size); - return 0; -} - static int create_string(struct greybus_module *gmod, struct greybus_descriptor_string *string, size_t desc_size) @@ -427,12 +412,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, data_size); break; - case GREYBUS_TYPE_SERIAL_NUMBER: - retval = create_serial_number(gmod, - &desc->serial_number, - data_size); - break; - case GREYBUS_TYPE_STRING: retval = create_string(gmod, &desc->string, data_size); break; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 0a0b0a591355..87e6218d88db 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -201,7 +201,6 @@ struct greybus_module { u16 module_number; struct greybus_descriptor_function function; struct greybus_descriptor_module module; - struct greybus_descriptor_serial_number serial_number; int num_cports; int num_strings; struct gmod_cport *cport[MAX_CPORTS_PER_MODULE]; diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 037d7a4fd8f2..6dda11cef5ef 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -22,7 +22,6 @@ enum greybus_descriptor_type { GREYBUS_TYPE_INVALID = 0x0000, GREYBUS_TYPE_FUNCTION = 0x0001, GREYBUS_TYPE_MODULE = 0x0002, - GREYBUS_TYPE_SERIAL_NUMBER = 0x0003, GREYBUS_TYPE_STRING = 0x0004, GREYBUS_TYPE_CPORT = 0x0005, }; @@ -62,14 +61,11 @@ struct greybus_descriptor_module { __le16 vendor; __le16 product; __le16 version; + __le64 serial_number; __u8 vendor_stringid; __u8 product_stringid; }; -struct greybus_descriptor_serial_number { - __le64 serial_number; -}; - struct greybus_descriptor_string { __le16 length; __u8 id; @@ -88,7 +84,6 @@ struct greybus_descriptor { union { struct greybus_descriptor_function function; struct greybus_descriptor_module module; - struct greybus_descriptor_serial_number serial_number; struct greybus_descriptor_string string; struct greybus_descriptor_cport cport; }; diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 8cedd4b0e468..55dc7b786ac8 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -82,6 +82,17 @@ greybus_module_attr(vendor); greybus_module_attr(product); greybus_module_attr(version); +static ssize_t module_serial_number_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct greybus_module *gmod = to_greybus_module(dev); + + return sprintf(buf, "%llX\n", + (unsigned long long)le64_to_cpu(gmod->module.serial_number)); +} +static DEVICE_ATTR_RO(module_serial_number); + static ssize_t module_vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -108,6 +119,7 @@ static struct attribute *module_attrs[] = { &dev_attr_module_vendor.attr, &dev_attr_module_product.attr, &dev_attr_module_version.attr, + &dev_attr_module_serial_number.attr, &dev_attr_module_vendor_string.attr, &dev_attr_module_product_string.attr, NULL, @@ -129,7 +141,8 @@ static umode_t module_attrs_are_visible(struct kobject *kobj, // or not easier? if (gmod->module.vendor || gmod->module.product || - gmod->module.version) + gmod->module.version || + gmod->module.serial_number) return a->mode; return 0; } @@ -140,38 +153,11 @@ static struct attribute_group module_attr_grp = { }; -/* Serial Number */ -static ssize_t serial_number_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct greybus_module *gmod = to_greybus_module(dev); - - return sprintf(buf, "%llX\n", - (unsigned long long)le64_to_cpu(gmod->serial_number.serial_number)); -} -static DEVICE_ATTR_RO(serial_number); - -static struct attribute *serial_number_attrs[] = { - &dev_attr_serial_number.attr, - NULL, -}; - -static umode_t serial_number_is_visible(struct kobject *kobj, - struct attribute *a, int n) -{ - return a->mode; -} - -static struct attribute_group serial_number_attr_grp = { - .attrs = serial_number_attrs, - .is_visible = serial_number_is_visible, -}; const struct attribute_group *greybus_module_groups[] = { &function_attr_grp, &module_attr_grp, - &serial_number_attr_grp, NULL, }; -- cgit v1.2.3-59-g8ed1b From 097724c2fb203d1bfa0853ddf73c3a5df030ab05 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Fri, 26 Sep 2014 20:49:50 -0500 Subject: greybus: remove unused function descriptor fields and change class->function_type Greybus spec was updated to remove a number of unused function descriptor fields. In addition, the class field was change to function_type to avoid confusion with the concept of high-level class drivers. Signed-off-by: Matt Porter Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 8 ++------ drivers/staging/greybus/sysfs.c | 17 ++++------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 6dda11cef5ef..2830d552f0e1 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -31,7 +31,7 @@ struct greybus_descriptor_header { __le16 type; /* enum greybus_descriptor_type */ }; -enum greybus_function_class { +enum greybus_function_type { GREYBUS_FUNCTION_CONTROL = 0x00, GREYBUS_FUNCTION_USB = 0x01, GREYBUS_FUNCTION_GPIO = 0x02, @@ -49,12 +49,8 @@ enum greybus_function_class { }; struct greybus_descriptor_function { - __le16 number; __le16 cport; - __u8 class; /* enum greybus_function_class */ - __u8 subclass; - __u8 protocol; - __u8 reserved; + __u8 function_type; /* enum greybus_function_type */ }; struct greybus_descriptor_module { diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 55dc7b786ac8..1f17388445ad 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -31,18 +31,12 @@ static ssize_t function_##field##_show(struct device *dev, \ } \ static DEVICE_ATTR_RO(function_##field) -greybus_function_attr(number); greybus_function_attr(cport); -greybus_function_attr(class); -greybus_function_attr(subclass); -greybus_function_attr(protocol); +greybus_function_attr(function_type); static struct attribute *function_attrs[] = { - &dev_attr_function_number.attr, &dev_attr_function_cport.attr, - &dev_attr_function_class.attr, - &dev_attr_function_subclass.attr, - &dev_attr_function_protocol.attr, + &dev_attr_function_function_type.attr, NULL, }; @@ -53,11 +47,8 @@ static umode_t function_attrs_are_visible(struct kobject *kobj, // FIXME - make this a dynamic structure to "know" if it really is here // or not easier? - if (gmod->function.number || - gmod->function.cport || - gmod->function.class || - gmod->function.subclass || - gmod->function.protocol) + if (gmod->function.cport || + gmod->function.function_type) return a->mode; return 0; } -- cgit v1.2.3-59-g8ed1b From cbd0fd7b9b6fd5c6d9f13f021a17a072e9d33147 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Fri, 26 Sep 2014 20:49:51 -0500 Subject: greybus: update string descriptor length field to __u8 type to match spec Greybus spec was updated to make the length field a single byte. Update the type and remove endian handling of that field in the core. Signed-off-by: Matt Porter Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/greybus_manifest.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index fc54ac9f5c91..05217c630ff7 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -279,7 +279,7 @@ static int create_string(struct greybus_module *gmod, return -EINVAL; } - string_size = le16_to_cpu(string->length); + string_size = string->length; gmod_string = kzalloc(sizeof(*gmod_string) + string_size + 1, GFP_KERNEL); if (!gmod_string) return -ENOMEM; diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 2830d552f0e1..8ffeb172e4d5 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -63,7 +63,7 @@ struct greybus_descriptor_module { }; struct greybus_descriptor_string { - __le16 length; + __u8 length; __u8 id; __u8 string[0]; }; -- cgit v1.2.3-59-g8ed1b From c41a36677365e6c5ad9e01ef50776052dfb6c0c5 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Fri, 26 Sep 2014 20:49:52 -0500 Subject: greybus: update descriptor type enums to match renumbering in spec Greybus spec updated the descriptor type values and added an additional class descriptor type. Change the enum accordingly. Signed-off-by: Matt Porter Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 8ffeb172e4d5..f1f323704107 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -20,8 +20,9 @@ struct greybus_manifest_header { enum greybus_descriptor_type { GREYBUS_TYPE_INVALID = 0x0000, - GREYBUS_TYPE_FUNCTION = 0x0001, - GREYBUS_TYPE_MODULE = 0x0002, + GREYBUS_TYPE_MODULE = 0x0001, + GREYBUS_TYPE_FUNCTION = 0x0002, + GREYBUS_TYPE_CLASS = 0x0003, GREYBUS_TYPE_STRING = 0x0004, GREYBUS_TYPE_CPORT = 0x0005, }; -- cgit v1.2.3-59-g8ed1b From 877b1ee82e4eb5d0fa56ff5eef310c75556c8762 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 24 Sep 2014 05:16:13 -0500 Subject: greybus: encapsulate URB status checking Define a new common function check_urb_status() that looks at the status in a completed URB (containing incoming data) and issues warnings in a consistent way. It returns -EAGAIN to signal an unrecognized status was seen, so the caller can ignore it and re-post the URB to receive the next incoming data. This consolidates three blocks of code into one. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 79 ++++++++++++++---------------------- 1 file changed, 30 insertions(+), 49 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index dfe96cb36a4e..7ce3a7743e58 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -227,17 +227,16 @@ static struct greybus_host_driver es1_driver = { .submit_gbuf = submit_gbuf, }; -/* Callback for when we get a SVC message */ -static void svc_callback(struct urb *urb) +/* Common function to report consistent warnings based on URB status */ +static int check_urb_status(struct urb *urb) { - struct es1_ap_dev *es1 = urb->context; struct device *dev = &urb->dev->dev; int status = urb->status; - int retval; switch (status) { case 0: - break; + return 0; + case -EOVERFLOW: dev_err(dev, "%s: overflow actual length is %d\n", __func__, urb->actual_length); @@ -246,11 +245,25 @@ static void svc_callback(struct urb *urb) case -ESHUTDOWN: case -EILSEQ: /* device is gone, stop sending */ - return; - default: - dev_err(dev, "%s: unknown status %d\n", __func__, status); - goto exit; + return status; } + dev_err(dev, "%s: unknown status %d\n", __func__, status); + + return -EAGAIN; +} + +/* Callback for when we get a SVC message */ +static void svc_in_callback(struct urb *urb) +{ + struct es1_ap_dev *es1 = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status == -EAGAIN) + goto exit; + if (status) + return; /* We have a message, create a new message structure, add it to the * list, and wake up our thread that will process the messages. @@ -268,27 +281,15 @@ static void cport_in_callback(struct urb *urb) { struct device *dev = &urb->dev->dev; struct es1_ap_dev *es1 = urb->context; - int status = urb->status; + int status = check_urb_status(urb); int retval; u8 cport; u8 *data; - switch (status) { - case 0: - break; - case -EOVERFLOW: - dev_err(dev, "%s: overflow actual length is %d\n", - __func__, urb->actual_length); - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - case -EILSEQ: - /* device is gone, stop sending */ - return; - default: - dev_err(dev, "%s: unknown status %d\n", __func__, status); + if (status == -EAGAIN) goto exit; - } + if (status) + return; /* The size has to be more then just an "empty" transfer */ if (urb->actual_length <= 2) { @@ -318,35 +319,15 @@ exit: static void cport_out_callback(struct urb *urb) { - struct device *dev = &urb->dev->dev; struct gbuf *gbuf = urb->context; struct es1_ap_dev *es1 = gbuf->hdpriv; unsigned long flags; - int status = urb->status; int i; - /* do we care about errors going back up? */ - switch (status) { - case 0: - break; - case -EOVERFLOW: - dev_err(dev, "%s: overflow actual length is %d\n", - __func__, urb->actual_length); - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - case -EILSEQ: - /* device is gone, stop sending */ - goto exit; - default: - dev_err(dev, "%s: unknown status %d\n", __func__, status); - goto exit; - } - - /* Tell the core the gbuf is properly sent */ - greybus_gbuf_finished(gbuf); + /* If no error, tell the core the gbuf is properly sent */ + if (!check_urb_status(urb)) + greybus_gbuf_finished(gbuf); -exit: /* * See if this was an urb in our pool, if so mark it "free", otherwise * we need to free it ourselves. @@ -443,7 +424,7 @@ static int ap_probe(struct usb_interface *interface, usb_fill_int_urb(es1->svc_urb, udev, usb_rcvintpipe(udev, es1->svc_endpoint), - es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_callback, + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, es1, svc_interval); retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); if (retval) -- cgit v1.2.3-59-g8ed1b From 0db32a6d5e58d85cfa4ffe5cebbde60917f61bba Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 24 Sep 2014 05:16:14 -0500 Subject: greybus: some more renames This patch renames of symbols, for better clarity and consistency. cport -> cport_id (when it represents a cport *number*) send_svc_msg -> submit_svc (like submit_gbuf) greybus_cport_in_data -> greybus_cport_in gb_new_ap_msg -> greybus_svc_in (like greybus_cport_in) cport->number -> cport->id (like cport_id) Making the svc and cport message stuff more similar is done with an eye toward having SVC messages and messages exchanged with other modules use some more common communication mechanisms. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 6 +++--- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/es1-ap-usb.c | 10 +++++----- drivers/staging/greybus/gbuf.c | 24 ++++++++++++------------ drivers/staging/greybus/greybus.h | 16 ++++++++-------- drivers/staging/greybus/greybus_manifest.h | 2 +- drivers/staging/greybus/test_sink.c | 4 ++-- drivers/staging/greybus/uart-gb.c | 2 +- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 78e9e4a1db2e..e3684299b9f3 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -53,7 +53,7 @@ static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) // FIXME - Do we need to do more than just pass it to the hd and then // free it? - retval = hd->driver->send_svc_msg(svc_msg, hd); + retval = hd->driver->submit_svc(svc_msg, hd); svc_msg_free(svc_msg); return retval; @@ -294,7 +294,7 @@ static void ap_process_event(struct work_struct *work) kfree(ap_msg); } -int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd) +int greybus_svc_in(u8 *data, int size, struct greybus_host_device *hd) { struct ap_msg *ap_msg; @@ -326,7 +326,7 @@ int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd) return 0; } -EXPORT_SYMBOL_GPL(gb_new_ap_msg); +EXPORT_SYMBOL_GPL(greybus_svc_in); int gb_ap_init(void) { diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 05217c630ff7..a240a92c267a 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -315,7 +315,7 @@ static int create_cport(struct greybus_module *gmod, if (!gmod_cport) return -ENOMEM; - gmod_cport->number = le16_to_cpu(cport->number); + gmod_cport->id = le16_to_cpu(cport->id); gmod_cport->size = le16_to_cpu(cport->size); gmod_cport->speed = cport->speed; diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 7ce3a7743e58..b6ab12450cd6 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -113,7 +113,7 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) * we will encode the cport number in the first byte of the buffer, so * set the second byte to be the "transfer buffer" */ - buffer[0] = gbuf->cport->number; + buffer[0] = gbuf->cport->id; gbuf->transfer_buffer = &buffer[1]; gbuf->transfer_buffer_length = size; gbuf->actual_length = size; @@ -139,7 +139,7 @@ static void free_gbuf_data(struct gbuf *gbuf) } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ -static int send_svc_msg(struct svc_msg *svc_msg, struct greybus_host_device *hd) +static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) { struct es1_ap_dev *es1 = hd_to_es1(hd); int retval; @@ -223,7 +223,7 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .alloc_gbuf_data = alloc_gbuf_data, .free_gbuf_data = free_gbuf_data, - .send_svc_msg = send_svc_msg, + .submit_svc = submit_svc, .submit_gbuf = submit_gbuf, }; @@ -268,7 +268,7 @@ static void svc_in_callback(struct urb *urb) /* We have a message, create a new message structure, add it to the * list, and wake up our thread that will process the messages. */ - gb_new_ap_msg(urb->transfer_buffer, urb->actual_length, es1->hd); + greybus_svc_in(urb->transfer_buffer, urb->actual_length, es1->hd); exit: /* resubmit the urb to get more messages */ @@ -307,7 +307,7 @@ static void cport_in_callback(struct urb *urb) data = &data[1]; /* Pass this data to the greybus core */ - greybus_cport_in_data(es1->hd, cport, data, urb->actual_length - 1); + greybus_cport_in(es1->hd, cport, data, urb->actual_length - 1); exit: /* put our urb back in the request pool */ diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 46896e829af0..94c2d6acb491 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -156,36 +156,36 @@ static struct gb_cport_handler cport_handler[MAX_CPORTS]; // need it, we don't have a dynamic system... int gb_register_cport_complete(struct greybus_module *gmod, - gbuf_complete_t handler, int cport, + gbuf_complete_t handler, int cport_id, void *context) { - if (cport_handler[cport].handler) + if (cport_handler[cport_id].handler) return -EINVAL; - cport_handler[cport].context = context; - cport_handler[cport].gmod = gmod; - cport_handler[cport].cport.number = cport; - cport_handler[cport].handler = handler; + cport_handler[cport_id].context = context; + cport_handler[cport_id].gmod = gmod; + cport_handler[cport_id].cport.id = cport_id; + cport_handler[cport_id].handler = handler; return 0; } -void gb_deregister_cport_complete(int cport) +void gb_deregister_cport_complete(int cport_id) { - cport_handler[cport].handler = NULL; + cport_handler[cport_id].handler = NULL; } -void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, +void greybus_cport_in(struct greybus_host_device *hd, int cport_id, u8 *data, size_t length) { struct gb_cport_handler *ch; struct gbuf *gbuf; /* first check to see if we have a cport handler for this cport */ - ch = &cport_handler[cport]; + ch = &cport_handler[cport_id]; if (!ch->handler) { /* Ugh, drop the data on the floor, after logging it... */ dev_err(hd->parent, "Received data for cport %d, but no handler!\n", - cport); + cport_id); return; } @@ -216,7 +216,7 @@ void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, queue_work(gbuf_workqueue, &gbuf->event); } -EXPORT_SYMBOL_GPL(greybus_cport_in_data); +EXPORT_SYMBOL_GPL(greybus_cport_in); /* Can be called in interrupt context, do the work and get out of here */ void greybus_gbuf_finished(struct gbuf *gbuf) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 87e6218d88db..794822066c7b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -91,9 +91,9 @@ Submit a SVC message to the hardware the host controller function send_svc_msg is called Receive gbuf messages - the host controller driver must call greybus_cport_in_data() with the data + the host controller driver must call greybus_cport_in() with the data Reveive SVC messages from the hardware - The host controller driver must call gb_new_ap_msg + The host controller driver must call greybus_svc_in */ @@ -102,7 +102,7 @@ struct gbuf; struct gmod_cport { - u16 number; + u16 id; u16 size; u8 speed; // valid??? // FIXME, what else? @@ -169,7 +169,7 @@ struct greybus_host_driver { int (*alloc_gbuf_data)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); void (*free_gbuf_data)(struct gbuf *gbuf); - int (*send_svc_msg)(struct svc_msg *svc_msg, + int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); int (*submit_gbuf)(struct gbuf *gbuf, struct greybus_host_device *hd, gfp_t gfp_mask); @@ -187,7 +187,7 @@ struct greybus_host_device { struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver, struct device *parent); void greybus_remove_hd(struct greybus_host_device *hd); -void greybus_cport_in_data(struct greybus_host_device *hd, int cport, u8 *data, +void greybus_cport_in(struct greybus_host_device *hd, int cport_id, u8 *data, size_t length); void greybus_gbuf_finished(struct gbuf *gbuf); @@ -289,7 +289,7 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size); void gb_remove_module(struct greybus_host_device *hd, u8 module_id); -int gb_new_ap_msg(u8 *data, int length, struct greybus_host_device *hd); +int greybus_svc_in(u8 *data, int length, struct greybus_host_device *hd); int gb_ap_init(void); void gb_ap_exit(void); int gb_debugfs_init(void); @@ -298,9 +298,9 @@ int gb_gbuf_init(void); void gb_gbuf_exit(void); int gb_register_cport_complete(struct greybus_module *gmod, - gbuf_complete_t handler, int cport, + gbuf_complete_t handler, int cport_id, void *context); -void gb_deregister_cport_complete(int cport); +void gb_deregister_cport_complete(int cport_id); extern const struct attribute_group *greybus_module_groups[]; diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index f1f323704107..6f019fa8f4ca 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -70,7 +70,7 @@ struct greybus_descriptor_string { }; struct greybus_descriptor_cport { - __le16 number; + __le16 id; __le16 size; __u8 speed; // FIXME __u8 reserved; diff --git a/drivers/staging/greybus/test_sink.c b/drivers/staging/greybus/test_sink.c index 9dbf6eeba8a6..8b254e3fb037 100644 --- a/drivers/staging/greybus/test_sink.c +++ b/drivers/staging/greybus/test_sink.c @@ -16,9 +16,9 @@ struct test_device { }; int gb_register_cport_complete(struct greybus_module *gmod, - gbuf_complete_t handler, int cport, + gbuf_complete_t handler, int cport_id, void *context); -void gb_deregister_cport_complete(int cport); +void gb_deregister_cport_complete(int cport_id); diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 2021f2775e22..7735a045da69 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -34,7 +34,7 @@ struct gb_tty { struct tty_port port; struct greybus_module *gmod; - int cport; + int cport_id; unsigned int minor; unsigned char clocal; unsigned int throttled:1; -- cgit v1.2.3-59-g8ed1b From 908a85d7dd09f5acc28de147a2e3dc84aa2d0b6b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 24 Sep 2014 05:16:16 -0500 Subject: greybus: update a few header file comments Add a GPLv2 tag and reword some comments at the top of "greybus_desc.h" and "svc_msg.h". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 8 +++++--- drivers/staging/greybus/svc_msg.h | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 6f019fa8f4ca..844a7f12ea8d 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -1,10 +1,12 @@ /* - * Greybus device descriptor definition + * Greybus module manifest definition * - * Defined in the "Greybus Application Protocol" document. - * See that document for any details on these values and structures. + * See "Greybus Application Protocol" document (version 0.draft) for + * details on these values and structures. * * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. */ #ifndef __GREYBUS_DESC_H diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index c290547305ac..8b936ed48d93 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -1,10 +1,12 @@ /* * Greybus AP <-> SVC message structure format. * - * Defined in the "Greybus Application Protocol" document. - * See that document for any details on these values and structures. + * See "Greybus Application Protocol" document (version 0.draft) for + * details on these values and structures. * * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. */ #ifndef __SVC_MSG_H -- cgit v1.2.3-59-g8ed1b From cb705e0dd0bc0614660aaf9adb0984c538532acf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 24 Sep 2014 05:16:17 -0500 Subject: greybus: fix document version number Two spots use the old "0.draft" version number for the Greybus protocol specification. We've updated that to be 0.1. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 2 +- drivers/staging/greybus/svc_msg.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 844a7f12ea8d..8865beec9bed 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -1,7 +1,7 @@ /* * Greybus module manifest definition * - * See "Greybus Application Protocol" document (version 0.draft) for + * See "Greybus Application Protocol" document (version 0.1) for * details on these values and structures. * * Copyright 2014 Google Inc. diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 8b936ed48d93..4b696040b4e1 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -1,7 +1,7 @@ /* * Greybus AP <-> SVC message structure format. * - * See "Greybus Application Protocol" document (version 0.draft) for + * See "Greybus Application Protocol" document (version 0.1) for * details on these values and structures. * * Copyright 2014 Google Inc. -- cgit v1.2.3-59-g8ed1b From 1dd7f58f8e24e03a3a5eea85a547a1ba91d82e81 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 26 Sep 2014 20:55:32 -0500 Subject: greybus: fix the guard in "greybus_manifest.h" The tag in the #ifndef guard surrounding the content of "greybus_manifest.h" needs to be updated to reflect the actual name of the file. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 8865beec9bed..75dea816fa69 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -9,8 +9,8 @@ * Released under the GPLv2 only. */ -#ifndef __GREYBUS_DESC_H -#define __GREYBUS_DESC_H +#ifndef __GREYBUS_MANIFEST_H +#define __GREYBUS_MANIFEST_H #pragma pack(push, 1) @@ -95,4 +95,4 @@ struct greybus_manifest { #pragma pack(pop) -#endif /* __GREYBUS_DESC_H */ +#endif /* __GREYBUS_MANIFEST_H */ -- cgit v1.2.3-59-g8ed1b From 72b0ffc019137b4877145ef7143cd69a6194b9c6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 26 Sep 2014 20:55:33 -0500 Subject: greybus: descriptor type is 1 byte The spec was changed to require only one byte to represent the type of a module descriptor. Update our data type and the values used to reflect that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 75dea816fa69..086705404536 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -21,17 +21,17 @@ struct greybus_manifest_header { }; enum greybus_descriptor_type { - GREYBUS_TYPE_INVALID = 0x0000, - GREYBUS_TYPE_MODULE = 0x0001, - GREYBUS_TYPE_FUNCTION = 0x0002, - GREYBUS_TYPE_CLASS = 0x0003, - GREYBUS_TYPE_STRING = 0x0004, - GREYBUS_TYPE_CPORT = 0x0005, + GREYBUS_TYPE_INVALID = 0x00, + GREYBUS_TYPE_MODULE = 0x01, + GREYBUS_TYPE_FUNCTION = 0x02, + GREYBUS_TYPE_CLASS = 0x03, + GREYBUS_TYPE_STRING = 0x04, + GREYBUS_TYPE_CPORT = 0x05, }; struct greybus_descriptor_header { __le16 size; - __le16 type; /* enum greybus_descriptor_type */ + __u8 type; /* enum greybus_descriptor_type */ }; enum greybus_function_type { -- cgit v1.2.3-59-g8ed1b From 01e8280150a2ef330f082a996479b96d75164353 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 26 Sep 2014 20:55:34 -0500 Subject: greybus: fix version check When we read a module manifest we are required to verify that its version is compatible with the version the present code is able to parse. All that's required is a check of the major version number. If the manifest's major version is greater than the software, the software can't assume it can parse it. All new code must be able to parse all old versions of the format. And any difference in minor version is supposed to have no effect on parsability. Update the version check to enforce this policy, and reword the error message to do a better job of explaining the situation. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index a240a92c267a..3cc54ed492ee 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -338,8 +338,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, struct greybus_manifest *manifest; int retval; int overall_size; - u8 version_major; - u8 version_minor; /* we have to have at _least_ the manifest header */ if (size <= sizeof(manifest->header)) @@ -367,16 +365,13 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, goto error; } - version_major = manifest->header.version_major; - version_minor = manifest->header.version_minor; - /* Validate major/minor number */ - if ((version_major != GREYBUS_VERSION_MAJOR) || - (version_minor != GREYBUS_VERSION_MINOR)) { + if (manifest->header.version_major > GREYBUS_VERSION_MAJOR) { dev_err(hd->parent, - "Invalid greybus versions, expected %d.%d, got %d.%d\n", - GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR, - version_major, version_minor); + "Manifest version too new (%hhu.%hhu > %hhu.%hhu)\n", + manifest->header.version_major, + manifest->header.version_minor, + GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); goto error; } -- cgit v1.2.3-59-g8ed1b From 51c75fd060a71dfdf4f836759a2b9300ae8af138 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 26 Sep 2014 20:55:35 -0500 Subject: greybus: reorder greybus_svc_in() arguments The two functions greybus_svc_in() and greybus_cport_in() do very similar things, but their arguments are in a different order. Move the greybus_host_device structure argument for greybus_svc_in() to be first so the functions' prototypes are better aligned. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 2 +- drivers/staging/greybus/es1-ap-usb.c | 2 +- drivers/staging/greybus/greybus.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index e3684299b9f3..f4470b5b2972 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -294,7 +294,7 @@ static void ap_process_event(struct work_struct *work) kfree(ap_msg); } -int greybus_svc_in(u8 *data, int size, struct greybus_host_device *hd) +int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size) { struct ap_msg *ap_msg; diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index b6ab12450cd6..291da4018cc0 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -268,7 +268,7 @@ static void svc_in_callback(struct urb *urb) /* We have a message, create a new message structure, add it to the * list, and wake up our thread that will process the messages. */ - greybus_svc_in(urb->transfer_buffer, urb->actual_length, es1->hd); + greybus_svc_in(es1->hd, urb->transfer_buffer, urb->actual_length); exit: /* resubmit the urb to get more messages */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 794822066c7b..1328dc9cc6f1 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -289,7 +289,7 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size); void gb_remove_module(struct greybus_host_device *hd, u8 module_id); -int greybus_svc_in(u8 *data, int length, struct greybus_host_device *hd); +int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); int gb_ap_init(void); void gb_ap_exit(void); int gb_debugfs_init(void); -- cgit v1.2.3-59-g8ed1b From f0f70916fba72ca70235ca40db43030ec8f467ad Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 30 Sep 2014 16:01:40 -0400 Subject: greybus: es1-ap-usb: adjust SVC buffer size to handle worst case The worst case message from the SVC->AP is a hotplug "plugged" event. It includes the module manifest which may be up to 64KB in size. Adjust our buffer allocation to allow for this. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 291da4018cc0..8df16437cc8b 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -9,12 +9,13 @@ #include #include #include +#include #include #include "greybus.h" #include "svc_msg.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_SVC_MSG_SIZE 2048 +#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE PAGE_SIZE -- cgit v1.2.3-59-g8ed1b From 1cfc667d75d3a38989365da387f52a1c303c435d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 30 Sep 2014 19:25:21 -0500 Subject: greybus: kill struct gmod_cport A UniPro (short header) segment has a 5-bit field to represent a CPort Id. In addition, the 7-bit L3 short header holds a UniPro device id. There can be no more than 128 devices in a UniPro network, but these two fields can be combined in ways to allow for over 2000 CPorts within a single device. As a result, a device id is represented with one byte, and a CPort Id within a device is always representable with a two byte value. This patch changes integral values that reresent CPort Ids so they use type u16 consistently. Separately, the contents of the gmod_cport structure were mostly fabricated, with the cport_id field being the only one that's meaningful. This patch gets rid of that structure, putting a simple u16 to represent the CPort Id everywhere it had been used before. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 14 +------------- drivers/staging/greybus/es1-ap-usb.c | 5 ++++- drivers/staging/greybus/gbuf.c | 22 +++++++++++----------- drivers/staging/greybus/greybus.h | 21 +++++++-------------- drivers/staging/greybus/kernel_ver.h | 3 +++ drivers/staging/greybus/svc_msg.h | 4 ++-- drivers/staging/greybus/test_sink.c | 4 ++-- drivers/staging/greybus/uart-gb.c | 2 +- 8 files changed, 31 insertions(+), 44 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 3cc54ed492ee..f8ba49cea0b9 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -160,8 +160,6 @@ static void greybus_module_release(struct device *dev) for (i = 0; i < gmod->num_strings; ++i) kfree(gmod->string[i]); - for (i = 0; i < gmod->num_cports; ++i) - kfree(gmod->cport[i]); kfree(gmod); } @@ -298,8 +296,6 @@ static int create_cport(struct greybus_module *gmod, struct greybus_descriptor_cport *cport, size_t desc_size) { - struct gmod_cport *gmod_cport; - if (gmod->num_cports == MAX_CPORTS_PER_MODULE) { dev_err(gmod->dev.parent, "too many cports for this module!\n"); return -EINVAL; @@ -311,15 +307,7 @@ static int create_cport(struct greybus_module *gmod, return -EINVAL; } - gmod_cport = kzalloc(sizeof(*gmod_cport), GFP_KERNEL); - if (!gmod_cport) - return -ENOMEM; - - gmod_cport->id = le16_to_cpu(cport->id); - gmod_cport->size = le16_to_cpu(cport->size); - gmod_cport->speed = cport->speed; - - gmod->cport[gmod->num_cports] = gmod_cport; + gmod->cport_ids[gmod->num_cports] = le16_to_cpu(cport->id); gmod->num_cports++; return 0; diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 8df16437cc8b..410acd304dee 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -11,8 +11,10 @@ #include #include #include + #include "greybus.h" #include "svc_msg.h" +#include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) @@ -114,7 +116,8 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) * we will encode the cport number in the first byte of the buffer, so * set the second byte to be the "transfer buffer" */ - buffer[0] = gbuf->cport->id; + BUG_ON(gbuf->cport_id > (u16)U8_MAX); + buffer[0] = gbuf->cport_id; gbuf->transfer_buffer = &buffer[1]; gbuf->transfer_buffer_length = size; gbuf->actual_length = size; diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 94c2d6acb491..7b4a72a7c4b4 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -27,7 +27,7 @@ static struct kmem_cache *gbuf_head_cache; static struct workqueue_struct *gbuf_workqueue; static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, - struct gmod_cport *cport, + u16 cport_id, gbuf_complete_t complete, gfp_t gfp_mask, void *context) @@ -40,7 +40,7 @@ static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, kref_init(&gbuf->kref); gbuf->gmod = gmod; - gbuf->cport = cport; + gbuf->cport_id = cport_id; INIT_WORK(&gbuf->event, cport_process_event); gbuf->complete = complete; gbuf->context = context; @@ -64,7 +64,7 @@ static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, * hardware designers for this issue... */ struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, - struct gmod_cport *cport, + u16 cport_id, gbuf_complete_t complete, unsigned int size, gfp_t gfp_mask, @@ -73,7 +73,7 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, struct gbuf *gbuf; int retval; - gbuf = __alloc_gbuf(gmod, cport, complete, gfp_mask, context); + gbuf = __alloc_gbuf(gmod, cport_id, complete, gfp_mask, context); if (!gbuf) return NULL; @@ -146,7 +146,7 @@ static void cport_process_event(struct work_struct *work) #define MAX_CPORTS 1024 struct gb_cport_handler { gbuf_complete_t handler; - struct gmod_cport cport; + u16 cport_id; struct greybus_module *gmod; void *context; }; @@ -156,25 +156,25 @@ static struct gb_cport_handler cport_handler[MAX_CPORTS]; // need it, we don't have a dynamic system... int gb_register_cport_complete(struct greybus_module *gmod, - gbuf_complete_t handler, int cport_id, + gbuf_complete_t handler, u16 cport_id, void *context) { if (cport_handler[cport_id].handler) return -EINVAL; cport_handler[cport_id].context = context; cport_handler[cport_id].gmod = gmod; - cport_handler[cport_id].cport.id = cport_id; + cport_handler[cport_id].cport_id = cport_id; cport_handler[cport_id].handler = handler; return 0; } -void gb_deregister_cport_complete(int cport_id) +void gb_deregister_cport_complete(u16 cport_id) { cport_handler[cport_id].handler = NULL; } -void greybus_cport_in(struct greybus_host_device *hd, int cport_id, u8 *data, - size_t length) +void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, + u8 *data, size_t length) { struct gb_cport_handler *ch; struct gbuf *gbuf; @@ -189,7 +189,7 @@ void greybus_cport_in(struct greybus_host_device *hd, int cport_id, u8 *data, return; } - gbuf = __alloc_gbuf(ch->gmod, &ch->cport, ch->handler, GFP_ATOMIC, + gbuf = __alloc_gbuf(ch->gmod, ch->cport_id, ch->handler, GFP_ATOMIC, ch->context); if (!gbuf) { /* Again, something bad went wrong, log it... */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 1328dc9cc6f1..4ca11f289ee0 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -101,13 +101,6 @@ struct gbuf; -struct gmod_cport { - u16 id; - u16 size; - u8 speed; // valid??? - // FIXME, what else? -}; - struct gmod_string { u16 length; u8 id; @@ -121,7 +114,7 @@ struct gbuf { void *hdpriv; struct greybus_module *gmod; - struct gmod_cport *cport; + u16 cport_id; int status; void *transfer_buffer; u32 transfer_flags; /* flags for the transfer buffer */ @@ -187,8 +180,8 @@ struct greybus_host_device { struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver, struct device *parent); void greybus_remove_hd(struct greybus_host_device *hd); -void greybus_cport_in(struct greybus_host_device *hd, int cport_id, u8 *data, - size_t length); +void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, + u8 *data, size_t length); void greybus_gbuf_finished(struct gbuf *gbuf); @@ -203,7 +196,7 @@ struct greybus_module { struct greybus_descriptor_module module; int num_cports; int num_strings; - struct gmod_cport *cport[MAX_CPORTS_PER_MODULE]; + u16 cport_ids[MAX_CPORTS_PER_MODULE]; struct gmod_string *string[MAX_STRINGS_PER_MODULE]; struct greybus_host_device *hd; @@ -218,7 +211,7 @@ struct greybus_module { #define to_greybus_module(d) container_of(d, struct greybus_module, dev) struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, - struct gmod_cport *cport, + u16 cport_id, gbuf_complete_t complete, unsigned int size, gfp_t gfp_mask, @@ -298,9 +291,9 @@ int gb_gbuf_init(void); void gb_gbuf_exit(void); int gb_register_cport_complete(struct greybus_module *gmod, - gbuf_complete_t handler, int cport_id, + gbuf_complete_t handler, u16 cport_id, void *context); -void gb_deregister_cport_complete(int cport_id); +void gb_deregister_cport_complete(u16 cport_id); extern const struct attribute_group *greybus_module_groups[]; diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 40cc2e8af525..4aa5b83bff60 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -18,5 +18,8 @@ struct device_attribute dev_attr_##_name = __ATTR_RO(_name) #endif +#ifndef U8_MAX +#define U8_MAX ((u8)~0U) +#endif /* ! U8_MAX */ #endif /* __GREYBUS_KERNEL_VER_H */ diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 4b696040b4e1..986368d10323 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -49,9 +49,9 @@ struct svc_function_handshake { struct svc_function_unipro_set_route { __u8 source_module_id; - __u8 source_cport_id; + __u8 source_cport_id; /* bottom 8 bits */ __u8 destination_module_id; - __u8 destination_cport_id; + __u8 destination_cport_id; /* bottom 8 bits */ }; struct svc_function_unipro_link_up { diff --git a/drivers/staging/greybus/test_sink.c b/drivers/staging/greybus/test_sink.c index 8b254e3fb037..811a237898cc 100644 --- a/drivers/staging/greybus/test_sink.c +++ b/drivers/staging/greybus/test_sink.c @@ -16,9 +16,9 @@ struct test_device { }; int gb_register_cport_complete(struct greybus_module *gmod, - gbuf_complete_t handler, int cport_id, + gbuf_complete_t handler, u16 cport_id, void *context); -void gb_deregister_cport_complete(int cport_id); +void gb_deregister_cport_complete(u16 cport_id); diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 7735a045da69..7f9d49869281 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -34,7 +34,7 @@ struct gb_tty { struct tty_port port; struct greybus_module *gmod; - int cport_id; + u16 cport_id; unsigned int minor; unsigned char clocal; unsigned int throttled:1; -- cgit v1.2.3-59-g8ed1b From a6cdb3492e845dd1c784d35e0cfec0c6ac66aaef Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 30 Sep 2014 18:21:36 -0700 Subject: greybus: es1: no BUG_ON() code, report an error and recover. --- drivers/staging/greybus/es1-ap-usb.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 410acd304dee..f23c67414ecf 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -116,7 +116,13 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) * we will encode the cport number in the first byte of the buffer, so * set the second byte to be the "transfer buffer" */ - BUG_ON(gbuf->cport_id > (u16)U8_MAX); + if (gbuf->cport_id > (u16)U8_MAX) { + pr_err("gbuf->cport_id is '%d' and is out of range!\n", + gbuf->cport_id); + kfree(buffer); + return -EINVAL; + } + buffer[0] = gbuf->cport_id; gbuf->transfer_buffer = &buffer[1]; gbuf->transfer_buffer_length = size; -- cgit v1.2.3-59-g8ed1b From 9c852d2b8feee2d559032363980b6372182cbe34 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 30 Sep 2014 19:25:22 -0500 Subject: greybus: clarify device vs module The Project ARA MDK states that a single module can have more than one interface block (up to 2 at the moment). An interface block consists of two bidirectional UniPro lanes (along with power and detect lines), and effectively represents a UniPro Device (with an id in the range 0-127). The service messages currently use "module_id" everywhere, even though in a lot of cases we really need to be talking about device ids. The easiest case of this to see is the "set route" request directed at a switch; a switch has no notion of modules, just UniPro devices. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc_msg.h | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 986368d10323..1f8b4f1b7017 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -48,14 +48,14 @@ struct svc_function_handshake { }; struct svc_function_unipro_set_route { - __u8 source_module_id; + __u8 source_device_id; __u8 source_cport_id; /* bottom 8 bits */ - __u8 destination_module_id; + __u8 destination_device_id; __u8 destination_cport_id; /* bottom 8 bits */ }; struct svc_function_unipro_link_up { - __u8 module_id; + __u8 device_id; }; enum svc_function_management_event { @@ -76,6 +76,11 @@ enum svc_function_hotplug_event { SVC_HOTUNPLUG_EVENT = 0x01, }; +/* XXX + * Does a hotplug come from module insertion, or from detection + * of each interface block (UniPro device) in a module? Assume + * the former for now. + */ struct svc_function_hotplug { __u8 hotplug_event; /* enum svc_function_hotplug_event */ __u8 module_id; @@ -87,6 +92,12 @@ enum svc_function_ddb_type { SVC_DDB_RESPONSE = 0x01, }; +/* XXX + * Will only the first interface block in a module be responsible + * for this? If a module has two interface blocks, will both supply + * the same information, or will it be partitioned? For now assume + * it's a per-module thing. + */ struct svc_function_ddb_get { __u8 module_id; __u8 message_id; @@ -129,6 +140,11 @@ struct svc_function_power_battery_status { struct svc_function_power_battery_status_request { }; +/* XXX + * Each interface block carries power, so it's possible these things + * are associated with each UniPro device and not just the module. + * For now it's safe to assume it's per-module. + */ struct svc_function_power { __u8 power_type; /* enum svc_function_power_type */ __u8 module_id; @@ -143,6 +159,7 @@ enum svc_function_epm_command_type { SVC_EPM_DISABLE = 0x01, }; +/* EPM's are associated with the module */ struct svc_function_epm { __u8 epm_command_type; /* enum svc_function_epm_command_type */ __u8 module_id; @@ -153,9 +170,10 @@ enum svc_function_suspend_command_type { SVC_SUSPEND_FIXME_2 = 0x01, }; +/* We'll want independent control for multi-interface block modules */ struct svc_function_suspend { __u8 suspend_command_type; /* enum function_suspend_command_type */ - __u8 module_id; + __u8 device_id; }; struct svc_msg { -- cgit v1.2.3-59-g8ed1b From 7a57479b1d6727a3635833d75396650a1914fa7e Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 1 Oct 2014 15:09:00 -0400 Subject: greybus: remove additional unused fields from the cport descriptor The Greybus spec was updated to remove some unused fields from the CPort descriptor definition. Remove them from the structure so we don't fail manifest parsing. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 086705404536..4d801d785348 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -73,9 +73,6 @@ struct greybus_descriptor_string { struct greybus_descriptor_cport { __le16 id; - __le16 size; - __u8 speed; // FIXME - __u8 reserved; }; struct greybus_descriptor { -- cgit v1.2.3-59-g8ed1b From 513c54cb0566dc8aa74b4e3c603a35ee9f8e07ae Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 1 Oct 2014 15:09:01 -0400 Subject: greybus: fix error message on parse of cport descriptor size Fix a simple cut and paste error that was reporting a serial number header size error rather than a cport descriptor size error. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index f8ba49cea0b9..c065beb2f9ab 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -303,7 +303,7 @@ static int create_cport(struct greybus_module *gmod, if (desc_size != sizeof(*cport)) { dev_err(gmod->dev.parent, - "invalid serial number header size %zu\n", desc_size); + "invalid cport descriptor size %zu\n", desc_size); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From ecf7d579713155e7d2e2aa76227c68c4f64c5146 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:10 -0500 Subject: greybus: descriptor type updates Some more updates to the definition of a manifest descriptor. - We get rid of function descriptors. The type of function is easily specified with the CPort it uses. - Add a new interface descriptor type. - Clean up the CPort descriptor structure, eliminating fields that serve no purpose and adding the function id field The sysfs stuff will be updated a little later to add entries for the Greybus interfaces associated with modules. Rearrange the order of a few things in "greybus_manifest.h". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 17 +------- drivers/staging/greybus/greybus.h | 10 ++--- drivers/staging/greybus/greybus_manifest.h | 63 ++++++++++++++++++++---------- drivers/staging/greybus/sysfs.c | 39 ------------------ 4 files changed, 47 insertions(+), 82 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index c065beb2f9ab..e93ee40bb0db 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -232,19 +232,6 @@ static const struct greybus_module_id fake_gb_id = { GREYBUS_DEVICE(0x42, 0x42) }; -static int create_function(struct greybus_module *gmod, - struct greybus_descriptor_function *function, - size_t desc_size) -{ - if (desc_size != sizeof(*function)) { - dev_err(gmod->dev.parent, "invalid function header size %zu\n", - desc_size); - return -EINVAL; - } - memcpy(&gmod->function, function, desc_size); - return 0; -} - static int create_module(struct greybus_module *gmod, struct greybus_descriptor_module *module, size_t desc_size) @@ -385,9 +372,7 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, data_size = (size_t)desc_size - sizeof(desc->header); switch (le16_to_cpu(desc->header.type)) { - case GREYBUS_TYPE_FUNCTION: - retval = create_function(gmod, &desc->function, - data_size); + case GREYBUS_TYPE_DEVICE: break; case GREYBUS_TYPE_MODULE: diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4ca11f289ee0..c8bd8eb4d724 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -192,7 +192,6 @@ void greybus_gbuf_finished(struct gbuf *gbuf); struct greybus_module { struct device dev; u16 module_number; - struct greybus_descriptor_function function; struct greybus_descriptor_module module; int num_cports; int num_strings; @@ -210,12 +209,9 @@ struct greybus_module { }; #define to_greybus_module(d) container_of(d, struct greybus_module, dev) -struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, - u16 cport_id, - gbuf_complete_t complete, - unsigned int size, - gfp_t gfp_mask, - void *context); +struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, u16 cport_id, + gbuf_complete_t complete, unsigned int size, + gfp_t gfp_mask, void *context); void greybus_free_gbuf(struct gbuf *gbuf); struct gbuf *greybus_get_gbuf(struct gbuf *gbuf); #define greybus_put_gbuf greybus_free_gbuf diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 4d801d785348..61189995f7d9 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -14,26 +14,15 @@ #pragma pack(push, 1) -struct greybus_manifest_header { - __le16 size; - __u8 version_major; - __u8 version_minor; -}; - enum greybus_descriptor_type { GREYBUS_TYPE_INVALID = 0x00, GREYBUS_TYPE_MODULE = 0x01, - GREYBUS_TYPE_FUNCTION = 0x02, + GREYBUS_TYPE_DEVICE = 0x02, GREYBUS_TYPE_CLASS = 0x03, GREYBUS_TYPE_STRING = 0x04, GREYBUS_TYPE_CPORT = 0x05, }; -struct greybus_descriptor_header { - __le16 size; - __u8 type; /* enum greybus_descriptor_type */ -}; - enum greybus_function_type { GREYBUS_FUNCTION_CONTROL = 0x00, GREYBUS_FUNCTION_USB = 0x01, @@ -51,11 +40,10 @@ enum greybus_function_type { GREYBUS_FUNCTION_VENDOR = 0xff, }; -struct greybus_descriptor_function { - __le16 cport; - __u8 function_type; /* enum greybus_function_type */ -}; - +/* + * A module descriptor describes information about a module as a + * whole, *not* the functions within it. + */ struct greybus_descriptor_module { __le16 vendor; __le16 product; @@ -65,26 +53,61 @@ struct greybus_descriptor_module { __u8 product_stringid; }; +/* + * A UniPro device normally supports a range of 32 CPorts (0..31). + * It is possible to support more than this by having a UniPro + * switch treat one device as if it were more than one. E.g., + * allocate 3 device ids (rather than the normal--1) to physical + * device 5, and configure the switch to route all packets destined + * for "encoded" device ids 5, 6, and 7 to physical device 5. + * Device 5 uses the encoded device id in incoming UniPro packets to + * determine which bank of 32 CPorts should receive the UniPro + * segment. + * + * The "scale" field in this structure is used to define the number + * of encoded device ids should be allocated for this physical + * device. Scale is normally 1, to represent 32 available CPorts. + * A scale value 2 represents up to 64 CPorts; scale value 3 + * represents up to 96 CPorts, and so on. + */ +struct greybus_descriptor_interface { + __u8 id; /* module-relative id (0..) */ + __u8 scale; /* indicates range of of CPorts supported */ + /* UniPro gear, number of in/out lanes */ +}; + +struct greybus_descriptor_cport { + __le16 id; + __u8 function_type; /* enum greybus_function_type */ +}; + struct greybus_descriptor_string { __u8 length; __u8 id; __u8 string[0]; }; -struct greybus_descriptor_cport { - __le16 id; +struct greybus_descriptor_header { + __le16 size; + __u8 type; /* enum greybus_descriptor_type */ }; struct greybus_descriptor { struct greybus_descriptor_header header; union { - struct greybus_descriptor_function function; struct greybus_descriptor_module module; struct greybus_descriptor_string string; + struct greybus_descriptor_interface interface; struct greybus_descriptor_cport cport; }; }; +struct greybus_manifest_header { + __le16 size; + __u8 version_major; + __u8 version_minor; +}; + struct greybus_manifest { struct greybus_manifest_header header; struct greybus_descriptor descriptors[0]; diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 1f17388445ad..a2dce7a0f197 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -20,44 +20,6 @@ #include "kernel_ver.h" -/* Function fields */ -#define greybus_function_attr(field) \ -static ssize_t function_##field##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - struct greybus_module *gmod = to_greybus_module(dev); \ - return sprintf(buf, "%d\n", gmod->function.field); \ -} \ -static DEVICE_ATTR_RO(function_##field) - -greybus_function_attr(cport); -greybus_function_attr(function_type); - -static struct attribute *function_attrs[] = { - &dev_attr_function_cport.attr, - &dev_attr_function_function_type.attr, - NULL, -}; - -static umode_t function_attrs_are_visible(struct kobject *kobj, - struct attribute *a, int n) -{ - struct greybus_module *gmod = to_greybus_module(kobj_to_dev(kobj)); - - // FIXME - make this a dynamic structure to "know" if it really is here - // or not easier? - if (gmod->function.cport || - gmod->function.function_type) - return a->mode; - return 0; -} - -static struct attribute_group function_attr_grp = { - .attrs = function_attrs, - .is_visible = function_attrs_are_visible, -}; - /* Module fields */ #define greybus_module_attr(field) \ static ssize_t module_##field##_show(struct device *dev, \ @@ -147,7 +109,6 @@ static struct attribute_group module_attr_grp = { const struct attribute_group *greybus_module_groups[] = { - &function_attr_grp, &module_attr_grp, NULL, }; -- cgit v1.2.3-59-g8ed1b From e1e9dbddfe71de1efba5bc77b3f2b374e2ba0104 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:11 -0500 Subject: greybus: isolate greybus module code Define new source files "module.h" and "module.c" to separate the definitions of the Greybus module abstraction from other code. Rename "greybus_module" to be "gb_module", for brevity. Do the same for a few other symbols with "greybus_module" in their names. A few (like greybus_module_id) are more visible outside this kernel module so we'll keep their names more descriptive. Add a definition for U8_MAX in "kernel_ver.h" (it appeared in 3.14). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/battery-gb.c | 6 +-- drivers/staging/greybus/core.c | 74 +++++++--------------------- drivers/staging/greybus/gbuf.c | 13 ++--- drivers/staging/greybus/gpio-gb.c | 6 +-- drivers/staging/greybus/greybus.h | 82 +++++++++++-------------------- drivers/staging/greybus/i2c-gb.c | 9 ++-- drivers/staging/greybus/module.c | 93 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/module.h | 57 ++++++++++++++++++++++ drivers/staging/greybus/sdio-gb.c | 6 +-- drivers/staging/greybus/sysfs.c | 19 ++++---- drivers/staging/greybus/test_sink.c | 4 +- drivers/staging/greybus/uart-gb.c | 8 ++-- 13 files changed, 233 insertions(+), 145 deletions(-) create mode 100644 drivers/staging/greybus/module.c create mode 100644 drivers/staging/greybus/module.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 2e048a010a23..9b5d8e9684a6 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -3,6 +3,7 @@ greybus-y := core.o \ sysfs.o \ debugfs.o \ ap.o \ + module.o \ i2c-gb.o \ gpio-gb.o \ sdio-gb.o \ diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 5f1bf004dfbc..1ef2f17623b0 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -18,7 +18,7 @@ struct gb_battery { // we will want to keep the battery stats in here as we will be getting // updates from the SVC "on the fly" so we don't have to always go ask // the battery for some information. Hopefully... - struct greybus_module *gmod; + struct gb_module *gmod; }; #define to_gb_battery(x) container_of(x, struct gb_battery, bat) @@ -100,7 +100,7 @@ static enum power_supply_property battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, }; -int gb_battery_probe(struct greybus_module *gmod, +int gb_battery_probe(struct gb_module *gmod, const struct greybus_module_id *id) { struct gb_battery *gb; @@ -130,7 +130,7 @@ int gb_battery_probe(struct greybus_module *gmod, return 0; } -void gb_battery_disconnect(struct greybus_module *gmod) +void gb_battery_disconnect(struct gb_module *gmod) { struct gb_battery *gb; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index e93ee40bb0db..934bdebe039c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -30,51 +30,13 @@ int greybus_disabled(void) } EXPORT_SYMBOL_GPL(greybus_disabled); -static int greybus_match_one_id(struct greybus_module *gmod, - const struct greybus_module_id *id) -{ - struct greybus_descriptor_module *module; - - module = &gmod->module; - - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && - (id->vendor != le16_to_cpu(module->vendor))) - return 0; - - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && - (id->product != le16_to_cpu(module->product))) - return 0; - - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && - (id->serial_number != le64_to_cpu(module->serial_number))) - return 0; - - return 1; -} - -static const struct greybus_module_id *greybus_match_id( - struct greybus_module *gmod, - const struct greybus_module_id *id) -{ - if (id == NULL) - return NULL; - - for (; id->vendor || id->product || id->serial_number || - id->driver_info ; id++) { - if (greybus_match_one_id(gmod, id)) - return id; - } - - return NULL; -} - static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct greybus_module *gmod = to_greybus_module(dev); + struct gb_module *gmod = to_gb_module(dev); const struct greybus_module_id *id; - id = greybus_match_id(gmod, driver->id_table); + id = gb_module_match_id(gmod, driver->id_table); if (id) return 1; /* FIXME - Dyanmic ids? */ @@ -83,7 +45,7 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { - /* struct greybus_module *gmod = to_greybus_module(dev); */ + /* struct gb_module *gmod = to_gb_module(dev); */ /* FIXME - add some uevents here... */ return 0; @@ -98,12 +60,12 @@ static struct bus_type greybus_bus_type = { static int greybus_probe(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct greybus_module *gmod = to_greybus_module(dev); + struct gb_module *gmod = to_gb_module(dev); const struct greybus_module_id *id; int retval; /* match id */ - id = greybus_match_id(gmod, driver->id_table); + id = gb_module_match_id(gmod, driver->id_table); if (!id) return -ENODEV; @@ -117,7 +79,7 @@ static int greybus_probe(struct device *dev) static int greybus_remove(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct greybus_module *gmod = to_greybus_module(dev); + struct gb_module *gmod = to_gb_module(dev); driver->disconnect(gmod); return 0; @@ -155,7 +117,7 @@ EXPORT_SYMBOL_GPL(greybus_deregister); static void greybus_module_release(struct device *dev) { - struct greybus_module *gmod = to_greybus_module(dev); + struct gb_module *gmod = to_gb_module(dev); int i; for (i = 0; i < gmod->num_strings; ++i) @@ -164,7 +126,7 @@ static void greybus_module_release(struct device *dev) } -const u8 *greybus_string(struct greybus_module *gmod, int id) +const u8 *greybus_string(struct gb_module *gmod, int id) { int i; struct gmod_string *string; @@ -185,7 +147,7 @@ static struct device_type greybus_module_type = { .release = greybus_module_release, }; -static int gb_init_subdevs(struct greybus_module *gmod, +static int gb_init_subdevs(struct gb_module *gmod, const struct greybus_module_id *id) { int retval; @@ -228,11 +190,11 @@ error_i2c: return retval; } -static const struct greybus_module_id fake_gb_id = { +static const struct greybus_module_id fake_greybus_module_id = { GREYBUS_DEVICE(0x42, 0x42) }; -static int create_module(struct greybus_module *gmod, +static int create_module(struct gb_module *gmod, struct greybus_descriptor_module *module, size_t desc_size) { @@ -245,7 +207,7 @@ static int create_module(struct greybus_module *gmod, return 0; } -static int create_string(struct greybus_module *gmod, +static int create_string(struct gb_module *gmod, struct greybus_descriptor_string *string, size_t desc_size) { @@ -279,7 +241,7 @@ static int create_string(struct greybus_module *gmod, return 0; } -static int create_cport(struct greybus_module *gmod, +static int create_cport(struct gb_module *gmod, struct greybus_descriptor_cport *cport, size_t desc_size) { @@ -309,7 +271,7 @@ static int create_cport(struct greybus_module *gmod, void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size) { - struct greybus_module *gmod; + struct gb_module *gmod; struct greybus_manifest *manifest; int retval; int overall_size; @@ -318,11 +280,10 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, if (size <= sizeof(manifest->header)) return; - gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); + gmod = gb_module_create(hd, module_id); if (!gmod) return; - gmod->module_number = module_id; gmod->dev.parent = hd->parent; gmod->dev.driver = NULL; gmod->dev.bus = &greybus_bus_type; @@ -400,7 +361,7 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, data += desc_size; } - retval = gb_init_subdevs(gmod, &fake_gb_id); + retval = gb_init_subdevs(gmod, &fake_greybus_module_id); if (retval) goto error; @@ -418,7 +379,7 @@ void gb_remove_module(struct greybus_host_device *hd, u8 module_id) // FIXME should be the remove_device call... } -void greybus_remove_device(struct greybus_module *gmod) +void greybus_remove_device(struct gb_module *gmod) { /* tear down all of the "sub device types" for this device */ gb_i2c_disconnect(gmod); @@ -453,6 +414,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver kref_init(&hd->kref); hd->parent = parent; hd->driver = driver; + INIT_LIST_HEAD(&hd->modules); return hd; } diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 7b4a72a7c4b4..fada12192726 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -26,7 +26,7 @@ static struct kmem_cache *gbuf_head_cache; /* Workqueue to handle Greybus buffer completions. */ static struct workqueue_struct *gbuf_workqueue; -static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, +static struct gbuf *__alloc_gbuf(struct gb_module *gmod, u16 cport_id, gbuf_complete_t complete, gfp_t gfp_mask, @@ -63,7 +63,7 @@ static struct gbuf *__alloc_gbuf(struct greybus_module *gmod, * that the driver can then fill up with the data to be sent out. Curse * hardware designers for this issue... */ -struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, +struct gbuf *greybus_alloc_gbuf(struct gb_module *gmod, u16 cport_id, gbuf_complete_t complete, unsigned int size, @@ -80,7 +80,7 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, gbuf->direction = GBUF_DIRECTION_OUT; /* Host controller specific allocation for the actual buffer */ - retval = gbuf->gmod->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); + retval = gmod->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); if (retval) { greybus_free_gbuf(gbuf); return NULL; @@ -147,7 +147,7 @@ static void cport_process_event(struct work_struct *work) struct gb_cport_handler { gbuf_complete_t handler; u16 cport_id; - struct greybus_module *gmod; + struct gb_module *gmod; void *context; }; @@ -155,8 +155,9 @@ static struct gb_cport_handler cport_handler[MAX_CPORTS]; // FIXME - use a lock for this list of handlers, but really, for now we don't // need it, we don't have a dynamic system... -int gb_register_cport_complete(struct greybus_module *gmod, - gbuf_complete_t handler, u16 cport_id, +int gb_register_cport_complete(struct gb_module *gmod, + gbuf_complete_t handler, + u16 cport_id, void *context) { if (cport_handler[cport_id].handler) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 4b7186651db5..2369175bf0f9 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -14,7 +14,7 @@ struct gb_gpio_device { struct gpio_chip chip; - struct greybus_module *gmod; + struct gb_module *gmod; struct gpio_chip *gpio; // FIXME - some lock? }; @@ -49,7 +49,7 @@ static void gpio_set(struct gpio_chip *gpio, unsigned nr, int val) // FIXME - do something there } -int gb_gpio_probe(struct greybus_module *gmod, +int gb_gpio_probe(struct gb_module *gmod, const struct greybus_module_id *id) { struct gb_gpio_device *gb_gpio; @@ -86,7 +86,7 @@ int gb_gpio_probe(struct greybus_module *gmod, return 0; } -void gb_gpio_disconnect(struct greybus_module *gmod) +void gb_gpio_disconnect(struct gb_module *gmod) { struct gb_gpio_device *gb_gpio_dev; int retval; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index c8bd8eb4d724..a4fad874a70e 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -11,12 +11,16 @@ #ifdef __KERNEL__ +#include #include #include +#include #include #include + #include "greybus_id.h" #include "greybus_manifest.h" +#include "module.h" /* Matches up with the Greybus Protocol specification document */ @@ -113,7 +117,7 @@ struct gbuf { struct kref kref; void *hdpriv; - struct greybus_module *gmod; + struct gb_module *gmod; u16 cport_id; int status; void *transfer_buffer; @@ -142,7 +146,7 @@ struct gbuf { * same module as the gpio pins, etc.) * * So, put the "private" data structures here in greybus.h and link to them off - * of the "main" greybus_module structure. + * of the "main" gb_module structure. */ struct gb_i2c_device; @@ -173,6 +177,8 @@ struct greybus_host_device { struct device *parent; const struct greybus_host_driver *driver; + struct list_head modules; + /* Private data for the host driver */ unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64)))); }; @@ -184,32 +190,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); void greybus_gbuf_finished(struct gbuf *gbuf); - -/* Increase these values if needed */ -#define MAX_CPORTS_PER_MODULE 10 -#define MAX_STRINGS_PER_MODULE 10 - -struct greybus_module { - struct device dev; - u16 module_number; - struct greybus_descriptor_module module; - int num_cports; - int num_strings; - u16 cport_ids[MAX_CPORTS_PER_MODULE]; - struct gmod_string *string[MAX_STRINGS_PER_MODULE]; - - struct greybus_host_device *hd; - - struct gb_i2c_device *gb_i2c_dev; - struct gb_gpio_device *gb_gpio_dev; - struct gb_sdio_host *gb_sdio_host; - struct gb_tty *gb_tty; - struct gb_usb_device *gb_usb_dev; - struct gb_battery *gb_battery; -}; -#define to_greybus_module(d) container_of(d, struct greybus_module, dev) - -struct gbuf *greybus_alloc_gbuf(struct greybus_module *gmod, u16 cport_id, +struct gbuf *greybus_alloc_gbuf(struct gb_module *gmod, u16 cport_id, gbuf_complete_t complete, unsigned int size, gfp_t gfp_mask, void *context); void greybus_free_gbuf(struct gbuf *gbuf); @@ -223,12 +204,12 @@ int greybus_kill_gbuf(struct gbuf *gbuf); struct greybus_driver { const char *name; - int (*probe)(struct greybus_module *gmod, + int (*probe)(struct gb_module *gmod, const struct greybus_module_id *id); - void (*disconnect)(struct greybus_module *gmod); + void (*disconnect)(struct gb_module *gmod); - int (*suspend)(struct greybus_module *gmod, pm_message_t message); - int (*resume)(struct greybus_module *gmod); + int (*suspend)(struct gb_module *gmod, pm_message_t message); + int (*resume)(struct gb_module *gmod); const struct greybus_module_id *id_table; @@ -236,16 +217,6 @@ struct greybus_driver { }; #define to_greybus_driver(d) container_of(d, struct greybus_driver, driver) -static inline void greybus_set_drvdata(struct greybus_module *gmod, void *data) -{ - dev_set_drvdata(&gmod->dev, data); -} - -static inline void *greybus_get_drvdata(struct greybus_module *gmod) -{ - return dev_get_drvdata(&gmod->dev); -} - /* Don't call these directly, use the module_greybus_driver() macro instead */ int greybus_register_driver(struct greybus_driver *driver, struct module *module, const char *mod_name); @@ -268,9 +239,9 @@ void greybus_deregister(struct greybus_driver *driver); int greybus_disabled(void); -void greybus_remove_device(struct greybus_module *gmod); +void greybus_remove_device(struct gb_module *gmod); -const u8 *greybus_string(struct greybus_module *gmod, int id); +const u8 *greybus_string(struct gb_module *gmod, int id); /* Internal functions to gb module, move to internal .h file eventually. */ @@ -286,7 +257,7 @@ void gb_debugfs_cleanup(void); int gb_gbuf_init(void); void gb_gbuf_exit(void); -int gb_register_cport_complete(struct greybus_module *gmod, +int gb_register_cport_complete(struct gb_module *gmod, gbuf_complete_t handler, u16 cport_id, void *context); void gb_deregister_cport_complete(u16 cport_id); @@ -298,16 +269,17 @@ extern const struct attribute_group *greybus_module_groups[]; * we have static functions for this, not "dynamic" drivers like we really * should in the end. */ -int gb_i2c_probe(struct greybus_module *gmod, const struct greybus_module_id *id); -void gb_i2c_disconnect(struct greybus_module *gmod); -int gb_gpio_probe(struct greybus_module *gmod, const struct greybus_module_id *id); -void gb_gpio_disconnect(struct greybus_module *gmod); -int gb_sdio_probe(struct greybus_module *gmod, const struct greybus_module_id *id); -void gb_sdio_disconnect(struct greybus_module *gmod); -int gb_tty_probe(struct greybus_module *gmod, const struct greybus_module_id *id); -void gb_tty_disconnect(struct greybus_module *gmod); -int gb_battery_probe(struct greybus_module *gmod, const struct greybus_module_id *id); -void gb_battery_disconnect(struct greybus_module *gmod); +int gb_i2c_probe(struct gb_module *gmod, const struct greybus_module_id *id); +void gb_i2c_disconnect(struct gb_module *gmod); +int gb_gpio_probe(struct gb_module *gmod, const struct greybus_module_id *id); +void gb_gpio_disconnect(struct gb_module *gmod); +int gb_sdio_probe(struct gb_module *gmod, const struct greybus_module_id *id); +void gb_sdio_disconnect(struct gb_module *gmod); +int gb_tty_probe(struct gb_module *gmod, const struct greybus_module_id *id); +void gb_tty_disconnect(struct gb_module *gmod); +int gb_battery_probe(struct gb_module *gmod, + const struct greybus_module_id *id); +void gb_battery_disconnect(struct gb_module *gmod); int gb_tty_init(void); void gb_tty_exit(void); diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index bd03f19a3568..c01f41b59c55 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -10,11 +10,12 @@ #include #include #include + #include "greybus.h" struct gb_i2c_device { struct i2c_adapter *adapter; - struct greybus_module *gmod; + struct gb_module *gmod; }; static const struct greybus_module_id id_table[] = { @@ -33,7 +34,7 @@ static s32 i2c_gb_access(struct i2c_adapter *adap, u16 addr, int size, union i2c_smbus_data *data) { struct gb_i2c_device *gb_i2c_dev; - struct greybus_module *gmod; + struct gb_module *gmod; gb_i2c_dev = i2c_get_adapdata(adap); gmod = gb_i2c_dev->gmod; @@ -75,7 +76,7 @@ static const struct i2c_algorithm smbus_algorithm = { .functionality = i2c_gb_func, }; -int gb_i2c_probe(struct greybus_module *gmod, +int gb_i2c_probe(struct gb_module *gmod, const struct greybus_module_id *id) { struct gb_i2c_device *gb_i2c_dev; @@ -115,7 +116,7 @@ error: return retval; } -void gb_i2c_disconnect(struct greybus_module *gmod) +void gb_i2c_disconnect(struct gb_module *gmod) { struct gb_i2c_device *gb_i2c_dev; diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c new file mode 100644 index 000000000000..31017c2cae2f --- /dev/null +++ b/drivers/staging/greybus/module.c @@ -0,0 +1,93 @@ +/* + * Greybus modules + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +/* XXX This could be per-host device */ +static DEFINE_SPINLOCK(gb_modules_lock); + +static int gb_module_match_one_id(struct gb_module *gmod, + const struct greybus_module_id *id) +{ + struct greybus_descriptor_module *module; + + module = &gmod->module; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && + (id->vendor != le16_to_cpu(module->vendor))) + return 0; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && + (id->product != le16_to_cpu(module->product))) + return 0; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && + (id->serial_number != le64_to_cpu(module->serial_number))) + return 0; + + return 1; +} + +const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, + const struct greybus_module_id *id) +{ + if (id == NULL) + return NULL; + + for (; id->vendor || id->product || id->serial_number || + id->driver_info; id++) { + if (gb_module_match_one_id(gmod, id)) + return id; + } + + return NULL; +} + +/* + * A Greybus module represents a user-replacable component on an Ara + * phone. + * + * Create a gb_module structure to represent a discovered module. + * The position within the Endo is encoded in the "module_id" argument. + * Returns a pointer to the new module or a null pointer if a + * failure occurs due to memory exhaustion. + */ +struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) +{ + struct gb_module *module; + + module = kzalloc(sizeof(*module), GFP_KERNEL); + if (!module) + return NULL; + + module->hd = hd; /* XXX refcount? */ + module->module_id = module_id; + + spin_lock_irq(&gb_modules_lock); + list_add_tail(&module->links, &hd->modules); + spin_unlock_irq(&gb_modules_lock); + + return module; +} + +/* + * Tear down a previously set up module. + */ +void gb_module_destroy(struct gb_module *module) +{ + if (WARN_ON(!module)) + return; + + spin_lock_irq(&gb_modules_lock); + list_del(&module->links); + spin_unlock_irq(&gb_modules_lock); + + /* kref_put(module->hd); */ + + kfree(module); +} diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h new file mode 100644 index 000000000000..0d0f19f9b9fc --- /dev/null +++ b/drivers/staging/greybus/module.h @@ -0,0 +1,57 @@ +/* + * Greybus modules + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __MODULE_H +#define __MODULE_H + +/* Increase these values if needed */ +#define MAX_CPORTS_PER_MODULE 10 +#define MAX_STRINGS_PER_MODULE 10 + +struct gb_module { + struct device dev; + + struct list_head links; /* greybus_host_device->modules */ + u8 module_id; /* Physical location within the Endo */ + + struct greybus_descriptor_module module; + int num_cports; + int num_strings; + u16 cport_ids[MAX_CPORTS_PER_MODULE]; + struct gmod_string *string[MAX_STRINGS_PER_MODULE]; + + struct greybus_host_device *hd; + + struct gb_i2c_device *gb_i2c_dev; + struct gb_gpio_device *gb_gpio_dev; + struct gb_sdio_host *gb_sdio_host; + struct gb_tty *gb_tty; + struct gb_usb_device *gb_usb_dev; + struct gb_battery *gb_battery; +}; +#define to_gb_module(d) container_of(d, struct gb_module, dev) + +static inline void +gb_module_set_drvdata(struct gb_module *gmod, void *data) +{ + dev_set_drvdata(&gmod->dev, data); +} + +static inline void *gb_module_get_drvdata(struct gb_module *gmod) +{ + return dev_get_drvdata(&gmod->dev); +} + +const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, + const struct greybus_module_id *id); + +struct gb_module *gb_module_create(struct greybus_host_device *hd, + u8 module_id); +void gb_module_destroy(struct gb_module *module); + +#endif /* __MODULE_H */ diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 3cf258aedc11..f7e80abcc0a0 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -7,9 +7,9 @@ */ #include -#include #include #include + #include "greybus.h" struct gb_sdio_host { @@ -45,7 +45,7 @@ static const struct mmc_host_ops gb_sd_ops = { .get_ro = gb_sd_get_ro, }; -int gb_sdio_probe(struct greybus_module *gmod, +int gb_sdio_probe(struct gb_module *gmod, const struct greybus_module_id *id) { struct mmc_host *mmc; @@ -65,7 +65,7 @@ int gb_sdio_probe(struct greybus_module *gmod, return 0; } -void gb_sdio_disconnect(struct greybus_module *gmod) +void gb_sdio_disconnect(struct gb_module *gmod) { struct mmc_host *mmc; struct gb_sdio_host *host; diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index a2dce7a0f197..804c0a0855b3 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -17,29 +17,28 @@ #include #include "greybus.h" - #include "kernel_ver.h" /* Module fields */ -#define greybus_module_attr(field) \ +#define gb_module_attr(field) \ static ssize_t module_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct greybus_module *gmod = to_greybus_module(dev); \ + struct gb_module *gmod = to_gb_module(dev); \ return sprintf(buf, "%x\n", gmod->module.field); \ } \ static DEVICE_ATTR_RO(module_##field) -greybus_module_attr(vendor); -greybus_module_attr(product); -greybus_module_attr(version); +gb_module_attr(vendor); +gb_module_attr(product); +gb_module_attr(version); static ssize_t module_serial_number_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct greybus_module *gmod = to_greybus_module(dev); + struct gb_module *gmod = to_gb_module(dev); return sprintf(buf, "%llX\n", (unsigned long long)le64_to_cpu(gmod->module.serial_number)); @@ -50,7 +49,7 @@ static ssize_t module_vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct greybus_module *gmod = to_greybus_module(dev); + struct gb_module *gmod = to_gb_module(dev); return sprintf(buf, "%s", greybus_string(gmod, gmod->module.vendor_stringid)); @@ -61,7 +60,7 @@ static ssize_t module_product_string_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct greybus_module *gmod = to_greybus_module(dev); + struct gb_module *gmod = to_gb_module(dev); return sprintf(buf, "%s", greybus_string(gmod, gmod->module.product_stringid)); @@ -81,7 +80,7 @@ static struct attribute *module_attrs[] = { static umode_t module_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { - struct greybus_module *gmod = to_greybus_module(kobj_to_dev(kobj)); + struct gb_module *gmod = to_gb_module(kobj_to_dev(kobj)); if ((a == &dev_attr_module_vendor_string.attr) && (gmod->module.vendor_stringid)) diff --git a/drivers/staging/greybus/test_sink.c b/drivers/staging/greybus/test_sink.c index 811a237898cc..1b477695b418 100644 --- a/drivers/staging/greybus/test_sink.c +++ b/drivers/staging/greybus/test_sink.c @@ -12,10 +12,10 @@ #include "greybus.h" struct test_device { - struct greybus_module *gmod; + struct gb_module *gmod; }; -int gb_register_cport_complete(struct greybus_module *gmod, +int gb_register_cport_complete(struct gb_module *gmod, gbuf_complete_t handler, u16 cport_id, void *context); void gb_deregister_cport_complete(u16 cport_id); diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 7f9d49869281..301868cd5878 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -26,14 +26,16 @@ #include #include #include + #include "greybus.h" +#include "module.h" #define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ #define GB_NAME "ttyGB" struct gb_tty { struct tty_port port; - struct greybus_module *gmod; + struct gb_module *gmod; u16 cport_id; unsigned int minor; unsigned char clocal; @@ -384,7 +386,7 @@ static const struct tty_operations gb_ops = { }; -int gb_tty_probe(struct greybus_module *gmod, +int gb_tty_probe(struct gb_module *gmod, const struct greybus_module_id *id) { struct gb_tty *gb_tty; @@ -430,7 +432,7 @@ error: return retval; } -void gb_tty_disconnect(struct greybus_module *gmod) +void gb_tty_disconnect(struct gb_module *gmod) { struct gb_tty *gb_tty = gmod->gb_tty; struct tty_struct *tty; -- cgit v1.2.3-59-g8ed1b From 8c12cde3c2b98ec2d298b2536bcb0cb1d7591b85 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:12 -0500 Subject: greybus: define greybus interface abstraction Define new source files "interface.h" and "interface.c" to contain the definitions of the Greybus interface abstraction. A Greybus interface represents a UniPro device present in a UniPro module. For Project Ara, each interface block on a module implements a UniPro interface. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/interface.c | 58 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/interface.h | 24 +++++++++++++++ drivers/staging/greybus/module.c | 1 + drivers/staging/greybus/module.h | 1 + 6 files changed, 86 insertions(+) create mode 100644 drivers/staging/greybus/interface.c create mode 100644 drivers/staging/greybus/interface.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 9b5d8e9684a6..21f635840a1c 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -4,6 +4,7 @@ greybus-y := core.o \ debugfs.o \ ap.o \ module.o \ + interface.o \ i2c-gb.o \ gpio-gb.o \ sdio-gb.o \ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index a4fad874a70e..c82c630478f4 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -21,6 +21,7 @@ #include "greybus_id.h" #include "greybus_manifest.h" #include "module.h" +#include "interface.h" /* Matches up with the Greybus Protocol specification document */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c new file mode 100644 index 000000000000..a6e6e0edf678 --- /dev/null +++ b/drivers/staging/greybus/interface.c @@ -0,0 +1,58 @@ +/* + * Greybus interfaces + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +/* XXX This could be per-host device or per-module */ +static DEFINE_SPINLOCK(gb_interfaces_lock); + +/* + * A Greybus interface represents a UniPro device present on a + * module. For Project Ara, each active Interface Block on a module + * implements a UniPro device, and therefore a Greybus interface. A + * Greybus module has at least one interface, but can have two (or + * even more). + * + * Create a gb_interface structure to represent a discovered + * interface. Returns a pointer to the new interface or a null + * pointer if a failure occurs due to memory exhaustion. + */ +struct gb_interface * +gb_interface_create(struct gb_module *gmod, u8 interface_id) +{ + struct gb_interface *interface; + + interface = kzalloc(sizeof(*interface), GFP_KERNEL); + if (!interface) + return NULL; + + interface->gmod = gmod; /* XXX refcount? */ + interface->interface_id = interface_id; + + spin_lock_irq(&gb_interfaces_lock); + list_add_tail(&interface->links, &gmod->interfaces); + spin_unlock_irq(&gb_interfaces_lock); + + return interface; +} + +/* + * Tear down a previously set up interface. + */ +void gb_interface_destroy(struct gb_interface *interface) +{ + if (WARN_ON(!interface)) + return; + + spin_lock_irq(&gb_interfaces_lock); + list_del(&interface->links); + spin_unlock_irq(&gb_interfaces_lock); + + /* kref_put(gmod); */ + kfree(interface); +} diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h new file mode 100644 index 000000000000..63ae762ae267 --- /dev/null +++ b/drivers/staging/greybus/interface.h @@ -0,0 +1,24 @@ +/* + * Greybus interfaces + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __INTERFACE_H +#define __INTERFACE_H + +#include + +struct gb_interface { + struct gb_module *gmod; + u8 interface_id; + + struct list_head links; /* module->interfaces */ +}; + +struct gb_interface *gb_interface_create(struct gb_module *gmod, u8 module_id); +void gb_interface_destroy(struct gb_interface *interface); + +#endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 31017c2cae2f..e77791295a82 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -67,6 +67,7 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) module->hd = hd; /* XXX refcount? */ module->module_id = module_id; + INIT_LIST_HEAD(&module->interfaces); spin_lock_irq(&gb_modules_lock); list_add_tail(&module->links, &hd->modules); diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 0d0f19f9b9fc..921c001911a2 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -16,6 +16,7 @@ struct gb_module { struct device dev; + struct list_head interfaces; struct list_head links; /* greybus_host_device->modules */ u8 module_id; /* Physical location within the Endo */ -- cgit v1.2.3-59-g8ed1b From ef0d2ba20142b5db051e2673d1373e128c990743 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:13 -0500 Subject: greybus: define greybus function abstraction Define new source files "function.h" and "function.c" to contain the definitions of the Greybus function abstraction. A Greybus function represents an active entity connected to a CPort implemented by a Greybus interface. A Greybus function has a type, which defines the protocol to be used to interact with the function. A Greybus interface normally has at least two functions, but potentially many more. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/function.c | 58 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/function.h | 25 ++++++++++++++++ drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/interface.c | 1 + drivers/staging/greybus/interface.h | 1 + 6 files changed, 87 insertions(+) create mode 100644 drivers/staging/greybus/function.c create mode 100644 drivers/staging/greybus/function.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 21f635840a1c..0efb6958322d 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -5,6 +5,7 @@ greybus-y := core.o \ ap.o \ module.o \ interface.o \ + function.o \ i2c-gb.o \ gpio-gb.o \ sdio-gb.o \ diff --git a/drivers/staging/greybus/function.c b/drivers/staging/greybus/function.c new file mode 100644 index 000000000000..b06265c53025 --- /dev/null +++ b/drivers/staging/greybus/function.c @@ -0,0 +1,58 @@ +/* + * Greybus functions + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +/* XXX This could be per-host device or per-module or per-interface */ +static DEFINE_SPINLOCK(gb_functions_lock); + +/* + * A Greybus function generically defines an entity associated with + * a CPort within a module. Each function has a type (e.g. i2c, + * GPIO, etc.) that defines how it behaves and how the AP interacts + * with it. + * + * Create a gb_function structure to represent a discovered + * function. Returns a pointer to the new function or a null + * pointer if a failure occurs due to memory exhaustion. + */ +struct gb_function *gb_function_create(struct gb_interface *interface, + u16 cport_id, enum greybus_function_type type) +{ + struct gb_function *function; + + function = kzalloc(sizeof(*function), GFP_KERNEL); + if (!function) + return NULL; + + function->interface = interface; /* XXX refcount? */ + function->cport_id = cport_id; + function->type = type; + + spin_lock_irq(&gb_functions_lock); + list_add_tail(&function->links, &interface->functions); + spin_unlock_irq(&gb_functions_lock); + + return function; +} + +/* + * Tear down a previously set up function. + */ +void gb_function_destroy(struct gb_function *function) +{ + if (WARN_ON(!function)) + return; + + spin_lock_irq(&gb_functions_lock); + list_del(&function->links); + spin_unlock_irq(&gb_functions_lock); + + /* kref_put(gmod); */ + kfree(function); +} diff --git a/drivers/staging/greybus/function.h b/drivers/staging/greybus/function.h new file mode 100644 index 000000000000..379ffcdabec7 --- /dev/null +++ b/drivers/staging/greybus/function.h @@ -0,0 +1,25 @@ +/* + * Greybus functions + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __FUNCTION_H +#define __FUNCTION_H + +struct gb_function { + struct gb_interface *interface; + u16 cport_id; + enum greybus_function_type type; + + struct list_head links; /* interface->functions */ +}; + +struct gb_function *gb_function_create(struct gb_interface *interface, + u16 cport_id, + enum greybus_function_type function_type); +void gb_function_destroy(struct gb_function *function); + +#endif /* __FUNCTION_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index c82c630478f4..4eb70af9e006 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -22,6 +22,7 @@ #include "greybus_manifest.h" #include "module.h" #include "interface.h" +#include "function.h" /* Matches up with the Greybus Protocol specification document */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index a6e6e0edf678..dc7d00975eee 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -33,6 +33,7 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) interface->gmod = gmod; /* XXX refcount? */ interface->interface_id = interface_id; + INIT_LIST_HEAD(&interface->functions); spin_lock_irq(&gb_interfaces_lock); list_add_tail(&interface->links, &gmod->interfaces); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 63ae762ae267..7c3feb7e98a8 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -14,6 +14,7 @@ struct gb_interface { struct gb_module *gmod; u8 interface_id; + struct list_head functions; struct list_head links; /* module->interfaces */ }; -- cgit v1.2.3-59-g8ed1b From c68adb2f2c999886f8e18e98ad4e69aec14c9f32 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:14 -0500 Subject: greybus: introduce a connection abstraction Within a UniPro network a pair of CPorts can be linked to form a UniPro Connection. This patch creates a new abstraction to represent an AP CPort that is connected with a CPort used by a function within a Greybus module. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/connection.c | 48 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/connection.h | 29 ++++++++++++++++++++++ drivers/staging/greybus/core.c | 10 ++++++++ drivers/staging/greybus/greybus.h | 2 ++ 5 files changed, 90 insertions(+) create mode 100644 drivers/staging/greybus/connection.c create mode 100644 drivers/staging/greybus/connection.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 0efb6958322d..ed39a5c6b6fd 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -6,6 +6,7 @@ greybus-y := core.o \ module.o \ interface.o \ function.o \ + connection.o \ i2c-gb.o \ gpio-gb.o \ sdio-gb.o \ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c new file mode 100644 index 000000000000..113c98542858 --- /dev/null +++ b/drivers/staging/greybus/connection.c @@ -0,0 +1,48 @@ +/* + * Greybus connections + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +/* + * Set up a Greybus connection, representing the bidirectional link + * between a CPort on a (local) Greybus host device and a CPort on + * another Greybus module. + * + * Returns a pointer to the new connection if successful, or a null + * pointer otherwise. + */ +struct gb_connection *gb_connection_create(struct greybus_host_device *hd, + u16 cport_id, struct gb_function *function) +{ + struct gb_connection *connection; + + connection = kzalloc(sizeof(*connection), GFP_KERNEL); + if (!connection) + return NULL; + + connection->hd = hd; /* XXX refcount? */ + connection->cport_id = cport_id; + connection->function = function; /* XXX refcount? */ + + return connection; +} + +/* + * Tear down a previously set up connection. + */ +void gb_connection_destroy(struct gb_connection *connection) +{ + if (WARN_ON(!connection)) + return; + + /* XXX Need to wait for any outstanding requests to complete */ + + /* kref_put(function); */ + /* kref_put(hd); */ + kfree(connection); +} diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h new file mode 100644 index 000000000000..79b3b07f94c4 --- /dev/null +++ b/drivers/staging/greybus/connection.h @@ -0,0 +1,29 @@ +/* + * Greybus connections + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __CONNECTION_H +#define __CONNECTION_H + +#include + +#include "greybus.h" +#include "function.h" + +struct gb_connection { + struct gb_function *function; + struct greybus_host_device *hd; + u16 cport_id; /* Host side */ + + struct list_head host_links; +}; + +bool gb_connection_setup(struct greybus_host_device *hd, u16 cport_id, + struct gb_function *function); +void gb_connection_teardown(struct gb_connection *connection); + +#endif /* __CONNECTION_H */ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 934bdebe039c..eb8f8e522e5b 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -147,12 +147,22 @@ static struct device_type greybus_module_type = { .release = greybus_module_release, }; +/* XXX + * This needs to be driven by the list of functions that the + * manifest says are present. + */ static int gb_init_subdevs(struct gb_module *gmod, const struct greybus_module_id *id) { int retval; /* Allocate all of the different "sub device types" for this device */ + + /* XXX + * Decide what exactly we should get supplied for the i2c + * probe, and then work that back to what should be present + * in the manifest. + */ retval = gb_i2c_probe(gmod, id); if (retval) goto error_i2c; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4eb70af9e006..732cc5e51dc4 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -23,6 +23,7 @@ #include "module.h" #include "interface.h" #include "function.h" +#include "connection.h" /* Matches up with the Greybus Protocol specification document */ @@ -180,6 +181,7 @@ struct greybus_host_device { const struct greybus_host_driver *driver; struct list_head modules; + struct list_head connections; /* Private data for the host driver */ unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64)))); -- cgit v1.2.3-59-g8ed1b From e88afa5811c741facff3fa695d340133ac8a1be1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:15 -0500 Subject: greybus: introduce an operation abstraction This patch defines a new "operation" abstraction. An operation is a request from by one end of a connection to the function (or AP) on the other, coupled with a matching response returned to the requestor. The request indicates some action to be performed by the target of the request (such as "read some data"). Once the action has completed the target sends back an operation response message. Additional data can be supplied by the sender with its request, and/or by the target with its resposne message. Each request message has a unique id, generated by the sender. The sender recognizes the matching response by the presence of this id value. Each end of a connection is responsible for creating unique ids for the requests it sends. An operation also has a type, whose interpretation is dependent on the function type on the end of the connection opposite the sender. It is up to the creator of an operation to fill in the data (if any) to be sent with the request. Note that not all requests are initiated by the AP. Incoming data on a module function can result in a request message being sent from that function to the AP to notify of the data's arrival. Once the AP has processed this, it sends a response to the sender. Every operation response contains a status byte. If it's value is 0, the operation was successful. Any other value indicates an error. Add a defintion of U16_MAX to "kernel_ver.h". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/connection.c | 14 +++ drivers/staging/greybus/connection.h | 5 + drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/kernel_ver.h | 4 + drivers/staging/greybus/operation.c | 171 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/operation.h | 69 ++++++++++++++ 7 files changed, 265 insertions(+) create mode 100644 drivers/staging/greybus/operation.c create mode 100644 drivers/staging/greybus/operation.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index ed39a5c6b6fd..d6c4cc3c89ff 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -7,6 +7,7 @@ greybus-y := core.o \ interface.o \ function.o \ connection.o \ + operation.o \ i2c-gb.o \ gpio-gb.o \ sdio-gb.o \ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 113c98542858..fa5ab5d30ae7 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -6,6 +6,9 @@ * Released under the GPLv2 only. */ +#include + +#include "kernel_ver.h" #include "greybus.h" /* @@ -13,6 +16,9 @@ * between a CPort on a (local) Greybus host device and a CPort on * another Greybus module. * + * A connection also maintains the state of operations sent over the + * connection. + * * Returns a pointer to the new connection if successful, or a null * pointer otherwise. */ @@ -28,6 +34,8 @@ struct gb_connection *gb_connection_create(struct greybus_host_device *hd, connection->hd = hd; /* XXX refcount? */ connection->cport_id = cport_id; connection->function = function; /* XXX refcount? */ + INIT_LIST_HEAD(&connection->operations); + atomic_set(&connection->op_cycle, 0); return connection; } @@ -41,8 +49,14 @@ void gb_connection_destroy(struct gb_connection *connection) return; /* XXX Need to wait for any outstanding requests to complete */ + WARN_ON(!list_empty(&connection->operations)); /* kref_put(function); */ /* kref_put(hd); */ kfree(connection); } + +u16 gb_connection_op_id(struct gb_connection *connection) +{ + return (u16)(atomic_inc_return(&connection->op_cycle) % U16_MAX); +} diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 79b3b07f94c4..c653c95d2834 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -20,10 +20,15 @@ struct gb_connection { u16 cport_id; /* Host side */ struct list_head host_links; + + struct list_head operations; + atomic_t op_cycle; }; bool gb_connection_setup(struct greybus_host_device *hd, u16 cport_id, struct gb_function *function); void gb_connection_teardown(struct gb_connection *connection); +u16 gb_connection_op_id(struct gb_connection *connection); + #endif /* __CONNECTION_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 732cc5e51dc4..9a66fd1e60b0 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -24,6 +24,7 @@ #include "interface.h" #include "function.h" #include "connection.h" +#include "operation.h" /* Matches up with the Greybus Protocol specification document */ diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 4aa5b83bff60..c9ea7a94f4e6 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -22,4 +22,8 @@ #define U8_MAX ((u8)~0U) #endif /* ! U8_MAX */ +#ifndef U16_MAX +#define U16_MAX ((u16)(~0U)) +#endif /* !U16_MAX */ + #endif /* __GREYBUS_KERNEL_VER_H */ diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c new file mode 100644 index 000000000000..5cb23aa3867a --- /dev/null +++ b/drivers/staging/greybus/operation.c @@ -0,0 +1,171 @@ +/* + * Greybus operations + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include + +#include "greybus.h" + +/* + * All operation messages (both requests and responses) begin with + * a common header that encodes the size of the data (header + * included). This header also contains a unique identifier, which + * is used to keep track of in-flight operations. Finally, the + * header contains a operation type field, whose interpretation is + * dependent on what type of device lies on the other end of the + * connection. Response messages are distinguished from request + * messages by setting the high bit (0x80) in the operation type + * value. + * + * The wire format for all numeric fields in the header is little + * endian. Any operation-specific data begins immediately after the + * header, and is 64-bit aligned. + */ +struct gb_operation_msg_hdr { + __le16 size; /* Size in bytes of header + payload */ + __le16 id; /* Operation unique id */ + __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ + /* 3 bytes pad, must be zero (ignore when read) */ +} __aligned(sizeof(u64)); + +/* XXX Could be per-host device, per-module, or even per-connection */ +static DEFINE_SPINLOCK(gb_operations_lock); + +/* + * An operations's response message has arrived. If no callback was + * supplied it was submitted for asynchronous completion, so we notify + * any waiters. Otherwise we assume calling the completion is enough + * and nobody else will be waiting. + */ +void gb_operation_complete(struct gb_operation *operation) +{ + if (operation->callback) + operation->callback(operation); + else + complete_all(&operation->completion); +} + +/* + * Wait for a submitted operatnoi to complete */ +int gb_operation_wait(struct gb_operation *operation) +{ + int ret; + + ret = wait_for_completion_interruptible(&operation->completion); + /* If interrupted, cancel the in-flight buffer */ + if (ret < 0) + ret = greybus_kill_gbuf(operation->gbuf); + return ret; + +} + +/* + * Submit an outbound operation. The caller has filled in any + * payload so the request message is ready to go. If non-null, + * the callback function supplied will be called when the response + * message has arrived indicating the operation is complete. A null + * callback function is used for a synchronous request; return from + * this function won't occur until the operation is complete (or an + * interrupt occurs). + */ +int gb_operation_submit(struct gb_operation *operation, + gb_operation_callback callback) +{ + int ret; + + /* XXX + * gfp is probably GFP_ATOMIC but really I think + * the gfp mask should go away. + */ + operation->callback = callback; + ret = greybus_submit_gbuf(operation->gbuf, GFP_KERNEL); + if (ret) + return ret; + if (!callback) + ret = gb_operation_wait(operation); + + return ret; +} + +/* + * Called when a greybus request message has actually been sent. + */ +static void gbuf_out_callback(struct gbuf *gbuf) +{ + /* Record it's been submitted; need response now */ +} + +/* + * Create a Greybus operation having a buffer big enough for an + * outgoing payload of the given size to be sent over the given + * connection. + * + * Returns a pointer to the new operation or a null pointer if a + * failure occurs due to memory exhaustion. + */ +struct gb_operation *gb_operation_create(struct gb_connection *connection, + size_t size) +{ + struct gb_operation *operation; + struct gb_operation_msg_hdr *header; + struct gbuf *gbuf; + + /* XXX Use a slab cache */ + operation = kzalloc(sizeof(*operation), GFP_KERNEL); + if (!operation) + return NULL; + + /* Our buffer holds a header in addition to the requested payload */ + size += sizeof(*header); + gbuf = greybus_alloc_gbuf(connection->function->interface->gmod, + connection->cport_id, + gbuf_out_callback, size, + GFP_KERNEL, operation); + if (gbuf) { + kfree(operation); + return NULL; + } + + operation->connection = connection; /* XXX refcount? */ + + /* Fill in the header structure and payload pointer */ + operation->gbuf = gbuf; + header = (struct gb_operation_msg_hdr *)&gbuf->transfer_buffer; + header->id = 0; + header->size = size; + operation->payload = (char *)header + sizeof(*header); + + operation->callback = NULL; /* set at submit time */ + init_completion(&operation->completion); + + spin_lock_irq(&gb_operations_lock); + list_add_tail(&operation->links, &connection->operations); + spin_unlock_irq(&gb_operations_lock); + + return operation; +} + +/* + * Destroy a previously created operation. + */ +void gb_operation_destroy(struct gb_operation *operation) +{ + if (WARN_ON(!operation)) + return; + + /* XXX Make sure it's not in flight */ + spin_lock_irq(&gb_operations_lock); + list_del(&operation->links); + spin_unlock_irq(&gb_operations_lock); + + greybus_free_gbuf(operation->gbuf); + + kfree(operation); +} diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h new file mode 100644 index 000000000000..96a7a0fcba56 --- /dev/null +++ b/drivers/staging/greybus/operation.h @@ -0,0 +1,69 @@ +/* + * Greybus operations + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __OPERATION_H +#define __OPERATION_H + +#include + +enum gb_operation_status { + GB_OP_SUCCESS = 0, + GB_OP_INVALID = 1, + GB_OP_NO_MEMORY = 2, + GB_OP_INTERRUPTED = 3, +}; + +/* + * A Greybus operation is a remote procedure call performed over a + * connection between the AP and a function on Greybus module. + * Every operation consists of a request message sent to the other + * end of the connection coupled with a reply returned to the + * sender. + * + * The state for managing active requests on a connection is held in + * the connection structure. + * + * YADA YADA + * + * submitting each request and providing its matching response to + * the caller when it arrives. Operations normally complete + * asynchronously, and when an operation's response arrives its + * callback function is executed. The callback pointer is supplied + * at the time the operation is submitted; a null callback pointer + * causes synchronous operation--the caller is blocked until + * the response arrives. In addition, it is possible to await + * the completion of a submitted asynchronous operation. + * + * A Greybus device operation includes a Greybus buffer to hold the + * data sent to the device. The only field within a Greybus + * operation that should be used by a caller is the payload pointer, + * which should be used to populate the request data. This pointer + * is guaranteed to be 64-bit aligned. + * XXX and callback? + */ +struct gb_operation; +typedef void (*gb_operation_callback)(struct gb_operation *); +struct gb_operation { + struct gb_connection *connection; + struct gbuf *gbuf; + void *payload; /* sender data */ + gb_operation_callback callback; /* If asynchronous */ + struct completion completion; /* Used if no callback */ + u8 result; + + struct list_head links; /* connection->operations */ +}; + +struct gb_operation *gb_operation_create(struct gb_connection *connection, + size_t size); +void gb_operation_destroy(struct gb_operation *operation); + +int gb_operation_wait(struct gb_operation *operation); +void gb_operation_complete(struct gb_operation *operation); + +#endif /* !__OPERATION_H */ -- cgit v1.2.3-59-g8ed1b From b09c94a1b71c82385ffa5f3c913d7d6e14eaea3d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:16 -0500 Subject: greybus: start improving manifest parsing Currently the module manifest parsing code is sort of representative only and is not really very useful. This patch begins doing "real" parsing of the module manifest. It scans the module manifest to identify the descriptors it holds. It then verifies there's only one module descriptor found, and initializes new some fields in the gb_module structure based on what it contains (converting what's found to native byte order). Note that if anything unexpected is found or other errors occur when parsing the manifest, the parse fails. Because we now save this converted information when it's parsed we no longer have a greybus_descriptor_module struct within a struct gb_module. And because we've already converted these values, we can do a little less work displaying values in sysfs. (We also now show vendor, product, and version values in the right byte order.) This eliminates the need for greybus_string(), so get rid of it. It also slightly simplifies the greybus module matching code. Move some existing parsing code into a new file, "manifest.c". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/core.c | 64 +------- drivers/staging/greybus/greybus.h | 3 +- drivers/staging/greybus/manifest.c | 313 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/manifest.h | 14 ++ drivers/staging/greybus/module.c | 13 +- drivers/staging/greybus/module.h | 9 +- drivers/staging/greybus/sysfs.c | 38 ++--- 8 files changed, 365 insertions(+), 90 deletions(-) create mode 100644 drivers/staging/greybus/manifest.c create mode 100644 drivers/staging/greybus/manifest.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index d6c4cc3c89ff..a303f81a542d 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -3,6 +3,7 @@ greybus-y := core.o \ sysfs.o \ debugfs.o \ ap.o \ + manifest.o \ module.o \ interface.o \ function.o \ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index eb8f8e522e5b..8e4493ccf793 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -126,22 +126,6 @@ static void greybus_module_release(struct device *dev) } -const u8 *greybus_string(struct gb_module *gmod, int id) -{ - int i; - struct gmod_string *string; - - if (!gmod) - return NULL; - - for (i = 0; i < gmod->num_strings; ++i) { - string = gmod->string[i]; - if (string->id == id) - return &string->string[0]; - } - return NULL; -} - static struct device_type greybus_module_type = { .name = "greybus_module", .release = greybus_module_release, @@ -204,19 +188,6 @@ static const struct greybus_module_id fake_greybus_module_id = { GREYBUS_DEVICE(0x42, 0x42) }; -static int create_module(struct gb_module *gmod, - struct greybus_descriptor_module *module, - size_t desc_size) -{ - if (desc_size != sizeof(*module)) { - dev_err(gmod->dev.parent, "invalid module header size %zu\n", - desc_size); - return -EINVAL; - } - memcpy(&gmod->module, module, desc_size); - return 0; -} - static int create_string(struct gb_module *gmod, struct greybus_descriptor_string *string, size_t desc_size) @@ -284,15 +255,16 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, struct gb_module *gmod; struct greybus_manifest *manifest; int retval; - int overall_size; - - /* we have to have at _least_ the manifest header */ - if (size <= sizeof(manifest->header)) - return; - gmod = gb_module_create(hd, module_id); - if (!gmod) + /* + * Parse the manifest and build up our data structures + * representing what's in it. + */ + gmod = gb_manifest_parse(data, size); + if (!gmod) { + dev_err(hd->parent, "manifest error\n"); return; + } gmod->dev.parent = hd->parent; gmod->dev.driver = NULL; @@ -303,24 +275,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, device_initialize(&gmod->dev); dev_set_name(&gmod->dev, "%d", module_id); - manifest = (struct greybus_manifest *)data; - overall_size = le16_to_cpu(manifest->header.size); - if (overall_size != size) { - dev_err(hd->parent, "size != manifest header size, %d != %d\n", - size, overall_size); - goto error; - } - - /* Validate major/minor number */ - if (manifest->header.version_major > GREYBUS_VERSION_MAJOR) { - dev_err(hd->parent, - "Manifest version too new (%hhu.%hhu > %hhu.%hhu)\n", - manifest->header.version_major, - manifest->header.version_minor, - GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); - goto error; - } - size -= sizeof(manifest->header); data += sizeof(manifest->header); while (size > 0) { @@ -347,8 +301,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, break; case GREYBUS_TYPE_MODULE: - retval = create_module(gmod, &desc->module, - data_size); break; case GREYBUS_TYPE_STRING: diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 9a66fd1e60b0..fabd74e41059 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -20,6 +20,7 @@ #include "greybus_id.h" #include "greybus_manifest.h" +#include "manifest.h" #include "module.h" #include "interface.h" #include "function.h" @@ -246,8 +247,6 @@ int greybus_disabled(void); void greybus_remove_device(struct gb_module *gmod); -const u8 *greybus_string(struct gb_module *gmod, int id); - /* Internal functions to gb module, move to internal .h file eventually. */ void gb_add_module(struct greybus_host_device *hd, u8 module_id, diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c new file mode 100644 index 000000000000..cdb7c24ed0af --- /dev/null +++ b/drivers/staging/greybus/manifest.c @@ -0,0 +1,313 @@ +/* + * Greybus module manifest parsing + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +#include "greybus.h" + +/* + * We scan the manifest once to identify where all the descriptors + * are. The result is a list of these manifest_desc structures. We + * then pick through them for what we're looking for (starting with + * the module descriptor). As each is processed we remove it from + * the list. When we're done the list should (probably) be empty. + */ +struct manifest_desc { + struct list_head links; + + size_t size; + void *data; + enum greybus_descriptor_type type; +}; + +static LIST_HEAD(manifest_descs); + +static void release_manifest_descriptor(struct manifest_desc *descriptor) +{ + list_del(&descriptor->links); + kfree(descriptor); +} + +static void release_manifest_descriptors(void) +{ + struct manifest_desc *descriptor; + struct manifest_desc *next; + + list_for_each_entry_safe(descriptor, next, &manifest_descs, links) + release_manifest_descriptor(descriptor); +} + +/* + * Validate the given descriptor. Its reported size must fit within + * the number of bytes reamining, and it must have a recognized + * type. Check that the reported size is at least as big as what + * we expect to see. (It could be bigger, perhaps for a new version + * of the format.) + * + * Returns the number of bytes consumed by the descriptor, or a + * negative errno. + */ +static int identify_descriptor(struct greybus_descriptor *desc, size_t size) +{ + struct greybus_descriptor_header *desc_header = &desc->header; + struct manifest_desc *descriptor; + int desc_size; + size_t expected_size; + + if (size < sizeof(*desc_header)) { + pr_err("manifest too small\n"); + return -EINVAL; /* Must at least have header */ + } + + desc_size = (int)le16_to_cpu(desc_header->size); + if ((size_t)desc_size > size) { + pr_err("descriptor too big\n"); + return -EINVAL; + } + + switch (desc_header->type) { + case GREYBUS_TYPE_MODULE: + if (desc_size < sizeof(struct greybus_descriptor_module)) { + pr_err("module descriptor too small (%u)\n", + desc_size); + return -EINVAL; + } + break; + case GREYBUS_TYPE_DEVICE: + break; + case GREYBUS_TYPE_CLASS: + pr_err("class descriptor found (ignoring)\n"); + break; + case GREYBUS_TYPE_STRING: + expected_size = sizeof(struct greybus_descriptor_header); + expected_size += sizeof(struct greybus_descriptor_string); + expected_size += (size_t)desc->string.length; + if (desc_size < expected_size) { + pr_err("string descriptor too small (%u)\n", + desc_size); + return -EINVAL; + } + break; + case GREYBUS_TYPE_CPORT: + if (desc_size < sizeof(struct greybus_descriptor_cport)) { + pr_err("cport descriptor too small (%u)\n", + desc_size); + return -EINVAL; + } + break; + case GREYBUS_TYPE_INVALID: + default: + pr_err("invalid descriptor type (%hhu)\n", desc_header->type); + return -EINVAL; + } + + descriptor = kzalloc(sizeof(*descriptor), GFP_KERNEL); + if (!descriptor) + return -ENOMEM; + + descriptor->size = desc_size; + descriptor->data = desc; + descriptor->type = desc_header->type; + list_add_tail(&descriptor->links, &manifest_descs); + + return desc_size; +} + +/* + * Find the string descriptor having the given id, validate it, and + * allocate a duplicate copy of it. The duplicate has an extra byte + * which guarantees the returned string is NUL-terminated. + * + * String index 0 is valid (it represents "no string"), and for + * that a null pointer is returned. + * + * Otherwise returns a pointer to a newly-allocated copy of the + * descriptor string, or an error-coded pointer on failure. + */ +static char *gb_string_get(u8 string_id) +{ + struct greybus_descriptor_string *desc_string; + struct manifest_desc *descriptor; + bool found = false; + char *string; + + /* A zero string id means no string (but no error) */ + if (!string_id) + return NULL; + + list_for_each_entry(descriptor, &manifest_descs, links) { + struct greybus_descriptor *desc; + + if (descriptor->type != GREYBUS_TYPE_STRING) + continue; + + desc = descriptor->data; + desc_string = &desc->string; + if (desc_string->id == string_id) { + found = true; + break; + } + } + if (!found) + return ERR_PTR(-ENOENT); + + /* Allocate an extra byte so we can guarantee it's NUL-terminated */ + string = kmemdup(&desc_string->string, (size_t)desc_string->length + 1, + GFP_KERNEL); + if (!string) + return ERR_PTR(-ENOMEM); + string[desc_string->length] = '\0'; + + /* Ok we've used this string, so we're done with it */ + release_manifest_descriptor(descriptor); + + return string; +} + +struct gb_module *gb_manifest_parse_module(struct manifest_desc *module_desc) +{ + struct greybus_descriptor *desc = module_desc->data; + struct greybus_descriptor_module *desc_module = &desc->module; + struct gb_module *gmod; + + gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); + if (!gmod) + return NULL; + + /* Handle the strings first--they can fail */ + gmod->vendor_string = gb_string_get(desc_module->vendor_stringid); + if (IS_ERR(gmod->vendor_string)) { + kfree(gmod); + return NULL; + } + gmod->product_string = gb_string_get(desc_module->product_stringid); + if (IS_ERR(gmod->product_string)) { + kfree(gmod->vendor_string); + kfree(gmod); + return NULL; + } + + gmod->vendor = le16_to_cpu(desc_module->vendor); + gmod->product = le16_to_cpu(desc_module->product); + gmod->version = le16_to_cpu(desc_module->version); + gmod->serial_number = le64_to_cpu(desc_module->serial_number); + + /* Release the module descriptor, now that we're done with it */ + release_manifest_descriptor(module_desc); + + return gmod; +} + +/* + * Parse a buffer containing a module manifest. + * + * If we find anything wrong with the content/format of the buffer + * we reject it. + * + * The first requirement is that the manifest's version is + * one we can parse. + * + * We make an initial pass through the buffer and identify all of + * the descriptors it contains, keeping track for each its type + * and the location size of its data in the buffer. + * + * Next we scan the descriptors, looking for a module descriptor; + * there must be exactly one of those. When found, we record the + * information it contains, and then remove that descriptor (and any + * string descriptors it refers to) from further consideration. + * + * After that we look for the module's interfaces--there must be at + * least one of those. + * + * Return a pointer to an initialized gb_module structure + * representing the content of the module manifest, or a null + * pointer if an error occurs. + */ +struct gb_module *gb_manifest_parse(void *data, size_t size) +{ + struct greybus_manifest *manifest; + struct greybus_manifest_header *header; + struct greybus_descriptor *desc; + struct manifest_desc *descriptor; + struct manifest_desc *module_desc = NULL; + struct gb_module *gmod; + u16 manifest_size; + u32 found = 0; + + /* we have to have at _least_ the manifest header */ + if (size <= sizeof(manifest->header)) { + pr_err("short manifest (%zu)\n", size); + return NULL; + } + + /* Make sure the size is right */ + manifest = data; + header = &manifest->header; + manifest_size = le16_to_cpu(header->size); + if (manifest_size != size) { + pr_err("manifest size mismatch %zu != %hu\n", + size, manifest_size); + return NULL; + } + + /* Validate major/minor number */ + if (header->version_major > GREYBUS_VERSION_MAJOR) { + pr_err("manifest version too new (%hhu.%hhu > %hhu.%hhu)\n", + header->version_major, header->version_minor, + GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); + return NULL; + } + + /* OK, find all the descriptors */ + desc = (struct greybus_descriptor *)(header + 1); + size -= sizeof(*header); + while (size) { + int desc_size; + + desc_size = identify_descriptor(desc, size); + if (desc_size <= 0) { + if (!desc_size) + pr_err("zero-sized manifest descriptor\n"); + goto out_err; + } + desc = (struct greybus_descriptor *)((char *)desc + desc_size); + size -= desc_size; + } + + /* There must be a single module descriptor */ + list_for_each_entry(descriptor, &manifest_descs, links) { + if (descriptor->type == GREYBUS_TYPE_MODULE) + if (!found++) + module_desc = descriptor; + } + if (found != 1) { + pr_err("manifest must have 1 module descriptor (%u found)\n", + found); + goto out_err; + } + + /* Parse the module manifest, starting with the module descriptor */ + gmod = gb_manifest_parse_module(module_desc); + + /* + * We really should have no remaining descriptors, but we + * don't know what newer format manifests might leave. + */ + if (!list_empty(&manifest_descs)) { + pr_info("excess descriptors in module manifest\n"); + release_manifest_descriptors(); + } + + return gmod; +out_err: + release_manifest_descriptors(); + + return NULL; +} diff --git a/drivers/staging/greybus/manifest.h b/drivers/staging/greybus/manifest.h new file mode 100644 index 000000000000..29cbf92bc2cf --- /dev/null +++ b/drivers/staging/greybus/manifest.h @@ -0,0 +1,14 @@ +/* + * Greybus module manifest parsing + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __MANIFEST_H +#define __MANIFEST_H + +struct gb_module *gb_manifest_parse(void *data, size_t size); + +#endif /* __MANIFEST_H */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index e77791295a82..6618d212d6e1 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -14,20 +14,16 @@ static DEFINE_SPINLOCK(gb_modules_lock); static int gb_module_match_one_id(struct gb_module *gmod, const struct greybus_module_id *id) { - struct greybus_descriptor_module *module; - - module = &gmod->module; - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && - (id->vendor != le16_to_cpu(module->vendor))) + (id->vendor != gmod->vendor)) return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && - (id->product != le16_to_cpu(module->product))) + (id->product != gmod->product)) return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && - (id->serial_number != le64_to_cpu(module->serial_number))) + (id->serial_number != gmod->serial_number)) return 0; return 1; @@ -84,6 +80,9 @@ void gb_module_destroy(struct gb_module *module) if (WARN_ON(!module)) return; + kfree(module->product_string); + kfree(module->vendor_string); + spin_lock_irq(&gb_modules_lock); list_del(&module->links); spin_unlock_irq(&gb_modules_lock); diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 921c001911a2..326176a57adf 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -20,7 +20,14 @@ struct gb_module { struct list_head links; /* greybus_host_device->modules */ u8 module_id; /* Physical location within the Endo */ - struct greybus_descriptor_module module; + /* Information taken from the manifest module descriptor */ + u16 vendor; + u16 product; + u16 version; + u64 serial_number; + char *vendor_string; + char *product_string; + int num_cports; int num_strings; u16 cport_ids[MAX_CPORTS_PER_MODULE]; diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 804c0a0855b3..f969d90d2502 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -26,7 +26,7 @@ static ssize_t module_##field##_show(struct device *dev, \ char *buf) \ { \ struct gb_module *gmod = to_gb_module(dev); \ - return sprintf(buf, "%x\n", gmod->module.field); \ + return sprintf(buf, "%x\n", gmod->field); \ } \ static DEVICE_ATTR_RO(module_##field) @@ -40,8 +40,7 @@ static ssize_t module_serial_number_show(struct device *dev, { struct gb_module *gmod = to_gb_module(dev); - return sprintf(buf, "%llX\n", - (unsigned long long)le64_to_cpu(gmod->module.serial_number)); + return sprintf(buf, "%llX\n", (unsigned long long)gmod->serial_number); } static DEVICE_ATTR_RO(module_serial_number); @@ -51,8 +50,7 @@ static ssize_t module_vendor_string_show(struct device *dev, { struct gb_module *gmod = to_gb_module(dev); - return sprintf(buf, "%s", - greybus_string(gmod, gmod->module.vendor_stringid)); + return sprintf(buf, "%s", gmod->vendor_string); } static DEVICE_ATTR_RO(module_vendor_string); @@ -62,8 +60,7 @@ static ssize_t module_product_string_show(struct device *dev, { struct gb_module *gmod = to_gb_module(dev); - return sprintf(buf, "%s", - greybus_string(gmod, gmod->module.product_stringid)); + return sprintf(buf, "%s", gmod->product_string); } static DEVICE_ATTR_RO(module_product_string); @@ -81,21 +78,17 @@ static umode_t module_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { struct gb_module *gmod = to_gb_module(kobj_to_dev(kobj)); + umode_t mode = a->mode; + + if (a == &dev_attr_module_vendor_string.attr && gmod->vendor_string) + return mode; + if (a == &dev_attr_module_product_string.attr && gmod->product_string) + return mode; + if (gmod->vendor || gmod->product || gmod->version) + return mode; + if (gmod->serial_number) + return mode; - if ((a == &dev_attr_module_vendor_string.attr) && - (gmod->module.vendor_stringid)) - return a->mode; - if ((a == &dev_attr_module_product_string.attr) && - (gmod->module.product_stringid)) - return a->mode; - - // FIXME - make this a dynamic structure to "know" if it really is here - // or not easier? - if (gmod->module.vendor || - gmod->module.product || - gmod->module.version || - gmod->module.serial_number) - return a->mode; return 0; } @@ -104,9 +97,6 @@ static struct attribute_group module_attr_grp = { .is_visible = module_attrs_are_visible, }; - - - const struct attribute_group *greybus_module_groups[] = { &module_attr_grp, NULL, -- cgit v1.2.3-59-g8ed1b From d88bfb5b7df54f81869b28a25511e8ab957c4ced Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:17 -0500 Subject: greybus: manifest interface descriptor parsing Add support for parsing one or more interface descriptors in a module manifest. There must be at least one, but we impose no limit on the number of interfaces associated with a module. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index cdb7c24ed0af..2c3cf7486c15 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -171,6 +171,45 @@ static char *gb_string_get(u8 string_id) return string; } +/* + * Find interface descriptors in the manifest and set up their data + * structures. Returns the number of interfaces set up for the + * given module. + */ +static u32 gb_manifest_parse_interfaces(struct gb_module *gmod) +{ + u32 count = 0; + + while (true) { + struct manifest_desc *descriptor; + struct greybus_descriptor_interface *desc_interface; + struct gb_interface *interface; + bool found = false; + + /* Find an interface descriptor */ + list_for_each_entry(descriptor, &manifest_descs, links) { + if (descriptor->type == GREYBUS_TYPE_DEVICE) { + found = true; + break; + } + } + if (!found) + break; + + /* Found one. Set up its interface structure*/ + desc_interface = descriptor->data; + interface = gb_interface_create(gmod, desc_interface->id); + if (!interface) + return 0; /* Error */ + count++; + + /* Done with this interface descriptor */ + release_manifest_descriptor(descriptor); + } + + return count; +} + struct gb_module *gb_manifest_parse_module(struct manifest_desc *module_desc) { struct greybus_descriptor *desc = module_desc->data; @@ -202,6 +241,13 @@ struct gb_module *gb_manifest_parse_module(struct manifest_desc *module_desc) /* Release the module descriptor, now that we're done with it */ release_manifest_descriptor(module_desc); + /* A module must have at least one interface descriptor */ + if (!gb_manifest_parse_interfaces(gmod)) { + pr_err("manifest interface descriptors not valid\n"); + gb_module_destroy(gmod); + return NULL; + } + return gmod; } -- cgit v1.2.3-59-g8ed1b From c095bbcfcb1728aea3e9730ea6997373e3744582 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:18 -0500 Subject: greybus: manifest cport descriptor parsing Add support for parsing one or more cports descriptors in a module manifest. There must be at least one for each interface, but we impose no limit on the number of interfaces associated with a module. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 2c3cf7486c15..bae260f0bc81 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -171,6 +171,49 @@ static char *gb_string_get(u8 string_id) return string; } +/* + * Find cport descriptors in the manifest and set up data structures + * for the functions that use them. Returns the number of interfaces + * set up for the given module, or 0 if there is an error. + */ +u32 gb_manifest_parse_cports(struct gb_interface *interface) +{ + u32 count = 0; + + while (true) { + struct manifest_desc *descriptor; + struct greybus_descriptor_cport *desc_cport; + enum greybus_function_type function_type; + u16 cport_id; + bool found; + + /* Find a cport descriptor */ + found = false; + list_for_each_entry(descriptor, &manifest_descs, links) { + if (descriptor->type == GREYBUS_TYPE_CPORT) { + found = true; + break; + } + } + if (!found) + break; + + /* Found one. Set up its function structure */ + desc_cport = descriptor->data; + function_type = + (enum greybus_function_type)desc_cport->function_type; + cport_id = le16_to_cpu(desc_cport->id); + if (!gb_function_create(interface, cport_id, function_type)) + return 0; /* Error */ + + count++; + /* Release the cport descriptor */ + release_manifest_descriptor(descriptor); + } + + return count; +} + /* * Find interface descriptors in the manifest and set up their data * structures. Returns the number of interfaces set up for the @@ -201,6 +244,11 @@ static u32 gb_manifest_parse_interfaces(struct gb_module *gmod) interface = gb_interface_create(gmod, desc_interface->id); if (!interface) return 0; /* Error */ + + /* Now go set up this interface's functions and cports */ + if (!gb_manifest_parse_cports(interface)) + return 0; /* Error parsing cports */ + count++; /* Done with this interface descriptor */ -- cgit v1.2.3-59-g8ed1b From 459164b1f4d8075e6eaf0fd8eaa54e84c9e3fb3a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 1 Oct 2014 21:54:19 -0500 Subject: greybus: kill off old manifest code Now that the new manifest code is in place, delete the old stuff from "core.c". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 112 ++++------------------------------------- 1 file changed, 9 insertions(+), 103 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 8e4493ccf793..d9bbc6737d7c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -188,60 +188,6 @@ static const struct greybus_module_id fake_greybus_module_id = { GREYBUS_DEVICE(0x42, 0x42) }; -static int create_string(struct gb_module *gmod, - struct greybus_descriptor_string *string, - size_t desc_size) -{ - int string_size; - struct gmod_string *gmod_string; - - if (gmod->num_strings == MAX_STRINGS_PER_MODULE) { - dev_err(gmod->dev.parent, - "too many strings for this module!\n"); - return -EINVAL; - } - - if (desc_size < sizeof(*string)) { - dev_err(gmod->dev.parent, "invalid string header size %zu\n", - desc_size); - return -EINVAL; - } - - string_size = string->length; - gmod_string = kzalloc(sizeof(*gmod_string) + string_size + 1, GFP_KERNEL); - if (!gmod_string) - return -ENOMEM; - - gmod_string->length = string_size; - gmod_string->id = string->id; - memcpy(&gmod_string->string, &string->string, string_size); - - gmod->string[gmod->num_strings] = gmod_string; - gmod->num_strings++; - - return 0; -} - -static int create_cport(struct gb_module *gmod, - struct greybus_descriptor_cport *cport, - size_t desc_size) -{ - if (gmod->num_cports == MAX_CPORTS_PER_MODULE) { - dev_err(gmod->dev.parent, "too many cports for this module!\n"); - return -EINVAL; - } - - if (desc_size != sizeof(*cport)) { - dev_err(gmod->dev.parent, - "invalid cport descriptor size %zu\n", desc_size); - return -EINVAL; - } - - gmod->cport_ids[gmod->num_cports] = le16_to_cpu(cport->id); - gmod->num_cports++; - - return 0; -} /** * gb_add_module @@ -253,7 +199,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size) { struct gb_module *gmod; - struct greybus_manifest *manifest; int retval; /* @@ -266,6 +211,15 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, return; } + /* + * XXX + * We've successfully parsed the manifest. Now we need to + * allocate CPort Id's for connecting to the CPorts found on + * other modules. For each of these, establish a connection + * between the local and remote CPorts (including + * configuring the switch to allow them to communicate). + */ + gmod->dev.parent = hd->parent; gmod->dev.driver = NULL; gmod->dev.bus = &greybus_bus_type; @@ -275,54 +229,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, device_initialize(&gmod->dev); dev_set_name(&gmod->dev, "%d", module_id); - size -= sizeof(manifest->header); - data += sizeof(manifest->header); - while (size > 0) { - struct greybus_descriptor *desc; - u16 desc_size; - size_t data_size; - - if (size < sizeof(desc->header)) { - dev_err(hd->parent, "remaining size %d too small\n", - size); - goto error; - } - desc = (struct greybus_descriptor *)data; - desc_size = le16_to_cpu(desc->header.size); - if (size < desc_size) { - dev_err(hd->parent, "descriptor size %d too big\n", - desc_size); - goto error; - } - data_size = (size_t)desc_size - sizeof(desc->header); - - switch (le16_to_cpu(desc->header.type)) { - case GREYBUS_TYPE_DEVICE: - break; - - case GREYBUS_TYPE_MODULE: - break; - - case GREYBUS_TYPE_STRING: - retval = create_string(gmod, &desc->string, data_size); - break; - - case GREYBUS_TYPE_CPORT: - retval = create_cport(gmod, &desc->cport, data_size); - break; - - case GREYBUS_TYPE_INVALID: - default: - dev_err(hd->parent, "invalid descriptor type %d\n", - desc->header.type); - goto error; - } - if (retval) - goto error; - size -= desc_size; - data += desc_size; - } - retval = gb_init_subdevs(gmod, &fake_greybus_module_id); if (retval) goto error; -- cgit v1.2.3-59-g8ed1b From b05890db5e75aa0627f3f5d2241cc90fc399a697 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 2 Oct 2014 12:30:01 -0500 Subject: greybus: fix connection header declarations Changes to the create/destroy connection functions were not properly reflected in the header file. Fix that. There's also no need to include anything other than "greybus.h". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index c653c95d2834..6094fb44b990 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -12,7 +12,6 @@ #include #include "greybus.h" -#include "function.h" struct gb_connection { struct gb_function *function; @@ -25,9 +24,9 @@ struct gb_connection { atomic_t op_cycle; }; -bool gb_connection_setup(struct greybus_host_device *hd, u16 cport_id, - struct gb_function *function); -void gb_connection_teardown(struct gb_connection *connection); +struct gb_connection *gb_connection_create(struct greybus_host_device *hd, + u16 cport_id, struct gb_function *function); +void gb_connection_destroy(struct gb_connection *connection); u16 gb_connection_op_id(struct gb_connection *connection); -- cgit v1.2.3-59-g8ed1b From 63cc932b02bc5e697b5ba8f04a5d846b61f38879 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 2 Oct 2014 12:30:02 -0500 Subject: greybus: October 1 updates Update the definitions in "greybus_manifest.h" to reflect the changes to the Greybus specification made on October 1. They are: - renaming "device" to be "interface" - renumbering greybus descriptor type - eliminating the notion of a "function" - defining a CPort's protocol in the CPort descriptor - having a "class" take on the types previously used for "function" - renaming "serial number" to be "unique id" (for now) - relying on an interface's maximum cport id to determine how much device+cport address space the interface consumes - adding a simple class descriptor - renaming gb_interface->interface_id to be gb_interface->id This also reorders some things to match ordering in the document, and adds some commentary for the various structures. Since greybus_function_type is gone, we eliminate the "type" field from a function structure. (Functions are going away, next.) Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/function.c | 3 +- drivers/staging/greybus/function.h | 4 +- drivers/staging/greybus/greybus_id.h | 2 +- drivers/staging/greybus/greybus_manifest.h | 83 +++++++++++++++++++----------- drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/interface.h | 2 +- drivers/staging/greybus/manifest.c | 29 ++++++----- drivers/staging/greybus/module.c | 4 +- drivers/staging/greybus/module.h | 2 +- drivers/staging/greybus/sysfs.c | 4 +- 10 files changed, 79 insertions(+), 56 deletions(-) diff --git a/drivers/staging/greybus/function.c b/drivers/staging/greybus/function.c index b06265c53025..bfc9ac7e4c81 100644 --- a/drivers/staging/greybus/function.c +++ b/drivers/staging/greybus/function.c @@ -22,7 +22,7 @@ static DEFINE_SPINLOCK(gb_functions_lock); * pointer if a failure occurs due to memory exhaustion. */ struct gb_function *gb_function_create(struct gb_interface *interface, - u16 cport_id, enum greybus_function_type type) + u16 cport_id) { struct gb_function *function; @@ -32,7 +32,6 @@ struct gb_function *gb_function_create(struct gb_interface *interface, function->interface = interface; /* XXX refcount? */ function->cport_id = cport_id; - function->type = type; spin_lock_irq(&gb_functions_lock); list_add_tail(&function->links, &interface->functions); diff --git a/drivers/staging/greybus/function.h b/drivers/staging/greybus/function.h index 379ffcdabec7..e1b30c515a2a 100644 --- a/drivers/staging/greybus/function.h +++ b/drivers/staging/greybus/function.h @@ -12,14 +12,12 @@ struct gb_function { struct gb_interface *interface; u16 cport_id; - enum greybus_function_type type; struct list_head links; /* interface->functions */ }; struct gb_function *gb_function_create(struct gb_interface *interface, - u16 cport_id, - enum greybus_function_type function_type); + u16 cport_id); void gb_function_destroy(struct gb_function *function); #endif /* __FUNCTION_H */ diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index 8b3bce6a9d0b..f8e7b38b5176 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -13,7 +13,7 @@ struct greybus_module_id { __u16 match_flags; __u16 vendor; __u16 product; - __u64 serial_number; + __u64 unique_id; kernel_ulong_t driver_info __attribute__((aligned(sizeof(kernel_ulong_t)))); diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 61189995f7d9..35e1c5df0424 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -17,13 +17,24 @@ enum greybus_descriptor_type { GREYBUS_TYPE_INVALID = 0x00, GREYBUS_TYPE_MODULE = 0x01, - GREYBUS_TYPE_DEVICE = 0x02, - GREYBUS_TYPE_CLASS = 0x03, - GREYBUS_TYPE_STRING = 0x04, - GREYBUS_TYPE_CPORT = 0x05, + GREYBUS_TYPE_STRING = 0x02, + GREYBUS_TYPE_INTERFACE = 0x03, + GREYBUS_TYPE_CPORT = 0x04, + GREYBUS_TYPE_CLASS = 0x05, }; -enum greybus_function_type { +enum greybus_protocol { + GREYBUS_PROTOCOL_CONTROL = 0x00, + GREYBUS_PROTOCOL_AP = 0x01, + GREYBUS_PROTOCOL_GPIO = 0x02, + GREYBUS_PROTOCOL_I2C = 0x03, + GREYBUS_PROTOCOL_UART = 0x04, + GREYBUS_PROTOCOL_HID = 0x05, + /* ... */ + GREYBUS_PROTOCOL_VENDOR = 0xff, +}; + +enum greybus_class_type { GREYBUS_FUNCTION_CONTROL = 0x00, GREYBUS_FUNCTION_USB = 0x01, GREYBUS_FUNCTION_GPIO = 0x02, @@ -48,57 +59,71 @@ struct greybus_descriptor_module { __le16 vendor; __le16 product; __le16 version; - __le64 serial_number; __u8 vendor_stringid; __u8 product_stringid; + __le64 unique_id; +}; + +/* + * The string in a string descriptor is not NUL-terminated. The + * size of the descriptor will be rounded up to a multiple of 4 + * bytes, by padding the string with 0x00 bytes if necessary. + */ +struct greybus_descriptor_string { + __u8 length; + __u8 id; + __u8 string[0]; }; /* - * A UniPro device normally supports a range of 32 CPorts (0..31). - * It is possible to support more than this by having a UniPro - * switch treat one device as if it were more than one. E.g., - * allocate 3 device ids (rather than the normal--1) to physical - * device 5, and configure the switch to route all packets destined - * for "encoded" device ids 5, 6, and 7 to physical device 5. - * Device 5 uses the encoded device id in incoming UniPro packets to - * determine which bank of 32 CPorts should receive the UniPro - * segment. + * An interface descriptor simply defines a module-unique id for + * each interface present on a module. Its sole purpose is to allow + * CPort descriptors to specify which interface they are associated + * with. Normally there's only one interface, with id 0. The + * second one must have id 1, and so on consecutively. * - * The "scale" field in this structure is used to define the number - * of encoded device ids should be allocated for this physical - * device. Scale is normally 1, to represent 32 available CPorts. - * A scale value 2 represents up to 64 CPorts; scale value 3 - * represents up to 96 CPorts, and so on. + * The largest CPort id associated with an interface (defined by a + * CPort descriptor in the manifest) is used to determine how to + * encode the device id and module number in UniPro packets + * that use the interface. */ struct greybus_descriptor_interface { __u8 id; /* module-relative id (0..) */ - __u8 scale; /* indicates range of of CPorts supported */ - /* UniPro gear, number of in/out lanes */ }; +/* + * A CPort descriptor indicates the id of the interface within the + * module it's associated with, along with the CPort id used to + * address the CPort. The protocol defines the format of messages + * exchanged using the CPort. + */ struct greybus_descriptor_cport { + __u8 interface; __le16 id; - __u8 function_type; /* enum greybus_function_type */ + __u8 protocol; /* enum greybus_protocol */ }; -struct greybus_descriptor_string { - __u8 length; - __u8 id; - __u8 string[0]; +/* + * A class descriptor defines functionality supplied by a module. + * Beyond that, not much else is defined yet... + */ +struct greybus_descriptor_class { + __u8 class; /* enum greybus_class_type */ }; struct greybus_descriptor_header { __le16 size; - __u8 type; /* enum greybus_descriptor_type */ + __u8 type; /* enum greybus_descriptor_type */ }; struct greybus_descriptor { - struct greybus_descriptor_header header; + struct greybus_descriptor_header header; union { struct greybus_descriptor_module module; struct greybus_descriptor_string string; struct greybus_descriptor_interface interface; struct greybus_descriptor_cport cport; + struct greybus_descriptor_class class; }; }; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index dc7d00975eee..a2c2f05f1fd9 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -32,7 +32,7 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) return NULL; interface->gmod = gmod; /* XXX refcount? */ - interface->interface_id = interface_id; + interface->id = interface_id; INIT_LIST_HEAD(&interface->functions); spin_lock_irq(&gb_interfaces_lock); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 7c3feb7e98a8..7a4b3704b6a0 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -13,7 +13,7 @@ struct gb_interface { struct gb_module *gmod; - u8 interface_id; + u8 id; struct list_head functions; struct list_head links; /* module->interfaces */ diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index bae260f0bc81..4066547cd2e5 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -80,11 +80,6 @@ static int identify_descriptor(struct greybus_descriptor *desc, size_t size) return -EINVAL; } break; - case GREYBUS_TYPE_DEVICE: - break; - case GREYBUS_TYPE_CLASS: - pr_err("class descriptor found (ignoring)\n"); - break; case GREYBUS_TYPE_STRING: expected_size = sizeof(struct greybus_descriptor_header); expected_size += sizeof(struct greybus_descriptor_string); @@ -95,6 +90,8 @@ static int identify_descriptor(struct greybus_descriptor *desc, size_t size) return -EINVAL; } break; + case GREYBUS_TYPE_INTERFACE: + break; case GREYBUS_TYPE_CPORT: if (desc_size < sizeof(struct greybus_descriptor_cport)) { pr_err("cport descriptor too small (%u)\n", @@ -102,6 +99,9 @@ static int identify_descriptor(struct greybus_descriptor *desc, size_t size) return -EINVAL; } break; + case GREYBUS_TYPE_CLASS: + pr_warn("class descriptor found (ignoring)\n"); + break; case GREYBUS_TYPE_INVALID: default: pr_err("invalid descriptor type (%hhu)\n", desc_header->type); @@ -183,7 +183,7 @@ u32 gb_manifest_parse_cports(struct gb_interface *interface) while (true) { struct manifest_desc *descriptor; struct greybus_descriptor_cport *desc_cport; - enum greybus_function_type function_type; + enum greybus_protocol protocol; u16 cport_id; bool found; @@ -191,19 +191,20 @@ u32 gb_manifest_parse_cports(struct gb_interface *interface) found = false; list_for_each_entry(descriptor, &manifest_descs, links) { if (descriptor->type == GREYBUS_TYPE_CPORT) { - found = true; - break; + desc_cport = descriptor->data; + if (desc_cport->interface == interface->id) { + found = true; + break; + } } } if (!found) break; /* Found one. Set up its function structure */ - desc_cport = descriptor->data; - function_type = - (enum greybus_function_type)desc_cport->function_type; + protocol = (enum greybus_protocol)desc_cport->protocol; cport_id = le16_to_cpu(desc_cport->id); - if (!gb_function_create(interface, cport_id, function_type)) + if (!gb_function_create(interface, cport_id)) return 0; /* Error */ count++; @@ -231,7 +232,7 @@ static u32 gb_manifest_parse_interfaces(struct gb_module *gmod) /* Find an interface descriptor */ list_for_each_entry(descriptor, &manifest_descs, links) { - if (descriptor->type == GREYBUS_TYPE_DEVICE) { + if (descriptor->type == GREYBUS_TYPE_INTERFACE) { found = true; break; } @@ -284,7 +285,7 @@ struct gb_module *gb_manifest_parse_module(struct manifest_desc *module_desc) gmod->vendor = le16_to_cpu(desc_module->vendor); gmod->product = le16_to_cpu(desc_module->product); gmod->version = le16_to_cpu(desc_module->version); - gmod->serial_number = le64_to_cpu(desc_module->serial_number); + gmod->unique_id = le64_to_cpu(desc_module->unique_id); /* Release the module descriptor, now that we're done with it */ release_manifest_descriptor(module_desc); diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 6618d212d6e1..1970e7b01081 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -23,7 +23,7 @@ static int gb_module_match_one_id(struct gb_module *gmod, return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && - (id->serial_number != gmod->serial_number)) + (id->unique_id != gmod->unique_id)) return 0; return 1; @@ -35,7 +35,7 @@ const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, if (id == NULL) return NULL; - for (; id->vendor || id->product || id->serial_number || + for (; id->vendor || id->product || id->unique_id || id->driver_info; id++) { if (gb_module_match_one_id(gmod, id)) return id; diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 326176a57adf..7b01950fa36e 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -24,9 +24,9 @@ struct gb_module { u16 vendor; u16 product; u16 version; - u64 serial_number; char *vendor_string; char *product_string; + u64 unique_id; int num_cports; int num_strings; diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index f969d90d2502..2777b8cf5b3a 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -40,7 +40,7 @@ static ssize_t module_serial_number_show(struct device *dev, { struct gb_module *gmod = to_gb_module(dev); - return sprintf(buf, "%llX\n", (unsigned long long)gmod->serial_number); + return sprintf(buf, "%llX\n", (unsigned long long)gmod->unique_id); } static DEVICE_ATTR_RO(module_serial_number); @@ -86,7 +86,7 @@ static umode_t module_attrs_are_visible(struct kobject *kobj, return mode; if (gmod->vendor || gmod->product || gmod->version) return mode; - if (gmod->serial_number) + if (gmod->unique_id) return mode; return 0; -- cgit v1.2.3-59-g8ed1b From 1bb3c724e85b3d13f6b2e455fb60c6e664f8f28d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 2 Oct 2014 12:30:03 -0500 Subject: greybus: create host device cport id map A Greybus host device has a pool of CPort Ids it can use. When we establish a connection with a CPort on another module we will need to allocate one from those that are available. This patch adds a bitmap to the greybus host device structure that allows cport ids to be allocated and freed as needed. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 75 +++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/greybus.h | 18 ++++++++++ 2 files changed, 93 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index d9bbc6737d7c..9669a34a36c5 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -30,6 +30,8 @@ int greybus_disabled(void) } EXPORT_SYMBOL_GPL(greybus_disabled); +static spinlock_t cport_id_map_lock; + static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(dev->driver); @@ -261,6 +263,69 @@ void greybus_remove_device(struct gb_module *gmod) static DEFINE_MUTEX(hd_mutex); +/* + * Allocate an available CPort Id for use on the given host device. + * Returns the CPort Id, or CPORT_ID_BAD of none remain. + * + * The lowest-available id is returned, so the first call is + * guaranteed to allocate CPort Id 0. + */ +u16 greybus_hd_cport_id_alloc(struct greybus_host_device *hd) +{ + unsigned long cport_id; + + /* If none left, return BAD */ + if (hd->cport_id_count == HOST_DEV_CPORT_ID_MAX) + return CPORT_ID_BAD; + + spin_lock_irq(&cport_id_map_lock); + cport_id = find_next_zero_bit(hd->cport_id_map, hd->cport_id_count, + hd->cport_id_next_free); + if (cport_id < hd->cport_id_count) { + hd->cport_id_next_free = cport_id + 1; /* Success */ + hd->cport_id_count++; + } else { + /* Lost a race for the last one */ + if (hd->cport_id_count != HOST_DEV_CPORT_ID_MAX) { + pr_err("bad cport_id_count in alloc"); + hd->cport_id_count = HOST_DEV_CPORT_ID_MAX; + } + cport_id = CPORT_ID_BAD; + } + spin_unlock_irq(&cport_id_map_lock); + + return cport_id; +} + +/* + * Free a previously-allocated CPort Id on the given host device. + */ +void greybus_hd_cport_id_free(struct greybus_host_device *hd, u16 cport_id) +{ + if (cport_id >= HOST_DEV_CPORT_ID_MAX) { + pr_err("bad cport_id %hu\n", cport_id); + return; + } + if (!hd->cport_id_count) { + pr_err("too many cport_id frees\n"); + return; + } + + spin_lock_irq(&cport_id_map_lock); + if (test_and_clear_bit(cport_id, hd->cport_id_map)) { + if (hd->cport_id_count) { + hd->cport_id_count--; + if (cport_id < hd->cport_id_next_free) + hd->cport_id_next_free = cport_id; + } else { + pr_err("bad cport_id_count in free"); + } + } else { + pr_err("duplicate cport_id %hu free\n", cport_id); + } + spin_unlock_irq(&cport_id_map_lock); +} + static void free_hd(struct kref *kref) { struct greybus_host_device *hd; @@ -284,6 +349,13 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver hd->driver = driver; INIT_LIST_HEAD(&hd->modules); + /* Pre-allocate CPort 0 for control stuff. XXX */ + if (greybus_hd_cport_id_alloc(hd) != 0) { + pr_err("couldn't allocate cport 0\n"); + kfree(hd); + return NULL; + } + return hd; } EXPORT_SYMBOL_GPL(greybus_create_hd); @@ -299,6 +371,9 @@ static int __init gb_init(void) { int retval; + BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD); + spin_lock_init(&cport_id_map_lock); + retval = gb_debugfs_init(); if (retval) { pr_err("debugfs failed\n"); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index fabd74e41059..077daf0a92bf 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -18,6 +18,7 @@ #include #include +#include "kernel_ver.h" #include "greybus_id.h" #include "greybus_manifest.h" #include "manifest.h" @@ -44,6 +45,15 @@ .match_flags = GREYBUS_DEVICE_ID_MATCH_SERIAL, \ .serial_number = (s), +/* XXX I couldn't get my Kconfig file to be noticed for out-of-tree build */ +#ifndef CONFIG_HOST_DEV_CPORT_ID_MAX +#define CONFIG_HOST_DEV_CPORT_ID_MAX 128 +#endif /* !CONFIG_HOST_DEV_CPORT_ID_MAX */ + +/* Maximum number of CPorts usable by a host device */ +/* XXX This should really be determined by the AP module manifest */ +#define HOST_DEV_CPORT_ID_MAX CONFIG_HOST_DEV_CPORT_ID_MAX +#define CPORT_ID_BAD U16_MAX /* UniPro max id is 4095 */ /* gbuf @@ -185,10 +195,18 @@ struct greybus_host_device { struct list_head modules; struct list_head connections; + spinlock_t cport_id_map_lock; + DECLARE_BITMAP(cport_id_map, HOST_DEV_CPORT_ID_MAX); + u16 cport_id_count; /* How many have been allocated */ + u16 cport_id_next_free; /* Where to start checking anyway */ + /* Private data for the host driver */ unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64)))); }; +u16 greybus_hd_cport_id_alloc(struct greybus_host_device *hd); +void greybus_hd_cport_id_free(struct greybus_host_device *hd, u16 cport_id); + struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver, struct device *parent); void greybus_remove_hd(struct greybus_host_device *hd); -- cgit v1.2.3-59-g8ed1b From 9e8a6860f5d113b42334b27f12217ee9fb9970f9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 2 Oct 2014 12:30:04 -0500 Subject: greybus: allocate connection host cport id Allocate a cport id from the host device whenever creating a connection. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 10 ++++++++-- drivers/staging/greybus/connection.h | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index fa5ab5d30ae7..b16ac4c0cc81 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -23,7 +23,7 @@ * pointer otherwise. */ struct gb_connection *gb_connection_create(struct greybus_host_device *hd, - u16 cport_id, struct gb_function *function) + struct gb_function *function) { struct gb_connection *connection; @@ -31,8 +31,13 @@ struct gb_connection *gb_connection_create(struct greybus_host_device *hd, if (!connection) return NULL; + connection->cport_id = greybus_hd_cport_id_alloc(hd); + if (connection->cport_id == CPORT_ID_BAD) { + kfree(connection); + return NULL; + } + connection->hd = hd; /* XXX refcount? */ - connection->cport_id = cport_id; connection->function = function; /* XXX refcount? */ INIT_LIST_HEAD(&connection->operations); atomic_set(&connection->op_cycle, 0); @@ -51,6 +56,7 @@ void gb_connection_destroy(struct gb_connection *connection) /* XXX Need to wait for any outstanding requests to complete */ WARN_ON(!list_empty(&connection->operations)); + greybus_hd_cport_id_free(connection->hd, connection->cport_id); /* kref_put(function); */ /* kref_put(hd); */ kfree(connection); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 6094fb44b990..f06ff92d7487 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -25,7 +25,7 @@ struct gb_connection { }; struct gb_connection *gb_connection_create(struct greybus_host_device *hd, - u16 cport_id, struct gb_function *function); + struct gb_function *function); void gb_connection_destroy(struct gb_connection *connection); u16 gb_connection_op_id(struct gb_connection *connection); -- cgit v1.2.3-59-g8ed1b From cd345074bb7cb2c2bdd6bc3b042c38a74128ed6c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 2 Oct 2014 12:30:05 -0500 Subject: greybus: get rid of functions now... We decided yesterday that we would no longer support the notion of a "function." Instead, a connection will simply exist between the AP and an interface on a module (and a CPort Id on each end). What was previously considered the "function type" will now be handled as the "protocol" associated with the connection. Update gb_connection_create() to take just the interface and a cport id associated with that interface. Right now every module points back to a host device, so for now we'll establish the connection back to that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 - drivers/staging/greybus/connection.c | 20 +++++++------ drivers/staging/greybus/connection.h | 13 ++++---- drivers/staging/greybus/function.c | 57 ------------------------------------ drivers/staging/greybus/function.h | 23 --------------- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/manifest.c | 2 +- drivers/staging/greybus/operation.c | 4 +-- 8 files changed, 22 insertions(+), 99 deletions(-) delete mode 100644 drivers/staging/greybus/function.c delete mode 100644 drivers/staging/greybus/function.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index a303f81a542d..42a3944f4eeb 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -6,7 +6,6 @@ greybus-y := core.o \ manifest.o \ module.o \ interface.o \ - function.o \ connection.o \ operation.o \ i2c-gb.o \ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b16ac4c0cc81..f3f774d2ed7b 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -22,23 +22,25 @@ * Returns a pointer to the new connection if successful, or a null * pointer otherwise. */ -struct gb_connection *gb_connection_create(struct greybus_host_device *hd, - struct gb_function *function) +struct gb_connection *gb_connection_create(struct gb_interface *interface, + u16 cport_id) { struct gb_connection *connection; + struct greybus_host_device *hd; connection = kzalloc(sizeof(*connection), GFP_KERNEL); if (!connection) return NULL; - connection->cport_id = greybus_hd_cport_id_alloc(hd); - if (connection->cport_id == CPORT_ID_BAD) { + hd = interface->gmod->hd; + connection->hd_cport_id = greybus_hd_cport_id_alloc(hd); + if (connection->hd_cport_id == CPORT_ID_BAD) { kfree(connection); return NULL; } - connection->hd = hd; /* XXX refcount? */ - connection->function = function; /* XXX refcount? */ + connection->interface = interface; /* XXX refcount? */ + connection->interface_cport_id = cport_id; INIT_LIST_HEAD(&connection->operations); atomic_set(&connection->op_cycle, 0); @@ -56,9 +58,9 @@ void gb_connection_destroy(struct gb_connection *connection) /* XXX Need to wait for any outstanding requests to complete */ WARN_ON(!list_empty(&connection->operations)); - greybus_hd_cport_id_free(connection->hd, connection->cport_id); - /* kref_put(function); */ - /* kref_put(hd); */ + greybus_hd_cport_id_free(connection->hd, connection->hd_cport_id); + /* kref_put(connection->interface); */ + /* kref_put(connection->hd); */ kfree(connection); } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index f06ff92d7487..530d6451c75d 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -14,18 +14,21 @@ #include "greybus.h" struct gb_connection { - struct gb_function *function; struct greybus_host_device *hd; - u16 cport_id; /* Host side */ + struct gb_interface *interface; + u16 hd_cport_id; + u16 interface_cport_id; - struct list_head host_links; + struct list_head hd_links; + struct list_head interface_links; + /* protocol */ struct list_head operations; atomic_t op_cycle; }; -struct gb_connection *gb_connection_create(struct greybus_host_device *hd, - struct gb_function *function); +struct gb_connection *gb_connection_create(struct gb_interface *interface, + u16 cport_id); void gb_connection_destroy(struct gb_connection *connection); u16 gb_connection_op_id(struct gb_connection *connection); diff --git a/drivers/staging/greybus/function.c b/drivers/staging/greybus/function.c deleted file mode 100644 index bfc9ac7e4c81..000000000000 --- a/drivers/staging/greybus/function.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Greybus functions - * - * Copyright 2014 Google Inc. - * - * Released under the GPLv2 only. - */ - -#include "greybus.h" - -/* XXX This could be per-host device or per-module or per-interface */ -static DEFINE_SPINLOCK(gb_functions_lock); - -/* - * A Greybus function generically defines an entity associated with - * a CPort within a module. Each function has a type (e.g. i2c, - * GPIO, etc.) that defines how it behaves and how the AP interacts - * with it. - * - * Create a gb_function structure to represent a discovered - * function. Returns a pointer to the new function or a null - * pointer if a failure occurs due to memory exhaustion. - */ -struct gb_function *gb_function_create(struct gb_interface *interface, - u16 cport_id) -{ - struct gb_function *function; - - function = kzalloc(sizeof(*function), GFP_KERNEL); - if (!function) - return NULL; - - function->interface = interface; /* XXX refcount? */ - function->cport_id = cport_id; - - spin_lock_irq(&gb_functions_lock); - list_add_tail(&function->links, &interface->functions); - spin_unlock_irq(&gb_functions_lock); - - return function; -} - -/* - * Tear down a previously set up function. - */ -void gb_function_destroy(struct gb_function *function) -{ - if (WARN_ON(!function)) - return; - - spin_lock_irq(&gb_functions_lock); - list_del(&function->links); - spin_unlock_irq(&gb_functions_lock); - - /* kref_put(gmod); */ - kfree(function); -} diff --git a/drivers/staging/greybus/function.h b/drivers/staging/greybus/function.h deleted file mode 100644 index e1b30c515a2a..000000000000 --- a/drivers/staging/greybus/function.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Greybus functions - * - * Copyright 2014 Google Inc. - * - * Released under the GPLv2 only. - */ - -#ifndef __FUNCTION_H -#define __FUNCTION_H - -struct gb_function { - struct gb_interface *interface; - u16 cport_id; - - struct list_head links; /* interface->functions */ -}; - -struct gb_function *gb_function_create(struct gb_interface *interface, - u16 cport_id); -void gb_function_destroy(struct gb_function *function); - -#endif /* __FUNCTION_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 077daf0a92bf..0813201260b1 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -24,7 +24,6 @@ #include "manifest.h" #include "module.h" #include "interface.h" -#include "function.h" #include "connection.h" #include "operation.h" diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 4066547cd2e5..6411c26928b3 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -204,7 +204,7 @@ u32 gb_manifest_parse_cports(struct gb_interface *interface) /* Found one. Set up its function structure */ protocol = (enum greybus_protocol)desc_cport->protocol; cport_id = le16_to_cpu(desc_cport->id); - if (!gb_function_create(interface, cport_id)) + if (!gb_connection_create(interface, cport_id)) return 0; /* Error */ count++; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5cb23aa3867a..b930d24ce552 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -124,8 +124,8 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, /* Our buffer holds a header in addition to the requested payload */ size += sizeof(*header); - gbuf = greybus_alloc_gbuf(connection->function->interface->gmod, - connection->cport_id, + gbuf = greybus_alloc_gbuf(connection->interface->gmod, + connection->hd_cport_id, gbuf_out_callback, size, GFP_KERNEL, operation); if (gbuf) { -- cgit v1.2.3-59-g8ed1b From ad1c449eb905ac1bf819afe9b799c11a6cf304a6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 2 Oct 2014 12:30:06 -0500 Subject: greybus: record connection protocol Record the protocol association with a connection when it gets created. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 +++- drivers/staging/greybus/connection.h | 4 ++-- drivers/staging/greybus/manifest.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f3f774d2ed7b..d63048c4f7de 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -23,7 +23,7 @@ * pointer otherwise. */ struct gb_connection *gb_connection_create(struct gb_interface *interface, - u16 cport_id) + u16 cport_id, enum greybus_protocol protocol) { struct gb_connection *connection; struct greybus_host_device *hd; @@ -41,6 +41,8 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, connection->hd = hd; /* XXX refcount? */ connection->interface = interface; /* XXX refcount? */ connection->interface_cport_id = cport_id; + connection->protocol = protocol; + INIT_LIST_HEAD(&connection->operations); atomic_set(&connection->op_cycle, 0); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 530d6451c75d..15bdc815286d 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -21,14 +21,14 @@ struct gb_connection { struct list_head hd_links; struct list_head interface_links; - /* protocol */ + enum greybus_protocol protocol; struct list_head operations; atomic_t op_cycle; }; struct gb_connection *gb_connection_create(struct gb_interface *interface, - u16 cport_id); + u16 cport_id, enum greybus_protocol protocol); void gb_connection_destroy(struct gb_connection *connection); u16 gb_connection_op_id(struct gb_connection *connection); diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 6411c26928b3..a694c886b9ea 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -204,7 +204,7 @@ u32 gb_manifest_parse_cports(struct gb_interface *interface) /* Found one. Set up its function structure */ protocol = (enum greybus_protocol)desc_cport->protocol; cport_id = le16_to_cpu(desc_cport->id); - if (!gb_connection_create(interface, cport_id)) + if (!gb_connection_create(interface, cport_id, protocol)) return 0; /* Error */ count++; -- cgit v1.2.3-59-g8ed1b From 322543a335c9dc9393a7472ce0b53a428ab3decd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Oct 2014 21:25:21 -0700 Subject: greybus: operation: fix endian issue in the operation message header size field. --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b930d24ce552..fe2f1a7137e5 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -139,7 +139,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, operation->gbuf = gbuf; header = (struct gb_operation_msg_hdr *)&gbuf->transfer_buffer; header->id = 0; - header->size = size; + header->size = cpu_to_le16(size); operation->payload = (char *)header + sizeof(*header); operation->callback = NULL; /* set at submit time */ -- cgit v1.2.3-59-g8ed1b From 2f30d9ffeee15fb655f1f5e288e69e46a3059b39 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Oct 2014 21:26:26 -0700 Subject: greybus: manifest: some minor sparse warning fixups. --- drivers/staging/greybus/manifest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index a694c886b9ea..ad7922aba32d 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -176,7 +176,7 @@ static char *gb_string_get(u8 string_id) * for the functions that use them. Returns the number of interfaces * set up for the given module, or 0 if there is an error. */ -u32 gb_manifest_parse_cports(struct gb_interface *interface) +static u32 gb_manifest_parse_cports(struct gb_interface *interface) { u32 count = 0; @@ -259,7 +259,7 @@ static u32 gb_manifest_parse_interfaces(struct gb_module *gmod) return count; } -struct gb_module *gb_manifest_parse_module(struct manifest_desc *module_desc) +static struct gb_module *gb_manifest_parse_module(struct manifest_desc *module_desc) { struct greybus_descriptor *desc = module_desc->data; struct greybus_descriptor_module *desc_module = &desc->module; -- cgit v1.2.3-59-g8ed1b From 32dff13d21120cfbf6196347f7bc74e9236fce1e Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Fri, 3 Oct 2014 13:38:24 -0400 Subject: greybus: fix gb_add_module() by enabling the device_add() Without the gb_module device being added, we have no parent device for any of the greybus subdevs to be added. Do the device_add() before creating subdevs as we need it then to register any children in the various greybus protocol drivers. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9669a34a36c5..8237f5f64015 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -231,14 +231,20 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, device_initialize(&gmod->dev); dev_set_name(&gmod->dev, "%d", module_id); - retval = gb_init_subdevs(gmod, &fake_greybus_module_id); + retval = device_add(&gmod->dev); if (retval) goto error; - // FIXME device_add(&gmod->dev); + retval = gb_init_subdevs(gmod, &fake_greybus_module_id); + if (retval) + goto error_subdevs; //return gmod; return; + +error_subdevs: + device_del(&gmod->dev); + error: put_device(&gmod->dev); greybus_module_release(&gmod->dev); -- cgit v1.2.3-59-g8ed1b From d7f9be4867e060712b6d9aaa1a8c98d15976c71d Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Fri, 3 Oct 2014 14:32:35 -0400 Subject: greybus: implement core module removal path Implement gb_remove_module() by finding the gb_module to be removed via the supplied module_id. Add support for removing the actual device into greybus_remove_device() after all the subdevs are disconnected. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 8237f5f64015..8811f35f9eaa 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -252,7 +252,19 @@ error: void gb_remove_module(struct greybus_host_device *hd, u8 module_id) { - // FIXME should be the remove_device call... + struct gb_module *gmod; + bool found = false; + + list_for_each_entry(gmod, &hd->modules, links) + if (gmod->module_id == module_id) { + found = true; + break; + } + + if (found) + greybus_remove_device(gmod); + else + dev_err(hd->parent, "module id %d remove error\n", module_id); } void greybus_remove_device(struct gb_module *gmod) @@ -264,7 +276,8 @@ void greybus_remove_device(struct gb_module *gmod) gb_tty_disconnect(gmod); gb_battery_disconnect(gmod); - // FIXME - device_remove(&gmod->dev); + device_del(&gmod->dev); + put_device(&gmod->dev); } static DEFINE_MUTEX(hd_mutex); -- cgit v1.2.3-59-g8ed1b From 748e1230cb921369738104415ed9352e81ccc413 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 3 Oct 2014 14:14:22 -0500 Subject: greybus: fix some hasty bugs Fix some omissions found in the code. - initialize and use the host device connections list - rename the interface connections list (was "functions") - use the interface connections list - define a spinlock protecting the connections lists - declare gb_operation_submit() in "operation.h" And the cport id map lock is per-host device, it's shared across all host devices. There's no need for one in struct greybus_host_device. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 12 ++++++++++++ drivers/staging/greybus/core.c | 5 +++-- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/interface.h | 2 +- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index d63048c4f7de..806eb8cd73e0 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -11,6 +11,8 @@ #include "kernel_ver.h" #include "greybus.h" +static DEFINE_SPINLOCK(gb_connections_lock); + /* * Set up a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -43,6 +45,11 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, connection->interface_cport_id = cport_id; connection->protocol = protocol; + spin_lock_irq(&gb_connections_lock); + list_add_tail(&connection->hd_links, &hd->connections); + list_add_tail(&connection->interface_links, &interface->connections); + spin_unlock_irq(&gb_connections_lock); + INIT_LIST_HEAD(&connection->operations); atomic_set(&connection->op_cycle, 0); @@ -60,6 +67,11 @@ void gb_connection_destroy(struct gb_connection *connection) /* XXX Need to wait for any outstanding requests to complete */ WARN_ON(!list_empty(&connection->operations)); + spin_lock_irq(&gb_connections_lock); + list_del(&connection->hd_links); + list_del(&connection->interface_links); + spin_unlock_irq(&gb_connections_lock); + greybus_hd_cport_id_free(connection->hd, connection->hd_cport_id); /* kref_put(connection->interface); */ /* kref_put(connection->hd); */ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 8811f35f9eaa..16100e720105 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -298,9 +298,9 @@ u16 greybus_hd_cport_id_alloc(struct greybus_host_device *hd) return CPORT_ID_BAD; spin_lock_irq(&cport_id_map_lock); - cport_id = find_next_zero_bit(hd->cport_id_map, hd->cport_id_count, + cport_id = find_next_zero_bit(hd->cport_id_map, HOST_DEV_CPORT_ID_MAX, hd->cport_id_next_free); - if (cport_id < hd->cport_id_count) { + if (cport_id < HOST_DEV_CPORT_ID_MAX) { hd->cport_id_next_free = cport_id + 1; /* Success */ hd->cport_id_count++; } else { @@ -367,6 +367,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver hd->parent = parent; hd->driver = driver; INIT_LIST_HEAD(&hd->modules); + INIT_LIST_HEAD(&hd->connections); /* Pre-allocate CPort 0 for control stuff. XXX */ if (greybus_hd_cport_id_alloc(hd) != 0) { diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 0813201260b1..460ace535322 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -194,7 +194,6 @@ struct greybus_host_device { struct list_head modules; struct list_head connections; - spinlock_t cport_id_map_lock; DECLARE_BITMAP(cport_id_map, HOST_DEV_CPORT_ID_MAX); u16 cport_id_count; /* How many have been allocated */ u16 cport_id_next_free; /* Where to start checking anyway */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index a2c2f05f1fd9..b9dd93093cc6 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -33,7 +33,7 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) interface->gmod = gmod; /* XXX refcount? */ interface->id = interface_id; - INIT_LIST_HEAD(&interface->functions); + INIT_LIST_HEAD(&interface->connections); spin_lock_irq(&gb_interfaces_lock); list_add_tail(&interface->links, &gmod->interfaces); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 7a4b3704b6a0..9c9ffe7715a5 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -14,7 +14,7 @@ struct gb_interface { struct gb_module *gmod; u8 id; - struct list_head functions; + struct list_head connections; struct list_head links; /* module->interfaces */ }; -- cgit v1.2.3-59-g8ed1b From 063e6ec221b6a68d28642e6653ae7cef2fdb11fd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 3 Oct 2014 14:14:23 -0500 Subject: greybus: fix greybus_class_type symbol names These were inadvertently not fixed when the type name was changed. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 35e1c5df0424..d31eb684c9f1 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -35,20 +35,20 @@ enum greybus_protocol { }; enum greybus_class_type { - GREYBUS_FUNCTION_CONTROL = 0x00, - GREYBUS_FUNCTION_USB = 0x01, - GREYBUS_FUNCTION_GPIO = 0x02, - GREYBUS_FUNCTION_SPI = 0x03, - GREYBUS_FUNCTION_UART = 0x04, - GREYBUS_FUNCTION_PWM = 0x05, - GREYBUS_FUNCTION_I2S = 0x06, - GREYBUS_FUNCTION_I2C = 0x07, - GREYBUS_FUNCTION_SDIO = 0x08, - GREYBUS_FUNCTION_HID = 0x09, - GREYBUS_FUNCTION_DISPLAY = 0x0a, - GREYBUS_FUNCTION_CAMERA = 0x0b, - GREYBUS_FUNCTION_SENSOR = 0x0c, - GREYBUS_FUNCTION_VENDOR = 0xff, + GREYBUS_CLASS_CONTROL = 0x00, + GREYBUS_CLASS_USB = 0x01, + GREYBUS_CLASS_GPIO = 0x02, + GREYBUS_CLASS_SPI = 0x03, + GREYBUS_CLASS_UART = 0x04, + GREYBUS_CLASS_PWM = 0x05, + GREYBUS_CLASS_I2S = 0x06, + GREYBUS_CLASS_I2C = 0x07, + GREYBUS_CLASS_SDIO = 0x08, + GREYBUS_CLASS_HID = 0x09, + GREYBUS_CLASS_DISPLAY = 0x0a, + GREYBUS_CLASS_CAMERA = 0x0b, + GREYBUS_CLASS_SENSOR = 0x0c, + GREYBUS_CLASS_VENDOR = 0xff, }; /* -- cgit v1.2.3-59-g8ed1b From 177404bd20fdabd77d04ad818d56ab34150fff4c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 3 Oct 2014 14:14:24 -0500 Subject: greybus: use ida for cport id allocation The ida mechanism for allocating ids may be overkill but it works. Don't preallocate the id 0 for control. That should be done when initializing connections based on the manifest anyway. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 40 ++++++++++++++++++-- drivers/staging/greybus/core.c | 71 +----------------------------------- drivers/staging/greybus/greybus.h | 6 +-- 3 files changed, 40 insertions(+), 77 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 806eb8cd73e0..c50472d07831 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -13,6 +13,39 @@ static DEFINE_SPINLOCK(gb_connections_lock); +/* + * Allocate an available CPort Id for use for the host side of the + * given connection. The lowest-available id is returned, so the + * first call is guaranteed to allocate CPort Id 0. + * + * Assigns the connection's hd_cport_id and returns true if successful. + * Returns false otherwise. + */ +static bool hd_connection_hd_cport_id_alloc(struct gb_connection *connection) +{ + struct ida *ida = &connection->hd->cport_id_map; + int id; + + id = ida_simple_get(ida, 0, HOST_DEV_CPORT_ID_MAX, GFP_KERNEL); + if (id < 0) + return false; + + connection->hd_cport_id = (u16)id; + + return true; +} + +/* + * Free a previously-allocated CPort Id on the given host device. + */ +static void hd_connection_hd_cport_id_free(struct gb_connection *connection) +{ + struct ida *ida = &connection->hd->cport_id_map; + + ida_simple_remove(ida, connection->hd_cport_id); + connection->hd_cport_id = CPORT_ID_BAD; +} + /* * Set up a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -35,8 +68,9 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, return NULL; hd = interface->gmod->hd; - connection->hd_cport_id = greybus_hd_cport_id_alloc(hd); - if (connection->hd_cport_id == CPORT_ID_BAD) { + connection->hd = hd; /* XXX refcount? */ + if (!hd_connection_hd_cport_id_alloc(connection)) { + /* kref_put(connection->hd); */ kfree(connection); return NULL; } @@ -72,7 +106,7 @@ void gb_connection_destroy(struct gb_connection *connection) list_del(&connection->interface_links); spin_unlock_irq(&gb_connections_lock); - greybus_hd_cport_id_free(connection->hd, connection->hd_cport_id); + hd_connection_hd_cport_id_free(connection); /* kref_put(connection->interface); */ /* kref_put(connection->hd); */ kfree(connection); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 16100e720105..40c899697a33 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -282,69 +282,6 @@ void greybus_remove_device(struct gb_module *gmod) static DEFINE_MUTEX(hd_mutex); -/* - * Allocate an available CPort Id for use on the given host device. - * Returns the CPort Id, or CPORT_ID_BAD of none remain. - * - * The lowest-available id is returned, so the first call is - * guaranteed to allocate CPort Id 0. - */ -u16 greybus_hd_cport_id_alloc(struct greybus_host_device *hd) -{ - unsigned long cport_id; - - /* If none left, return BAD */ - if (hd->cport_id_count == HOST_DEV_CPORT_ID_MAX) - return CPORT_ID_BAD; - - spin_lock_irq(&cport_id_map_lock); - cport_id = find_next_zero_bit(hd->cport_id_map, HOST_DEV_CPORT_ID_MAX, - hd->cport_id_next_free); - if (cport_id < HOST_DEV_CPORT_ID_MAX) { - hd->cport_id_next_free = cport_id + 1; /* Success */ - hd->cport_id_count++; - } else { - /* Lost a race for the last one */ - if (hd->cport_id_count != HOST_DEV_CPORT_ID_MAX) { - pr_err("bad cport_id_count in alloc"); - hd->cport_id_count = HOST_DEV_CPORT_ID_MAX; - } - cport_id = CPORT_ID_BAD; - } - spin_unlock_irq(&cport_id_map_lock); - - return cport_id; -} - -/* - * Free a previously-allocated CPort Id on the given host device. - */ -void greybus_hd_cport_id_free(struct greybus_host_device *hd, u16 cport_id) -{ - if (cport_id >= HOST_DEV_CPORT_ID_MAX) { - pr_err("bad cport_id %hu\n", cport_id); - return; - } - if (!hd->cport_id_count) { - pr_err("too many cport_id frees\n"); - return; - } - - spin_lock_irq(&cport_id_map_lock); - if (test_and_clear_bit(cport_id, hd->cport_id_map)) { - if (hd->cport_id_count) { - hd->cport_id_count--; - if (cport_id < hd->cport_id_next_free) - hd->cport_id_next_free = cport_id; - } else { - pr_err("bad cport_id_count in free"); - } - } else { - pr_err("duplicate cport_id %hu free\n", cport_id); - } - spin_unlock_irq(&cport_id_map_lock); -} - static void free_hd(struct kref *kref) { struct greybus_host_device *hd; @@ -368,13 +305,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver hd->driver = driver; INIT_LIST_HEAD(&hd->modules); INIT_LIST_HEAD(&hd->connections); - - /* Pre-allocate CPort 0 for control stuff. XXX */ - if (greybus_hd_cport_id_alloc(hd) != 0) { - pr_err("couldn't allocate cport 0\n"); - kfree(hd); - return NULL; - } + ida_init(&hd->cport_id_map); return hd; } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 460ace535322..4404d93b5a33 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "kernel_ver.h" #include "greybus_id.h" @@ -194,7 +195,7 @@ struct greybus_host_device { struct list_head modules; struct list_head connections; - DECLARE_BITMAP(cport_id_map, HOST_DEV_CPORT_ID_MAX); + struct ida cport_id_map; u16 cport_id_count; /* How many have been allocated */ u16 cport_id_next_free; /* Where to start checking anyway */ @@ -202,9 +203,6 @@ struct greybus_host_device { unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64)))); }; -u16 greybus_hd_cport_id_alloc(struct greybus_host_device *hd); -void greybus_hd_cport_id_free(struct greybus_host_device *hd, u16 cport_id); - struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver, struct device *parent); void greybus_remove_hd(struct greybus_host_device *hd); -- cgit v1.2.3-59-g8ed1b From 937d0da83ffde769f24b5124d66f793045c5da10 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 3 Oct 2014 14:14:25 -0500 Subject: greybus: fix module setup The code that was setting up a module was not properly initializing the module data structure. Fixing this required a little rework. Now gb_add_module() (which the host device pointer and module id) allocates and initializes the structure, and passes it to gb_manifest_parse() for populating it further. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 13 ++++++-- drivers/staging/greybus/manifest.c | 63 +++++++++++++++++--------------------- drivers/staging/greybus/manifest.h | 3 +- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 40c899697a33..41fb7caf0d8b 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -203,14 +203,19 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, struct gb_module *gmod; int retval; + gmod = gb_module_create(hd, module_id); + if (!gmod) { + dev_err(hd->parent, "failed to create module\n"); + return; + } + /* * Parse the manifest and build up our data structures * representing what's in it. */ - gmod = gb_manifest_parse(data, size); - if (!gmod) { + if (!gb_manifest_parse(gmod, data, size)) { dev_err(hd->parent, "manifest error\n"); - return; + goto error; } /* @@ -246,6 +251,8 @@ error_subdevs: device_del(&gmod->dev); error: + gb_module_destroy(gmod); + put_device(&gmod->dev); greybus_module_release(&gmod->dev); } diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index ad7922aba32d..03282fd643b3 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -259,27 +259,20 @@ static u32 gb_manifest_parse_interfaces(struct gb_module *gmod) return count; } -static struct gb_module *gb_manifest_parse_module(struct manifest_desc *module_desc) +static bool gb_manifest_parse_module(struct gb_module *gmod, + struct manifest_desc *module_desc) { struct greybus_descriptor *desc = module_desc->data; struct greybus_descriptor_module *desc_module = &desc->module; - struct gb_module *gmod; - - gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); - if (!gmod) - return NULL; /* Handle the strings first--they can fail */ gmod->vendor_string = gb_string_get(desc_module->vendor_stringid); - if (IS_ERR(gmod->vendor_string)) { - kfree(gmod); - return NULL; - } + if (IS_ERR(gmod->vendor_string)) + return false; + gmod->product_string = gb_string_get(desc_module->product_stringid); if (IS_ERR(gmod->product_string)) { - kfree(gmod->vendor_string); - kfree(gmod); - return NULL; + goto out_err; } gmod->vendor = le16_to_cpu(desc_module->vendor); @@ -293,11 +286,17 @@ static struct gb_module *gb_manifest_parse_module(struct manifest_desc *module_d /* A module must have at least one interface descriptor */ if (!gb_manifest_parse_interfaces(gmod)) { pr_err("manifest interface descriptors not valid\n"); - gb_module_destroy(gmod); - return NULL; + goto out_err; } - return gmod; + return true; +out_err: + kfree(gmod->product_string); + gmod->product_string = NULL; + kfree(gmod->vendor_string); + gmod->vendor_string = NULL; + + return false; } /* @@ -321,25 +320,23 @@ static struct gb_module *gb_manifest_parse_module(struct manifest_desc *module_d * After that we look for the module's interfaces--there must be at * least one of those. * - * Return a pointer to an initialized gb_module structure - * representing the content of the module manifest, or a null - * pointer if an error occurs. + * Returns true if parsing was successful, false otherwise. */ -struct gb_module *gb_manifest_parse(void *data, size_t size) +bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) { struct greybus_manifest *manifest; struct greybus_manifest_header *header; struct greybus_descriptor *desc; struct manifest_desc *descriptor; - struct manifest_desc *module_desc = NULL; - struct gb_module *gmod; + struct manifest_desc *module_desc = false; u16 manifest_size; u32 found = 0; + bool result = false; /* we have to have at _least_ the manifest header */ if (size <= sizeof(manifest->header)) { pr_err("short manifest (%zu)\n", size); - return NULL; + return false; } /* Make sure the size is right */ @@ -349,7 +346,7 @@ struct gb_module *gb_manifest_parse(void *data, size_t size) if (manifest_size != size) { pr_err("manifest size mismatch %zu != %hu\n", size, manifest_size); - return NULL; + return false; } /* Validate major/minor number */ @@ -357,7 +354,7 @@ struct gb_module *gb_manifest_parse(void *data, size_t size) pr_err("manifest version too new (%hhu.%hhu > %hhu.%hhu)\n", header->version_major, header->version_minor, GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); - return NULL; + return false; } /* OK, find all the descriptors */ @@ -370,7 +367,7 @@ struct gb_module *gb_manifest_parse(void *data, size_t size) if (desc_size <= 0) { if (!desc_size) pr_err("zero-sized manifest descriptor\n"); - goto out_err; + goto out; } desc = (struct greybus_descriptor *)((char *)desc + desc_size); size -= desc_size; @@ -385,24 +382,20 @@ struct gb_module *gb_manifest_parse(void *data, size_t size) if (found != 1) { pr_err("manifest must have 1 module descriptor (%u found)\n", found); - goto out_err; + goto out; } /* Parse the module manifest, starting with the module descriptor */ - gmod = gb_manifest_parse_module(module_desc); + result = gb_manifest_parse_module(gmod, module_desc); /* * We really should have no remaining descriptors, but we * don't know what newer format manifests might leave. */ - if (!list_empty(&manifest_descs)) { + if (!list_empty(&manifest_descs)) pr_info("excess descriptors in module manifest\n"); - release_manifest_descriptors(); - } - - return gmod; -out_err: +out: release_manifest_descriptors(); - return NULL; + return false; } diff --git a/drivers/staging/greybus/manifest.h b/drivers/staging/greybus/manifest.h index 29cbf92bc2cf..a1fe2c1281ad 100644 --- a/drivers/staging/greybus/manifest.h +++ b/drivers/staging/greybus/manifest.h @@ -9,6 +9,7 @@ #ifndef __MANIFEST_H #define __MANIFEST_H -struct gb_module *gb_manifest_parse(void *data, size_t size); +struct gb_module; +bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size); #endif /* __MANIFEST_H */ -- cgit v1.2.3-59-g8ed1b From 652433f3fe97e3609acd9afcb9b50ec62f7a8d05 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 3 Oct 2014 15:05:19 -0500 Subject: greybus: gbuf: have caller set actual_length A Greybus buffer has both a transfer_buffer_size field, which is the size in bytes of the transfer buffer, and an actual_length field, which is the number of bytes in that buffer that are actually consumed. The user of the buffer--and not the buffer allocation method--should be setting the actual_length field. Stop setting the actual length on the es1-ap-usb alloc_gbuf_data method. And *do* set it in gb_operation_create(), where we can presume the operation being allocated will consume all the bytes requested. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 1 - drivers/staging/greybus/operation.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index f23c67414ecf..360149923c17 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -126,7 +126,6 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) buffer[0] = gbuf->cport_id; gbuf->transfer_buffer = &buffer[1]; gbuf->transfer_buffer_length = size; - gbuf->actual_length = size; /* When we send the gbuf, we need this pointer to be here */ gbuf->hdpriv = es1; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index fe2f1a7137e5..75f6e50b2ee6 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -132,6 +132,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, kfree(operation); return NULL; } + gbuf->actual_length = size; /* Record what we'll use */ operation->connection = connection; /* XXX refcount? */ -- cgit v1.2.3-59-g8ed1b From b0b657555c8f9bb64189a0ff8824de1ad992f889 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 3 Oct 2014 15:05:20 -0500 Subject: greybus: specify type when creating an operation The type of an operation belongs in the operation header, which shouldn't be touched by users of the interface. So specify it at operation creation time. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 +++-- drivers/staging/greybus/operation.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 75f6e50b2ee6..03f6660616b3 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -111,7 +111,7 @@ static void gbuf_out_callback(struct gbuf *gbuf) * failure occurs due to memory exhaustion. */ struct gb_operation *gb_operation_create(struct gb_connection *connection, - size_t size) + size_t size, u8 type) { struct gb_operation *operation; struct gb_operation_msg_hdr *header; @@ -139,8 +139,9 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, /* Fill in the header structure and payload pointer */ operation->gbuf = gbuf; header = (struct gb_operation_msg_hdr *)&gbuf->transfer_buffer; - header->id = 0; header->size = cpu_to_le16(size); + header->id = 0; /* Filled in when submitted */ + header->type = type; operation->payload = (char *)header + sizeof(*header); operation->callback = NULL; /* set at submit time */ diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 96a7a0fcba56..0dff703bb2f4 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -60,7 +60,7 @@ struct gb_operation { }; struct gb_operation *gb_operation_create(struct gb_connection *connection, - size_t size); + size_t size, u8 type); void gb_operation_destroy(struct gb_operation *operation); int gb_operation_wait(struct gb_operation *operation); -- cgit v1.2.3-59-g8ed1b From 8a30672a526adcf797bcd3e40d4ef90d8b794f3f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 3 Oct 2014 15:05:21 -0500 Subject: greybus: add a connection->private field This will be used for driver-specific data for whatever drives the other end of the connection. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 15bdc815286d..f2588a74904a 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -25,6 +25,8 @@ struct gb_connection { struct list_head operations; atomic_t op_cycle; + + void *private; }; struct gb_connection *gb_connection_create(struct gb_interface *interface, -- cgit v1.2.3-59-g8ed1b From eeeed42250f851a9c3368448b63c5aeaae65962a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 3 Oct 2014 15:05:22 -0500 Subject: greybus: define gb_connection_err() Define a function that prints error information about a Greybus connection in a standard format. This adopts the convention that [M:I:C] represents the "path" the connection represents--specifying the module id, the interface number on that module, and the connection id on that interface. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 18 ++++++++++++++++++ drivers/staging/greybus/connection.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c50472d07831..80ebe1d305ae 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -116,3 +116,21 @@ u16 gb_connection_op_id(struct gb_connection *connection) { return (u16)(atomic_inc_return(&connection->op_cycle) % U16_MAX); } + +void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + pr_err("greybus: [%hhu:%hhu:%hu]: %pV\n", + connection->interface->gmod->module_id, + connection->interface->id, + connection->interface_cport_id, &vaf); + + va_end(args); +} diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index f2588a74904a..61e94357db3a 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -35,4 +35,7 @@ void gb_connection_destroy(struct gb_connection *connection); u16 gb_connection_op_id(struct gb_connection *connection); +__printf(2, 3) +void gb_connection_err(struct gb_connection *connection, const char *fmt, ...); + #endif /* __CONNECTION_H */ -- cgit v1.2.3-59-g8ed1b From 2fb5c518fcbc6c33f36039c23a9fe38a64ad4040 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 4 Oct 2014 18:43:41 -0700 Subject: greybus: manifest.c: minor sparse cleanup --- drivers/staging/greybus/manifest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 03282fd643b3..f0468d768fe7 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -328,7 +328,7 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) struct greybus_manifest_header *header; struct greybus_descriptor *desc; struct manifest_desc *descriptor; - struct manifest_desc *module_desc = false; + struct manifest_desc *module_desc = NULL; u16 manifest_size; u32 found = 0; bool result = false; -- cgit v1.2.3-59-g8ed1b From f6aec2516a1afcab2cdd1897098afe8b4bb435f2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 6 Oct 2014 06:53:06 -0500 Subject: greybus: fix two misnamed functions I guess I got a little hd crazy. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 80ebe1d305ae..53086126f961 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -21,7 +21,7 @@ static DEFINE_SPINLOCK(gb_connections_lock); * Assigns the connection's hd_cport_id and returns true if successful. * Returns false otherwise. */ -static bool hd_connection_hd_cport_id_alloc(struct gb_connection *connection) +static bool gb_connection_hd_cport_id_alloc(struct gb_connection *connection) { struct ida *ida = &connection->hd->cport_id_map; int id; @@ -38,7 +38,7 @@ static bool hd_connection_hd_cport_id_alloc(struct gb_connection *connection) /* * Free a previously-allocated CPort Id on the given host device. */ -static void hd_connection_hd_cport_id_free(struct gb_connection *connection) +static void gb_connection_hd_cport_id_free(struct gb_connection *connection) { struct ida *ida = &connection->hd->cport_id_map; @@ -69,7 +69,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, hd = interface->gmod->hd; connection->hd = hd; /* XXX refcount? */ - if (!hd_connection_hd_cport_id_alloc(connection)) { + if (!gb_connection_hd_cport_id_alloc(connection)) { /* kref_put(connection->hd); */ kfree(connection); return NULL; @@ -106,7 +106,7 @@ void gb_connection_destroy(struct gb_connection *connection) list_del(&connection->interface_links); spin_unlock_irq(&gb_connections_lock); - hd_connection_hd_cport_id_free(connection); + gb_connection_hd_cport_id_free(connection); /* kref_put(connection->interface); */ /* kref_put(connection->hd); */ kfree(connection); -- cgit v1.2.3-59-g8ed1b From 369fb8324969a94c2f243ea56641993ee2061e60 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 6 Oct 2014 06:53:07 -0500 Subject: greybus: bury some dead code One data structure and a few fields in another one are no longer used, and were not removed when they should have been. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4404d93b5a33..f4ca18aba2a6 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -120,12 +120,6 @@ struct gbuf; -struct gmod_string { - u16 length; - u8 id; - u8 string[0]; -}; - typedef void (*gbuf_complete_t)(struct gbuf *gbuf); struct gbuf { @@ -194,10 +188,7 @@ struct greybus_host_device { struct list_head modules; struct list_head connections; - struct ida cport_id_map; - u16 cport_id_count; /* How many have been allocated */ - u16 cport_id_next_free; /* Where to start checking anyway */ /* Private data for the host driver */ unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64)))); -- cgit v1.2.3-59-g8ed1b From ee9ebe4d0b78e64f0c3741085d230ea18c15f4e4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 6 Oct 2014 06:53:08 -0500 Subject: greybus: add bg_hd_connection_find() Add a function that looks up a connection given the host device pointer an the host cport id. This will be used to determine which connection an incoming message is associated with. Replace the list tracking host device connections with a red-black tree so lookup can scale and be done quickly. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 54 ++++++++++++++++++++++++++++++++++-- drivers/staging/greybus/connection.h | 5 +++- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/greybus.h | 2 +- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 53086126f961..449ae34bf531 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -13,6 +13,56 @@ static DEFINE_SPINLOCK(gb_connections_lock); +static void _gb_hd_connection_insert(struct greybus_host_device *hd, + struct gb_connection *connection) +{ + struct rb_root *root = &hd->connections; + struct rb_node *node = &connection->hd_node; + struct rb_node **link = &root->rb_node; + struct rb_node *above = NULL; + u16 cport_id = connection->hd_cport_id; + + while (*link) { + struct gb_connection *connection; + + above = *link; + connection = rb_entry(above, struct gb_connection, hd_node); + if (connection->hd_cport_id > cport_id) + link = &above->rb_left; + else if (connection->hd_cport_id < cport_id) + link = &above->rb_right; + } + rb_link_node(node, above, link); + rb_insert_color(node, root); +} + +static void _gb_hd_connection_remove(struct gb_connection *connection) +{ + rb_erase(&connection->hd_node, &connection->hd->connections); +} + +struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, + u16 cport_id) +{ + struct gb_connection *connection = NULL; + struct rb_node *node; + + spin_lock_irq(&gb_connections_lock); + node = hd->connections.rb_node; + while (node) { + connection = rb_entry(node, struct gb_connection, hd_node); + if (connection->hd_cport_id > cport_id) + node = node->rb_left; + else if (connection->hd_cport_id < cport_id) + node = node->rb_right; + else + break; + } + spin_unlock_irq(&gb_connections_lock); + + return connection; +} + /* * Allocate an available CPort Id for use for the host side of the * given connection. The lowest-available id is returned, so the @@ -80,7 +130,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, connection->protocol = protocol; spin_lock_irq(&gb_connections_lock); - list_add_tail(&connection->hd_links, &hd->connections); + _gb_hd_connection_insert(hd, connection); list_add_tail(&connection->interface_links, &interface->connections); spin_unlock_irq(&gb_connections_lock); @@ -102,8 +152,8 @@ void gb_connection_destroy(struct gb_connection *connection) WARN_ON(!list_empty(&connection->operations)); spin_lock_irq(&gb_connections_lock); - list_del(&connection->hd_links); list_del(&connection->interface_links); + _gb_hd_connection_remove(connection); spin_unlock_irq(&gb_connections_lock); gb_connection_hd_cport_id_free(connection); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 61e94357db3a..89d58e5707bc 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -19,7 +19,7 @@ struct gb_connection { u16 hd_cport_id; u16 interface_cport_id; - struct list_head hd_links; + struct rb_node hd_node; struct list_head interface_links; enum greybus_protocol protocol; @@ -33,6 +33,9 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, u16 cport_id, enum greybus_protocol protocol); void gb_connection_destroy(struct gb_connection *connection); +struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, + u16 cport_id); + u16 gb_connection_op_id(struct gb_connection *connection); __printf(2, 3) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 41fb7caf0d8b..51d7e59362d5 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -311,7 +311,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver hd->parent = parent; hd->driver = driver; INIT_LIST_HEAD(&hd->modules); - INIT_LIST_HEAD(&hd->connections); + hd->connections = RB_ROOT; ida_init(&hd->cport_id_map); return hd; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f4ca18aba2a6..ae7d32244c53 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -187,7 +187,7 @@ struct greybus_host_device { const struct greybus_host_driver *driver; struct list_head modules; - struct list_head connections; + struct rb_root connections; struct ida cport_id_map; /* Private data for the host driver */ -- cgit v1.2.3-59-g8ed1b From 00d2e7588c79a2b9681f243d06ad02a254ab4ff9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 6 Oct 2014 06:53:09 -0500 Subject: greybus: look up connection for recevied messages Look up the connection that an incoming message is associated with. This is the start of making message handling oriented toward the the connection rather than the cport. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index fada12192726..17c1af8a00e8 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -179,6 +179,14 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, { struct gb_cport_handler *ch; struct gbuf *gbuf; + struct gb_connection *connection; + + connection = gb_hd_connection_find(hd, cport_id); + if (!connection) { + dev_err(hd->parent, + "nonexistent connection (%zu bytes dropped)\n", length); + return; + } /* first check to see if we have a cport handler for this cport */ ch = &cport_handler[cport_id]; -- cgit v1.2.3-59-g8ed1b From 6eb3f4bdec331c951906792479a0f7ea49fe9a5c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 6 Oct 2014 06:53:10 -0500 Subject: greybus: allocate gbufs using the connection Switch to using the connection rather than the host device as the locus for doing Greybus buffer allocation. A connection encapsulates both the host device (whose driver is what's required for allocation) and the *destination* cport id. Record the connection a gbuf is associated with rather than the host module and (unspecified) cport id. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 10 +++++----- drivers/staging/greybus/gbuf.c | 22 ++++++++++------------ drivers/staging/greybus/greybus.h | 5 ++--- drivers/staging/greybus/operation.c | 4 +--- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 360149923c17..502b353540fa 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -95,7 +95,7 @@ static void cport_out_callback(struct urb *urb); */ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) { - struct es1_ap_dev *es1 = hd_to_es1(gbuf->gmod->hd); + struct es1_ap_dev *es1 = hd_to_es1(gbuf->connection->hd); u8 *buffer; if (size > ES1_GBUF_MSG_SIZE) { @@ -116,14 +116,14 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) * we will encode the cport number in the first byte of the buffer, so * set the second byte to be the "transfer buffer" */ - if (gbuf->cport_id > (u16)U8_MAX) { - pr_err("gbuf->cport_id is '%d' and is out of range!\n", - gbuf->cport_id); + if (gbuf->connection->interface_cport_id > (u16)U8_MAX) { + pr_err("gbuf->interface_cport_id (%hd) is out of range!\n", + gbuf->connection->interface_cport_id); kfree(buffer); return -EINVAL; } - buffer[0] = gbuf->cport_id; + buffer[0] = gbuf->connection->interface_cport_id; gbuf->transfer_buffer = &buffer[1]; gbuf->transfer_buffer_length = size; diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 17c1af8a00e8..2afd889a9ebe 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -26,8 +26,7 @@ static struct kmem_cache *gbuf_head_cache; /* Workqueue to handle Greybus buffer completions. */ static struct workqueue_struct *gbuf_workqueue; -static struct gbuf *__alloc_gbuf(struct gb_module *gmod, - u16 cport_id, +static struct gbuf *__alloc_gbuf(struct gb_connection *connection, gbuf_complete_t complete, gfp_t gfp_mask, void *context) @@ -39,8 +38,7 @@ static struct gbuf *__alloc_gbuf(struct gb_module *gmod, return NULL; kref_init(&gbuf->kref); - gbuf->gmod = gmod; - gbuf->cport_id = cport_id; + gbuf->connection = connection; INIT_WORK(&gbuf->event, cport_process_event); gbuf->complete = complete; gbuf->context = context; @@ -63,8 +61,7 @@ static struct gbuf *__alloc_gbuf(struct gb_module *gmod, * that the driver can then fill up with the data to be sent out. Curse * hardware designers for this issue... */ -struct gbuf *greybus_alloc_gbuf(struct gb_module *gmod, - u16 cport_id, +struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, gbuf_complete_t complete, unsigned int size, gfp_t gfp_mask, @@ -73,14 +70,14 @@ struct gbuf *greybus_alloc_gbuf(struct gb_module *gmod, struct gbuf *gbuf; int retval; - gbuf = __alloc_gbuf(gmod, cport_id, complete, gfp_mask, context); + gbuf = __alloc_gbuf(connection, complete, gfp_mask, context); if (!gbuf) return NULL; gbuf->direction = GBUF_DIRECTION_OUT; /* Host controller specific allocation for the actual buffer */ - retval = gmod->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); + retval = connection->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); if (retval) { greybus_free_gbuf(gbuf); return NULL; @@ -98,7 +95,7 @@ static void free_gbuf(struct kref *kref) /* If the direction is "out" then the host controller frees the data */ if (gbuf->direction == GBUF_DIRECTION_OUT) { - gbuf->gmod->hd->driver->free_gbuf_data(gbuf); + gbuf->connection->hd->driver->free_gbuf_data(gbuf); } else { /* we "own" this in data, so free it ourselves */ kfree(gbuf->transfer_buffer); @@ -125,7 +122,9 @@ EXPORT_SYMBOL_GPL(greybus_get_gbuf); int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { - return gbuf->gmod->hd->driver->submit_gbuf(gbuf, gbuf->gmod->hd, gfp_mask); + struct greybus_host_device *hd = gbuf->connection->hd; + + return hd->driver->submit_gbuf(gbuf, hd, gfp_mask); } int greybus_kill_gbuf(struct gbuf *gbuf) @@ -198,8 +197,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, return; } - gbuf = __alloc_gbuf(ch->gmod, ch->cport_id, ch->handler, GFP_ATOMIC, - ch->context); + gbuf = __alloc_gbuf(connection, ch->handler, GFP_ATOMIC, ch->context); if (!gbuf) { /* Again, something bad went wrong, log it... */ pr_err("can't allocate gbuf???\n"); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index ae7d32244c53..e46d27e6ffec 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -126,8 +126,7 @@ struct gbuf { struct kref kref; void *hdpriv; - struct gb_module *gmod; - u16 cport_id; + struct gb_connection *connection; int status; void *transfer_buffer; u32 transfer_flags; /* flags for the transfer buffer */ @@ -201,7 +200,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); void greybus_gbuf_finished(struct gbuf *gbuf); -struct gbuf *greybus_alloc_gbuf(struct gb_module *gmod, u16 cport_id, +struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, gbuf_complete_t complete, unsigned int size, gfp_t gfp_mask, void *context); void greybus_free_gbuf(struct gbuf *gbuf); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 03f6660616b3..cca3918174cf 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -124,9 +124,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, /* Our buffer holds a header in addition to the requested payload */ size += sizeof(*header); - gbuf = greybus_alloc_gbuf(connection->interface->gmod, - connection->hd_cport_id, - gbuf_out_callback, size, + gbuf = greybus_alloc_gbuf(connection, gbuf_out_callback, size, GFP_KERNEL, operation); if (gbuf) { kfree(operation); -- cgit v1.2.3-59-g8ed1b From 9a6f6314d17ce2b66a6293b86f79afda6e9a563b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 6 Oct 2014 06:53:11 -0500 Subject: greybus: use alloc_gbuf_data() for both directions Change the "direction" flag field of a gbuf to be a Boolean called "outbound". Add a Boolean outbound flag to alloc_gbuf_data(), and use it for allocating the data buffer for gbufs for data being transferred in either direction. Update free_gbuf_data() accordingly--letting the host device driver's gbuf data free function handle all of them. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 30 +++++++++++++++++++----------- drivers/staging/greybus/gbuf.c | 27 ++++++++------------------- drivers/staging/greybus/greybus.h | 9 ++++----- drivers/staging/greybus/operation.c | 2 +- 4 files changed, 32 insertions(+), 36 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 502b353540fa..ef0ac7f424cf 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -93,9 +93,11 @@ static void cport_out_callback(struct urb *urb); * void *transfer_buffer; * u32 transfer_buffer_length; */ -static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) +static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, + gfp_t gfp_mask) { struct es1_ap_dev *es1 = hd_to_es1(gbuf->connection->hd); + u32 cport_reserve = gbuf->outbound ? 1 : 0; u8 *buffer; if (size > ES1_GBUF_MSG_SIZE) { @@ -107,8 +109,12 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) * but for ES1, it's so dirt simple, we don't have a choice... * * Also, do a "slow" allocation now, if we need speed, use a cache + * + * For ES1 outbound buffers need to insert their target + * CPort Id before the data; set aside an extra byte for + * that purpose in that case. */ - buffer = kmalloc(size + 1, gfp_mask); + buffer = kmalloc(cport_reserve + size, gfp_mask); if (!buffer) return -ENOMEM; @@ -123,8 +129,10 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) return -EINVAL; } - buffer[0] = gbuf->connection->interface_cport_id; - gbuf->transfer_buffer = &buffer[1]; + /* Insert the cport id for outbound buffers */ + if (gbuf->outbound) + *buffer++ = gbuf->connection->interface_cport_id; + gbuf->transfer_buffer = buffer; gbuf->transfer_buffer_length = size; /* When we send the gbuf, we need this pointer to be here */ @@ -136,15 +144,15 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) /* Free the memory we allocated with a gbuf */ static void free_gbuf_data(struct gbuf *gbuf) { - u8 *transfer_buffer; - u8 *buffer; + u8 *transfer_buffer = gbuf->transfer_buffer; - transfer_buffer = gbuf->transfer_buffer; /* Can be called with a NULL transfer_buffer on some error paths */ - if (transfer_buffer) { - buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ - kfree(buffer); - } + if (!transfer_buffer) + return; + + if (gbuf->outbound) + transfer_buffer--; /* Back up to cport id */ + kfree(transfer_buffer); } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 2afd889a9ebe..62d0cb63a83e 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -27,6 +27,7 @@ static struct kmem_cache *gbuf_head_cache; static struct workqueue_struct *gbuf_workqueue; static struct gbuf *__alloc_gbuf(struct gb_connection *connection, + bool outbound, gbuf_complete_t complete, gfp_t gfp_mask, void *context) @@ -40,6 +41,7 @@ static struct gbuf *__alloc_gbuf(struct gb_connection *connection, kref_init(&gbuf->kref); gbuf->connection = connection; INIT_WORK(&gbuf->event, cport_process_event); + gbuf->outbound = outbound; gbuf->complete = complete; gbuf->context = context; @@ -64,18 +66,17 @@ static struct gbuf *__alloc_gbuf(struct gb_connection *connection, struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, gbuf_complete_t complete, unsigned int size, + bool outbound, gfp_t gfp_mask, void *context) { struct gbuf *gbuf; int retval; - gbuf = __alloc_gbuf(connection, complete, gfp_mask, context); + gbuf = __alloc_gbuf(connection, outbound, complete, gfp_mask, context); if (!gbuf) return NULL; - gbuf->direction = GBUF_DIRECTION_OUT; - /* Host controller specific allocation for the actual buffer */ retval = connection->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); if (retval) { @@ -93,13 +94,7 @@ static void free_gbuf(struct kref *kref) { struct gbuf *gbuf = container_of(kref, struct gbuf, kref); - /* If the direction is "out" then the host controller frees the data */ - if (gbuf->direction == GBUF_DIRECTION_OUT) { - gbuf->connection->hd->driver->free_gbuf_data(gbuf); - } else { - /* we "own" this in data, so free it ourselves */ - kfree(gbuf->transfer_buffer); - } + gbuf->connection->hd->driver->free_gbuf_data(gbuf); kmem_cache_free(gbuf_head_cache, gbuf); } @@ -197,14 +192,14 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, return; } - gbuf = __alloc_gbuf(connection, ch->handler, GFP_ATOMIC, ch->context); + gbuf = greybus_alloc_gbuf(connection, ch->handler, length, false, + GFP_ATOMIC, ch->context); + if (!gbuf) { /* Again, something bad went wrong, log it... */ pr_err("can't allocate gbuf???\n"); return; } - gbuf->hdpriv = hd; - gbuf->direction = GBUF_DIRECTION_IN; /* * FIXME: @@ -212,13 +207,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, * be, we should move to a model where the hd "owns" all buffers, but we * want something up and working first for now. */ - gbuf->transfer_buffer = kmalloc(length, GFP_ATOMIC); - if (!gbuf->transfer_buffer) { - kfree(gbuf); - return; - } memcpy(gbuf->transfer_buffer, data, length); - gbuf->transfer_buffer_length = length; gbuf->actual_length = length; queue_work(gbuf_workqueue, &gbuf->event); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index e46d27e6ffec..12a6cbf15b9e 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -133,9 +133,7 @@ struct gbuf { u32 transfer_buffer_length; u32 actual_length; -#define GBUF_DIRECTION_OUT 0 -#define GBUF_DIRECTION_IN 1 - unsigned int direction : 1; /* 0 is out, 1 is in */ + bool outbound; /* AP-relative data direction */ void *context; struct work_struct event; @@ -172,7 +170,8 @@ struct svc_msg; struct greybus_host_driver { size_t hd_priv_size; - int (*alloc_gbuf_data)(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask); + int (*alloc_gbuf_data)(struct gbuf *gbuf, unsigned int size, + gfp_t gfp_mask); void (*free_gbuf_data)(struct gbuf *gbuf); int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); @@ -202,7 +201,7 @@ void greybus_gbuf_finished(struct gbuf *gbuf); struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, gbuf_complete_t complete, unsigned int size, - gfp_t gfp_mask, void *context); + bool outbound, gfp_t gfp_mask, void *context); void greybus_free_gbuf(struct gbuf *gbuf); struct gbuf *greybus_get_gbuf(struct gbuf *gbuf); #define greybus_put_gbuf greybus_free_gbuf diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index cca3918174cf..b5351b277676 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -125,7 +125,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, /* Our buffer holds a header in addition to the requested payload */ size += sizeof(*header); gbuf = greybus_alloc_gbuf(connection, gbuf_out_callback, size, - GFP_KERNEL, operation); + true, GFP_KERNEL, operation); if (gbuf) { kfree(operation); return NULL; -- cgit v1.2.3-59-g8ed1b From fdb594f1366327f10cbc8167a9923a5e88a381fa Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 6 Oct 2014 06:53:12 -0500 Subject: greybus: kill __alloc_gbuf() GEt rid of __alloc_gbuf(), now that it's used in only one place. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 62d0cb63a83e..2ef6ead8ed17 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -26,28 +26,6 @@ static struct kmem_cache *gbuf_head_cache; /* Workqueue to handle Greybus buffer completions. */ static struct workqueue_struct *gbuf_workqueue; -static struct gbuf *__alloc_gbuf(struct gb_connection *connection, - bool outbound, - gbuf_complete_t complete, - gfp_t gfp_mask, - void *context) -{ - struct gbuf *gbuf; - - gbuf = kmem_cache_zalloc(gbuf_head_cache, gfp_mask); - if (!gbuf) - return NULL; - - kref_init(&gbuf->kref); - gbuf->connection = connection; - INIT_WORK(&gbuf->event, cport_process_event); - gbuf->outbound = outbound; - gbuf->complete = complete; - gbuf->context = context; - - return gbuf; -} - /** * greybus_alloc_gbuf - allocate a greybus buffer * @@ -73,14 +51,21 @@ struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, struct gbuf *gbuf; int retval; - gbuf = __alloc_gbuf(connection, outbound, complete, gfp_mask, context); + gbuf = kmem_cache_zalloc(gbuf_head_cache, gfp_mask); if (!gbuf) return NULL; + kref_init(&gbuf->kref); + gbuf->connection = connection; + INIT_WORK(&gbuf->event, cport_process_event); + gbuf->outbound = outbound; + gbuf->complete = complete; + gbuf->context = context; + /* Host controller specific allocation for the actual buffer */ retval = connection->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); if (retval) { - greybus_free_gbuf(gbuf); + kmem_cache_free(gbuf_head_cache, gbuf); return NULL; } -- cgit v1.2.3-59-g8ed1b From e8caf9a31b90d7c596cfcd5810dbf6910f3cbcc6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 6 Oct 2014 06:53:13 -0500 Subject: greybus: get rid of gbuf->hdpriv Last time I tried to kill off gbuf->context my efforts were shot down. Now that I've got the connection infrastructure in place, maybe I'll have more luck getting rid of gbuf->hdpriv. The only place it's used is to stash the es1_ap_dev structure pointer in the buffer. But that information is now available through the buffer's connection, so we don't need to use the hdpriv field any more. So get rid of it, and use hd_to_es1(gbuf->connection->hd) to get at what we need. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 6 +----- drivers/staging/greybus/greybus.h | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index ef0ac7f424cf..97dd9b6a2bc1 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -96,7 +96,6 @@ static void cport_out_callback(struct urb *urb); static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) { - struct es1_ap_dev *es1 = hd_to_es1(gbuf->connection->hd); u32 cport_reserve = gbuf->outbound ? 1 : 0; u8 *buffer; @@ -135,9 +134,6 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gbuf->transfer_buffer = buffer; gbuf->transfer_buffer_length = size; - /* When we send the gbuf, we need this pointer to be here */ - gbuf->hdpriv = es1; - return 0; } @@ -337,7 +333,7 @@ exit: static void cport_out_callback(struct urb *urb) { struct gbuf *gbuf = urb->context; - struct es1_ap_dev *es1 = gbuf->hdpriv; + struct es1_ap_dev *es1 = hd_to_es1(gbuf->connection->hd); unsigned long flags; int i; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 12a6cbf15b9e..851f5ae095cc 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -124,7 +124,6 @@ typedef void (*gbuf_complete_t)(struct gbuf *gbuf); struct gbuf { struct kref kref; - void *hdpriv; struct gb_connection *connection; int status; -- cgit v1.2.3-59-g8ed1b From 7a13e2f68831cba76cb699103b27219649ca6e57 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Mon, 6 Oct 2014 09:58:44 -0400 Subject: greybus: fix manifest parsing problem with descriptor payload The internal struct manifest_desc needs the data payload, rather than the entire descriptor with header to be populated into the data field. Also fix two places where the parser was trying to extract the entire descriptor with header for the data payload field. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index f0468d768fe7..43ece7403c77 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -113,7 +113,7 @@ static int identify_descriptor(struct greybus_descriptor *desc, size_t size) return -ENOMEM; descriptor->size = desc_size; - descriptor->data = desc; + descriptor->data = (u8 *)desc + sizeof(*desc_header); descriptor->type = desc_header->type; list_add_tail(&descriptor->links, &manifest_descs); @@ -143,13 +143,11 @@ static char *gb_string_get(u8 string_id) return NULL; list_for_each_entry(descriptor, &manifest_descs, links) { - struct greybus_descriptor *desc; if (descriptor->type != GREYBUS_TYPE_STRING) continue; - desc = descriptor->data; - desc_string = &desc->string; + desc_string = descriptor->data; if (desc_string->id == string_id) { found = true; break; @@ -262,8 +260,7 @@ static u32 gb_manifest_parse_interfaces(struct gb_module *gmod) static bool gb_manifest_parse_module(struct gb_module *gmod, struct manifest_desc *module_desc) { - struct greybus_descriptor *desc = module_desc->data; - struct greybus_descriptor_module *desc_module = &desc->module; + struct greybus_descriptor_module *desc_module = module_desc->data; /* Handle the strings first--they can fail */ gmod->vendor_string = gb_string_get(desc_module->vendor_stringid); -- cgit v1.2.3-59-g8ed1b From e86905b6cd73ce62af88b3526dbc10c14d47016d Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Mon, 6 Oct 2014 13:26:02 -0400 Subject: greybus: gb_hd_connection_find(): fix "not found" case Without this, null-testing the return value of this function is broken. Signed-off-by: Marti Bolivar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 449ae34bf531..07a593d2cbd1 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -56,8 +56,10 @@ struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, else if (connection->hd_cport_id < cport_id) node = node->rb_right; else - break; + goto found; } + connection = NULL; + found: spin_unlock_irq(&gb_connections_lock); return connection; -- cgit v1.2.3-59-g8ed1b From ff8aed527455feea7d36bb45dd529b7931150087 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Mon, 6 Oct 2014 13:46:36 -0400 Subject: greybus: fix gb_manifest_parse() successful return path Even if we successfully parse a manifest we are returning failure. Instead, we now proudly proclaim success. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 43ece7403c77..09fcde9dd7ce 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -364,6 +364,7 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) if (desc_size <= 0) { if (!desc_size) pr_err("zero-sized manifest descriptor\n"); + result = false; goto out; } desc = (struct greybus_descriptor *)((char *)desc + desc_size); @@ -379,6 +380,7 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) if (found != 1) { pr_err("manifest must have 1 module descriptor (%u found)\n", found); + result = false; goto out; } @@ -394,5 +396,5 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) out: release_manifest_descriptors(); - return false; + return result; } -- cgit v1.2.3-59-g8ed1b From 25b7b6d04bdfc76631cc02012f33f10bc27dd27e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Oct 2014 20:29:40 -0700 Subject: greybus: connection: properly lock idr We had a lock, but we never used it, so move it to be per-hd, like the idr structure is. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 +++++- drivers/staging/greybus/core.c | 4 +--- drivers/staging/greybus/greybus.h | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 07a593d2cbd1..1a2bec22b424 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -78,7 +78,9 @@ static bool gb_connection_hd_cport_id_alloc(struct gb_connection *connection) struct ida *ida = &connection->hd->cport_id_map; int id; + spin_lock(&connection->hd->cport_id_map_lock); id = ida_simple_get(ida, 0, HOST_DEV_CPORT_ID_MAX, GFP_KERNEL); + spin_unlock(&connection->hd->cport_id_map_lock); if (id < 0) return false; @@ -94,7 +96,9 @@ static void gb_connection_hd_cport_id_free(struct gb_connection *connection) { struct ida *ida = &connection->hd->cport_id_map; + spin_lock(&connection->hd->cport_id_map_lock); ida_simple_remove(ida, connection->hd_cport_id); + spin_unlock(&connection->hd->cport_id_map_lock); connection->hd_cport_id = CPORT_ID_BAD; } @@ -126,7 +130,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, kfree(connection); return NULL; } - connection->hd = hd; /* XXX refcount? */ + connection->interface = interface; /* XXX refcount? */ connection->interface_cport_id = cport_id; connection->protocol = protocol; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 51d7e59362d5..6c4107e20762 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -30,8 +30,6 @@ int greybus_disabled(void) } EXPORT_SYMBOL_GPL(greybus_disabled); -static spinlock_t cport_id_map_lock; - static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(dev->driver); @@ -313,6 +311,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver INIT_LIST_HEAD(&hd->modules); hd->connections = RB_ROOT; ida_init(&hd->cport_id_map); + spin_lock_init(&hd->cport_id_map_lock); return hd; } @@ -330,7 +329,6 @@ static int __init gb_init(void) int retval; BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD); - spin_lock_init(&cport_id_map_lock); retval = gb_debugfs_init(); if (retval) { diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 851f5ae095cc..20c1a034f691 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -186,6 +186,7 @@ struct greybus_host_device { struct list_head modules; struct rb_root connections; struct ida cport_id_map; + spinlock_t cport_id_map_lock; /* Private data for the host driver */ unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64)))); -- cgit v1.2.3-59-g8ed1b From 8faa8da9a621821dfc5945cff415c4d3f904f3ce Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Oct 2014 20:34:48 -0700 Subject: greybus: greybus.h: remove transfer_flags We didn't use them, so drop it. Also some other checkpatch cleanups while I was in there. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 20c1a034f691..39ccdc4a63a8 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -34,7 +34,7 @@ #define GREYBUS_VERSION_MINOR 0x01 #define GREYBUS_DEVICE_ID_MATCH_DEVICE \ - (GREYBUS_DEVICE_ID_MATCH_VENDOR | GREYBUS_DEVICE_ID_MATCH_PRODUCT) + (GREYBUS_DEVICE_ID_MATCH_VENDOR | GREYBUS_DEVICE_ID_MATCH_PRODUCT) #define GREYBUS_DEVICE(v, p) \ .match_flags = GREYBUS_DEVICE_ID_MATCH_DEVICE, \ @@ -128,7 +128,6 @@ struct gbuf { struct gb_connection *connection; int status; void *transfer_buffer; - u32 transfer_flags; /* flags for the transfer buffer */ u32 transfer_buffer_length; u32 actual_length; @@ -139,10 +138,6 @@ struct gbuf { gbuf_complete_t complete; }; -/* - * gbuf->transfer_flags - */ -#define GBUF_FREE_BUFFER BIT(0) /* Free the transfer buffer with the gbuf */ /* For SP1 hardware, we are going to "hardcode" each device to have all logical * blocks in order to be able to address them as one unified "unit". Then @@ -189,10 +184,10 @@ struct greybus_host_device { spinlock_t cport_id_map_lock; /* Private data for the host driver */ - unsigned long hd_priv[0] __attribute__ ((aligned(sizeof(s64)))); + unsigned long hd_priv[0] __aligned(sizeof(s64)); }; -struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *host_driver, +struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, struct device *parent); void greybus_remove_hd(struct greybus_host_device *hd); void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, -- cgit v1.2.3-59-g8ed1b From c4f37c62fafef55972dfbce31d253df9b4d85bd0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Oct 2014 20:37:08 -0700 Subject: greybus: greybus_id.h: checkpatch cleanup --- drivers/staging/greybus/greybus_id.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index f8e7b38b5176..c6cd2e8fa7c1 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -15,8 +15,7 @@ struct greybus_module_id { __u16 product; __u64 unique_id; - kernel_ulong_t driver_info - __attribute__((aligned(sizeof(kernel_ulong_t)))); + kernel_ulong_t driver_info __aligned(sizeof(kernel_ulong_t)); }; /* Used to match the greybus_module_id */ -- cgit v1.2.3-59-g8ed1b From 62e120f599c203da7ffa455581405ff26786d83c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Oct 2014 20:37:18 -0700 Subject: greybus: svc_msg.h: add bsd license to file so that firmware can use it. --- drivers/staging/greybus/svc_msg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 1f8b4f1b7017..57d0a91ff60b 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -6,7 +6,7 @@ * * Copyright 2014 Google Inc. * - * Released under the GPLv2 only. + * Released under the GPLv2 and BSD license. */ #ifndef __SVC_MSG_H -- cgit v1.2.3-59-g8ed1b From 9b60aa02bae723db131ae8f4e460c09a3b528a3f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Oct 2014 20:37:53 -0700 Subject: greybus: greybus_manifest.h: add BSD license so that firmware can share it. --- drivers/staging/greybus/greybus_manifest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index d31eb684c9f1..4fd27d3823a1 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -6,7 +6,7 @@ * * Copyright 2014 Google Inc. * - * Released under the GPLv2 only. + * Released under the GPLv2 and BSD licenses. */ #ifndef __GREYBUS_MANIFEST_H -- cgit v1.2.3-59-g8ed1b From 8218605d20fff015bb3f76060ad7fe87d4362fc0 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 10 Oct 2014 14:42:33 -0700 Subject: greybus: Changes required for integrating into the Android build In order to easily integrate into the Android build, include an Android.mk. Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Android.mk | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 drivers/staging/greybus/Android.mk diff --git a/drivers/staging/greybus/Android.mk b/drivers/staging/greybus/Android.mk new file mode 100644 index 000000000000..773129badf62 --- /dev/null +++ b/drivers/staging/greybus/Android.mk @@ -0,0 +1,29 @@ +.PHONY: build-greybus + +$(PRODUCT_OUT)/ramdisk.img: build-greybus + +include $(CLEAR_VARS) +GREYBUS_SRC_PATH := $(ANDROID_BUILD_TOP)/external/greybus/ +LOCAL_PATH := $(GREYBUS_SRC_PATH) +LOCAL_SRC_FILES := greybus.ko +LOCAL_MODULE := $(LOCAL_SRC_FILES) +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/root/lib/modules +$(LOCAL_PATH)/$(LOCAL_SRC_FILES): build-greybus +include $(BUILD_PREBUILT) + +KDIRARG := KERNELDIR="${ANDROID_PRODUCT_OUT}/obj/kernel" +ifneq ($(ANDROID_64),) + ARCHARG := ARCH=arm64 + FLAGARG := EXTRA_CFLAGS+=-fno-pic +else + ARCHARG := ARCH=arm +endif +ARGS := $(KDIRARG) $(ARCHARG) $(FLAGARG) + +build-greybus: android_kernel + make clean -C $(GREYBUS_SRC_PATH) + cd $(GREYBUS_SRC_PATH) &&\ + $(MAKE) -j$(MAKE_JOBS) CROSS_COMPILE=$(KERNEL_TOOLS_PREFIX) $(ARGS) + mkdir -p $(PRODUCT_OUT)/root/lib/modules + cp $(GREYBUS_SRC_PATH)/greybus.ko $(PRODUCT_OUT)/root/lib/modules -- cgit v1.2.3-59-g8ed1b From 8fd39e3dcb881501726ed0161855ed7a61f642ef Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Mon, 13 Oct 2014 03:00:53 -0400 Subject: greybus: es1-ap-usb: handle -EPROTO in check_urb_status() On a disconnect we can also have a status of -EPROTO. This results in a flood of error messages due to the -EAGAIN handling of unsupported status results. Fix this by also returning status when we have -EPROTO. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 97dd9b6a2bc1..1a67d9e15c2a 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -257,6 +257,7 @@ static int check_urb_status(struct urb *urb) case -ENOENT: case -ESHUTDOWN: case -EILSEQ: + case -EPROTO: /* device is gone, stop sending */ return status; } -- cgit v1.2.3-59-g8ed1b From 051fb04712593a1bebdf638cd9f9935db2ce48aa Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:24 -0500 Subject: greybus: don't assume subdevs are valid Most of the disconnect routines for the "subdevs" of a module blindly assume that initialization of the subdev was successful. Fix this by checking for null pointers. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 2 ++ drivers/staging/greybus/gpio-gb.c | 2 ++ drivers/staging/greybus/i2c-gb.c | 2 ++ drivers/staging/greybus/sdio-gb.c | 4 +++- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 1ef2f17623b0..eaced9ab39d8 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -135,6 +135,8 @@ void gb_battery_disconnect(struct gb_module *gmod) struct gb_battery *gb; gb = gmod->gb_battery; + if (!gb) + return; power_supply_unregister(&gb->bat); diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 2369175bf0f9..da20028e7c46 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -92,6 +92,8 @@ void gb_gpio_disconnect(struct gb_module *gmod) int retval; gb_gpio_dev = gmod->gb_gpio_dev; + if (!gb_gpio_dev) + return; retval = gpiochip_remove(&gb_gpio_dev->chip); kfree(gb_gpio_dev); diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index c01f41b59c55..0da195898ad4 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -121,6 +121,8 @@ void gb_i2c_disconnect(struct gb_module *gmod) struct gb_i2c_device *gb_i2c_dev; gb_i2c_dev = gmod->gb_i2c_dev; + if (!gb_i2c_dev) + return; i2c_del_adapter(gb_i2c_dev->adapter); kfree(gb_i2c_dev->adapter); kfree(gb_i2c_dev); diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index f7e80abcc0a0..239fcf773acb 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -71,8 +71,10 @@ void gb_sdio_disconnect(struct gb_module *gmod) struct gb_sdio_host *host; host = gmod->gb_sdio_host; - mmc = host->mmc; + if (!host) + return; + mmc = host->mmc; mmc_remove_host(mmc); mmc_free_host(mmc); } -- cgit v1.2.3-59-g8ed1b From ad8cd0d643f0c3f86755a0169bb7c9597005fa8e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:25 -0500 Subject: greybus: unlock gbuf mutex on free To drop a reference on a gbuf, greybus_free_gbuf() is called. That uses kref_put_mutex() to drop the refernce under protection of gbuf_mutex. However the release routine, free_gbuf(), never releases the mutex as it should. Fix that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 2ef6ead8ed17..a33dbb417312 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -82,6 +82,7 @@ static void free_gbuf(struct kref *kref) gbuf->connection->hd->driver->free_gbuf_data(gbuf); kmem_cache_free(gbuf_head_cache, gbuf); + mutex_unlock(&gbuf_mutex); } void greybus_free_gbuf(struct gbuf *gbuf) -- cgit v1.2.3-59-g8ed1b From a06df4b08cee2cd201b3cbb7ee8312ea68cc2047 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:26 -0500 Subject: greybus: unlock hd mutex on free When free_hd() is called, hd_mutex is held. It is the responsibility of free_hd() to drop that mutex. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 6c4107e20762..67628719f8d1 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -294,6 +294,7 @@ static void free_hd(struct kref *kref) hd = container_of(kref, struct greybus_host_device, kref); kfree(hd); + mutex_unlock(&hd_mutex); } struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver, -- cgit v1.2.3-59-g8ed1b From 170229d19586955e30604b2de37557e09a70b66c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:27 -0500 Subject: greybus: don't restrict input buffer size Don't assume that input buffers have any particular content. The only thing the gbuf layer needs to be concerned with is the presence of the cport_id byte at the beginning of a transfer. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 1a67d9e15c2a..e55ad03b56d6 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -305,10 +305,9 @@ static void cport_in_callback(struct urb *urb) if (status) return; - /* The size has to be more then just an "empty" transfer */ - if (urb->actual_length <= 2) { - dev_err(dev, "%s: \"short\" cport in transfer of %d bytes?\n", - __func__, urb->actual_length); + /* The size has to be at least one, for the cport id */ + if (!urb->actual_length) { + dev_err(dev, "%s: no cport id in input buffer?\n", __func__); goto exit; } @@ -338,10 +337,6 @@ static void cport_out_callback(struct urb *urb) unsigned long flags; int i; - /* If no error, tell the core the gbuf is properly sent */ - if (!check_urb_status(urb)) - greybus_gbuf_finished(gbuf); - /* * See if this was an urb in our pool, if so mark it "free", otherwise * we need to free it ourselves. -- cgit v1.2.3-59-g8ed1b From a7901d5ef7ff6bb6b7fdc500d9383450b23ba059 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:28 -0500 Subject: greybus: zero all data buffers Don't assume the buffer data area will all be overwritten. Zero all buffer space, to avoid sending crap over the wire. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index e55ad03b56d6..2405670d8afc 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -113,7 +113,7 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, * CPort Id before the data; set aside an extra byte for * that purpose in that case. */ - buffer = kmalloc(cport_reserve + size, gfp_mask); + buffer = kzalloc(cport_reserve + size, gfp_mask); if (!buffer) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 61418b9fd9a4e38b5ba41f7c1703083a989dda09 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:29 -0500 Subject: greybus: drop hd arg from submit_gbuf method Every gbuf is associated with a connection when it is created. And a connection contains a pointer to the host device that will carry messages. So there's no need for the submit_gbuf() method to have the host device pointer passed to it, the function can get it from the gbuf's connection. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 4 ++-- drivers/staging/greybus/gbuf.c | 2 +- drivers/staging/greybus/greybus.h | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 2405670d8afc..5acf5a75e44b 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -206,9 +206,9 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) return urb; } -static int submit_gbuf(struct gbuf *gbuf, struct greybus_host_device *hd, - gfp_t gfp_mask) +static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { + struct greybus_host_device *hd = gbuf->connection->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; int retval; diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index a33dbb417312..6580deb5dbe8 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -105,7 +105,7 @@ int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { struct greybus_host_device *hd = gbuf->connection->hd; - return hd->driver->submit_gbuf(gbuf, hd, gfp_mask); + return hd->driver->submit_gbuf(gbuf, gfp_mask); } int greybus_kill_gbuf(struct gbuf *gbuf) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 39ccdc4a63a8..033c7abe96f0 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -169,8 +169,7 @@ struct greybus_host_driver { void (*free_gbuf_data)(struct gbuf *gbuf); int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); - int (*submit_gbuf)(struct gbuf *gbuf, struct greybus_host_device *hd, - gfp_t gfp_mask); + int (*submit_gbuf)(struct gbuf *gbuf, gfp_t gfp_mask); }; struct greybus_host_device { -- cgit v1.2.3-59-g8ed1b From 17d265f6a69aef5920cf3d6669735239cb792184 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:30 -0500 Subject: greybus: stop init_subdevs stuff Upcoming patches are going to set up devices based on what is discovered in the module manifest. Get rid of the hard-coded initialization done by gb_init_subdevs(), along with other related code. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 75 ++------------------------------------- drivers/staging/greybus/greybus.h | 17 --------- 2 files changed, 2 insertions(+), 90 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 67628719f8d1..720ab47033fb 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -131,59 +131,6 @@ static struct device_type greybus_module_type = { .release = greybus_module_release, }; -/* XXX - * This needs to be driven by the list of functions that the - * manifest says are present. - */ -static int gb_init_subdevs(struct gb_module *gmod, - const struct greybus_module_id *id) -{ - int retval; - - /* Allocate all of the different "sub device types" for this device */ - - /* XXX - * Decide what exactly we should get supplied for the i2c - * probe, and then work that back to what should be present - * in the manifest. - */ - retval = gb_i2c_probe(gmod, id); - if (retval) - goto error_i2c; - - retval = gb_gpio_probe(gmod, id); - if (retval) - goto error_gpio; - - retval = gb_sdio_probe(gmod, id); - if (retval) - goto error_sdio; - - retval = gb_tty_probe(gmod, id); - if (retval) - goto error_tty; - - retval = gb_battery_probe(gmod, id); - if (retval) - goto error_battery; - return 0; - -error_battery: - gb_tty_disconnect(gmod); - -error_tty: - gb_sdio_disconnect(gmod); - -error_sdio: - gb_gpio_disconnect(gmod); - -error_gpio: - gb_i2c_disconnect(gmod); - -error_i2c: - return retval; -} - static const struct greybus_module_id fake_greybus_module_id = { GREYBUS_DEVICE(0x42, 0x42) }; @@ -235,19 +182,8 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, dev_set_name(&gmod->dev, "%d", module_id); retval = device_add(&gmod->dev); - if (retval) - goto error; - - retval = gb_init_subdevs(gmod, &fake_greybus_module_id); - if (retval) - goto error_subdevs; - - //return gmod; - return; - -error_subdevs: - device_del(&gmod->dev); - + if (!retval) + return; /* Success */ error: gb_module_destroy(gmod); @@ -274,13 +210,6 @@ void gb_remove_module(struct greybus_host_device *hd, u8 module_id) void greybus_remove_device(struct gb_module *gmod) { - /* tear down all of the "sub device types" for this device */ - gb_i2c_disconnect(gmod); - gb_gpio_disconnect(gmod); - gb_sdio_disconnect(gmod); - gb_tty_disconnect(gmod); - gb_battery_disconnect(gmod); - device_del(&gmod->dev); put_device(&gmod->dev); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 033c7abe96f0..1970106d70a0 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -265,23 +265,6 @@ void gb_deregister_cport_complete(u16 cport_id); extern const struct attribute_group *greybus_module_groups[]; -/* - * Because we are allocating a data structure per "type" in the greybus device, - * we have static functions for this, not "dynamic" drivers like we really - * should in the end. - */ -int gb_i2c_probe(struct gb_module *gmod, const struct greybus_module_id *id); -void gb_i2c_disconnect(struct gb_module *gmod); -int gb_gpio_probe(struct gb_module *gmod, const struct greybus_module_id *id); -void gb_gpio_disconnect(struct gb_module *gmod); -int gb_sdio_probe(struct gb_module *gmod, const struct greybus_module_id *id); -void gb_sdio_disconnect(struct gb_module *gmod); -int gb_tty_probe(struct gb_module *gmod, const struct greybus_module_id *id); -void gb_tty_disconnect(struct gb_module *gmod); -int gb_battery_probe(struct gb_module *gmod, - const struct greybus_module_id *id); -void gb_battery_disconnect(struct gb_module *gmod); - int gb_tty_init(void); void gb_tty_exit(void); -- cgit v1.2.3-59-g8ed1b From 22b320f400f38afac70fff0472c4df1cf1bfeee5 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:31 -0500 Subject: greybus: add response buffer to an operation We need to track both request messages and response messages in operations. So add another gbuf (and payload pointer) field to the operation structure, and rename them to indicate which one is which. Allow the creator specify the size of the response buffer; just leave it a null pointer if the size is 0. Define a new helper function gb_operation_gbuf_create() to encapsulate creating either a request or a response buffer. Any buffer associated with a connection will (eventually) have been created as part of an operation. So stash the operation pointer in the gbuf as the context pointer. Whether a buffer is for the request or the response can be determined by pointer comparison. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 125 ++++++++++++++++++++++++++---------- drivers/staging/greybus/operation.h | 12 +++- 2 files changed, 100 insertions(+), 37 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b5351b277676..43ad24424d37 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -13,6 +13,12 @@ #include "greybus.h" +/* + * The top bit of the type in an operation message header indicates + * whether the message is a request (bit clear) or response (bit set) + */ +#define GB_OPERATION_TYPE_RESPONSE 0x80 + /* * All operation messages (both requests and responses) begin with * a common header that encodes the size of the data (header @@ -61,7 +67,7 @@ int gb_operation_wait(struct gb_operation *operation) ret = wait_for_completion_interruptible(&operation->completion); /* If interrupted, cancel the in-flight buffer */ if (ret < 0) - ret = greybus_kill_gbuf(operation->gbuf); + ret = greybus_kill_gbuf(operation->request); return ret; } @@ -80,12 +86,14 @@ int gb_operation_submit(struct gb_operation *operation, { int ret; - /* XXX - * gfp is probably GFP_ATOMIC but really I think - * the gfp mask should go away. + /* + * XXX + * I think the order of operations is going to be + * significant, and if so, we may need a mutex to surround + * setting the operation id and submitting the gbuf. */ operation->callback = callback; - ret = greybus_submit_gbuf(operation->gbuf, GFP_KERNEL); + ret = greybus_submit_gbuf(operation->request, GFP_KERNEL); if (ret) return ret; if (!callback) @@ -95,52 +103,100 @@ int gb_operation_submit(struct gb_operation *operation, } /* - * Called when a greybus request message has actually been sent. + * Called when an operation buffer completes. */ -static void gbuf_out_callback(struct gbuf *gbuf) +static void gb_operation_gbuf_complete(struct gbuf *gbuf) { - /* Record it's been submitted; need response now */ + /* TODO */ } /* - * Create a Greybus operation having a buffer big enough for an - * outgoing payload of the given size to be sent over the given - * connection. + * Allocate a buffer to be used for an operation request or response + * message. Both types of message contain a header, which is filled + * in here. W + */ +struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, + u8 type, size_t size, bool outbound) +{ + struct gb_connection *connection = operation->connection; + struct gb_operation_msg_hdr *header; + struct gbuf *gbuf; + gfp_t gfp_flags = outbound ? GFP_KERNEL : GFP_ATOMIC; + + /* Operation buffers hold a header in addition to their payload */ + size += sizeof(*header); + gbuf = greybus_alloc_gbuf(connection, gb_operation_gbuf_complete, + size, outbound, gfp_flags, operation); + if (!gbuf) + return NULL; + + /* Fill in the header structure */ + header = (struct gb_operation_msg_hdr *)gbuf->transfer_buffer; + header->size = cpu_to_le16(size); + header->id = 0; /* Filled in when submitted */ + header->type = type; + + return gbuf; +} + +/* + * Create a Greybus operation to be sent over the given connection. + * The request buffer will big enough for a payload of the given + * size. Outgoing requests must specify the size of the response + * buffer size, which must be sufficient to hold all expected + * response data. + * + * Incoming requests will supply a response size of 0, and in that + * case no response buffer is allocated. (A response always + * includes a status byte, so 0 is not a valid size.) Whatever + * handles the operation request is responsible for allocating the + * response buffer. * - * Returns a pointer to the new operation or a null pointer if a - * failure occurs due to memory exhaustion. + * Returns a pointer to the new operation or a null pointer if an + * error occurs. */ struct gb_operation *gb_operation_create(struct gb_connection *connection, - size_t size, u8 type) + u8 type, size_t request_size, + size_t response_size) { struct gb_operation *operation; - struct gb_operation_msg_hdr *header; - struct gbuf *gbuf; + gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC; + + if (!request_size) { + gb_connection_err(connection, "zero-sized request"); + return NULL; + } /* XXX Use a slab cache */ - operation = kzalloc(sizeof(*operation), GFP_KERNEL); + operation = kzalloc(sizeof(*operation), gfp_flags); if (!operation) return NULL; + operation->connection = connection; /* XXX refcount? */ - /* Our buffer holds a header in addition to the requested payload */ - size += sizeof(*header); - gbuf = greybus_alloc_gbuf(connection, gbuf_out_callback, size, - true, GFP_KERNEL, operation); - if (gbuf) { + operation->request = gb_operation_gbuf_create(operation, type, + request_size, true); + if (!operation->request) { kfree(operation); return NULL; } - gbuf->actual_length = size; /* Record what we'll use */ - - operation->connection = connection; /* XXX refcount? */ - - /* Fill in the header structure and payload pointer */ - operation->gbuf = gbuf; - header = (struct gb_operation_msg_hdr *)&gbuf->transfer_buffer; - header->size = cpu_to_le16(size); - header->id = 0; /* Filled in when submitted */ - header->type = type; - operation->payload = (char *)header + sizeof(*header); + operation->request_payload = operation->request->transfer_buffer + + sizeof(struct gb_operation_msg_hdr); + /* We always use the full request buffer */ + operation->request->actual_length = request_size; + + if (response_size) { + type |= GB_OPERATION_TYPE_RESPONSE; + operation->response = gb_operation_gbuf_create(operation, + type, response_size, false); + if (!operation->response) { + greybus_free_gbuf(operation->request); + kfree(operation); + return NULL; + } + operation->response_payload = + operation->response->transfer_buffer + + sizeof(struct gb_operation_msg_hdr); + } operation->callback = NULL; /* set at submit time */ init_completion(&operation->completion); @@ -165,7 +221,8 @@ void gb_operation_destroy(struct gb_operation *operation) list_del(&operation->links); spin_unlock_irq(&gb_operations_lock); - greybus_free_gbuf(operation->gbuf); + greybus_free_gbuf(operation->response); + greybus_free_gbuf(operation->request); kfree(operation); } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 0dff703bb2f4..8279a00dadac 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -50,17 +50,23 @@ struct gb_operation; typedef void (*gb_operation_callback)(struct gb_operation *); struct gb_operation { struct gb_connection *connection; - struct gbuf *gbuf; - void *payload; /* sender data */ + struct gbuf *request; + struct gbuf *response; + gb_operation_callback callback; /* If asynchronous */ struct completion completion; /* Used if no callback */ u8 result; struct list_head links; /* connection->operations */ + + /* These are what's used by caller */ + void *request_payload; + void *response_payload; }; struct gb_operation *gb_operation_create(struct gb_connection *connection, - size_t size, u8 type); + u8 type, size_t request_size, + size_t response_size); void gb_operation_destroy(struct gb_operation *operation); int gb_operation_wait(struct gb_operation *operation); -- cgit v1.2.3-59-g8ed1b From 84d148b10e26d55b41726c7b5a6d227f10b39b0a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:32 -0500 Subject: greybus: add gb_operation_find() Add a red-black tree indexed by operation id to a connection to allow pending operations (whose requests are in-flight) to be found when their matching response is recieved. Assign the id at the time an operation is inserted, and update the operation's message header(s) to include it. Rename gb_connection_op_id() to be more consistent with the naming conventions being used elsewhere. (Noting now that this may switch to a simple list implementation based on Greg's assertion that lists are faster than red-black trees for up to a few hundred entries.) Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 3 +- drivers/staging/greybus/connection.h | 3 +- drivers/staging/greybus/operation.c | 86 +++++++++++++++++++++++++++++++++++- drivers/staging/greybus/operation.h | 4 +- 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1a2bec22b424..740f491bf5fc 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -141,6 +141,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, spin_unlock_irq(&gb_connections_lock); INIT_LIST_HEAD(&connection->operations); + connection->pending = RB_ROOT; atomic_set(&connection->op_cycle, 0); return connection; @@ -168,7 +169,7 @@ void gb_connection_destroy(struct gb_connection *connection) kfree(connection); } -u16 gb_connection_op_id(struct gb_connection *connection) +u16 gb_connection_operation_id(struct gb_connection *connection) { return (u16)(atomic_inc_return(&connection->op_cycle) % U16_MAX); } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 89d58e5707bc..5862ce05933e 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -24,6 +24,7 @@ struct gb_connection { enum greybus_protocol protocol; struct list_head operations; + struct rb_root pending; /* awaiting reponse */ atomic_t op_cycle; void *private; @@ -36,7 +37,7 @@ void gb_connection_destroy(struct gb_connection *connection); struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, u16 cport_id); -u16 gb_connection_op_id(struct gb_connection *connection); +u16 gb_connection_operation_id(struct gb_connection *connection); __printf(2, 3) void gb_connection_err(struct gb_connection *connection, const char *fmt, ...); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 43ad24424d37..b56a2b93c6d7 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -44,6 +44,75 @@ struct gb_operation_msg_hdr { /* XXX Could be per-host device, per-module, or even per-connection */ static DEFINE_SPINLOCK(gb_operations_lock); +static void gb_operation_insert(struct gb_operation *operation) +{ + struct gb_connection *connection = operation->connection; + struct rb_root *root = &connection->pending; + struct rb_node *node = &operation->node; + struct rb_node **link = &root->rb_node; + struct rb_node *above = NULL; + struct gb_operation_msg_hdr *header; + __le16 wire_id; + + /* + * Assign the operation's id, and store it in the header of + * both request and response message headers. + */ + operation->id = gb_connection_operation_id(connection); + wire_id = cpu_to_le16(operation->id); + header = operation->request->transfer_buffer; + header->id = wire_id; + + /* OK, insert the operation into its connection's tree */ + spin_lock_irq(&gb_operations_lock); + + while (*link) { + struct gb_operation *other; + + above = *link; + other = rb_entry(above, struct gb_operation, node); + header = other->request->transfer_buffer; + if (other->id > operation->id) + link = &above->rb_left; + else if (other->id < operation->id) + link = &above->rb_right; + } + rb_link_node(node, above, link); + rb_insert_color(node, root); + + spin_unlock_irq(&gb_operations_lock); +} + +static void gb_operation_remove(struct gb_operation *operation) +{ + spin_lock_irq(&gb_operations_lock); + rb_erase(&operation->node, &operation->connection->pending); + spin_unlock_irq(&gb_operations_lock); +} + +static struct gb_operation * +gb_operation_find(struct gb_connection *connection, u16 id) +{ + struct gb_operation *operation; + struct rb_node *node; + bool found = false; + + spin_lock_irq(&gb_operations_lock); + node = connection->pending.rb_node; + while (node && !found) { + operation = rb_entry(node, struct gb_operation, node); + if (operation->id > id) + node = node->rb_left; + else if (operation->id < id) + node = node->rb_right; + else + found = true; + } + spin_unlock_irq(&gb_operations_lock); + + return found ? operation : NULL; +} + /* * An operations's response message has arrived. If no callback was * supplied it was submitted for asynchronous completion, so we notify @@ -93,6 +162,7 @@ int gb_operation_submit(struct gb_operation *operation, * setting the operation id and submitting the gbuf. */ operation->callback = callback; + gb_operation_insert(operation); ret = greybus_submit_gbuf(operation->request, GFP_KERNEL); if (ret) return ret; @@ -107,7 +177,21 @@ int gb_operation_submit(struct gb_operation *operation, */ static void gb_operation_gbuf_complete(struct gbuf *gbuf) { - /* TODO */ + struct gb_operation *operation; + struct gb_operation_msg_hdr *header; + u16 id; + + /* + * This isn't right, but it keeps things balanced until we + * can set up operation response handling. + */ + header = gbuf->transfer_buffer; + id = le16_to_cpu(header->id); + operation = gb_operation_find(gbuf->connection, id); + if (operation) + gb_operation_remove(operation); + else + gb_connection_err(gbuf->connection, "operation not found"); } /* diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 8279a00dadac..5d863ed90f8b 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -52,12 +52,14 @@ struct gb_operation { struct gb_connection *connection; struct gbuf *request; struct gbuf *response; + u16 id; + u8 result; gb_operation_callback callback; /* If asynchronous */ struct completion completion; /* Used if no callback */ - u8 result; struct list_head links; /* connection->operations */ + struct rb_node node; /* connection->pending */ /* These are what's used by caller */ void *request_payload; -- cgit v1.2.3-59-g8ed1b From d90c25b0a279b006becc36c166f27e99578409cd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:33 -0500 Subject: greybus: let operation layer examine incoming data Give the operation layer a chance to examine incoming data so that it can handle it appropriately. Treat the data as an operation message header. If it's a response, look up the operation it's associated with. If it's not, create a new operation. Copy the incoming data into the request or response buffer. The next patch adds a work queue to pick up handling the request or response from there. Get rid of gb_operation_submit(). Instead, we have two functions, one for sending an operation's request message, the other for sending an operation's response message. Not fully functional yet, still just filling things in... Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 1 + drivers/staging/greybus/operation.c | 132 +++++++++++++++++++++++++++--------- drivers/staging/greybus/operation.h | 7 ++ 3 files changed, 108 insertions(+), 32 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 6580deb5dbe8..97b45e9c84ba 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -167,6 +167,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, "nonexistent connection (%zu bytes dropped)\n", length); return; } + gb_connection_operation_recv(connection, data, length); /* first check to see if we have a cport handler for this cport */ ch = &cport_handler[cport_id]; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b56a2b93c6d7..092ceb696a59 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -19,6 +19,11 @@ */ #define GB_OPERATION_TYPE_RESPONSE 0x80 +/* + * XXX This needs to be coordinated with host driver parameters + */ +#define GB_OPERATION_MESSAGE_SIZE_MAX 4096 + /* * All operation messages (both requests and responses) begin with * a common header that encodes the size of the data (header @@ -93,7 +98,7 @@ static void gb_operation_remove(struct gb_operation *operation) static struct gb_operation * gb_operation_find(struct gb_connection *connection, u16 id) { - struct gb_operation *operation; + struct gb_operation *operation = NULL; struct rb_node *node; bool found = false; @@ -121,10 +126,12 @@ gb_operation_find(struct gb_connection *connection, u16 id) */ void gb_operation_complete(struct gb_operation *operation) { + /* XXX Should probably report bad status if no callback */ if (operation->callback) operation->callback(operation); else complete_all(&operation->completion); + gb_operation_destroy(operation); } /* @@ -141,42 +148,12 @@ int gb_operation_wait(struct gb_operation *operation) } -/* - * Submit an outbound operation. The caller has filled in any - * payload so the request message is ready to go. If non-null, - * the callback function supplied will be called when the response - * message has arrived indicating the operation is complete. A null - * callback function is used for a synchronous request; return from - * this function won't occur until the operation is complete (or an - * interrupt occurs). - */ -int gb_operation_submit(struct gb_operation *operation, - gb_operation_callback callback) -{ - int ret; - - /* - * XXX - * I think the order of operations is going to be - * significant, and if so, we may need a mutex to surround - * setting the operation id and submitting the gbuf. - */ - operation->callback = callback; - gb_operation_insert(operation); - ret = greybus_submit_gbuf(operation->request, GFP_KERNEL); - if (ret) - return ret; - if (!callback) - ret = gb_operation_wait(operation); - - return ret; -} - /* * Called when an operation buffer completes. */ static void gb_operation_gbuf_complete(struct gbuf *gbuf) { + /* Don't do anything */ struct gb_operation *operation; struct gb_operation_msg_hdr *header; u16 id; @@ -310,3 +287,94 @@ void gb_operation_destroy(struct gb_operation *operation) kfree(operation); } + +/* + * Send an operation request message. The caller has filled in + * any payload so the request message is ready to go. If non-null, + * the callback function supplied will be called when the response + * message has arrived indicating the operation is complete. A null + * callback function is used for a synchronous request; return from + * this function won't occur until the operation is complete (or an + * interrupt occurs). + */ +int gb_operation_request_send(struct gb_operation *operation, + gb_operation_callback callback) +{ + int ret; + + /* + * XXX + * I think the order of operations is going to be + * significant, and if so, we may need a mutex to surround + * setting the operation id and submitting the gbuf. + */ + operation->callback = callback; + gb_operation_insert(operation); + ret = greybus_submit_gbuf(operation->request, GFP_KERNEL); + if (ret) + return ret; + if (!callback) + ret = gb_operation_wait(operation); + + return ret; +} + +/* + * Send a response for an incoming operation request. + */ +int gb_operation_response_send(struct gb_operation *operation) +{ + /* XXX + * Caller needs to have set operation->response->actual_length + */ + gb_operation_remove(operation); + gb_operation_destroy(operation); + + return 0; +} + +void gb_connection_operation_recv(struct gb_connection *connection, + void *data, size_t size) +{ + struct gb_operation_msg_hdr *header; + struct gb_operation *operation; + struct gbuf *gbuf; + u16 msg_size; + + if (size > GB_OPERATION_MESSAGE_SIZE_MAX) { + gb_connection_err(connection, "message too big"); + return; + } + + header = data; + msg_size = le16_to_cpu(header->size); + if (header->type & GB_OPERATION_TYPE_RESPONSE) { + u16 id = le16_to_cpu(header->id); + + operation = gb_operation_find(connection, id); + if (!operation) { + gb_connection_err(connection, "operation not found"); + return; + } + gb_operation_remove(operation); + gbuf = operation->response; + if (size > gbuf->transfer_buffer_length) { + gb_connection_err(connection, "recv buffer too small"); + return; + } + } else { + WARN_ON(msg_size != size); + operation = gb_operation_create(connection, header->type, + msg_size, 0); + if (!operation) { + gb_connection_err(connection, "can't create operation"); + return; + } + gbuf = operation->request; + } + + memcpy(gbuf->transfer_buffer, data, msg_size); + gbuf->actual_length = msg_size; + + /* XXX And now we let a work queue handle the rest */ +} diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 5d863ed90f8b..2ed708a83967 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -66,11 +66,18 @@ struct gb_operation { void *response_payload; }; +void gb_connection_operation_recv(struct gb_connection *connection, + void *data, size_t size); + struct gb_operation *gb_operation_create(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size); void gb_operation_destroy(struct gb_operation *operation); +int gb_operation_request_send(struct gb_operation *operation, + gb_operation_callback callback); +int gb_operation_response_send(struct gb_operation *operation); + int gb_operation_wait(struct gb_operation *operation); void gb_operation_complete(struct gb_operation *operation); -- cgit v1.2.3-59-g8ed1b From 2eb585f8df3f2121751ff8cf9b2cd8040575bff2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:34 -0500 Subject: greybus: move receive handling to operation layer Create a work queue to do the bulk of processing of received operation request or response messages. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 10 +++ drivers/staging/greybus/operation.c | 143 +++++++++++++++++++++++++++--------- drivers/staging/greybus/operation.h | 5 ++ 3 files changed, 123 insertions(+), 35 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 720ab47033fb..b5f666a6255e 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -284,6 +284,12 @@ static int __init gb_init(void) goto error_gbuf; } + retval = gb_operation_init(); + if (retval) { + pr_err("gb_operation_init failed\n"); + goto error_operation; + } + retval = gb_tty_init(); if (retval) { pr_err("gb_tty_init failed\n"); @@ -293,6 +299,9 @@ static int __init gb_init(void) return 0; error_tty: + gb_operation_exit(); + +error_operation: gb_gbuf_exit(); error_gbuf: @@ -310,6 +319,7 @@ error_bus: static void __exit gb_exit(void) { gb_tty_exit(); + gb_operation_exit(); gb_gbuf_exit(); gb_ap_exit(); bus_unregister(&greybus_bus_type); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 092ceb696a59..7753bf748e2c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -24,6 +24,9 @@ */ #define GB_OPERATION_MESSAGE_SIZE_MAX 4096 +/* Workqueue to handle Greybus operation completions. */ +static struct workqueue_struct *gb_operation_recv_workqueue; + /* * All operation messages (both requests and responses) begin with * a common header that encodes the size of the data (header @@ -126,16 +129,13 @@ gb_operation_find(struct gb_connection *connection, u16 id) */ void gb_operation_complete(struct gb_operation *operation) { - /* XXX Should probably report bad status if no callback */ if (operation->callback) operation->callback(operation); else complete_all(&operation->completion); - gb_operation_destroy(operation); } -/* - * Wait for a submitted operatnoi to complete */ +/* Wait for a submitted operation to complete */ int gb_operation_wait(struct gb_operation *operation) { int ret; @@ -148,46 +148,100 @@ int gb_operation_wait(struct gb_operation *operation) } + +typedef void (*gb_operation_recv_handler)(struct gb_operation *operation); +static gb_operation_recv_handler gb_operation_recv_handlers[] = { + [GREYBUS_PROTOCOL_CONTROL] = NULL, + [GREYBUS_PROTOCOL_AP] = NULL, + [GREYBUS_PROTOCOL_GPIO] = NULL, + [GREYBUS_PROTOCOL_I2C] = NULL, + [GREYBUS_PROTOCOL_UART] = NULL, + [GREYBUS_PROTOCOL_HID] = NULL, + [GREYBUS_PROTOCOL_VENDOR] = NULL, +}; + +static void gb_operation_request_handle(struct gb_operation *operation) +{ + u8 protocol = operation->connection->protocol; + + /* Subtract one from array size to stay within u8 range */ + if (protocol <= (u8)(ARRAY_SIZE(gb_operation_recv_handlers) - 1)) { + gb_operation_recv_handler handler; + + handler = gb_operation_recv_handlers[protocol]; + if (handler) { + handler(operation); /* Handle the request */ + return; + } + } + + gb_connection_err(operation->connection, "unrecognized protocol %u\n", + (unsigned int)protocol); + operation->result = GB_OP_PROTOCOL_BAD; + gb_operation_complete(operation); +} + /* - * Called when an operation buffer completes. + * Either this operation contains an incoming request, or its + * response has arrived. An incoming request will have a null + * response buffer pointer (it is the responsibility of the request + * handler to allocate and fill in the response buffer). */ -static void gb_operation_gbuf_complete(struct gbuf *gbuf) +static void gb_operation_recv_work(struct work_struct *recv_work) { - /* Don't do anything */ struct gb_operation *operation; - struct gb_operation_msg_hdr *header; - u16 id; + bool incoming_request; - /* - * This isn't right, but it keeps things balanced until we - * can set up operation response handling. - */ - header = gbuf->transfer_buffer; - id = le16_to_cpu(header->id); - operation = gb_operation_find(gbuf->connection, id); - if (operation) - gb_operation_remove(operation); + operation = container_of(recv_work, struct gb_operation, recv_work); + incoming_request = operation->response == NULL; + if (incoming_request) + gb_operation_request_handle(operation); + gb_operation_complete(operation); + + /* We're finished with the buffer we read into */ + if (incoming_request) + greybus_gbuf_finished(operation->request); else - gb_connection_err(gbuf->connection, "operation not found"); + greybus_gbuf_finished(operation->response); +} + +/* + * Buffer completion function. We get notified whenever any buffer + * completes. For outbound messages, this tells us that the message + * has been sent. For inbound messages, it means the data has + * landed in the buffer and is ready to be processed. + * + * Either way, we don't do anything. We don't really care when an + * outbound message has been sent, and for incoming messages we + * we'll be done with everything we need to do before we mark it + * finished. + * + * XXX We may want to record that a buffer is (or is no longer) in flight. + */ +static void gb_operation_gbuf_complete(struct gbuf *gbuf) +{ + return; } /* * Allocate a buffer to be used for an operation request or response - * message. Both types of message contain a header, which is filled - * in here. W + * message. For outgoing messages, both types of message contain a + * common header, which is filled in here. Incoming requests or + * responses also contain the same header, but there's no need to + * initialize it here (it'll be overwritten by the incoming + * message). */ struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, - u8 type, size_t size, bool outbound) + u8 type, size_t size, bool data_out) { struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; struct gbuf *gbuf; - gfp_t gfp_flags = outbound ? GFP_KERNEL : GFP_ATOMIC; + gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC; - /* Operation buffers hold a header in addition to their payload */ size += sizeof(*header); gbuf = greybus_alloc_gbuf(connection, gb_operation_gbuf_complete, - size, outbound, gfp_flags, operation); + size, data_out, gfp_flags, operation); if (!gbuf) return NULL; @@ -222,11 +276,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, { struct gb_operation *operation; gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC; - - if (!request_size) { - gb_connection_err(connection, "zero-sized request"); - return NULL; - } + bool outgoing = response_size != 0; /* XXX Use a slab cache */ operation = kzalloc(sizeof(*operation), gfp_flags); @@ -235,7 +285,8 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, operation->connection = connection; /* XXX refcount? */ operation->request = gb_operation_gbuf_create(operation, type, - request_size, true); + request_size, + outgoing); if (!operation->request) { kfree(operation); return NULL; @@ -245,10 +296,11 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, /* We always use the full request buffer */ operation->request->actual_length = request_size; - if (response_size) { + if (outgoing) { type |= GB_OPERATION_TYPE_RESPONSE; operation->response = gb_operation_gbuf_create(operation, - type, response_size, false); + type, response_size, + false); if (!operation->response) { greybus_free_gbuf(operation->request); kfree(operation); @@ -259,6 +311,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, sizeof(struct gb_operation_msg_hdr); } + INIT_WORK(&operation->recv_work, gb_operation_recv_work); operation->callback = NULL; /* set at submit time */ init_completion(&operation->completion); @@ -333,6 +386,11 @@ int gb_operation_response_send(struct gb_operation *operation) return 0; } +/* + * Handle data arriving on a connection. This is called in + * interrupt context, so just copy the incoming data into a buffer + * and do remaining handling via a work queue. + */ void gb_connection_operation_recv(struct gb_connection *connection, void *data, size_t size) { @@ -354,7 +412,7 @@ void gb_connection_operation_recv(struct gb_connection *connection, operation = gb_operation_find(connection, id); if (!operation) { gb_connection_err(connection, "operation not found"); - return; + return; } gb_operation_remove(operation); gbuf = operation->response; @@ -376,5 +434,20 @@ void gb_connection_operation_recv(struct gb_connection *connection, memcpy(gbuf->transfer_buffer, data, msg_size); gbuf->actual_length = msg_size; - /* XXX And now we let a work queue handle the rest */ + /* The rest will be handled in work queue context */ + queue_work(gb_operation_recv_workqueue, &operation->recv_work); +} + +int gb_operation_init(void) +{ + gb_operation_recv_workqueue = alloc_workqueue("greybus_recv", 0, 1); + if (!gb_operation_recv_workqueue) + return -ENOMEM; + + return 0; +} + +void gb_operation_exit(void) +{ + destroy_workqueue(gb_operation_recv_workqueue); } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 2ed708a83967..d5ec582e21a2 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -16,6 +16,7 @@ enum gb_operation_status { GB_OP_INVALID = 1, GB_OP_NO_MEMORY = 2, GB_OP_INTERRUPTED = 3, + GB_OP_PROTOCOL_BAD = 4, }; /* @@ -55,6 +56,7 @@ struct gb_operation { u16 id; u8 result; + struct work_struct recv_work; gb_operation_callback callback; /* If asynchronous */ struct completion completion; /* Used if no callback */ @@ -81,4 +83,7 @@ int gb_operation_response_send(struct gb_operation *operation); int gb_operation_wait(struct gb_operation *operation); void gb_operation_complete(struct gb_operation *operation); +int gb_operation_init(void); +void gb_operation_exit(void); + #endif /* !__OPERATION_H */ -- cgit v1.2.3-59-g8ed1b From 574341c67213afdc9dcb5aefb4f6fa84644438bb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:35 -0500 Subject: greybus: add device initialization Set up the infrastructure for initializing connections based on their protocol. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 24 +++++++++++++++++++++ drivers/staging/greybus/connection.h | 2 ++ drivers/staging/greybus/core.c | 8 ++++--- drivers/staging/greybus/greybus.h | 2 ++ drivers/staging/greybus/interface.c | 15 +++++++++++++ drivers/staging/greybus/interface.h | 2 ++ drivers/staging/greybus/module.c | 41 ++++++++++++++++++++++++------------ drivers/staging/greybus/module.h | 2 ++ 8 files changed, 79 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 740f491bf5fc..0143a4f6bd12 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -191,3 +191,27 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) va_end(args); } + +/* + * XXX Protocols should have a set of function pointers: + * ->init (called here, to initialize the device) + * ->input_handler + * ->exit (reverse of init) + */ +int gb_connection_init(struct gb_connection *connection) +{ + switch (connection->protocol) { + case GREYBUS_PROTOCOL_I2C: + case GREYBUS_PROTOCOL_CONTROL: + case GREYBUS_PROTOCOL_AP: + case GREYBUS_PROTOCOL_GPIO: + case GREYBUS_PROTOCOL_UART: + case GREYBUS_PROTOCOL_HID: + case GREYBUS_PROTOCOL_VENDOR: + default: + gb_connection_err(connection, "unimplemented protocol %u", + (u32)connection->protocol); + break; + } + return -ENXIO; +} diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 5862ce05933e..bb22c52c2f01 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -34,6 +34,8 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, u16 cport_id, enum greybus_protocol protocol); void gb_connection_destroy(struct gb_connection *connection); +int gb_connection_init(struct gb_connection *connection); + struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, u16 cport_id); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index b5f666a6255e..38f867db9411 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -182,8 +182,11 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, dev_set_name(&gmod->dev, "%d", module_id); retval = device_add(&gmod->dev); - if (!retval) - return; /* Success */ + if (retval) + goto error; + + gb_module_interfaces_init(gmod); + return; error: gb_module_destroy(gmod); @@ -253,7 +256,6 @@ void greybus_remove_hd(struct greybus_host_device *hd) } EXPORT_SYMBOL_GPL(greybus_remove_hd); - static int __init gb_init(void) { int retval; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 1970106d70a0..d92ba5178cf1 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -265,6 +265,8 @@ void gb_deregister_cport_complete(u16 cport_id); extern const struct attribute_group *greybus_module_groups[]; +int gb_i2c_device_init(struct gb_connection *connection); + int gb_tty_init(void); void gb_tty_exit(void); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index b9dd93093cc6..0c2fdd3f7ea2 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -57,3 +57,18 @@ void gb_interface_destroy(struct gb_interface *interface) /* kref_put(gmod); */ kfree(interface); } + +int gb_interface_connections_init(struct gb_interface *interface) +{ + struct gb_connection *connection; + int ret = 0; + + list_for_each_entry(connection, &interface->connections, + interface_links) { + ret = gb_connection_init(connection); + if (ret) + break; + } + + return ret; +} diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 9c9ffe7715a5..1019a981f5f6 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -22,4 +22,6 @@ struct gb_interface { struct gb_interface *gb_interface_create(struct gb_module *gmod, u8 module_id); void gb_interface_destroy(struct gb_interface *interface); +int gb_interface_connections_init(struct gb_interface *interface); + #endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 1970e7b01081..699cd003e367 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -55,39 +55,52 @@ const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, */ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) { - struct gb_module *module; + struct gb_module *gmod; - module = kzalloc(sizeof(*module), GFP_KERNEL); - if (!module) + gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); + if (!gmod) return NULL; - module->hd = hd; /* XXX refcount? */ - module->module_id = module_id; - INIT_LIST_HEAD(&module->interfaces); + gmod->hd = hd; /* XXX refcount? */ + gmod->module_id = module_id; + INIT_LIST_HEAD(&gmod->interfaces); spin_lock_irq(&gb_modules_lock); - list_add_tail(&module->links, &hd->modules); + list_add_tail(&gmod->links, &hd->modules); spin_unlock_irq(&gb_modules_lock); - return module; + return gmod; } /* * Tear down a previously set up module. */ -void gb_module_destroy(struct gb_module *module) +void gb_module_destroy(struct gb_module *gmod) { - if (WARN_ON(!module)) + if (WARN_ON(!gmod)) return; - kfree(module->product_string); - kfree(module->vendor_string); + kfree(gmod->product_string); + kfree(gmod->vendor_string); spin_lock_irq(&gb_modules_lock); - list_del(&module->links); + list_del(&gmod->links); spin_unlock_irq(&gb_modules_lock); /* kref_put(module->hd); */ - kfree(module); + kfree(gmod); +} + +void gb_module_interfaces_init(struct gb_module *gmod) +{ + struct gb_interface *interface; + int ret = 0; + + list_for_each_entry(interface, &gmod->interfaces, links) { + ret = gb_interface_connections_init(interface); + if (ret) + dev_err(gmod->hd->parent, + "module interface init error %d\n", ret); + } } diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 7b01950fa36e..114f15750380 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -62,4 +62,6 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id); void gb_module_destroy(struct gb_module *module); +void gb_module_interfaces_init(struct gb_module *gmod); + #endif /* __MODULE_H */ -- cgit v1.2.3-59-g8ed1b From c149f8ffb2cbd1fa77f2c964bc547577835af647 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:36 -0500 Subject: greybus: kill off gbuf work queue At this point all incoming messages are handled by the operation code, so this obviates the need for the gbuf workqueue. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 25 +------------------------ drivers/staging/greybus/greybus.h | 1 - 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 97b45e9c84ba..12c8c23e23ab 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -15,17 +15,11 @@ #include #include #include -#include #include "greybus.h" -static void cport_process_event(struct work_struct *work); - static struct kmem_cache *gbuf_head_cache; -/* Workqueue to handle Greybus buffer completions. */ -static struct workqueue_struct *gbuf_workqueue; - /** * greybus_alloc_gbuf - allocate a greybus buffer * @@ -57,7 +51,6 @@ struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, kref_init(&gbuf->kref); gbuf->connection = connection; - INIT_WORK(&gbuf->event, cport_process_event); gbuf->outbound = outbound; gbuf->complete = complete; gbuf->context = context; @@ -114,15 +107,6 @@ int greybus_kill_gbuf(struct gbuf *gbuf) return -ENOMEM; } -static void cport_process_event(struct work_struct *work) -{ - struct gbuf *gbuf = container_of(work, struct gbuf, event); - - /* Call the completion handler, then drop our reference */ - gbuf->complete(gbuf); - greybus_put_gbuf(gbuf); -} - #define MAX_CPORTS 1024 struct gb_cport_handler { gbuf_complete_t handler; @@ -196,24 +180,18 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, */ memcpy(gbuf->transfer_buffer, data, length); gbuf->actual_length = length; - - queue_work(gbuf_workqueue, &gbuf->event); } EXPORT_SYMBOL_GPL(greybus_cport_in); /* Can be called in interrupt context, do the work and get out of here */ void greybus_gbuf_finished(struct gbuf *gbuf) { - queue_work(gbuf_workqueue, &gbuf->event); + gbuf->complete(gbuf); } EXPORT_SYMBOL_GPL(greybus_gbuf_finished); int gb_gbuf_init(void) { - gbuf_workqueue = alloc_workqueue("greybus_gbuf", 0, 1); - if (!gbuf_workqueue) - return -ENOMEM; - gbuf_head_cache = kmem_cache_create("gbuf_head_cache", sizeof(struct gbuf), 0, 0, NULL); return 0; @@ -221,6 +199,5 @@ int gb_gbuf_init(void) void gb_gbuf_exit(void) { - destroy_workqueue(gbuf_workqueue); kmem_cache_destroy(gbuf_head_cache); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index d92ba5178cf1..854122b85cbe 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -134,7 +134,6 @@ struct gbuf { bool outbound; /* AP-relative data direction */ void *context; - struct work_struct event; gbuf_complete_t complete; }; -- cgit v1.2.3-59-g8ed1b From 98d35ba22e6328491559892e796f276de7ba3d00 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:37 -0500 Subject: greybus: kill old cport handlers The original CPort message handlers are not needed. All incoming data is passed to handlers based on the protocol used over the connection over which the data was transferred. So get rid of the old CPort handler code. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 12c8c23e23ab..348ee7c27a07 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -141,8 +141,6 @@ void gb_deregister_cport_complete(u16 cport_id) void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length) { - struct gb_cport_handler *ch; - struct gbuf *gbuf; struct gb_connection *connection; connection = gb_hd_connection_find(hd, cport_id); @@ -152,34 +150,6 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, return; } gb_connection_operation_recv(connection, data, length); - - /* first check to see if we have a cport handler for this cport */ - ch = &cport_handler[cport_id]; - if (!ch->handler) { - /* Ugh, drop the data on the floor, after logging it... */ - dev_err(hd->parent, - "Received data for cport %d, but no handler!\n", - cport_id); - return; - } - - gbuf = greybus_alloc_gbuf(connection, ch->handler, length, false, - GFP_ATOMIC, ch->context); - - if (!gbuf) { - /* Again, something bad went wrong, log it... */ - pr_err("can't allocate gbuf???\n"); - return; - } - - /* - * FIXME: - * Very dumb copy data method for now, if this is slow (odds are it will - * be, we should move to a model where the hd "owns" all buffers, but we - * want something up and working first for now. - */ - memcpy(gbuf->transfer_buffer, data, length); - gbuf->actual_length = length; } EXPORT_SYMBOL_GPL(greybus_cport_in); -- cgit v1.2.3-59-g8ed1b From ed8800dc376edd7aa10815fb9df83a66c7557031 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:38 -0500 Subject: greybus: add i2c driver This patch adds the i2c driver, based on the use of Greybus operations over Greybus connections. It basically replaces almost all of what was previously found in "i2c-gb.c". When gb_connection_device_init(connection) is called, any connection that talks the GREYBUS_PROTOCOL_I2C is passed to gb_i2c_device_init() to be initialized. Initialization involves verifying the code is able to support the version of the protocol. For I2C, we then query the functionality mask, and set the retry count and timeout to default values. After that, we set up the i2c device and associate it with the connection. The i2c_algorithm methods are then implemented by translating them into Greybus operations. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 1 + drivers/staging/greybus/i2c-gb.c | 533 ++++++++++++++++++++++++++++++----- drivers/staging/greybus/operation.c | 10 +- 3 files changed, 471 insertions(+), 73 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 0143a4f6bd12..fc692f0ad2eb 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -202,6 +202,7 @@ int gb_connection_init(struct gb_connection *connection) { switch (connection->protocol) { case GREYBUS_PROTOCOL_I2C: + return gb_i2c_device_init(connection); case GREYBUS_PROTOCOL_CONTROL: case GREYBUS_PROTOCOL_AP: case GREYBUS_PROTOCOL_GPIO: diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 0da195898ad4..c4e47effa9f5 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -14,127 +14,516 @@ #include "greybus.h" struct gb_i2c_device { - struct i2c_adapter *adapter; - struct gb_module *gmod; + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + + u32 functionality; + u16 timeout_msec; + u8 retries; + + struct i2c_adapter *adapter; +}; + +/* Version of the Greybus i2c protocol we support */ +#define GB_I2C_VERSION_MAJOR 0x00 +#define GB_I2C_VERSION_MINOR 0x01 + +/* Greybus i2c request types */ +#define GB_I2C_TYPE_INVALID 0x00 +#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2C_TYPE_FUNCTIONALITY 0x02 +#define GB_I2C_TYPE_TIMEOUT 0x03 +#define GB_I2C_TYPE_RETRIES 0x04 +#define GB_I2C_TYPE_TRANSFER 0x05 +#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +#define GB_I2C_RETRIES_DEFAULT 3 +#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ + +/* version request has no payload */ +struct gb_i2c_proto_version_response { + __u8 status; + __u8 major; + __u8 minor; +}; + +/* functionality request has no payload */ +struct gb_i2c_functionality_response { + __u8 status; + __le32 functionality; }; -static const struct greybus_module_id id_table[] = { - { GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */ - { }, /* terminating NULL entry */ +struct gb_i2c_timeout_request { + __le16 msec; +}; +struct gb_i2c_timeout_response { + __u8 status; +}; + +struct gb_i2c_retries_request { + __u8 retries; +}; +struct gb_i2c_retries_response { + __u8 status; +}; + +/* + * Outgoing data immediately follows the op count and ops array. + * The data for each write (master -> slave) op in the array is sent + * in order, with no (e.g. pad) bytes separating them. + * + * Short reads cause the entire transfer request to fail So response + * payload consists only of bytes read, and the number of bytes is + * exactly what was specified in the corresponding op. Like + * outgoing data, the incoming data is in order and contiguous. + */ +struct gb_i2c_transfer_op { + __le16 addr; + __le16 flags; + __le16 size; }; -/* We BETTER be able to do SMBUS protocl calls, otherwise we are bit-banging the - * slowest thing possible over the fastest bus possible, crazy... - * FIXME - research this, for now just assume we can +struct gb_i2c_transfer_request { + __le16 op_count; + struct gb_i2c_transfer_op ops[0]; /* op_count of these */ +}; +struct gb_i2c_transfer_response { + __u8 status; + __u8 data[0]; /* inbound data */ +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) +{ + struct gb_connection *connection = gb_i2c_dev->connection; + struct gb_operation *operation; + struct gb_i2c_proto_version_response *response; + int ret; + + /* A protocol version request has no payload */ + operation = gb_operation_create(connection, + GB_I2C_TYPE_PROTOCOL_VERSION, + 0, sizeof(*response)); + if (!operation) + return -ENOMEM; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("version operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "version response %hhu", + response->status); + ret = -EIO; + } else { + if (response->major > GB_I2C_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response->major, GB_I2C_VERSION_MAJOR); + ret = -ENOTSUPP; + goto out; + } + gb_i2c_dev->version_major = response->major; + gb_i2c_dev->version_minor = response->minor; + } +out: + + gb_operation_destroy(operation); + + return ret; +} + +/* + * Map Greybus i2c functionality bits into Linux ones */ +static u32 gb_i2c_functionality_map(u32 gb_i2c_functionality) +{ + return gb_i2c_functionality; /* All bits the same for now */ +} + +static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) +{ + struct gb_connection *connection = gb_i2c_dev->connection; + struct gb_operation *operation; + struct gb_i2c_functionality_response *response; + u32 functionality; + int ret; + + /* A functionality request has no payload */ + operation = gb_operation_create(connection, + GB_I2C_TYPE_FUNCTIONALITY, + 0, sizeof(*response)); + if (!operation) + return -ENOMEM; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("functionality operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "functionality response %hhu", + response->status); + ret = -EIO; + } else { + functionality = le32_to_cpu(response->functionality); + gb_i2c_dev->functionality = + gb_i2c_functionality_map(functionality); + } +out: + gb_operation_destroy(operation); + return ret; +} -static s32 i2c_gb_access(struct i2c_adapter *adap, u16 addr, - unsigned short flags, char read_write, u8 command, - int size, union i2c_smbus_data *data) +static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) +{ + struct gb_connection *connection = gb_i2c_dev->connection; + struct gb_operation *operation; + struct gb_i2c_timeout_request *request; + struct gb_i2c_timeout_response *response; + int ret; + + operation = gb_operation_create(connection, GB_I2C_TYPE_TIMEOUT, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->msec = cpu_to_le16(msec); + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("timeout operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "timeout response %hhu", + response->status); + ret = -EIO; + } else { + gb_i2c_dev->timeout_msec = msec; + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, + u8 retries) +{ + struct gb_connection *connection = gb_i2c_dev->connection; + struct gb_operation *operation; + struct gb_i2c_retries_request *request; + struct gb_i2c_retries_response *response; + int ret; + + operation = gb_operation_create(connection, GB_I2C_TYPE_RETRIES, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->retries = retries; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("retries operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "retries response %hhu", + response->status); + ret = -EIO; + } else { + gb_i2c_dev->retries = retries; + } +out: + gb_operation_destroy(operation); + + return ret; +} + + +/* + * Map Linux i2c_msg flags into Greybus i2c transfer op flags. + */ +static u16 gb_i2c_transfer_op_flags_map(u16 flags) +{ + return flags; /* All flags the same for now */ +} + +static void +gb_i2c_fill_transfer_op(struct gb_i2c_transfer_op *op, struct i2c_msg *msg) +{ + u16 flags = gb_i2c_transfer_op_flags_map(msg->flags); + + op->addr = cpu_to_le16(msg->addr); + op->flags = cpu_to_le16(flags); + op->size = cpu_to_le16(msg->len); +} + +static struct gb_operation * +gb_i2c_transfer_request(struct gb_connection *connection, + struct i2c_msg *msgs, u32 msg_count) +{ + struct gb_i2c_transfer_request *request; + struct gb_operation *operation; + struct gb_i2c_transfer_op *op; + struct i2c_msg *msg; + u32 data_out_size = 0; + u32 data_in_size = 1; /* Response begins with a status byte */ + size_t request_size; + void *data; + u16 op_count; + u32 i; + + if (msg_count > (u32)U16_MAX) { + gb_connection_err(connection, "msg_count (%u) too big", + msg_count); + return NULL; + } + op_count = (u16)msg_count; + + /* + * In addition to space for all message descriptors we need + * to have enough to hold all outbound message data. + */ + msg = msgs; + for (i = 0; i < msg_count; i++, msg++) + if (msg->flags & I2C_M_RD) + data_in_size += (u32)msg->len; + else + data_out_size += (u32)msg->len; + + request_size = sizeof(struct gb_i2c_transfer_request); + request_size += msg_count * sizeof(struct gb_i2c_transfer_op); + request_size += data_out_size; + + /* Response consists only of incoming data */ + operation = gb_operation_create(connection, GB_I2C_TYPE_TRANSFER, + request_size, data_in_size); + if (!operation) + return NULL; + + request = operation->request_payload; + request->op_count = cpu_to_le16(op_count); + /* Fill in the ops array */ + op = &request->ops[0]; + msg = msgs; + for (i = 0; i < msg_count; i++) + gb_i2c_fill_transfer_op(op++, msg++); + + if (!data_out_size) + return operation; + + /* Copy over the outgoing data; it starts after the last op */ + data = op; + msg = msgs; + for (i = 0; i < msg_count; i++) { + if (!(msg->flags & I2C_M_RD)) { + memcpy(data, msg->buf, msg->len); + data += msg->len; + } + msg++; + } + + return operation; +} + +static void gb_i2c_transfer_response(struct i2c_msg *msgs, u32 msg_count, + void *data) +{ + struct i2c_msg *msg = msgs; + u32 i; + + for (i = 0; i < msg_count; i++) { + if (msg->flags & I2C_M_RD) { + memcpy(msg->buf, data, msg->len); + data += msg->len; + } + msg++; + } +} + +static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, + struct i2c_msg *msgs, u32 msg_count) +{ + struct gb_connection *connection = gb_i2c_dev->connection; + struct gb_i2c_transfer_response *response; + struct gb_operation *operation; + int ret; + + operation = gb_i2c_transfer_request(connection, msgs, msg_count); + if (!operation) + return -ENOMEM; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("transfer operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "transfer response %hhu", + response->status); + ret = -EIO; + } else { + gb_i2c_transfer_response(msgs, msg_count, response->data); + ret = msg_count; + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int msg_count) +{ + struct gb_i2c_device *gb_i2c_dev; + + gb_i2c_dev = i2c_get_adapdata(adap); + + return gb_i2c_transfer_operation(gb_i2c_dev, msgs, msg_count); +} + +#if 0 +/* Later */ +static int gb_i2c_smbus_xfer(struct i2c_adapter *adap, + u16 addr, unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) { struct gb_i2c_device *gb_i2c_dev; - struct gb_module *gmod; gb_i2c_dev = i2c_get_adapdata(adap); - gmod = gb_i2c_dev->gmod; - - // FIXME - do the actual work of sending a i2c message here... - switch (size) { - case I2C_SMBUS_QUICK: - case I2C_SMBUS_BYTE: - case I2C_SMBUS_BYTE_DATA: - case I2C_SMBUS_WORD_DATA: - case I2C_SMBUS_PROC_CALL: - case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_I2C_BLOCK_BROKEN: - case I2C_SMBUS_BLOCK_PROC_CALL: - case I2C_SMBUS_I2C_BLOCK_DATA: - default: - dev_err(&gmod->dev, "Unsupported transaction %d\n", size); - return -EOPNOTSUPP; - } return 0; } +#endif -static u32 i2c_gb_func(struct i2c_adapter *adapter) +static u32 gb_i2c_functionality(struct i2c_adapter *adap) { - // FIXME - someone figure out what we really can support, for now just guess... - return I2C_FUNC_SMBUS_QUICK | - I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA | - I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | - I2C_FUNC_SMBUS_PEC | - I2C_FUNC_SMBUS_READ_I2C_BLOCK; + struct gb_i2c_device *gb_i2c_dev = i2c_get_adapdata(adap); + + return gb_i2c_dev->functionality; } -static const struct i2c_algorithm smbus_algorithm = { - .smbus_xfer = i2c_gb_access, - .functionality = i2c_gb_func, +static const struct i2c_algorithm gb_i2c_algorithm = { + .master_xfer = gb_i2c_master_xfer, + /* .smbus_xfer = gb_i2c_smbus_xfer, */ + .functionality = gb_i2c_functionality, }; -int gb_i2c_probe(struct gb_module *gmod, - const struct greybus_module_id *id) +/* + * Do initial setup of the i2c device. This includes verifying we + * can support it (based on the protocol version it advertises). + * If that's OK, we get and cached its functionality bits, and + * set up the retry count and timeout. + * + * Note: gb_i2c_dev->connection is assumed to have been valid. + */ +static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) +{ + int ret; + + /* First thing we need to do is check the version */ + ret = gb_i2c_proto_version_operation(gb_i2c_dev); + if (ret) + return ret; + + /* Assume the functionality never changes, just get it once */ + ret = gb_i2c_functionality_operation(gb_i2c_dev); + if (ret) + return ret; + + /* Set up our default retry count and timeout */ + ret = gb_i2c_retries_operation(gb_i2c_dev, GB_I2C_RETRIES_DEFAULT); + if (ret) + return ret; + + return gb_i2c_timeout_operation(gb_i2c_dev, GB_I2C_TIMEOUT_DEFAULT); +} + +int gb_i2c_device_init(struct gb_connection *connection) { struct gb_i2c_device *gb_i2c_dev; - struct i2c_adapter *adapter; - int retval; + struct i2c_adapter *adapter = NULL; + int ret; gb_i2c_dev = kzalloc(sizeof(*gb_i2c_dev), GFP_KERNEL); if (!gb_i2c_dev) return -ENOMEM; + + gb_i2c_dev->connection = connection; /* refcount? */ + + ret = gb_i2c_device_setup(gb_i2c_dev); + if (ret) + goto out_err; + + /* Looks good; allocate and set up our i2c adapter */ adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); if (!adapter) { - kfree(gb_i2c_dev); - return -ENOMEM; + ret = -ENOMEM; + goto out_err; } - i2c_set_adapdata(adapter, gb_i2c_dev); adapter->owner = THIS_MODULE; adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; - adapter->algo = &smbus_algorithm; - adapter->dev.parent = &gmod->dev; - adapter->retries = 3; /* we have to pick something... */ + adapter->algo = &gb_i2c_algorithm; + /* adapter->algo_data = what? */ + adapter->timeout = gb_i2c_dev->timeout_msec * HZ / 1000; + adapter->retries = gb_i2c_dev->retries; + + /* XXX I think this parent device is wrong, but it uses existing code */ + adapter->dev.parent = &connection->interface->gmod->dev; snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); - retval = i2c_add_adapter(adapter); - if (retval) { - dev_err(&gmod->dev, "Can not add SMBus adapter\n"); - goto error; - } + i2c_set_adapdata(adapter, gb_i2c_dev); + + ret = i2c_add_adapter(adapter); + if (ret) + goto out_err; - gb_i2c_dev->gmod = gmod; gb_i2c_dev->adapter = adapter; + connection->private = gb_i2c_dev; - gmod->gb_i2c_dev = gb_i2c_dev; return 0; -error: +out_err: kfree(adapter); + /* kref_put(gb_i2c_dev->connection) */ kfree(gb_i2c_dev); - return retval; + + return ret; } -void gb_i2c_disconnect(struct gb_module *gmod) +void gb_i2c_device_exit(struct gb_connection *connection) { - struct gb_i2c_device *gb_i2c_dev; + struct gb_i2c_device *gb_i2c_dev = connection->private; - gb_i2c_dev = gmod->gb_i2c_dev; - if (!gb_i2c_dev) - return; i2c_del_adapter(gb_i2c_dev->adapter); kfree(gb_i2c_dev->adapter); + /* kref_put(gb_i2c_dev->connection) */ kfree(gb_i2c_dev); } #if 0 -static struct greybus_driver i2c_gb_driver = { - .probe = gb_i2c_probe, - .disconnect = gb_i2c_disconnect, - .id_table = id_table, -}; - module_greybus_driver(i2c_gb_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 7753bf748e2c..a49d92957c6b 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -148,13 +148,21 @@ int gb_operation_wait(struct gb_operation *operation) } +/* + * This handler is used if no operation response messages are ever + * expected for a given protocol. + */ +static void gb_operation_recv_none(struct gb_operation *operation) +{ + /* Nothing to do! */ +} typedef void (*gb_operation_recv_handler)(struct gb_operation *operation); static gb_operation_recv_handler gb_operation_recv_handlers[] = { [GREYBUS_PROTOCOL_CONTROL] = NULL, [GREYBUS_PROTOCOL_AP] = NULL, [GREYBUS_PROTOCOL_GPIO] = NULL, - [GREYBUS_PROTOCOL_I2C] = NULL, + [GREYBUS_PROTOCOL_I2C] = gb_operation_recv_none, [GREYBUS_PROTOCOL_UART] = NULL, [GREYBUS_PROTOCOL_HID] = NULL, [GREYBUS_PROTOCOL_VENDOR] = NULL, -- cgit v1.2.3-59-g8ed1b From bb2e1c9626c9bfe0b3558830a70681eaf1f71e2d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 16 Oct 2014 06:35:39 -0500 Subject: greybus: initial operations-based GPIO driver First cut. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 1 + drivers/staging/greybus/gpio-gb.c | 811 ++++++++++++++++++++++++++++++++--- drivers/staging/greybus/greybus.h | 1 + 3 files changed, 759 insertions(+), 54 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index fc692f0ad2eb..a51b4781a2b6 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -206,6 +206,7 @@ int gb_connection_init(struct gb_connection *connection) case GREYBUS_PROTOCOL_CONTROL: case GREYBUS_PROTOCOL_AP: case GREYBUS_PROTOCOL_GPIO: + return gb_gpio_controller_init(connection); case GREYBUS_PROTOCOL_UART: case GREYBUS_PROTOCOL_HID: case GREYBUS_PROTOCOL_VENDOR: diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index da20028e7c46..7f03676ef46f 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -12,101 +12,804 @@ #include #include "greybus.h" -struct gb_gpio_device { - struct gpio_chip chip; - struct gb_module *gmod; - struct gpio_chip *gpio; - // FIXME - some lock? +struct gb_gpio_line { + /* The following has to be an array of line_max entries */ + /* --> make them just a flags field */ + u8 active: 1, + direction: 1, /* 0 = output, 1 = input */ + value: 1; /* 0 = low, 1 = high */ + u16 debounce_usec; +}; + +struct gb_gpio_controller { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + u8 line_max; /* max line number */ + struct gb_gpio_line *lines; + + struct gpio_chip chip; + struct gpio_chip *gpio; +}; +#define gpio_chip_to_gb_gpio_controller(chip) \ + container_of(chip, struct gb_gpio_controller, chip) + +/* Version of the Greybus GPIO protocol we support */ +#define GB_GPIO_VERSION_MAJOR 0x00 +#define GB_GPIO_VERSION_MINOR 0x01 + +/* Greybus GPIO request types */ +#define GB_GPIO_TYPE_INVALID 0x00 +#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 +#define GB_GPIO_TYPE_LINE_COUNT 0x02 +#define GB_GPIO_TYPE_ACTIVATE 0x03 +#define GB_GPIO_TYPE_DEACTIVATE 0x04 +#define GB_GPIO_TYPE_GET_DIRECTION 0x05 +#define GB_GPIO_TYPE_DIRECTION_IN 0x06 +#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 +#define GB_GPIO_TYPE_GET_VALUE 0x08 +#define GB_GPIO_TYPE_SET_VALUE 0x09 +#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a +#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ + +/* version request has no payload */ +struct gb_gpio_proto_version_response { + __u8 status; + __u8 major; + __u8 minor; +}; + +/* line count request has no payload */ +struct gb_gpio_line_count_response { + __u8 status; + __u8 count; +}; + +struct gb_gpio_activate_request { + __u8 which; +}; +struct gb_gpio_activate_response { + __u8 status; +}; + +struct gb_gpio_deactivate_request { + __u8 which; +}; +struct gb_gpio_deactivate_response { + __u8 status; +}; + +struct gb_gpio_get_direction_request { + __u8 which; +}; +struct gb_gpio_get_direction_response { + __u8 status; + __u8 direction; +}; + +struct gb_gpio_direction_in_request { + __u8 which; +}; +struct gb_gpio_direction_in_response { + __u8 status; +}; + +struct gb_gpio_direction_out_request { + __u8 which; + __u8 value; +}; +struct gb_gpio_direction_out_response { + __u8 status; +}; + +struct gb_gpio_get_value_request { + __u8 status; + __u8 which; +}; +struct gb_gpio_get_value_response { + __u8 status; + __u8 value; +}; + +struct gb_gpio_set_value_request { + __u8 which; + __u8 value; +}; +struct gb_gpio_set_value_response { + __u8 status; }; -static const struct greybus_module_id id_table[] = { - { GREYBUS_DEVICE(0x44, 0x44) }, /* make shit up */ - { }, /* terminating NULL entry */ +struct gb_gpio_set_debounce_request { + __u8 which; + __le16 usec; +}; +struct gb_gpio_set_debounce_response { + __u8 status; }; -static int direction_input(struct gpio_chip *gpio, unsigned nr) + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_controller) +{ + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_proto_version_response *response; + int ret; + + /* protocol version request has no payload */ + operation = gb_operation_create(connection, + GB_GPIO_TYPE_PROTOCOL_VERSION, + 0, sizeof(*response)); + if (!operation) + return -ENOMEM; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("version operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "version response %hhu", + response->status); + ret = -EIO; + } else { + if (response->major > GB_GPIO_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response->major, GB_GPIO_VERSION_MAJOR); + ret = -ENOTSUPP; + goto out; + } + gb_gpio_controller->version_major = response->major; + gb_gpio_controller->version_minor = response->minor; +printk("%s: version_major = %u version_minor = %u\n", __func__, + gb_gpio_controller->version_major, gb_gpio_controller->version_minor); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_line_count_operation(struct gb_gpio_controller *gb_gpio_controller) { - //struct gb_gpio_device *gb_gpio_dev = container_of(gpio, struct gb_gpio_device, chip); + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_line_count_response *response; + int ret; + + /* line count request has no payload */ + operation = gb_operation_create(connection, + GB_GPIO_TYPE_LINE_COUNT, + 0, sizeof(*response)); + if (!operation) + return -ENOMEM; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("line count operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "line count response %hhu", + response->status); + ret = -EIO; + } else { + gb_gpio_controller->line_max = response->count; +printk("%s: count = %u\n", __func__, + gb_gpio_controller->line_max + 1); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_controller, + u8 which) +{ + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_activate_request *request; + struct gb_gpio_activate_response *response; + int ret; + + if (which > gb_gpio_controller->line_max) + return -EINVAL; + + /* activate response has no payload */ + operation = gb_operation_create(connection, + GB_GPIO_TYPE_ACTIVATE, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("activate operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "activate response %hhu", + response->status); + ret = -EIO; + } else { + gb_gpio_controller->lines[which].active = true; +printk("%s: %u is now active\n", __func__, which); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_controller, + u8 which) +{ + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_deactivate_request *request; + struct gb_gpio_deactivate_response *response; + int ret; + + if (which > gb_gpio_controller->line_max) + return -EINVAL; + + /* deactivate response has no payload */ + operation = gb_operation_create(connection, + GB_GPIO_TYPE_DEACTIVATE, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("deactivate operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "deactivate response %hhu", + response->status); + ret = -EIO; + } else { + gb_gpio_controller->lines[which].active = false; +printk("%s: %u is now inactive\n", __func__, which); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_controller, + u8 which) +{ + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_get_direction_request *request; + struct gb_gpio_get_direction_response *response; + int ret; + + if (which > gb_gpio_controller->line_max) + return -EINVAL; + + operation = gb_operation_create(connection, + GB_GPIO_TYPE_GET_DIRECTION, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("get direction operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "get direction response %hhu", + response->status); + ret = -EIO; + } else { + u8 direction = response->direction; + + if (direction && direction != 1) + pr_warn("gpio %u direction was %u (should be 0 or 1)\n", + which, direction); + gb_gpio_controller->lines[which].direction = direction ? 1 : 0; +printk("%s: direction of %u is %s\n", __func__, which, + direction ? "in" : "out"); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_controller, + u8 which) +{ + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_direction_in_request *request; + struct gb_gpio_direction_in_response *response; + int ret; + + if (which > gb_gpio_controller->line_max) + return -EINVAL; + + /* direction_in response has no payload */ + operation = gb_operation_create(connection, + GB_GPIO_TYPE_DIRECTION_IN, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("direction in operation failed (%d)\n", ret); + goto out; + } - // FIXME - do something there + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "direction in response %hhu", + response->status); + ret = -EIO; + } else { + gb_gpio_controller->lines[which].direction = 1; +printk("%s: direction of %u is now in\n", __func__, which); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_controller, + u8 which, bool value_high) +{ + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_direction_out_request *request; + struct gb_gpio_direction_out_response *response; + int ret; + + if (which > gb_gpio_controller->line_max) + return -EINVAL; + + /* direction_out response has no payload */ + operation = gb_operation_create(connection, + GB_GPIO_TYPE_DIRECTION_OUT, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + request->value = value_high ? 1 : 0; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("direction out operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "direction out response %hhu", + response->status); + ret = -EIO; + } else { + gb_gpio_controller->lines[which].direction = 0; +printk("%s: direction of %u is now out, value %s\n", __func__, + which, value_high ? "high" : "low"); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_controller, + u8 which) +{ + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_get_value_request *request; + struct gb_gpio_get_value_response *response; + int ret; + + if (which > gb_gpio_controller->line_max) + return -EINVAL; + + operation = gb_operation_create(connection, + GB_GPIO_TYPE_GET_VALUE, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("get value operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "get value response %hhu", + response->status); + ret = -EIO; + } else { + u8 value = response->value; + + if (value && value != 1) + pr_warn("gpio %u value was %u (should be 0 or 1)\n", + which, value); + gb_gpio_controller->lines[which].value = value ? 1 : 0; + /* XXX should this set direction to out? */ +printk("%s: value of %u is %s\n", __func__, + which, gb_gpio_controller->lines[which].value ? "high" : "low"); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_controller, + u8 which, bool value_high) +{ + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_set_value_request *request; + struct gb_gpio_set_value_response *response; + int ret; + + if (which > gb_gpio_controller->line_max) + return -EINVAL; + + /* set_value response has no payload */ + operation = gb_operation_create(connection, + GB_GPIO_TYPE_SET_VALUE, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + request->value = value_high ? 1 : 0; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("set value operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "set value response %hhu", + response->status); + ret = -EIO; + } else { + /* XXX should this set direction to out? */ + gb_gpio_controller->lines[which].value = request->value; +printk("%s: out value of %u is now %s\n", __func__, + which, gb_gpio_controller->lines[which].value ? "high" : "low"); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_controller, + u8 which, u16 debounce_usec) +{ + struct gb_connection *connection = gb_gpio_controller->connection; + struct gb_operation *operation; + struct gb_gpio_set_debounce_request *request; + struct gb_gpio_set_debounce_response *response; + int ret; + + if (which > gb_gpio_controller->line_max) + return -EINVAL; + + /* set_debounce response has no payload */ + operation = gb_operation_create(connection, + GB_GPIO_TYPE_SET_DEBOUNCE, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + request->usec = le16_to_cpu(debounce_usec); + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("set debounce operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "set debounce response %hhu", + response->status); + ret = -EIO; + } else { + gb_gpio_controller->lines[which].debounce_usec = request->usec; +printk("%s: debounce of %u is now %hu usec\n", __func__, + which, gb_gpio_controller->lines[which].debounce_usec); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset < chip->base || offset >= chip->base + chip->ngpio) + return -EINVAL; + which = (u8)(offset - chip->base); + ret = gb_gpio_activate_operation(gb_gpio_controller, which); + if (ret) + ; /* return ret; */ return 0; } -static int direction_output(struct gpio_chip *gpio, unsigned nr, int val) +static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset < chip->base || offset >= chip->base + chip->ngpio) { + pr_err("bad offset %u supplied (must be %u..%u)\n", + offset, chip->base, chip->base + chip->ngpio - 1); + return; + } + which = (u8)(offset - chip->base); + ret = gb_gpio_deactivate_operation(gb_gpio_controller, which); + if (ret) + ; /* return ret; */ +} + +static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset < chip->base || offset >= chip->base + chip->ngpio) + return -EINVAL; + which = (u8)(offset - chip->base); + ret = gb_gpio_get_direction_operation(gb_gpio_controller, which); + if (ret) + ; /* return ret; */ + return gb_gpio_controller->lines[which].direction ? 1 : 0; +} + +static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - // FIXME - do something there + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset < chip->base || offset >= chip->base + chip->ngpio) + return -EINVAL; + which = (u8)(offset - chip->base); + ret = gb_gpio_direction_in_operation(gb_gpio_controller, which); + if (ret) + ; /* return ret; */ return 0; } -static int gpio_get(struct gpio_chip *gpio, unsigned nr) +static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) { - // FIXME - do something there + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset < chip->base || offset >= chip->base + chip->ngpio) + return -EINVAL; + which = (u8)(offset - chip->base); + ret = gb_gpio_direction_out_operation(gb_gpio_controller, which, !!value); + if (ret) + ; /* return ret; */ return 0; } -static void gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset < chip->base || offset >= chip->base + chip->ngpio) + return -EINVAL; + which = (u8)(offset - chip->base); + ret = gb_gpio_get_value_operation(gb_gpio_controller, which); + if (ret) + return ret; + return (int)gb_gpio_controller->lines[which].value; +} + +static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { - // FIXME - do something there + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset < chip->base || offset >= chip->base + chip->ngpio) { + pr_err("bad offset %u supplied (must be %u..%u)\n", + offset, chip->base, chip->base + chip->ngpio - 1); + return; + } + which = (u8)(offset - chip->base); + ret = gb_gpio_set_value_operation(gb_gpio_controller, which, !!value); + if (ret) + ; /* return ret; */ } -int gb_gpio_probe(struct gb_module *gmod, - const struct greybus_module_id *id) +static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) { - struct gb_gpio_device *gb_gpio; + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u16 usec; + u8 which; + int ret; + + if (offset < chip->base || offset >= chip->base + chip->ngpio) + return -EINVAL; + which = (u8)(offset - chip->base); + if (debounce > (unsigned int)U16_MAX) + return -EINVAL; + usec = (u8)debounce; + ret = gb_gpio_set_debounce_operation(gb_gpio_controller, which, usec); + if (ret) + ; /* return ret; */ + + return 0; /* XXX */ +} + +static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + u8 which; + + if (offset < chip->base || offset >= chip->base + chip->ngpio) + return -EINVAL; + which = (u8)(offset - chip->base); + + return 0; /* XXX */ +} + +static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + return; /* XXX */ +} + +int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controller) +{ + u32 line_count; + size_t size; + int ret; + + /* First thing we need to do is check the version */ + ret = gb_gpio_proto_version_operation(gb_gpio_controller); + if (ret) + ; /* return ret; */ + + /* Now find out how many lines there are */ + ret = gb_gpio_line_count_operation(gb_gpio_controller); + if (ret) + ; /* return ret; */ + line_count = (u32)gb_gpio_controller->line_max + 1; + size = line_count * sizeof(*gb_gpio_controller->lines); + gb_gpio_controller->lines = kzalloc(size, GFP_KERNEL); + if (!gb_gpio_controller->lines) + return -ENOMEM; + + return ret; +} + +int gb_gpio_controller_init(struct gb_connection *connection) +{ + struct gb_gpio_controller *gb_gpio_controller; struct gpio_chip *gpio; - struct device *dev = &gmod->dev; - int retval; + int ret; - gb_gpio = kzalloc(sizeof(*gb_gpio), GFP_KERNEL); - if (!gb_gpio) + gb_gpio_controller = kzalloc(sizeof(*gb_gpio_controller), GFP_KERNEL); + if (!gb_gpio_controller) return -ENOMEM; - gb_gpio->gmod = gmod; + gb_gpio_controller->connection = connection; + + ret = gb_gpio_controller_setup(gb_gpio_controller); + if (ret) + goto out_err; - gpio = &gb_gpio->chip; + gpio = &gb_gpio_controller->chip; gpio->label = "greybus_gpio"; - gpio->owner = THIS_MODULE; - gpio->direction_input = direction_input; - gpio->direction_output = direction_output; - gpio->get = gpio_get; - gpio->set = gpio_set; - gpio->dbg_show = NULL; - gpio->base = 0; // FIXME!!! - gpio->ngpio = 42; // FIXME!!! - gpio->can_sleep = false; // FIXME!!! + gpio->owner = THIS_MODULE; /* XXX Module get? */ + + gpio->request = gb_gpio_request; + gpio->free = gb_gpio_free; + gpio->get_direction = gb_gpio_get_direction; + gpio->direction_input = gb_gpio_direction_input; + gpio->direction_output = gb_gpio_direction_output; + gpio->get = gb_gpio_get; + gpio->set = gb_gpio_set; + gpio->set_debounce = gb_gpio_set_debounce; + gpio->to_irq = gb_gpio_to_irq; + gpio->dbg_show = gb_gpio_dbg_show; - gmod->gb_gpio_dev = gb_gpio; + gpio->base = -1; /* Allocate base dynamically */ + gpio->ngpio = gb_gpio_controller->line_max + 1; + gpio->can_sleep = true; /* XXX */ - retval = gpiochip_add(gpio); - if (retval) { - dev_err(dev, "Failed to register GPIO\n"); - return retval; + ret = gpiochip_add(gpio); + if (ret) { + pr_err("Failed to register GPIO\n"); + return ret; } + connection->private = gb_gpio_controller; return 0; +out_err: + kfree(gb_gpio_controller); + return ret; } -void gb_gpio_disconnect(struct gb_module *gmod) +void gb_gpio_controller_exit(struct gb_connection *connection) { - struct gb_gpio_device *gb_gpio_dev; - int retval; + struct gb_gpio_controller *gb_gpio_controller = connection->private; + int ret; - gb_gpio_dev = gmod->gb_gpio_dev; - if (!gb_gpio_dev) + if (!gb_gpio_controller) return; - retval = gpiochip_remove(&gb_gpio_dev->chip); - kfree(gb_gpio_dev); + ret = gpiochip_remove(&gb_gpio_controller->chip); + /* kref_put(gb_gpio_controller->connection) */ + kfree(gb_gpio_controller); } #if 0 -static struct greybus_driver gpio_gb_driver = { - .probe = gb_gpio_probe, - .disconnect = gb_gpio_disconnect, - .id_table = id_table, -}; - -module_greybus_driver(gpio_gb_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Greybus GPIO driver"); MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 854122b85cbe..d4f5d940eeb1 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -265,6 +265,7 @@ void gb_deregister_cport_complete(u16 cport_id); extern const struct attribute_group *greybus_module_groups[]; int gb_i2c_device_init(struct gb_connection *connection); +int gb_gpio_controller_init(struct gb_connection *connection); int gb_tty_init(void); void gb_tty_exit(void); -- cgit v1.2.3-59-g8ed1b From d75286852bb3bd575bd8707f80ca0b362a7ae5a6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 17 Oct 2014 05:09:21 -0500 Subject: greybus: add write retry support for i2c It is expected that i2c writes may fail, and in that case the driver simply retries some number of times before actually treating it as a failure. Define a GB_OP_RETRY status, which is interpreted by the i2c driver as an indication a retry is in order. We just translate that into an EAGAIN error passed back to the i2c core. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c-gb.c | 10 +++++++--- drivers/staging/greybus/operation.h | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index c4e47effa9f5..e1a0ed9dc6f2 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -382,9 +382,13 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, response = operation->response_payload; if (response->status) { - gb_connection_err(connection, "transfer response %hhu", - response->status); - ret = -EIO; + if (response->status == GB_OP_RETRY) { + ret = -EAGAIN; + } else { + gb_connection_err(connection, "transfer response %hhu", + response->status); + ret = -EIO; + } } else { gb_i2c_transfer_response(msgs, msg_count, response->data); ret = msg_count; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index d5ec582e21a2..59aad3a38d17 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -16,7 +16,8 @@ enum gb_operation_status { GB_OP_INVALID = 1, GB_OP_NO_MEMORY = 2, GB_OP_INTERRUPTED = 3, - GB_OP_PROTOCOL_BAD = 4, + GB_OP_RETRY = 4, + GB_OP_PROTOCOL_BAD = 5, }; /* -- cgit v1.2.3-59-g8ed1b From bedfdf30565ef533b578d90a9dae5483347c8ea1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 17 Oct 2014 05:18:22 -0500 Subject: greybus: update gbuf status for completion handlers Currently, if a USB urb completes with an error, that error status is not transferred back to the gbuf that it's associated with. For inbound data there's not a lot we can do about an error, but for outbound data, this means there is no notification to the submitter that something went wrong. For outbound data copy the urb status directly back to the gbuf as its status. Follow USB's lead and set the status to -EINPROGRESS while a gbuf is "in flight." Assign a gbuf an initial status value of -EBADR to help identify use of never-set status values. When an inbound urb fails (SVC or CPort), currently the urb is just leaked, more or less (i.e., we lose an urb posted to receive incoming data). Change that so such an error is reported, but then re-submitted. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 18 ++++++++++++------ drivers/staging/greybus/gbuf.c | 3 +++ drivers/staging/greybus/operation.c | 1 + 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 5acf5a75e44b..21fe4fdd4103 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -274,10 +274,11 @@ static void svc_in_callback(struct urb *urb) int status = check_urb_status(urb); int retval; - if (status == -EAGAIN) + if (status) { + if (status != -EAGAIN) + dev_err(dev, "urb svc in error %d (dropped)\n", status); goto exit; - if (status) - return; + } /* We have a message, create a new message structure, add it to the * list, and wake up our thread that will process the messages. @@ -300,10 +301,12 @@ static void cport_in_callback(struct urb *urb) u8 cport; u8 *data; - if (status == -EAGAIN) + if (status) { + if (status != -EAGAIN) + dev_err(dev, "urb cport in error %d (dropped)\n", + status); goto exit; - if (status) - return; + } /* The size has to be at least one, for the cport id */ if (!urb->actual_length) { @@ -337,6 +340,9 @@ static void cport_out_callback(struct urb *urb) unsigned long flags; int i; + /* Record whether the transfer was successful */ + gbuf->status = check_urb_status(urb); + /* * See if this was an urb in our pool, if so mark it "free", otherwise * we need to free it ourselves. diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 348ee7c27a07..9b435af27cca 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -54,6 +54,7 @@ struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, gbuf->outbound = outbound; gbuf->complete = complete; gbuf->context = context; + gbuf->status = -EBADR; /* Initial value--means "never set" */ /* Host controller specific allocation for the actual buffer */ retval = connection->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); @@ -98,6 +99,8 @@ int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { struct greybus_host_device *hd = gbuf->connection->hd; + gbuf->status = -EINPROGRESS; + return hd->driver->submit_gbuf(gbuf, gfp_mask); } diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index a49d92957c6b..4d19eec790f0 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -424,6 +424,7 @@ void gb_connection_operation_recv(struct gb_connection *connection, } gb_operation_remove(operation); gbuf = operation->response; + gbuf->status = GB_OP_SUCCESS; /* If we got here we're good */ if (size > gbuf->transfer_buffer_length) { gb_connection_err(connection, "recv buffer too small"); return; -- cgit v1.2.3-59-g8ed1b From f012a520e1a8bb5d05de3307334a8de4dd95afdf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 17 Oct 2014 21:03:49 -0500 Subject: greybus: report gbuf errors If a gbuf completion indicates an error has occurred, report it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 4d19eec790f0..4cbe33e21254 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -224,10 +224,29 @@ static void gb_operation_recv_work(struct work_struct *recv_work) * we'll be done with everything we need to do before we mark it * finished. * - * XXX We may want to record that a buffer is (or is no longer) in flight. + * XXX We may want to record that a request is (or is no longer) in flight. */ static void gb_operation_gbuf_complete(struct gbuf *gbuf) { + if (gbuf->status) { + struct gb_operation *operation = gbuf->context; + struct gb_operation_msg_hdr *header; + int id; + int type; + + if (gbuf == operation->request) + header = operation->request_payload; + else if (gbuf == operation->response) + header = operation->response_payload; + else + header = NULL; + id = header ? (int)header->id : -1; + type = header ? (int)header->type : -1; + + gb_connection_err(operation->connection, + "operation %d type %d gbuf error %d", + id, type, gbuf->status); + } return; } -- cgit v1.2.3-59-g8ed1b From 6ce3e03f717820eb8edd92c9ac659e2a050727db Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Oct 2014 13:27:42 +0800 Subject: greybus: greybus_manifest.h: fix up class protocol numbers to match the spec. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 4fd27d3823a1..2f1249a63abd 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -36,18 +36,20 @@ enum greybus_protocol { enum greybus_class_type { GREYBUS_CLASS_CONTROL = 0x00, - GREYBUS_CLASS_USB = 0x01, + GREYBUS_CLASS_AP = 0x01, GREYBUS_CLASS_GPIO = 0x02, - GREYBUS_CLASS_SPI = 0x03, + GREYBUS_CLASS_I2C = 0x03, GREYBUS_CLASS_UART = 0x04, - GREYBUS_CLASS_PWM = 0x05, - GREYBUS_CLASS_I2S = 0x06, - GREYBUS_CLASS_I2C = 0x07, - GREYBUS_CLASS_SDIO = 0x08, - GREYBUS_CLASS_HID = 0x09, - GREYBUS_CLASS_DISPLAY = 0x0a, - GREYBUS_CLASS_CAMERA = 0x0b, - GREYBUS_CLASS_SENSOR = 0x0c, + GREYBUS_CLASS_HID = 0x05, + GREYBUS_CLASS_USB = 0x06, + GREYBUS_CLASS_SDIO = 0x07, + GREYBUS_CLASS_BATTERY = 0x08, + GREYBUS_CLASS_PWM = 0x09, + GREYBUS_CLASS_I2S = 0x0a, + GREYBUS_CLASS_SPI = 0x0b, + GREYBUS_CLASS_DISPLAY = 0x0c, + GREYBUS_CLASS_CAMERA = 0x0d, + GREYBUS_CLASS_SENSOR = 0x0e, GREYBUS_CLASS_VENDOR = 0xff, }; -- cgit v1.2.3-59-g8ed1b From 213aefe206d288eee4a2d480d9c6b2889b441682 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Oct 2014 13:40:02 +0800 Subject: greybus: gpio-gb: allow it to build properly for all current kernel versions. GPIO remove changed the api for 3.17 to try to make up for some previously foolish design decisions. Handle that in kernel_ver.h to make the code simple. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 3 +-- drivers/staging/greybus/kernel_ver.h | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 7f03676ef46f..53464e39ba6d 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -799,12 +799,11 @@ out_err: void gb_gpio_controller_exit(struct gb_connection *connection) { struct gb_gpio_controller *gb_gpio_controller = connection->private; - int ret; if (!gb_gpio_controller) return; - ret = gpiochip_remove(&gb_gpio_controller->chip); + gb_gpiochip_remove(&gb_gpio_controller->chip); /* kref_put(gb_gpio_controller->connection) */ kfree(gb_gpio_controller); } diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index c9ea7a94f4e6..e0fea182c3b5 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -26,4 +26,26 @@ #define U16_MAX ((u16)(~0U)) #endif /* !U16_MAX */ +/* + * The GPIO api sucks rocks in places, like removal, so work around their + * explicit requirements of catching the return value for kernels older than + * 3.17, which they explicitly changed in the 3.17 kernel. Consistency is + * overrated. + */ +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) +static inline void gb_gpiochip_remove(struct gpio_chip *chip) +{ + gpiochip_remove(chip); +} +#else +static inline void gb_gpiochip_remove(struct gpio_chip *chip) +{ + int ret; + ret = gpiochip_remove(chip); +} +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ -- cgit v1.2.3-59-g8ed1b From 43789c319ee80140fd1d07b85e1a619a8e01e466 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Oct 2014 15:09:49 +0800 Subject: greybus: battery-gb: provide accessors for a few more functions Put the hard coded values in a function to make it easier to see what needs to be done here. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index eaced9ab39d8..685cff5ff9e8 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -27,12 +27,24 @@ static const struct greybus_module_id id_table[] = { { }, /* terminating NULL entry */ }; +static int get_tech(struct gb_battery *gb) +{ + // FIXME - guess! + return POWER_SUPPLY_TECHNOLOGY_NiMH; +} + static int get_status(struct gb_battery *gb) { // FIXME!!! return 0; } +static int get_max_voltage(struct gb_battery *gb) +{ + // FIXME!!! + return 4700000; +} + static int get_capacity(struct gb_battery *gb) { // FIXME!!! @@ -59,8 +71,7 @@ static int get_property(struct power_supply *b, switch (psp) { case POWER_SUPPLY_PROP_TECHNOLOGY: - // FIXME - guess! - val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH; + val->intval = get_tech(gb); break; case POWER_SUPPLY_PROP_STATUS: @@ -68,7 +79,7 @@ static int get_property(struct power_supply *b, break; case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = 4700000; // FIXME - guess??? + val->intval = get_max_voltage(gb); break; case POWER_SUPPLY_PROP_CAPACITY: -- cgit v1.2.3-59-g8ed1b From 47ee0d135e7b1e9feebbe20342bacb99e4ae2ff6 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Mon, 20 Oct 2014 01:51:18 -0400 Subject: greybus: gpio-gb: remove unused status field from struct gb_gpio_get_value_request probably a cut and paste error got this unused status field. remove it. Signed-off-by: Matt Porter Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 53464e39ba6d..f3fb0b67b2d4 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -105,7 +105,6 @@ struct gb_gpio_direction_out_response { }; struct gb_gpio_get_value_request { - __u8 status; __u8 which; }; struct gb_gpio_get_value_response { -- cgit v1.2.3-59-g8ed1b From 2bb7eae8be12e8510fdb00aca6886b91ce72cb25 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Oct 2014 15:24:57 +0800 Subject: greybus: battery: some hooking up to the greybus core Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 13 ++++++++----- drivers/staging/greybus/connection.c | 6 ++++-- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/operation.c | 1 + 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 685cff5ff9e8..3288101c6319 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -18,7 +18,10 @@ struct gb_battery { // we will want to keep the battery stats in here as we will be getting // updates from the SVC "on the fly" so we don't have to always go ask // the battery for some information. Hopefully... - struct gb_module *gmod; + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + }; #define to_gb_battery(x) container_of(x, struct gb_battery, bat) @@ -111,8 +114,7 @@ static enum power_supply_property battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, }; -int gb_battery_probe(struct gb_module *gmod, - const struct greybus_module_id *id) +int gb_battery_device_init(struct gb_connection *connection) { struct gb_battery *gb; struct power_supply *b; @@ -122,6 +124,8 @@ int gb_battery_probe(struct gb_module *gmod, if (!gb) return -ENOMEM; + gb->connection = connection; // FIXME refcount! + b = &gb->bat; // FIXME - get a better (i.e. unique) name // FIXME - anything else needs to be set? @@ -131,12 +135,11 @@ int gb_battery_probe(struct gb_module *gmod, b->num_properties = ARRAY_SIZE(battery_props), b->get_property = get_property, - retval = power_supply_register(&gmod->dev, b); + retval = power_supply_register(&connection->interface->gmod->dev, b); if (retval) { kfree(gb); return retval; } - gmod->gb_battery = gb; return 0; } diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index a51b4781a2b6..c9d524b8274b 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -203,10 +203,12 @@ int gb_connection_init(struct gb_connection *connection) switch (connection->protocol) { case GREYBUS_PROTOCOL_I2C: return gb_i2c_device_init(connection); - case GREYBUS_PROTOCOL_CONTROL: - case GREYBUS_PROTOCOL_AP: case GREYBUS_PROTOCOL_GPIO: return gb_gpio_controller_init(connection); + case GREYBUS_PROTOCOL_BATTERY: + return gb_battery_device_init(connection); + case GREYBUS_PROTOCOL_CONTROL: + case GREYBUS_PROTOCOL_AP: case GREYBUS_PROTOCOL_UART: case GREYBUS_PROTOCOL_HID: case GREYBUS_PROTOCOL_VENDOR: diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index d4f5d940eeb1..c09572c70392 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -265,6 +265,7 @@ void gb_deregister_cport_complete(u16 cport_id); extern const struct attribute_group *greybus_module_groups[]; int gb_i2c_device_init(struct gb_connection *connection); +int gb_battery_device_init(struct gb_connection *connection); int gb_gpio_controller_init(struct gb_connection *connection); int gb_tty_init(void); diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 2f1249a63abd..62023f8cfb09 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -30,6 +30,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_I2C = 0x03, GREYBUS_PROTOCOL_UART = 0x04, GREYBUS_PROTOCOL_HID = 0x05, + GREYBUS_PROTOCOL_BATTERY = 0x08, /* ... */ GREYBUS_PROTOCOL_VENDOR = 0xff, }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 4cbe33e21254..4639212a96da 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -165,6 +165,7 @@ static gb_operation_recv_handler gb_operation_recv_handlers[] = { [GREYBUS_PROTOCOL_I2C] = gb_operation_recv_none, [GREYBUS_PROTOCOL_UART] = NULL, [GREYBUS_PROTOCOL_HID] = NULL, + [GREYBUS_PROTOCOL_BATTERY] = gb_operation_recv_none, [GREYBUS_PROTOCOL_VENDOR] = NULL, }; -- cgit v1.2.3-59-g8ed1b From 42d4a22d6b8e66f1e4d08a643429dbe5321eb458 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Oct 2014 16:02:56 +0800 Subject: greybus: add LED protocol numbers --- drivers/staging/greybus/connection.c | 1 + drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/operation.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c9d524b8274b..7a86c7cf3264 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -211,6 +211,7 @@ int gb_connection_init(struct gb_connection *connection) case GREYBUS_PROTOCOL_AP: case GREYBUS_PROTOCOL_UART: case GREYBUS_PROTOCOL_HID: + case GREYBUS_PROTOCOL_LED: case GREYBUS_PROTOCOL_VENDOR: default: gb_connection_err(connection, "unimplemented protocol %u", diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 62023f8cfb09..c18ee112f6df 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -31,6 +31,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_UART = 0x04, GREYBUS_PROTOCOL_HID = 0x05, GREYBUS_PROTOCOL_BATTERY = 0x08, + GREYBUS_PROTOCOL_LED = 0x0e, /* ... */ GREYBUS_PROTOCOL_VENDOR = 0xff, }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 4639212a96da..1176c97261c9 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -166,6 +166,7 @@ static gb_operation_recv_handler gb_operation_recv_handlers[] = { [GREYBUS_PROTOCOL_UART] = NULL, [GREYBUS_PROTOCOL_HID] = NULL, [GREYBUS_PROTOCOL_BATTERY] = gb_operation_recv_none, + [GREYBUS_PROTOCOL_LED] = NULL, [GREYBUS_PROTOCOL_VENDOR] = NULL, }; -- cgit v1.2.3-59-g8ed1b From ff6e0b9c2f05e9f3a06e84af5af2fa8a2c8098f8 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Mon, 20 Oct 2014 06:39:45 -0400 Subject: greybus: gpio-gb: fix offset error checking and usage Offset (or hwgpio num) is the offset within a gpiochip, not the unique gpio namespace number. Adjust the error checking and use of offset in our operation calls to fix this. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 58 +++++++++++++++------------------------ 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index f3fb0b67b2d4..32bf45ff7e8b 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -579,13 +579,12 @@ out: static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) { struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u8 which; int ret; - if (offset < chip->base || offset >= chip->base + chip->ngpio) + if (offset < 0 || offset >= chip->ngpio) return -EINVAL; - which = (u8)(offset - chip->base); - ret = gb_gpio_activate_operation(gb_gpio_controller, which); + printk("passed check\n"); + ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); if (ret) ; /* return ret; */ return 0; @@ -594,16 +593,14 @@ static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) { struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u8 which; int ret; - if (offset < chip->base || offset >= chip->base + chip->ngpio) { - pr_err("bad offset %u supplied (must be %u..%u)\n", - offset, chip->base, chip->base + chip->ngpio - 1); + if (offset < 0 || offset >= chip->ngpio) { + pr_err("bad offset %u supplied (must be 0..%u)\n", + offset, chip->ngpio - 1); return; } - which = (u8)(offset - chip->base); - ret = gb_gpio_deactivate_operation(gb_gpio_controller, which); + ret = gb_gpio_deactivate_operation(gb_gpio_controller, (u8)offset); if (ret) ; /* return ret; */ } @@ -614,9 +611,9 @@ static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) u8 which; int ret; - if (offset < chip->base || offset >= chip->base + chip->ngpio) + if (offset < 0 || offset >= chip->ngpio) return -EINVAL; - which = (u8)(offset - chip->base); + which = (u8)offset; ret = gb_gpio_get_direction_operation(gb_gpio_controller, which); if (ret) ; /* return ret; */ @@ -626,13 +623,11 @@ static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u8 which; int ret; - if (offset < chip->base || offset >= chip->base + chip->ngpio) + if (offset < 0 || offset >= chip->ngpio) return -EINVAL; - which = (u8)(offset - chip->base); - ret = gb_gpio_direction_in_operation(gb_gpio_controller, which); + ret = gb_gpio_direction_in_operation(gb_gpio_controller, (u8)offset); if (ret) ; /* return ret; */ return 0; @@ -642,13 +637,11 @@ static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u8 which; int ret; - if (offset < chip->base || offset >= chip->base + chip->ngpio) + if (offset < 0 || offset >= chip->ngpio) return -EINVAL; - which = (u8)(offset - chip->base); - ret = gb_gpio_direction_out_operation(gb_gpio_controller, which, !!value); + ret = gb_gpio_direction_out_operation(gb_gpio_controller, (u8)offset, !!value); if (ret) ; /* return ret; */ return 0; @@ -660,9 +653,9 @@ static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) u8 which; int ret; - if (offset < chip->base || offset >= chip->base + chip->ngpio) + if (offset < 0 || offset >= chip->ngpio) return -EINVAL; - which = (u8)(offset - chip->base); + which = (u8)offset; ret = gb_gpio_get_value_operation(gb_gpio_controller, which); if (ret) return ret; @@ -672,16 +665,14 @@ static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u8 which; int ret; - if (offset < chip->base || offset >= chip->base + chip->ngpio) { - pr_err("bad offset %u supplied (must be %u..%u)\n", - offset, chip->base, chip->base + chip->ngpio - 1); + if (offset < 0 || offset >= chip->ngpio) { + pr_err("bad offset %u supplied (must be 0..%u)\n", + offset, chip->ngpio - 1); return; } - which = (u8)(offset - chip->base); - ret = gb_gpio_set_value_operation(gb_gpio_controller, which, !!value); + ret = gb_gpio_set_value_operation(gb_gpio_controller, (u8)offset, !!value); if (ret) ; /* return ret; */ } @@ -691,16 +682,14 @@ static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, { struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); u16 usec; - u8 which; int ret; - if (offset < chip->base || offset >= chip->base + chip->ngpio) + if (offset < 0 || offset >= chip->ngpio) return -EINVAL; - which = (u8)(offset - chip->base); if (debounce > (unsigned int)U16_MAX) return -EINVAL; usec = (u8)debounce; - ret = gb_gpio_set_debounce_operation(gb_gpio_controller, which, usec); + ret = gb_gpio_set_debounce_operation(gb_gpio_controller, (u8)offset, usec); if (ret) ; /* return ret; */ @@ -709,11 +698,8 @@ static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - u8 which; - - if (offset < chip->base || offset >= chip->base + chip->ngpio) + if (offset < 0 || offset >= chip->ngpio) return -EINVAL; - which = (u8)(offset - chip->base); return 0; /* XXX */ } -- cgit v1.2.3-59-g8ed1b From 8597e6b2b9df0a1c777e3183c105d78a470d0c66 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 20 Oct 2014 16:45:50 +0530 Subject: greybus: Fix parameters of core_param() core_param() takes four parameters instead of three and so results in this compilation error: greybus/core.c:25:33: error: macro "core_param" requires 4 arguments, but only 3 given core_param(nogreybus, bool, 0444); ^ Fix this by adding proper arguments to it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 38f867db9411..0a37b95d226b 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -22,7 +22,7 @@ static bool nogreybus; #ifdef MODULE module_param(nogreybus, bool, 0444); #else -core_param(nogreybus, bool, 0444); +core_param(nogreybus, nogreybus, bool, 0444); #endif int greybus_disabled(void) { -- cgit v1.2.3-59-g8ed1b From 6813e35a0ea48749b4d0f1eb1c4df35f4fa2a4c1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 20 Oct 2014 16:46:18 +0530 Subject: greybus: .gitignore: minor updates Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/.gitignore b/drivers/staging/greybus/.gitignore index a07ab9d2eb46..f7dc3ba2a771 100644 --- a/drivers/staging/greybus/.gitignore +++ b/drivers/staging/greybus/.gitignore @@ -7,3 +7,6 @@ Module.symvers *.swp .tmp_versions tags +cscope.* +ncscope.* +*.patch -- cgit v1.2.3-59-g8ed1b From 599dc6aa8ed91addf38814de22bdb5bb813a95aa Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 20 Oct 2014 10:27:55 -0500 Subject: greybus: properly drop device reference Drop the USB device reference taken at the top of ap_probe() in the event greybus_create_hd() fails. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 21fe4fdd4103..21db78fb863f 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -386,8 +386,10 @@ static int ap_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); hd = greybus_create_hd(&es1_driver, &udev->dev); - if (!hd) + if (!hd) { + usb_put_dev(udev); return -ENOMEM; + } es1 = hd_to_es1(hd); es1->hd = hd; -- cgit v1.2.3-59-g8ed1b From 5b3db0ddaaf7e844dd0efbaa11a1cec4700aff34 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 20 Oct 2014 10:27:56 -0500 Subject: greybus: create a slab cache for operations Everything we do on greybus will involve an operation, so create a slab cache for that frequently-allocated data structure. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 40 +++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 1176c97261c9..da455f019e33 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -24,6 +24,8 @@ */ #define GB_OPERATION_MESSAGE_SIZE_MAX 4096 +static struct kmem_cache *gb_operation_cache; + /* Workqueue to handle Greybus operation completions. */ static struct workqueue_struct *gb_operation_recv_workqueue; @@ -307,8 +309,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC; bool outgoing = response_size != 0; - /* XXX Use a slab cache */ - operation = kzalloc(sizeof(*operation), gfp_flags); + operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) return NULL; operation->connection = connection; /* XXX refcount? */ @@ -316,10 +317,8 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, operation->request = gb_operation_gbuf_create(operation, type, request_size, outgoing); - if (!operation->request) { - kfree(operation); - return NULL; - } + if (!operation->request) + goto err_cache; operation->request_payload = operation->request->transfer_buffer + sizeof(struct gb_operation_msg_hdr); /* We always use the full request buffer */ @@ -330,11 +329,8 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, operation->response = gb_operation_gbuf_create(operation, type, response_size, false); - if (!operation->response) { - greybus_free_gbuf(operation->request); - kfree(operation); - return NULL; - } + if (!operation->response) + goto err_request; operation->response_payload = operation->response->transfer_buffer + sizeof(struct gb_operation_msg_hdr); @@ -349,6 +345,13 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, spin_unlock_irq(&gb_operations_lock); return operation; + +err_request: + greybus_free_gbuf(operation->request); +err_cache: + kmem_cache_free(gb_operation_cache, operation); + + return NULL; } /* @@ -367,7 +370,7 @@ void gb_operation_destroy(struct gb_operation *operation) greybus_free_gbuf(operation->response); greybus_free_gbuf(operation->request); - kfree(operation); + kmem_cache_free(gb_operation_cache, operation); } /* @@ -470,14 +473,25 @@ void gb_connection_operation_recv(struct gb_connection *connection, int gb_operation_init(void) { + gb_operation_cache = kmem_cache_create("gb_operation_cache", + sizeof(struct gb_operation), 0, 0, NULL); + if (!gb_operation_cache) + return -ENOMEM; + gb_operation_recv_workqueue = alloc_workqueue("greybus_recv", 0, 1); - if (!gb_operation_recv_workqueue) + if (!gb_operation_recv_workqueue) { + kmem_cache_destroy(gb_operation_cache); + gb_operation_cache = NULL; return -ENOMEM; + } return 0; } void gb_operation_exit(void) { + kmem_cache_destroy(gb_operation_cache); + gb_operation_cache = NULL; destroy_workqueue(gb_operation_recv_workqueue); + gb_operation_recv_workqueue = NULL; } -- cgit v1.2.3-59-g8ed1b From 09c521dc0a98141ae3c1b431c967d90d46c372e8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 20 Oct 2014 10:27:57 -0500 Subject: greybus: drop gb_* device fields from gb_module A struct gb_module has a bunch of fields from the earlier skeleton code, where a module was assumed to possibly have one of every type of device available on the GP Bridge. The manifest parsing code changed it so these things will be related to connection endpoints, so these gb_module fields are no longer needed. A few of these (battery and sdio) haven't been implemented the "new way" yet, so just leave a bit of the code that was there commented out for now. Also, gb_tty seems to be partially implemented and I don't want to remove that without knowing where it's headed, so that one stays. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 2 ++ drivers/staging/greybus/module.h | 5 ----- drivers/staging/greybus/sdio-gb.c | 4 +++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 3288101c6319..09c3290e1eaf 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -146,6 +146,7 @@ int gb_battery_device_init(struct gb_connection *connection) void gb_battery_disconnect(struct gb_module *gmod) { +#if 0 struct gb_battery *gb; gb = gmod->gb_battery; @@ -155,6 +156,7 @@ void gb_battery_disconnect(struct gb_module *gmod) power_supply_unregister(&gb->bat); kfree(gb); +#endif } #if 0 diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 114f15750380..e32135f3b738 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -35,12 +35,7 @@ struct gb_module { struct greybus_host_device *hd; - struct gb_i2c_device *gb_i2c_dev; - struct gb_gpio_device *gb_gpio_dev; - struct gb_sdio_host *gb_sdio_host; struct gb_tty *gb_tty; - struct gb_usb_device *gb_usb_dev; - struct gb_battery *gb_battery; }; #define to_gb_module(d) container_of(d, struct gb_module, dev) diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 239fcf773acb..19c7c4ac81eb 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -61,12 +61,13 @@ int gb_sdio_probe(struct gb_module *gmod, mmc->ops = &gb_sd_ops; // FIXME - set up size limits we can handle. - gmod->gb_sdio_host = host; + // gmod->gb_sdio_host = host; return 0; } void gb_sdio_disconnect(struct gb_module *gmod) { +#if 0 struct mmc_host *mmc; struct gb_sdio_host *host; @@ -77,6 +78,7 @@ void gb_sdio_disconnect(struct gb_module *gmod) mmc = host->mmc; mmc_remove_host(mmc); mmc_free_host(mmc); +#endif } #if 0 -- cgit v1.2.3-59-g8ed1b From 6ff5e00a176878c218c2b27e4437e6af8f46a28c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 20 Oct 2014 10:27:58 -0500 Subject: greybus: remove cports and strings from gb_module We no longer keep copies of strings found in the manifuest in a module's strings array, so we can get rid of the strings array. Similarly, the new manifest parsing code sets up connections for each cport id advertised for a module, so the cport array is no longer needed either. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 --- drivers/staging/greybus/module.h | 5 ----- 2 files changed, 8 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 0a37b95d226b..b59dee171446 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -118,10 +118,7 @@ EXPORT_SYMBOL_GPL(greybus_deregister); static void greybus_module_release(struct device *dev) { struct gb_module *gmod = to_gb_module(dev); - int i; - for (i = 0; i < gmod->num_strings; ++i) - kfree(gmod->string[i]); kfree(gmod); } diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index e32135f3b738..2c6707d6d85f 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -28,11 +28,6 @@ struct gb_module { char *product_string; u64 unique_id; - int num_cports; - int num_strings; - u16 cport_ids[MAX_CPORTS_PER_MODULE]; - struct gmod_string *string[MAX_STRINGS_PER_MODULE]; - struct greybus_host_device *hd; struct gb_tty *gb_tty; -- cgit v1.2.3-59-g8ed1b From 6892537f61b1eac275ad50e093708e5e3c8664bd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 20 Oct 2014 10:27:59 -0500 Subject: greybus: move ap_disconnect() The next patch has ap_probe() reference ap_disconnect(). To prepare for that, move ap_disconnect() up in the file. This is done as a separate commit to make it easier to see this move involves no other change to that function. This and the next commit can be squashed if desired. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 58 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 21db78fb863f..4473c9af97c6 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -266,6 +266,35 @@ static int check_urb_status(struct urb *urb) return -EAGAIN; } +static void ap_disconnect(struct usb_interface *interface) +{ + struct es1_ap_dev *es1; + int i; + + es1 = usb_get_intfdata(interface); + if (!es1) + return; + + /* Tear down everything! */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + usb_kill_urb(es1->cport_out_urb[i]); + usb_free_urb(es1->cport_out_urb[i]); + } + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + usb_kill_urb(es1->cport_in_urb[i]); + usb_free_urb(es1->cport_in_urb[i]); + kfree(es1->cport_in_buffer[i]); + } + + usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); + usb_put_dev(es1->usb_dev); + kfree(es1->svc_buffer); + greybus_remove_hd(es1->hd); + usb_set_intfdata(interface, NULL); +} + /* Callback for when we get a SVC message */ static void svc_in_callback(struct urb *urb) { @@ -503,35 +532,6 @@ error: return retval; } -static void ap_disconnect(struct usb_interface *interface) -{ - struct es1_ap_dev *es1; - int i; - - es1 = usb_get_intfdata(interface); - if (!es1) - return; - - /* Tear down everything! */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - usb_kill_urb(es1->cport_out_urb[i]); - usb_free_urb(es1->cport_out_urb[i]); - } - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - usb_kill_urb(es1->cport_in_urb[i]); - usb_free_urb(es1->cport_in_urb[i]); - kfree(es1->cport_in_buffer[i]); - } - - usb_kill_urb(es1->svc_urb); - usb_free_urb(es1->svc_urb); - usb_put_dev(es1->usb_dev); - kfree(es1->svc_buffer); - greybus_remove_hd(es1->hd); - usb_set_intfdata(interface, NULL); -} - static struct usb_driver es1_ap_driver = { .name = "es1_ap_driver", .probe = ap_probe, -- cgit v1.2.3-59-g8ed1b From 1ec1d6dd357766cb6d1816ad61cc0f7ca3dbc960 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 20 Oct 2014 10:28:00 -0500 Subject: greybus: leverage ap_disconnect() in ap_probe() With a few minor changes, ap_disconnect() can correctly handle cleaning up even a partially initialized USB interface. Make those changes, and then use ap_disconnect() to simplify cleanup for all the error paths in ap_probe(). Reset all fields as they're cleaned up to facilitate debugging. Signed-off-by: Alex Elder --- drivers/staging/greybus/es1-ap-usb.c | 58 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 4473c9af97c6..12eb9b2bee0f 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -277,22 +277,37 @@ static void ap_disconnect(struct usb_interface *interface) /* Tear down everything! */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - usb_kill_urb(es1->cport_out_urb[i]); - usb_free_urb(es1->cport_out_urb[i]); + struct urb *urb = es1->cport_out_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + es1->cport_out_urb[i] = NULL; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ } for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - usb_kill_urb(es1->cport_in_urb[i]); - usb_free_urb(es1->cport_in_urb[i]); + struct urb *urb = es1->cport_in_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); kfree(es1->cport_in_buffer[i]); + es1->cport_in_buffer[i] = NULL; } usb_kill_urb(es1->svc_urb); usb_free_urb(es1->svc_urb); - usb_put_dev(es1->usb_dev); + es1->svc_urb = NULL; kfree(es1->svc_buffer); - greybus_remove_hd(es1->hd); + es1->svc_buffer = NULL; + usb_set_intfdata(interface, NULL); + greybus_remove_hd(es1->hd); + + usb_put_dev(es1->usb_dev); } /* Callback for when we get a SVC message */ @@ -466,7 +481,7 @@ static int ap_probe(struct usb_interface *interface, es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); if (!es1->svc_urb) - goto error_int_urb; + goto error; usb_fill_int_urb(es1->svc_urb, udev, usb_rcvintpipe(udev, es1->svc_endpoint), @@ -474,7 +489,7 @@ static int ap_probe(struct usb_interface *interface, es1, svc_interval); retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); if (retval) - goto error_submit_urb; + goto error; /* Allocate buffers for our cport in messages and start them up */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { @@ -483,10 +498,10 @@ static int ap_probe(struct usb_interface *interface, urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) - goto error_bulk_in_urb; + goto error; buffer = kmalloc(ES1_GBUF_MSG_SIZE, GFP_KERNEL); if (!buffer) - goto error_bulk_in_urb; + goto error; usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, es1->cport_in_endpoint), @@ -495,7 +510,7 @@ static int ap_probe(struct usb_interface *interface, es1->cport_in_buffer[i] = buffer; retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) - goto error_bulk_in_urb; + goto error; } /* Allocate urbs for our CPort OUT messages */ @@ -504,31 +519,16 @@ static int ap_probe(struct usb_interface *interface, urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) - goto error_bulk_out_urb; + goto error; es1->cport_out_urb[i] = urb; es1->cport_out_urb_busy[i] = false; /* just to be anal */ } return 0; - -error_bulk_out_urb: - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) - usb_free_urb(es1->cport_out_urb[i]); - -error_bulk_in_urb: - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - usb_kill_urb(es1->cport_in_urb[i]); - usb_free_urb(es1->cport_in_urb[i]); - kfree(es1->cport_in_buffer[i]); - } - -error_submit_urb: - usb_free_urb(es1->svc_urb); -error_int_urb: - kfree(es1->svc_buffer); error: - greybus_remove_hd(es1->hd); + ap_disconnect(interface); + return retval; } -- cgit v1.2.3-59-g8ed1b From c0855bfdd65dd5c100d5c63daab5897a117d8b49 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 21 Oct 2014 14:31:24 +0800 Subject: greybus: battery-gb: Add battery communication with the module This adds support to talk to the battery to get the various requests made to it, based on the battery protocol defined in the Greybus Specification. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 231 +++++++++++++++++++++++++++++++++-- 1 file changed, 219 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 09c3290e1eaf..eca1d0948705 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -25,45 +25,245 @@ struct gb_battery { }; #define to_gb_battery(x) container_of(x, struct gb_battery, bat) +/* Version of the Greybus battery protocol we support */ +#define GB_BATTERY_VERSION_MAJOR 0x00 +#define GB_BATTERY_VERSION_MINOR 0x01 + +/* Greybus battery request types */ +#define GB_BATTERY_TYPE_INVALID 0x00 +#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01 +#define GB_BATTERY_TYPE_TECHNOLOGY 0x02 +#define GB_BATTERY_TYPE_STATUS 0x03 +#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 +#define GB_BATTERY_TYPE_CAPACITY 0x05 +#define GB_BATTERY_TYPE_TEMPERATURE 0x06 +#define GB_BATTERY_TYPE_VOLTAGE 0x07 + +struct gb_battery_proto_version_response { + __u8 status; + __u8 major; + __u8 minor; +}; + +/* Should match up with battery types in linux/power_supply.h */ +#define GB_BATTERY_TECH_UNKNOWN 0x0000 +#define GB_BATTERY_TECH_NiMH 0x0001 +#define GB_BATTERY_TECH_LION 0x0002 +#define GB_BATTERY_TECH_LIPO 0x0003 +#define GB_BATTERY_TECH_LiFe 0x0004 +#define GB_BATTERY_TECH_NiCd 0x0005 +#define GB_BATTERY_TECH_LiMn 0x0006 + +struct gb_battery_technology_request { + __u8 status; + __le32 technology; +}; + +/* Should match up with battery status in linux/power_supply.h */ +#define GB_BATTERY_STATUS_UNKNOWN 0x0000 +#define GB_BATTERY_STATUS_CHARGING 0x0001 +#define GB_BATTERY_STATUS_DISCHARGING 0x0002 +#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003 +#define GB_BATTERY_STATUS_FULL 0x0004 + +struct gb_battery_status_request { + __u8 status; + __le16 battery_status; +}; + +struct gb_battery_max_voltage_request { + __u8 status; + __le32 max_voltage; +}; + +struct gb_battery_capacity_request { + __u8 status; + __le32 capacity; +}; + +struct gb_battery_temperature_request { + __u8 status; + __le32 temperature; +}; + +struct gb_battery_voltage_request { + __u8 status; + __le32 voltage; +}; + + static const struct greybus_module_id id_table[] = { { GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */ { }, /* terminating NULL entry */ }; +static int battery_operation(struct gb_battery *gb, int type, + void *response, int response_size) +{ + struct gb_connection *connection = gb->connection; + struct gb_operation *operation; + struct gb_battery_technology_request *fake_request; + u8 *local_response; + int ret; + + local_response = kmalloc(response_size, GFP_KERNEL); + if (!local_response) + return -ENOMEM; + + operation = gb_operation_create(connection, type, 0, response_size); + if (!operation) { + kfree(local_response); + return -ENOMEM; + } + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("version operation failed (%d)\n", ret); + goto out; + } + + /* + * We only want to look at the status, and all requests have the same + * layout for where the status is, so cast this to a random request so + * we can see the status easier. + */ + fake_request = (struct gb_battery_technology_request *)local_response; + if (fake_request->status) { + gb_connection_err(connection, "version response %hhu", + fake_request->status); + ret = -EIO; + } else { + /* Good request, so copy to the caller's buffer */ + memcpy(response, local_response, response_size); + } +out: + gb_operation_destroy(operation); + + return ret; +} + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_battery *gb) +{ + struct gb_battery_proto_version_response version_request; + int retval; + + retval = battery_operation(gb, GB_BATTERY_TYPE_PROTOCOL_VERSION, + &version_request, sizeof(version_request)); + if (retval) + return retval; + + if (version_request.major > GB_BATTERY_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + version_request.major, GB_BATTERY_VERSION_MAJOR); + return -ENOTSUPP; + } + + gb->version_major = version_request.major; + gb->version_minor = version_request.minor; + return 0; +} + static int get_tech(struct gb_battery *gb) { - // FIXME - guess! - return POWER_SUPPLY_TECHNOLOGY_NiMH; + struct gb_battery_technology_request tech_request; + u32 technology; + int retval; + + retval = battery_operation(gb, GB_BATTERY_TYPE_TECHNOLOGY, + &tech_request, sizeof(tech_request)); + if (retval) + return retval; + + /* + * We have a one-to-one mapping of tech types to power_supply + * status, so just return that value. + */ + technology = le32_to_cpu(tech_request.technology); + return technology; } static int get_status(struct gb_battery *gb) { - // FIXME!!! - return 0; + struct gb_battery_status_request status_request; + u16 battery_status; + int retval; + + retval = battery_operation(gb, GB_BATTERY_TYPE_STATUS, + &status_request, sizeof(status_request)); + if (retval) + return retval; + + /* + * We have a one-to-one mapping of battery status to power_supply + * status, so just return that value. + */ + battery_status = le16_to_cpu(status_request.battery_status); + return battery_status; } static int get_max_voltage(struct gb_battery *gb) { - // FIXME!!! - return 4700000; + struct gb_battery_max_voltage_request volt_request; + u32 max_voltage; + int retval; + + retval = battery_operation(gb, GB_BATTERY_TYPE_MAX_VOLTAGE, + &volt_request, sizeof(volt_request)); + if (retval) + return retval; + + max_voltage = le32_to_cpu(volt_request.max_voltage); + return max_voltage; } static int get_capacity(struct gb_battery *gb) { - // FIXME!!! - return 0; + struct gb_battery_capacity_request capacity_request; + u32 capacity; + int retval; + + retval = battery_operation(gb, GB_BATTERY_TYPE_CAPACITY, + &capacity_request, sizeof(capacity_request)); + if (retval) + return retval; + + capacity = le32_to_cpu(capacity_request.capacity); + return capacity; } static int get_temp(struct gb_battery *gb) { - // FIXME!!! - return 0; + struct gb_battery_temperature_request temp_request; + u32 temperature; + int retval; + + retval = battery_operation(gb, GB_BATTERY_TYPE_TEMPERATURE, + &temp_request, sizeof(temp_request)); + if (retval) + return retval; + + temperature = le32_to_cpu(temp_request.temperature); + return temperature; } static int get_voltage(struct gb_battery *gb) { - // FIXME!!! - return 0; + struct gb_battery_voltage_request voltage_request; + u32 voltage; + int retval; + + retval = battery_operation(gb, GB_BATTERY_TYPE_VOLTAGE, + &voltage_request, sizeof(voltage_request)); + if (retval) + return retval; + + voltage = le32_to_cpu(voltage_request.voltage); + return voltage; } static int get_property(struct power_supply *b, @@ -126,6 +326,13 @@ int gb_battery_device_init(struct gb_connection *connection) gb->connection = connection; // FIXME refcount! + /* Check the version */ + retval = get_version(gb); + if (retval) { + kfree(gb); + return retval; + } + b = &gb->bat; // FIXME - get a better (i.e. unique) name // FIXME - anything else needs to be set? -- cgit v1.2.3-59-g8ed1b From 03130a77d52bd738cdcee17627aa5e3d19fdbeeb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 20 Oct 2014 23:01:02 -0500 Subject: greybus: fix op_cycle logic The function that computes the operation id for a connection is wrongly using MOD rather than AND. Fix that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7a86c7cf3264..09fe25d5fee4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -171,7 +171,7 @@ void gb_connection_destroy(struct gb_connection *connection) u16 gb_connection_operation_id(struct gb_connection *connection) { - return (u16)(atomic_inc_return(&connection->op_cycle) % U16_MAX); + return (u16)(atomic_inc_return(&connection->op_cycle) & (int)U16_MAX); } void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) -- cgit v1.2.3-59-g8ed1b From fb305c335ca3c7f175c18654d952880ec91df9ef Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 20 Oct 2014 23:01:03 -0500 Subject: greybus: stash power supply pointer in connection The battery code was not stashing a copy of its private data pointer. It'll be needed in the next patch. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 09c3290e1eaf..5d0db61d49d2 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -125,6 +125,7 @@ int gb_battery_device_init(struct gb_connection *connection) return -ENOMEM; gb->connection = connection; // FIXME refcount! + connection->private = gb; b = &gb->bat; // FIXME - get a better (i.e. unique) name -- cgit v1.2.3-59-g8ed1b From 697e55d35dcb441cc5bd800efae0f98ec8d63fd9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 20 Oct 2014 23:01:04 -0500 Subject: greybus: improve module cleanup code When a module gets destroyed all of its state and the state of its interfaces and connections (etc.) need to be torn down. This is not now being done properly. Add this teardown code. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 8 ++++++++ drivers/staging/greybus/connection.c | 24 ++++++++++++++++++++++++ drivers/staging/greybus/connection.h | 1 + drivers/staging/greybus/core.c | 13 ++++++------- drivers/staging/greybus/greybus.h | 5 +++++ drivers/staging/greybus/interface.c | 14 ++++++++++++++ drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/module.c | 23 +++++++++++++++++++---- 8 files changed, 78 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 5d0db61d49d2..a3b2bee77299 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -145,6 +145,14 @@ int gb_battery_device_init(struct gb_connection *connection) return 0; } +void gb_battery_device_exit(struct gb_connection *connection) +{ + struct gb_battery *gb = connection->private; + + power_supply_unregister(&gb->bat); + kfree(gb); +} + void gb_battery_disconnect(struct gb_module *gmod) { #if 0 diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 09fe25d5fee4..e2340d8392ed 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -220,3 +220,27 @@ int gb_connection_init(struct gb_connection *connection) } return -ENXIO; } + +void gb_connection_exit(struct gb_connection *connection) +{ + switch (connection->protocol) { + case GREYBUS_PROTOCOL_I2C: + gb_i2c_device_exit(connection); + break; + case GREYBUS_PROTOCOL_GPIO: + gb_gpio_controller_exit(connection); + break; + case GREYBUS_PROTOCOL_BATTERY: + gb_battery_device_exit(connection); + break; + case GREYBUS_PROTOCOL_CONTROL: + case GREYBUS_PROTOCOL_AP: + case GREYBUS_PROTOCOL_UART: + case GREYBUS_PROTOCOL_HID: + case GREYBUS_PROTOCOL_VENDOR: + default: + gb_connection_err(connection, "unimplemented protocol %u", + (u32)connection->protocol); + break; + } +} diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index bb22c52c2f01..685c1ffcb1ca 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -35,6 +35,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, void gb_connection_destroy(struct gb_connection *connection); int gb_connection_init(struct gb_connection *connection); +void gb_connection_exit(struct gb_connection *connection); struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, u16 cport_id); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index b59dee171446..bc27ad68cc85 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -119,10 +119,9 @@ static void greybus_module_release(struct device *dev) { struct gb_module *gmod = to_gb_module(dev); - kfree(gmod); + gb_module_destroy(gmod); } - static struct device_type greybus_module_type = { .name = "greybus_module", .release = greybus_module_release, @@ -157,7 +156,7 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, */ if (!gb_manifest_parse(gmod, data, size)) { dev_err(hd->parent, "manifest error\n"); - goto error; + goto err_module; } /* @@ -180,14 +179,14 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, retval = device_add(&gmod->dev); if (retval) - goto error; + goto err_device; gb_module_interfaces_init(gmod); - return; -error: - gb_module_destroy(gmod); + return; +err_device: put_device(&gmod->dev); +err_module: greybus_module_release(&gmod->dev); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index c09572c70392..bbd90b4cec91 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -265,8 +265,13 @@ void gb_deregister_cport_complete(u16 cport_id); extern const struct attribute_group *greybus_module_groups[]; int gb_i2c_device_init(struct gb_connection *connection); +void gb_i2c_device_exit(struct gb_connection *connection); + int gb_battery_device_init(struct gb_connection *connection); +void gb_battery_device_exit(struct gb_connection *connection); + int gb_gpio_controller_init(struct gb_connection *connection); +void gb_gpio_controller_exit(struct gb_connection *connection); int gb_tty_init(void); void gb_tty_exit(void); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 0c2fdd3f7ea2..645f05b3d590 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -54,6 +54,8 @@ void gb_interface_destroy(struct gb_interface *interface) list_del(&interface->links); spin_unlock_irq(&gb_interfaces_lock); + gb_interface_connections_exit(interface); + /* kref_put(gmod); */ kfree(interface); } @@ -72,3 +74,15 @@ int gb_interface_connections_init(struct gb_interface *interface) return ret; } + +void gb_interface_connections_exit(struct gb_interface *interface) +{ + struct gb_connection *connection; + struct gb_connection *next; + + list_for_each_entry_safe(connection, next, &interface->connections, + interface_links) { + gb_connection_exit(connection); + gb_connection_destroy(connection); + } +} diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 1019a981f5f6..f0c9e1d5a3b3 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -23,5 +23,6 @@ struct gb_interface *gb_interface_create(struct gb_module *gmod, u8 module_id); void gb_interface_destroy(struct gb_interface *interface); int gb_interface_connections_init(struct gb_interface *interface); +void gb_interface_connections_exit(struct gb_interface *interface); #endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 699cd003e367..2883947fb630 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -44,6 +44,15 @@ const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, return NULL; } +static void gb_module_interfaces_exit(struct gb_module *gmod) +{ + struct gb_interface *interface; + struct gb_interface *next; + + list_for_each_entry_safe(interface, next, &gmod->interfaces, links) + gb_interface_destroy(interface); +} + /* * A Greybus module represents a user-replacable component on an Ara * phone. @@ -62,7 +71,7 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) return NULL; gmod->hd = hd; /* XXX refcount? */ - gmod->module_id = module_id; + gmod->module_id = module_id; /* XXX check for dups */ INIT_LIST_HEAD(&gmod->interfaces); spin_lock_irq(&gb_modules_lock); @@ -80,15 +89,21 @@ void gb_module_destroy(struct gb_module *gmod) if (WARN_ON(!gmod)) return; - kfree(gmod->product_string); - kfree(gmod->vendor_string); - spin_lock_irq(&gb_modules_lock); list_del(&gmod->links); spin_unlock_irq(&gb_modules_lock); + gb_module_interfaces_exit(gmod); + /* XXX Do something with gmod->gb_tty */ + + put_device(&gmod->dev); + /* kfree(gmod->dev->name); */ + + kfree(gmod->product_string); + kfree(gmod->vendor_string); /* kref_put(module->hd); */ + kfree(gmod); } -- cgit v1.2.3-59-g8ed1b From 98f4ab2df9b51356b48210188b4fbff41789acbb Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 21 Oct 2014 01:52:27 -0400 Subject: greybus: ap: add support for the AP Device ID unipro management function message The AP needs to know its assigned Device ID in order to establish Greybus connections between CPorts. We could have pulled the Device ID from the controller hardware in a driver specific manner, but instead we define one generic message from the SVC to let the AP know this information. Add this additional unipro management message and handle it by setting the supplied Device ID in the struct greybus_host_device. The greybus core will use this to populate the source Device ID when establishing a connection between the AP and another module's CPort. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 9 +++++++-- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/svc_msg.h | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index f4470b5b2972..5b6335643bc0 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -113,8 +113,13 @@ static void svc_management(struct svc_function_unipro_management *management, return; } - /* What? An AP should not get this message */ - dev_err(hd->parent, "Got an svc management message???\n"); + switch (management->management_packet_type) { + case SVC_MANAGEMENT_AP_DEVICE_ID: + hd->device_id = management->ap_device_id.device_id; + break; + default: + dev_err(hd->parent, "Unhandled UniPro management message\n"); + } } static void svc_hotplug(struct svc_function_hotplug *hotplug, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index bbd90b4cec91..a4e1f4b2b18e 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -180,6 +180,7 @@ struct greybus_host_device { struct rb_root connections; struct ida cport_id_map; spinlock_t cport_id_map_lock; + u8 device_id; /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 57d0a91ff60b..4aa7e177f076 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -58,9 +58,14 @@ struct svc_function_unipro_link_up { __u8 device_id; }; +struct svc_function_ap_device_id { + __u8 device_id; +}; + enum svc_function_management_event { SVC_MANAGEMENT_SET_ROUTE = 0x00, SVC_MANAGEMENT_LINK_UP = 0x01, + SVC_MANAGEMENT_AP_DEVICE_ID = 0x02, }; struct svc_function_unipro_management { @@ -68,6 +73,7 @@ struct svc_function_unipro_management { union { struct svc_function_unipro_set_route set_route; struct svc_function_unipro_link_up link_up; + struct svc_function_ap_device_id ap_device_id; }; }; -- cgit v1.2.3-59-g8ed1b From c9346e19b25f9abc7630200d7d180a20224a956c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 21 Oct 2014 15:51:53 +0800 Subject: greybus: battery-gb.c: fix memory leak found by Viresh Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 03c16f7a5301..fd2b86e5579c 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -139,6 +139,7 @@ static int battery_operation(struct gb_battery *gb, int type, } out: gb_operation_destroy(operation); + kfree(local_response); return ret; } -- cgit v1.2.3-59-g8ed1b From 0369a459982f58688235000fc8990b70e7553e6e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 21 Oct 2014 16:25:13 +0800 Subject: greybus: battery-gb: Allow kernel values to get out of sync with greybus spec We can't know that the greybus values and the kernel values for a number of battery enumerated types will remain in sync. And as theses are sent by an external device from the kernel, we have to explicitly check these values. Reported-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 52 +++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index fd2b86e5579c..592b68d5115a 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -181,10 +181,35 @@ static int get_tech(struct gb_battery *gb) return retval; /* - * We have a one-to-one mapping of tech types to power_supply - * status, so just return that value. + * Map greybus values to power_supply values. Hopefully these are + * "identical" which should allow gcc to optomize the code away to + * nothing. */ technology = le32_to_cpu(tech_request.technology); + switch (technology) { + case GB_BATTERY_TECH_NiMH: + technology = POWER_SUPPLY_TECHNOLOGY_NiMH; + break; + case GB_BATTERY_TECH_LION: + technology = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case GB_BATTERY_TECH_LIPO: + technology = POWER_SUPPLY_TECHNOLOGY_LIPO; + break; + case GB_BATTERY_TECH_LiFe: + technology = POWER_SUPPLY_TECHNOLOGY_LiFe; + break; + case GB_BATTERY_TECH_NiCd: + technology = POWER_SUPPLY_TECHNOLOGY_NiCd; + break; + case GB_BATTERY_TECH_LiMn: + technology = POWER_SUPPLY_TECHNOLOGY_LiMn; + break; + case GB_BATTERY_TECH_UNKNOWN: + default: + technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + break; + } return technology; } @@ -200,10 +225,29 @@ static int get_status(struct gb_battery *gb) return retval; /* - * We have a one-to-one mapping of battery status to power_supply - * status, so just return that value. + * Map greybus values to power_supply values. Hopefully these are + * "identical" which should allow gcc to optomize the code away to + * nothing. */ battery_status = le16_to_cpu(status_request.battery_status); + switch (battery_status) { + case GB_BATTERY_STATUS_CHARGING: + battery_status = POWER_SUPPLY_STATUS_CHARGING; + break; + case GB_BATTERY_STATUS_DISCHARGING: + battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case GB_BATTERY_STATUS_NOT_CHARGING: + battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case GB_BATTERY_STATUS_FULL: + battery_status = POWER_SUPPLY_STATUS_FULL; + break; + case GB_BATTERY_STATUS_UNKNOWN: + default: + battery_status = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } return battery_status; } -- cgit v1.2.3-59-g8ed1b From 6271b5bac99c2d06543adbdbecb9157d77765831 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 21 Oct 2014 22:43:29 -0400 Subject: greybus: module: add gb_module_find() Add support for getting a struct gb_module from a Module ID. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 11 +++++++++++ drivers/staging/greybus/module.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 2883947fb630..50139f41fe20 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -107,6 +107,17 @@ void gb_module_destroy(struct gb_module *gmod) kfree(gmod); } +struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) +{ + struct gb_module *module; + + list_for_each_entry(module, &hd->modules, links) + if (module->module_id == module_id) + return module; + + return NULL; +} + void gb_module_interfaces_init(struct gb_module *gmod) { struct gb_interface *interface; diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 2c6707d6d85f..82def46e5ab1 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -52,6 +52,9 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id); void gb_module_destroy(struct gb_module *module); +struct gb_module *gb_module_find(struct greybus_host_device *hd, + u8 module_id); + void gb_module_interfaces_init(struct gb_module *gmod); #endif /* __MODULE_H */ -- cgit v1.2.3-59-g8ed1b From 1a4c013a44add7f174e0507f0240f3b3cc810301 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 21 Oct 2014 22:43:30 -0400 Subject: greybus: interface: add gb_interface_find() Add support for getting a struct gb_interface from an Interface ID. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 12 ++++++++++++ drivers/staging/greybus/interface.h | 3 +++ 2 files changed, 15 insertions(+) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 645f05b3d590..0415db51051e 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -60,6 +60,18 @@ void gb_interface_destroy(struct gb_interface *interface) kfree(interface); } +struct gb_interface *gb_interface_find(struct gb_module *module, + u8 interface_id) +{ + struct gb_interface *interface; + + list_for_each_entry(interface, &module->interfaces, links) + if (interface->id == interface_id) + return interface; + + return NULL; +} + int gb_interface_connections_init(struct gb_interface *interface) { struct gb_connection *connection; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index f0c9e1d5a3b3..907b6341665c 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -14,6 +14,7 @@ struct gb_interface { struct gb_module *gmod; u8 id; + u8 device_id; struct list_head connections; struct list_head links; /* module->interfaces */ @@ -22,6 +23,8 @@ struct gb_interface { struct gb_interface *gb_interface_create(struct gb_module *gmod, u8 module_id); void gb_interface_destroy(struct gb_interface *interface); +struct gb_interface *gb_interface_find(struct gb_module *gmod, u8 interface_id); + int gb_interface_connections_init(struct gb_interface *interface); void gb_interface_connections_exit(struct gb_interface *interface); -- cgit v1.2.3-59-g8ed1b From 6232b073d44646e3051a8871feb2deaabe9d624c Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 21 Oct 2014 22:43:31 -0400 Subject: greybus: ap: process the UniPro link up message The link up message is the event that tells the AP what device ID has been assigned to a particular interface on a module during enumeration. The link up is sent *only* after the hotplug event for a particular module has been sent to the AP. The link up payload must carry the Module ID and Interface ID to uniquely identify the struct gb_interface to which the Device ID has been assigned. After processing of the link up message, the interface's device_id field will contain the assigned Device ID so that the AP has the information necessary to issue network route commands. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 19 +++++++++++++++++++ drivers/staging/greybus/svc_msg.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 5b6335643bc0..c8f30f64e375 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -106,6 +106,9 @@ static void svc_handshake(struct svc_function_handshake *handshake, static void svc_management(struct svc_function_unipro_management *management, int payload_length, struct greybus_host_device *hd) { + struct gb_module *module; + struct gb_interface *interface; + if (payload_length != sizeof(struct svc_function_unipro_management)) { dev_err(hd->parent, "Illegal size of svc management message %d\n", @@ -114,6 +117,22 @@ static void svc_management(struct svc_function_unipro_management *management, } switch (management->management_packet_type) { + case SVC_MANAGEMENT_LINK_UP: + module = gb_module_find(hd, management->link_up.module_id); + if (!module) { + dev_err(hd->parent, "Module ID %d not found\n", + management->link_up.module_id); + return; + } + interface = gb_interface_find(module, + management->link_up.interface_id); + if (!interface) { + dev_err(hd->parent, "Interface ID %d not found\n", + management->link_up.interface_id); + return; + } + interface->device_id = management->link_up.device_id; + break; case SVC_MANAGEMENT_AP_DEVICE_ID: hd->device_id = management->ap_device_id.device_id; break; diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 4aa7e177f076..47597e6cd47b 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -55,6 +55,8 @@ struct svc_function_unipro_set_route { }; struct svc_function_unipro_link_up { + __u8 module_id; + __u8 interface_id; __u8 device_id; }; -- cgit v1.2.3-59-g8ed1b From e390b193d6abef0ed3316e3738cbadba8e9c7e17 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 22 Oct 2014 02:06:08 -0400 Subject: greybus: svc: remove unneeded fields from the unipro set route message payload CPort connections are being handled in the application layer connection protocol and the layer 3 switch doesn't care about them. Also, the switch doesn't care about a source device id when setting up the route table. Reduce the message to just the necessary destination device ID. As the SVC is aware of which switch port it found the module/interface and assigned the device ID, we can simply tell the SVC to set a route to the device ID it has reported to the AP as being active. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc_msg.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 47597e6cd47b..661cbaeb159e 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -48,10 +48,7 @@ struct svc_function_handshake { }; struct svc_function_unipro_set_route { - __u8 source_device_id; - __u8 source_cport_id; /* bottom 8 bits */ - __u8 destination_device_id; - __u8 destination_cport_id; /* bottom 8 bits */ + __u8 device_id; }; struct svc_function_unipro_link_up { -- cgit v1.2.3-59-g8ed1b From 060b93ddbb5dedd10bddb61664815752db56a9f3 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 22 Oct 2014 02:06:09 -0400 Subject: greybus: ap: add svc_set_route_send() command and use it on a link up event When the AP receives a link up event, request that the SVC set a route to the interface's device id (this device id has been previously reported to the AP). In the future, we may not always immediately set a route upon receiving a link up event but this is sufficient for the known use cases at this time. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index c8f30f64e375..46553b1cf312 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -103,6 +103,23 @@ static void svc_handshake(struct svc_function_handshake *handshake, svc_msg_send(svc_msg, hd); } +static void svc_set_route_send(struct gb_interface *interface, + struct greybus_host_device *hd) +{ + struct svc_msg *svc_msg; + + svc_msg = svc_msg_alloc(SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT); + if (!svc_msg) + return; + + svc_msg->header.function_id = SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT; + svc_msg->header.message_type = SVC_MSG_DATA; + svc_msg->header.payload_length = + cpu_to_le16(sizeof(struct svc_function_unipro_set_route)); + svc_msg->management.set_route.device_id = interface->device_id; + svc_msg_send(svc_msg, hd); +} + static void svc_management(struct svc_function_unipro_management *management, int payload_length, struct greybus_host_device *hd) { @@ -132,6 +149,7 @@ static void svc_management(struct svc_function_unipro_management *management, return; } interface->device_id = management->link_up.device_id; + svc_set_route_send(interface, hd); break; case SVC_MANAGEMENT_AP_DEVICE_ID: hd->device_id = management->ap_device_id.device_id; -- cgit v1.2.3-59-g8ed1b From 63e4a8ee8fdd9d86e7d2f16a99d82ee03fa0a785 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 22 Oct 2014 16:38:07 +0800 Subject: greybus: module: fix double free of module Also properly clean up all modules when you remove a host driver Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 19 +++++++++++++++---- drivers/staging/greybus/greybus.h | 2 -- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index bc27ad68cc85..480e12bac84a 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -190,6 +190,13 @@ err_module: greybus_module_release(&gmod->dev); } +static void gb_delete_module(struct gb_module *gmod) +{ + /* FIXME - tear down interfaces first */ + + device_del(&gmod->dev); +} + void gb_remove_module(struct greybus_host_device *hd, u8 module_id) { struct gb_module *gmod; @@ -202,15 +209,18 @@ void gb_remove_module(struct greybus_host_device *hd, u8 module_id) } if (found) - greybus_remove_device(gmod); + gb_delete_module(gmod); else dev_err(hd->parent, "module id %d remove error\n", module_id); } -void greybus_remove_device(struct gb_module *gmod) +static void gb_remove_modules(struct greybus_host_device *hd) { - device_del(&gmod->dev); - put_device(&gmod->dev); + struct gb_module *gmod, *temp; + + list_for_each_entry_safe(gmod, temp, &hd->modules, links) { + gb_delete_module(gmod); + } } static DEFINE_MUTEX(hd_mutex); @@ -248,6 +258,7 @@ EXPORT_SYMBOL_GPL(greybus_create_hd); void greybus_remove_hd(struct greybus_host_device *hd) { + gb_remove_modules(hd); kref_put_mutex(&hd->kref, free_hd, &hd_mutex); } EXPORT_SYMBOL_GPL(greybus_remove_hd); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index a4e1f4b2b18e..4baa37289334 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -242,8 +242,6 @@ void greybus_deregister(struct greybus_driver *driver); int greybus_disabled(void); -void greybus_remove_device(struct gb_module *gmod); - /* Internal functions to gb module, move to internal .h file eventually. */ void gb_add_module(struct greybus_host_device *hd, u8 module_id, -- cgit v1.2.3-59-g8ed1b From e816e3741956746d7a0e1992e4a4e96e8af5ab30 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 22 Oct 2014 02:04:28 -0500 Subject: greybus: time out operation requests Arrange for operation requests that takke too long to time out. At the moment, nothing happens when that occurs (other than a silly message getting printed). When the connection and operation and interface and module code are cleaned up properly, this event should most likely cause the affected module to get torn down. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 10 ++++++++++ drivers/staging/greybus/connection.h | 1 + drivers/staging/greybus/operation.c | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index e2340d8392ed..368f05662e3d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -102,6 +102,15 @@ static void gb_connection_hd_cport_id_free(struct gb_connection *connection) connection->hd_cport_id = CPORT_ID_BAD; } +static void connection_timeout(struct work_struct *work) +{ + struct gb_connection *connection; + + connection = + container_of(work, struct gb_connection, timeout_work.work); + printk("timeout!\n"); +} + /* * Set up a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -143,6 +152,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, INIT_LIST_HEAD(&connection->operations); connection->pending = RB_ROOT; atomic_set(&connection->op_cycle, 0); + INIT_DELAYED_WORK(&connection->timeout_work, connection_timeout); return connection; } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 685c1ffcb1ca..b5901a1b1a74 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -26,6 +26,7 @@ struct gb_connection { struct list_head operations; struct rb_root pending; /* awaiting reponse */ atomic_t op_cycle; + struct delayed_work timeout_work; void *private; }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index da455f019e33..e70e8a3faeae 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -19,6 +19,8 @@ */ #define GB_OPERATION_TYPE_RESPONSE 0x80 +#define CONNECTION_TIMEOUT_DEFAULT 1000 /* milliseconds */ + /* * XXX This needs to be coordinated with host driver parameters */ @@ -62,6 +64,8 @@ static void gb_operation_insert(struct gb_operation *operation) struct rb_node **link = &root->rb_node; struct rb_node *above = NULL; struct gb_operation_msg_hdr *header; + unsigned long timeout; + bool start_timer; __le16 wire_id; /* @@ -76,6 +80,16 @@ static void gb_operation_insert(struct gb_operation *operation) /* OK, insert the operation into its connection's tree */ spin_lock_irq(&gb_operations_lock); + /* + * We impose a time limit for requests to complete. If + * there are no requests pending there is no need for a + * timer. So if this will be the only one in flight we'll + * need to start the timer. Otherwise we just update the + * existing one to give this request a full timeout period + * to complete. + */ + start_timer = RB_EMPTY_ROOT(root); + while (*link) { struct gb_operation *other; @@ -89,15 +103,31 @@ static void gb_operation_insert(struct gb_operation *operation) } rb_link_node(node, above, link); rb_insert_color(node, root); - spin_unlock_irq(&gb_operations_lock); + + timeout = msecs_to_jiffies(CONNECTION_TIMEOUT_DEFAULT); + if (start_timer) + schedule_delayed_work(&connection->timeout_work, timeout); + else + mod_delayed_work(system_wq, &connection->timeout_work, timeout); } static void gb_operation_remove(struct gb_operation *operation) { + struct gb_connection *connection = operation->connection; + bool last_pending; + spin_lock_irq(&gb_operations_lock); - rb_erase(&operation->node, &operation->connection->pending); + rb_erase(&operation->node, &connection->pending); + last_pending = RB_EMPTY_ROOT(&connection->pending); spin_unlock_irq(&gb_operations_lock); + + /* + * If there are no more pending requests, we can stop the + * timeout timer. + */ + if (last_pending) + cancel_delayed_work(&connection->timeout_work); } static struct gb_operation * -- cgit v1.2.3-59-g8ed1b From e1158df0634ab771297fc7510dd78bcbe83e8c87 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 22 Oct 2014 02:04:29 -0500 Subject: greybus: define operation_cancel() Define a new function operation_cancel() that cancels an outstanding operation. Use it to clear out any operations that might be pending at the time a connection is torn down. Note: This code isn't really functional yet, partially because greybus_kill_gbuf() is not implemented. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 7 +++++++ drivers/staging/greybus/operation.c | 18 ++++++++++++++++++ drivers/staging/greybus/operation.h | 2 ++ 3 files changed, 27 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 368f05662e3d..9bcda993685a 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -162,12 +162,19 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, */ void gb_connection_destroy(struct gb_connection *connection) { + struct gb_operation *operation; + struct gb_operation *next; + if (WARN_ON(!connection)) return; /* XXX Need to wait for any outstanding requests to complete */ WARN_ON(!list_empty(&connection->operations)); + list_for_each_entry_safe(operation, next, &connection->operations, + links) { + gb_operation_cancel(operation); + } spin_lock_irq(&gb_connections_lock); list_del(&connection->interface_links); _gb_hd_connection_remove(connection); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index e70e8a3faeae..afb42d5e1941 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -501,6 +501,24 @@ void gb_connection_operation_recv(struct gb_connection *connection, queue_work(gb_operation_recv_workqueue, &operation->recv_work); } +/* + * Cancel an operation. + */ +void gb_operation_cancel(struct gb_operation *operation) +{ + int ret; + + operation->canceled = true; + ret = greybus_kill_gbuf(operation->request); + if (ret) + pr_warn("error %d killing request gbuf\n", ret); + if (operation->response) { + ret = greybus_kill_gbuf(operation->response); + if (ret) + pr_warn("error %d killing response gbuf\n", ret); + } +} + int gb_operation_init(void) { gb_operation_cache = kmem_cache_create("gb_operation_cache", diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 59aad3a38d17..1a8e6b94cb08 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -55,6 +55,7 @@ struct gb_operation { struct gbuf *request; struct gbuf *response; u16 id; + bool canceled; u8 result; struct work_struct recv_work; @@ -81,6 +82,7 @@ int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback); int gb_operation_response_send(struct gb_operation *operation); +void gb_operation_cancel(struct gb_operation *operation); int gb_operation_wait(struct gb_operation *operation); void gb_operation_complete(struct gb_operation *operation); -- cgit v1.2.3-59-g8ed1b From 36561f23a80b7c44320f34a3b6e6833616e50200 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 22 Oct 2014 02:04:30 -0500 Subject: greybus: define connection state Define the state of a connection. A connection will not be enabled until it has been successfully set up. Once it starts getting torn down its state will move to "being destroyed". Don't send any operation request messages unless the connection is enabled. And drop any incoming messages if if the connection is not enabled. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 23 +++++++++++++++++++---- drivers/staging/greybus/connection.h | 9 +++++++++ drivers/staging/greybus/operation.c | 6 ++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 9bcda993685a..b1e933fc7044 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -143,6 +143,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, connection->interface = interface; /* XXX refcount? */ connection->interface_cport_id = cport_id; connection->protocol = protocol; + connection->state = GB_CONNECTION_STATE_DISABLED; spin_lock_irq(&gb_connections_lock); _gb_hd_connection_insert(hd, connection); @@ -217,13 +218,20 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) */ int gb_connection_init(struct gb_connection *connection) { + int ret; + + /* Need to enable the connection to initialize it */ + connection->state = GB_CONNECTION_STATE_ENABLED; switch (connection->protocol) { case GREYBUS_PROTOCOL_I2C: - return gb_i2c_device_init(connection); + ret = gb_i2c_device_init(connection); + break; case GREYBUS_PROTOCOL_GPIO: - return gb_gpio_controller_init(connection); + ret = gb_gpio_controller_init(connection); + break; case GREYBUS_PROTOCOL_BATTERY: - return gb_battery_device_init(connection); + ret = gb_battery_device_init(connection); + break; case GREYBUS_PROTOCOL_CONTROL: case GREYBUS_PROTOCOL_AP: case GREYBUS_PROTOCOL_UART: @@ -233,13 +241,20 @@ int gb_connection_init(struct gb_connection *connection) default: gb_connection_err(connection, "unimplemented protocol %u", (u32)connection->protocol); + ret = -ENXIO; break; } - return -ENXIO; + + if (ret) + connection->state = GB_CONNECTION_STATE_ERROR; + + return ret; } void gb_connection_exit(struct gb_connection *connection) { + connection->state = GB_CONNECTION_STATE_DESTROYING; + switch (connection->protocol) { case GREYBUS_PROTOCOL_I2C: gb_i2c_device_exit(connection); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index b5901a1b1a74..a16e52a8ba18 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -13,6 +13,14 @@ #include "greybus.h" +enum gb_connection_state { + GB_CONNECTION_STATE_INVALID = 0, + GB_CONNECTION_STATE_DISABLED = 1, + GB_CONNECTION_STATE_ENABLED = 2, + GB_CONNECTION_STATE_ERROR = 3, + GB_CONNECTION_STATE_DESTROYING = 4, +}; + struct gb_connection { struct greybus_host_device *hd; struct gb_interface *interface; @@ -22,6 +30,7 @@ struct gb_connection { struct rb_node hd_node; struct list_head interface_links; enum greybus_protocol protocol; + enum gb_connection_state state; struct list_head operations; struct rb_root pending; /* awaiting reponse */ diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index afb42d5e1941..5d23d1977297 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -417,6 +417,9 @@ int gb_operation_request_send(struct gb_operation *operation, { int ret; + if (operation->connection->state != GB_CONNECTION_STATE_ENABLED) + return -ENOTCONN; + /* * XXX * I think the order of operations is going to be @@ -461,6 +464,9 @@ void gb_connection_operation_recv(struct gb_connection *connection, struct gbuf *gbuf; u16 msg_size; + if (connection->state != GB_CONNECTION_STATE_ENABLED) + return; + if (size > GB_OPERATION_MESSAGE_SIZE_MAX) { gb_connection_err(connection, "message too big"); return; -- cgit v1.2.3-59-g8ed1b From 525f1467bc22ad7d0866444b3a57c21ba64a0dd2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 22 Oct 2014 02:04:31 -0500 Subject: greybus: make svc_set_route_send() public Give svc_set_route_send() non-private scope so it can be used by a function outside "ap.c" in the next patch. Change its type so it can tell its caller if an error occurs. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 12 +++++++----- drivers/staging/greybus/greybus.h | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 46553b1cf312..7ea329cfa1eb 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -100,24 +100,26 @@ static void svc_handshake(struct svc_function_handshake *handshake, svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR; svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR; svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; - svc_msg_send(svc_msg, hd); + + (void)svc_msg_send(svc_msg, hd); } -static void svc_set_route_send(struct gb_interface *interface, +int svc_set_route_send(struct gb_interface *interface, struct greybus_host_device *hd) { struct svc_msg *svc_msg; svc_msg = svc_msg_alloc(SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT); if (!svc_msg) - return; + return -ENOMEM; svc_msg->header.function_id = SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT; svc_msg->header.message_type = SVC_MSG_DATA; svc_msg->header.payload_length = cpu_to_le16(sizeof(struct svc_function_unipro_set_route)); svc_msg->management.set_route.device_id = interface->device_id; - svc_msg_send(svc_msg, hd); + + return svc_msg_send(svc_msg, hd); } static void svc_management(struct svc_function_unipro_management *management, @@ -149,7 +151,7 @@ static void svc_management(struct svc_function_unipro_management *management, return; } interface->device_id = management->link_up.device_id; - svc_set_route_send(interface, hd); + (void)svc_set_route_send(interface, hd); break; case SVC_MANAGEMENT_AP_DEVICE_ID: hd->device_id = management->ap_device_id.device_id; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4baa37289334..617d55ca7e1d 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -275,7 +275,8 @@ void gb_gpio_controller_exit(struct gb_connection *connection); int gb_tty_init(void); void gb_tty_exit(void); - +int svc_set_route_send(struct gb_interface *interface, + struct greybus_host_device *hd); #endif /* __KERNEL__ */ #endif /* __LINUX_GREYBUS_H */ -- cgit v1.2.3-59-g8ed1b From c41b4f121240df46e5d901ad8aa9b9051b6c58e1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 22 Oct 2014 02:04:32 -0500 Subject: greybus: only initialize interfaces when up Rather than bringing up all interfaces described in the manifest, wait until we get a link up message, and at that time go initialize the link. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 17 ++++++++--------- drivers/staging/greybus/core.c | 2 -- drivers/staging/greybus/module.c | 24 +++++++++++++++++------- drivers/staging/greybus/module.h | 3 ++- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 7ea329cfa1eb..1a9112a21bbb 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -126,7 +126,7 @@ static void svc_management(struct svc_function_unipro_management *management, int payload_length, struct greybus_host_device *hd) { struct gb_module *module; - struct gb_interface *interface; + int ret; if (payload_length != sizeof(struct svc_function_unipro_management)) { dev_err(hd->parent, @@ -143,15 +143,14 @@ static void svc_management(struct svc_function_unipro_management *management, management->link_up.module_id); return; } - interface = gb_interface_find(module, - management->link_up.interface_id); - if (!interface) { - dev_err(hd->parent, "Interface ID %d not found\n", + ret = gb_module_interface_init(module, + management->link_up.interface_id, + management->link_up.device_id); + if (ret) + dev_err(hd->parent, "error %d initializing" + "module %hhu interface %hhu\n", + ret, management->link_up.module_id, management->link_up.interface_id); - return; - } - interface->device_id = management->link_up.device_id; - (void)svc_set_route_send(interface, hd); break; case SVC_MANAGEMENT_AP_DEVICE_ID: hd->device_id = management->ap_device_id.device_id; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 480e12bac84a..9f4ae1c63fa0 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -181,8 +181,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, if (retval) goto err_device; - gb_module_interfaces_init(gmod); - return; err_device: put_device(&gmod->dev); diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 50139f41fe20..bcb0ff098164 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -118,15 +118,25 @@ struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) return NULL; } -void gb_module_interfaces_init(struct gb_module *gmod) +int +gb_module_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) { struct gb_interface *interface; - int ret = 0; + int ret; - list_for_each_entry(interface, &gmod->interfaces, links) { - ret = gb_interface_connections_init(interface); - if (ret) - dev_err(gmod->hd->parent, - "module interface init error %d\n", ret); + interface = gb_interface_find(gmod, interface_id); + if (!interface) { + dev_err(gmod->hd->parent, "module %hhu not found\n", + interface_id); + return -ENOENT; } + ret = gb_interface_connections_init(interface); + if (ret) { + dev_err(gmod->hd->parent, "module interface init error %d\n", + ret); + return ret; + } + interface->device_id = device_id; + + return svc_set_route_send(interface, gmod->hd); } diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 82def46e5ab1..74ac4fdd3c83 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -55,6 +55,7 @@ void gb_module_destroy(struct gb_module *module); struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id); -void gb_module_interfaces_init(struct gb_module *gmod); +int gb_module_interface_init(struct gb_module *gmod, u8 module_id, + u8 device_id); #endif /* __MODULE_H */ -- cgit v1.2.3-59-g8ed1b From 8a9bf8a99669e365bdc89d27bbc29bf0216472f6 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 22 Oct 2014 03:22:48 -0400 Subject: greybus: svc: remove the DDB function message support We removed the DDB function messages from the spec as they are not needed. Now remove it from the code. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 16 ---------------- drivers/staging/greybus/svc_msg.h | 39 +++------------------------------------ 2 files changed, 3 insertions(+), 52 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 1a9112a21bbb..8156df08d9bd 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -201,19 +201,6 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, } } -static void svc_ddb(struct svc_function_ddb *ddb, - int payload_length, struct greybus_host_device *hd) -{ - /* - * Need to properly validate payload_length once we start - * to handle ddb messages, but for now, we don't, so no need to check - * anything. - */ - - /* What? An AP should not get this message */ - dev_err(hd->parent, "Got an svc DDB message???\n"); -} - static void svc_power(struct svc_function_power *power, int payload_length, struct greybus_host_device *hd) { @@ -315,9 +302,6 @@ static void ap_process_event(struct work_struct *work) case SVC_FUNCTION_HOTPLUG: svc_hotplug(&svc_msg->hotplug, payload_length, hd); break; - case SVC_FUNCTION_DDB: - svc_ddb(&svc_msg->ddb, payload_length, hd); - break; case SVC_FUNCTION_POWER: svc_power(&svc_msg->power, payload_length, hd); break; diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 661cbaeb159e..d76b6217beb0 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -18,10 +18,9 @@ enum svc_function_id { SVC_FUNCTION_HANDSHAKE = 0x00, SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, SVC_FUNCTION_HOTPLUG = 0x02, - SVC_FUNCTION_DDB = 0x03, - SVC_FUNCTION_POWER = 0x04, - SVC_FUNCTION_EPM = 0x05, - SVC_FUNCTION_SUSPEND = 0x06, + SVC_FUNCTION_POWER = 0x03, + SVC_FUNCTION_EPM = 0x04, + SVC_FUNCTION_SUSPEND = 0x05, }; enum svc_msg_type { @@ -92,37 +91,6 @@ struct svc_function_hotplug { __u8 data[0]; }; -enum svc_function_ddb_type { - SVC_DDB_GET = 0x00, - SVC_DDB_RESPONSE = 0x01, -}; - -/* XXX - * Will only the first interface block in a module be responsible - * for this? If a module has two interface blocks, will both supply - * the same information, or will it be partitioned? For now assume - * it's a per-module thing. - */ -struct svc_function_ddb_get { - __u8 module_id; - __u8 message_id; -}; - -struct svc_function_ddb_response { - __u8 module_id; - __u8 message_id; - __le16 descriptor_length; - __u8 ddb[0]; -}; - -struct svc_function_ddb { - __u8 ddb_type; /* enum svc_function_ddb_type */ - union { - struct svc_function_ddb_get ddb_get; - struct svc_function_ddb_response ddb_response; - }; -}; - enum svc_function_power_type { SVC_POWER_BATTERY_STATUS = 0x00, SVC_POWER_BATTERY_STATUS_REQUEST = 0x01, @@ -187,7 +155,6 @@ struct svc_msg { struct svc_function_handshake handshake; struct svc_function_unipro_management management; struct svc_function_hotplug hotplug; - struct svc_function_ddb ddb; struct svc_function_power power; struct svc_function_epm epm; struct svc_function_suspend suspend; -- cgit v1.2.3-59-g8ed1b From 65e50f95f18066442093d4c49528804a9bc8956f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 22 Oct 2014 05:36:17 -0500 Subject: greybus: set route before sending packets The route for a connection needs to be set *before* we initialize the connection. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index bcb0ff098164..b1ec0904fb28 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -130,13 +130,21 @@ gb_module_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) interface_id); return -ENOENT; } + + ret = svc_set_route_send(interface, gmod->hd); + if (ret) { + dev_err(gmod->hd->parent, "failed to set route (%d)\n", ret); + return ret; + } + ret = gb_interface_connections_init(interface); if (ret) { dev_err(gmod->hd->parent, "module interface init error %d\n", ret); + /* XXX clear route */ return ret; } interface->device_id = device_id; - return svc_set_route_send(interface, gmod->hd); + return 0; } -- cgit v1.2.3-59-g8ed1b From 2d5e4fa9dc2e629b1ace5fa715f1d6bb2a71d61f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 22 Oct 2014 05:36:18 -0500 Subject: greybus: update AP id service message Rename and renumber the values for the AP ID service message and related symbols to match the recently-updated spec. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 6 +++--- drivers/staging/greybus/svc_msg.h | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 8156df08d9bd..24a15e9c6009 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -136,6 +136,9 @@ static void svc_management(struct svc_function_unipro_management *management, } switch (management->management_packet_type) { + case SVC_MANAGEMENT_AP_ID: + hd->device_id = management->ap_id.device_id; + break; case SVC_MANAGEMENT_LINK_UP: module = gb_module_find(hd, management->link_up.module_id); if (!module) { @@ -152,9 +155,6 @@ static void svc_management(struct svc_function_unipro_management *management, ret, management->link_up.module_id, management->link_up.interface_id); break; - case SVC_MANAGEMENT_AP_DEVICE_ID: - hd->device_id = management->ap_device_id.device_id; - break; default: dev_err(hd->parent, "Unhandled UniPro management message\n"); } diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index d76b6217beb0..5b545129e63d 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -56,22 +56,23 @@ struct svc_function_unipro_link_up { __u8 device_id; }; -struct svc_function_ap_device_id { +struct svc_function_ap_id { + __u8 module_id; __u8 device_id; }; enum svc_function_management_event { - SVC_MANAGEMENT_SET_ROUTE = 0x00, + SVC_MANAGEMENT_AP_ID = 0x00, SVC_MANAGEMENT_LINK_UP = 0x01, - SVC_MANAGEMENT_AP_DEVICE_ID = 0x02, + SVC_MANAGEMENT_SET_ROUTE = 0x02, }; struct svc_function_unipro_management { __u8 management_packet_type; /* enum svc_function_management_event */ union { - struct svc_function_unipro_set_route set_route; + struct svc_function_ap_id ap_id; struct svc_function_unipro_link_up link_up; - struct svc_function_ap_device_id ap_device_id; + struct svc_function_unipro_set_route set_route; }; }; -- cgit v1.2.3-59-g8ed1b From f0f61b90427b776b884821cde483528580f6d630 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Oct 2014 17:34:46 +0800 Subject: greybus: hook up greybus to the driver model This patch hooks up modules, interfaces, and connections to the driver model. Now we have a correct hierarchy, and drivers can be correctly bound to the proper portions in the future. Devices are correctly reference counted and torn down in the proper order on removal of a module. Some basic sysfs attributes have been created for interfaces and connections. Module attributes are not working properly, but that will be fixed in future changes. This has been tested on Alex's machine, with multiple hotplug and unplug operations of a module working correctly. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 59 ++++++++++++++++++++++++++++-- drivers/staging/greybus/connection.h | 2 + drivers/staging/greybus/core.c | 50 ++++++------------------- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/interface.c | 71 +++++++++++++++++++++++++++++++----- drivers/staging/greybus/interface.h | 4 +- drivers/staging/greybus/module.c | 36 +++++++++++++----- 7 files changed, 161 insertions(+), 62 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b1e933fc7044..2d71679ff66c 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -111,6 +111,44 @@ static void connection_timeout(struct work_struct *work) printk("timeout!\n"); } +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_connection *connection = to_gb_connection(dev); + + return sprintf(buf, "%d", connection->state); +} +static DEVICE_ATTR_RO(state); + +static ssize_t protocol_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_connection *connection = to_gb_connection(dev); + + return sprintf(buf, "%d", connection->protocol); +} +static DEVICE_ATTR_RO(protocol); + +static struct attribute *connection_attrs[] = { + &dev_attr_state.attr, + &dev_attr_protocol.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(connection); + +static void gb_connection_release(struct device *dev) +{ + struct gb_connection *connection = to_gb_connection(dev); + + kfree(connection); +} + +static struct device_type greybus_connection_type = { + .name = "greybus_connection", + .release = gb_connection_release, +}; + /* * Set up a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -127,6 +165,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, { struct gb_connection *connection; struct greybus_host_device *hd; + int retval; connection = kzalloc(sizeof(*connection), GFP_KERNEL); if (!connection) @@ -145,6 +184,21 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, connection->protocol = protocol; connection->state = GB_CONNECTION_STATE_DISABLED; + connection->dev.parent = &interface->dev; + connection->dev.driver = NULL; + connection->dev.bus = &greybus_bus_type; + connection->dev.type = &greybus_connection_type; + connection->dev.groups = connection_groups; + device_initialize(&connection->dev); + dev_set_name(&connection->dev, "%s:%d", + dev_name(&interface->dev), cport_id); + + retval = device_add(&connection->dev); + if (retval) { + kfree(connection); + return NULL; + } + spin_lock_irq(&gb_connections_lock); _gb_hd_connection_insert(hd, connection); list_add_tail(&connection->interface_links, &interface->connections); @@ -182,9 +236,8 @@ void gb_connection_destroy(struct gb_connection *connection) spin_unlock_irq(&gb_connections_lock); gb_connection_hd_cport_id_free(connection); - /* kref_put(connection->interface); */ - /* kref_put(connection->hd); */ - kfree(connection); + + device_del(&connection->dev); } u16 gb_connection_operation_id(struct gb_connection *connection) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index a16e52a8ba18..19dd91dae062 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -24,6 +24,7 @@ enum gb_connection_state { struct gb_connection { struct greybus_host_device *hd; struct gb_interface *interface; + struct device dev; u16 hd_cport_id; u16 interface_cport_id; @@ -39,6 +40,7 @@ struct gb_connection { void *private; }; +#define to_gb_connection(d) container_of(d, struct gb_connection, dev) struct gb_connection *gb_connection_create(struct gb_interface *interface, u16 cport_id, enum greybus_protocol protocol); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9f4ae1c63fa0..853bfd6503f6 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -48,10 +48,14 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) /* struct gb_module *gmod = to_gb_module(dev); */ /* FIXME - add some uevents here... */ + + /* FIXME - be sure to check the type to know how to handle modules and + * interfaces differently */ + return 0; } -static struct bus_type greybus_bus_type = { +struct bus_type greybus_bus_type = { .name = "greybus", .match = greybus_module_match, .uevent = greybus_uevent, @@ -115,18 +119,6 @@ void greybus_deregister(struct greybus_driver *driver) EXPORT_SYMBOL_GPL(greybus_deregister); -static void greybus_module_release(struct device *dev) -{ - struct gb_module *gmod = to_gb_module(dev); - - gb_module_destroy(gmod); -} - -static struct device_type greybus_module_type = { - .name = "greybus_module", - .release = greybus_module_release, -}; - static const struct greybus_module_id fake_greybus_module_id = { GREYBUS_DEVICE(0x42, 0x42) }; @@ -142,7 +134,6 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size) { struct gb_module *gmod; - int retval; gmod = gb_module_create(hd, module_id); if (!gmod) { @@ -168,31 +159,10 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, * configuring the switch to allow them to communicate). */ - gmod->dev.parent = hd->parent; - gmod->dev.driver = NULL; - gmod->dev.bus = &greybus_bus_type; - gmod->dev.type = &greybus_module_type; - gmod->dev.groups = greybus_module_groups; - gmod->dev.dma_mask = hd->parent->dma_mask; - device_initialize(&gmod->dev); - dev_set_name(&gmod->dev, "%d", module_id); - - retval = device_add(&gmod->dev); - if (retval) - goto err_device; - return; -err_device: - put_device(&gmod->dev); -err_module: - greybus_module_release(&gmod->dev); -} - -static void gb_delete_module(struct gb_module *gmod) -{ - /* FIXME - tear down interfaces first */ - device_del(&gmod->dev); +err_module: + gb_module_destroy(gmod); } void gb_remove_module(struct greybus_host_device *hd, u8 module_id) @@ -207,7 +177,7 @@ void gb_remove_module(struct greybus_host_device *hd, u8 module_id) } if (found) - gb_delete_module(gmod); + gb_module_destroy(gmod); else dev_err(hd->parent, "module id %d remove error\n", module_id); } @@ -217,7 +187,7 @@ static void gb_remove_modules(struct greybus_host_device *hd) struct gb_module *gmod, *temp; list_for_each_entry_safe(gmod, temp, &hd->modules, links) { - gb_delete_module(gmod); + gb_module_destroy(gmod); } } @@ -256,6 +226,8 @@ EXPORT_SYMBOL_GPL(greybus_create_hd); void greybus_remove_hd(struct greybus_host_device *hd) { + /* Tear down all modules that happen to be associated with this host + * controller */ gb_remove_modules(hd); kref_put_mutex(&hd->kref, free_hd, &hd_mutex); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 617d55ca7e1d..f287f3b0a3e5 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -261,6 +261,7 @@ int gb_register_cport_complete(struct gb_module *gmod, void *context); void gb_deregister_cport_complete(u16 cport_id); +extern struct bus_type greybus_bus_type; extern const struct attribute_group *greybus_module_groups[]; int gb_i2c_device_init(struct gb_connection *connection); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 0415db51051e..a7375a2809d5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -8,6 +8,35 @@ #include "greybus.h" +static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_interface *interface = to_gb_interface(dev); + + return sprintf(buf, "%d", interface->device_id); +} +static DEVICE_ATTR_RO(device_id); + +static struct attribute *interface_attrs[] = { + &dev_attr_device_id.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(interface); + +static void gb_interface_release(struct device *dev) +{ + struct gb_interface *interface = to_gb_interface(dev); + + kfree(interface); +} + +static struct device_type greybus_interface_type = { + .name = "greybus_interface", + .release = gb_interface_release, +}; + + /* XXX This could be per-host device or per-module */ static DEFINE_SPINLOCK(gb_interfaces_lock); @@ -26,6 +55,7 @@ struct gb_interface * gb_interface_create(struct gb_module *gmod, u8 interface_id) { struct gb_interface *interface; + int retval; interface = kzalloc(sizeof(*interface), GFP_KERNEL); if (!interface) @@ -33,8 +63,25 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) interface->gmod = gmod; /* XXX refcount? */ interface->id = interface_id; + interface->device_id = 0xff; /* Invalid device id to start with */ INIT_LIST_HEAD(&interface->connections); + /* Build up the interface device structures and register it with the + * driver core */ + interface->dev.parent = &gmod->dev; + interface->dev.driver = NULL; + interface->dev.bus = &greybus_bus_type; + interface->dev.type = &greybus_interface_type; + interface->dev.groups = interface_groups; + device_initialize(&interface->dev); + dev_set_name(&interface->dev, "%d:%d", gmod->module_id, interface_id); + + retval = device_add(&interface->dev); + if (retval) { + kfree(interface); + return NULL; + } + spin_lock_irq(&gb_interfaces_lock); list_add_tail(&interface->links, &gmod->interfaces); spin_unlock_irq(&gb_interfaces_lock); @@ -45,19 +92,21 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) /* * Tear down a previously set up interface. */ -void gb_interface_destroy(struct gb_interface *interface) +void gb_interface_destroy(struct gb_module *gmod) { - if (WARN_ON(!interface)) + struct gb_interface *interface; + struct gb_interface *temp; + + if (WARN_ON(!gmod)) return; spin_lock_irq(&gb_interfaces_lock); - list_del(&interface->links); + list_for_each_entry_safe(interface, temp, &gmod->interfaces, links) { + list_del(&interface->links); + gb_interface_connections_exit(interface); + device_del(&interface->dev); + } spin_unlock_irq(&gb_interfaces_lock); - - gb_interface_connections_exit(interface); - - /* kref_put(gmod); */ - kfree(interface); } struct gb_interface *gb_interface_find(struct gb_module *module, @@ -65,9 +114,13 @@ struct gb_interface *gb_interface_find(struct gb_module *module, { struct gb_interface *interface; + spin_lock_irq(&gb_interfaces_lock); list_for_each_entry(interface, &module->interfaces, links) - if (interface->id == interface_id) + if (interface->id == interface_id) { + spin_unlock_irq(&gb_interfaces_lock); return interface; + } + spin_unlock_irq(&gb_interfaces_lock); return NULL; } diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 907b6341665c..50b0317d267e 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -12,6 +12,7 @@ #include struct gb_interface { + struct device dev; struct gb_module *gmod; u8 id; u8 device_id; @@ -19,9 +20,10 @@ struct gb_interface { struct list_head links; /* module->interfaces */ }; +#define to_gb_interface(d) container_of(d, struct gb_interface, dev) struct gb_interface *gb_interface_create(struct gb_module *gmod, u8 module_id); -void gb_interface_destroy(struct gb_interface *interface); +void gb_interface_destroy(struct gb_module *gmod); struct gb_interface *gb_interface_find(struct gb_module *gmod, u8 interface_id); diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index b1ec0904fb28..f9415c0f735e 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -44,15 +44,18 @@ const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, return NULL; } -static void gb_module_interfaces_exit(struct gb_module *gmod) +static void greybus_module_release(struct device *dev) { - struct gb_interface *interface; - struct gb_interface *next; + struct gb_module *gmod = to_gb_module(dev); - list_for_each_entry_safe(interface, next, &gmod->interfaces, links) - gb_interface_destroy(interface); + kfree(gmod); } +static struct device_type greybus_module_type = { + .name = "greybus_module", + .release = greybus_module_release, +}; + /* * A Greybus module represents a user-replacable component on an Ara * phone. @@ -65,6 +68,7 @@ static void gb_module_interfaces_exit(struct gb_module *gmod) struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) { struct gb_module *gmod; + int retval; gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); if (!gmod) @@ -78,6 +82,21 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) list_add_tail(&gmod->links, &hd->modules); spin_unlock_irq(&gb_modules_lock); + gmod->dev.parent = hd->parent; + gmod->dev.driver = NULL; + gmod->dev.bus = &greybus_bus_type; + gmod->dev.type = &greybus_module_type; + gmod->dev.groups = greybus_module_groups; + gmod->dev.dma_mask = hd->parent->dma_mask; + device_initialize(&gmod->dev); + dev_set_name(&gmod->dev, "%d", module_id); + + retval = device_add(&gmod->dev); + if (retval) { + put_device(&gmod->dev); + return NULL; + } + return gmod; } @@ -93,18 +112,15 @@ void gb_module_destroy(struct gb_module *gmod) list_del(&gmod->links); spin_unlock_irq(&gb_modules_lock); - gb_module_interfaces_exit(gmod); /* XXX Do something with gmod->gb_tty */ - put_device(&gmod->dev); - /* kfree(gmod->dev->name); */ + gb_interface_destroy(gmod); kfree(gmod->product_string); kfree(gmod->vendor_string); /* kref_put(module->hd); */ - - kfree(gmod); + device_del(&gmod->dev); } struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) -- cgit v1.2.3-59-g8ed1b From c4a432d3db8b9fa0145048ac4a84bac54c7a9d73 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 24 Oct 2014 05:02:02 -0500 Subject: greybus: fix repeated input errors I screwed up the error handling in a patch the other day. If we get an error on an input URB we should not re-submit it. Signed-off-by: Alex Elder --- drivers/staging/greybus/es1-ap-usb.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 12eb9b2bee0f..bd416b4849bd 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -319,9 +319,10 @@ static void svc_in_callback(struct urb *urb) int retval; if (status) { - if (status != -EAGAIN) - dev_err(dev, "urb svc in error %d (dropped)\n", status); - goto exit; + if (status == -EAGAIN) + goto exit; + dev_err(dev, "urb svc in error %d (dropped)\n", status); + return; } /* We have a message, create a new message structure, add it to the @@ -346,10 +347,10 @@ static void cport_in_callback(struct urb *urb) u8 *data; if (status) { - if (status != -EAGAIN) - dev_err(dev, "urb cport in error %d (dropped)\n", - status); - goto exit; + if (status == -EAGAIN) + goto exit; + dev_err(dev, "urb cport in error %d (dropped)\n", status); + return; } /* The size has to be at least one, for the cport id */ -- cgit v1.2.3-59-g8ed1b From 44dd970be94a468c60e0745536f29115dd40d368 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Oct 2014 17:54:02 +0800 Subject: greybus: module: enable all sysfs attributes We were thinking that module attributes were known at the time the device was created in the system, so we could query them to know if the sysfs file was present or not. Unfortunatly that's not the case, we create the device before we parse the values, so just always show the sysfs attributes. If there is no such attribute, the sysfs file will be empty (i.e. for the string attributes.) Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sysfs.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 2777b8cf5b3a..42d54eb07377 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -74,27 +74,8 @@ static struct attribute *module_attrs[] = { NULL, }; -static umode_t module_attrs_are_visible(struct kobject *kobj, - struct attribute *a, int n) -{ - struct gb_module *gmod = to_gb_module(kobj_to_dev(kobj)); - umode_t mode = a->mode; - - if (a == &dev_attr_module_vendor_string.attr && gmod->vendor_string) - return mode; - if (a == &dev_attr_module_product_string.attr && gmod->product_string) - return mode; - if (gmod->vendor || gmod->product || gmod->version) - return mode; - if (gmod->unique_id) - return mode; - - return 0; -} - static struct attribute_group module_attr_grp = { .attrs = module_attrs, - .is_visible = module_attrs_are_visible, }; const struct attribute_group *greybus_module_groups[] = { -- cgit v1.2.3-59-g8ed1b From 3e6d5f3a6103b20c70f4d75b90d2c798045cc7ef Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Oct 2014 18:33:59 +0800 Subject: greybus: i2c: point to the proper parent device Use the connection, not the host controller, as the parent device of the i2c device. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c-gb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index e1a0ed9dc6f2..9ecfd795ea84 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -496,8 +496,7 @@ int gb_i2c_device_init(struct gb_connection *connection) adapter->timeout = gb_i2c_dev->timeout_msec * HZ / 1000; adapter->retries = gb_i2c_dev->retries; - /* XXX I think this parent device is wrong, but it uses existing code */ - adapter->dev.parent = &connection->interface->gmod->dev; + adapter->dev.parent = &connection->dev; snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); i2c_set_adapdata(adapter, gb_i2c_dev); -- cgit v1.2.3-59-g8ed1b From 066799c18ff476642858fb0e4050c5c600c47e4c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Oct 2014 18:46:15 +0800 Subject: greybus: module: don't create duplicate module ids If we somehow get a hotplug event for a module id that we already have created[1], don't try to create it again, or sysfs will complain loudly. Instead, abort the creation properly. [1] If, for example, you happened to run a script on a greybus emulator twice in a row... Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/module.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index f9415c0f735e..54e8f9e68d25 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -70,6 +70,13 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) struct gb_module *gmod; int retval; + gmod = gb_module_find(hd, module_id); + if (gmod) { + dev_err(hd->parent, "Duplicate module id %d will not be created\n", + module_id); + return NULL; + } + gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); if (!gmod) return NULL; -- cgit v1.2.3-59-g8ed1b From f9624ded90d0f4f6077b243d8526da671ba58d25 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 12:30:15 +0800 Subject: greybus: operation: fix some sparse warnings One of which was "real". Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5d23d1977297..c94e50965f28 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -274,8 +274,14 @@ static void gb_operation_gbuf_complete(struct gbuf *gbuf) header = operation->response_payload; else header = NULL; - id = header ? (int)header->id : -1; - type = header ? (int)header->type : -1; + + if (header) { + id = le16_to_cpu(header->id); + type = header->type; + } else { + id = -1; + type = -1; + } gb_connection_err(operation->connection, "operation %d type %d gbuf error %d", @@ -292,8 +298,9 @@ static void gb_operation_gbuf_complete(struct gbuf *gbuf) * initialize it here (it'll be overwritten by the incoming * message). */ -struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, - u8 type, size_t size, bool data_out) +static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, + u8 type, size_t size, + bool data_out) { struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; -- cgit v1.2.3-59-g8ed1b From d4c8247b56a3c3f9c8c879189338d2fa13a281a0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 12:33:47 +0800 Subject: greybus: gpio-gb: fix some endian sparse warnings that were real. Not like we are ever going to use a BE cpu, but it's good to be "correct"... Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 32bf45ff7e8b..069ee69dc0e7 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -551,7 +551,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con return -ENOMEM; request = operation->request_payload; request->which = which; - request->usec = le16_to_cpu(debounce_usec); + request->usec = cpu_to_le16(debounce_usec); /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); @@ -566,9 +566,9 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con response->status); ret = -EIO; } else { - gb_gpio_controller->lines[which].debounce_usec = request->usec; -printk("%s: debounce of %u is now %hu usec\n", __func__, - which, gb_gpio_controller->lines[which].debounce_usec); + gb_gpio_controller->lines[which].debounce_usec = le16_to_cpu(request->usec); + printk("%s: debounce of %u is now %hu usec\n", __func__, which, + gb_gpio_controller->lines[which].debounce_usec); } out: gb_operation_destroy(operation); @@ -709,7 +709,7 @@ static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) return; /* XXX */ } -int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controller) +static int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controller) { u32 line_count; size_t size; -- cgit v1.2.3-59-g8ed1b From d81448849a6a919bdc6366b44a7664f817c94d84 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 13:31:01 +0800 Subject: greybus: gbuf: implement gbuf_kill_gbuf() Hook up gbuf_kill_gbuf() by implementing yet-another-host-controller callback and a gbuf-specific pointer to hold the tracking data the hcd needs in order to be able to abort a transfer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/es1-ap-usb.c | 15 +++++++++++++++ drivers/staging/greybus/gbuf.c | 8 ++++++-- drivers/staging/greybus/greybus.h | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index bd416b4849bd..a2072944325d 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -224,6 +224,8 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) if (!urb) return -ENOMEM; + gbuf->hcd_data = urb; + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), buffer, gbuf->transfer_buffer_length + 1, @@ -232,12 +234,24 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) return retval; } +static int abort_gbuf(struct gbuf *gbuf) +{ + struct urb *urb = gbuf->hcd_data; + + if (!urb) + return -EINVAL; + + usb_kill_urb(urb); + return 0; +} + static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .alloc_gbuf_data = alloc_gbuf_data, .free_gbuf_data = free_gbuf_data, .submit_svc = submit_svc, .submit_gbuf = submit_gbuf, + .abort_gbuf = abort_gbuf, }; /* Common function to report consistent warnings based on URB status */ @@ -387,6 +401,7 @@ static void cport_out_callback(struct urb *urb) /* Record whether the transfer was successful */ gbuf->status = check_urb_status(urb); + gbuf->hcd_data = NULL; /* * See if this was an urb in our pool, if so mark it "free", otherwise diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 9b435af27cca..726a1f4bac41 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -106,8 +106,12 @@ int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) int greybus_kill_gbuf(struct gbuf *gbuf) { - // FIXME - implement - return -ENOMEM; + struct greybus_host_device *hd = gbuf->connection->hd; + + if (gbuf->status != -EINPROGRESS) + return -EINVAL; + + return hd->driver->abort_gbuf(gbuf); } #define MAX_CPORTS 1024 diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f287f3b0a3e5..8b6ea0544598 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -134,6 +134,7 @@ struct gbuf { bool outbound; /* AP-relative data direction */ void *context; + void *hcd_data; /* for the HCD to track the gbuf */ gbuf_complete_t complete; }; @@ -169,6 +170,7 @@ struct greybus_host_driver { int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); int (*submit_gbuf)(struct gbuf *gbuf, gfp_t gfp_mask); + int (*abort_gbuf)(struct gbuf *gbuf); }; struct greybus_host_device { -- cgit v1.2.3-59-g8ed1b From 724b619dfbe1238eb8b9bd4916eeaf40b5640b28 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 13:32:27 +0800 Subject: greybus: core: check for valid hcd callbacks When registering a host controller, verify that all of the needed callbacks are present, so we don't have to do the testing on any "hot" paths when we want to send real data. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder alloc_gbuf_data) || + (!driver->free_gbuf_data) || + (!driver->submit_svc) || + (!driver->submit_gbuf) || + (!driver->abort_gbuf)) { + pr_err("Must implement all greybus_host_driver callbacks!\n"); + return NULL; + } + hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL); if (!hd) return NULL; -- cgit v1.2.3-59-g8ed1b From 4afbba0703b2b2a2a9355e5d29a7a8ddcff92953 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 14:01:06 +0800 Subject: greybus: core: make greybus_kill_gbuf not return a value We can't do anything if killing a gbuf fails, so just make this function "always" be successful. At the same time, make the host controller function also be called "kill_gbuf" to keep the terminology in sync. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/es1-ap-usb.c | 7 +++---- drivers/staging/greybus/gbuf.c | 6 +++--- drivers/staging/greybus/greybus.h | 4 ++-- drivers/staging/greybus/operation.c | 15 ++++----------- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 1436b3d9427d..61fd6906541d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -216,7 +216,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver (!driver->free_gbuf_data) || (!driver->submit_svc) || (!driver->submit_gbuf) || - (!driver->abort_gbuf)) { + (!driver->kill_gbuf)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); return NULL; } diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index a2072944325d..b8784b14160d 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -234,15 +234,14 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) return retval; } -static int abort_gbuf(struct gbuf *gbuf) +static void kill_gbuf(struct gbuf *gbuf) { struct urb *urb = gbuf->hcd_data; if (!urb) - return -EINVAL; + return; usb_kill_urb(urb); - return 0; } static struct greybus_host_driver es1_driver = { @@ -251,7 +250,7 @@ static struct greybus_host_driver es1_driver = { .free_gbuf_data = free_gbuf_data, .submit_svc = submit_svc, .submit_gbuf = submit_gbuf, - .abort_gbuf = abort_gbuf, + .kill_gbuf = kill_gbuf, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 726a1f4bac41..4f591aa07863 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -104,14 +104,14 @@ int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) return hd->driver->submit_gbuf(gbuf, gfp_mask); } -int greybus_kill_gbuf(struct gbuf *gbuf) +void greybus_kill_gbuf(struct gbuf *gbuf) { struct greybus_host_device *hd = gbuf->connection->hd; if (gbuf->status != -EINPROGRESS) - return -EINVAL; + return; - return hd->driver->abort_gbuf(gbuf); + hd->driver->kill_gbuf(gbuf); } #define MAX_CPORTS 1024 diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 8b6ea0544598..1e3f31d4a058 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -170,7 +170,7 @@ struct greybus_host_driver { int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); int (*submit_gbuf)(struct gbuf *gbuf, gfp_t gfp_mask); - int (*abort_gbuf)(struct gbuf *gbuf); + void (*kill_gbuf)(struct gbuf *gbuf); }; struct greybus_host_device { @@ -203,7 +203,7 @@ struct gbuf *greybus_get_gbuf(struct gbuf *gbuf); #define greybus_put_gbuf greybus_free_gbuf int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags); -int greybus_kill_gbuf(struct gbuf *gbuf); +void greybus_kill_gbuf(struct gbuf *gbuf); struct greybus_driver { diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c94e50965f28..9b889b169bf5 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -175,7 +175,7 @@ int gb_operation_wait(struct gb_operation *operation) ret = wait_for_completion_interruptible(&operation->completion); /* If interrupted, cancel the in-flight buffer */ if (ret < 0) - ret = greybus_kill_gbuf(operation->request); + greybus_kill_gbuf(operation->request); return ret; } @@ -519,17 +519,10 @@ void gb_connection_operation_recv(struct gb_connection *connection, */ void gb_operation_cancel(struct gb_operation *operation) { - int ret; - operation->canceled = true; - ret = greybus_kill_gbuf(operation->request); - if (ret) - pr_warn("error %d killing request gbuf\n", ret); - if (operation->response) { - ret = greybus_kill_gbuf(operation->response); - if (ret) - pr_warn("error %d killing response gbuf\n", ret); - } + greybus_kill_gbuf(operation->request); + if (operation->response) + greybus_kill_gbuf(operation->response); } int gb_operation_init(void) -- cgit v1.2.3-59-g8ed1b From 708971e43c29d103c22409cf66b6213033518da3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 15:40:09 +0800 Subject: greybus: operation: make the timeout a per-operation thing, not per-connection An operation is what can timeout, not a connection itself. So notify the operation timedout, and the connection can then do with it as it sees fit, if necessary. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 10 --------- drivers/staging/greybus/connection.h | 1 - drivers/staging/greybus/operation.c | 41 ++++++++++++++++++++++++------------ drivers/staging/greybus/operation.h | 3 ++- 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2d71679ff66c..bbae16346050 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -102,15 +102,6 @@ static void gb_connection_hd_cport_id_free(struct gb_connection *connection) connection->hd_cport_id = CPORT_ID_BAD; } -static void connection_timeout(struct work_struct *work) -{ - struct gb_connection *connection; - - connection = - container_of(work, struct gb_connection, timeout_work.work); - printk("timeout!\n"); -} - static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -207,7 +198,6 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, INIT_LIST_HEAD(&connection->operations); connection->pending = RB_ROOT; atomic_set(&connection->op_cycle, 0); - INIT_DELAYED_WORK(&connection->timeout_work, connection_timeout); return connection; } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 19dd91dae062..17fde8fa285d 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -36,7 +36,6 @@ struct gb_connection { struct list_head operations; struct rb_root pending; /* awaiting reponse */ atomic_t op_cycle; - struct delayed_work timeout_work; void *private; }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 9b889b169bf5..736c2c167f85 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -19,7 +19,7 @@ */ #define GB_OPERATION_TYPE_RESPONSE 0x80 -#define CONNECTION_TIMEOUT_DEFAULT 1000 /* milliseconds */ +#define OPERATION_TIMEOUT_DEFAULT 1000 /* milliseconds */ /* * XXX This needs to be coordinated with host driver parameters @@ -105,29 +105,25 @@ static void gb_operation_insert(struct gb_operation *operation) rb_insert_color(node, root); spin_unlock_irq(&gb_operations_lock); - timeout = msecs_to_jiffies(CONNECTION_TIMEOUT_DEFAULT); + timeout = msecs_to_jiffies(OPERATION_TIMEOUT_DEFAULT); if (start_timer) - schedule_delayed_work(&connection->timeout_work, timeout); + schedule_delayed_work(&operation->timeout_work, timeout); else - mod_delayed_work(system_wq, &connection->timeout_work, timeout); + mod_delayed_work(system_wq, &operation->timeout_work, timeout); } static void gb_operation_remove(struct gb_operation *operation) { struct gb_connection *connection = operation->connection; - bool last_pending; + /* Shut down our timeout timer */ + cancel_delayed_work(&operation->timeout_work); + + /* Take us off of the list of pending operations */ spin_lock_irq(&gb_operations_lock); rb_erase(&operation->node, &connection->pending); - last_pending = RB_EMPTY_ROOT(&connection->pending); spin_unlock_irq(&gb_operations_lock); - /* - * If there are no more pending requests, we can stop the - * timeout timer. - */ - if (last_pending) - cancel_delayed_work(&connection->timeout_work); } static struct gb_operation * @@ -159,7 +155,7 @@ gb_operation_find(struct gb_connection *connection, u16 id) * any waiters. Otherwise we assume calling the completion is enough * and nobody else will be waiting. */ -void gb_operation_complete(struct gb_operation *operation) +static void gb_operation_complete(struct gb_operation *operation) { if (operation->callback) operation->callback(operation); @@ -247,6 +243,24 @@ static void gb_operation_recv_work(struct work_struct *recv_work) greybus_gbuf_finished(operation->response); } +/* + * Timeout call for the operation. + * + * If this fires, something went wrong, so mark the result as timed out, and + * run the completion handler, which (hopefully) should clean up the operation + * properly. + */ +static void operation_timeout(struct work_struct *work) +{ + struct gb_operation *operation; + + operation = container_of(work, struct gb_operation, timeout_work.work); + printk("timeout!\n"); + + operation->result = GB_OP_TIMEOUT; + gb_operation_complete(operation); +} + /* * Buffer completion function. We get notified whenever any buffer * completes. For outbound messages, this tells us that the message @@ -376,6 +390,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, INIT_WORK(&operation->recv_work, gb_operation_recv_work); operation->callback = NULL; /* set at submit time */ init_completion(&operation->completion); + INIT_DELAYED_WORK(&operation->timeout_work, operation_timeout); spin_lock_irq(&gb_operations_lock); list_add_tail(&operation->links, &connection->operations); diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 1a8e6b94cb08..965ad9c1aa8c 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -18,6 +18,7 @@ enum gb_operation_status { GB_OP_INTERRUPTED = 3, GB_OP_RETRY = 4, GB_OP_PROTOCOL_BAD = 5, + GB_OP_TIMEOUT = 0xff, }; /* @@ -61,6 +62,7 @@ struct gb_operation { struct work_struct recv_work; gb_operation_callback callback; /* If asynchronous */ struct completion completion; /* Used if no callback */ + struct delayed_work timeout_work; struct list_head links; /* connection->operations */ struct rb_node node; /* connection->pending */ @@ -84,7 +86,6 @@ int gb_operation_response_send(struct gb_operation *operation); void gb_operation_cancel(struct gb_operation *operation); int gb_operation_wait(struct gb_operation *operation); -void gb_operation_complete(struct gb_operation *operation); int gb_operation_init(void); void gb_operation_exit(void); -- cgit v1.2.3-59-g8ed1b From 81d631cae83e32e3dcab0d20d9c387592e1fa230 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 27 Oct 2014 03:48:31 -0500 Subject: greybus: gpio: kill gpio_controller->gpio This field is never used (and not needed) so get rid of it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 069ee69dc0e7..8781f816d968 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -29,7 +29,6 @@ struct gb_gpio_controller { struct gb_gpio_line *lines; struct gpio_chip chip; - struct gpio_chip *gpio; }; #define gpio_chip_to_gb_gpio_controller(chip) \ container_of(chip, struct gb_gpio_controller, chip) -- cgit v1.2.3-59-g8ed1b From 81f4e22732f0a29826168899d7e62839812d34dd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 27 Oct 2014 03:48:32 -0500 Subject: greybus: embed the i2c adapter struct We don't need to dynamically allocate the i2c adapter structure, we can just embed it right in struct gb_i2c_device. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c-gb.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 9ecfd795ea84..e20801169b8d 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -22,7 +22,7 @@ struct gb_i2c_device { u16 timeout_msec; u8 retries; - struct i2c_adapter *adapter; + struct i2c_adapter adapter; }; /* Version of the Greybus i2c protocol we support */ @@ -469,7 +469,7 @@ static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) int gb_i2c_device_init(struct gb_connection *connection) { struct gb_i2c_device *gb_i2c_dev; - struct i2c_adapter *adapter = NULL; + struct i2c_adapter *adapter; int ret; gb_i2c_dev = kzalloc(sizeof(*gb_i2c_dev), GFP_KERNEL); @@ -482,13 +482,8 @@ int gb_i2c_device_init(struct gb_connection *connection) if (ret) goto out_err; - /* Looks good; allocate and set up our i2c adapter */ - adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); - if (!adapter) { - ret = -ENOMEM; - goto out_err; - } - + /* Looks good; up our i2c adapter */ + adapter = &gb_i2c_dev->adapter; adapter->owner = THIS_MODULE; adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adapter->algo = &gb_i2c_algorithm; @@ -504,12 +499,10 @@ int gb_i2c_device_init(struct gb_connection *connection) if (ret) goto out_err; - gb_i2c_dev->adapter = adapter; connection->private = gb_i2c_dev; return 0; out_err: - kfree(adapter); /* kref_put(gb_i2c_dev->connection) */ kfree(gb_i2c_dev); @@ -520,8 +513,7 @@ void gb_i2c_device_exit(struct gb_connection *connection) { struct gb_i2c_device *gb_i2c_dev = connection->private; - i2c_del_adapter(gb_i2c_dev->adapter); - kfree(gb_i2c_dev->adapter); + i2c_del_adapter(&gb_i2c_dev->adapter); /* kref_put(gb_i2c_dev->connection) */ kfree(gb_i2c_dev); } -- cgit v1.2.3-59-g8ed1b From aed0bc6e6856d467061e8de2f3adc713c3561628 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 17:32:34 +0800 Subject: greybus: uart-gb: convert over to the connection interface Move the uart code over to use the "new" connection interface, instead of the "old" module interface. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 ++++++-- drivers/staging/greybus/greybus.h | 3 +++ drivers/staging/greybus/uart-gb.c | 29 ++++++++++------------------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2d71679ff66c..2d3421f6aa10 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -285,9 +285,11 @@ int gb_connection_init(struct gb_connection *connection) case GREYBUS_PROTOCOL_BATTERY: ret = gb_battery_device_init(connection); break; + case GREYBUS_PROTOCOL_UART: + ret = gb_uart_device_init(connection); + break; case GREYBUS_PROTOCOL_CONTROL: case GREYBUS_PROTOCOL_AP: - case GREYBUS_PROTOCOL_UART: case GREYBUS_PROTOCOL_HID: case GREYBUS_PROTOCOL_LED: case GREYBUS_PROTOCOL_VENDOR: @@ -318,9 +320,11 @@ void gb_connection_exit(struct gb_connection *connection) case GREYBUS_PROTOCOL_BATTERY: gb_battery_device_exit(connection); break; + case GREYBUS_PROTOCOL_UART: + gb_uart_device_exit(connection); + break; case GREYBUS_PROTOCOL_CONTROL: case GREYBUS_PROTOCOL_AP: - case GREYBUS_PROTOCOL_UART: case GREYBUS_PROTOCOL_HID: case GREYBUS_PROTOCOL_VENDOR: default: diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 1e3f31d4a058..c700b0d78a10 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -275,6 +275,9 @@ void gb_battery_device_exit(struct gb_connection *connection); int gb_gpio_controller_init(struct gb_connection *connection); void gb_gpio_controller_exit(struct gb_connection *connection); +int gb_uart_device_init(struct gb_connection *connection); +void gb_uart_device_exit(struct gb_connection *connection); + int gb_tty_init(void); void gb_tty_exit(void); diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 301868cd5878..d15a49b3b11d 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -35,7 +35,7 @@ struct gb_tty { struct tty_port port; - struct gb_module *gmod; + struct gb_connection *connection; u16 cport_id; unsigned int minor; unsigned char clocal; @@ -386,8 +386,7 @@ static const struct tty_operations gb_ops = { }; -int gb_tty_probe(struct gb_module *gmod, - const struct greybus_module_id *id) +int gb_uart_device_init(struct gb_connection *connection) { struct gb_tty *gb_tty; struct device *tty_dev; @@ -401,14 +400,14 @@ int gb_tty_probe(struct gb_module *gmod, minor = alloc_minor(gb_tty); if (minor < 0) { if (minor == -ENOSPC) { - dev_err(&gmod->dev, "no more free minor numbers\n"); + dev_err(&connection->dev, "no more free minor numbers\n"); return -ENODEV; } return minor; } gb_tty->minor = minor; - gb_tty->gmod = gmod; + gb_tty->connection = connection; spin_lock_init(&gb_tty->write_lock); spin_lock_init(&gb_tty->read_lock); init_waitqueue_head(&gb_tty->wioctl); @@ -416,10 +415,10 @@ int gb_tty_probe(struct gb_module *gmod, /* FIXME - allocate gb buffers */ - gmod->gb_tty = gb_tty; + connection->private = gb_tty; tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, - &gmod->dev); + &connection->dev); if (IS_ERR(tty_dev)) { retval = PTR_ERR(tty_dev); goto error; @@ -427,14 +426,14 @@ int gb_tty_probe(struct gb_module *gmod, return 0; error: - gmod->gb_tty = NULL; + connection->private = NULL; release_minor(gb_tty); return retval; } -void gb_tty_disconnect(struct gb_module *gmod) +void gb_uart_device_exit(struct gb_connection *connection) { - struct gb_tty *gb_tty = gmod->gb_tty; + struct gb_tty *gb_tty = connection->private; struct tty_struct *tty; if (!gb_tty) @@ -444,7 +443,7 @@ void gb_tty_disconnect(struct gb_module *gmod) gb_tty->disconnected = true; wake_up_all(&gb_tty->wioctl); - gmod->gb_tty = NULL; + connection->private = NULL; mutex_unlock(&gb_tty->mutex); tty = tty_port_tty_get(&gb_tty->port); @@ -463,14 +462,6 @@ void gb_tty_disconnect(struct gb_module *gmod) kfree(gb_tty); } -#if 0 -static struct greybus_driver tty_gb_driver = { - .probe = gb_tty_probe, - .disconnect = gb_tty_disconnect, - .id_table = id_table, -}; -#endif - int __init gb_tty_init(void) { int retval = 0; -- cgit v1.2.3-59-g8ed1b From 4d98098942bda347cd9b5ce1b8f253e631ddf2a8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 17:42:45 +0800 Subject: greybus: uart-gb: remove global init functions The uart-gb code needs to init the tty core before it can add devices. Previously we hard-coded this in the greybus core, move this to be "dynamic" and self-contained within the uart-gb.c file. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 10 ---------- drivers/staging/greybus/greybus.h | 3 --- drivers/staging/greybus/uart-gb.c | 41 ++++++++++++++++++--------------------- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 61fd6906541d..7c0cb6227b1f 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -282,17 +282,8 @@ static int __init gb_init(void) goto error_operation; } - retval = gb_tty_init(); - if (retval) { - pr_err("gb_tty_init failed\n"); - goto error_tty; - } - return 0; -error_tty: - gb_operation_exit(); - error_operation: gb_gbuf_exit(); @@ -310,7 +301,6 @@ error_bus: static void __exit gb_exit(void) { - gb_tty_exit(); gb_operation_exit(); gb_gbuf_exit(); gb_ap_exit(); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index c700b0d78a10..deb34f1d5fbf 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -278,9 +278,6 @@ void gb_gpio_controller_exit(struct gb_connection *connection); int gb_uart_device_init(struct gb_connection *connection); void gb_uart_device_exit(struct gb_connection *connection); -int gb_tty_init(void); -void gb_tty_exit(void); - int svc_set_route_send(struct gb_interface *interface, struct greybus_host_device *hd); diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index d15a49b3b11d..7c9229da77e4 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -59,6 +59,10 @@ static const struct greybus_module_id id_table[] = { static struct tty_driver *gb_tty_driver; static DEFINE_IDR(tty_minors); static DEFINE_MUTEX(table_lock); +static atomic_t reference_count = ATOMIC_INIT(0); + +static int gb_tty_init(void); +static void gb_tty_exit(void); static struct gb_tty *get_gb_by_minor(unsigned minor) { @@ -393,6 +397,15 @@ int gb_uart_device_init(struct gb_connection *connection) int retval; int minor; + /* First time here, initialize the tty structures */ + if (atomic_inc_return(&reference_count) == 1) { + retval = gb_tty_init(); + if (retval) { + atomic_dec(&reference_count); + return retval; + } + } + gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); if (!gb_tty) return -ENOMEM; @@ -460,9 +473,13 @@ void gb_uart_device_exit(struct gb_connection *connection) tty_port_put(&gb_tty->port); kfree(gb_tty); + + /* If last device is gone, tear down the tty structures */ + if (atomic_dec_return(&reference_count) == 0) + gb_tty_exit(); } -int __init gb_tty_init(void) +static int gb_tty_init(void) { int retval = 0; @@ -490,40 +507,20 @@ int __init gb_tty_init(void) goto fail_put_gb_tty; } -#if 0 - retval = greybus_register(&tty_gb_driver); - if (retval) { - pr_err("Can not register greybus driver.\n"); - goto fail_unregister_gb_tty; - } -#endif - return 0; -/* fail_unregister_gb_tty: */ - tty_unregister_driver(gb_tty_driver); fail_put_gb_tty: put_tty_driver(gb_tty_driver); fail_unregister_dev: return retval; } -void __exit gb_tty_exit(void) +static void gb_tty_exit(void) { int major = MAJOR(gb_tty_driver->major); int minor = gb_tty_driver->minor_start; -#if 0 - greybus_deregister(&tty_gb_driver); -#endif tty_unregister_driver(gb_tty_driver); put_tty_driver(gb_tty_driver); unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); } - -#if 0 -module_init(gb_tty_init); -module_exit(gb_tty_exit); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Kroah-Hartman "); -#endif -- cgit v1.2.3-59-g8ed1b From 6507cced6bf00c53108034a4954b9b8b73e6a216 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 17:58:54 +0800 Subject: greybus: FIXME/XXX removals: We have proper reference counting now Now that we have proper reference counting for modules, interfaces, and connections, no need to worry about grabbing a pointer to your "parent" structure, all is good. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 2 +- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/operation.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 592b68d5115a..185fa2c4b55f 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -369,7 +369,7 @@ int gb_battery_device_init(struct gb_connection *connection) if (!gb) return -ENOMEM; - gb->connection = connection; // FIXME refcount! + gb->connection = connection; connection->private = gb; /* Check the version */ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2d3421f6aa10..722bee4579ea 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -179,7 +179,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, return NULL; } - connection->interface = interface; /* XXX refcount? */ + connection->interface = interface; connection->interface_cport_id = cport_id; connection->protocol = protocol; connection->state = GB_CONNECTION_STATE_DISABLED; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index a7375a2809d5..163cdc439e0c 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -61,7 +61,7 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) if (!interface) return NULL; - interface->gmod = gmod; /* XXX refcount? */ + interface->gmod = gmod; interface->id = interface_id; interface->device_id = 0xff; /* Invalid device id to start with */ INIT_LIST_HEAD(&interface->connections); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 9b889b169bf5..df3a50227625 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -349,7 +349,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) return NULL; - operation->connection = connection; /* XXX refcount? */ + operation->connection = connection; operation->request = gb_operation_gbuf_create(operation, type, request_size, -- cgit v1.2.3-59-g8ed1b From eec5883f5179367821ab5102be897231dc64fa62 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Oct 2014 18:00:13 +0800 Subject: greybus: module: remove unneeded XXX comment about module id values We do properly check for duplicate module ids, as fixed in 008d85d90ae1ab31f1f7b80f245f6ee2eb5aed49 "module: don't create duplicate module ids", so remove the XXX marker. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 54e8f9e68d25..f65aea680be7 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -82,7 +82,7 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) return NULL; gmod->hd = hd; /* XXX refcount? */ - gmod->module_id = module_id; /* XXX check for dups */ + gmod->module_id = module_id; INIT_LIST_HEAD(&gmod->interfaces); spin_lock_irq(&gb_modules_lock); -- cgit v1.2.3-59-g8ed1b From f348964c266c6b2db80af8b7a75a6f9ef566f1c3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Oct 2014 09:27:50 +0800 Subject: greybus: kernel_ver.h: add ATTRIBUTE_GROUPS() macro for older kernels This was added in 3.11, and we need it for 3.10 Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index e0fea182c3b5..e4cda5ce65fa 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -48,4 +48,19 @@ static inline void gb_gpiochip_remove(struct gpio_chip *chip) } #endif +/* + * ATTRIBUTE_GROUPS showed up in 3.11-rc2, but we need to build on 3.10, so add + * it here. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) +#define ATTRIBUTE_GROUPS(name) \ +static const struct attribute_group name##_group = { \ + .attrs = name##_attrs, \ +}; \ +static const struct attribute_group *name##_groups[] = { \ + &name##_group, \ + NULL, \ +} +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ -- cgit v1.2.3-59-g8ed1b From 3689f9744a029ee9b7b38fb177249d2812ffa676 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 27 Oct 2014 06:04:30 -0500 Subject: greybus: begin abstracting connection operations This is part 1 of abstracting the connection operations into a set of methods. This will avoid some big switch statements, but more importantly this will be needed for supporting multiple versions of each protocol. For now only two methods are defined. The init method is used to set up the device (or whatever the CPort represents) and the exit method tears it down. There may need to be additional operations added in the future, and once versioning is used we might stash the version number in this structure as well. The next patch adds dynamic registratration of these protocol handlers, and will do away with the switch statement now found in gb_connection_init(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 9 +++++++-- drivers/staging/greybus/connection.c | 36 +++++++++--------------------------- drivers/staging/greybus/connection.h | 11 +++++++++++ drivers/staging/greybus/gpio-gb.c | 9 +++++++-- drivers/staging/greybus/greybus.h | 12 ++++-------- drivers/staging/greybus/i2c-gb.c | 9 +++++++-- drivers/staging/greybus/uart-gb.c | 9 +++++++-- 7 files changed, 52 insertions(+), 43 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 185fa2c4b55f..74698c143bdb 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -359,7 +359,7 @@ static enum power_supply_property battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, }; -int gb_battery_device_init(struct gb_connection *connection) +static int gb_battery_connection_init(struct gb_connection *connection) { struct gb_battery *gb; struct power_supply *b; @@ -397,7 +397,7 @@ int gb_battery_device_init(struct gb_connection *connection) return 0; } -void gb_battery_device_exit(struct gb_connection *connection) +static void gb_battery_connection_exit(struct gb_connection *connection) { struct gb_battery *gb = connection->private; @@ -405,6 +405,11 @@ void gb_battery_device_exit(struct gb_connection *connection) kfree(gb); } +struct gb_connection_handler gb_battery_connection_handler = { + .connection_init = gb_battery_connection_init, + .connection_exit = gb_battery_connection_exit, +}; + void gb_battery_disconnect(struct gb_module *gmod) { #if 0 diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 722bee4579ea..1a849ce0ae1d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -277,16 +277,16 @@ int gb_connection_init(struct gb_connection *connection) connection->state = GB_CONNECTION_STATE_ENABLED; switch (connection->protocol) { case GREYBUS_PROTOCOL_I2C: - ret = gb_i2c_device_init(connection); + connection->handler = &gb_i2c_connection_handler; break; case GREYBUS_PROTOCOL_GPIO: - ret = gb_gpio_controller_init(connection); + connection->handler = &gb_gpio_connection_handler; break; case GREYBUS_PROTOCOL_BATTERY: - ret = gb_battery_device_init(connection); + connection->handler = &gb_battery_connection_handler; break; case GREYBUS_PROTOCOL_UART: - ret = gb_uart_device_init(connection); + connection->handler = &gb_uart_connection_handler; break; case GREYBUS_PROTOCOL_CONTROL: case GREYBUS_PROTOCOL_AP: @@ -308,28 +308,10 @@ int gb_connection_init(struct gb_connection *connection) void gb_connection_exit(struct gb_connection *connection) { - connection->state = GB_CONNECTION_STATE_DESTROYING; - - switch (connection->protocol) { - case GREYBUS_PROTOCOL_I2C: - gb_i2c_device_exit(connection); - break; - case GREYBUS_PROTOCOL_GPIO: - gb_gpio_controller_exit(connection); - break; - case GREYBUS_PROTOCOL_BATTERY: - gb_battery_device_exit(connection); - break; - case GREYBUS_PROTOCOL_UART: - gb_uart_device_exit(connection); - break; - case GREYBUS_PROTOCOL_CONTROL: - case GREYBUS_PROTOCOL_AP: - case GREYBUS_PROTOCOL_HID: - case GREYBUS_PROTOCOL_VENDOR: - default: - gb_connection_err(connection, "unimplemented protocol %u", - (u32)connection->protocol); - break; + if (!connection->handler) { + gb_connection_err(connection, "uninitialized connection"); + return; } + connection->state = GB_CONNECTION_STATE_DESTROYING; + connection->handler->connection_exit(connection); } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 19dd91dae062..4492d2f67ec4 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -21,6 +21,15 @@ enum gb_connection_state { GB_CONNECTION_STATE_DESTROYING = 4, }; +struct gb_connection; +typedef int (*gb_connection_init_t)(struct gb_connection *); +typedef void (*gb_connection_exit_t)(struct gb_connection *); + +struct gb_connection_handler { + gb_connection_init_t connection_init; + gb_connection_exit_t connection_exit; +}; + struct gb_connection { struct greybus_host_device *hd; struct gb_interface *interface; @@ -38,6 +47,8 @@ struct gb_connection { atomic_t op_cycle; struct delayed_work timeout_work; + struct gb_connection_handler *handler; + void *private; }; #define to_gb_connection(d) container_of(d, struct gb_connection, dev) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 8781f816d968..6ba1f633a436 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -732,7 +732,7 @@ static int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controlle return ret; } -int gb_gpio_controller_init(struct gb_connection *connection) +static int gb_gpio_connection_init(struct gb_connection *connection) { struct gb_gpio_controller *gb_gpio_controller; struct gpio_chip *gpio; @@ -780,7 +780,7 @@ out_err: return ret; } -void gb_gpio_controller_exit(struct gb_connection *connection) +static void gb_gpio_connection_exit(struct gb_connection *connection) { struct gb_gpio_controller *gb_gpio_controller = connection->private; @@ -792,6 +792,11 @@ void gb_gpio_controller_exit(struct gb_connection *connection) kfree(gb_gpio_controller); } +struct gb_connection_handler gb_gpio_connection_handler = { + .connection_init = gb_gpio_connection_init, + .connection_exit = gb_gpio_connection_exit, +}; + #if 0 MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Greybus GPIO driver"); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index deb34f1d5fbf..f907e0fe7fc9 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -266,14 +266,10 @@ void gb_deregister_cport_complete(u16 cport_id); extern struct bus_type greybus_bus_type; extern const struct attribute_group *greybus_module_groups[]; -int gb_i2c_device_init(struct gb_connection *connection); -void gb_i2c_device_exit(struct gb_connection *connection); - -int gb_battery_device_init(struct gb_connection *connection); -void gb_battery_device_exit(struct gb_connection *connection); - -int gb_gpio_controller_init(struct gb_connection *connection); -void gb_gpio_controller_exit(struct gb_connection *connection); +extern struct gb_connection_handler gb_i2c_connection_handler; +extern struct gb_connection_handler gb_gpio_connection_handler; +extern struct gb_connection_handler gb_battery_connection_handler; +extern struct gb_connection_handler gb_uart_connection_handler; int gb_uart_device_init(struct gb_connection *connection); void gb_uart_device_exit(struct gb_connection *connection); diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index e20801169b8d..5d3e2f2061b0 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -466,7 +466,7 @@ static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) return gb_i2c_timeout_operation(gb_i2c_dev, GB_I2C_TIMEOUT_DEFAULT); } -int gb_i2c_device_init(struct gb_connection *connection) +static int gb_i2c_connection_init(struct gb_connection *connection) { struct gb_i2c_device *gb_i2c_dev; struct i2c_adapter *adapter; @@ -509,7 +509,7 @@ out_err: return ret; } -void gb_i2c_device_exit(struct gb_connection *connection) +static void gb_i2c_connection_exit(struct gb_connection *connection) { struct gb_i2c_device *gb_i2c_dev = connection->private; @@ -518,6 +518,11 @@ void gb_i2c_device_exit(struct gb_connection *connection) kfree(gb_i2c_dev); } +struct gb_connection_handler gb_i2c_connection_handler = { + .connection_init = gb_i2c_connection_init, + .connection_exit = gb_i2c_connection_exit, +}; + #if 0 module_greybus_driver(i2c_gb_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 7c9229da77e4..370a78fb231f 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -390,7 +390,7 @@ static const struct tty_operations gb_ops = { }; -int gb_uart_device_init(struct gb_connection *connection) +int gb_uart_connection_init(struct gb_connection *connection) { struct gb_tty *gb_tty; struct device *tty_dev; @@ -444,7 +444,7 @@ error: return retval; } -void gb_uart_device_exit(struct gb_connection *connection) +void gb_uart_connection_exit(struct gb_connection *connection) { struct gb_tty *gb_tty = connection->private; struct tty_struct *tty; @@ -524,3 +524,8 @@ static void gb_tty_exit(void) put_tty_driver(gb_tty_driver); unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); } + +struct gb_connection_handler gb_uart_connection_handler = { + .connection_init = gb_uart_connection_init, + .connection_exit = gb_uart_connection_exit, +}; -- cgit v1.2.3-59-g8ed1b From 059b093616313683a3d6259646d21f64d2756838 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Oct 2014 09:49:33 +0800 Subject: greybus: uart-gb: mark some functions static Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 370a78fb231f..f88a301eb9fe 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -390,7 +390,7 @@ static const struct tty_operations gb_ops = { }; -int gb_uart_connection_init(struct gb_connection *connection) +static int gb_uart_connection_init(struct gb_connection *connection) { struct gb_tty *gb_tty; struct device *tty_dev; @@ -444,7 +444,7 @@ error: return retval; } -void gb_uart_connection_exit(struct gb_connection *connection) +static void gb_uart_connection_exit(struct gb_connection *connection) { struct gb_tty *gb_tty = connection->private; struct tty_struct *tty; -- cgit v1.2.3-59-g8ed1b From 5e8e8ff6d0bf804e1f85154f21bcca699e2f8aa9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Oct 2014 09:50:56 +0800 Subject: greybus: battery-gb: remove some #if 0 code We aren't going to have individual modules for the gb protocols, so just remove this useless code, it was throwing up warnings in sparse. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 74698c143bdb..b7d8e2c8fb85 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -409,30 +409,3 @@ struct gb_connection_handler gb_battery_connection_handler = { .connection_init = gb_battery_connection_init, .connection_exit = gb_battery_connection_exit, }; - -void gb_battery_disconnect(struct gb_module *gmod) -{ -#if 0 - struct gb_battery *gb; - - gb = gmod->gb_battery; - if (!gb) - return; - - power_supply_unregister(&gb->bat); - - kfree(gb); -#endif -} - -#if 0 -static struct greybus_driver battery_gb_driver = { - .probe = gb_battery_probe, - .disconnect = gb_battery_disconnect, - .id_table = id_table, -}; - -module_greybus_driver(battery_gb_driver); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Kroah-Hartman "); -#endif -- cgit v1.2.3-59-g8ed1b From a2f4763f4829c5efb9e38b9d25385d464e0e277a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Oct 2014 10:17:09 +0800 Subject: greybus: sdio-gb: convert to the connection interface. No one is using sdio yet, but convert to the connection interface to remove the last user of the "old" module interface. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/sdio-gb.c | 31 ++++++++++++------------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f907e0fe7fc9..3a8d8f1c077c 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -151,7 +151,6 @@ struct gbuf { struct gb_i2c_device; struct gb_gpio_device; -struct gb_sdio_host; struct gb_tty; struct gb_usb_device; struct gb_battery; @@ -270,6 +269,7 @@ extern struct gb_connection_handler gb_i2c_connection_handler; extern struct gb_connection_handler gb_gpio_connection_handler; extern struct gb_connection_handler gb_battery_connection_handler; extern struct gb_connection_handler gb_uart_connection_handler; +extern struct gb_connection_handler gb_sdio_connection_handler; int gb_uart_device_init(struct gb_connection *connection); void gb_uart_device_exit(struct gb_connection *connection); diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 19c7c4ac81eb..e9c311780556 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -13,6 +13,7 @@ #include "greybus.h" struct gb_sdio_host { + struct gb_connection *connection; struct mmc_host *mmc; struct mmc_request *mrq; // FIXME - some lock? @@ -45,13 +46,12 @@ static const struct mmc_host_ops gb_sd_ops = { .get_ro = gb_sd_get_ro, }; -int gb_sdio_probe(struct gb_module *gmod, - const struct greybus_module_id *id) +static int gb_sdio_connection_init(struct gb_connection *connection) { struct mmc_host *mmc; struct gb_sdio_host *host; - mmc = mmc_alloc_host(sizeof(struct gb_sdio_host), &gmod->dev); + mmc = mmc_alloc_host(sizeof(struct gb_sdio_host), &connection->dev); if (!mmc) return -ENOMEM; @@ -60,36 +60,29 @@ int gb_sdio_probe(struct gb_module *gmod, mmc->ops = &gb_sd_ops; // FIXME - set up size limits we can handle. + // FIXME - register the host controller. - // gmod->gb_sdio_host = host; + host->connection = connection; + connection->private = host; return 0; } -void gb_sdio_disconnect(struct gb_module *gmod) +static void gb_sdio_connection_exit(struct gb_connection *connection) { -#if 0 struct mmc_host *mmc; struct gb_sdio_host *host; - host = gmod->gb_sdio_host; + host = connection->private; if (!host) return; mmc = host->mmc; mmc_remove_host(mmc); mmc_free_host(mmc); -#endif + connection->private = NULL; } -#if 0 -static struct greybus_driver sd_gb_driver = { - .probe = gb_sdio_probe, - .disconnect = gb_sdio_disconnect, - .id_table = id_table, +struct gb_connection_handler gb_sdio_connection_handler = { + .connection_init = gb_sdio_connection_init, + .connection_exit = gb_sdio_connection_exit, }; - -module_greybus_driver(sd_gb_driver); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Greybus SD/MMC Host driver"); -MODULE_AUTHOR("Greg Kroah-Hartman "); -#endif -- cgit v1.2.3-59-g8ed1b From c7a90cb5990313b61cfe5cba859ce34d537075b6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Oct 2014 17:09:35 +0800 Subject: greybus: #if 0 cleanups Remove some #if 0 chunks for the old-style greybus driver macros Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 6 ------ drivers/staging/greybus/i2c-gb.c | 6 ------ 2 files changed, 12 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 6ba1f633a436..df7dbae85d63 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -796,9 +796,3 @@ struct gb_connection_handler gb_gpio_connection_handler = { .connection_init = gb_gpio_connection_init, .connection_exit = gb_gpio_connection_exit, }; - -#if 0 -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Greybus GPIO driver"); -MODULE_AUTHOR("Greg Kroah-Hartman "); -#endif diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 5d3e2f2061b0..b26464a14b32 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -522,9 +522,3 @@ struct gb_connection_handler gb_i2c_connection_handler = { .connection_init = gb_i2c_connection_init, .connection_exit = gb_i2c_connection_exit, }; - -#if 0 -module_greybus_driver(i2c_gb_driver); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Kroah-Hartman "); -#endif -- cgit v1.2.3-59-g8ed1b From cea54c12f7badb18f2adbf718b8451f5d14a654d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Oct 2014 17:49:59 +0800 Subject: greybus: Remove id_table usages We aren't using an id_table for "drivers" at this moment, as the whole driver model interaction is under heavy rework. So remove these for now to keep things easier to understand for future patches. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 5 ----- drivers/staging/greybus/core.c | 5 ----- drivers/staging/greybus/sdio-gb.c | 5 ----- drivers/staging/greybus/uart-gb.c | 5 ----- 4 files changed, 20 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index b7d8e2c8fb85..28c0d0b9aefe 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -92,11 +92,6 @@ struct gb_battery_voltage_request { }; -static const struct greybus_module_id id_table[] = { - { GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */ - { }, /* terminating NULL entry */ -}; - static int battery_operation(struct gb_battery *gb, int type, void *response, int response_size) { diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 7c0cb6227b1f..252d131c3ae8 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -119,11 +119,6 @@ void greybus_deregister(struct greybus_driver *driver) EXPORT_SYMBOL_GPL(greybus_deregister); -static const struct greybus_module_id fake_greybus_module_id = { - GREYBUS_DEVICE(0x42, 0x42) -}; - - /** * gb_add_module * diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index e9c311780556..30caba8d0fa5 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -19,11 +19,6 @@ struct gb_sdio_host { // FIXME - some lock? }; -static const struct greybus_module_id id_table[] = { - { GREYBUS_DEVICE(0x43, 0x43) }, /* make shit up */ - { }, /* terminating NULL entry */ -}; - static void gb_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) { // FIXME - do something here... diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index f88a301eb9fe..b52d9e11f536 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -51,11 +51,6 @@ struct gb_tty { struct mutex mutex; }; -static const struct greybus_module_id id_table[] = { - { GREYBUS_DEVICE(0x45, 0x45) }, /* make shit up */ - { }, /* terminating NULL entry */ -}; - static struct tty_driver *gb_tty_driver; static DEFINE_IDR(tty_minors); static DEFINE_MUTEX(table_lock); -- cgit v1.2.3-59-g8ed1b From f9ab34c2bf1ed120802ae64ae3f0c4ba6b46c6b3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Oct 2014 18:20:24 +0800 Subject: greybus: es1-ap-usb: document the lack of callback for the outgoing bulk urbs We don't need a callback for bulk out urbs to do anything except put the urb back in the pool. Document why we do this and what is involved. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index b8784b14160d..2fccece25641 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -418,6 +418,31 @@ static void cport_out_callback(struct urb *urb) /* If urb is not NULL, then we need to free this urb */ usb_free_urb(urb); + + /* + * Yes, you are right, we aren't telling anyone that the urb finished. + * "That's crazy! How does this all even work?" you might be saying. + * The "magic" is the idea that greybus works on the "operation" level, + * not the "send a buffer" level. All operations are "round-trip" with + * a response from the device that the operation finished, or it will + * time out. Because of that, we don't care that this urb finished, or + * failed, or did anything else, as higher levels of the protocol stack + * will handle completions and timeouts and the rest. + * + * This protocol is "needed" due to some hardware restrictions on the + * current generation of Unipro controllers. Think about it for a + * minute, this is a USB driver, talking to a Unipro bridge, impediance + * mismatch is huge, yet the Unipro controller are even more + * underpowered than this little USB controller. We rely on the round + * trip to keep stalls in the Unipro controllers from happening so that + * we can keep data flowing properly, no matter how slow it might be. + * + * Once again, a wonderful bus protocol cut down in its prime by a naive + * controller chip. We dream of the day we have a "real" HCD for + * Unipro. Until then, we suck it up and make the hardware work, as + * that's the job of the firmware and kernel. + * + */ } /* -- cgit v1.2.3-59-g8ed1b From 53cbb00933a5181cf57ae799a6fc8cbecba973a4 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 28 Oct 2014 10:08:12 -0400 Subject: greybus: module: set device_id when initializing an interface gb_module_interface_init() looks for the interface corresponding to the supplied interface_id, but fails to configure the device_id that goes with it. This results in a set route command being set with an uninitialized and bogus value. Fix it. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index f65aea680be7..4c6e084177d2 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -153,6 +153,7 @@ gb_module_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) interface_id); return -ENOENT; } + interface->device_id = device_id; ret = svc_set_route_send(interface, gmod->hd); if (ret) { -- cgit v1.2.3-59-g8ed1b From 755a21a9bfeef4bd8145d0896a1b6f668f6d99f3 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 28 Oct 2014 10:08:13 -0400 Subject: greybus: connection: call connection_init hook after setting the handler In gb_connection_init() we set the connection_handler for each supported protocol, but we never call the connection_init hook after doing so. This results in a failure being returned so fix it by calling the connection_init hook to get a good return and the associated driver initialized. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f76855c609c3..69ea3a788771 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -290,6 +290,8 @@ int gb_connection_init(struct gb_connection *connection) break; } + ret = connection->handler->connection_init(connection); + if (ret) connection->state = GB_CONNECTION_STATE_ERROR; -- cgit v1.2.3-59-g8ed1b From b29699602df46a8dffe01bedf2a601c4e2699a96 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 28 Oct 2014 19:35:58 -0500 Subject: greybus: drop the cport id on error In gb_connection_create(), if an error occurs adding a connection's device, the cport id assigned to the AP end of the connection is not getting freed. Fix that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 69ea3a788771..3fee64773976 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -186,6 +186,8 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, retval = device_add(&connection->dev); if (retval) { + gb_connection_hd_cport_id_free(connection); + /* kref_put(connection->hd); */ kfree(connection); return NULL; } -- cgit v1.2.3-59-g8ed1b From 7fba0079ad9a5b1b851947ad3c5b90093b2fc415 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 28 Oct 2014 19:35:59 -0500 Subject: greybus: use protocol_id for numeric values Switch to using "protocol_id" to refer to a byte-sized numeric protocol number. A "protocol" will represent a protocol structure (created in the next patch). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 20 ++++++++++---------- drivers/staging/greybus/connection.h | 5 +++-- drivers/staging/greybus/greybus_manifest.h | 4 ++-- drivers/staging/greybus/manifest.c | 6 +++--- drivers/staging/greybus/operation.c | 10 +++++----- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3fee64773976..6d5085d2b6aa 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -111,18 +111,18 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(state); -static ssize_t protocol_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t +protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_connection *connection = to_gb_connection(dev); - return sprintf(buf, "%d", connection->protocol); + return sprintf(buf, "%d", connection->protocol_id); } -static DEVICE_ATTR_RO(protocol); +static DEVICE_ATTR_RO(protocol_id); static struct attribute *connection_attrs[] = { &dev_attr_state.attr, - &dev_attr_protocol.attr, + &dev_attr_protocol_id.attr, NULL, }; @@ -152,7 +152,7 @@ static struct device_type greybus_connection_type = { * pointer otherwise. */ struct gb_connection *gb_connection_create(struct gb_interface *interface, - u16 cport_id, enum greybus_protocol protocol) + u16 cport_id, u8 protocol_id) { struct gb_connection *connection; struct greybus_host_device *hd; @@ -172,7 +172,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, connection->interface = interface; connection->interface_cport_id = cport_id; - connection->protocol = protocol; + connection->protocol_id = protocol_id; connection->state = GB_CONNECTION_STATE_DISABLED; connection->dev.parent = &interface->dev; @@ -267,7 +267,7 @@ int gb_connection_init(struct gb_connection *connection) /* Need to enable the connection to initialize it */ connection->state = GB_CONNECTION_STATE_ENABLED; - switch (connection->protocol) { + switch (connection->protocol_id) { case GREYBUS_PROTOCOL_I2C: connection->handler = &gb_i2c_connection_handler; break; @@ -286,8 +286,8 @@ int gb_connection_init(struct gb_connection *connection) case GREYBUS_PROTOCOL_LED: case GREYBUS_PROTOCOL_VENDOR: default: - gb_connection_err(connection, "unimplemented protocol %u", - (u32)connection->protocol); + gb_connection_err(connection, "unimplemented protocol %hhu", + connection->protocol_id); ret = -ENXIO; break; } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 88a9398c79d6..830abe7060ba 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -39,7 +39,8 @@ struct gb_connection { struct rb_node hd_node; struct list_head interface_links; - enum greybus_protocol protocol; + u8 protocol_id; + enum gb_connection_state state; struct list_head operations; @@ -53,7 +54,7 @@ struct gb_connection { #define to_gb_connection(d) container_of(d, struct gb_connection, dev) struct gb_connection *gb_connection_create(struct gb_interface *interface, - u16 cport_id, enum greybus_protocol protocol); + u16 cport_id, u8 protocol_id); void gb_connection_destroy(struct gb_connection *connection); int gb_connection_init(struct gb_connection *connection); diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index c18ee112f6df..c6988ce2818a 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -98,13 +98,13 @@ struct greybus_descriptor_interface { /* * A CPort descriptor indicates the id of the interface within the * module it's associated with, along with the CPort id used to - * address the CPort. The protocol defines the format of messages + * address the CPort. The protocol id defines the format of messages * exchanged using the CPort. */ struct greybus_descriptor_cport { __u8 interface; __le16 id; - __u8 protocol; /* enum greybus_protocol */ + __u8 protocol_id; /* enum greybus_protocol */ }; /* diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 09fcde9dd7ce..2e22f545e15b 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -181,7 +181,7 @@ static u32 gb_manifest_parse_cports(struct gb_interface *interface) while (true) { struct manifest_desc *descriptor; struct greybus_descriptor_cport *desc_cport; - enum greybus_protocol protocol; + u8 protocol_id; u16 cport_id; bool found; @@ -200,9 +200,9 @@ static u32 gb_manifest_parse_cports(struct gb_interface *interface) break; /* Found one. Set up its function structure */ - protocol = (enum greybus_protocol)desc_cport->protocol; + protocol_id = desc_cport->protocol_id; cport_id = le16_to_cpu(desc_cport->id); - if (!gb_connection_create(interface, cport_id, protocol)) + if (!gb_connection_create(interface, cport_id, protocol_id)) return 0; /* Error */ count++; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index dc354252bef5..0388242d9b79 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -200,21 +200,21 @@ static gb_operation_recv_handler gb_operation_recv_handlers[] = { static void gb_operation_request_handle(struct gb_operation *operation) { - u8 protocol = operation->connection->protocol; + u8 protocol_id = operation->connection->protocol_id; /* Subtract one from array size to stay within u8 range */ - if (protocol <= (u8)(ARRAY_SIZE(gb_operation_recv_handlers) - 1)) { + if (protocol_id <= (u8)(ARRAY_SIZE(gb_operation_recv_handlers) - 1)) { gb_operation_recv_handler handler; - handler = gb_operation_recv_handlers[protocol]; + handler = gb_operation_recv_handlers[protocol_id]; if (handler) { handler(operation); /* Handle the request */ return; } } - gb_connection_err(operation->connection, "unrecognized protocol %u\n", - (unsigned int)protocol); + gb_connection_err(operation->connection, + "unrecognized protocol id %hhu\n", protocol_id); operation->result = GB_OP_PROTOCOL_BAD; gb_operation_complete(operation); } -- cgit v1.2.3-59-g8ed1b From 4ccb6b7abb8ee4ff6fc28468ffe893caa730ea13 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 28 Oct 2014 19:36:00 -0500 Subject: greybus: introduce protocol abstraction Define a protocol structure that will allow protocols to be registered dynamically. For now we just introduce a bookkeeping data structure. Upcoming patches will move protocol-related methods into the protocol structure, and will start registering protocol handlers dynamically. A list of connections using a given protocol is maintained so we can tell when a protocol is no longer in use. This may not be necessary (we could use a kref instead) but it may turn out to be a good way to clean things up. The interface is gb_protocol_get() and gb_protocol_put() for a connection, allowing the protocol to be looked up and the connection structure to be inserted into its list. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/connection.c | 17 +++-- drivers/staging/greybus/connection.h | 4 +- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/operation.c | 2 +- drivers/staging/greybus/protocol.c | 122 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/protocol.h | 26 ++++++++ 7 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 drivers/staging/greybus/protocol.c create mode 100644 drivers/staging/greybus/protocol.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 42a3944f4eeb..39874deacab0 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -7,6 +7,7 @@ greybus-y := core.o \ module.o \ interface.o \ connection.o \ + protocol.o \ operation.o \ i2c-gb.o \ gpio-gb.o \ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 6d5085d2b6aa..dac47b33855f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -116,7 +116,7 @@ protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_connection *connection = to_gb_connection(dev); - return sprintf(buf, "%d", connection->protocol_id); + return sprintf(buf, "%d", connection->protocol->id); } static DEVICE_ATTR_RO(protocol_id); @@ -162,17 +162,23 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, if (!connection) return NULL; + INIT_LIST_HEAD(&connection->protocol_links); + if (!gb_protocol_get(connection, protocol_id)) { + kfree(connection); + return NULL; + } + hd = interface->gmod->hd; connection->hd = hd; /* XXX refcount? */ if (!gb_connection_hd_cport_id_alloc(connection)) { /* kref_put(connection->hd); */ + gb_protocol_put(connection); kfree(connection); return NULL; } connection->interface = interface; connection->interface_cport_id = cport_id; - connection->protocol_id = protocol_id; connection->state = GB_CONNECTION_STATE_DISABLED; connection->dev.parent = &interface->dev; @@ -188,6 +194,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, if (retval) { gb_connection_hd_cport_id_free(connection); /* kref_put(connection->hd); */ + gb_protocol_put(connection); kfree(connection); return NULL; } @@ -228,6 +235,8 @@ void gb_connection_destroy(struct gb_connection *connection) spin_unlock_irq(&gb_connections_lock); gb_connection_hd_cport_id_free(connection); + /* kref_put(connection->hd); */ + gb_protocol_put(connection); device_del(&connection->dev); } @@ -267,7 +276,7 @@ int gb_connection_init(struct gb_connection *connection) /* Need to enable the connection to initialize it */ connection->state = GB_CONNECTION_STATE_ENABLED; - switch (connection->protocol_id) { + switch (connection->protocol->id) { case GREYBUS_PROTOCOL_I2C: connection->handler = &gb_i2c_connection_handler; break; @@ -287,7 +296,7 @@ int gb_connection_init(struct gb_connection *connection) case GREYBUS_PROTOCOL_VENDOR: default: gb_connection_err(connection, "unimplemented protocol %hhu", - connection->protocol_id); + connection->protocol->id); ret = -ENXIO; break; } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 830abe7060ba..8056993c00ac 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -39,7 +39,9 @@ struct gb_connection { struct rb_node hd_node; struct list_head interface_links; - u8 protocol_id; + + struct gb_protocol *protocol; + struct list_head protocol_links; enum gb_connection_state state; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 3a8d8f1c077c..f6c90e0c0f22 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -26,6 +26,7 @@ #include "module.h" #include "interface.h" #include "connection.h" +#include "protocol.h" #include "operation.h" diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 0388242d9b79..cc278bcb499d 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -200,7 +200,7 @@ static gb_operation_recv_handler gb_operation_recv_handlers[] = { static void gb_operation_request_handle(struct gb_operation *operation) { - u8 protocol_id = operation->connection->protocol_id; + u8 protocol_id = operation->connection->protocol->id; /* Subtract one from array size to stay within u8 range */ if (protocol_id <= (u8)(ARRAY_SIZE(gb_operation_recv_handlers) - 1)) { diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c new file mode 100644 index 000000000000..52944ecea075 --- /dev/null +++ b/drivers/staging/greybus/protocol.c @@ -0,0 +1,122 @@ +/* + * Greybus protocol handling + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +/* Global list of registered protocols */ +static DEFINE_SPINLOCK(gb_protocols_lock); +static LIST_HEAD(gb_protocols); + +/* Caller must hold gb_protocols_lock */ +struct gb_protocol *_gb_protocol_find(u8 id) +{ + struct gb_protocol *protocol; + + list_for_each_entry(protocol, &gb_protocols, links) + if (protocol->id == id) + return protocol; + return NULL; +} + +/* This is basically for debug */ +static struct gb_protocol *gb_protocol_find(u8 id) +{ + struct gb_protocol *protocol; + + spin_lock_irq(&gb_protocols_lock); + protocol = _gb_protocol_find(id); + spin_unlock_irq(&gb_protocols_lock); + + return protocol; +} + +/* Returns true if protocol was succesfully registered, false otherwise */ +bool gb_protocol_register(u8 id) +{ + struct gb_protocol *protocol; + struct gb_protocol *existing; + + /* Initialize it speculatively */ + protocol = kzalloc(sizeof(*protocol), GFP_KERNEL); + if (!protocol) + return false; + protocol->id = id; + INIT_LIST_HEAD(&protocol->connections); + + spin_lock_irq(&gb_protocols_lock); + existing = _gb_protocol_find(id); + if (!existing) + list_add(&protocol->links, &gb_protocols); + spin_unlock_irq(&gb_protocols_lock); + + if (existing) { + kfree(protocol); + protocol = NULL; + } + + return protocol != NULL; +} + +/* Returns true if successful, false otherwise */ +bool gb_protocol_deregister(struct gb_protocol *protocol) +{ + spin_lock_irq(&gb_protocols_lock); + if (list_empty(&protocol->connections)) + list_del(&protocol->links); + else + protocol = NULL; /* Protocol is still in use */ + spin_unlock_irq(&gb_protocols_lock); + kfree(protocol); + + return protocol != NULL; +} + +/* Returns true if successful, false otherwise */ +bool gb_protocol_get(struct gb_connection *connection, u8 id) +{ + struct gb_protocol *protocol; + + /* Sanity */ + if (!list_empty(&connection->protocol_links) || + !connection->protocol->id) { + gb_connection_err(connection, + "connection already has protocol"); + return false; + } + + spin_lock_irq(&gb_protocols_lock); + protocol = _gb_protocol_find(id); + if (protocol) + list_add(&connection->protocol_links, &protocol->connections); + spin_unlock_irq(&gb_protocols_lock); + connection->protocol = protocol; + + return protocol != NULL; +} + +void gb_protocol_put(struct gb_connection *connection) +{ + struct gb_protocol *protocol = connection->protocol; + + /* Sanity checks */ + if (list_empty(&connection->protocol_links)) { + gb_connection_err(connection, + "connection protocol not recorded"); + return; + } + if (!protocol || gb_protocol_find(protocol->id) != protocol) { + gb_connection_err(connection, + "connection has undefined protocol"); + return; + } + + spin_lock_irq(&gb_protocols_lock); + list_del(&connection->protocol_links); + connection->protocol = NULL; + spin_unlock_irq(&gb_protocols_lock); +} diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h new file mode 100644 index 000000000000..d244e9d3eca1 --- /dev/null +++ b/drivers/staging/greybus/protocol.h @@ -0,0 +1,26 @@ +/* + * Greybus protocol handling + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __PROTOCOL_H +#define __PROTOCOL_H + +#include "greybus.h" + +struct gb_protocol { + u8 id; + struct list_head connections; /* protocol users */ + struct list_head links; /* global list */ +}; + +bool gb_protocol_register(u8 id); +bool gb_protocol_deregister(struct gb_protocol *protocol); + +bool gb_protocol_get(struct gb_connection *connection, u8 id); +void gb_protocol_put(struct gb_connection *connection); + +#endif /* __PROTOCOL_H */ -- cgit v1.2.3-59-g8ed1b From 4b640bb1357a3eed16a04b23167dc3de4294dcd9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 29 Oct 2014 09:57:08 +0800 Subject: greybus: connection: fix up error patch logic in gb_connection_create() Once you have called device_initialize() you have to call put_device() on the structure to clean it up on an error path, otherwise you will leak memory. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index dac47b33855f..c4b650522ac2 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -169,9 +169,8 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, } hd = interface->gmod->hd; - connection->hd = hd; /* XXX refcount? */ + connection->hd = hd; if (!gb_connection_hd_cport_id_alloc(connection)) { - /* kref_put(connection->hd); */ gb_protocol_put(connection); kfree(connection); return NULL; @@ -193,9 +192,8 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, retval = device_add(&connection->dev); if (retval) { gb_connection_hd_cport_id_free(connection); - /* kref_put(connection->hd); */ gb_protocol_put(connection); - kfree(connection); + put_device(&connection->dev); return NULL; } -- cgit v1.2.3-59-g8ed1b From 2dcf6871dd19a8129929203776c57cddbb07bcc3 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 30 Oct 2014 15:28:18 -0700 Subject: greybus: build: Add -fno-pic for 32bit arm as well In order for 32bit arm devices using the android toolchain to load the greybus module, I need to add -fno-pic in the build arguments as well. Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/Android.mk b/drivers/staging/greybus/Android.mk index 773129badf62..924099b42c06 100644 --- a/drivers/staging/greybus/Android.mk +++ b/drivers/staging/greybus/Android.mk @@ -18,6 +18,7 @@ ifneq ($(ANDROID_64),) FLAGARG := EXTRA_CFLAGS+=-fno-pic else ARCHARG := ARCH=arm + FLAGARG := EXTRA_CFLAGS+=-fno-pic endif ARGS := $(KDIRARG) $(ARCHARG) $(FLAGARG) -- cgit v1.2.3-59-g8ed1b From 96fd8c2bfdda4b6a2223f235ebdd35e4d391d075 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:03:08 -0600 Subject: greybus: fix a bug in gb_operation_gbuf_complete() The gbuf completion routine was using the request payload pointers (which point at the area *past* the message header) rather than the header. This didn't matter much for now, it was only used in the error path. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index cc278bcb499d..9cb9c9d590d2 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -283,9 +283,9 @@ static void gb_operation_gbuf_complete(struct gbuf *gbuf) int type; if (gbuf == operation->request) - header = operation->request_payload; + header = operation->request->transfer_buffer; else if (gbuf == operation->response) - header = operation->response_payload; + header = operation->response->transfer_buffer; else header = NULL; -- cgit v1.2.3-59-g8ed1b From 00ace9739042a867bbbaaceefb6ad88571358ea5 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:03:09 -0600 Subject: greybus: drop a redundant assignment Get rid of a duplicate assignment of an interface's device id. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 4c6e084177d2..3a64a2b3eb06 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -168,7 +168,6 @@ gb_module_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) /* XXX clear route */ return ret; } - interface->device_id = device_id; return 0; } -- cgit v1.2.3-59-g8ed1b From 0570afcf24930e4c4078a637d93c61fa448fbd10 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:03:10 -0600 Subject: greybus: make _gb_protocol_find() static This function should have private scope. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 52944ecea075..e0bcd4bc28a5 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -13,7 +13,7 @@ static DEFINE_SPINLOCK(gb_protocols_lock); static LIST_HEAD(gb_protocols); /* Caller must hold gb_protocols_lock */ -struct gb_protocol *_gb_protocol_find(u8 id) +static struct gb_protocol *_gb_protocol_find(u8 id) { struct gb_protocol *protocol; -- cgit v1.2.3-59-g8ed1b From 545a697512a0b9c08ae4f152b7dd146f02dcbb92 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:03:11 -0600 Subject: greybus: control printing message There's a message that gets printed in gb_manifest_parse() if excess descriptors are found in the manifest. This should only be printed if the parse was successful. If it was not successful it's not really very helpful. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 2e22f545e15b..563463955cb0 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -391,7 +391,7 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) * We really should have no remaining descriptors, but we * don't know what newer format manifests might leave. */ - if (!list_empty(&manifest_descs)) + if (result && !list_empty(&manifest_descs)) pr_info("excess descriptors in module manifest\n"); out: release_manifest_descriptors(); -- cgit v1.2.3-59-g8ed1b From 6b09938a48d4fd86596c75b19aea3286da2e4d9f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:03:12 -0600 Subject: greybus: improve some error messages Add a few error messages to help explain the reason for failures. Add a missing space in a message in svc_management(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 2 +- drivers/staging/greybus/connection.c | 3 +++ drivers/staging/greybus/interface.c | 2 ++ drivers/staging/greybus/module.c | 2 ++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 24a15e9c6009..a8cd7e7cdd60 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -150,7 +150,7 @@ static void svc_management(struct svc_function_unipro_management *management, management->link_up.interface_id, management->link_up.device_id); if (ret) - dev_err(hd->parent, "error %d initializing" + dev_err(hd->parent, "error %d initializing " "module %hhu interface %hhu\n", ret, management->link_up.module_id, management->link_up.interface_id); diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c4b650522ac2..1a8f53ef34cf 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -164,6 +164,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, INIT_LIST_HEAD(&connection->protocol_links); if (!gb_protocol_get(connection, protocol_id)) { + pr_err("protocol 0x%02hhx not found\n", protocol_id); kfree(connection); return NULL; } @@ -191,6 +192,8 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, retval = device_add(&connection->dev); if (retval) { + pr_err("failed to add connection device for cport 0x%04hx\n", + cport_id); gb_connection_hd_cport_id_free(connection); gb_protocol_put(connection); put_device(&connection->dev); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 163cdc439e0c..eb63f638fc2c 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -78,6 +78,8 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) retval = device_add(&interface->dev); if (retval) { + pr_err("failed to add interface device for id 0x%02hhx\n", + interface_id); kfree(interface); return NULL; } diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 3a64a2b3eb06..9cf98cd8d53d 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -100,6 +100,8 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) retval = device_add(&gmod->dev); if (retval) { + pr_err("failed to add module device for id 0x%02hhx\n", + module_id); put_device(&gmod->dev); return NULL; } -- cgit v1.2.3-59-g8ed1b From 6ae7fa4520c9a3e316996320ad6d6439f08bb63c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:12:50 -0600 Subject: greybus: identify protocol by id *and* version Right now we only look up a protocol based on its protocol id. Add support for maintaining a major and minor version as well, and use them when looking up a protocol. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 5 ++++- drivers/staging/greybus/protocol.c | 31 ++++++++++++++++++++----------- drivers/staging/greybus/protocol.h | 19 ++++++++++++++----- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1a8f53ef34cf..703c286f5631 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -157,13 +157,16 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, struct gb_connection *connection; struct greybus_host_device *hd; int retval; + u8 major = 0; + u8 minor = 1; connection = kzalloc(sizeof(*connection), GFP_KERNEL); if (!connection) return NULL; INIT_LIST_HEAD(&connection->protocol_links); - if (!gb_protocol_get(connection, protocol_id)) { + /* XXX Will have to establish connections to get version */ + if (!gb_protocol_get(connection, protocol_id, major, minor)) { pr_err("protocol 0x%02hhx not found\n", protocol_id); kfree(connection); return NULL; diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index e0bcd4bc28a5..704b180f0ffb 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -13,30 +13,31 @@ static DEFINE_SPINLOCK(gb_protocols_lock); static LIST_HEAD(gb_protocols); /* Caller must hold gb_protocols_lock */ -static struct gb_protocol *_gb_protocol_find(u8 id) +static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) { struct gb_protocol *protocol; list_for_each_entry(protocol, &gb_protocols, links) - if (protocol->id == id) + if (protocol->id == id && protocol->major == major + && protocol->minor == minor) return protocol; return NULL; } /* This is basically for debug */ -static struct gb_protocol *gb_protocol_find(u8 id) +static struct gb_protocol *gb_protocol_find(u8 id, u8 major, u8 minor) { struct gb_protocol *protocol; spin_lock_irq(&gb_protocols_lock); - protocol = _gb_protocol_find(id); + protocol = _gb_protocol_find(id, major, minor); spin_unlock_irq(&gb_protocols_lock); return protocol; } /* Returns true if protocol was succesfully registered, false otherwise */ -bool gb_protocol_register(u8 id) +bool gb_protocol_register(u8 id, u8 major, u8 minor) { struct gb_protocol *protocol; struct gb_protocol *existing; @@ -46,10 +47,12 @@ bool gb_protocol_register(u8 id) if (!protocol) return false; protocol->id = id; + protocol->major = major; + protocol->minor = minor; INIT_LIST_HEAD(&protocol->connections); spin_lock_irq(&gb_protocols_lock); - existing = _gb_protocol_find(id); + existing = _gb_protocol_find(id, major, minor); if (!existing) list_add(&protocol->links, &gb_protocols); spin_unlock_irq(&gb_protocols_lock); @@ -77,7 +80,8 @@ bool gb_protocol_deregister(struct gb_protocol *protocol) } /* Returns true if successful, false otherwise */ -bool gb_protocol_get(struct gb_connection *connection, u8 id) +bool +gb_protocol_get(struct gb_connection *connection, u8 id, u8 major, u8 minor) { struct gb_protocol *protocol; @@ -90,7 +94,7 @@ bool gb_protocol_get(struct gb_connection *connection, u8 id) } spin_lock_irq(&gb_protocols_lock); - protocol = _gb_protocol_find(id); + protocol = _gb_protocol_find(id, major, minor); if (protocol) list_add(&connection->protocol_links, &protocol->connections); spin_unlock_irq(&gb_protocols_lock); @@ -102,6 +106,8 @@ bool gb_protocol_get(struct gb_connection *connection, u8 id) void gb_protocol_put(struct gb_connection *connection) { struct gb_protocol *protocol = connection->protocol; + u8 major = protocol->major; + u8 minor = protocol->minor; /* Sanity checks */ if (list_empty(&connection->protocol_links)) { @@ -109,9 +115,12 @@ void gb_protocol_put(struct gb_connection *connection) "connection protocol not recorded"); return; } - if (!protocol || gb_protocol_find(protocol->id) != protocol) { - gb_connection_err(connection, - "connection has undefined protocol"); + if (!protocol) { + gb_connection_err(connection, "connection has no protocol"); + return; + } + if (gb_protocol_find(protocol->id, major, minor) != protocol) { + gb_connection_err(connection, "connection protocol not found"); return; } diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index d244e9d3eca1..d53f67def779 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -11,16 +11,25 @@ #include "greybus.h" +/* + * Protocols having the same id but different major and/or minor + * version numbers are treated as distinct protocols. If it makes + * sense someday we could group protocols having the same id. + */ struct gb_protocol { - u8 id; - struct list_head connections; /* protocol users */ - struct list_head links; /* global list */ + u8 id; + u8 major; + u8 minor; + + struct list_head connections; /* protocol users */ + struct list_head links; /* global list */ }; -bool gb_protocol_register(u8 id); +bool gb_protocol_register(u8 id, u8 major, u8 minor); bool gb_protocol_deregister(struct gb_protocol *protocol); -bool gb_protocol_get(struct gb_connection *connection, u8 id); +bool gb_protocol_get(struct gb_connection *connection, u8 id, + u8 major, u8 minor); void gb_protocol_put(struct gb_connection *connection); #endif /* __PROTOCOL_H */ -- cgit v1.2.3-59-g8ed1b From 0e44765743c06664773475cd07684a70a29a6816 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:12:51 -0600 Subject: greybus: count rather than list protocol users We don't really need a list of protocol users, we can just keep track of how many there are. Get rid of the list and use a count instead. Also, have gb_protocol_get() return the protocol rather than assigning a passed-in connection pointer's protocol. Make a comparable change to the gb_protocol_put() interface. Get rid of gb_protocol_find() (the version that locks), because it is no longer needed. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 11 ++--- drivers/staging/greybus/connection.h | 1 - drivers/staging/greybus/protocol.c | 91 ++++++++++++++++-------------------- drivers/staging/greybus/protocol.h | 7 ++- 4 files changed, 47 insertions(+), 63 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 703c286f5631..e3000f7eb799 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -164,9 +164,9 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, if (!connection) return NULL; - INIT_LIST_HEAD(&connection->protocol_links); /* XXX Will have to establish connections to get version */ - if (!gb_protocol_get(connection, protocol_id, major, minor)) { + connection->protocol = gb_protocol_get(protocol_id, major, minor); + if (!connection->protocol) { pr_err("protocol 0x%02hhx not found\n", protocol_id); kfree(connection); return NULL; @@ -175,7 +175,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, hd = interface->gmod->hd; connection->hd = hd; if (!gb_connection_hd_cport_id_alloc(connection)) { - gb_protocol_put(connection); + gb_protocol_put(connection->protocol); kfree(connection); return NULL; } @@ -198,7 +198,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, pr_err("failed to add connection device for cport 0x%04hx\n", cport_id); gb_connection_hd_cport_id_free(connection); - gb_protocol_put(connection); + gb_protocol_put(connection->protocol); put_device(&connection->dev); return NULL; } @@ -239,8 +239,7 @@ void gb_connection_destroy(struct gb_connection *connection) spin_unlock_irq(&gb_connections_lock); gb_connection_hd_cport_id_free(connection); - /* kref_put(connection->hd); */ - gb_protocol_put(connection); + gb_protocol_put(connection->protocol); device_del(&connection->dev); } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 8056993c00ac..ea54334238bd 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -41,7 +41,6 @@ struct gb_connection { struct list_head interface_links; struct gb_protocol *protocol; - struct list_head protocol_links; enum gb_connection_state state; diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 704b180f0ffb..347d52c3445c 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -24,18 +24,6 @@ static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) return NULL; } -/* This is basically for debug */ -static struct gb_protocol *gb_protocol_find(u8 id, u8 major, u8 minor) -{ - struct gb_protocol *protocol; - - spin_lock_irq(&gb_protocols_lock); - protocol = _gb_protocol_find(id, major, minor); - spin_unlock_irq(&gb_protocols_lock); - - return protocol; -} - /* Returns true if protocol was succesfully registered, false otherwise */ bool gb_protocol_register(u8 id, u8 major, u8 minor) { @@ -49,7 +37,6 @@ bool gb_protocol_register(u8 id, u8 major, u8 minor) protocol->id = id; protocol->major = major; protocol->minor = minor; - INIT_LIST_HEAD(&protocol->connections); spin_lock_irq(&gb_protocols_lock); existing = _gb_protocol_find(id, major, minor); @@ -68,64 +55,64 @@ bool gb_protocol_register(u8 id, u8 major, u8 minor) /* Returns true if successful, false otherwise */ bool gb_protocol_deregister(struct gb_protocol *protocol) { + u8 protocol_count; + spin_lock_irq(&gb_protocols_lock); - if (list_empty(&protocol->connections)) - list_del(&protocol->links); - else - protocol = NULL; /* Protocol is still in use */ + protocol = _gb_protocol_find(protocol->id, protocol->major, + protocol->minor); + if (protocol) { + protocol_count = protocol->count; + if (!protocol_count) + list_del(&protocol->links); + } spin_unlock_irq(&gb_protocols_lock); kfree(protocol); - return protocol != NULL; + return protocol && !protocol_count; } -/* Returns true if successful, false otherwise */ -bool -gb_protocol_get(struct gb_connection *connection, u8 id, u8 major, u8 minor) +/* Returns the requested protocol if available, or a null pointer */ +struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) { struct gb_protocol *protocol; - - /* Sanity */ - if (!list_empty(&connection->protocol_links) || - !connection->protocol->id) { - gb_connection_err(connection, - "connection already has protocol"); - return false; - } + u8 protocol_count; spin_lock_irq(&gb_protocols_lock); protocol = _gb_protocol_find(id, major, minor); - if (protocol) - list_add(&connection->protocol_links, &protocol->connections); + if (protocol) { + protocol_count = protocol->count; + if (protocol_count != U8_MAX) + protocol->count++; + } spin_unlock_irq(&gb_protocols_lock); - connection->protocol = protocol; - return protocol != NULL; + if (protocol) + WARN_ON(protocol_count == U8_MAX); + else + pr_err("protocol id %hhu version %hhu.%hhu not found\n", + id, major, minor); + + return protocol; } -void gb_protocol_put(struct gb_connection *connection) +void gb_protocol_put(struct gb_protocol *protocol) { - struct gb_protocol *protocol = connection->protocol; u8 major = protocol->major; u8 minor = protocol->minor; - - /* Sanity checks */ - if (list_empty(&connection->protocol_links)) { - gb_connection_err(connection, - "connection protocol not recorded"); - return; - } - if (!protocol) { - gb_connection_err(connection, "connection has no protocol"); - return; - } - if (gb_protocol_find(protocol->id, major, minor) != protocol) { - gb_connection_err(connection, "connection protocol not found"); - return; - } + u8 protocol_count; spin_lock_irq(&gb_protocols_lock); - list_del(&connection->protocol_links); - connection->protocol = NULL; + protocol = _gb_protocol_find(protocol->id, protocol->major, + protocol->minor); + if (protocol) { + protocol_count = protocol->count; + if (protocol_count) + protocol->count--; + } spin_unlock_irq(&gb_protocols_lock); + if (protocol) + WARN_ON(!protocol_count); + else + pr_err("protocol id %hhu version %hhu.%hhu not found\n", + protocol->id, major, minor); } diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index d53f67def779..aa7b5548e8a8 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -20,16 +20,15 @@ struct gb_protocol { u8 id; u8 major; u8 minor; + u8 count; - struct list_head connections; /* protocol users */ struct list_head links; /* global list */ }; bool gb_protocol_register(u8 id, u8 major, u8 minor); bool gb_protocol_deregister(struct gb_protocol *protocol); -bool gb_protocol_get(struct gb_connection *connection, u8 id, - u8 major, u8 minor); -void gb_protocol_put(struct gb_connection *connection); +struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); +void gb_protocol_put(struct gb_protocol *protocol); #endif /* __PROTOCOL_H */ -- cgit v1.2.3-59-g8ed1b From dbb8894e0c3cf1661d83466faa277a8d436a1cba Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:12:52 -0600 Subject: greybus: order the protocols list Add protocols to the global list in sorted order, based on their protocol id, and then their major and minor version number. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 66 +++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 347d52c3445c..6fec32eea76d 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -17,10 +17,24 @@ static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) { struct gb_protocol *protocol; - list_for_each_entry(protocol, &gb_protocols, links) - if (protocol->id == id && protocol->major == major - && protocol->minor == minor) - return protocol; + list_for_each_entry(protocol, &gb_protocols, links) { + if (protocol->id < id) + continue; + if (protocol->id > id) + break; + + if (protocol->major > major) + continue; + if (protocol->major < major) + break; + + if (protocol->minor > minor) + continue; + if (protocol->minor < minor) + break; + + return protocol; + } return NULL; } @@ -38,24 +52,52 @@ bool gb_protocol_register(u8 id, u8 major, u8 minor) protocol->major = major; protocol->minor = minor; + /* + * The protocols list is sorted first by protocol id (low to + * high), then by major version (high to low), and finally + * by minor version (high to low). Searching only by + * protocol id will produce the newest implemented version + * of the protocol. + */ spin_lock_irq(&gb_protocols_lock); - existing = _gb_protocol_find(id, major, minor); - if (!existing) - list_add(&protocol->links, &gb_protocols); - spin_unlock_irq(&gb_protocols_lock); - if (existing) { + list_for_each_entry(existing, &gb_protocols, links) { + if (existing->id < id) + continue; + if (existing->id > id) + break; + + if (existing->major > major) + continue; + if (existing->major < major) + break; + + if (existing->minor > minor) + continue; + if (existing->minor < minor) + break; + + /* A matching protocol has already been registered */ + spin_unlock_irq(&gb_protocols_lock); kfree(protocol); - protocol = NULL; + + return false; } - return protocol != NULL; + /* + * We need to insert the protocol here, before the existing one + * (or before the head if we searched the whole list) + */ + list_add_tail(&protocol->links, &existing->links); + spin_unlock_irq(&gb_protocols_lock); + + return true; } /* Returns true if successful, false otherwise */ bool gb_protocol_deregister(struct gb_protocol *protocol) { - u8 protocol_count; + u8 protocol_count = 0; spin_lock_irq(&gb_protocols_lock); protocol = _gb_protocol_find(protocol->id, protocol->major, -- cgit v1.2.3-59-g8ed1b From 19d03decd3415a7a0ec6c378720058f29f9568cc Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:12:53 -0600 Subject: greybus: register preallocated protocols Set up protocol structures as static objects in each protocol source file. Pass the address of that in--rather than the protocol id and version information--to the protocol registration routine. Call a central routine to register all our pre-defined protocols. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 16 ++++++++ drivers/staging/greybus/core.c | 14 +++++-- drivers/staging/greybus/gpio-gb.c | 16 ++++++++ drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/i2c-gb.c | 16 ++++++++ drivers/staging/greybus/protocol.c | 64 ++++++++++++++++++++++++------ drivers/staging/greybus/protocol.h | 25 +++++++++++- drivers/staging/greybus/sdio-gb.c | 16 ++++++++ drivers/staging/greybus/uart-gb.c | 16 ++++++++ 9 files changed, 166 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 28c0d0b9aefe..457daf700543 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -404,3 +404,19 @@ struct gb_connection_handler gb_battery_connection_handler = { .connection_init = gb_battery_connection_init, .connection_exit = gb_battery_connection_exit, }; + +static struct gb_protocol battery_protocol = { + .id = GREYBUS_PROTOCOL_BATTERY, + .major = 0, + .minor = 1, +}; + +bool gb_battery_protocol_init(void) +{ + return gb_protocol_register(&battery_protocol); +} + +void gb_battery_protocol_exit(void) +{ + gb_protocol_deregister(&battery_protocol); +} diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 252d131c3ae8..1d05c35a1b60 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -277,17 +277,23 @@ static int __init gb_init(void) goto error_operation; } - return 0; + if (!gb_protocol_init()) { + /* This only fails for duplicate protocol registration */ + retval = -EEXIST; + pr_err("gb_protocol_init failed\n"); + goto error_protocol; + } + return 0; /* Success */ + +error_protocol: + gb_operation_exit(); error_operation: gb_gbuf_exit(); - error_gbuf: gb_ap_exit(); - error_ap: bus_unregister(&greybus_bus_type); - error_bus: gb_debugfs_cleanup(); diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index df7dbae85d63..242b91a99d22 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -796,3 +796,19 @@ struct gb_connection_handler gb_gpio_connection_handler = { .connection_init = gb_gpio_connection_init, .connection_exit = gb_gpio_connection_exit, }; + +static struct gb_protocol gpio_protocol = { + .id = GREYBUS_PROTOCOL_GPIO, + .major = 0, + .minor = 1, +}; + +bool gb_gpio_protocol_init(void) +{ + return gb_protocol_register(&gpio_protocol); +} + +void gb_gpio_protocol_exit(void) +{ + gb_protocol_deregister(&gpio_protocol); +} diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index c6988ce2818a..844ab8a745b0 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -30,6 +30,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_I2C = 0x03, GREYBUS_PROTOCOL_UART = 0x04, GREYBUS_PROTOCOL_HID = 0x05, + GREYBUS_PROTOCOL_SDIO = 0x06, GREYBUS_PROTOCOL_BATTERY = 0x08, GREYBUS_PROTOCOL_LED = 0x0e, /* ... */ diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index b26464a14b32..c8fae17d4b77 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -522,3 +522,19 @@ struct gb_connection_handler gb_i2c_connection_handler = { .connection_init = gb_i2c_connection_init, .connection_exit = gb_i2c_connection_exit, }; + +static struct gb_protocol i2c_protocol = { + .id = GREYBUS_PROTOCOL_I2C, + .major = 0, + .minor = 1, +}; + +bool gb_i2c_protocol_init(void) +{ + return gb_protocol_register(&i2c_protocol); +} + +void gb_i2c_protocol_exit(void) +{ + gb_protocol_deregister(&i2c_protocol); +} diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 6fec32eea76d..93e0af3c6b4e 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -39,18 +39,12 @@ static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) } /* Returns true if protocol was succesfully registered, false otherwise */ -bool gb_protocol_register(u8 id, u8 major, u8 minor) +bool gb_protocol_register(struct gb_protocol *protocol) { - struct gb_protocol *protocol; struct gb_protocol *existing; - - /* Initialize it speculatively */ - protocol = kzalloc(sizeof(*protocol), GFP_KERNEL); - if (!protocol) - return false; - protocol->id = id; - protocol->major = major; - protocol->minor = minor; + u8 id = protocol->id; + u8 major = protocol->major; + u8 minor = protocol->minor; /* * The protocols list is sorted first by protocol id (low to @@ -79,7 +73,6 @@ bool gb_protocol_register(u8 id, u8 major, u8 minor) /* A matching protocol has already been registered */ spin_unlock_irq(&gb_protocols_lock); - kfree(protocol); return false; } @@ -94,7 +87,17 @@ bool gb_protocol_register(u8 id, u8 major, u8 minor) return true; } -/* Returns true if successful, false otherwise */ +/* + * De-register a previously registered protocol. + * + * XXX Currently this fails (and reports an error to the caller) if + * XXX the protocol is currently in use. We may want to forcefully + * XXX kill off a protocol and all its active users at some point. + * XXX But I think that's better handled by quescing modules that + * XXX have users and having those users drop their reference. + * + * Returns true if successful, false otherwise. + */ bool gb_protocol_deregister(struct gb_protocol *protocol) { u8 protocol_count = 0; @@ -108,7 +111,6 @@ bool gb_protocol_deregister(struct gb_protocol *protocol) list_del(&protocol->links); } spin_unlock_irq(&gb_protocols_lock); - kfree(protocol); return protocol && !protocol_count; } @@ -158,3 +160,39 @@ void gb_protocol_put(struct gb_protocol *protocol) pr_err("protocol id %hhu version %hhu.%hhu not found\n", protocol->id, major, minor); } + +bool gb_protocol_init(void) +{ + bool ret = true; + + if (!gb_battery_protocol_init()) { + pr_err("error initializing battery protocol\n"); + ret = false; + } + if (!gb_gpio_protocol_init()) { + pr_err("error initializing gpio protocol\n"); + ret = false; + } + if (!gb_i2c_protocol_init()) { + pr_err("error initializing i2c protocol\n"); + ret = false; + } + if (!gb_uart_protocol_init()) { + pr_err("error initializing uart protocol\n"); + ret = false; + } + if (!gb_sdio_protocol_init()) { + pr_err("error initializing sdio protocol\n"); + ret = false; + } + return ret; +} + +void gb_protocol_exit(void) +{ + gb_sdio_protocol_exit(); + gb_uart_protocol_exit(); + gb_i2c_protocol_exit(); + gb_gpio_protocol_exit(); + gb_battery_protocol_exit(); +} diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index aa7b5548e8a8..c2adfdca8bf7 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -25,10 +25,33 @@ struct gb_protocol { struct list_head links; /* global list */ }; -bool gb_protocol_register(u8 id, u8 major, u8 minor); +bool gb_protocol_register(struct gb_protocol *protocol); bool gb_protocol_deregister(struct gb_protocol *protocol); struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); void gb_protocol_put(struct gb_protocol *protocol); +/* + * These are defined in their respective protocol source files. + * Declared here for now. They could be added via modules, or maybe + * just use initcalls (which level?). + */ +extern bool gb_battery_protocol_init(void); +extern void gb_battery_protocol_exit(void); + +extern bool gb_gpio_protocol_init(void); +extern void gb_gpio_protocol_exit(void); + +extern bool gb_i2c_protocol_init(void); +extern void gb_i2c_protocol_exit(void); + +extern bool gb_uart_protocol_init(void); +extern void gb_uart_protocol_exit(void); + +extern bool gb_sdio_protocol_init(void); +extern void gb_sdio_protocol_exit(void); + +bool gb_protocol_init(void); +void gb_protocol_exit(void); + #endif /* __PROTOCOL_H */ diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 30caba8d0fa5..8fbfbca37c48 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -81,3 +81,19 @@ struct gb_connection_handler gb_sdio_connection_handler = { .connection_init = gb_sdio_connection_init, .connection_exit = gb_sdio_connection_exit, }; + +static struct gb_protocol sdio_protocol = { + .id = GREYBUS_PROTOCOL_SDIO, + .major = 0, + .minor = 1, +}; + +bool gb_sdio_protocol_init(void) +{ + return gb_protocol_register(&sdio_protocol); +} + +void gb_sdio_protocol_exit(void) +{ + gb_protocol_deregister(&sdio_protocol); +} diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index b52d9e11f536..5596644e952b 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -524,3 +524,19 @@ struct gb_connection_handler gb_uart_connection_handler = { .connection_init = gb_uart_connection_init, .connection_exit = gb_uart_connection_exit, }; + +static struct gb_protocol uart_protocol = { + .id = GREYBUS_PROTOCOL_UART, + .major = 0, + .minor = 1, +}; + +bool gb_uart_protocol_init(void) +{ + return gb_protocol_register(&uart_protocol); +} + +void gb_uart_protocol_exit(void) +{ + gb_protocol_deregister(&uart_protocol); +} -- cgit v1.2.3-59-g8ed1b From 5d9fd7e1ba801ff83bb657ad364581947400898c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:12:54 -0600 Subject: greybus: move methods into protocol Get rid of the connection handler structure, and instead put the methods that were there into the protocol structure. Eliminate the big switch statement in connection_init() and just call the connection's protocol's init function there directly. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 7 ++---- drivers/staging/greybus/connection.c | 43 +++++++----------------------------- drivers/staging/greybus/connection.h | 11 --------- drivers/staging/greybus/gpio-gb.c | 7 ++---- drivers/staging/greybus/i2c-gb.c | 7 ++---- drivers/staging/greybus/protocol.h | 6 +++++ drivers/staging/greybus/sdio-gb.c | 7 ++---- drivers/staging/greybus/uart-gb.c | 7 ++---- 8 files changed, 24 insertions(+), 71 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 457daf700543..a4015673bf4c 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -400,15 +400,12 @@ static void gb_battery_connection_exit(struct gb_connection *connection) kfree(gb); } -struct gb_connection_handler gb_battery_connection_handler = { - .connection_init = gb_battery_connection_init, - .connection_exit = gb_battery_connection_exit, -}; - static struct gb_protocol battery_protocol = { .id = GREYBUS_PROTOCOL_BATTERY, .major = 0, .minor = 1, + .connection_init = gb_battery_connection_init, + .connection_exit = gb_battery_connection_exit, }; bool gb_battery_protocol_init(void) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index e3000f7eb799..586457f027de 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -267,45 +267,18 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) va_end(args); } -/* - * XXX Protocols should have a set of function pointers: - * ->init (called here, to initialize the device) - * ->input_handler - * ->exit (reverse of init) - */ int gb_connection_init(struct gb_connection *connection) { int ret; - /* Need to enable the connection to initialize it */ - connection->state = GB_CONNECTION_STATE_ENABLED; - switch (connection->protocol->id) { - case GREYBUS_PROTOCOL_I2C: - connection->handler = &gb_i2c_connection_handler; - break; - case GREYBUS_PROTOCOL_GPIO: - connection->handler = &gb_gpio_connection_handler; - break; - case GREYBUS_PROTOCOL_BATTERY: - connection->handler = &gb_battery_connection_handler; - break; - case GREYBUS_PROTOCOL_UART: - connection->handler = &gb_uart_connection_handler; - break; - case GREYBUS_PROTOCOL_CONTROL: - case GREYBUS_PROTOCOL_AP: - case GREYBUS_PROTOCOL_HID: - case GREYBUS_PROTOCOL_LED: - case GREYBUS_PROTOCOL_VENDOR: - default: - gb_connection_err(connection, "unimplemented protocol %hhu", - connection->protocol->id); - ret = -ENXIO; - break; + if (!connection->protocol) { + gb_connection_err(connection, "uninitialized connection"); + return -EIO; } - ret = connection->handler->connection_init(connection); - + /* Need to enable the connection to initialize it */ + connection->state = GB_CONNECTION_STATE_ENABLED; + ret = connection->protocol->connection_init(connection); if (ret) connection->state = GB_CONNECTION_STATE_ERROR; @@ -314,10 +287,10 @@ int gb_connection_init(struct gb_connection *connection) void gb_connection_exit(struct gb_connection *connection) { - if (!connection->handler) { + if (!connection->protocol) { gb_connection_err(connection, "uninitialized connection"); return; } connection->state = GB_CONNECTION_STATE_DESTROYING; - connection->handler->connection_exit(connection); + connection->protocol->connection_exit(connection); } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index ea54334238bd..893c02af3710 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -21,15 +21,6 @@ enum gb_connection_state { GB_CONNECTION_STATE_DESTROYING = 4, }; -struct gb_connection; -typedef int (*gb_connection_init_t)(struct gb_connection *); -typedef void (*gb_connection_exit_t)(struct gb_connection *); - -struct gb_connection_handler { - gb_connection_init_t connection_init; - gb_connection_exit_t connection_exit; -}; - struct gb_connection { struct greybus_host_device *hd; struct gb_interface *interface; @@ -48,8 +39,6 @@ struct gb_connection { struct rb_root pending; /* awaiting reponse */ atomic_t op_cycle; - struct gb_connection_handler *handler; - void *private; }; #define to_gb_connection(d) container_of(d, struct gb_connection, dev) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 242b91a99d22..a4ee35671434 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -792,15 +792,12 @@ static void gb_gpio_connection_exit(struct gb_connection *connection) kfree(gb_gpio_controller); } -struct gb_connection_handler gb_gpio_connection_handler = { - .connection_init = gb_gpio_connection_init, - .connection_exit = gb_gpio_connection_exit, -}; - static struct gb_protocol gpio_protocol = { .id = GREYBUS_PROTOCOL_GPIO, .major = 0, .minor = 1, + .connection_init = gb_gpio_connection_init, + .connection_exit = gb_gpio_connection_exit, }; bool gb_gpio_protocol_init(void) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index c8fae17d4b77..60db15ee364d 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -518,15 +518,12 @@ static void gb_i2c_connection_exit(struct gb_connection *connection) kfree(gb_i2c_dev); } -struct gb_connection_handler gb_i2c_connection_handler = { - .connection_init = gb_i2c_connection_init, - .connection_exit = gb_i2c_connection_exit, -}; - static struct gb_protocol i2c_protocol = { .id = GREYBUS_PROTOCOL_I2C, .major = 0, .minor = 1, + .connection_init = gb_i2c_connection_init, + .connection_exit = gb_i2c_connection_exit, }; bool gb_i2c_protocol_init(void) diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index c2adfdca8bf7..32178f1f2671 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -11,6 +11,9 @@ #include "greybus.h" +typedef int (*gb_connection_init_t)(struct gb_connection *); +typedef void (*gb_connection_exit_t)(struct gb_connection *); + /* * Protocols having the same id but different major and/or minor * version numbers are treated as distinct protocols. If it makes @@ -23,6 +26,9 @@ struct gb_protocol { u8 count; struct list_head links; /* global list */ + + gb_connection_init_t connection_init; + gb_connection_exit_t connection_exit; }; bool gb_protocol_register(struct gb_protocol *protocol); diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 8fbfbca37c48..9e5fb8f26425 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -77,15 +77,12 @@ static void gb_sdio_connection_exit(struct gb_connection *connection) connection->private = NULL; } -struct gb_connection_handler gb_sdio_connection_handler = { - .connection_init = gb_sdio_connection_init, - .connection_exit = gb_sdio_connection_exit, -}; - static struct gb_protocol sdio_protocol = { .id = GREYBUS_PROTOCOL_SDIO, .major = 0, .minor = 1, + .connection_init = gb_sdio_connection_init, + .connection_exit = gb_sdio_connection_exit, }; bool gb_sdio_protocol_init(void) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 5596644e952b..56db8911d800 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -520,15 +520,12 @@ static void gb_tty_exit(void) unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); } -struct gb_connection_handler gb_uart_connection_handler = { - .connection_init = gb_uart_connection_init, - .connection_exit = gb_uart_connection_exit, -}; - static struct gb_protocol uart_protocol = { .id = GREYBUS_PROTOCOL_UART, .major = 0, .minor = 1, + .connection_init = gb_uart_connection_init, + .connection_exit = gb_uart_connection_exit, }; bool gb_uart_protocol_init(void) -- cgit v1.2.3-59-g8ed1b From f8fb05e2b89eddaadce319fb55a532731393e630 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 5 Nov 2014 16:12:55 -0600 Subject: greybus: add an incoming request receive method Define a new protocol method intended to handle the receipt of an incoming operation request. Most protocols have no expected incoming requests and can leave this null. If a request arrives for a protocol with no request receive handler an error is reported and the request fails. Get rid of the previous fixed array of receive handlers, it's no longer needed. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 1 + drivers/staging/greybus/gpio-gb.c | 1 + drivers/staging/greybus/i2c-gb.c | 1 + drivers/staging/greybus/operation.c | 45 ++++++++++-------------------------- drivers/staging/greybus/protocol.h | 4 ++++ drivers/staging/greybus/sdio-gb.c | 1 + drivers/staging/greybus/uart-gb.c | 1 + 7 files changed, 21 insertions(+), 33 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index a4015673bf4c..4bd7aed0a27b 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -406,6 +406,7 @@ static struct gb_protocol battery_protocol = { .minor = 1, .connection_init = gb_battery_connection_init, .connection_exit = gb_battery_connection_exit, + .request_recv = NULL, /* no incoming requests */ }; bool gb_battery_protocol_init(void) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index a4ee35671434..40833fa69efe 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -798,6 +798,7 @@ static struct gb_protocol gpio_protocol = { .minor = 1, .connection_init = gb_gpio_connection_init, .connection_exit = gb_gpio_connection_exit, + .request_recv = NULL, /* no incoming requests */ }; bool gb_gpio_protocol_init(void) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 60db15ee364d..c179078b8a2e 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -524,6 +524,7 @@ static struct gb_protocol i2c_protocol = { .minor = 1, .connection_init = gb_i2c_connection_init, .connection_exit = gb_i2c_connection_exit, + .request_recv = NULL, /* no incoming requests */ }; bool gb_i2c_protocol_init(void) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 9cb9c9d590d2..24707f67622c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -176,46 +176,25 @@ int gb_operation_wait(struct gb_operation *operation) } -/* - * This handler is used if no operation response messages are ever - * expected for a given protocol. - */ -static void gb_operation_recv_none(struct gb_operation *operation) -{ - /* Nothing to do! */ -} - -typedef void (*gb_operation_recv_handler)(struct gb_operation *operation); -static gb_operation_recv_handler gb_operation_recv_handlers[] = { - [GREYBUS_PROTOCOL_CONTROL] = NULL, - [GREYBUS_PROTOCOL_AP] = NULL, - [GREYBUS_PROTOCOL_GPIO] = NULL, - [GREYBUS_PROTOCOL_I2C] = gb_operation_recv_none, - [GREYBUS_PROTOCOL_UART] = NULL, - [GREYBUS_PROTOCOL_HID] = NULL, - [GREYBUS_PROTOCOL_BATTERY] = gb_operation_recv_none, - [GREYBUS_PROTOCOL_LED] = NULL, - [GREYBUS_PROTOCOL_VENDOR] = NULL, -}; - static void gb_operation_request_handle(struct gb_operation *operation) { - u8 protocol_id = operation->connection->protocol->id; - - /* Subtract one from array size to stay within u8 range */ - if (protocol_id <= (u8)(ARRAY_SIZE(gb_operation_recv_handlers) - 1)) { - gb_operation_recv_handler handler; + struct gb_protocol *protocol = operation->connection->protocol; + struct gb_operation_msg_hdr *header; - handler = gb_operation_recv_handlers[protocol_id]; - if (handler) { - handler(operation); /* Handle the request */ - return; - } + /* + * If the protocol has no incoming request handler, report + * an error and mark the request bad. + */ + if (protocol->request_recv) { + protocol->request_recv(operation); + goto out; } + header = operation->request->transfer_buffer; gb_connection_err(operation->connection, - "unrecognized protocol id %hhu\n", protocol_id); + "unexpected incoming request type 0x%02hhx\n", header->type); operation->result = GB_OP_PROTOCOL_BAD; +out: gb_operation_complete(operation); } diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 32178f1f2671..a236401b48ab 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -11,8 +11,11 @@ #include "greybus.h" +struct gb_operation; + typedef int (*gb_connection_init_t)(struct gb_connection *); typedef void (*gb_connection_exit_t)(struct gb_connection *); +typedef void (*gb_request_recv_t)(struct gb_operation *); /* * Protocols having the same id but different major and/or minor @@ -29,6 +32,7 @@ struct gb_protocol { gb_connection_init_t connection_init; gb_connection_exit_t connection_exit; + gb_request_recv_t request_recv; }; bool gb_protocol_register(struct gb_protocol *protocol); diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 9e5fb8f26425..4775ed0c7bea 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -83,6 +83,7 @@ static struct gb_protocol sdio_protocol = { .minor = 1, .connection_init = gb_sdio_connection_init, .connection_exit = gb_sdio_connection_exit, + .request_recv = NULL, /* no incoming requests */ }; bool gb_sdio_protocol_init(void) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 56db8911d800..9638d0e8aa0d 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -526,6 +526,7 @@ static struct gb_protocol uart_protocol = { .minor = 1, .connection_init = gb_uart_connection_init, .connection_exit = gb_uart_connection_exit, + .request_recv = NULL, /* no incoming requests */ }; bool gb_uart_protocol_init(void) -- cgit v1.2.3-59-g8ed1b From 8d59897978aa0dfb3562f130e568e898e6952e72 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 6 Nov 2014 07:00:59 -0600 Subject: greybus: kill test_sink.c This file is an artifact of some early testing, but it is otherwise unused. So get rid of it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 - drivers/staging/greybus/test_sink.c | 38 ------------------------------------- 2 files changed, 39 deletions(-) delete mode 100644 drivers/staging/greybus/test_sink.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 39874deacab0..bb90895deea6 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -17,7 +17,6 @@ greybus-y := core.o \ obj-m += greybus.o obj-m += es1-ap-usb.o -obj-m += test_sink.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/test_sink.c b/drivers/staging/greybus/test_sink.c deleted file mode 100644 index 1b477695b418..000000000000 --- a/drivers/staging/greybus/test_sink.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Test "sink" Greybus driver. - * - * Copyright 2014 Google Inc. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include "greybus.h" - -struct test_device { - struct gb_module *gmod; -}; - -int gb_register_cport_complete(struct gb_module *gmod, - gbuf_complete_t handler, u16 cport_id, - void *context); -void gb_deregister_cport_complete(u16 cport_id); - - - -static int test_init(void) -{ - return 0; -} - -static void test_exit(void) -{ -} - -module_init(test_init); -module_exit(test_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From 7a0eed065ded836bbb6b71da965cef0d6a0aeecf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 6 Nov 2014 07:01:00 -0600 Subject: greybus: kill old cport handler code Handling of incoming requests has been moved into the Greybus connection and protocol layers. As a result, the original cport oriented handler code is no longer used. So get rid of it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 31 ------------------------------- drivers/staging/greybus/greybus.h | 5 ----- 2 files changed, 36 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 4f591aa07863..17141b6832cd 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -114,37 +114,6 @@ void greybus_kill_gbuf(struct gbuf *gbuf) hd->driver->kill_gbuf(gbuf); } -#define MAX_CPORTS 1024 -struct gb_cport_handler { - gbuf_complete_t handler; - u16 cport_id; - struct gb_module *gmod; - void *context; -}; - -static struct gb_cport_handler cport_handler[MAX_CPORTS]; -// FIXME - use a lock for this list of handlers, but really, for now we don't -// need it, we don't have a dynamic system... - -int gb_register_cport_complete(struct gb_module *gmod, - gbuf_complete_t handler, - u16 cport_id, - void *context) -{ - if (cport_handler[cport_id].handler) - return -EINVAL; - cport_handler[cport_id].context = context; - cport_handler[cport_id].gmod = gmod; - cport_handler[cport_id].cport_id = cport_id; - cport_handler[cport_id].handler = handler; - return 0; -} - -void gb_deregister_cport_complete(u16 cport_id) -{ - cport_handler[cport_id].handler = NULL; -} - void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length) { diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f6c90e0c0f22..834cbaa6fade 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -258,11 +258,6 @@ void gb_debugfs_cleanup(void); int gb_gbuf_init(void); void gb_gbuf_exit(void); -int gb_register_cport_complete(struct gb_module *gmod, - gbuf_complete_t handler, u16 cport_id, - void *context); -void gb_deregister_cport_complete(u16 cport_id); - extern struct bus_type greybus_bus_type; extern const struct attribute_group *greybus_module_groups[]; -- cgit v1.2.3-59-g8ed1b From c69a50f2ce2eaec8ee9f50081a88589d1231ef3b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 6 Nov 2014 07:01:01 -0600 Subject: greybus: get rid of gbuf->actual_length Right now, the actual_length field of a gbuf is only ever assigned, never used. We now fill gbufs only with operation messages, and they encode within them the amount of space "actually used" in a buffer in a request-specific way. As a result, there's no need for the gbuf->actual_length field, so we can remove it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/operation.c | 6 ------ 2 files changed, 7 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 834cbaa6fade..175bd00381c7 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -130,7 +130,6 @@ struct gbuf { int status; void *transfer_buffer; u32 transfer_buffer_length; - u32 actual_length; bool outbound; /* AP-relative data direction */ diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 24707f67622c..720cc76f629c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -351,8 +351,6 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, goto err_cache; operation->request_payload = operation->request->transfer_buffer + sizeof(struct gb_operation_msg_hdr); - /* We always use the full request buffer */ - operation->request->actual_length = request_size; if (outgoing) { type |= GB_OPERATION_TYPE_RESPONSE; @@ -443,9 +441,6 @@ int gb_operation_request_send(struct gb_operation *operation, */ int gb_operation_response_send(struct gb_operation *operation) { - /* XXX - * Caller needs to have set operation->response->actual_length - */ gb_operation_remove(operation); gb_operation_destroy(operation); @@ -502,7 +497,6 @@ void gb_connection_operation_recv(struct gb_connection *connection, } memcpy(gbuf->transfer_buffer, data, msg_size); - gbuf->actual_length = msg_size; /* The rest will be handled in work queue context */ queue_work(gb_operation_recv_workqueue, &operation->recv_work); -- cgit v1.2.3-59-g8ed1b From ef45fa33a4c9b37440ef2e3af55843066c6149ce Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 6 Nov 2014 07:01:02 -0600 Subject: greybus: record gbuf->operation Currently a gbuf records a pointer to the connection it's associated with. We now know only use gbufs in operation messages, so we can point a gbuf at its operation instead. This still gives access to the connection where needed, but it also will provide all the context we'll ever need for a gbuf, and this allows us (in the next patch) to remove the gbuf->context field as well. So switch to recording in a gbuf the operation rather than the connection it is associated with. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 11 ++++++----- drivers/staging/greybus/gbuf.c | 14 ++++++++------ drivers/staging/greybus/greybus.h | 4 ++-- drivers/staging/greybus/operation.c | 3 +-- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 2fccece25641..645edbda11d0 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -96,6 +96,7 @@ static void cport_out_callback(struct urb *urb); static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) { + struct gb_connection *connection = gbuf->operation->connection; u32 cport_reserve = gbuf->outbound ? 1 : 0; u8 *buffer; @@ -121,16 +122,16 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, * we will encode the cport number in the first byte of the buffer, so * set the second byte to be the "transfer buffer" */ - if (gbuf->connection->interface_cport_id > (u16)U8_MAX) { + if (connection->interface_cport_id > (u16)U8_MAX) { pr_err("gbuf->interface_cport_id (%hd) is out of range!\n", - gbuf->connection->interface_cport_id); + connection->interface_cport_id); kfree(buffer); return -EINVAL; } /* Insert the cport id for outbound buffers */ if (gbuf->outbound) - *buffer++ = gbuf->connection->interface_cport_id; + *buffer++ = connection->interface_cport_id; gbuf->transfer_buffer = buffer; gbuf->transfer_buffer_length = size; @@ -208,7 +209,7 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { - struct greybus_host_device *hd = gbuf->connection->hd; + struct greybus_host_device *hd = gbuf->operation->connection->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; int retval; @@ -394,7 +395,7 @@ exit: static void cport_out_callback(struct urb *urb) { struct gbuf *gbuf = urb->context; - struct es1_ap_dev *es1 = hd_to_es1(gbuf->connection->hd); + struct es1_ap_dev *es1 = hd_to_es1(gbuf->operation->connection->hd); unsigned long flags; int i; diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 17141b6832cd..817e26249f29 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -35,13 +35,14 @@ static struct kmem_cache *gbuf_head_cache; * that the driver can then fill up with the data to be sent out. Curse * hardware designers for this issue... */ -struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, +struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, gbuf_complete_t complete, unsigned int size, bool outbound, gfp_t gfp_mask, void *context) { + struct greybus_host_device *hd = operation->connection->hd; struct gbuf *gbuf; int retval; @@ -50,14 +51,14 @@ struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, return NULL; kref_init(&gbuf->kref); - gbuf->connection = connection; + gbuf->operation = operation; gbuf->outbound = outbound; gbuf->complete = complete; gbuf->context = context; gbuf->status = -EBADR; /* Initial value--means "never set" */ /* Host controller specific allocation for the actual buffer */ - retval = connection->hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); + retval = hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); if (retval) { kmem_cache_free(gbuf_head_cache, gbuf); return NULL; @@ -72,8 +73,9 @@ static DEFINE_MUTEX(gbuf_mutex); static void free_gbuf(struct kref *kref) { struct gbuf *gbuf = container_of(kref, struct gbuf, kref); + struct greybus_host_device *hd = gbuf->operation->connection->hd; - gbuf->connection->hd->driver->free_gbuf_data(gbuf); + hd->driver->free_gbuf_data(gbuf); kmem_cache_free(gbuf_head_cache, gbuf); mutex_unlock(&gbuf_mutex); @@ -97,7 +99,7 @@ EXPORT_SYMBOL_GPL(greybus_get_gbuf); int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { - struct greybus_host_device *hd = gbuf->connection->hd; + struct greybus_host_device *hd = gbuf->operation->connection->hd; gbuf->status = -EINPROGRESS; @@ -106,7 +108,7 @@ int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) void greybus_kill_gbuf(struct gbuf *gbuf) { - struct greybus_host_device *hd = gbuf->connection->hd; + struct greybus_host_device *hd = gbuf->operation->connection->hd; if (gbuf->status != -EINPROGRESS) return; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 175bd00381c7..34e8584905ee 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -126,7 +126,7 @@ typedef void (*gbuf_complete_t)(struct gbuf *gbuf); struct gbuf { struct kref kref; - struct gb_connection *connection; + struct gb_operation *operation; int status; void *transfer_buffer; u32 transfer_buffer_length; @@ -194,7 +194,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); void greybus_gbuf_finished(struct gbuf *gbuf); -struct gbuf *greybus_alloc_gbuf(struct gb_connection *connection, +struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, gbuf_complete_t complete, unsigned int size, bool outbound, gfp_t gfp_mask, void *context); void greybus_free_gbuf(struct gbuf *gbuf); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 720cc76f629c..20ad4527418b 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -295,13 +295,12 @@ static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, u8 type, size_t size, bool data_out) { - struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; struct gbuf *gbuf; gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC; size += sizeof(*header); - gbuf = greybus_alloc_gbuf(connection, gb_operation_gbuf_complete, + gbuf = greybus_alloc_gbuf(operation, gb_operation_gbuf_complete, size, data_out, gfp_flags, operation); if (!gbuf) return NULL; -- cgit v1.2.3-59-g8ed1b From a77b06809b9214e23e603248d7609d6efa55f833 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 6 Nov 2014 07:01:03 -0600 Subject: greybus: remove gbuf->context A gbuf now records a pointer to its operation. The only thing ever stored in a gbuf context pointer is the gbuf's operation. Therefore there's no longer any need to maintain the context pointer, so get rid of it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 5 +---- drivers/staging/greybus/greybus.h | 3 +-- drivers/staging/greybus/operation.c | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 817e26249f29..af077ff4d257 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -28,7 +28,6 @@ static struct kmem_cache *gbuf_head_cache; * @complete: callback when the gbuf is finished with * @size: size of the buffer * @gfp_mask: allocation mask - * @context: context added to the gbuf by the driver * * TODO: someday it will be nice to handle DMA, but for now, due to the * architecture we are stuck with, the greybus core has to allocate the buffer @@ -39,8 +38,7 @@ struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, gbuf_complete_t complete, unsigned int size, bool outbound, - gfp_t gfp_mask, - void *context) + gfp_t gfp_mask) { struct greybus_host_device *hd = operation->connection->hd; struct gbuf *gbuf; @@ -54,7 +52,6 @@ struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, gbuf->operation = operation; gbuf->outbound = outbound; gbuf->complete = complete; - gbuf->context = context; gbuf->status = -EBADR; /* Initial value--means "never set" */ /* Host controller specific allocation for the actual buffer */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 34e8584905ee..a4af64f934c4 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -133,7 +133,6 @@ struct gbuf { bool outbound; /* AP-relative data direction */ - void *context; void *hcd_data; /* for the HCD to track the gbuf */ gbuf_complete_t complete; }; @@ -196,7 +195,7 @@ void greybus_gbuf_finished(struct gbuf *gbuf); struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, gbuf_complete_t complete, unsigned int size, - bool outbound, gfp_t gfp_mask, void *context); + bool outbound, gfp_t gfp_mask); void greybus_free_gbuf(struct gbuf *gbuf); struct gbuf *greybus_get_gbuf(struct gbuf *gbuf); #define greybus_put_gbuf greybus_free_gbuf diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 20ad4527418b..575e586ee8cf 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -256,7 +256,7 @@ static void operation_timeout(struct work_struct *work) static void gb_operation_gbuf_complete(struct gbuf *gbuf) { if (gbuf->status) { - struct gb_operation *operation = gbuf->context; + struct gb_operation *operation = gbuf->operation; struct gb_operation_msg_hdr *header; int id; int type; @@ -301,7 +301,7 @@ static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, size += sizeof(*header); gbuf = greybus_alloc_gbuf(operation, gb_operation_gbuf_complete, - size, data_out, gfp_flags, operation); + size, data_out, gfp_flags); if (!gbuf) return NULL; -- cgit v1.2.3-59-g8ed1b From eecf6deb578a250fc3336286a0759281569966ec Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 6 Nov 2014 07:01:04 -0600 Subject: greybus: get rid of greybus_gbuf_finished() All greybus_gbuf_finished() does is call the gbuf's complete method. Currently, greybus_gbuf_finished() is only ever called in one place, and that place can call the complete method directly instead. That allows us to eliminate greybus_gbuf_finished(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 7 ------- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/operation.c | 6 ++++-- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index af077ff4d257..1e5562e7e933 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -128,13 +128,6 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, } EXPORT_SYMBOL_GPL(greybus_cport_in); -/* Can be called in interrupt context, do the work and get out of here */ -void greybus_gbuf_finished(struct gbuf *gbuf) -{ - gbuf->complete(gbuf); -} -EXPORT_SYMBOL_GPL(greybus_gbuf_finished); - int gb_gbuf_init(void) { gbuf_head_cache = kmem_cache_create("gbuf_head_cache", diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index a4af64f934c4..6f2369ec1f7c 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -191,7 +191,6 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, void greybus_remove_hd(struct greybus_host_device *hd); void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); -void greybus_gbuf_finished(struct gbuf *gbuf); struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, gbuf_complete_t complete, unsigned int size, diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 575e586ee8cf..6bb7568e2f86 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -207,6 +207,7 @@ out: static void gb_operation_recv_work(struct work_struct *recv_work) { struct gb_operation *operation; + struct gbuf *gbuf; bool incoming_request; operation = container_of(recv_work, struct gb_operation, recv_work); @@ -217,9 +218,10 @@ static void gb_operation_recv_work(struct work_struct *recv_work) /* We're finished with the buffer we read into */ if (incoming_request) - greybus_gbuf_finished(operation->request); + gbuf = operation->request; else - greybus_gbuf_finished(operation->response); + gbuf = operation->response; + gbuf->complete(gbuf); } /* -- cgit v1.2.3-59-g8ed1b From 3a0e3c3efde8655958dc10673f374a8cc983cca2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 6 Nov 2014 07:01:05 -0600 Subject: greybus: move gb_operation_gbuf_complete() Simple move of a block of code, done as a separate commit to make it easier to see that's all that's going on. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 86 ++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 6bb7568e2f86..16ee7ce2cfaa 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -198,6 +198,49 @@ out: gb_operation_complete(operation); } +/* + * Buffer completion function. We get notified whenever any buffer + * completes. For outbound messages, this tells us that the message + * has been sent. For inbound messages, it means the data has + * landed in the buffer and is ready to be processed. + * + * Either way, we don't do anything. We don't really care when an + * outbound message has been sent, and for incoming messages we + * we'll be done with everything we need to do before we mark it + * finished. + * + * XXX We may want to record that a request is (or is no longer) in flight. + */ +static void gb_operation_gbuf_complete(struct gbuf *gbuf) +{ + if (gbuf->status) { + struct gb_operation *operation = gbuf->operation; + struct gb_operation_msg_hdr *header; + int id; + int type; + + if (gbuf == operation->request) + header = operation->request->transfer_buffer; + else if (gbuf == operation->response) + header = operation->response->transfer_buffer; + else + header = NULL; + + if (header) { + id = le16_to_cpu(header->id); + type = header->type; + } else { + id = -1; + type = -1; + } + + gb_connection_err(operation->connection, + "operation %d type %d gbuf error %d", + id, type, gbuf->status); + } + return; +} + /* * Either this operation contains an incoming request, or its * response has arrived. An incoming request will have a null @@ -242,49 +285,6 @@ static void operation_timeout(struct work_struct *work) gb_operation_complete(operation); } -/* - * Buffer completion function. We get notified whenever any buffer - * completes. For outbound messages, this tells us that the message - * has been sent. For inbound messages, it means the data has - * landed in the buffer and is ready to be processed. - * - * Either way, we don't do anything. We don't really care when an - * outbound message has been sent, and for incoming messages we - * we'll be done with everything we need to do before we mark it - * finished. - * - * XXX We may want to record that a request is (or is no longer) in flight. - */ -static void gb_operation_gbuf_complete(struct gbuf *gbuf) -{ - if (gbuf->status) { - struct gb_operation *operation = gbuf->operation; - struct gb_operation_msg_hdr *header; - int id; - int type; - - if (gbuf == operation->request) - header = operation->request->transfer_buffer; - else if (gbuf == operation->response) - header = operation->response->transfer_buffer; - else - header = NULL; - - if (header) { - id = le16_to_cpu(header->id); - type = header->type; - } else { - id = -1; - type = -1; - } - - gb_connection_err(operation->connection, - "operation %d type %d gbuf error %d", - id, type, gbuf->status); - } - return; -} - /* * Allocate a buffer to be used for an operation request or response * message. For outgoing messages, both types of message contain a -- cgit v1.2.3-59-g8ed1b From 5d2207e7047ebd110307f5f60c440f7119f999b8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 6 Nov 2014 07:01:06 -0600 Subject: greybus: kill gbuf->complete The gbuf complete method is a callback that allows the creator of a gbuf to know when all processing on a gbuf is done. We now only ever allocate gbufs for use in Greybus operations, and in that case we only ever supply gb_operation_gbuf_complete() as the completion callback. Furthermore, the only place gbuf->complete() is called is in gb_operation_recv_work(). Knowing this, we can just call gb_operation_gbuf_complete() directly from gb_operation_recv_work(), and get rid of the gbuf->complete() method entirely. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 2 -- drivers/staging/greybus/greybus.h | 10 ++-------- drivers/staging/greybus/operation.c | 5 ++--- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 1e5562e7e933..8b5a438e3b01 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -35,7 +35,6 @@ static struct kmem_cache *gbuf_head_cache; * hardware designers for this issue... */ struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, - gbuf_complete_t complete, unsigned int size, bool outbound, gfp_t gfp_mask) @@ -51,7 +50,6 @@ struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, kref_init(&gbuf->kref); gbuf->operation = operation; gbuf->outbound = outbound; - gbuf->complete = complete; gbuf->status = -EBADR; /* Initial value--means "never set" */ /* Host controller specific allocation for the actual buffer */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 6f2369ec1f7c..296a6a280d34 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -118,11 +118,6 @@ */ - -struct gbuf; - -typedef void (*gbuf_complete_t)(struct gbuf *gbuf); - struct gbuf { struct kref kref; @@ -134,7 +129,6 @@ struct gbuf { bool outbound; /* AP-relative data direction */ void *hcd_data; /* for the HCD to track the gbuf */ - gbuf_complete_t complete; }; @@ -193,8 +187,8 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, - gbuf_complete_t complete, unsigned int size, - bool outbound, gfp_t gfp_mask); + unsigned int size, bool outbound, + gfp_t gfp_mask); void greybus_free_gbuf(struct gbuf *gbuf); struct gbuf *greybus_get_gbuf(struct gbuf *gbuf); #define greybus_put_gbuf greybus_free_gbuf diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 16ee7ce2cfaa..302ab00d1250 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -264,7 +264,7 @@ static void gb_operation_recv_work(struct work_struct *recv_work) gbuf = operation->request; else gbuf = operation->response; - gbuf->complete(gbuf); + gb_operation_gbuf_complete(gbuf); } /* @@ -302,8 +302,7 @@ static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC; size += sizeof(*header); - gbuf = greybus_alloc_gbuf(operation, gb_operation_gbuf_complete, - size, data_out, gfp_flags); + gbuf = greybus_alloc_gbuf(operation, size, data_out, gfp_flags); if (!gbuf) return NULL; -- cgit v1.2.3-59-g8ed1b From 34c6507ca8e675ca0afa11ca0076d108027b0b2f Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 13 Nov 2014 09:14:13 -0500 Subject: greybus: add pwm protocol driver Add a PWM driver that implements the Greybus PWM protocol. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/protocol.c | 4 + drivers/staging/greybus/protocol.h | 3 + drivers/staging/greybus/pwm-gb.c | 531 +++++++++++++++++++++++++++++ 5 files changed, 540 insertions(+) create mode 100644 drivers/staging/greybus/pwm-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index bb90895deea6..7ec70fe6bd5d 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -11,6 +11,7 @@ greybus-y := core.o \ operation.o \ i2c-gb.o \ gpio-gb.o \ + pwm-gb.o \ sdio-gb.o \ uart-gb.o \ battery-gb.o diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 844ab8a745b0..a0af9a261aa8 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -32,6 +32,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_HID = 0x05, GREYBUS_PROTOCOL_SDIO = 0x06, GREYBUS_PROTOCOL_BATTERY = 0x08, + GREYBUS_PROTOCOL_PWM = 0x09, GREYBUS_PROTOCOL_LED = 0x0e, /* ... */ GREYBUS_PROTOCOL_VENDOR = 0xff, diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 93e0af3c6b4e..8df2b4e7b802 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -177,6 +177,10 @@ bool gb_protocol_init(void) pr_err("error initializing i2c protocol\n"); ret = false; } + if (!gb_pwm_protocol_init()) { + pr_err("error initializing pwm protocol\n"); + ret = false; + } if (!gb_uart_protocol_init()) { pr_err("error initializing uart protocol\n"); ret = false; diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index a236401b48ab..f57f0db4f819 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -55,6 +55,9 @@ extern void gb_gpio_protocol_exit(void); extern bool gb_i2c_protocol_init(void); extern void gb_i2c_protocol_exit(void); +extern bool gb_pwm_protocol_init(void); +extern void gb_pwm_protocol_exit(void); + extern bool gb_uart_protocol_init(void); extern void gb_uart_protocol_exit(void); diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c new file mode 100644 index 000000000000..44a4f64acd8e --- /dev/null +++ b/drivers/staging/greybus/pwm-gb.c @@ -0,0 +1,531 @@ +/* + * PWM Greybus driver. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_pwm_chip { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + u8 pwm_max; /* max pwm number */ + + struct pwm_chip chip; + struct pwm_chip *pwm; +}; +#define pwm_chip_to_gb_pwm_chip(chip) \ + container_of(chip, struct gb_pwm_chip, chip) + +/* Version of the Greybus PWM protocol we support */ +#define GB_PWM_VERSION_MAJOR 0x00 +#define GB_PWM_VERSION_MINOR 0x01 + +/* Greybus PWM request types */ +#define GB_PWM_TYPE_INVALID 0x00 +#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 +#define GB_PWM_TYPE_PWM_COUNT 0x02 +#define GB_PWM_TYPE_ACTIVATE 0x03 +#define GB_PWM_TYPE_DEACTIVATE 0x04 +#define GB_PWM_TYPE_CONFIG 0x05 +#define GB_PWM_TYPE_POLARITY 0x06 +#define GB_PWM_TYPE_ENABLE 0x07 +#define GB_PWM_TYPE_DISABLE 0x08 +#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +struct gb_pwm_simple_response { + __u8 status; +}; + +/* version request has no payload */ +struct gb_pwm_proto_version_response { + __u8 status; + __u8 major; + __u8 minor; +}; + +/* pwm count request has no payload */ +struct gb_pwm_count_response { + __u8 status; + __u8 count; +}; + +struct gb_pwm_activate_request { + __u8 which; +}; + +struct gb_pwm_deactivate_request { + __u8 which; +}; + +struct gb_pwm_config_request { + __u8 which; + __u32 duty; + __u32 period; +}; + +struct gb_pwm_polarity_request { + __u8 which; + __u8 polarity; +}; + +struct gb_pwm_enable_request { + __u8 which; +}; + +struct gb_pwm_disable_request { + __u8 which; +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) +{ + struct gb_connection *connection = pwmc->connection; + struct gb_operation *operation; + struct gb_pwm_proto_version_response *response; + int ret; + + /* protocol version request has no payload */ + operation = gb_operation_create(connection, + GB_PWM_TYPE_PROTOCOL_VERSION, + 0, sizeof(*response)); + if (!operation) + return -ENOMEM; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("version operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "version response %hhu", + response->status); + ret = -EIO; + } else { + if (response->major > GB_PWM_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response->major, GB_PWM_VERSION_MAJOR); + ret = -ENOTSUPP; + goto out; + } + pwmc->version_major = response->major; + pwmc->version_minor = response->minor; + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) +{ + struct gb_connection *connection = pwmc->connection; + struct gb_operation *operation; + struct gb_pwm_count_response *response; + int ret; + + /* pwm count request has no payload */ + operation = gb_operation_create(connection, GB_PWM_TYPE_PWM_COUNT, + 0, sizeof(*response)); + if (!operation) + return -ENOMEM; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("line count operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "pwm count response %hhu", + response->status); + ret = -EIO; + } else + pwmc->pwm_max = response->count; +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_connection *connection = pwmc->connection; + struct gb_operation *operation; + struct gb_pwm_activate_request *request; + struct gb_pwm_simple_response *response; + int ret; + + if (which > pwmc->pwm_max) + return -EINVAL; + + /* activate response has no payload */ + operation = gb_operation_create(connection, GB_PWM_TYPE_ACTIVATE, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("activate operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "activate response %hhu", + response->status); + ret = -EIO; + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_connection *connection = pwmc->connection; + struct gb_operation *operation; + struct gb_pwm_deactivate_request *request; + struct gb_pwm_simple_response *response; + int ret; + + if (which > pwmc->pwm_max) + return -EINVAL; + + /* deactivate response has no payload */ + operation = gb_operation_create(connection, GB_PWM_TYPE_DEACTIVATE, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("deactivate operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "deactivate response %hhu", + response->status); + ret = -EIO; + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, + u8 which, u32 duty, u32 period) +{ + struct gb_connection *connection = pwmc->connection; + struct gb_operation *operation; + struct gb_pwm_config_request *request; + struct gb_pwm_simple_response *response; + int ret; + + if (which > pwmc->pwm_max) + return -EINVAL; + + operation = gb_operation_create(connection, GB_PWM_TYPE_CONFIG, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + request->duty = duty; + request->period = period; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("config operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "config response %hhu", + response->status); + ret = -EIO; + } +out: + gb_operation_destroy(operation); + + return ret; +} + + +static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, + u8 which, u8 polarity) +{ + struct gb_connection *connection = pwmc->connection; + struct gb_operation *operation; + struct gb_pwm_polarity_request *request; + struct gb_pwm_simple_response *response; + int ret; + + if (which > pwmc->pwm_max) + return -EINVAL; + + operation = gb_operation_create(connection, GB_PWM_TYPE_POLARITY, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + request->polarity = polarity; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("set polarity operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "set polarity response %hhu", + response->status); + ret = -EIO; + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_connection *connection = pwmc->connection; + struct gb_operation *operation; + struct gb_pwm_enable_request *request; + struct gb_pwm_simple_response *response; + int ret; + + if (which > pwmc->pwm_max) + return -EINVAL; + + /* enable response has no payload */ + operation = gb_operation_create(connection, GB_PWM_TYPE_ENABLE, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("enable operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "enable response %hhu", + response->status); + ret = -EIO; + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_connection *connection = pwmc->connection; + struct gb_operation *operation; + struct gb_pwm_disable_request *request; + struct gb_pwm_simple_response *response; + int ret; + + if (which > pwmc->pwm_max) + return -EINVAL; + + /* disable response has no payload */ + operation = gb_operation_create(connection, GB_PWM_TYPE_DISABLE, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->which = which; + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("disable operation failed (%d)\n", ret); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "disable response %hhu", + response->status); + ret = -EIO; + } +out: + gb_operation_destroy(operation); + + return ret; +} + +static int gb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_activate_operation(pwmc, pwm->hwpwm); +}; + +static void gb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + if (test_bit(PWMF_ENABLED, &pwm->flags)) + dev_warn(chip->dev, "freeing PWM device without disabling\n"); + + gb_pwm_deactivate_operation(pwmc, pwm->hwpwm); +} + +static int gb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_config_operation(pwmc, pwm->hwpwm, duty_ns, period_ns); +}; + +static int gb_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_set_polarity_operation(pwmc, pwm->hwpwm, polarity); +}; + +static int gb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_enable_operation(pwmc, pwm->hwpwm); +}; + +static void gb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + gb_pwm_disable_operation(pwmc, pwm->hwpwm); +}; + +static const struct pwm_ops gb_pwm_ops = { + .request = gb_pwm_request, + .free = gb_pwm_free, + .config = gb_pwm_config, + .set_polarity = gb_pwm_set_polarity, + .enable = gb_pwm_enable, + .disable = gb_pwm_disable, + .owner = THIS_MODULE, +}; + +static int gb_pwm_connection_init(struct gb_connection *connection) +{ + struct gb_pwm_chip *pwmc; + struct pwm_chip *pwm; + int ret; + + pwmc = kzalloc(sizeof(*pwmc), GFP_KERNEL); + if (!pwmc) + return -ENOMEM; + pwmc->connection = connection; + + /* Check for compatible protocol version */ + ret = gb_pwm_proto_version_operation(pwmc); + if (ret) + goto out_err; + + /* Query number of pwms present */ + ret = gb_pwm_count_operation(pwmc); + if (ret) + goto out_err; + + pwm = &pwmc->chip; + + pwm->dev = &connection->dev; + pwm->ops = &gb_pwm_ops; + pwm->base = -1; /* Allocate base dynamically */ + pwm->npwm = pwmc->pwm_max + 1; + pwm->can_sleep = true; /* FIXME */ + + ret = pwmchip_add(pwm); + if (ret) { + pr_err("Failed to register PWM\n"); + return ret; + } + connection->private = pwmc; + + return 0; +out_err: + kfree(pwmc); + return ret; +} + +static void gb_pwm_connection_exit(struct gb_connection *connection) +{ + struct gb_pwm_chip *pwmc = connection->private; + + if (!pwmc) + return; + + pwmchip_remove(&pwmc->chip); + /* kref_put(pwmc->connection) */ + kfree(pwmc); +} + +static struct gb_protocol pwm_protocol = { + .id = GREYBUS_PROTOCOL_PWM, + .major = 0, + .minor = 1, + .connection_init = gb_pwm_connection_init, + .connection_exit = gb_pwm_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +bool gb_pwm_protocol_init(void) +{ + return gb_protocol_register(&pwm_protocol); +} + +void gb_pwm_protocol_exit(void) +{ + gb_protocol_deregister(&pwm_protocol); +} -- cgit v1.2.3-59-g8ed1b From 3e9cb4a11831e8c0bf91cd880845b6b35051a484 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 12 Nov 2014 15:17:50 -0600 Subject: greybus: don't assume PAGE_SIZE for URB size The buffers allocated for CPort URBS are ES1_GBUF_MSG_SIZE bytes. But usb_fill_bulk_urb() passes PAGE_SIZE as its size. They happen to be the same, but the code is wrong, so fix it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 645edbda11d0..f82f665261b2 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -546,7 +546,8 @@ static int ap_probe(struct usb_interface *interface, usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, es1->cport_in_endpoint), - buffer, PAGE_SIZE, cport_in_callback, es1); + buffer, ES1_GBUF_MSG_SIZE, cport_in_callback, + es1); es1->cport_in_urb[i] = urb; es1->cport_in_buffer[i] = buffer; retval = usb_submit_urb(urb, GFP_KERNEL); -- cgit v1.2.3-59-g8ed1b From 10520528fee29f29ec3d4d3f651294247b07c0a9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 12 Nov 2014 15:17:51 -0600 Subject: greybus: fix request timeout bug This commit changed the timeout behavior for operations: 6a8732e operation: make the timeout a per-operation thing... It unfortunately left in place some code that was only appropriate for per-connection timeouts. In particular, the timer for an operation is currently getting started only if no existing operations are in flight. Fix that oversight, and schedule an operation's timer unconditionally. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 302ab00d1250..beac3536ca68 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -65,31 +65,16 @@ static void gb_operation_insert(struct gb_operation *operation) struct rb_node *above = NULL; struct gb_operation_msg_hdr *header; unsigned long timeout; - bool start_timer; - __le16 wire_id; - /* - * Assign the operation's id, and store it in the header of - * both request and response message headers. + /* Assign the operation's id, and store it in the header of + * the request message header. */ operation->id = gb_connection_operation_id(connection); - wire_id = cpu_to_le16(operation->id); header = operation->request->transfer_buffer; - header->id = wire_id; + header->id = cpu_to_le16(operation->id); - /* OK, insert the operation into its connection's tree */ + /* OK, insert the operation into its connection's pending tree */ spin_lock_irq(&gb_operations_lock); - - /* - * We impose a time limit for requests to complete. If - * there are no requests pending there is no need for a - * timer. So if this will be the only one in flight we'll - * need to start the timer. Otherwise we just update the - * existing one to give this request a full timeout period - * to complete. - */ - start_timer = RB_EMPTY_ROOT(root); - while (*link) { struct gb_operation *other; @@ -105,11 +90,9 @@ static void gb_operation_insert(struct gb_operation *operation) rb_insert_color(node, root); spin_unlock_irq(&gb_operations_lock); + /* We impose a time limit for requests to complete. */ timeout = msecs_to_jiffies(OPERATION_TIMEOUT_DEFAULT); - if (start_timer) - schedule_delayed_work(&operation->timeout_work, timeout); - else - mod_delayed_work(system_wq, &operation->timeout_work, timeout); + schedule_delayed_work(&operation->timeout_work, timeout); } static void gb_operation_remove(struct gb_operation *operation) -- cgit v1.2.3-59-g8ed1b From 8350e7a01110cbee813256a8ebcda99bf11b8ec3 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 12 Nov 2014 15:17:52 -0600 Subject: greybus: move timeout out of gb_operation_insert() Currently, gb_operation_insert() arranges to time out a request if it takes too long. Move this out of that function and into gb_operation_request_send(), so we know it's getting set up after the request has actually be sent. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index beac3536ca68..5f3e52d8d485 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -64,7 +64,6 @@ static void gb_operation_insert(struct gb_operation *operation) struct rb_node **link = &root->rb_node; struct rb_node *above = NULL; struct gb_operation_msg_hdr *header; - unsigned long timeout; /* Assign the operation's id, and store it in the header of * the request message header. @@ -89,10 +88,6 @@ static void gb_operation_insert(struct gb_operation *operation) rb_link_node(node, above, link); rb_insert_color(node, root); spin_unlock_irq(&gb_operations_lock); - - /* We impose a time limit for requests to complete. */ - timeout = msecs_to_jiffies(OPERATION_TIMEOUT_DEFAULT); - schedule_delayed_work(&operation->timeout_work, timeout); } static void gb_operation_remove(struct gb_operation *operation) @@ -397,6 +392,7 @@ void gb_operation_destroy(struct gb_operation *operation) int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback) { + unsigned long timeout; int ret; if (operation->connection->state != GB_CONNECTION_STATE_ENABLED) @@ -413,6 +409,10 @@ int gb_operation_request_send(struct gb_operation *operation, ret = greybus_submit_gbuf(operation->request, GFP_KERNEL); if (ret) return ret; + + /* We impose a time limit for requests to complete. */ + timeout = msecs_to_jiffies(OPERATION_TIMEOUT_DEFAULT); + schedule_delayed_work(&operation->timeout_work, timeout); if (!callback) ret = gb_operation_wait(operation); -- cgit v1.2.3-59-g8ed1b From b8616da875ea2f36bdeaff49f1373ea1e37406f4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 12 Nov 2014 15:17:53 -0600 Subject: greybus: simplify pending operations tracking Greg raised the alarm when I first put in the red-black tree for tracking pending operations. The reality as that we're not likely to have that many operations in flight at any one time, so the complexity of the red-black tree is most likely unwarranted. I already This pulls out the red-black tree and uses a simple list instead. A connection maintains two lists of operations. An operation starts on its connection's operations list. It is moved to the pending list when its request message is sent. And it is moved back to the operations list when the response message arrives. It is removed from whatever list it's in when the operation is destroyed. We reuse the single operation->links field for both lists. Only outgoing requests are ever "pending." Incoming requests are transient--we receive them, process them, send the response, and then we're done. Change a few function names so it's clear we're working with the pending list. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/connection.h | 2 +- drivers/staging/greybus/operation.c | 52 ++++++++++-------------------------- drivers/staging/greybus/operation.h | 3 +-- 4 files changed, 17 insertions(+), 42 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 586457f027de..377ac7d3afea 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -209,7 +209,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, spin_unlock_irq(&gb_connections_lock); INIT_LIST_HEAD(&connection->operations); - connection->pending = RB_ROOT; + INIT_LIST_HEAD(&connection->pending); atomic_set(&connection->op_cycle, 0); return connection; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 893c02af3710..861e0661157c 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -36,7 +36,7 @@ struct gb_connection { enum gb_connection_state state; struct list_head operations; - struct rb_root pending; /* awaiting reponse */ + struct list_head pending; /* awaiting reponse */ atomic_t op_cycle; void *private; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5f3e52d8d485..cc4921081345 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -56,13 +56,9 @@ struct gb_operation_msg_hdr { /* XXX Could be per-host device, per-module, or even per-connection */ static DEFINE_SPINLOCK(gb_operations_lock); -static void gb_operation_insert(struct gb_operation *operation) +static void gb_pending_operation_insert(struct gb_operation *operation) { struct gb_connection *connection = operation->connection; - struct rb_root *root = &connection->pending; - struct rb_node *node = &operation->node; - struct rb_node **link = &root->rb_node; - struct rb_node *above = NULL; struct gb_operation_msg_hdr *header; /* Assign the operation's id, and store it in the header of @@ -72,25 +68,13 @@ static void gb_operation_insert(struct gb_operation *operation) header = operation->request->transfer_buffer; header->id = cpu_to_le16(operation->id); - /* OK, insert the operation into its connection's pending tree */ + /* Insert the operation into its connection's pending list */ spin_lock_irq(&gb_operations_lock); - while (*link) { - struct gb_operation *other; - - above = *link; - other = rb_entry(above, struct gb_operation, node); - header = other->request->transfer_buffer; - if (other->id > operation->id) - link = &above->rb_left; - else if (other->id < operation->id) - link = &above->rb_right; - } - rb_link_node(node, above, link); - rb_insert_color(node, root); + list_move_tail(&operation->links, &connection->pending); spin_unlock_irq(&gb_operations_lock); } -static void gb_operation_remove(struct gb_operation *operation) +static void gb_pending_operation_remove(struct gb_operation *operation) { struct gb_connection *connection = operation->connection; @@ -99,29 +83,22 @@ static void gb_operation_remove(struct gb_operation *operation) /* Take us off of the list of pending operations */ spin_lock_irq(&gb_operations_lock); - rb_erase(&operation->node, &connection->pending); + list_move_tail(&operation->links, &connection->operations); spin_unlock_irq(&gb_operations_lock); - } static struct gb_operation * -gb_operation_find(struct gb_connection *connection, u16 id) +gb_pending_operation_find(struct gb_connection *connection, u16 id) { - struct gb_operation *operation = NULL; - struct rb_node *node; + struct gb_operation *operation; bool found = false; spin_lock_irq(&gb_operations_lock); - node = connection->pending.rb_node; - while (node && !found) { - operation = rb_entry(node, struct gb_operation, node); - if (operation->id > id) - node = node->rb_left; - else if (operation->id < id) - node = node->rb_right; - else + list_for_each_entry(operation, &connection->pending, links) + if (operation->id == id) { found = true; - } + break; + } spin_unlock_irq(&gb_operations_lock); return found ? operation : NULL; @@ -405,7 +382,7 @@ int gb_operation_request_send(struct gb_operation *operation, * setting the operation id and submitting the gbuf. */ operation->callback = callback; - gb_operation_insert(operation); + gb_pending_operation_insert(operation); ret = greybus_submit_gbuf(operation->request, GFP_KERNEL); if (ret) return ret; @@ -424,7 +401,6 @@ int gb_operation_request_send(struct gb_operation *operation, */ int gb_operation_response_send(struct gb_operation *operation) { - gb_operation_remove(operation); gb_operation_destroy(operation); return 0; @@ -456,12 +432,12 @@ void gb_connection_operation_recv(struct gb_connection *connection, if (header->type & GB_OPERATION_TYPE_RESPONSE) { u16 id = le16_to_cpu(header->id); - operation = gb_operation_find(connection, id); + operation = gb_pending_operation_find(connection, id); if (!operation) { gb_connection_err(connection, "operation not found"); return; } - gb_operation_remove(operation); + gb_pending_operation_remove(operation); gbuf = operation->response; gbuf->status = GB_OP_SUCCESS; /* If we got here we're good */ if (size > gbuf->transfer_buffer_length) { diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 965ad9c1aa8c..4913f720a7cd 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -64,8 +64,7 @@ struct gb_operation { struct completion completion; /* Used if no callback */ struct delayed_work timeout_work; - struct list_head links; /* connection->operations */ - struct rb_node node; /* connection->pending */ + struct list_head links; /* connection->{operations,pending} */ /* These are what's used by caller */ void *request_payload; -- cgit v1.2.3-59-g8ed1b From 360a8779d96c7c592ee79d035afe9bdf05695204 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 12 Nov 2014 15:17:54 -0600 Subject: greybus: op_cycle doesn't need to be atomic We can update a connection's operation id counter under spinlock, and thereby avoid the need to maintain it in an atomic variable. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ------ drivers/staging/greybus/connection.h | 4 +--- drivers/staging/greybus/operation.c | 15 ++++++++------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 377ac7d3afea..bb26157615a4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -210,7 +210,6 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, INIT_LIST_HEAD(&connection->operations); INIT_LIST_HEAD(&connection->pending); - atomic_set(&connection->op_cycle, 0); return connection; } @@ -244,11 +243,6 @@ void gb_connection_destroy(struct gb_connection *connection) device_del(&connection->dev); } -u16 gb_connection_operation_id(struct gb_connection *connection) -{ - return (u16)(atomic_inc_return(&connection->op_cycle) & (int)U16_MAX); -} - void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) { struct va_format vaf; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 861e0661157c..3aa86955b748 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -35,9 +35,9 @@ struct gb_connection { enum gb_connection_state state; + u16 op_cycle; struct list_head operations; struct list_head pending; /* awaiting reponse */ - atomic_t op_cycle; void *private; }; @@ -53,8 +53,6 @@ void gb_connection_exit(struct gb_connection *connection); struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, u16 cport_id); -u16 gb_connection_operation_id(struct gb_connection *connection); - __printf(2, 3) void gb_connection_err(struct gb_connection *connection, const char *fmt, ...); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index cc4921081345..72e5ef9ecad8 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -61,17 +61,18 @@ static void gb_pending_operation_insert(struct gb_operation *operation) struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; - /* Assign the operation's id, and store it in the header of - * the request message header. + /* + * Assign the operation's id and move it into its + * connection's pending list. */ - operation->id = gb_connection_operation_id(connection); - header = operation->request->transfer_buffer; - header->id = cpu_to_le16(operation->id); - - /* Insert the operation into its connection's pending list */ spin_lock_irq(&gb_operations_lock); + operation->id = ++connection->op_cycle; list_move_tail(&operation->links, &connection->pending); spin_unlock_irq(&gb_operations_lock); + + /* Store the operation id in the request header */ + header = operation->request->transfer_buffer; + header->id = cpu_to_le16(operation->id); } static void gb_pending_operation_remove(struct gb_operation *operation) -- cgit v1.2.3-59-g8ed1b From c3cf278533c6e5f3df385afce8eb11889e58cfe3 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 12 Nov 2014 15:17:55 -0600 Subject: greybus: pass operation type on request receive When an incoming request is received, the operation type is encoded in the header and is not available in the payload. Add the operation type as a parameter to the request_recv method so the request handler knows what to do. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 +++-- drivers/staging/greybus/protocol.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 72e5ef9ecad8..f4554528e7ab 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -137,16 +137,17 @@ static void gb_operation_request_handle(struct gb_operation *operation) struct gb_protocol *protocol = operation->connection->protocol; struct gb_operation_msg_hdr *header; + header = operation->request->transfer_buffer; + /* * If the protocol has no incoming request handler, report * an error and mark the request bad. */ if (protocol->request_recv) { - protocol->request_recv(operation); + protocol->request_recv(header->type, operation); goto out; } - header = operation->request->transfer_buffer; gb_connection_err(operation->connection, "unexpected incoming request type 0x%02hhx\n", header->type); operation->result = GB_OP_PROTOCOL_BAD; diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index f57f0db4f819..1aeb34068a3f 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -15,7 +15,7 @@ struct gb_operation; typedef int (*gb_connection_init_t)(struct gb_connection *); typedef void (*gb_connection_exit_t)(struct gb_connection *); -typedef void (*gb_request_recv_t)(struct gb_operation *); +typedef void (*gb_request_recv_t)(u8, struct gb_operation *); /* * Protocols having the same id but different major and/or minor -- cgit v1.2.3-59-g8ed1b From 437caa8a0c2570b4d33c873b14f348ce39b657da Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 12 Nov 2014 15:59:32 -0500 Subject: greybus: remove unused gb_connection_handler externs Remove some leftover cruft from recent refactoring of connection handlers. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 296a6a280d34..d64c18146ee1 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -252,12 +252,6 @@ void gb_gbuf_exit(void); extern struct bus_type greybus_bus_type; extern const struct attribute_group *greybus_module_groups[]; -extern struct gb_connection_handler gb_i2c_connection_handler; -extern struct gb_connection_handler gb_gpio_connection_handler; -extern struct gb_connection_handler gb_battery_connection_handler; -extern struct gb_connection_handler gb_uart_connection_handler; -extern struct gb_connection_handler gb_sdio_connection_handler; - int gb_uart_device_init(struct gb_connection *connection); void gb_uart_device_exit(struct gb_connection *connection); -- cgit v1.2.3-59-g8ed1b From e2f345ab40762ffa4456ad08956c2a74bc3a0649 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:29 +0530 Subject: greybus: gitignore: few more additions Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/.gitignore b/drivers/staging/greybus/.gitignore index f7dc3ba2a771..83957733724c 100644 --- a/drivers/staging/greybus/.gitignore +++ b/drivers/staging/greybus/.gitignore @@ -1,9 +1,11 @@ +.* *.cmd *.ko *.mod.c modules.order Module.symvers *.o +*.o.* *.swp .tmp_versions tags -- cgit v1.2.3-59-g8ed1b From 43d9431f6bc9dd7cf0f3b9e8a3cd94bb61745da8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:30 +0530 Subject: greybus: manifest: no need to initialize 'result' Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 563463955cb0..ebc24940d672 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -328,7 +328,7 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) struct manifest_desc *module_desc = NULL; u16 manifest_size; u32 found = 0; - bool result = false; + bool result; /* we have to have at _least_ the manifest header */ if (size <= sizeof(manifest->header)) { -- cgit v1.2.3-59-g8ed1b From 4ed16a81c179d8e72bd127067e344959d1d6b1be Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:31 +0530 Subject: greybus: manifest: initialize variable during definition Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index ebc24940d672..964f85ed603c 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -183,10 +183,9 @@ static u32 gb_manifest_parse_cports(struct gb_interface *interface) struct greybus_descriptor_cport *desc_cport; u8 protocol_id; u16 cport_id; - bool found; + bool found = false; /* Find a cport descriptor */ - found = false; list_for_each_entry(descriptor, &manifest_descs, links) { if (descriptor->type == GREYBUS_TYPE_CPORT) { desc_cport = descriptor->data; -- cgit v1.2.3-59-g8ed1b From 50fc08f8c023d1e698207342e003f1b6d4bca1f5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:32 +0530 Subject: greybus: manifest: don't free unallocated resources Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 964f85ed603c..777abf400325 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -268,7 +268,7 @@ static bool gb_manifest_parse_module(struct gb_module *gmod, gmod->product_string = gb_string_get(desc_module->product_stringid); if (IS_ERR(gmod->product_string)) { - goto out_err; + goto out_free_vendor_string; } gmod->vendor = le16_to_cpu(desc_module->vendor); @@ -289,6 +289,7 @@ static bool gb_manifest_parse_module(struct gb_module *gmod, out_err: kfree(gmod->product_string); gmod->product_string = NULL; +out_free_vendor_string: kfree(gmod->vendor_string); gmod->vendor_string = NULL; -- cgit v1.2.3-59-g8ed1b From 35a52cafbc214844f5940fe756bc2af01d987626 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:33 +0530 Subject: greybus: Fix missing gb_protocol_exit() on gb_exit() Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 1d05c35a1b60..7428206a32a0 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -302,6 +302,7 @@ error_bus: static void __exit gb_exit(void) { + gb_protocol_exit(); gb_operation_exit(); gb_gbuf_exit(); gb_ap_exit(); -- cgit v1.2.3-59-g8ed1b From 669f5faf84998227b2604d310a7f33ac90189b8c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:34 +0530 Subject: greybus: don't set ->dev.driver to NULL when it is already NULL Parent objects of 'dev' are allocated with kzalloc() and so all of their fields are initialized with 0. Hence no need of marking them NULL again. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 1 - drivers/staging/greybus/interface.c | 1 - drivers/staging/greybus/module.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index bb26157615a4..5373f44b08c1 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -185,7 +185,6 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, connection->state = GB_CONNECTION_STATE_DISABLED; connection->dev.parent = &interface->dev; - connection->dev.driver = NULL; connection->dev.bus = &greybus_bus_type; connection->dev.type = &greybus_connection_type; connection->dev.groups = connection_groups; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index eb63f638fc2c..f7f9bfda4f9b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -69,7 +69,6 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) /* Build up the interface device structures and register it with the * driver core */ interface->dev.parent = &gmod->dev; - interface->dev.driver = NULL; interface->dev.bus = &greybus_bus_type; interface->dev.type = &greybus_interface_type; interface->dev.groups = interface_groups; diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 9cf98cd8d53d..f5d7dc84c1d1 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -90,7 +90,6 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) spin_unlock_irq(&gb_modules_lock); gmod->dev.parent = hd->parent; - gmod->dev.driver = NULL; gmod->dev.bus = &greybus_bus_type; gmod->dev.type = &greybus_module_type; gmod->dev.groups = greybus_module_groups; -- cgit v1.2.3-59-g8ed1b From 37d8afc42b30e8883a30eb2dadb1ff4f94caa33b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:35 +0530 Subject: greybus: debug: mark debug messages with pr_debug() instead of printk Also fix indentation. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 42 +++++++++++++++++++++---------------- drivers/staging/greybus/operation.c | 2 +- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 40833fa69efe..473df4d47ac1 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -167,8 +167,10 @@ static int gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_co } gb_gpio_controller->version_major = response->major; gb_gpio_controller->version_minor = response->minor; -printk("%s: version_major = %u version_minor = %u\n", __func__, - gb_gpio_controller->version_major, gb_gpio_controller->version_minor); + + pr_debug("%s: version_major = %u version_minor = %u\n", __func__, + gb_gpio_controller->version_major, + gb_gpio_controller->version_minor); } out: gb_operation_destroy(operation); @@ -204,8 +206,9 @@ static int gb_gpio_line_count_operation(struct gb_gpio_controller *gb_gpio_contr ret = -EIO; } else { gb_gpio_controller->line_max = response->count; -printk("%s: count = %u\n", __func__, - gb_gpio_controller->line_max + 1); + + pr_debug("%s: count = %u\n", __func__, + gb_gpio_controller->line_max + 1); } out: gb_operation_destroy(operation); @@ -248,7 +251,8 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control ret = -EIO; } else { gb_gpio_controller->lines[which].active = true; -printk("%s: %u is now active\n", __func__, which); + + pr_debug("%s: %u is now active\n", __func__, which); } out: gb_operation_destroy(operation); @@ -291,7 +295,7 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr ret = -EIO; } else { gb_gpio_controller->lines[which].active = false; -printk("%s: %u is now inactive\n", __func__, which); + pr_debug("%s: %u is now inactive\n", __func__, which); } out: gb_operation_destroy(operation); @@ -338,8 +342,8 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co pr_warn("gpio %u direction was %u (should be 0 or 1)\n", which, direction); gb_gpio_controller->lines[which].direction = direction ? 1 : 0; -printk("%s: direction of %u is %s\n", __func__, which, - direction ? "in" : "out"); + pr_debug("%s: direction of %u is %s\n", __func__, which, + direction ? "in" : "out"); } out: gb_operation_destroy(operation); @@ -382,7 +386,7 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con ret = -EIO; } else { gb_gpio_controller->lines[which].direction = 1; -printk("%s: direction of %u is now in\n", __func__, which); + pr_debug("%s: direction of %u is now in\n", __func__, which); } out: gb_operation_destroy(operation); @@ -426,8 +430,8 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co ret = -EIO; } else { gb_gpio_controller->lines[which].direction = 0; -printk("%s: direction of %u is now out, value %s\n", __func__, - which, value_high ? "high" : "low"); + pr_debug("%s: direction of %u is now out, value %s\n", __func__, + which, value_high ? "high" : "low"); } out: gb_operation_destroy(operation); @@ -475,8 +479,9 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro which, value); gb_gpio_controller->lines[which].value = value ? 1 : 0; /* XXX should this set direction to out? */ -printk("%s: value of %u is %s\n", __func__, - which, gb_gpio_controller->lines[which].value ? "high" : "low"); + pr_debug("%s: value of %u is %s\n", __func__, which, + gb_gpio_controller->lines[which].value ? "high" : + "low"); } out: gb_operation_destroy(operation); @@ -521,8 +526,9 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro } else { /* XXX should this set direction to out? */ gb_gpio_controller->lines[which].value = request->value; -printk("%s: out value of %u is now %s\n", __func__, - which, gb_gpio_controller->lines[which].value ? "high" : "low"); + pr_debug("%s: out value of %u is now %s\n", __func__, which, + gb_gpio_controller->lines[which].value ? "high" : + "low"); } out: gb_operation_destroy(operation); @@ -566,8 +572,8 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con ret = -EIO; } else { gb_gpio_controller->lines[which].debounce_usec = le16_to_cpu(request->usec); - printk("%s: debounce of %u is now %hu usec\n", __func__, which, - gb_gpio_controller->lines[which].debounce_usec); + pr_debug("%s: debounce of %u is now %hu usec\n", __func__, which, + gb_gpio_controller->lines[which].debounce_usec); } out: gb_operation_destroy(operation); @@ -582,7 +588,7 @@ static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) if (offset < 0 || offset >= chip->ngpio) return -EINVAL; - printk("passed check\n"); + pr_debug("%s: passed check\n", __func__); ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); if (ret) ; /* return ret; */ diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f4554528e7ab..7fcd8e4285ea 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -236,7 +236,7 @@ static void operation_timeout(struct work_struct *work) struct gb_operation *operation; operation = container_of(work, struct gb_operation, timeout_work.work); - printk("timeout!\n"); + pr_debug("%s: timeout!\n", __func__); operation->result = GB_OP_TIMEOUT; gb_operation_complete(operation); -- cgit v1.2.3-59-g8ed1b From 132d03ea8989949ed40d52ea6bd4c5aaa083381b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:36 +0530 Subject: greybus: sysfs: generalize gb_module_attr() to capture more cases Most of the attribute routines are created with gb_module_attr() and few are left out because they weren't printing 32 bit hexadecimal values. Extend gb_module_attr() to cover more cases. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sysfs.c | 45 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 42d54eb07377..44b0c707a5c4 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -20,55 +20,28 @@ #include "kernel_ver.h" /* Module fields */ -#define gb_module_attr(field) \ +#define gb_module_attr(field, type) \ static ssize_t module_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_module *gmod = to_gb_module(dev); \ - return sprintf(buf, "%x\n", gmod->field); \ + return sprintf(buf, "%"#type"\n", gmod->field); \ } \ static DEVICE_ATTR_RO(module_##field) -gb_module_attr(vendor); -gb_module_attr(product); -gb_module_attr(version); - -static ssize_t module_serial_number_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct gb_module *gmod = to_gb_module(dev); - - return sprintf(buf, "%llX\n", (unsigned long long)gmod->unique_id); -} -static DEVICE_ATTR_RO(module_serial_number); - -static ssize_t module_vendor_string_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct gb_module *gmod = to_gb_module(dev); - - return sprintf(buf, "%s", gmod->vendor_string); -} -static DEVICE_ATTR_RO(module_vendor_string); - -static ssize_t module_product_string_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct gb_module *gmod = to_gb_module(dev); - - return sprintf(buf, "%s", gmod->product_string); -} -static DEVICE_ATTR_RO(module_product_string); +gb_module_attr(vendor, x); +gb_module_attr(product, x); +gb_module_attr(version, x); +gb_module_attr(unique_id, llX); +gb_module_attr(vendor_string, s); +gb_module_attr(product_string, s); static struct attribute *module_attrs[] = { &dev_attr_module_vendor.attr, &dev_attr_module_product.attr, &dev_attr_module_version.attr, - &dev_attr_module_serial_number.attr, + &dev_attr_module_unique_id.attr, &dev_attr_module_vendor_string.attr, &dev_attr_module_product_string.attr, NULL, -- cgit v1.2.3-59-g8ed1b From 0a68a16b914472aa3c9dbac81df501b9c8dffb86 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:37 +0530 Subject: greybus: module: free resources properly on failures Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index f5d7dc84c1d1..8cbe65b0c01a 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -85,10 +85,6 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) gmod->module_id = module_id; INIT_LIST_HEAD(&gmod->interfaces); - spin_lock_irq(&gb_modules_lock); - list_add_tail(&gmod->links, &hd->modules); - spin_unlock_irq(&gb_modules_lock); - gmod->dev.parent = hd->parent; gmod->dev.bus = &greybus_bus_type; gmod->dev.type = &greybus_module_type; @@ -102,9 +98,14 @@ struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) pr_err("failed to add module device for id 0x%02hhx\n", module_id); put_device(&gmod->dev); + kfree(gmod); return NULL; } + spin_lock_irq(&gb_modules_lock); + list_add_tail(&gmod->links, &hd->modules); + spin_unlock_irq(&gb_modules_lock); + return gmod; } -- cgit v1.2.3-59-g8ed1b From b97c46ecaf27c815469f5f7e6a325f0cb53869fe Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:38 +0530 Subject: greybus: interface: free resources properly on failures Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index f7f9bfda4f9b..537ef80a599a 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -79,6 +79,7 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) if (retval) { pr_err("failed to add interface device for id 0x%02hhx\n", interface_id); + put_device(&interface->dev); kfree(interface); return NULL; } -- cgit v1.2.3-59-g8ed1b From a68bd742c068924950d17e2c5122fb9e710360ed Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:39 +0530 Subject: greybus: connection: free resources properly on failures Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 5373f44b08c1..32f1ec55b48f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -199,6 +199,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, gb_connection_hd_cport_id_free(connection); gb_protocol_put(connection->protocol); put_device(&connection->dev); + kfree(connection); return NULL; } -- cgit v1.2.3-59-g8ed1b From 85f3aeeda6047a42cd16782692864da697e53c85 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Nov 2014 18:14:40 +0530 Subject: greybus: manifest: remove extra loop for finding module descriptor Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 777abf400325..6b2c5ee03458 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -54,7 +54,8 @@ static void release_manifest_descriptors(void) * Returns the number of bytes consumed by the descriptor, or a * negative errno. */ -static int identify_descriptor(struct greybus_descriptor *desc, size_t size) +static int identify_descriptor(struct greybus_descriptor *desc, size_t size, + bool *is_module) { struct greybus_descriptor_header *desc_header = &desc->header; struct manifest_desc *descriptor; @@ -79,6 +80,7 @@ static int identify_descriptor(struct greybus_descriptor *desc, size_t size) desc_size); return -EINVAL; } + *is_module = true; break; case GREYBUS_TYPE_STRING: expected_size = sizeof(struct greybus_descriptor_header); @@ -309,7 +311,7 @@ out_free_vendor_string: * the descriptors it contains, keeping track for each its type * and the location size of its data in the buffer. * - * Next we scan the descriptors, looking for a module descriptor; + * We also identify the module descriptor during this iteration, * there must be exactly one of those. When found, we record the * information it contains, and then remove that descriptor (and any * string descriptors it refers to) from further consideration. @@ -359,8 +361,9 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) size -= sizeof(*header); while (size) { int desc_size; + bool is_module = false; - desc_size = identify_descriptor(desc, size); + desc_size = identify_descriptor(desc, size, &is_module); if (desc_size <= 0) { if (!desc_size) pr_err("zero-sized manifest descriptor\n"); @@ -369,19 +372,17 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) } desc = (struct greybus_descriptor *)((char *)desc + desc_size); size -= desc_size; - } - /* There must be a single module descriptor */ - list_for_each_entry(descriptor, &manifest_descs, links) { - if (descriptor->type == GREYBUS_TYPE_MODULE) - if (!found++) + if (is_module) { + if (++found > 1) { + pr_err("manifest must have 1 module descriptor (%u found)\n", + found); + result = false; + goto out; + } else { module_desc = descriptor; - } - if (found != 1) { - pr_err("manifest must have 1 module descriptor (%u found)\n", - found); - result = false; - goto out; + } + } } /* Parse the module manifest, starting with the module descriptor */ -- cgit v1.2.3-59-g8ed1b From 219871e25270bcb8082a98264c48b78725a74335 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 09:47:39 +0530 Subject: greybus: manifest: improve print message Suggested-by: Alex Elder Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 6b2c5ee03458..d1357a2e64d7 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -375,8 +375,7 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) if (is_module) { if (++found > 1) { - pr_err("manifest must have 1 module descriptor (%u found)\n", - found); + pr_err("multiple module descriptors found in manifest\n"); result = false; goto out; } else { -- cgit v1.2.3-59-g8ed1b From 3dc05f187185cb5795054b1a7f490fe695c779fe Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:24:58 +0530 Subject: greybus: core: remove unnecessary braces Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 7428206a32a0..0f4a18b4b002 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -181,9 +181,8 @@ static void gb_remove_modules(struct greybus_host_device *hd) { struct gb_module *gmod, *temp; - list_for_each_entry_safe(gmod, temp, &hd->modules, links) { + list_for_each_entry_safe(gmod, temp, &hd->modules, links) gb_module_destroy(gmod); - } } static DEFINE_MUTEX(hd_mutex); -- cgit v1.2.3-59-g8ed1b From 95bd99def54d972b2d528a91acb0152da0cdaeec Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:24:59 +0530 Subject: greybus: core: use 'drv' instead of dev->driver Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 0f4a18b4b002..4604649b5698 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -32,7 +32,7 @@ EXPORT_SYMBOL_GPL(greybus_disabled); static int greybus_module_match(struct device *dev, struct device_driver *drv) { - struct greybus_driver *driver = to_greybus_driver(dev->driver); + struct greybus_driver *driver = to_greybus_driver(drv); struct gb_module *gmod = to_gb_module(dev); const struct greybus_module_id *id; -- cgit v1.2.3-59-g8ed1b From 837b3b7c04c1df9dbf5e8dc4b15aaa28e7b6439b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:25:00 +0530 Subject: greybus: operation: free resources in the reverse order of allocation Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 7fcd8e4285ea..603697e5c116 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -493,8 +493,8 @@ int gb_operation_init(void) void gb_operation_exit(void) { - kmem_cache_destroy(gb_operation_cache); - gb_operation_cache = NULL; destroy_workqueue(gb_operation_recv_workqueue); gb_operation_recv_workqueue = NULL; + kmem_cache_destroy(gb_operation_cache); + gb_operation_cache = NULL; } -- cgit v1.2.3-59-g8ed1b From f66832dae0ad61732d07c40a3c81adbc935fa1b3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:25:01 +0530 Subject: greybus: nullify dangling pointers Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 1 + drivers/staging/greybus/debugfs.c | 1 + drivers/staging/greybus/gbuf.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index a8cd7e7cdd60..9d743f28e08e 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -367,6 +367,7 @@ int gb_ap_init(void) void gb_ap_exit(void) { destroy_workqueue(ap_workqueue); + ap_workqueue = NULL; } diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c index ef292f43db45..4755a36d0522 100644 --- a/drivers/staging/greybus/debugfs.c +++ b/drivers/staging/greybus/debugfs.c @@ -26,4 +26,5 @@ int gb_debugfs_init(void) void gb_debugfs_cleanup(void) { debugfs_remove_recursive(gb_debug_root); + gb_debug_root = NULL; } diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 8b5a438e3b01..d5cfb38d6d75 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -136,4 +136,5 @@ int gb_gbuf_init(void) void gb_gbuf_exit(void) { kmem_cache_destroy(gbuf_head_cache); + gbuf_head_cache = NULL; } -- cgit v1.2.3-59-g8ed1b From 38d61ddf95734c3fe69c31b867cc26932db63235 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:25:02 +0530 Subject: greybus: connection: try cancelling operations only if list isn't empty Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 32f1ec55b48f..4515bcd9dac8 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -226,11 +226,10 @@ void gb_connection_destroy(struct gb_connection *connection) return; /* XXX Need to wait for any outstanding requests to complete */ - WARN_ON(!list_empty(&connection->operations)); - - list_for_each_entry_safe(operation, next, &connection->operations, - links) { - gb_operation_cancel(operation); + if (WARN_ON(!list_empty(&connection->operations))) { + list_for_each_entry_safe(operation, next, + &connection->operations, links) + gb_operation_cancel(operation); } spin_lock_irq(&gb_connections_lock); list_del(&connection->interface_links); -- cgit v1.2.3-59-g8ed1b From 1dd90df403692f718564638543e73b6d1f734f70 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:25:03 +0530 Subject: greybus: manifest: make sure manifest_descs list is empty before parsing manifest Just an extra check to make sure the list isn't corrupted. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index d1357a2e64d7..4b85c5353978 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -332,6 +332,10 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) u32 found = 0; bool result; + /* Manifest descriptor list should be empty here */ + if (WARN_ON(!list_empty(&manifest_descs))) + return false; + /* we have to have at _least_ the manifest header */ if (size <= sizeof(manifest->header)) { pr_err("short manifest (%zu)\n", size); -- cgit v1.2.3-59-g8ed1b From ab34291da5537f8b1e037873591d6a5d54e00749 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:25:04 +0530 Subject: greybus: connection: fix duplicating naming in _gb_hd_connection_insert() Though this doesn't cause any logical issues as far as the behavior of the routine is concerned as the local variable would be considered inside the 'while' loop. But its better not to use the same name for variables at different levels. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 4515bcd9dac8..f460df4b4ec8 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -23,13 +23,13 @@ static void _gb_hd_connection_insert(struct greybus_host_device *hd, u16 cport_id = connection->hd_cport_id; while (*link) { - struct gb_connection *connection; + struct gb_connection *_connection; above = *link; - connection = rb_entry(above, struct gb_connection, hd_node); - if (connection->hd_cport_id > cport_id) + _connection = rb_entry(above, struct gb_connection, hd_node); + if (_connection->hd_cport_id > cport_id) link = &above->rb_left; - else if (connection->hd_cport_id < cport_id) + else if (_connection->hd_cport_id < cport_id) link = &above->rb_right; } rb_link_node(node, above, link); -- cgit v1.2.3-59-g8ed1b From 577f5f974c874228573451b9a521df7962f8b7de Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:25:05 +0530 Subject: greybus: core: re-use gb_module_find() in gb_remove_module() Also fix print message. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 4604649b5698..851a3021259f 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -162,19 +162,12 @@ err_module: void gb_remove_module(struct greybus_host_device *hd, u8 module_id) { - struct gb_module *gmod; - bool found = false; - - list_for_each_entry(gmod, &hd->modules, links) - if (gmod->module_id == module_id) { - found = true; - break; - } + struct gb_module *gmod = gb_module_find(hd, module_id); - if (found) + if (gmod) gb_module_destroy(gmod); else - dev_err(hd->parent, "module id %d remove error\n", module_id); + dev_err(hd->parent, "module id %d not found\n", module_id); } static void gb_remove_modules(struct greybus_host_device *hd) -- cgit v1.2.3-59-g8ed1b From 9ca4d62f15bc8e1977ed2c6c2dfc84449b6ab35f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:25:06 +0530 Subject: greybus: module: move gb_module_find() to a more logical location Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 8cbe65b0c01a..22b35e427926 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -44,6 +44,17 @@ const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, return NULL; } +struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) +{ + struct gb_module *module; + + list_for_each_entry(module, &hd->modules, links) + if (module->module_id == module_id) + return module; + + return NULL; +} + static void greybus_module_release(struct device *dev) { struct gb_module *gmod = to_gb_module(dev); @@ -132,17 +143,6 @@ void gb_module_destroy(struct gb_module *gmod) device_del(&gmod->dev); } -struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) -{ - struct gb_module *module; - - list_for_each_entry(module, &hd->modules, links) - if (module->module_id == module_id) - return module; - - return NULL; -} - int gb_module_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) { -- cgit v1.2.3-59-g8ed1b From 676daaf45869e810dcbe97cf6996f7c8e2fb7d32 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:25:07 +0530 Subject: greybus: module: move module specific code to module.c Some of module specific routines were present in core.c instead of module.c. Move them to the right place. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 59 --------------------------------------- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/module.c | 59 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 59 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 851a3021259f..be190e723a23 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -119,65 +119,6 @@ void greybus_deregister(struct greybus_driver *driver) EXPORT_SYMBOL_GPL(greybus_deregister); -/** - * gb_add_module - * - * Pass in a buffer that _should_ contain a Greybus module manifest - * and register a greybus device structure with the kernel core. - */ -void gb_add_module(struct greybus_host_device *hd, u8 module_id, - u8 *data, int size) -{ - struct gb_module *gmod; - - gmod = gb_module_create(hd, module_id); - if (!gmod) { - dev_err(hd->parent, "failed to create module\n"); - return; - } - - /* - * Parse the manifest and build up our data structures - * representing what's in it. - */ - if (!gb_manifest_parse(gmod, data, size)) { - dev_err(hd->parent, "manifest error\n"); - goto err_module; - } - - /* - * XXX - * We've successfully parsed the manifest. Now we need to - * allocate CPort Id's for connecting to the CPorts found on - * other modules. For each of these, establish a connection - * between the local and remote CPorts (including - * configuring the switch to allow them to communicate). - */ - - return; - -err_module: - gb_module_destroy(gmod); -} - -void gb_remove_module(struct greybus_host_device *hd, u8 module_id) -{ - struct gb_module *gmod = gb_module_find(hd, module_id); - - if (gmod) - gb_module_destroy(gmod); - else - dev_err(hd->parent, "module id %d not found\n", module_id); -} - -static void gb_remove_modules(struct greybus_host_device *hd) -{ - struct gb_module *gmod, *temp; - - list_for_each_entry_safe(gmod, temp, &hd->modules, links) - gb_module_destroy(gmod); -} - static DEFINE_MUTEX(hd_mutex); static void free_hd(struct kref *kref) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index d64c18146ee1..284be8472148 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -240,6 +240,7 @@ int greybus_disabled(void); void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size); void gb_remove_module(struct greybus_host_device *hd, u8 module_id); +void gb_remove_modules(struct greybus_host_device *hd); int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); int gb_ap_init(void); diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 22b35e427926..9583b5a18bd6 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -143,6 +143,65 @@ void gb_module_destroy(struct gb_module *gmod) device_del(&gmod->dev); } +/** + * gb_add_module + * + * Pass in a buffer that _should_ contain a Greybus module manifest + * and register a greybus device structure with the kernel core. + */ +void gb_add_module(struct greybus_host_device *hd, u8 module_id, + u8 *data, int size) +{ + struct gb_module *gmod; + + gmod = gb_module_create(hd, module_id); + if (!gmod) { + dev_err(hd->parent, "failed to create module\n"); + return; + } + + /* + * Parse the manifest and build up our data structures + * representing what's in it. + */ + if (!gb_manifest_parse(gmod, data, size)) { + dev_err(hd->parent, "manifest error\n"); + goto err_module; + } + + /* + * XXX + * We've successfully parsed the manifest. Now we need to + * allocate CPort Id's for connecting to the CPorts found on + * other modules. For each of these, establish a connection + * between the local and remote CPorts (including + * configuring the switch to allow them to communicate). + */ + + return; + +err_module: + gb_module_destroy(gmod); +} + +void gb_remove_module(struct greybus_host_device *hd, u8 module_id) +{ + struct gb_module *gmod = gb_module_find(hd, module_id); + + if (gmod) + gb_module_destroy(gmod); + else + dev_err(hd->parent, "module id %d not found\n", module_id); +} + +void gb_remove_modules(struct greybus_host_device *hd) +{ + struct gb_module *gmod, *temp; + + list_for_each_entry_safe(gmod, temp, &hd->modules, links) + gb_module_destroy(gmod); +} + int gb_module_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) { -- cgit v1.2.3-59-g8ed1b From 2206ea9cf8c56a0b98db3498ae5b6642a995a0a8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Nov 2014 17:25:08 +0530 Subject: greybus: interface: move gb_module_interface_init() to interface.c That's where it belong to. Also rename it in a similar way to: gb_interface_create() and gb_interface_destroy(). Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 2 +- drivers/staging/greybus/interface.c | 30 ++++++++++++++++++++++++++++++ drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/module.c | 31 ------------------------------- drivers/staging/greybus/module.h | 3 --- 5 files changed, 32 insertions(+), 35 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 9d743f28e08e..990e5c0620b9 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -146,7 +146,7 @@ static void svc_management(struct svc_function_unipro_management *management, management->link_up.module_id); return; } - ret = gb_module_interface_init(module, + ret = gb_interface_init(module, management->link_up.interface_id, management->link_up.device_id); if (ret) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 537ef80a599a..04c864f2105f 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -111,6 +111,36 @@ void gb_interface_destroy(struct gb_module *gmod) spin_unlock_irq(&gb_interfaces_lock); } +int gb_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) +{ + struct gb_interface *interface; + int ret; + + interface = gb_interface_find(gmod, interface_id); + if (!interface) { + dev_err(gmod->hd->parent, "module %hhu not found\n", + interface_id); + return -ENOENT; + } + interface->device_id = device_id; + + ret = svc_set_route_send(interface, gmod->hd); + if (ret) { + dev_err(gmod->hd->parent, "failed to set route (%d)\n", ret); + return ret; + } + + ret = gb_interface_connections_init(interface); + if (ret) { + dev_err(gmod->hd->parent, "module interface init error %d\n", + ret); + /* XXX clear route */ + return ret; + } + + return 0; +} + struct gb_interface *gb_interface_find(struct gb_module *module, u8 interface_id) { diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 50b0317d267e..bfd1781786ac 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -24,6 +24,7 @@ struct gb_interface { struct gb_interface *gb_interface_create(struct gb_module *gmod, u8 module_id); void gb_interface_destroy(struct gb_module *gmod); +int gb_interface_init(struct gb_module *gmod, u8 module_id, u8 device_id); struct gb_interface *gb_interface_find(struct gb_module *gmod, u8 interface_id); diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 9583b5a18bd6..51bd5c6131ce 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -201,34 +201,3 @@ void gb_remove_modules(struct greybus_host_device *hd) list_for_each_entry_safe(gmod, temp, &hd->modules, links) gb_module_destroy(gmod); } - -int -gb_module_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) -{ - struct gb_interface *interface; - int ret; - - interface = gb_interface_find(gmod, interface_id); - if (!interface) { - dev_err(gmod->hd->parent, "module %hhu not found\n", - interface_id); - return -ENOENT; - } - interface->device_id = device_id; - - ret = svc_set_route_send(interface, gmod->hd); - if (ret) { - dev_err(gmod->hd->parent, "failed to set route (%d)\n", ret); - return ret; - } - - ret = gb_interface_connections_init(interface); - if (ret) { - dev_err(gmod->hd->parent, "module interface init error %d\n", - ret); - /* XXX clear route */ - return ret; - } - - return 0; -} diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 74ac4fdd3c83..9e5358bae00a 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -55,7 +55,4 @@ void gb_module_destroy(struct gb_module *module); struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id); -int gb_module_interface_init(struct gb_module *gmod, u8 module_id, - u8 device_id); - #endif /* __MODULE_H */ -- cgit v1.2.3-59-g8ed1b From b7be8d2eb354ba896034276a4ef68013f2720ba5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 14 Nov 2014 14:37:32 -0800 Subject: greybus: Revert "manifest: improve print message" This reverts commit b8ba855506906de71df5b12b50cdbbf7259a930c. needed to revert an older change. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 4b85c5353978..7b0621db0a09 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -379,7 +379,8 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) if (is_module) { if (++found > 1) { - pr_err("multiple module descriptors found in manifest\n"); + pr_err("manifest must have 1 module descriptor (%u found)\n", + found); result = false; goto out; } else { -- cgit v1.2.3-59-g8ed1b From 86bf33afa3c1d52f01f9e12c2e26b730acb6bbd6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 14 Nov 2014 14:37:56 -0800 Subject: greybus: Revert "manifest: remove extra loop for finding module descriptor" This reverts commit 4d1529e6687d53878b71cdcd646e28e10d62c2e8. Alex reports that this causes problems. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 7b0621db0a09..9014611c6a7a 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -54,8 +54,7 @@ static void release_manifest_descriptors(void) * Returns the number of bytes consumed by the descriptor, or a * negative errno. */ -static int identify_descriptor(struct greybus_descriptor *desc, size_t size, - bool *is_module) +static int identify_descriptor(struct greybus_descriptor *desc, size_t size) { struct greybus_descriptor_header *desc_header = &desc->header; struct manifest_desc *descriptor; @@ -80,7 +79,6 @@ static int identify_descriptor(struct greybus_descriptor *desc, size_t size, desc_size); return -EINVAL; } - *is_module = true; break; case GREYBUS_TYPE_STRING: expected_size = sizeof(struct greybus_descriptor_header); @@ -311,7 +309,7 @@ out_free_vendor_string: * the descriptors it contains, keeping track for each its type * and the location size of its data in the buffer. * - * We also identify the module descriptor during this iteration, + * Next we scan the descriptors, looking for a module descriptor; * there must be exactly one of those. When found, we record the * information it contains, and then remove that descriptor (and any * string descriptors it refers to) from further consideration. @@ -365,9 +363,8 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) size -= sizeof(*header); while (size) { int desc_size; - bool is_module = false; - desc_size = identify_descriptor(desc, size, &is_module); + desc_size = identify_descriptor(desc, size); if (desc_size <= 0) { if (!desc_size) pr_err("zero-sized manifest descriptor\n"); @@ -376,17 +373,19 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) } desc = (struct greybus_descriptor *)((char *)desc + desc_size); size -= desc_size; + } - if (is_module) { - if (++found > 1) { - pr_err("manifest must have 1 module descriptor (%u found)\n", - found); - result = false; - goto out; - } else { + /* There must be a single module descriptor */ + list_for_each_entry(descriptor, &manifest_descs, links) { + if (descriptor->type == GREYBUS_TYPE_MODULE) + if (!found++) module_desc = descriptor; - } - } + } + if (found != 1) { + pr_err("manifest must have 1 module descriptor (%u found)\n", + found); + result = false; + goto out; } /* Parse the module manifest, starting with the module descriptor */ -- cgit v1.2.3-59-g8ed1b From 0ac5a838811c5921783cd8da410b23ee673b7e55 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 15 Nov 2014 12:12:16 -0800 Subject: greybus: skeleton for future uevents. Implement a skeleton for the uevent framework, to be filled in later when we figure out what type of module "matching" we want to do for things (connections, interfaces, modules, etc.) Based on a patch from Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/core.c | 37 ++++++++++++++++++++++++++++++++---- drivers/staging/greybus/greybus.h | 19 ++++++++++++++++++ drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/module.c | 2 +- 5 files changed, 55 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f460df4b4ec8..5927f2d979c2 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -135,7 +135,7 @@ static void gb_connection_release(struct device *dev) kfree(connection); } -static struct device_type greybus_connection_type = { +struct device_type greybus_connection_type = { .name = "greybus_connection", .release = gb_connection_release, }; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index be190e723a23..9567324c71ec 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -45,13 +45,42 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { - /* struct gb_module *gmod = to_gb_module(dev); */ + struct gb_module *gmod = NULL; + struct gb_interface *interface = NULL; + struct gb_connection *connection = NULL; + + if (is_gb_module(dev)) { + gmod = to_gb_module(dev); + } else if (is_gb_interface(dev)) { + interface = to_gb_interface(dev); + gmod = interface->gmod; + } else if (is_gb_connection(dev)) { + connection = to_gb_connection(dev); + interface = connection->interface; + gmod = interface->gmod; + } else { + dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n"); + return -EINVAL; + } - /* FIXME - add some uevents here... */ + if (connection) { + // FIXME + // add a uevent that can "load" a connection type + return 0; + } - /* FIXME - be sure to check the type to know how to handle modules and - * interfaces differently */ + if (interface) { + // FIXME + // add a uevent that can "load" a interface type + // This is what we need to bind a driver to so use the info + // in gmod here as well + return 0; + } + // FIXME + // "just" a module, be vague here, nothing binds to a module except + // the greybus core, so there's not much, if anything, we need to + // advertise. return 0; } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 284be8472148..eb5eb6080783 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -259,5 +259,24 @@ void gb_uart_device_exit(struct gb_connection *connection); int svc_set_route_send(struct gb_interface *interface, struct greybus_host_device *hd); +extern struct device_type greybus_module_type; +extern struct device_type greybus_interface_type; +extern struct device_type greybus_connection_type; + +static inline int is_gb_module(const struct device *dev) +{ + return dev->type == &greybus_module_type; +} + +static inline int is_gb_interface(const struct device *dev) +{ + return dev->type == &greybus_interface_type; +} + +static inline int is_gb_connection(const struct device *dev) +{ + return dev->type == &greybus_connection_type; +} + #endif /* __KERNEL__ */ #endif /* __LINUX_GREYBUS_H */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 04c864f2105f..38c104fc8675 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -31,7 +31,7 @@ static void gb_interface_release(struct device *dev) kfree(interface); } -static struct device_type greybus_interface_type = { +struct device_type greybus_interface_type = { .name = "greybus_interface", .release = gb_interface_release, }; diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 51bd5c6131ce..f432bea3c818 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -62,7 +62,7 @@ static void greybus_module_release(struct device *dev) kfree(gmod); } -static struct device_type greybus_module_type = { +struct device_type greybus_module_type = { .name = "greybus_module", .release = greybus_module_release, }; -- cgit v1.2.3-59-g8ed1b From b4be40435284fab95de39a16690e3580ff32fb06 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 15 Nov 2014 18:30:00 -0800 Subject: greybus: Greybus UART connection driver Flush out the Greybus UART driver to actually implement greybus requests. The number of Greybus Protocol operations has been reduced down to a managable number, and, if you look closely, you will notice it follows the CDC ACM USB specification, which can drive UART devices quite well, no need for complex UART state changes, leave all of that logic up to the firmware, if it wants/needs it. The Greybus Protocol spec has been updated to match the driver. TODO: There are 2 requests from the device to the host that need to be implemented. As this isn't fully hooked up in the Greybus core, that is not implemented here yet either. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 431 +++++++++++++++++++++++++++++++++++--- 1 file changed, 407 insertions(+), 24 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 9638d0e8aa0d..533c3c20d095 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -33,6 +33,83 @@ #define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ #define GB_NAME "ttyGB" +/* Version of the Greybus PWM protocol we support */ +#define GB_UART_VERSION_MAJOR 0x00 +#define GB_UART_VERSION_MINOR 0x01 + +/* Greybus UART request types */ +#define GB_UART_REQ_INVALID 0x00 +#define GB_UART_REQ_PROTOCOL_VERSION 0x01 +#define GB_UART_REQ_SEND_DATA 0x02 +#define GB_UART_REQ_RECEIVE_DATA 0x03 /* Unsolicited data */ +#define GB_UART_REQ_SET_LINE_CODING 0x04 +#define GB_UART_REQ_SET_CONTROL_LINE_STATE 0x05 +#define GB_UART_REQ_SET_BREAK 0x06 +#define GB_UART_REQ_SERIAL_STATE 0x07 /* Unsolicited data */ +#define GB_UART_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +struct gb_uart_proto_version_response { + __u8 status; + __u8 major; + __u8 minor; +}; + +struct gb_uart_send_data_request { + __le16 size; + __u8 data[0]; +}; + +struct gb_serial_line_coding { + __le32 rate; + __u8 format; +#define GB_SERIAL_1_STOP_BITS 0 +#define GB_SERIAL_1_5_STOP_BITS 1 +#define GB_SERIAL_2_STOP_BITS 2 + + __u8 parity; +#define GB_SERIAL_NO_PARITY 0 +#define GB_SERIAL_ODD_PARITY 1 +#define GB_SERIAL_EVEN_PARITY 2 +#define GB_SERIAL_MARK_PARITY 3 +#define GB_SERIAL_SPACE_PARITY 4 + + __u8 data; +} __attribute__ ((packed)); + +struct gb_uart_set_line_coding_request { + struct gb_serial_line_coding line_coding; +}; + +/* output control lines */ +#define GB_UART_CTRL_DTR 0x01 +#define GB_UART_CTRL_RTS 0x02 + +struct gb_uart_set_control_line_state_request { + __le16 control; +}; + +struct gb_uart_set_break_request { + __u8 state; +}; + +/* input control lines and line errors */ +#define GB_UART_CTRL_DCD 0x01 +#define GB_UART_CTRL_DSR 0x02 +#define GB_UART_CTRL_BRK 0x04 +#define GB_UART_CTRL_RI 0x08 + +#define GB_UART_CTRL_FRAMING 0x10 +#define GB_UART_CTRL_PARITY 0x20 +#define GB_UART_CTRL_OVERRUN 0x40 + +struct gb_uart_serial_state_request { + __u16 control; +}; + +struct gb_uart_simple_response { + __u8 status; +}; + struct gb_tty { struct tty_port port; struct gb_connection *connection; @@ -49,15 +126,247 @@ struct gb_tty { struct async_icount oldcount; wait_queue_head_t wioctl; struct mutex mutex; + u8 version_major; + u8 version_minor; + unsigned int ctrlin; /* input control lines */ + unsigned int ctrlout; /* output control lines */ + struct gb_serial_line_coding line_coding; }; + static struct tty_driver *gb_tty_driver; static DEFINE_IDR(tty_minors); static DEFINE_MUTEX(table_lock); static atomic_t reference_count = ATOMIC_INIT(0); -static int gb_tty_init(void); -static void gb_tty_exit(void); + +static int request_operation(struct gb_connection *connection, int type, + void *response, int response_size) +{ + struct gb_operation *operation; + struct gb_uart_simple_response *fake_request; + u8 *local_response; + int ret; + + local_response = kmalloc(response_size, GFP_KERNEL); + if (!local_response) + return -ENOMEM; + + operation = gb_operation_create(connection, type, 0, response_size); + if (!operation) { + kfree(local_response); + return -ENOMEM; + } + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("version operation failed (%d)\n", ret); + goto out; + } + + /* + * We only want to look at the status, and all requests have the same + * layout for where the status is, so cast this to a random request so + * we can see the status easier. + */ + fake_request = (struct gb_uart_simple_response *)local_response; + if (fake_request->status) { + gb_connection_err(connection, "response %hhu", + fake_request->status); + ret = -EIO; + } else { + /* Good request, so copy to the caller's buffer */ + memcpy(response, local_response, response_size); + } +out: + gb_operation_destroy(operation); + kfree(local_response); + + return ret; +} + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_tty *tty) +{ + struct gb_uart_proto_version_response version_request; + int retval; + + retval = request_operation(tty->connection, + GB_UART_REQ_PROTOCOL_VERSION, + &version_request, sizeof(version_request)); + if (retval) + return retval; + + if (version_request.major > GB_UART_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + version_request.major, GB_UART_VERSION_MAJOR); + return -ENOTSUPP; + } + + tty->version_major = version_request.major; + tty->version_minor = version_request.minor; + return 0; +} + +static int send_data(struct gb_tty *tty, u16 size, const u8 *data) +{ + struct gb_connection *connection = tty->connection; + struct gb_operation *operation; + struct gb_uart_send_data_request *request; + struct gb_uart_simple_response *response; + int retval; + + if (!data || !size) + return 0; + + operation = gb_operation_create(connection, GB_UART_REQ_SEND_DATA, + sizeof(*request) + size, + sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->size = cpu_to_le16(size); + memcpy(&request->data[0], data, size); + + /* Synchronous operation--no callback */ + retval = gb_operation_request_send(operation, NULL); + if (retval) { + dev_err(&connection->dev, + "send data operation failed (%d)\n", retval); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "send data response %hhu", + response->status); + retval = -EIO; + } +out: + gb_operation_destroy(operation); + + return retval; +} + +static int send_line_coding(struct gb_tty *tty, + struct gb_serial_line_coding *line_coding) +{ + struct gb_connection *connection = tty->connection; + struct gb_operation *operation; + struct gb_uart_set_line_coding_request *request; + struct gb_uart_simple_response *response; + int retval; + + operation = gb_operation_create(connection, GB_UART_REQ_SET_LINE_CODING, + sizeof(*request), + sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + memcpy(&request->line_coding, line_coding, sizeof(*line_coding)); + + /* Synchronous operation--no callback */ + retval = gb_operation_request_send(operation, NULL); + if (retval) { + dev_err(&connection->dev, + "send line coding operation failed (%d)\n", retval); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "send line coding response %hhu", + response->status); + retval = -EIO; + } +out: + gb_operation_destroy(operation); + + return retval; +} + +static int send_control(struct gb_tty *tty, u16 control) +{ + struct gb_connection *connection = tty->connection; + struct gb_operation *operation; + struct gb_uart_set_control_line_state_request *request; + struct gb_uart_simple_response *response; + int retval; + + operation = gb_operation_create(connection, + GB_UART_REQ_SET_CONTROL_LINE_STATE, + sizeof(*request), + sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->control = cpu_to_le16(control); + + /* Synchronous operation--no callback */ + retval = gb_operation_request_send(operation, NULL); + if (retval) { + dev_err(&connection->dev, + "send control operation failed (%d)\n", retval); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "send control response %hhu", + response->status); + retval = -EIO; + } +out: + gb_operation_destroy(operation); + + return retval; +} + +static int send_break(struct gb_tty *tty, u8 state) +{ + struct gb_connection *connection = tty->connection; + struct gb_operation *operation; + struct gb_uart_set_break_request *request; + struct gb_uart_simple_response *response; + int retval; + + if ((state != 0) && (state != 1)) { + dev_err(&connection->dev, "invalid break state of %d\n", state); + return -EINVAL; + } + + operation = gb_operation_create(connection, GB_UART_REQ_SET_BREAK, + sizeof(*request), + sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->state = state; + + /* Synchronous operation--no callback */ + retval = gb_operation_request_send(operation, NULL); + if (retval) { + dev_err(&connection->dev, + "send break operation failed (%d)\n", retval); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "send break response %hhu", + response->status); + retval = -EIO; + } +out: + gb_operation_destroy(operation); + + return retval; +} + static struct gb_tty *get_gb_by_minor(unsigned minor) { @@ -152,11 +461,9 @@ static void gb_tty_hangup(struct tty_struct *tty) static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { -// struct gb_tty *gb_tty = tty->driver_data; - - // FIXME - actually implement... + struct gb_tty *gb_tty = tty->driver_data; - return 0; + return send_data(gb_tty, count, buf); } static int gb_tty_write_room(struct tty_struct *tty) @@ -177,33 +484,91 @@ static int gb_tty_chars_in_buffer(struct tty_struct *tty) static int gb_tty_break_ctl(struct tty_struct *tty, int state) { -// struct gb_tty *gb_tty = tty->driver_data; + struct gb_tty *gb_tty = tty->driver_data; - // FIXME - send a break, if asked to... - return 0; + return send_break(gb_tty, state ? 1 : 0); } -static void gb_tty_set_termios(struct tty_struct *tty, struct ktermios *old) +static void gb_tty_set_termios(struct tty_struct *tty, + struct ktermios *termios_old) { - // FIXME - is this it??? - tty_termios_copy_hw(&tty->termios, old); + struct gb_tty *gb_tty = tty->driver_data; + struct ktermios *termios = &tty->termios; + struct gb_serial_line_coding newline; + int newctrl = gb_tty->ctrlout; + + newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); + newline.format = termios->c_cflag & CSTOPB ? 2 : 0; + newline.parity = termios->c_cflag & PARENB ? + (termios->c_cflag & PARODD ? 1 : 2) + + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; + + switch (termios->c_cflag & CSIZE) { + case CS5: + newline.data = 5; + break; + case CS6: + newline.data = 6; + break; + case CS7: + newline.data = 7; + break; + case CS8: + default: + newline.data = 8; + break; + } + + /* FIXME: needs to clear unsupported bits in the termios */ + gb_tty->clocal = ((termios->c_cflag & CLOCAL) != 0); + + if (C_BAUD(tty) == B0) { + newline.rate = gb_tty->line_coding.rate; + newctrl &= GB_UART_CTRL_DTR; + } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { + newctrl |= GB_UART_CTRL_DTR; + } + + if (newctrl != gb_tty->ctrlout) { + gb_tty->ctrlout = newctrl; + send_control(gb_tty, newctrl); + } + + if (memcpy(&gb_tty->line_coding, &newline, sizeof(newline))) { + memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); + send_line_coding(gb_tty, &gb_tty->line_coding); + } } static int gb_tty_tiocmget(struct tty_struct *tty) { -// struct gb_tty *gb_tty = tty->driver_data; + struct gb_tty *gb_tty = tty->driver_data; - // FIXME - get some tiocms! - return 0; + return (gb_tty->ctrlout & GB_UART_CTRL_DTR ? TIOCM_DTR : 0) | + (gb_tty->ctrlout & GB_UART_CTRL_RTS ? TIOCM_RTS : 0) | + (gb_tty->ctrlin & GB_UART_CTRL_DSR ? TIOCM_DSR : 0) | + (gb_tty->ctrlin & GB_UART_CTRL_RI ? TIOCM_RI : 0) | + (gb_tty->ctrlin & GB_UART_CTRL_DCD ? TIOCM_CD : 0) | + TIOCM_CTS; } static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { -// struct gb_tty *gb_tty = tty->driver_data; + struct gb_tty *gb_tty = tty->driver_data; + unsigned int newctrl = gb_tty->ctrlout; - // FIXME - set some tiocms! - return 0; + set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | + (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); + clear = (clear & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | + (clear & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); + + newctrl = (newctrl & ~clear) | set; + if (gb_tty->ctrlout == newctrl) + return 0; + + gb_tty->ctrlout = newctrl; + return send_control(gb_tty, newctrl); } static void gb_tty_throttle(struct tty_struct *tty) @@ -385,6 +750,9 @@ static const struct tty_operations gb_ops = { }; +static int gb_tty_init(void); +static void gb_tty_exit(void); + static int gb_uart_connection_init(struct gb_connection *connection) { struct gb_tty *gb_tty; @@ -404,27 +772,40 @@ static int gb_uart_connection_init(struct gb_connection *connection) gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); if (!gb_tty) return -ENOMEM; + gb_tty->connection = connection; + + /* Check for compatible protocol version */ + retval = get_version(gb_tty); + if (retval) + goto error_version; minor = alloc_minor(gb_tty); if (minor < 0) { if (minor == -ENOSPC) { - dev_err(&connection->dev, "no more free minor numbers\n"); + dev_err(&connection->dev, + "no more free minor numbers\n"); return -ENODEV; } return minor; } gb_tty->minor = minor; - gb_tty->connection = connection; spin_lock_init(&gb_tty->write_lock); spin_lock_init(&gb_tty->read_lock); init_waitqueue_head(&gb_tty->wioctl); mutex_init(&gb_tty->mutex); - /* FIXME - allocate gb buffers */ - connection->private = gb_tty; + send_control(gb_tty, gb_tty->ctrlout); + + /* initialize the uart to be 9600n81 */ + gb_tty->line_coding.rate = cpu_to_le32(9600); + gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; + gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; + gb_tty->line_coding.data = 8; + send_line_coding(gb_tty, &gb_tty->line_coding); + tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, &connection->dev); if (IS_ERR(tty_dev)) { @@ -434,8 +815,10 @@ static int gb_uart_connection_init(struct gb_connection *connection) return 0; error: - connection->private = NULL; release_minor(gb_tty); +error_version: + connection->private = NULL; + kfree(gb_tty); return retval; } @@ -526,7 +909,7 @@ static struct gb_protocol uart_protocol = { .minor = 1, .connection_init = gb_uart_connection_init, .connection_exit = gb_uart_connection_exit, - .request_recv = NULL, /* no incoming requests */ + .request_recv = NULL, /* FIXME we have 2 types of requests!!! */ }; bool gb_uart_protocol_init(void) -- cgit v1.2.3-59-g8ed1b From 19363a2ca1aa7dc48214e4556fa5bfecaf570f3f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:36 -0600 Subject: greybus: move operation timeout teardown Move the cancel_delayed_work() call so it's done separate from the removing the operation from the pending list. This should have been part of this commit: d3809f7 greybus: move timeout out of gb_operation_insert() Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 603697e5c116..344f58f5c39c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -79,9 +79,6 @@ static void gb_pending_operation_remove(struct gb_operation *operation) { struct gb_connection *connection = operation->connection; - /* Shut down our timeout timer */ - cancel_delayed_work(&operation->timeout_work); - /* Take us off of the list of pending operations */ spin_lock_irq(&gb_operations_lock); list_move_tail(&operation->links, &connection->operations); @@ -439,6 +436,7 @@ void gb_connection_operation_recv(struct gb_connection *connection, gb_connection_err(connection, "operation not found"); return; } + cancel_delayed_work(&operation->timeout_work); gb_pending_operation_remove(operation); gbuf = operation->response; gbuf->status = GB_OP_SUCCESS; /* If we got here we're good */ -- cgit v1.2.3-59-g8ed1b From ea590d5cbded81a4a7863260c2918ecebf9d1182 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:37 -0600 Subject: greybus: delete some lines in "greybus.h" This gets rid of a block of unnecessary forward declarations in "greybus.h". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index eb5eb6080783..41be5792443c 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -142,11 +142,6 @@ struct gbuf { * of the "main" gb_module structure. */ -struct gb_i2c_device; -struct gb_gpio_device; -struct gb_tty; -struct gb_usb_device; -struct gb_battery; struct greybus_host_device; struct svc_msg; -- cgit v1.2.3-59-g8ed1b From b37716f67298191aabd9f0ef5ccdf2edd3d1a50c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:38 -0600 Subject: greybus: kill gb_operation_gbuf_complete() It's possible this function was destined to do something important, but at this point it's pretty pointless. Get rid of it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 51 ------------------------------------- 1 file changed, 51 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 344f58f5c39c..f1c7dcf7d15d 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -152,49 +152,6 @@ out: gb_operation_complete(operation); } -/* - * Buffer completion function. We get notified whenever any buffer - * completes. For outbound messages, this tells us that the message - * has been sent. For inbound messages, it means the data has - * landed in the buffer and is ready to be processed. - * - * Either way, we don't do anything. We don't really care when an - * outbound message has been sent, and for incoming messages we - * we'll be done with everything we need to do before we mark it - * finished. - * - * XXX We may want to record that a request is (or is no longer) in flight. - */ -static void gb_operation_gbuf_complete(struct gbuf *gbuf) -{ - if (gbuf->status) { - struct gb_operation *operation = gbuf->operation; - struct gb_operation_msg_hdr *header; - int id; - int type; - - if (gbuf == operation->request) - header = operation->request->transfer_buffer; - else if (gbuf == operation->response) - header = operation->response->transfer_buffer; - else - header = NULL; - - if (header) { - id = le16_to_cpu(header->id); - type = header->type; - } else { - id = -1; - type = -1; - } - - gb_connection_err(operation->connection, - "operation %d type %d gbuf error %d", - id, type, gbuf->status); - } - return; -} - /* * Either this operation contains an incoming request, or its * response has arrived. An incoming request will have a null @@ -204,7 +161,6 @@ static void gb_operation_gbuf_complete(struct gbuf *gbuf) static void gb_operation_recv_work(struct work_struct *recv_work) { struct gb_operation *operation; - struct gbuf *gbuf; bool incoming_request; operation = container_of(recv_work, struct gb_operation, recv_work); @@ -212,13 +168,6 @@ static void gb_operation_recv_work(struct work_struct *recv_work) if (incoming_request) gb_operation_request_handle(operation); gb_operation_complete(operation); - - /* We're finished with the buffer we read into */ - if (incoming_request) - gbuf = operation->request; - else - gbuf = operation->response; - gb_operation_gbuf_complete(gbuf); } /* -- cgit v1.2.3-59-g8ed1b From 78496db0128bd50281e1318602f64ed9509d4b6a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:39 -0600 Subject: greybus: clean up gb_connection_operation_recv() This patch does some cleanup of gb_connection_operation_recv(). - Improve the header comments - Verify message is big enough for header before interpreting beginning of the message as a header - Verify at buffer creation time rather than receive time that no operation buffer is bigger than the maximum allowed. We can then compare the incoming data size against the buffer. - When a response message arrives, record its status in the operation result, not in the buffer status. - Record a buffer overflow as an operation error. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 25 +++++++++++++++++++------ drivers/staging/greybus/operation.h | 1 + 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f1c7dcf7d15d..bc68a5f9be66 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -204,6 +204,9 @@ static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, struct gbuf *gbuf; gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC; + if (size > GB_OPERATION_MESSAGE_SIZE_MAX) + return NULL; /* Message too big */ + size += sizeof(*header); gbuf = greybus_alloc_gbuf(operation, size, data_out, gfp_flags); if (!gbuf) @@ -355,9 +358,18 @@ int gb_operation_response_send(struct gb_operation *operation) } /* - * Handle data arriving on a connection. This is called in - * interrupt context, so just copy the incoming data into a buffer - * and do remaining handling via a work queue. + * Handle data arriving on a connection. As soon as we return, the + * incoming data buffer will be reused, so we need to copy the data + * into one of our own operation message buffers. + * + * If the incoming data is an operation response message, look up + * the operation and copy the incoming data into its response + * buffer. Otherwise allocate a new operation and copy the incoming + * data into its request buffer. + * + * This is called in interrupt context, so just copy the incoming + * data into the buffer and do remaining handling via a work queue. + * */ void gb_connection_operation_recv(struct gb_connection *connection, void *data, size_t size) @@ -370,8 +382,8 @@ void gb_connection_operation_recv(struct gb_connection *connection, if (connection->state != GB_CONNECTION_STATE_ENABLED) return; - if (size > GB_OPERATION_MESSAGE_SIZE_MAX) { - gb_connection_err(connection, "message too big"); + if (size < sizeof(*header)) { + gb_connection_err(connection, "message too small"); return; } @@ -388,11 +400,12 @@ void gb_connection_operation_recv(struct gb_connection *connection, cancel_delayed_work(&operation->timeout_work); gb_pending_operation_remove(operation); gbuf = operation->response; - gbuf->status = GB_OP_SUCCESS; /* If we got here we're good */ if (size > gbuf->transfer_buffer_length) { + operation->result = GB_OP_OVERFLOW; gb_connection_err(connection, "recv buffer too small"); return; } + operation->result = GB_OP_SUCCESS; } else { WARN_ON(msg_size != size); operation = gb_operation_create(connection, header->type, diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 4913f720a7cd..f30b162f78b7 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -18,6 +18,7 @@ enum gb_operation_status { GB_OP_INTERRUPTED = 3, GB_OP_RETRY = 4, GB_OP_PROTOCOL_BAD = 5, + GB_OP_OVERFLOW = 6, GB_OP_TIMEOUT = 0xff, }; -- cgit v1.2.3-59-g8ed1b From c7d0f258fb4d9ccb0e26bde0036abeda02575ad6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:40 -0600 Subject: greybus: reference count operations Add a reference counter to the operations structure. We'll need this when operations are actually allowed to complete asynchronously. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 14 +++++++++++--- drivers/staging/greybus/operation.h | 8 +++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index bc68a5f9be66..2b33d336bfad 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -274,6 +274,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, operation->callback = NULL; /* set at submit time */ init_completion(&operation->completion); INIT_DELAYED_WORK(&operation->timeout_work, operation_timeout); + kref_init(&operation->kref); spin_lock_irq(&gb_operations_lock); list_add_tail(&operation->links, &connection->operations); @@ -292,10 +293,11 @@ err_cache: /* * Destroy a previously created operation. */ -void gb_operation_destroy(struct gb_operation *operation) +static void _gb_operation_destroy(struct kref *kref) { - if (WARN_ON(!operation)) - return; + struct gb_operation *operation; + + operation = container_of(kref, struct gb_operation, kref); /* XXX Make sure it's not in flight */ spin_lock_irq(&gb_operations_lock); @@ -308,6 +310,12 @@ void gb_operation_destroy(struct gb_operation *operation) kmem_cache_free(gb_operation_cache, operation); } +void gb_operation_put(struct gb_operation *operation) +{ + if (!WARN_ON(!operation)) + kref_put(&operation->kref, _gb_operation_destroy); +} + /* * Send an operation request message. The caller has filled in * any payload so the request message is ready to go. If non-null, diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index f30b162f78b7..dc15c2f61e30 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -65,6 +65,7 @@ struct gb_operation { struct completion completion; /* Used if no callback */ struct delayed_work timeout_work; + struct kref kref; struct list_head links; /* connection->{operations,pending} */ /* These are what's used by caller */ @@ -78,7 +79,12 @@ void gb_connection_operation_recv(struct gb_connection *connection, struct gb_operation *gb_operation_create(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size); -void gb_operation_destroy(struct gb_operation *operation); +struct gb_operation *gb_operation_get(struct gb_operation *operation); +void gb_operation_put(struct gb_operation *operation); +static inline void gb_operation_destroy(struct gb_operation *operation) +{ + gb_operation_put(operation); +} int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback); -- cgit v1.2.3-59-g8ed1b From 63921d88724168da6d7868aa5eae8def6888a253 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:41 -0600 Subject: greybus: record a gbuf's destination CPort id Rather than indicating whether a gbuf is intended for outbound data, record its destination CPort id. That's what's really needed by the ES1 host driver. Use CPORT_ID_BAD when the buffer is intended for inbound data. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 7 ++++--- drivers/staging/greybus/gbuf.c | 4 ++-- drivers/staging/greybus/greybus.h | 6 +++--- drivers/staging/greybus/operation.c | 7 ++++++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index f82f665261b2..df0af1331370 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -97,7 +97,7 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) { struct gb_connection *connection = gbuf->operation->connection; - u32 cport_reserve = gbuf->outbound ? 1 : 0; + u32 cport_reserve = gbuf->dest_cport_id == CPORT_ID_BAD ? 0 : 1; u8 *buffer; if (size > ES1_GBUF_MSG_SIZE) { @@ -130,7 +130,7 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, } /* Insert the cport id for outbound buffers */ - if (gbuf->outbound) + if (cport_reserve) *buffer++ = connection->interface_cport_id; gbuf->transfer_buffer = buffer; gbuf->transfer_buffer_length = size; @@ -147,7 +147,8 @@ static void free_gbuf_data(struct gbuf *gbuf) if (!transfer_buffer) return; - if (gbuf->outbound) + /* Account for the cport id in outbound buffers */ + if (gbuf->dest_cport_id != CPORT_ID_BAD) transfer_buffer--; /* Back up to cport id */ kfree(transfer_buffer); } diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index d5cfb38d6d75..1b6a31d43a4f 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -35,8 +35,8 @@ static struct kmem_cache *gbuf_head_cache; * hardware designers for this issue... */ struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, + u16 dest_cport_id, unsigned int size, - bool outbound, gfp_t gfp_mask) { struct greybus_host_device *hd = operation->connection->hd; @@ -49,7 +49,7 @@ struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, kref_init(&gbuf->kref); gbuf->operation = operation; - gbuf->outbound = outbound; + gbuf->dest_cport_id = dest_cport_id; gbuf->status = -EBADR; /* Initial value--means "never set" */ /* Host controller specific allocation for the actual buffer */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 41be5792443c..b817c7615516 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -122,12 +122,12 @@ struct gbuf { struct kref kref; struct gb_operation *operation; + u16 dest_cport_id; /* Destination CPort id */ int status; + void *transfer_buffer; u32 transfer_buffer_length; - bool outbound; /* AP-relative data direction */ - void *hcd_data; /* for the HCD to track the gbuf */ }; @@ -182,7 +182,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, - unsigned int size, bool outbound, + u16 dest_cport_id, unsigned int size, gfp_t gfp_mask); void greybus_free_gbuf(struct gbuf *gbuf); struct gbuf *greybus_get_gbuf(struct gbuf *gbuf); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 2b33d336bfad..24e0a525821a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -203,12 +203,17 @@ static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, struct gb_operation_msg_hdr *header; struct gbuf *gbuf; gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC; + u16 dest_cport_id; if (size > GB_OPERATION_MESSAGE_SIZE_MAX) return NULL; /* Message too big */ + if (data_out) + dest_cport_id = operation->connection->interface_cport_id; + else + dest_cport_id = CPORT_ID_BAD; size += sizeof(*header); - gbuf = greybus_alloc_gbuf(operation, size, data_out, gfp_flags); + gbuf = greybus_alloc_gbuf(operation, dest_cport_id, size, gfp_flags); if (!gbuf) return NULL; -- cgit v1.2.3-59-g8ed1b From 6af29086bfbe201723a82b732ba5b12ecb2dfc53 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:42 -0600 Subject: greybus: use gbuf's destination cport id If the buffer allocated in the ES1 alloc_gbuf_data() routine is for outbound data, we are getting the destination CPort id from the connection. Switch to using the copy of the destination cport id we now have in the gbuf instead. Check for a valid CPort id there only if we're inserting it into the buffer. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index df0af1331370..7698463ffa89 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -96,7 +96,6 @@ static void cport_out_callback(struct urb *urb); static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) { - struct gb_connection *connection = gbuf->operation->connection; u32 cport_reserve = gbuf->dest_cport_id == CPORT_ID_BAD ? 0 : 1; u8 *buffer; @@ -118,20 +117,16 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, if (!buffer) return -ENOMEM; - /* - * we will encode the cport number in the first byte of the buffer, so - * set the second byte to be the "transfer buffer" - */ - if (connection->interface_cport_id > (u16)U8_MAX) { - pr_err("gbuf->interface_cport_id (%hd) is out of range!\n", - connection->interface_cport_id); - kfree(buffer); - return -EINVAL; - } - /* Insert the cport id for outbound buffers */ - if (cport_reserve) - *buffer++ = connection->interface_cport_id; + if (cport_reserve) { + if (gbuf->dest_cport_id > (u16)U8_MAX) { + pr_err("gbuf->dest_cport_id (%hd) is out of range!\n", + gbuf->dest_cport_id); + kfree(buffer); + return -EINVAL; + } + *buffer++ = gbuf->dest_cport_id; + } gbuf->transfer_buffer = buffer; gbuf->transfer_buffer_length = size; -- cgit v1.2.3-59-g8ed1b From ba99346828089d3833a5c31106b60b3a8cddb91d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:43 -0600 Subject: greybus: record the host device in a gbuf The only thing we now use the gbuf->operation pointer for is to get access to its connection's host device. Record the host device pointer directly in the gbuf, rather than keeping a pointer to the operation. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 4 ++-- drivers/staging/greybus/gbuf.c | 16 +++++----------- drivers/staging/greybus/greybus.h | 4 ++-- drivers/staging/greybus/operation.c | 6 ++++-- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 7698463ffa89..e24012a9b6db 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -205,7 +205,7 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { - struct greybus_host_device *hd = gbuf->operation->connection->hd; + struct greybus_host_device *hd = gbuf->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; int retval; @@ -391,7 +391,7 @@ exit: static void cport_out_callback(struct urb *urb) { struct gbuf *gbuf = urb->context; - struct es1_ap_dev *es1 = hd_to_es1(gbuf->operation->connection->hd); + struct es1_ap_dev *es1 = hd_to_es1(gbuf->hd); unsigned long flags; int i; diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 1b6a31d43a4f..6f8873af09e0 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -34,12 +34,11 @@ static struct kmem_cache *gbuf_head_cache; * that the driver can then fill up with the data to be sent out. Curse * hardware designers for this issue... */ -struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, +struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, u16 dest_cport_id, unsigned int size, gfp_t gfp_mask) { - struct greybus_host_device *hd = operation->connection->hd; struct gbuf *gbuf; int retval; @@ -48,7 +47,7 @@ struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, return NULL; kref_init(&gbuf->kref); - gbuf->operation = operation; + gbuf->hd = hd; gbuf->dest_cport_id = dest_cport_id; gbuf->status = -EBADR; /* Initial value--means "never set" */ @@ -68,9 +67,8 @@ static DEFINE_MUTEX(gbuf_mutex); static void free_gbuf(struct kref *kref) { struct gbuf *gbuf = container_of(kref, struct gbuf, kref); - struct greybus_host_device *hd = gbuf->operation->connection->hd; - hd->driver->free_gbuf_data(gbuf); + gbuf->hd->driver->free_gbuf_data(gbuf); kmem_cache_free(gbuf_head_cache, gbuf); mutex_unlock(&gbuf_mutex); @@ -94,21 +92,17 @@ EXPORT_SYMBOL_GPL(greybus_get_gbuf); int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { - struct greybus_host_device *hd = gbuf->operation->connection->hd; - gbuf->status = -EINPROGRESS; - return hd->driver->submit_gbuf(gbuf, gfp_mask); + return gbuf->hd->driver->submit_gbuf(gbuf, gfp_mask); } void greybus_kill_gbuf(struct gbuf *gbuf) { - struct greybus_host_device *hd = gbuf->operation->connection->hd; - if (gbuf->status != -EINPROGRESS) return; - hd->driver->kill_gbuf(gbuf); + gbuf->hd->driver->kill_gbuf(gbuf); } void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index b817c7615516..c86eb25dd38d 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -121,7 +121,7 @@ struct gbuf { struct kref kref; - struct gb_operation *operation; + struct greybus_host_device *hd; u16 dest_cport_id; /* Destination CPort id */ int status; @@ -181,7 +181,7 @@ void greybus_remove_hd(struct greybus_host_device *hd); void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); -struct gbuf *greybus_alloc_gbuf(struct gb_operation *operation, +struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, u16 dest_cport_id, unsigned int size, gfp_t gfp_mask); void greybus_free_gbuf(struct gbuf *gbuf); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 24e0a525821a..c0161a2c8cee 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -200,6 +200,7 @@ static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, u8 type, size_t size, bool data_out) { + struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; struct gbuf *gbuf; gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC; @@ -209,11 +210,12 @@ static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, return NULL; /* Message too big */ if (data_out) - dest_cport_id = operation->connection->interface_cport_id; + dest_cport_id = connection->interface_cport_id; else dest_cport_id = CPORT_ID_BAD; size += sizeof(*header); - gbuf = greybus_alloc_gbuf(operation, dest_cport_id, size, gfp_flags); + gbuf = greybus_alloc_gbuf(connection->hd, dest_cport_id, + size, gfp_flags); if (!gbuf) return NULL; -- cgit v1.2.3-59-g8ed1b From 2c43ce49672eacd1df87df92c2ddfdb02e276be0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:44 -0600 Subject: greybus: use a simple list of hd connections First of all, there's a bug in _gb_hd_connection_insert, which Viresh found. But pointing out that problem just called attention to the fact that I have planning to to remove the affected block of code. The set of connections associated with a host device is currently maintained in a red-black tree. The number of connections we're likely to have is on the order of a hundred, and at least for now isn't even going to approach that. When this code first went in, Greg asserted that using a list is speedier than a red-black tree for smallish numbers of elements (maybe up to a few hundred?). So this patch just removes the host device's red-black tree of connections, using a simple list instead. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 44 ++++-------------------------------- drivers/staging/greybus/connection.h | 2 +- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/greybus.h | 2 +- 4 files changed, 7 insertions(+), 43 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 5927f2d979c2..ecd7931e024d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -13,51 +13,15 @@ static DEFINE_SPINLOCK(gb_connections_lock); -static void _gb_hd_connection_insert(struct greybus_host_device *hd, - struct gb_connection *connection) -{ - struct rb_root *root = &hd->connections; - struct rb_node *node = &connection->hd_node; - struct rb_node **link = &root->rb_node; - struct rb_node *above = NULL; - u16 cport_id = connection->hd_cport_id; - - while (*link) { - struct gb_connection *_connection; - - above = *link; - _connection = rb_entry(above, struct gb_connection, hd_node); - if (_connection->hd_cport_id > cport_id) - link = &above->rb_left; - else if (_connection->hd_cport_id < cport_id) - link = &above->rb_right; - } - rb_link_node(node, above, link); - rb_insert_color(node, root); -} - -static void _gb_hd_connection_remove(struct gb_connection *connection) -{ - rb_erase(&connection->hd_node, &connection->hd->connections); -} - struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, u16 cport_id) { struct gb_connection *connection = NULL; - struct rb_node *node; spin_lock_irq(&gb_connections_lock); - node = hd->connections.rb_node; - while (node) { - connection = rb_entry(node, struct gb_connection, hd_node); - if (connection->hd_cport_id > cport_id) - node = node->rb_left; - else if (connection->hd_cport_id < cport_id) - node = node->rb_right; - else + list_for_each_entry(connection, &hd->connections, hd_links) + if (connection->hd_cport_id == cport_id) goto found; - } connection = NULL; found: spin_unlock_irq(&gb_connections_lock); @@ -204,7 +168,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, } spin_lock_irq(&gb_connections_lock); - _gb_hd_connection_insert(hd, connection); + list_add_tail(&connection->hd_links, &hd->connections); list_add_tail(&connection->interface_links, &interface->connections); spin_unlock_irq(&gb_connections_lock); @@ -233,7 +197,7 @@ void gb_connection_destroy(struct gb_connection *connection) } spin_lock_irq(&gb_connections_lock); list_del(&connection->interface_links); - _gb_hd_connection_remove(connection); + list_del(&connection->hd_links); spin_unlock_irq(&gb_connections_lock); gb_connection_hd_cport_id_free(connection); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 3aa86955b748..5e969672b793 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -28,7 +28,7 @@ struct gb_connection { u16 hd_cport_id; u16 interface_cport_id; - struct rb_node hd_node; + struct list_head hd_links; struct list_head interface_links; struct gb_protocol *protocol; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9567324c71ec..96bd97481cf2 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -186,7 +186,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver hd->parent = parent; hd->driver = driver; INIT_LIST_HEAD(&hd->modules); - hd->connections = RB_ROOT; + INIT_LIST_HEAD(&hd->connections); ida_init(&hd->cport_id_map); spin_lock_init(&hd->cport_id_map_lock); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index c86eb25dd38d..bb395280e7d3 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -166,7 +166,7 @@ struct greybus_host_device { const struct greybus_host_driver *driver; struct list_head modules; - struct rb_root connections; + struct list_head connections; struct ida cport_id_map; spinlock_t cport_id_map_lock; u8 device_id; -- cgit v1.2.3-59-g8ed1b From 1e776f31838f1037319fe54ea7e676a8871c6603 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 08:08:45 -0600 Subject: greybus: get rid of cport_id_map_lock The only time we get a cport id is when setting up a new connection. We already have a (coarser-grained) spin lock that's used to protect the connection lists, and we can use that same lock for protecting the hd's connection id map. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 ++++---- drivers/staging/greybus/core.c | 1 - drivers/staging/greybus/greybus.h | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ecd7931e024d..cb6e2e1c085c 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -42,9 +42,9 @@ static bool gb_connection_hd_cport_id_alloc(struct gb_connection *connection) struct ida *ida = &connection->hd->cport_id_map; int id; - spin_lock(&connection->hd->cport_id_map_lock); + spin_lock_irq(&gb_connections_lock); id = ida_simple_get(ida, 0, HOST_DEV_CPORT_ID_MAX, GFP_KERNEL); - spin_unlock(&connection->hd->cport_id_map_lock); + spin_unlock_irq(&gb_connections_lock); if (id < 0) return false; @@ -60,9 +60,9 @@ static void gb_connection_hd_cport_id_free(struct gb_connection *connection) { struct ida *ida = &connection->hd->cport_id_map; - spin_lock(&connection->hd->cport_id_map_lock); + spin_lock_irq(&gb_connections_lock); ida_simple_remove(ida, connection->hd_cport_id); - spin_unlock(&connection->hd->cport_id_map_lock); + spin_unlock_irq(&gb_connections_lock); connection->hd_cport_id = CPORT_ID_BAD; } diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 96bd97481cf2..26e4b44bdf44 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -188,7 +188,6 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver INIT_LIST_HEAD(&hd->modules); INIT_LIST_HEAD(&hd->connections); ida_init(&hd->cport_id_map); - spin_lock_init(&hd->cport_id_map_lock); return hd; } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index bb395280e7d3..90469bb83b27 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -168,7 +168,6 @@ struct greybus_host_device { struct list_head modules; struct list_head connections; struct ida cport_id_map; - spinlock_t cport_id_map_lock; u8 device_id; /* Private data for the host driver */ -- cgit v1.2.3-59-g8ed1b From ed7538e5a36ee7b369cb0ab0616394cb2f892a9f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 17 Nov 2014 15:15:34 -0800 Subject: greybus: uart: handle NULL size requests in request_operation() Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 533c3c20d095..a4f745afa885 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -177,7 +177,8 @@ static int request_operation(struct gb_connection *connection, int type, ret = -EIO; } else { /* Good request, so copy to the caller's buffer */ - memcpy(response, local_response, response_size); + if (response_size && response) + memcpy(response, local_response, response_size); } out: gb_operation_destroy(operation); -- cgit v1.2.3-59-g8ed1b From 68190676b78a4736081e13d67d3d5bc2a519df5c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 17 Nov 2014 15:19:14 -0800 Subject: greybus: greybus_manifest.h: update with full list of protocols The protocol values had gotten out of sync with the Greybus Protocol specification document, so bring them back into sync by changing a few values, and adding the missing values. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index a0af9a261aa8..e2ec55888914 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -30,10 +30,17 @@ enum greybus_protocol { GREYBUS_PROTOCOL_I2C = 0x03, GREYBUS_PROTOCOL_UART = 0x04, GREYBUS_PROTOCOL_HID = 0x05, - GREYBUS_PROTOCOL_SDIO = 0x06, + GREYBUS_PROTOCOL_USB = 0x06, + GREYBUS_PROTOCOL_SDIO = 0x07, GREYBUS_PROTOCOL_BATTERY = 0x08, GREYBUS_PROTOCOL_PWM = 0x09, - GREYBUS_PROTOCOL_LED = 0x0e, + GREYBUS_PROTOCOL_I2S = 0x0a, + GREYBUS_PROTOCOL_SPI = 0x0b, + GREYBUS_PROTOCOL_DISPLAY = 0x0c, + GREYBUS_PROTOCOL_CAMERA = 0x0d, + GREYBUS_PROTOCOL_SENSOR = 0x0e, + GREYBUS_PROTOCOL_LED = 0x0f, + GREYBUS_PROTOCOL_VIBRATOR = 0x10, /* ... */ GREYBUS_PROTOCOL_VENDOR = 0xff, }; -- cgit v1.2.3-59-g8ed1b From ac4029fb60abd00f63cfb4c626f1653a918dcc66 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 17 Nov 2014 16:03:34 -0800 Subject: greybus: vibrator-gb: add vibrator driver This driver implements the Greybus vibrator protocol, as defined in the Greybus protocol specification. It interacts to userspace with a single sysfs file, "timeout", and a separate "class" called "vibrator". That interface can/should be changed in the future depending on what Android wants for its HAL, but for now should be good enough to test with. There are some changes needed to kernel_ver.h to support some sysfs/driver core changes that happened after the 3.10 kernel was released to try to make the code simpler. Even with those changes, there are #ifdefs in the code to do different things depending on the kernel version to implement the same userspace api. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 3 +- drivers/staging/greybus/kernel_ver.h | 12 ++ drivers/staging/greybus/protocol.c | 5 + drivers/staging/greybus/protocol.h | 3 + drivers/staging/greybus/vibrator-gb.c | 298 ++++++++++++++++++++++++++++++++++ 5 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/greybus/vibrator-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 7ec70fe6bd5d..198539cd0a06 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -14,7 +14,8 @@ greybus-y := core.o \ pwm-gb.o \ sdio-gb.o \ uart-gb.o \ - battery-gb.o + battery-gb.o \ + vibrator-gb.o obj-m += greybus.o obj-m += es1-ap-usb.o diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index e4cda5ce65fa..ca0da11e4d8d 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -18,6 +18,18 @@ struct device_attribute dev_attr_##_name = __ATTR_RO(_name) #endif +#ifndef DEVICE_ATTR_WO +#define DEVICE_ATTR_WO(_name) \ + struct device_attribute dev_attr_##_name = __ATTR_WO(_name) +#endif + +#ifndef __ATTR_WO +#define __ATTR_WO(_name) { \ + .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ + .store = _name##_store, \ +} +#endif + #ifndef U8_MAX #define U8_MAX ((u8)~0U) #endif /* ! U8_MAX */ diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 8df2b4e7b802..346120f8d4e3 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -189,11 +189,16 @@ bool gb_protocol_init(void) pr_err("error initializing sdio protocol\n"); ret = false; } + if (!gb_vibrator_protocol_init()) { + pr_err("error initializing vibrator protocol\n"); + ret = false; + } return ret; } void gb_protocol_exit(void) { + gb_vibrator_protocol_exit(); gb_sdio_protocol_exit(); gb_uart_protocol_exit(); gb_i2c_protocol_exit(); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 1aeb34068a3f..69a40079a2c5 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -64,6 +64,9 @@ extern void gb_uart_protocol_exit(void); extern bool gb_sdio_protocol_init(void); extern void gb_sdio_protocol_exit(void); +extern bool gb_vibrator_protocol_init(void); +extern void gb_vibrator_protocol_exit(void); + bool gb_protocol_init(void); void gb_protocol_exit(void); diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c new file mode 100644 index 000000000000..2fcb2a45c254 --- /dev/null +++ b/drivers/staging/greybus/vibrator-gb.c @@ -0,0 +1,298 @@ +/* + * I2C bridge driver for the Greybus "generic" I2C module. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include +#include "greybus.h" + +struct gb_vibrator_device { + struct gb_connection *connection; + struct device *dev; + u8 version_major; + u8 version_minor; +}; + +/* Version of the Greybus i2c protocol we support */ +#define GB_VIBRATOR_VERSION_MAJOR 0x00 +#define GB_VIBRATOR_VERSION_MINOR 0x01 + +/* Greybus Vibrator request types */ +#define GB_VIBRATOR_TYPE_INVALID 0x00 +#define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01 +#define GB_VIBRATOR_TYPE_ON 0x02 +#define GB_VIBRATOR_TYPE_OFF 0x03 +#define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +struct gb_vibrator_proto_version_response { + __u8 status; + __u8 major; + __u8 minor; +}; + +struct gb_vibrator_on_request { + __le16 timeout_ms; +}; + +struct gb_vibrator_simple_response { + __u8 status; +}; + +static int request_operation(struct gb_connection *connection, int type, + void *response, int response_size) +{ + struct gb_operation *operation; + struct gb_vibrator_simple_response *fake_request; + u8 *local_response; + int ret; + + local_response = kmalloc(response_size, GFP_KERNEL); + if (!local_response) + return -ENOMEM; + + operation = gb_operation_create(connection, type, 0, response_size); + if (!operation) { + kfree(local_response); + return -ENOMEM; + } + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) { + pr_err("version operation failed (%d)\n", ret); + goto out; + } + + /* + * We only want to look at the status, and all requests have the same + * layout for where the status is, so cast this to a random request so + * we can see the status easier. + */ + fake_request = (struct gb_vibrator_simple_response *)local_response; + if (fake_request->status) { + gb_connection_err(connection, "response %hhu", + fake_request->status); + ret = -EIO; + } else { + /* Good request, so copy to the caller's buffer */ + if (response_size && response) + memcpy(response, local_response, response_size); + } +out: + gb_operation_destroy(operation); + kfree(local_response); + + return ret; +} + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_vibrator_device *vib) +{ + struct gb_connection *connection = vib->connection; + struct gb_vibrator_proto_version_response version_request; + int retval; + + retval = request_operation(connection, + GB_VIBRATOR_TYPE_PROTOCOL_VERSION, + &version_request, sizeof(version_request)); + if (retval) + return retval; + + if (version_request.major > GB_VIBRATOR_VERSION_MAJOR) { + dev_err(&connection->dev, + "unsupported major version (%hhu > %hhu)\n", + version_request.major, GB_VIBRATOR_VERSION_MAJOR); + return -ENOTSUPP; + } + + vib->version_major = version_request.major; + vib->version_minor = version_request.minor; + return 0; +} + +static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) +{ + struct gb_connection *connection = vib->connection; + struct gb_operation *operation; + struct gb_vibrator_on_request *request; + struct gb_vibrator_simple_response *response; + int retval; + + operation = gb_operation_create(connection, GB_VIBRATOR_TYPE_ON, + sizeof(*request), sizeof(*response)); + if (!operation) + return -ENOMEM; + request = operation->request_payload; + request->timeout_ms = cpu_to_le16(timeout_ms); + + /* Synchronous operation--no callback */ + retval = gb_operation_request_send(operation, NULL); + if (retval) { + dev_err(&connection->dev, + "send data operation failed (%d)\n", retval); + goto out; + } + + response = operation->response_payload; + if (response->status) { + gb_connection_err(connection, "send data response %hhu", + response->status); + retval = -EIO; + } +out: + gb_operation_destroy(operation); + + return retval; + + return 0; +} + +static int turn_off(struct gb_vibrator_device *vib) +{ + struct gb_connection *connection = vib->connection; + int retval; + + retval = request_operation(connection, GB_VIBRATOR_TYPE_OFF, NULL, 0); + if (retval) + return retval; + + return 0; +} + +static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gb_vibrator_device *vib = dev_get_drvdata(dev); + unsigned long val; + int retval; + + retval = kstrtoul(buf, 10, &val); + if (retval < 0) { + dev_err(dev, "could not parse timeout value %d\n", retval); + return retval; + } + + if (val < 0) + return -EINVAL; + if (val) + retval = turn_on(vib, (u16)val); + else + retval = turn_off(vib); + if (retval) + return retval; + + return count; +} +static DEVICE_ATTR_WO(timeout); + +static struct attribute *vibrator_attrs[] = { + &dev_attr_timeout.attr, + NULL, +}; +ATTRIBUTE_GROUPS(vibrator); + +static struct class vibrator_class = { + .name = "vibrator", + .owner = THIS_MODULE, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + .dev_groups = vibrator_groups, +#endif +}; + +static int minor; + +static int gb_vibrator_connection_init(struct gb_connection *connection) +{ + struct gb_vibrator_device *vib; + struct device *dev; + int retval; + + vib = kzalloc(sizeof(*vib), GFP_KERNEL); + if (!vib) + return -ENOMEM; + + vib->connection = connection; + + retval = get_version(vib); + if (retval) + goto error; + + /* + * FIXME: for now we create a device in sysfs for the vibrator, but odds + * are there is a "real" device somewhere in the kernel for this, but I + * can't find it at the moment... + */ + dev = device_create(&vibrator_class, NULL, MKDEV(0, 0), vib, + "vibrator%d", minor); + if (IS_ERR(dev)) { + retval = -EINVAL; + goto error; + } + minor++; + vib->dev = dev; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) + /* + * Newer kernels handle this in a race-free manner, for us, we need + * to "open code this :( + */ + retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); + if (retval) { + device_unregister(dev); + goto error; + } +#endif + + return 0; + +error: + kfree(vib); + return retval; +} + +static void gb_vibrator_connection_exit(struct gb_connection *connection) +{ + struct gb_vibrator_device *vib = connection->private; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) + sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); +#endif + device_unregister(vib->dev); + kfree(vib); +} + +static struct gb_protocol vibrator_protocol = { + .id = GREYBUS_PROTOCOL_VIBRATOR, + .major = 0, + .minor = 1, + .connection_init = gb_vibrator_connection_init, + .connection_exit = gb_vibrator_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +bool gb_vibrator_protocol_init(void) +{ + int retval; + + retval = class_register(&vibrator_class); + if (retval) + return retval; + + return gb_protocol_register(&vibrator_protocol); +} + +void gb_vibrator_protocol_exit(void) +{ + gb_protocol_deregister(&vibrator_protocol); + class_unregister(&vibrator_class); +} -- cgit v1.2.3-59-g8ed1b From 4efe6065ce0e68fc6216114d061a14b9828c0e00 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 17 Nov 2014 16:55:54 -0800 Subject: greybus: vibrator-gb: fixes based on Marti's review comments. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 14 +++++++------- drivers/staging/greybus/vibrator-gb.c | 16 +++++----------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index ca0da11e4d8d..a39ceafb9d9b 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -13,6 +13,13 @@ #ifndef __GREYBUS_KERNEL_VER_H #define __GREYBUS_KERNEL_VER_H +#ifndef __ATTR_WO +#define __ATTR_WO(_name) { \ + .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ + .store = _name##_store, \ +} +#endif + #ifndef DEVICE_ATTR_RO #define DEVICE_ATTR_RO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RO(_name) @@ -23,13 +30,6 @@ struct device_attribute dev_attr_##_name = __ATTR_WO(_name) #endif -#ifndef __ATTR_WO -#define __ATTR_WO(_name) { \ - .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ - .store = _name##_store, \ -} -#endif - #ifndef U8_MAX #define U8_MAX ((u8)~0U) #endif /* ! U8_MAX */ diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index 2fcb2a45c254..22356afb1bad 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -1,5 +1,5 @@ /* - * I2C bridge driver for the Greybus "generic" I2C module. + * Greybus Vibrator protocol driver. * * Copyright 2014 Google Inc. * @@ -153,20 +153,13 @@ out: gb_operation_destroy(operation); return retval; - - return 0; } static int turn_off(struct gb_vibrator_device *vib) { struct gb_connection *connection = vib->connection; - int retval; - retval = request_operation(connection, GB_VIBRATOR_TYPE_OFF, NULL, 0); - if (retval) - return retval; - - return 0; + return request_operation(connection, GB_VIBRATOR_TYPE_OFF, NULL, 0); } static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, @@ -232,7 +225,7 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) * are there is a "real" device somewhere in the kernel for this, but I * can't find it at the moment... */ - dev = device_create(&vibrator_class, NULL, MKDEV(0, 0), vib, + dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib, "vibrator%d", minor); if (IS_ERR(dev)) { retval = -EINVAL; @@ -243,7 +236,8 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) /* - * Newer kernels handle this in a race-free manner, for us, we need + * Newer kernels handle this in a race-free manner, by the dev_groups + * field in the struct class up above. But for older kernels, we need * to "open code this :( */ retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); -- cgit v1.2.3-59-g8ed1b From 396671b139ff5d1271d6a1d9d465f244dfa6188d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 17 Nov 2014 17:12:50 -0800 Subject: greybus: vibrator-gb: proper allocate minor numbers Change the driver to allocate minors in a proper manner, using the idr interface. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/vibrator-gb.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index 22356afb1bad..e4237518e04b 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -11,11 +11,13 @@ #include #include #include +#include #include "greybus.h" struct gb_vibrator_device { struct gb_connection *connection; struct device *dev; + int minor; /* vibrator minor number */ u8 version_major; u8 version_minor; }; @@ -202,7 +204,7 @@ static struct class vibrator_class = { #endif }; -static int minor; +static DEFINE_IDR(minors); static int gb_vibrator_connection_init(struct gb_connection *connection) { @@ -221,17 +223,21 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) goto error; /* - * FIXME: for now we create a device in sysfs for the vibrator, but odds - * are there is a "real" device somewhere in the kernel for this, but I + * For now we create a device in sysfs for the vibrator, but odds are + * there is a "real" device somewhere in the kernel for this, but I * can't find it at the moment... */ + vib->minor = idr_alloc(&minors, vib, 0, 0, GFP_KERNEL); + if (vib->minor < 0) { + retval = vib->minor; + goto error; + } dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib, - "vibrator%d", minor); + "vibrator%d", vib->minor); if (IS_ERR(dev)) { retval = -EINVAL; goto error; } - minor++; vib->dev = dev; #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) @@ -261,6 +267,7 @@ static void gb_vibrator_connection_exit(struct gb_connection *connection) #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); #endif + idr_remove(&minors, vib->minor); device_unregister(vib->dev); kfree(vib); } -- cgit v1.2.3-59-g8ed1b From 6e5dd0bbbb046df2b6a5ba72d74b611c1f15f467 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:28 -0600 Subject: greybus: kill greybus_{get,put}_gbuf() These functions are never used, so we can get rid of them. Since there's no reference-getting function any more, we no longer need "gbuf_mutex" to avoid racing gets and puts, so get rid of that too. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 14 +------------- drivers/staging/greybus/greybus.h | 2 -- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 6f8873af09e0..92da63257526 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -62,8 +62,6 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, } EXPORT_SYMBOL_GPL(greybus_alloc_gbuf); -static DEFINE_MUTEX(gbuf_mutex); - static void free_gbuf(struct kref *kref) { struct gbuf *gbuf = container_of(kref, struct gbuf, kref); @@ -71,25 +69,15 @@ static void free_gbuf(struct kref *kref) gbuf->hd->driver->free_gbuf_data(gbuf); kmem_cache_free(gbuf_head_cache, gbuf); - mutex_unlock(&gbuf_mutex); } void greybus_free_gbuf(struct gbuf *gbuf) { /* drop the reference count and get out of here */ - kref_put_mutex(&gbuf->kref, free_gbuf, &gbuf_mutex); + kref_put(&gbuf->kref, free_gbuf); } EXPORT_SYMBOL_GPL(greybus_free_gbuf); -struct gbuf *greybus_get_gbuf(struct gbuf *gbuf) -{ - mutex_lock(&gbuf_mutex); - kref_get(&gbuf->kref); - mutex_unlock(&gbuf_mutex); - return gbuf; -} -EXPORT_SYMBOL_GPL(greybus_get_gbuf); - int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { gbuf->status = -EINPROGRESS; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 90469bb83b27..3af338223609 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -184,8 +184,6 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, u16 dest_cport_id, unsigned int size, gfp_t gfp_mask); void greybus_free_gbuf(struct gbuf *gbuf); -struct gbuf *greybus_get_gbuf(struct gbuf *gbuf); -#define greybus_put_gbuf greybus_free_gbuf int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags); void greybus_kill_gbuf(struct gbuf *gbuf); -- cgit v1.2.3-59-g8ed1b From 2f528c8bf7199c5eba93ea344a502910dc3a2806 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:29 -0600 Subject: greybus: kill gbuf->kref Since there is only ever one reference to a gbuf, we don't need a kref to figure out when it can be freed. Get rid of the kref and its supporting code. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 12 +----------- drivers/staging/greybus/greybus.h | 2 -- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 92da63257526..1d0dd4acfa2a 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -46,7 +45,6 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, if (!gbuf) return NULL; - kref_init(&gbuf->kref); gbuf->hd = hd; gbuf->dest_cport_id = dest_cport_id; gbuf->status = -EBADR; /* Initial value--means "never set" */ @@ -62,20 +60,12 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, } EXPORT_SYMBOL_GPL(greybus_alloc_gbuf); -static void free_gbuf(struct kref *kref) +void greybus_free_gbuf(struct gbuf *gbuf) { - struct gbuf *gbuf = container_of(kref, struct gbuf, kref); - gbuf->hd->driver->free_gbuf_data(gbuf); kmem_cache_free(gbuf_head_cache, gbuf); } - -void greybus_free_gbuf(struct gbuf *gbuf) -{ - /* drop the reference count and get out of here */ - kref_put(&gbuf->kref, free_gbuf); -} EXPORT_SYMBOL_GPL(greybus_free_gbuf); int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 3af338223609..173170065261 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -119,8 +119,6 @@ */ struct gbuf { - struct kref kref; - struct greybus_host_device *hd; u16 dest_cport_id; /* Destination CPort id */ int status; -- cgit v1.2.3-59-g8ed1b From 3c3cef400ea7eadc87a66a974cd9e388ad99640a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:30 -0600 Subject: greybus: move the definition of struct gbuf We no longer need struct gbuf defined in "greybus.h". An upcoming patch will embed a gbuf struct (not a pointer) into the operation structure, and to do that we'll need the struct defined prior to the operation. Just move the gbuf definition into "operation.h". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 75 +------------------------------------ drivers/staging/greybus/operation.h | 14 ++++++- 2 files changed, 14 insertions(+), 75 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 173170065261..d6bfe6b2f70f 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -56,80 +56,6 @@ #define HOST_DEV_CPORT_ID_MAX CONFIG_HOST_DEV_CPORT_ID_MAX #define CPORT_ID_BAD U16_MAX /* UniPro max id is 4095 */ -/* - gbuf - - This is the "main" data structure to send / receive Greybus messages - - There are two different "views" of a gbuf structure: - - a greybus driver - - a greybus host controller - - A Greybus driver needs to worry about the following: - - creating a gbuf - - putting data into a gbuf - - sending a gbuf to a device - - receiving a gbuf from a device - - Creating a gbuf: - A greybus driver calls greybus_alloc_gbuf() - Putting data into a gbuf: - copy data into gbuf->transfer_buffer - Send a gbuf: - A greybus driver calls greybus_submit_gbuf() - The completion function in a gbuf will be called if the gbuf is successful - or not. That completion function runs in user context. After the - completion function is called, the gbuf must not be touched again as the - greybus core "owns" it. But, if a greybus driver wants to "hold on" to a - gbuf after the completion function has been called, a reference must be - grabbed on the gbuf with a call to greybus_get_gbuf(). When finished with - the gbuf, call greybus_free_gbuf() and when the last reference count is - dropped, it will be removed from the system. - Receive a gbuf: - A greybus driver calls gb_register_cport_complete() with a pointer to the - callback function to be called for when a gbuf is received from a specific - cport and device. That callback will be made in user context with a gbuf - when it is received. To stop receiving messages, call - gb_deregister_cport_complete() for a specific cport. - - - Greybus Host controller drivers need to provide - - a way to allocate the transfer buffer for a gbuf - - a way to free the transfer buffer for a gbuf when it is "finished" - - a way to submit gbuf for transmissions - - notify the core the gbuf is complete - - receive gbuf from the wire and submit them to the core - - a way to send and receive svc messages - Allocate a transfer buffer - the host controller function alloc_gbuf_data is called - Free a transfer buffer - the host controller function free_gbuf_data is called - Submit a gbuf to the hardware - the host controller function submit_gbuf is called - Notify the gbuf is complete - the host controller driver must call greybus_gbuf_finished() - Submit a SVC message to the hardware - the host controller function send_svc_msg is called - Receive gbuf messages - the host controller driver must call greybus_cport_in() with the data - Reveive SVC messages from the hardware - The host controller driver must call greybus_svc_in - - -*/ - -struct gbuf { - struct greybus_host_device *hd; - u16 dest_cport_id; /* Destination CPort id */ - int status; - - void *transfer_buffer; - u32 transfer_buffer_length; - - void *hcd_data; /* for the HCD to track the gbuf */ -}; - - /* For SP1 hardware, we are going to "hardcode" each device to have all logical * blocks in order to be able to address them as one unified "unit". Then * higher up layers will then be able to talk to them as one logical block and @@ -142,6 +68,7 @@ struct gbuf { struct greybus_host_device; struct svc_msg; +struct gbuf; /* Greybus "Host driver" structure, needed by a host controller driver to be * able to handle both SVC control as well as "real" greybus messages diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index dc15c2f61e30..c19313608e81 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -11,6 +11,8 @@ #include +struct gb_operation; + enum gb_operation_status { GB_OP_SUCCESS = 0, GB_OP_INVALID = 1, @@ -22,6 +24,17 @@ enum gb_operation_status { GB_OP_TIMEOUT = 0xff, }; +struct gbuf { + struct greybus_host_device *hd; + u16 dest_cport_id; /* Destination CPort id */ + int status; + + void *transfer_buffer; + u32 transfer_buffer_length; + + void *hcd_data; /* for the HCD to track the gbuf */ +}; + /* * A Greybus operation is a remote procedure call performed over a * connection between the AP and a function on Greybus module. @@ -50,7 +63,6 @@ enum gb_operation_status { * is guaranteed to be 64-bit aligned. * XXX and callback? */ -struct gb_operation; typedef void (*gb_operation_callback)(struct gb_operation *); struct gb_operation { struct gb_connection *connection; -- cgit v1.2.3-59-g8ed1b From 3690a826fae5102ed5daed2340926885980d51ab Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:31 -0600 Subject: greybus: define struct gb_message A Greybus buffer (gbuf) is a generic buffer used for data transfer over a Greybus interconnect. We only ever use gbufs in operations, which always involve exactly two of them. The lifetime of a gbuf is therefore directly connected to the lifetime of an operation, so there no real need to manage gbufs separate from operations. This patch begins the process of removing the gbuf abstraction, on favor of a new data type, gb_message. The purpose of a gb_message is--like a gbuf--to represent data to be transferred over Greybus. However a gb_message is oriented toward the more restrictive way we do Greybus transfers--as operation messages (either a request or a response). This patch simply defines the structure in its initial form, and defines the request and response fields in a Greybus operation structure as embedded instances of that type. The gbuf pointer is defined within the gb_message structure, and as a result lots of code needs to be tweaked to reference the request and response gbufs as subfields of the request and response structures. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 36 +++++++++++++++---------------- drivers/staging/greybus/i2c-gb.c | 16 +++++++------- drivers/staging/greybus/operation.c | 42 +++++++++++++++++++------------------ drivers/staging/greybus/operation.h | 14 +++++++------ drivers/staging/greybus/pwm-gb.c | 28 ++++++++++++------------- drivers/staging/greybus/uart-gb.c | 16 +++++++------- 6 files changed, 78 insertions(+), 74 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 473df4d47ac1..6f4609da7f97 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -153,7 +153,7 @@ static int gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_co goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "version response %hhu", response->status); @@ -199,7 +199,7 @@ static int gb_gpio_line_count_operation(struct gb_gpio_controller *gb_gpio_contr goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "line count response %hhu", response->status); @@ -234,7 +234,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; /* Synchronous operation--no callback */ @@ -244,7 +244,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "activate response %hhu", response->status); @@ -278,7 +278,7 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; /* Synchronous operation--no callback */ @@ -288,7 +288,7 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "deactivate response %hhu", response->status); @@ -320,7 +320,7 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; /* Synchronous operation--no callback */ @@ -330,7 +330,7 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "get direction response %hhu", response->status); @@ -369,7 +369,7 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; /* Synchronous operation--no callback */ @@ -379,7 +379,7 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "direction in response %hhu", response->status); @@ -412,7 +412,7 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; request->value = value_high ? 1 : 0; @@ -423,7 +423,7 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "direction out response %hhu", response->status); @@ -456,7 +456,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; /* Synchronous operation--no callback */ @@ -466,7 +466,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "get value response %hhu", response->status); @@ -507,7 +507,7 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; request->value = value_high ? 1 : 0; @@ -518,7 +518,7 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "set value response %hhu", response->status); @@ -554,7 +554,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; request->usec = cpu_to_le16(debounce_usec); @@ -565,7 +565,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "set debounce response %hhu", response->status); diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index c179078b8a2e..3374173b012a 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -118,7 +118,7 @@ static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "version response %hhu", response->status); @@ -170,7 +170,7 @@ static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "functionality response %hhu", response->status); @@ -198,7 +198,7 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->msec = cpu_to_le16(msec); /* Synchronous operation--no callback */ @@ -208,7 +208,7 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "timeout response %hhu", response->status); @@ -235,7 +235,7 @@ static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->retries = retries; /* Synchronous operation--no callback */ @@ -245,7 +245,7 @@ static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "retries response %hhu", response->status); @@ -321,7 +321,7 @@ gb_i2c_transfer_request(struct gb_connection *connection, if (!operation) return NULL; - request = operation->request_payload; + request = operation->request.payload; request->op_count = cpu_to_le16(op_count); /* Fill in the ops array */ op = &request->ops[0]; @@ -380,7 +380,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { if (response->status == GB_OP_RETRY) { ret = -EAGAIN; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c0161a2c8cee..82f4f2098109 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -71,7 +71,7 @@ static void gb_pending_operation_insert(struct gb_operation *operation) spin_unlock_irq(&gb_operations_lock); /* Store the operation id in the request header */ - header = operation->request->transfer_buffer; + header = operation->request.gbuf->transfer_buffer; header->id = cpu_to_le16(operation->id); } @@ -124,7 +124,7 @@ int gb_operation_wait(struct gb_operation *operation) ret = wait_for_completion_interruptible(&operation->completion); /* If interrupted, cancel the in-flight buffer */ if (ret < 0) - greybus_kill_gbuf(operation->request); + greybus_kill_gbuf(operation->request.gbuf); return ret; } @@ -134,7 +134,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) struct gb_protocol *protocol = operation->connection->protocol; struct gb_operation_msg_hdr *header; - header = operation->request->transfer_buffer; + header = operation->request.gbuf->transfer_buffer; /* * If the protocol has no incoming request handler, report @@ -164,7 +164,7 @@ static void gb_operation_recv_work(struct work_struct *recv_work) bool incoming_request; operation = container_of(recv_work, struct gb_operation, recv_work); - incoming_request = operation->response == NULL; + incoming_request = operation->response.gbuf == NULL; if (incoming_request) gb_operation_request_handle(operation); gb_operation_complete(operation); @@ -257,23 +257,25 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, return NULL; operation->connection = connection; - operation->request = gb_operation_gbuf_create(operation, type, + operation->request.gbuf = gb_operation_gbuf_create(operation, type, request_size, outgoing); - if (!operation->request) + if (!operation->request.gbuf) goto err_cache; - operation->request_payload = operation->request->transfer_buffer + + operation->request.operation = operation; + operation->request.payload = operation->request.gbuf->transfer_buffer + sizeof(struct gb_operation_msg_hdr); if (outgoing) { type |= GB_OPERATION_TYPE_RESPONSE; - operation->response = gb_operation_gbuf_create(operation, + operation->response.gbuf = gb_operation_gbuf_create(operation, type, response_size, false); - if (!operation->response) + if (!operation->response.gbuf) goto err_request; - operation->response_payload = - operation->response->transfer_buffer + + operation->response.operation = operation; + operation->response.payload = + operation->response.gbuf->transfer_buffer + sizeof(struct gb_operation_msg_hdr); } @@ -290,7 +292,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, return operation; err_request: - greybus_free_gbuf(operation->request); + greybus_free_gbuf(operation->request.gbuf); err_cache: kmem_cache_free(gb_operation_cache, operation); @@ -311,8 +313,8 @@ static void _gb_operation_destroy(struct kref *kref) list_del(&operation->links); spin_unlock_irq(&gb_operations_lock); - greybus_free_gbuf(operation->response); - greybus_free_gbuf(operation->request); + greybus_free_gbuf(operation->response.gbuf); + greybus_free_gbuf(operation->request.gbuf); kmem_cache_free(gb_operation_cache, operation); } @@ -349,7 +351,7 @@ int gb_operation_request_send(struct gb_operation *operation, */ operation->callback = callback; gb_pending_operation_insert(operation); - ret = greybus_submit_gbuf(operation->request, GFP_KERNEL); + ret = greybus_submit_gbuf(operation->request.gbuf, GFP_KERNEL); if (ret) return ret; @@ -414,7 +416,7 @@ void gb_connection_operation_recv(struct gb_connection *connection, } cancel_delayed_work(&operation->timeout_work); gb_pending_operation_remove(operation); - gbuf = operation->response; + gbuf = operation->response.gbuf; if (size > gbuf->transfer_buffer_length) { operation->result = GB_OP_OVERFLOW; gb_connection_err(connection, "recv buffer too small"); @@ -429,7 +431,7 @@ void gb_connection_operation_recv(struct gb_connection *connection, gb_connection_err(connection, "can't create operation"); return; } - gbuf = operation->request; + gbuf = operation->request.gbuf; } memcpy(gbuf->transfer_buffer, data, msg_size); @@ -444,9 +446,9 @@ void gb_connection_operation_recv(struct gb_connection *connection, void gb_operation_cancel(struct gb_operation *operation) { operation->canceled = true; - greybus_kill_gbuf(operation->request); - if (operation->response) - greybus_kill_gbuf(operation->response); + greybus_kill_gbuf(operation->request.gbuf); + if (operation->response.gbuf) + greybus_kill_gbuf(operation->response.gbuf); } int gb_operation_init(void) diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index c19313608e81..a18713457ba1 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -35,6 +35,12 @@ struct gbuf { void *hcd_data; /* for the HCD to track the gbuf */ }; +struct gb_message { + void *payload; + struct gb_operation *operation; + struct gbuf *gbuf; +}; + /* * A Greybus operation is a remote procedure call performed over a * connection between the AP and a function on Greybus module. @@ -66,8 +72,8 @@ struct gbuf { typedef void (*gb_operation_callback)(struct gb_operation *); struct gb_operation { struct gb_connection *connection; - struct gbuf *request; - struct gbuf *response; + struct gb_message request; + struct gb_message response; u16 id; bool canceled; @@ -79,10 +85,6 @@ struct gb_operation { struct kref kref; struct list_head links; /* connection->{operations,pending} */ - - /* These are what's used by caller */ - void *request_payload; - void *response_payload; }; void gb_connection_operation_recv(struct gb_connection *connection, diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index 44a4f64acd8e..0f465522223a 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -110,7 +110,7 @@ static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "version response %hhu", response->status); @@ -151,7 +151,7 @@ static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "pwm count response %hhu", response->status); @@ -181,7 +181,7 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; /* Synchronous operation--no callback */ @@ -191,7 +191,7 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "activate response %hhu", response->status); @@ -220,7 +220,7 @@ static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; /* Synchronous operation--no callback */ @@ -230,7 +230,7 @@ static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "deactivate response %hhu", response->status); @@ -258,7 +258,7 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; request->duty = duty; request->period = period; @@ -270,7 +270,7 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "config response %hhu", response->status); @@ -299,7 +299,7 @@ static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; request->polarity = polarity; @@ -310,7 +310,7 @@ static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "set polarity response %hhu", response->status); @@ -339,7 +339,7 @@ static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; /* Synchronous operation--no callback */ @@ -349,7 +349,7 @@ static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "enable response %hhu", response->status); @@ -378,7 +378,7 @@ static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->which = which; /* Synchronous operation--no callback */ @@ -388,7 +388,7 @@ static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "disable response %hhu", response->status); diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index a4f745afa885..a8c342eb09d3 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -229,7 +229,7 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->size = cpu_to_le16(size); memcpy(&request->data[0], data, size); @@ -241,7 +241,7 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "send data response %hhu", response->status); @@ -267,7 +267,7 @@ static int send_line_coding(struct gb_tty *tty, sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; memcpy(&request->line_coding, line_coding, sizeof(*line_coding)); /* Synchronous operation--no callback */ @@ -278,7 +278,7 @@ static int send_line_coding(struct gb_tty *tty, goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "send line coding response %hhu", response->status); @@ -304,7 +304,7 @@ static int send_control(struct gb_tty *tty, u16 control) sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->control = cpu_to_le16(control); /* Synchronous operation--no callback */ @@ -315,7 +315,7 @@ static int send_control(struct gb_tty *tty, u16 control) goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "send control response %hhu", response->status); @@ -345,7 +345,7 @@ static int send_break(struct gb_tty *tty, u8 state) sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->state = state; /* Synchronous operation--no callback */ @@ -356,7 +356,7 @@ static int send_break(struct gb_tty *tty, u8 state) goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "send break response %hhu", response->status); -- cgit v1.2.3-59-g8ed1b From c7f82d5dc07181b56b9596adab3a3891ace357fd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:32 -0600 Subject: greybus: start using struct gb_message This converts some of the operation code to start leveraging the new gb_message type. Instead of creating the request and response gbufs, we initialize (and tear down with a new function) the request and response message structures. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 70 ++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 82f4f2098109..18186fd2db8c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -23,6 +23,7 @@ /* * XXX This needs to be coordinated with host driver parameters + * XXX May need to reduce to allow for message header within a page */ #define GB_OPERATION_MESSAGE_SIZE_MAX 4096 @@ -196,36 +197,56 @@ static void operation_timeout(struct work_struct *work) * initialize it here (it'll be overwritten by the incoming * message). */ -static struct gbuf *gb_operation_gbuf_create(struct gb_operation *operation, - u8 type, size_t size, - bool data_out) +static int gb_operation_message_init(struct gb_operation *operation, + u8 type, size_t size, + bool request, bool data_out) { struct gb_connection *connection = operation->connection; + struct gb_message *message; struct gb_operation_msg_hdr *header; - struct gbuf *gbuf; gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC; u16 dest_cport_id; if (size > GB_OPERATION_MESSAGE_SIZE_MAX) - return NULL; /* Message too big */ + return -E2BIG; + if (request) { + message = &operation->request; + } else { + message = &operation->response; + type |= GB_OPERATION_TYPE_RESPONSE; + } if (data_out) dest_cport_id = connection->interface_cport_id; else dest_cport_id = CPORT_ID_BAD; + + if (message->gbuf) + return -EALREADY; /* Sanity check */ size += sizeof(*header); - gbuf = greybus_alloc_gbuf(connection->hd, dest_cport_id, + message->gbuf = greybus_alloc_gbuf(connection->hd, dest_cport_id, size, gfp_flags); - if (!gbuf) - return NULL; + if (!message->gbuf) + return -ENOMEM; /* Fill in the header structure */ - header = (struct gb_operation_msg_hdr *)gbuf->transfer_buffer; + header = (struct gb_operation_msg_hdr *)message->gbuf->transfer_buffer; header->size = cpu_to_le16(size); header->id = 0; /* Filled in when submitted */ header->type = type; - return gbuf; + message->payload = header + 1; + message->operation = operation; + + return 0; +} + +static void gb_operation_message_exit(struct gb_message *message) +{ + message->operation = NULL; + message->payload = NULL; + greybus_free_gbuf(message->gbuf); + message->gbuf = NULL; } /* @@ -251,32 +272,23 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, struct gb_operation *operation; gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC; bool outgoing = response_size != 0; + int ret; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) return NULL; operation->connection = connection; - operation->request.gbuf = gb_operation_gbuf_create(operation, type, - request_size, - outgoing); - if (!operation->request.gbuf) + ret = gb_operation_message_init(operation, type, request_size, + true, outgoing); + if (ret) goto err_cache; - operation->request.operation = operation; - operation->request.payload = operation->request.gbuf->transfer_buffer + - sizeof(struct gb_operation_msg_hdr); if (outgoing) { - type |= GB_OPERATION_TYPE_RESPONSE; - operation->response.gbuf = gb_operation_gbuf_create(operation, - type, response_size, - false); - if (!operation->response.gbuf) + ret = gb_operation_message_init(operation, type, response_size, + false, false); + if (ret) goto err_request; - operation->response.operation = operation; - operation->response.payload = - operation->response.gbuf->transfer_buffer + - sizeof(struct gb_operation_msg_hdr); } INIT_WORK(&operation->recv_work, gb_operation_recv_work); @@ -292,7 +304,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, return operation; err_request: - greybus_free_gbuf(operation->request.gbuf); + gb_operation_message_exit(&operation->request); err_cache: kmem_cache_free(gb_operation_cache, operation); @@ -313,8 +325,8 @@ static void _gb_operation_destroy(struct kref *kref) list_del(&operation->links); spin_unlock_irq(&gb_operations_lock); - greybus_free_gbuf(operation->response.gbuf); - greybus_free_gbuf(operation->request.gbuf); + gb_operation_message_exit(&operation->response); + gb_operation_message_exit(&operation->request); kmem_cache_free(gb_operation_cache, operation); } -- cgit v1.2.3-59-g8ed1b From bb88896eaf2577b8938f05a8f70c45cee0714a18 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:33 -0600 Subject: greybus: move gbuf initialization to caller Change greybus_alloc_gbuf() so all it does is allocate the gbuf data structure. Move all of the initialization of the gbuf structure in the caller. Do the inverse in the caller prior to freeing the gbuf structure via greybus_free_gbuf(). Use a null gbuf->transfer_buffer pointer rather than a null gbuf pointer to indicate an unused gbuf. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbuf.c | 22 +--------------------- drivers/staging/greybus/greybus.h | 3 ++- drivers/staging/greybus/operation.c | 20 ++++++++++++++++---- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 1d0dd4acfa2a..5ffd257de68f 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -38,32 +38,12 @@ struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, unsigned int size, gfp_t gfp_mask) { - struct gbuf *gbuf; - int retval; - - gbuf = kmem_cache_zalloc(gbuf_head_cache, gfp_mask); - if (!gbuf) - return NULL; - - gbuf->hd = hd; - gbuf->dest_cport_id = dest_cport_id; - gbuf->status = -EBADR; /* Initial value--means "never set" */ - - /* Host controller specific allocation for the actual buffer */ - retval = hd->driver->alloc_gbuf_data(gbuf, size, gfp_mask); - if (retval) { - kmem_cache_free(gbuf_head_cache, gbuf); - return NULL; - } - - return gbuf; + return kmem_cache_zalloc(gbuf_head_cache, gfp_mask); } EXPORT_SYMBOL_GPL(greybus_alloc_gbuf); void greybus_free_gbuf(struct gbuf *gbuf) { - gbuf->hd->driver->free_gbuf_data(gbuf); - kmem_cache_free(gbuf_head_cache, gbuf); } EXPORT_SYMBOL_GPL(greybus_free_gbuf); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index d6bfe6b2f70f..e1f918d50df2 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -106,7 +106,8 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, - u16 dest_cport_id, unsigned int size, + u16 dest_cport_id, + unsigned int size, gfp_t gfp_mask); void greybus_free_gbuf(struct gbuf *gbuf); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 18186fd2db8c..ab03e3edc807 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -202,10 +202,13 @@ static int gb_operation_message_init(struct gb_operation *operation, bool request, bool data_out) { struct gb_connection *connection = operation->connection; + struct greybus_host_device *hd = connection->hd; struct gb_message *message; struct gb_operation_msg_hdr *header; + struct gbuf *gbuf; gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC; u16 dest_cport_id; + int ret; if (size > GB_OPERATION_MESSAGE_SIZE_MAX) return -E2BIG; @@ -224,19 +227,27 @@ static int gb_operation_message_init(struct gb_operation *operation, if (message->gbuf) return -EALREADY; /* Sanity check */ size += sizeof(*header); - message->gbuf = greybus_alloc_gbuf(connection->hd, dest_cport_id, - size, gfp_flags); - if (!message->gbuf) + gbuf = greybus_alloc_gbuf(hd, dest_cport_id, size, gfp_flags); + if (!gbuf) return -ENOMEM; + gbuf->hd = hd; + gbuf->dest_cport_id = dest_cport_id; + gbuf->status = -EBADR; /* Initial value--means "never set" */ + ret = hd->driver->alloc_gbuf_data(gbuf, size, gfp_flags); + if (ret) { + greybus_free_gbuf(gbuf); + return ret; + } /* Fill in the header structure */ - header = (struct gb_operation_msg_hdr *)message->gbuf->transfer_buffer; + header = (struct gb_operation_msg_hdr *)gbuf->transfer_buffer; header->size = cpu_to_le16(size); header->id = 0; /* Filled in when submitted */ header->type = type; message->payload = header + 1; message->operation = operation; + message->gbuf = gbuf; return 0; } @@ -245,6 +256,7 @@ static void gb_operation_message_exit(struct gb_message *message) { message->operation = NULL; message->payload = NULL; + message->gbuf->hd->driver->free_gbuf_data(message->gbuf); greybus_free_gbuf(message->gbuf); message->gbuf = NULL; } -- cgit v1.2.3-59-g8ed1b From f7935e333ad2f94ccf8dcc8185a2ec836bb81adb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:34 -0600 Subject: greybus: use null gbuf->transfer_buffer Make sure gbuf->transfer_buffer gets reset to NULL when the buffer is freed. We can leverage that to do a little extra error checking. We'll also use a null transfer buffer in the next patch to indicate an unused gbuf. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index e24012a9b6db..e276f0c4e3cd 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -99,6 +99,9 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, u32 cport_reserve = gbuf->dest_cport_id == CPORT_ID_BAD ? 0 : 1; u8 *buffer; + if (gbuf->transfer_buffer) + return -EALREADY; + if (size > ES1_GBUF_MSG_SIZE) { pr_err("guf was asked to be bigger than %ld!\n", ES1_GBUF_MSG_SIZE); @@ -146,6 +149,7 @@ static void free_gbuf_data(struct gbuf *gbuf) if (gbuf->dest_cport_id != CPORT_ID_BAD) transfer_buffer--; /* Back up to cport id */ kfree(transfer_buffer); + gbuf->transfer_buffer = NULL; } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ @@ -214,6 +218,8 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) struct urb *urb; transfer_buffer = gbuf->transfer_buffer; + if (!transfer_buffer) + return -EINVAL; buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ /* Find a free urb */ -- cgit v1.2.3-59-g8ed1b From bc46fabccdf4565f1228d4d775680d9c85934024 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:35 -0600 Subject: greybus: embed gbufs into operation message structure Embed the gbuf structures for operation messages into the message structure rather than pointing to a dynamically allocated one. Use a null gbuf->transfer_buffer pointer rather than a null gbuf pointer to indicate an unused gbuf. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 38 +++++++++++++++---------------------- drivers/staging/greybus/operation.h | 2 +- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index ab03e3edc807..7eaea71f8604 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -72,7 +72,7 @@ static void gb_pending_operation_insert(struct gb_operation *operation) spin_unlock_irq(&gb_operations_lock); /* Store the operation id in the request header */ - header = operation->request.gbuf->transfer_buffer; + header = operation->request.gbuf.transfer_buffer; header->id = cpu_to_le16(operation->id); } @@ -125,7 +125,7 @@ int gb_operation_wait(struct gb_operation *operation) ret = wait_for_completion_interruptible(&operation->completion); /* If interrupted, cancel the in-flight buffer */ if (ret < 0) - greybus_kill_gbuf(operation->request.gbuf); + greybus_kill_gbuf(&operation->request.gbuf); return ret; } @@ -135,7 +135,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) struct gb_protocol *protocol = operation->connection->protocol; struct gb_operation_msg_hdr *header; - header = operation->request.gbuf->transfer_buffer; + header = operation->request.gbuf.transfer_buffer; /* * If the protocol has no incoming request handler, report @@ -165,7 +165,7 @@ static void gb_operation_recv_work(struct work_struct *recv_work) bool incoming_request; operation = container_of(recv_work, struct gb_operation, recv_work); - incoming_request = operation->response.gbuf == NULL; + incoming_request = operation->response.gbuf.transfer_buffer == NULL; if (incoming_request) gb_operation_request_handle(operation); gb_operation_complete(operation); @@ -212,6 +212,7 @@ static int gb_operation_message_init(struct gb_operation *operation, if (size > GB_OPERATION_MESSAGE_SIZE_MAX) return -E2BIG; + size += sizeof(*header); if (request) { message = &operation->request; @@ -219,25 +220,19 @@ static int gb_operation_message_init(struct gb_operation *operation, message = &operation->response; type |= GB_OPERATION_TYPE_RESPONSE; } + gbuf = &message->gbuf; + if (data_out) dest_cport_id = connection->interface_cport_id; else dest_cport_id = CPORT_ID_BAD; - if (message->gbuf) - return -EALREADY; /* Sanity check */ - size += sizeof(*header); - gbuf = greybus_alloc_gbuf(hd, dest_cport_id, size, gfp_flags); - if (!gbuf) - return -ENOMEM; gbuf->hd = hd; gbuf->dest_cport_id = dest_cport_id; gbuf->status = -EBADR; /* Initial value--means "never set" */ ret = hd->driver->alloc_gbuf_data(gbuf, size, gfp_flags); - if (ret) { - greybus_free_gbuf(gbuf); + if (ret) return ret; - } /* Fill in the header structure */ header = (struct gb_operation_msg_hdr *)gbuf->transfer_buffer; @@ -247,7 +242,6 @@ static int gb_operation_message_init(struct gb_operation *operation, message->payload = header + 1; message->operation = operation; - message->gbuf = gbuf; return 0; } @@ -256,9 +250,7 @@ static void gb_operation_message_exit(struct gb_message *message) { message->operation = NULL; message->payload = NULL; - message->gbuf->hd->driver->free_gbuf_data(message->gbuf); - greybus_free_gbuf(message->gbuf); - message->gbuf = NULL; + message->gbuf.hd->driver->free_gbuf_data(&message->gbuf); } /* @@ -375,7 +367,7 @@ int gb_operation_request_send(struct gb_operation *operation, */ operation->callback = callback; gb_pending_operation_insert(operation); - ret = greybus_submit_gbuf(operation->request.gbuf, GFP_KERNEL); + ret = greybus_submit_gbuf(&operation->request.gbuf, GFP_KERNEL); if (ret) return ret; @@ -440,7 +432,7 @@ void gb_connection_operation_recv(struct gb_connection *connection, } cancel_delayed_work(&operation->timeout_work); gb_pending_operation_remove(operation); - gbuf = operation->response.gbuf; + gbuf = &operation->response.gbuf; if (size > gbuf->transfer_buffer_length) { operation->result = GB_OP_OVERFLOW; gb_connection_err(connection, "recv buffer too small"); @@ -455,7 +447,7 @@ void gb_connection_operation_recv(struct gb_connection *connection, gb_connection_err(connection, "can't create operation"); return; } - gbuf = operation->request.gbuf; + gbuf = &operation->request.gbuf; } memcpy(gbuf->transfer_buffer, data, msg_size); @@ -470,9 +462,9 @@ void gb_connection_operation_recv(struct gb_connection *connection, void gb_operation_cancel(struct gb_operation *operation) { operation->canceled = true; - greybus_kill_gbuf(operation->request.gbuf); - if (operation->response.gbuf) - greybus_kill_gbuf(operation->response.gbuf); + greybus_kill_gbuf(&operation->request.gbuf); + if (operation->response.gbuf.transfer_buffer) + greybus_kill_gbuf(&operation->response.gbuf); } int gb_operation_init(void) diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index a18713457ba1..f43531dbcf33 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -38,7 +38,7 @@ struct gbuf { struct gb_message { void *payload; struct gb_operation *operation; - struct gbuf *gbuf; + struct gbuf gbuf; }; /* -- cgit v1.2.3-59-g8ed1b From 4e5007e5c27e012dd50db4c96cb9f57d235df1ee Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:36 -0600 Subject: greybus: kill the gbuf slab cache Nobody dynamically allocates gbufs any more, so we can get rid of the allocation and free routines, as as the slab cache and its related code. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 9 -------- drivers/staging/greybus/gbuf.c | 45 --------------------------------------- drivers/staging/greybus/greybus.h | 6 ------ 3 files changed, 60 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 26e4b44bdf44..588e62412fd3 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -226,12 +226,6 @@ static int __init gb_init(void) goto error_ap; } - retval = gb_gbuf_init(); - if (retval) { - pr_err("gb_gbuf_init failed\n"); - goto error_gbuf; - } - retval = gb_operation_init(); if (retval) { pr_err("gb_operation_init failed\n"); @@ -250,8 +244,6 @@ static int __init gb_init(void) error_protocol: gb_operation_exit(); error_operation: - gb_gbuf_exit(); -error_gbuf: gb_ap_exit(); error_ap: bus_unregister(&greybus_bus_type); @@ -265,7 +257,6 @@ static void __exit gb_exit(void) { gb_protocol_exit(); gb_operation_exit(); - gb_gbuf_exit(); gb_ap_exit(); bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c index 5ffd257de68f..d47cf367e412 100644 --- a/drivers/staging/greybus/gbuf.c +++ b/drivers/staging/greybus/gbuf.c @@ -13,41 +13,9 @@ #include #include #include -#include #include "greybus.h" -static struct kmem_cache *gbuf_head_cache; - -/** - * greybus_alloc_gbuf - allocate a greybus buffer - * - * @gmod: greybus device that wants to allocate this - * @cport: cport to send the data to - * @complete: callback when the gbuf is finished with - * @size: size of the buffer - * @gfp_mask: allocation mask - * - * TODO: someday it will be nice to handle DMA, but for now, due to the - * architecture we are stuck with, the greybus core has to allocate the buffer - * that the driver can then fill up with the data to be sent out. Curse - * hardware designers for this issue... - */ -struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, - u16 dest_cport_id, - unsigned int size, - gfp_t gfp_mask) -{ - return kmem_cache_zalloc(gbuf_head_cache, gfp_mask); -} -EXPORT_SYMBOL_GPL(greybus_alloc_gbuf); - -void greybus_free_gbuf(struct gbuf *gbuf) -{ - kmem_cache_free(gbuf_head_cache, gbuf); -} -EXPORT_SYMBOL_GPL(greybus_free_gbuf); - int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { gbuf->status = -EINPROGRESS; @@ -77,16 +45,3 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, gb_connection_operation_recv(connection, data, length); } EXPORT_SYMBOL_GPL(greybus_cport_in); - -int gb_gbuf_init(void) -{ - gbuf_head_cache = kmem_cache_create("gbuf_head_cache", - sizeof(struct gbuf), 0, 0, NULL); - return 0; -} - -void gb_gbuf_exit(void) -{ - kmem_cache_destroy(gbuf_head_cache); - gbuf_head_cache = NULL; -} diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index e1f918d50df2..30d5625ea4ff 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -105,12 +105,6 @@ void greybus_remove_hd(struct greybus_host_device *hd); void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); -struct gbuf *greybus_alloc_gbuf(struct greybus_host_device *hd, - u16 dest_cport_id, - unsigned int size, - gfp_t gfp_mask); -void greybus_free_gbuf(struct gbuf *gbuf); - int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags); void greybus_kill_gbuf(struct gbuf *gbuf); -- cgit v1.2.3-59-g8ed1b From 374e6a269cc3b1f044be78215c3e96021796de7d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 17 Nov 2014 18:08:37 -0600 Subject: greybus: kill off the last of gbuf.c Only three functions remain in "gbuf.c". Move one of them into "connection.c" and the other two into "operation.c". Some more cleanup is coming that will further straighten out gbufs but for now there's no sense in drawing this out any longer. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 - drivers/staging/greybus/connection.c | 15 ++++++++++++ drivers/staging/greybus/connection.h | 2 ++ drivers/staging/greybus/gbuf.c | 47 ------------------------------------ drivers/staging/greybus/greybus.h | 6 ----- drivers/staging/greybus/operation.c | 14 +++++++++++ 6 files changed, 31 insertions(+), 54 deletions(-) delete mode 100644 drivers/staging/greybus/gbuf.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 7ec70fe6bd5d..c19fc84a724b 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,5 +1,4 @@ greybus-y := core.o \ - gbuf.o \ sysfs.o \ debugfs.o \ ap.o \ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index cb6e2e1c085c..258d96cdba67 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -29,6 +29,21 @@ struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, return connection; } +void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, + u8 *data, size_t length) +{ + struct gb_connection *connection; + + connection = gb_hd_connection_find(hd, cport_id); + if (!connection) { + dev_err(hd->parent, + "nonexistent connection (%zu bytes dropped)\n", length); + return; + } + gb_connection_operation_recv(connection, data, length); +} +EXPORT_SYMBOL_GPL(greybus_cport_in); + /* * Allocate an available CPort Id for use for the host side of the * given connection. The lowest-available id is returned, so the diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 5e969672b793..bcaad47aaa03 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -53,6 +53,8 @@ void gb_connection_exit(struct gb_connection *connection); struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, u16 cport_id); +void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, + u8 *data, size_t length); __printf(2, 3) void gb_connection_err(struct gb_connection *connection, const char *fmt, ...); diff --git a/drivers/staging/greybus/gbuf.c b/drivers/staging/greybus/gbuf.c deleted file mode 100644 index d47cf367e412..000000000000 --- a/drivers/staging/greybus/gbuf.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Greybus gbuf handling - * - * Copyright 2014 Google Inc. - * - * Released under the GPLv2 only. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include - -#include "greybus.h" - -int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) -{ - gbuf->status = -EINPROGRESS; - - return gbuf->hd->driver->submit_gbuf(gbuf, gfp_mask); -} - -void greybus_kill_gbuf(struct gbuf *gbuf) -{ - if (gbuf->status != -EINPROGRESS) - return; - - gbuf->hd->driver->kill_gbuf(gbuf); -} - -void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, - u8 *data, size_t length) -{ - struct gb_connection *connection; - - connection = gb_hd_connection_find(hd, cport_id); - if (!connection) { - dev_err(hd->parent, - "nonexistent connection (%zu bytes dropped)\n", length); - return; - } - gb_connection_operation_recv(connection, data, length); -} -EXPORT_SYMBOL_GPL(greybus_cport_in); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 30d5625ea4ff..301bd4598c11 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -102,12 +102,6 @@ struct greybus_host_device { struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, struct device *parent); void greybus_remove_hd(struct greybus_host_device *hd); -void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, - u8 *data, size_t length); - -int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t mem_flags); -void greybus_kill_gbuf(struct gbuf *gbuf); - struct greybus_driver { const char *name; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 7eaea71f8604..223988327795 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -103,6 +103,20 @@ gb_pending_operation_find(struct gb_connection *connection, u16 id) return found ? operation : NULL; } +static int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) +{ + gbuf->status = -EINPROGRESS; + + return gbuf->hd->driver->submit_gbuf(gbuf, gfp_mask); +} + +static void greybus_kill_gbuf(struct gbuf *gbuf) +{ + if (gbuf->status != -EINPROGRESS) + return; + + gbuf->hd->driver->kill_gbuf(gbuf); +} /* * An operations's response message has arrived. If no callback was * supplied it was submitted for asynchronous completion, so we notify -- cgit v1.2.3-59-g8ed1b From 4b992018da759ff471872a3483b03cb0a649b698 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 18 Nov 2014 12:21:27 -0800 Subject: greybus: vibrator-gb: fix up api changes responses changed in 'master' so fix up things so that the vibrator driver works properly. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/vibrator-gb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index e4237518e04b..b9ec772b2459 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -134,7 +134,7 @@ static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request_payload; + request = operation->request.payload; request->timeout_ms = cpu_to_le16(timeout_ms); /* Synchronous operation--no callback */ @@ -145,7 +145,7 @@ static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) goto out; } - response = operation->response_payload; + response = operation->response.payload; if (response->status) { gb_connection_err(connection, "send data response %hhu", response->status); -- cgit v1.2.3-59-g8ed1b From 4f83b8d34964ef343afe5e8f731a0e37e311a42d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:38 -0600 Subject: greybus: fix an allocation flag bug We allocate message buffers with GFP_KERNEL allocation flags if possible. However when an incoming request message is received we can be in interrupt context, so we must use GFP_ATOMIC in that case. The computation of gfp_flags in gb_operation_message_init() is wrong. It is needlessly using GFP_ATOMIC when allocating outbound response buffers. Fix the flawed logic. Change the name of "data_out" to be "outbound" to be consistent with usage elsewhere. (Data/messages are "inbound" or "outbound"; requests are "incoming" or "outgoing".) Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 223988327795..103fc9746796 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -213,14 +213,14 @@ static void operation_timeout(struct work_struct *work) */ static int gb_operation_message_init(struct gb_operation *operation, u8 type, size_t size, - bool request, bool data_out) + bool request, bool outbound) { struct gb_connection *connection = operation->connection; struct greybus_host_device *hd = connection->hd; struct gb_message *message; struct gb_operation_msg_hdr *header; struct gbuf *gbuf; - gfp_t gfp_flags = data_out ? GFP_KERNEL : GFP_ATOMIC; + gfp_t gfp_flags = request && !outbound ? GFP_ATOMIC : GFP_KERNEL; u16 dest_cport_id; int ret; @@ -236,7 +236,7 @@ static int gb_operation_message_init(struct gb_operation *operation, } gbuf = &message->gbuf; - if (data_out) + if (outbound) dest_cport_id = connection->interface_cport_id; else dest_cport_id = CPORT_ID_BAD; -- cgit v1.2.3-59-g8ed1b From 5259ef138cbc78c537ca9f375eb0d18f21320c01 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:39 -0600 Subject: greybus: prepend cport byte for all gbufs Treat communication buffers for both inbound and outbound data the same way, prepending a "destination cport id" byte before the data in the buffer. Currently this is done only for outbound data buffers. This isn't needed for inbound data, but handling it this way allows the free routine to work without knowing whether the buffer was used for sending or receiving. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 43 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index e276f0c4e3cd..062fb1a818ba 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -96,7 +96,7 @@ static void cport_out_callback(struct urb *urb); static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) { - u32 cport_reserve = gbuf->dest_cport_id == CPORT_ID_BAD ? 0 : 1; + u8 dest_cport_id = gbuf->dest_cport_id; u8 *buffer; if (gbuf->transfer_buffer) @@ -107,29 +107,29 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, ES1_GBUF_MSG_SIZE); } - /* For ES2 we need to figure out what cport is going to what endpoint, - * but for ES1, it's so dirt simple, we don't have a choice... + /* + * For ES1 we need to insert a byte at the front of the data + * to indicate the destination CPort id. So we allocate one + * extra byte to allow for that. * - * Also, do a "slow" allocation now, if we need speed, use a cache + * This is only needed for outbound data, but we handle + * buffers for inbound data the same way for consistency. * - * For ES1 outbound buffers need to insert their target - * CPort Id before the data; set aside an extra byte for - * that purpose in that case. + * XXX Do we need to indicate the destination device id too? */ - buffer = kzalloc(cport_reserve + size, gfp_mask); + buffer = kzalloc(1 + size, gfp_mask); if (!buffer) return -ENOMEM; /* Insert the cport id for outbound buffers */ - if (cport_reserve) { - if (gbuf->dest_cport_id > (u16)U8_MAX) { - pr_err("gbuf->dest_cport_id (%hd) is out of range!\n", - gbuf->dest_cport_id); - kfree(buffer); - return -EINVAL; - } - *buffer++ = gbuf->dest_cport_id; + if (dest_cport_id != CPORT_ID_BAD && dest_cport_id > (u16)U8_MAX) { + pr_err("dest_cport_id (%hd) is out of range!\n", + gbuf->dest_cport_id); + kfree(buffer); + return -EINVAL; } + *buffer++ = gbuf->dest_cport_id; + gbuf->transfer_buffer = buffer; gbuf->transfer_buffer_length = size; @@ -145,9 +145,8 @@ static void free_gbuf_data(struct gbuf *gbuf) if (!transfer_buffer) return; - /* Account for the cport id in outbound buffers */ - if (gbuf->dest_cport_id != CPORT_ID_BAD) - transfer_buffer--; /* Back up to cport id */ + /* Account for the prepended cport id */ + transfer_buffer--; kfree(transfer_buffer); gbuf->transfer_buffer = NULL; } @@ -222,6 +221,12 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) return -EINVAL; buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ + /* Do one last check of the target CPort id */ + if (*buffer == CPORT_ID_BAD) { + pr_err("request to submit inbound buffer\n"); + return -EINVAL; + } + /* Find a free urb */ urb = next_free_urb(es1, gfp_mask); if (!urb) -- cgit v1.2.3-59-g8ed1b From 06a4a061f1917ab6dfdddfbf4a13c0a87f207602 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:40 -0600 Subject: greybus: improve data buffer alignment For ES1 we need to insert the destination CPort id in whatever we supply for sending over UniPro. Currently we allocate one extra byte supply the caller with an address that's offset by one from the beginning of the allocated space. As a result we always return a poorly-aligned buffer pointer. Instead, allocate enough space so that we can return a better aligned buffer to the caller. Notes: - It may be that it's more important to supply an aligned address to the hardware. - We probably need to be more careful about writing into these buffers at unaligned offsets anyway. (E.g., writing a 2-byte value at an odd offset can't be assumed to work.) Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 15 ++++++++------- drivers/staging/greybus/greybus.h | 3 +++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 062fb1a818ba..a98a2cb67211 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -20,7 +20,6 @@ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE PAGE_SIZE - static const struct usb_device_id id_table[] = { /* Made up numbers for the SVC USB Bridge in ES1 */ { USB_DEVICE(0xffff, 0x0001) }, @@ -109,17 +108,19 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, /* * For ES1 we need to insert a byte at the front of the data - * to indicate the destination CPort id. So we allocate one - * extra byte to allow for that. + * to indicate the destination CPort id. We only need one + * extra byte, but we allocate four extra bytes to allow the + * buffer returned to be aligned on a four-byte boundary. * * This is only needed for outbound data, but we handle * buffers for inbound data the same way for consistency. * * XXX Do we need to indicate the destination device id too? */ - buffer = kzalloc(1 + size, gfp_mask); + buffer = kzalloc(GB_BUFFER_ALIGN + size, gfp_mask); if (!buffer) return -ENOMEM; + buffer += GB_BUFFER_ALIGN; /* Insert the cport id for outbound buffers */ if (dest_cport_id != CPORT_ID_BAD && dest_cport_id > (u16)U8_MAX) { @@ -128,7 +129,7 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, kfree(buffer); return -EINVAL; } - *buffer++ = gbuf->dest_cport_id; + *(buffer - 1) = gbuf->dest_cport_id; gbuf->transfer_buffer = buffer; gbuf->transfer_buffer_length = size; @@ -145,8 +146,8 @@ static void free_gbuf_data(struct gbuf *gbuf) if (!transfer_buffer) return; - /* Account for the prepended cport id */ - transfer_buffer--; + /* Account for the space set aside for the prepended cport id */ + transfer_buffer -= GB_BUFFER_ALIGN; kfree(transfer_buffer); gbuf->transfer_buffer = NULL; } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 301bd4598c11..fa8065156192 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -70,6 +70,9 @@ struct greybus_host_device; struct svc_msg; struct gbuf; +/* Buffers allocated from the host driver will be aligned to this multiple */ +#define GB_BUFFER_ALIGN sizeof(u32) + /* Greybus "Host driver" structure, needed by a host controller driver to be * able to handle both SVC control as well as "real" greybus messages */ -- cgit v1.2.3-59-g8ed1b From 0f4c808a7ea2ee3d81f5c3047bd14d7057cbfe37 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:41 -0600 Subject: greybus: fill in destination data at send time For ES1 we need to insert the destination CPort id before the data to be sent over UniPro. Currently this is done at the time the buffer is created, but there's no need to do so until we're actually going to send the content of the buffer. Move the setting of that destination information into submit_gbuf(). Note that this allows us to defer initializing a few other gbuf fields until after we know the buffer allocation has succeeded. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 23 ++++++++++------------- drivers/staging/greybus/operation.c | 6 +++--- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index a98a2cb67211..a92f8934928a 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -95,7 +95,6 @@ static void cport_out_callback(struct urb *urb); static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, gfp_t gfp_mask) { - u8 dest_cport_id = gbuf->dest_cport_id; u8 *buffer; if (gbuf->transfer_buffer) @@ -122,15 +121,6 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, return -ENOMEM; buffer += GB_BUFFER_ALIGN; - /* Insert the cport id for outbound buffers */ - if (dest_cport_id != CPORT_ID_BAD && dest_cport_id > (u16)U8_MAX) { - pr_err("dest_cport_id (%hd) is out of range!\n", - gbuf->dest_cport_id); - kfree(buffer); - return -EINVAL; - } - *(buffer - 1) = gbuf->dest_cport_id; - gbuf->transfer_buffer = buffer; gbuf->transfer_buffer_length = size; @@ -212,6 +202,7 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) struct greybus_host_device *hd = gbuf->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; + u16 dest_cport_id = gbuf->dest_cport_id; int retval; u8 *transfer_buffer; u8 *buffer; @@ -222,11 +213,17 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) return -EINVAL; buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ - /* Do one last check of the target CPort id */ - if (*buffer == CPORT_ID_BAD) { - pr_err("request to submit inbound buffer\n"); + /* Do one last check of the target CPort id before filling it in */ + if (dest_cport_id == CPORT_ID_BAD) { + pr_err("request to send inbound data buffer\n"); + return -EINVAL; + } + if (dest_cport_id > (u16)U8_MAX) { + pr_err("dest_cport_id (%hd) is out of range for ES1\n", + dest_cport_id); return -EINVAL; } + *buffer = dest_cport_id; /* Find a free urb */ urb = next_free_urb(es1, gfp_mask); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 103fc9746796..b5cd9a234fb6 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -241,12 +241,12 @@ static int gb_operation_message_init(struct gb_operation *operation, else dest_cport_id = CPORT_ID_BAD; - gbuf->hd = hd; - gbuf->dest_cport_id = dest_cport_id; - gbuf->status = -EBADR; /* Initial value--means "never set" */ ret = hd->driver->alloc_gbuf_data(gbuf, size, gfp_flags); if (ret) return ret; + gbuf->hd = hd; + gbuf->dest_cport_id = dest_cport_id; + gbuf->status = -EBADR; /* Initial value--means "never set" */ /* Fill in the header structure */ header = (struct gb_operation_msg_hdr *)gbuf->transfer_buffer; -- cgit v1.2.3-59-g8ed1b From d2a259f213c925f404eb7491fae8fa03a56b3467 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:42 -0600 Subject: greybus: allocate space without gbufs This begins the transition to buffer allocation that does not rely on the gbuf construct. The host driver allocation routine will return a pointer to the buffer to be used, and the caller will be responsible for keeping track of that pointer, as well as the requested buffer size. Rename the allocation method to reflect it's no longer tied to a gbuf. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/es1-ap-usb.c | 25 ++++++------------------- drivers/staging/greybus/greybus.h | 3 +-- drivers/staging/greybus/operation.c | 8 ++++---- 4 files changed, 12 insertions(+), 26 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 588e62412fd3..ab50e2d6f817 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -169,7 +169,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * Validate that the driver implements all of the callbacks * so that we don't have to every time we make them. */ - if ((!driver->alloc_gbuf_data) || + if ((!driver->buffer_alloc) || (!driver->free_gbuf_data) || (!driver->submit_svc) || (!driver->submit_gbuf) || diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index a92f8934928a..98ab05d9cead 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -85,21 +85,12 @@ static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) static void cport_out_callback(struct urb *urb); /* - * Allocate the actual buffer for this gbuf and device and cport - * - * We are responsible for setting the following fields in a struct gbuf: - * void *hcpriv; - * void *transfer_buffer; - * u32 transfer_buffer_length; + * Allocate a buffer to be sent via UniPro. */ -static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, - gfp_t gfp_mask) +static void *buffer_alloc(unsigned int size, gfp_t gfp_mask) { u8 *buffer; - if (gbuf->transfer_buffer) - return -EALREADY; - if (size > ES1_GBUF_MSG_SIZE) { pr_err("guf was asked to be bigger than %ld!\n", ES1_GBUF_MSG_SIZE); @@ -117,14 +108,10 @@ static int alloc_gbuf_data(struct gbuf *gbuf, unsigned int size, * XXX Do we need to indicate the destination device id too? */ buffer = kzalloc(GB_BUFFER_ALIGN + size, gfp_mask); - if (!buffer) - return -ENOMEM; - buffer += GB_BUFFER_ALIGN; + if (buffer) + buffer += GB_BUFFER_ALIGN; - gbuf->transfer_buffer = buffer; - gbuf->transfer_buffer_length = size; - - return 0; + return buffer; } /* Free the memory we allocated with a gbuf */ @@ -252,7 +239,7 @@ static void kill_gbuf(struct gbuf *gbuf) static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), - .alloc_gbuf_data = alloc_gbuf_data, + .buffer_alloc = buffer_alloc, .free_gbuf_data = free_gbuf_data, .submit_svc = submit_svc, .submit_gbuf = submit_gbuf, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index fa8065156192..82ab1e6973d2 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -79,8 +79,7 @@ struct gbuf; struct greybus_host_driver { size_t hd_priv_size; - int (*alloc_gbuf_data)(struct gbuf *gbuf, unsigned int size, - gfp_t gfp_mask); + void *(*buffer_alloc)(unsigned int size, gfp_t gfp_mask); void (*free_gbuf_data)(struct gbuf *gbuf); int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b5cd9a234fb6..e3669a7a7901 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -222,7 +222,6 @@ static int gb_operation_message_init(struct gb_operation *operation, struct gbuf *gbuf; gfp_t gfp_flags = request && !outbound ? GFP_ATOMIC : GFP_KERNEL; u16 dest_cport_id; - int ret; if (size > GB_OPERATION_MESSAGE_SIZE_MAX) return -E2BIG; @@ -241,9 +240,10 @@ static int gb_operation_message_init(struct gb_operation *operation, else dest_cport_id = CPORT_ID_BAD; - ret = hd->driver->alloc_gbuf_data(gbuf, size, gfp_flags); - if (ret) - return ret; + gbuf->transfer_buffer = hd->driver->buffer_alloc(size, gfp_flags); + if (!gbuf->transfer_buffer) + return -ENOMEM; + gbuf->transfer_buffer_length = size; gbuf->hd = hd; gbuf->dest_cport_id = dest_cport_id; gbuf->status = -EBADR; /* Initial value--means "never set" */ -- cgit v1.2.3-59-g8ed1b From 9ec5411adf7cd872424f579701a91fffd508270b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:43 -0600 Subject: greybus: free space without gbufs Switch the host driver free routine to take a pointer to the previously-allocated buffer that should be freed. Rename the method to reflect it's no longer tied to a gbuf. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 +-- drivers/staging/greybus/es1-ap-usb.c | 17 ++++++++--------- drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/operation.c | 4 +++- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index ab50e2d6f817..0f03521c53f9 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -169,8 +169,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * Validate that the driver implements all of the callbacks * so that we don't have to every time we make them. */ - if ((!driver->buffer_alloc) || - (!driver->free_gbuf_data) || + if ((!driver->buffer_alloc) || (!driver->buffer_free) || (!driver->submit_svc) || (!driver->submit_gbuf) || (!driver->kill_gbuf)) { diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 98ab05d9cead..660c36367cbd 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -114,19 +114,18 @@ static void *buffer_alloc(unsigned int size, gfp_t gfp_mask) return buffer; } -/* Free the memory we allocated with a gbuf */ -static void free_gbuf_data(struct gbuf *gbuf) +/* Free a previously-allocated buffer */ +static void buffer_free(void *buffer) { - u8 *transfer_buffer = gbuf->transfer_buffer; + u8 *allocated = buffer; - /* Can be called with a NULL transfer_buffer on some error paths */ - if (!transfer_buffer) + /* Can be called with a NULL buffer on some error paths */ + if (!allocated) return; /* Account for the space set aside for the prepended cport id */ - transfer_buffer -= GB_BUFFER_ALIGN; - kfree(transfer_buffer); - gbuf->transfer_buffer = NULL; + allocated -= GB_BUFFER_ALIGN; + kfree(allocated); } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ @@ -240,7 +239,7 @@ static void kill_gbuf(struct gbuf *gbuf) static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .buffer_alloc = buffer_alloc, - .free_gbuf_data = free_gbuf_data, + .buffer_free = buffer_free, .submit_svc = submit_svc, .submit_gbuf = submit_gbuf, .kill_gbuf = kill_gbuf, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 82ab1e6973d2..f27dcaf067ca 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -80,7 +80,7 @@ struct greybus_host_driver { size_t hd_priv_size; void *(*buffer_alloc)(unsigned int size, gfp_t gfp_mask); - void (*free_gbuf_data)(struct gbuf *gbuf); + void (*buffer_free)(void *buffer); int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); int (*submit_gbuf)(struct gbuf *gbuf, gfp_t gfp_mask); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index e3669a7a7901..c04aced63204 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -264,7 +264,9 @@ static void gb_operation_message_exit(struct gb_message *message) { message->operation = NULL; message->payload = NULL; - message->gbuf.hd->driver->free_gbuf_data(&message->gbuf); + message->gbuf.hd->driver->buffer_free(message->gbuf.transfer_buffer); + message->gbuf.transfer_buffer = NULL; + message->gbuf.transfer_buffer_length = 0; } /* -- cgit v1.2.3-59-g8ed1b From a9163b2c30c9e110530ed5f56bc5296bb152aa98 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:44 -0600 Subject: greybus: cancel buffers via magic cookie Change the interface for canceling in-flight buffers to take a magic cookie value as argument rather than a gbuf. Right now we pass the gbuf->hcd_data pointer that's assumed to have been set by the submit routine. But the next patch will change the submit routine to return the cookie to be used, and the caller will be responsible for keeping track of it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 ++-- drivers/staging/greybus/es1-ap-usb.c | 16 +++++++++------- drivers/staging/greybus/greybus.h | 4 ++-- drivers/staging/greybus/operation.c | 3 ++- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 0f03521c53f9..39f8c4a5c2d2 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -170,9 +170,9 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * so that we don't have to every time we make them. */ if ((!driver->buffer_alloc) || (!driver->buffer_free) || - (!driver->submit_svc) || (!driver->submit_gbuf) || - (!driver->kill_gbuf)) { + (!driver->buffer_cancel) || + (!driver->submit_svc)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); return NULL; } diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 660c36367cbd..c4a7def1dda7 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -226,13 +226,15 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) return retval; } -static void kill_gbuf(struct gbuf *gbuf) +static void buffer_cancel(void *cookie) { - struct urb *urb = gbuf->hcd_data; - - if (!urb) - return; + struct urb *urb = cookie; + /* + * We really should be defensive and track all outstanding + * (sent) buffers rather than trusting the cookie provided + * is valid. For the time being, this will do. + */ usb_kill_urb(urb); } @@ -240,9 +242,9 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .buffer_alloc = buffer_alloc, .buffer_free = buffer_free, - .submit_svc = submit_svc, .submit_gbuf = submit_gbuf, - .kill_gbuf = kill_gbuf, + .buffer_cancel = buffer_cancel, + .submit_svc = submit_svc, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f27dcaf067ca..a9b2b459d7ad 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -81,10 +81,10 @@ struct greybus_host_driver { void *(*buffer_alloc)(unsigned int size, gfp_t gfp_mask); void (*buffer_free)(void *buffer); + int (*submit_gbuf)(struct gbuf *gbuf, gfp_t gfp_mask); + void (*buffer_cancel)(void *cookie); int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); - int (*submit_gbuf)(struct gbuf *gbuf, gfp_t gfp_mask); - void (*kill_gbuf)(struct gbuf *gbuf); }; struct greybus_host_device { diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c04aced63204..26c9dd688cc3 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -115,8 +115,9 @@ static void greybus_kill_gbuf(struct gbuf *gbuf) if (gbuf->status != -EINPROGRESS) return; - gbuf->hd->driver->kill_gbuf(gbuf); + gbuf->hd->driver->buffer_cancel(gbuf->hcd_data); } + /* * An operations's response message has arrived. If no callback was * supplied it was submitted for asynchronous completion, so we notify -- cgit v1.2.3-59-g8ed1b From fa23ffeee6949ab5962fe2727ffb107574123aaf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:45 -0600 Subject: greybus: stash hd as context for all URBs This changes the context value stashed with each USB URB so that it is always the host device pointer. In cport_out_callback() this allows us to get away with *not* requiring the gbuf for handling completions any more. We are (currently) ignoring the gbuf status value returned anyway, so we'll skip setting it altogether. Greg's comments in cport_out_callback() point out that ignoring this was misguided, and handling send errors will be put in place in an upcoming patch. The context is set to the host device pointer for SVC receive and CPort receive URBs for consistency--because we can. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index c4a7def1dda7..9801d08fbc08 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -221,7 +221,7 @@ static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), buffer, gbuf->transfer_buffer_length + 1, - cport_out_callback, gbuf); + cport_out_callback, hd); retval = usb_submit_urb(urb, gfp_mask); return retval; } @@ -320,7 +320,8 @@ static void ap_disconnect(struct usb_interface *interface) /* Callback for when we get a SVC message */ static void svc_in_callback(struct urb *urb) { - struct es1_ap_dev *es1 = urb->context; + struct greybus_host_device *hd = urb->context; + struct es1_ap_dev *es1 = hd_to_es1(hd); struct device *dev = &urb->dev->dev; int status = check_urb_status(urb); int retval; @@ -346,8 +347,9 @@ exit: static void cport_in_callback(struct urb *urb) { + struct greybus_host_device *hd = urb->context; + struct es1_ap_dev *es1 = hd_to_es1(hd); struct device *dev = &urb->dev->dev; - struct es1_ap_dev *es1 = urb->context; int status = check_urb_status(urb); int retval; u8 cport; @@ -387,15 +389,12 @@ exit: static void cport_out_callback(struct urb *urb) { - struct gbuf *gbuf = urb->context; - struct es1_ap_dev *es1 = hd_to_es1(gbuf->hd); + struct greybus_host_device *hd = urb->context; + struct es1_ap_dev *es1 = hd_to_es1(hd); unsigned long flags; + /* int status = check_urb_status(urb); */ int i; - /* Record whether the transfer was successful */ - gbuf->status = check_urb_status(urb); - gbuf->hcd_data = NULL; - /* * See if this was an urb in our pool, if so mark it "free", otherwise * we need to free it ourselves. @@ -414,6 +413,8 @@ static void cport_out_callback(struct urb *urb) usb_free_urb(urb); /* + * Rest assured Greg, this craziness is getting fixed. + * * Yes, you are right, we aren't telling anyone that the urb finished. * "That's crazy! How does this all even work?" you might be saying. * The "magic" is the idea that greybus works on the "operation" level, @@ -520,7 +521,7 @@ static int ap_probe(struct usb_interface *interface, usb_fill_int_urb(es1->svc_urb, udev, usb_rcvintpipe(udev, es1->svc_endpoint), es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, - es1, svc_interval); + hd, svc_interval); retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); if (retval) goto error; @@ -540,7 +541,7 @@ static int ap_probe(struct usb_interface *interface, usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, es1->cport_in_endpoint), buffer, ES1_GBUF_MSG_SIZE, cport_in_callback, - es1); + hd); es1->cport_in_urb[i] = urb; es1->cport_in_buffer[i] = buffer; retval = usb_submit_urb(urb, GFP_KERNEL); -- cgit v1.2.3-59-g8ed1b From 58a5bdc7358ae87d2f7b8c85319f624651b7555b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:46 -0600 Subject: greybus: send buffers without gbufs Change the method that sends messages so that it sends "raw" buffers rather than gbufs. To do this, we supply the host device and destination CPort when sending. As with other recent patches, change the name of the method to reflect that we're no longer dealing with gbufs. The interface has changed as well. Now this routine will return a "cookie" value. The cookie is used to represent the outgoing request, and is supplied by the caller if necessary to cancel a previously-sent buffer. We'll store the result in gbuf->hcd_data for now (which produces the same result as before...). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 +- drivers/staging/greybus/es1-ap-usb.c | 56 ++++++++++++++++++++++++------------ drivers/staging/greybus/greybus.h | 3 +- drivers/staging/greybus/operation.c | 12 ++++++-- 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 39f8c4a5c2d2..04fc5412c351 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -170,8 +170,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * so that we don't have to every time we make them. */ if ((!driver->buffer_alloc) || (!driver->buffer_free) || - (!driver->submit_gbuf) || - (!driver->buffer_cancel) || + (!driver->buffer_send) || (!driver->buffer_cancel) || (!driver->submit_svc)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); return NULL; diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 9801d08fbc08..3404dc59a151 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -183,47 +183,65 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) return urb; } -static int submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) +/* + * Returns an opaque cookie value if successful, or a pointer coded + * error otherwise. If the caller wishes to cancel the in-flight + * buffer, it must supply the returned cookie to the cancel routine. + */ +static void *buffer_send(struct greybus_host_device *hd, u16 dest_cport_id, + void *buffer, size_t buffer_size, gfp_t gfp_mask) { - struct greybus_host_device *hd = gbuf->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; - u16 dest_cport_id = gbuf->dest_cport_id; + u8 *transfer_buffer = buffer; + int transfer_buffer_size; int retval; - u8 *transfer_buffer; - u8 *buffer; struct urb *urb; - transfer_buffer = gbuf->transfer_buffer; - if (!transfer_buffer) - return -EINVAL; - buffer = &transfer_buffer[-1]; /* yes, we mean -1 */ + if (!buffer) { + pr_err("null buffer supplied to send\n"); + return ERR_PTR(-EINVAL); + } + if (buffer_size > (size_t)INT_MAX) { + pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); + return ERR_PTR(-EINVAL); + } + transfer_buffer--; + transfer_buffer_size = buffer_size + 1; - /* Do one last check of the target CPort id before filling it in */ + /* + * The data actually transferred will include an indication + * of where the data should be sent. Do one last check of + * the target CPort id before filling it in. + */ if (dest_cport_id == CPORT_ID_BAD) { pr_err("request to send inbound data buffer\n"); - return -EINVAL; + return ERR_PTR(-EINVAL); } if (dest_cport_id > (u16)U8_MAX) { pr_err("dest_cport_id (%hd) is out of range for ES1\n", dest_cport_id); - return -EINVAL; + return ERR_PTR(-EINVAL); } - *buffer = dest_cport_id; + /* OK, the destination is fine; record it in the transfer buffer */ + *transfer_buffer = dest_cport_id; /* Find a free urb */ urb = next_free_urb(es1, gfp_mask); if (!urb) - return -ENOMEM; - - gbuf->hcd_data = urb; + return ERR_PTR(-ENOMEM); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), - buffer, gbuf->transfer_buffer_length + 1, + transfer_buffer, transfer_buffer_size, cport_out_callback, hd); retval = usb_submit_urb(urb, gfp_mask); - return retval; + if (retval) { + pr_err("error %d submitting URB\n", retval); + return ERR_PTR(retval); + } + + return urb; } static void buffer_cancel(void *cookie) @@ -242,7 +260,7 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .buffer_alloc = buffer_alloc, .buffer_free = buffer_free, - .submit_gbuf = submit_gbuf, + .buffer_send = buffer_send, .buffer_cancel = buffer_cancel, .submit_svc = submit_svc, }; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index a9b2b459d7ad..4ac7376fe815 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -81,7 +81,8 @@ struct greybus_host_driver { void *(*buffer_alloc)(unsigned int size, gfp_t gfp_mask); void (*buffer_free)(void *buffer); - int (*submit_gbuf)(struct gbuf *gbuf, gfp_t gfp_mask); + void *(*buffer_send)(struct greybus_host_device *hd, u16 dest_cport_id, + void *buffer, size_t buffer_size, gfp_t gfp_mask); void (*buffer_cancel)(void *cookie); int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 26c9dd688cc3..33cc4145db3c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -106,8 +106,16 @@ gb_pending_operation_find(struct gb_connection *connection, u16 id) static int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) { gbuf->status = -EINPROGRESS; - - return gbuf->hd->driver->submit_gbuf(gbuf, gfp_mask); + gbuf->hcd_data = gbuf->hd->driver->buffer_send(gbuf->hd, + gbuf->dest_cport_id, gbuf->transfer_buffer, + gbuf->transfer_buffer_length, gfp_mask); + if (IS_ERR(gbuf->hcd_data)) { + gbuf->status = PTR_ERR(gbuf->hcd_data); + gbuf->hcd_data = NULL; + + return gbuf->status; + } + return 0; } static void greybus_kill_gbuf(struct gbuf *gbuf) -- cgit v1.2.3-59-g8ed1b From 6a70736aca05d4c8acd80f30bf485dd785ae1a2b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:47 -0600 Subject: greybus: rework message initialization Rework gb_opreation_message_init() so it doesn't use a struct gbuf local variable. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 33cc4145db3c..57694e03c187 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -228,7 +228,6 @@ static int gb_operation_message_init(struct gb_operation *operation, struct greybus_host_device *hd = connection->hd; struct gb_message *message; struct gb_operation_msg_hdr *header; - struct gbuf *gbuf; gfp_t gfp_flags = request && !outbound ? GFP_ATOMIC : GFP_KERNEL; u16 dest_cport_id; @@ -242,23 +241,22 @@ static int gb_operation_message_init(struct gb_operation *operation, message = &operation->response; type |= GB_OPERATION_TYPE_RESPONSE; } - gbuf = &message->gbuf; if (outbound) dest_cport_id = connection->interface_cport_id; else dest_cport_id = CPORT_ID_BAD; - gbuf->transfer_buffer = hd->driver->buffer_alloc(size, gfp_flags); - if (!gbuf->transfer_buffer) + message->gbuf.transfer_buffer = hd->driver->buffer_alloc(size, gfp_flags); + if (!message->gbuf.transfer_buffer) return -ENOMEM; - gbuf->transfer_buffer_length = size; - gbuf->hd = hd; - gbuf->dest_cport_id = dest_cport_id; - gbuf->status = -EBADR; /* Initial value--means "never set" */ + message->gbuf.transfer_buffer_length = size; + message->gbuf.hd = hd; + message->gbuf.dest_cport_id = dest_cport_id; + message->gbuf.status = -EBADR; /* Initial value--means "never set" */ /* Fill in the header structure */ - header = (struct gb_operation_msg_hdr *)gbuf->transfer_buffer; + header = message->gbuf.transfer_buffer; header->size = cpu_to_le16(size); header->id = 0; /* Filled in when submitted */ header->type = type; -- cgit v1.2.3-59-g8ed1b From 002fe66a7d8bdbea058025a5804f5e0a375226da Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:48 -0600 Subject: greybus: send messages, not gbufs Rework greybus_submit_gbuf() to be oriented toward an operation message rather than a gbuf, and rename it accordingly. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 57694e03c187..d22b9275ba96 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -103,17 +103,21 @@ gb_pending_operation_find(struct gb_connection *connection, u16 id) return found ? operation : NULL; } -static int greybus_submit_gbuf(struct gbuf *gbuf, gfp_t gfp_mask) +static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) { - gbuf->status = -EINPROGRESS; - gbuf->hcd_data = gbuf->hd->driver->buffer_send(gbuf->hd, - gbuf->dest_cport_id, gbuf->transfer_buffer, - gbuf->transfer_buffer_length, gfp_mask); - if (IS_ERR(gbuf->hcd_data)) { - gbuf->status = PTR_ERR(gbuf->hcd_data); - gbuf->hcd_data = NULL; - - return gbuf->status; + struct greybus_host_device *hd = message->gbuf.hd; + + message->gbuf.status = -EINPROGRESS; + message->gbuf.hcd_data = hd->driver->buffer_send(hd, + message->gbuf.dest_cport_id, + message->gbuf.transfer_buffer, + message->gbuf.transfer_buffer_length, + gfp_mask); + if (IS_ERR(message->gbuf.hcd_data)) { + message->gbuf.status = PTR_ERR(message->gbuf.hcd_data); + message->gbuf.hcd_data = NULL; + + return message->gbuf.status; } return 0; } @@ -390,7 +394,7 @@ int gb_operation_request_send(struct gb_operation *operation, */ operation->callback = callback; gb_pending_operation_insert(operation); - ret = greybus_submit_gbuf(&operation->request.gbuf, GFP_KERNEL); + ret = gb_message_send(&operation->request, GFP_KERNEL); if (ret) return ret; -- cgit v1.2.3-59-g8ed1b From 35b1342bb040a1e12d82b46ae296f660684a2d23 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:49 -0600 Subject: greybus: cancel messages, not gbufs Rework greybus_kill_gbuf() to be oriented toward an operation message rather than a gbuf, and rename it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d22b9275ba96..5d5cce68680e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -122,12 +122,12 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) return 0; } -static void greybus_kill_gbuf(struct gbuf *gbuf) +static void gb_message_cancel(struct gb_message *message) { - if (gbuf->status != -EINPROGRESS) + if (message->gbuf.status != -EINPROGRESS) return; - gbuf->hd->driver->buffer_cancel(gbuf->hcd_data); + message->gbuf.hd->driver->buffer_cancel(message->gbuf.hcd_data); } /* @@ -152,7 +152,7 @@ int gb_operation_wait(struct gb_operation *operation) ret = wait_for_completion_interruptible(&operation->completion); /* If interrupted, cancel the in-flight buffer */ if (ret < 0) - greybus_kill_gbuf(&operation->request.gbuf); + gb_message_cancel(&operation->request); return ret; } @@ -489,9 +489,9 @@ void gb_connection_operation_recv(struct gb_connection *connection, void gb_operation_cancel(struct gb_operation *operation) { operation->canceled = true; - greybus_kill_gbuf(&operation->request.gbuf); + gb_message_cancel(&operation->request); if (operation->response.gbuf.transfer_buffer) - greybus_kill_gbuf(&operation->response.gbuf); + gb_message_cancel(&operation->response); } int gb_operation_init(void) -- cgit v1.2.3-59-g8ed1b From 61089e89e50ba10592670518c0f5611c33d64f39 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:50 -0600 Subject: greybus: rework receve handling Rework gb_connection_operation_recv() to be more oriented toward an operation message, and to no longer use a struct gbuf local variable. Rename it to be a little more wieldy. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/operation.c | 12 ++++++------ drivers/staging/greybus/operation.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 258d96cdba67..584f49164261 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -40,7 +40,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, "nonexistent connection (%zu bytes dropped)\n", length); return; } - gb_connection_operation_recv(connection, data, length); + gb_connection_recv(connection, data, length); } EXPORT_SYMBOL_GPL(greybus_cport_in); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5d5cce68680e..254864effe27 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -431,12 +431,12 @@ int gb_operation_response_send(struct gb_operation *operation) * data into the buffer and do remaining handling via a work queue. * */ -void gb_connection_operation_recv(struct gb_connection *connection, +void gb_connection_recv(struct gb_connection *connection, void *data, size_t size) { struct gb_operation_msg_hdr *header; struct gb_operation *operation; - struct gbuf *gbuf; + struct gb_message *message; u16 msg_size; if (connection->state != GB_CONNECTION_STATE_ENABLED) @@ -459,8 +459,8 @@ void gb_connection_operation_recv(struct gb_connection *connection, } cancel_delayed_work(&operation->timeout_work); gb_pending_operation_remove(operation); - gbuf = &operation->response.gbuf; - if (size > gbuf->transfer_buffer_length) { + message = &operation->response; + if (size > message->gbuf.transfer_buffer_length) { operation->result = GB_OP_OVERFLOW; gb_connection_err(connection, "recv buffer too small"); return; @@ -474,10 +474,10 @@ void gb_connection_operation_recv(struct gb_connection *connection, gb_connection_err(connection, "can't create operation"); return; } - gbuf = &operation->request.gbuf; + message = &operation->request; } - memcpy(gbuf->transfer_buffer, data, msg_size); + memcpy(message->gbuf.transfer_buffer, data, msg_size); /* The rest will be handled in work queue context */ queue_work(gb_operation_recv_workqueue, &operation->recv_work); diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index f43531dbcf33..a9d4b8a1adc3 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -87,7 +87,7 @@ struct gb_operation { struct list_head links; /* connection->{operations,pending} */ }; -void gb_connection_operation_recv(struct gb_connection *connection, +void gb_connection_recv(struct gb_connection *connection, void *data, size_t size); struct gb_operation *gb_operation_create(struct gb_connection *connection, -- cgit v1.2.3-59-g8ed1b From e238e641ee79db947f1f1222204ae12258061d94 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:51 -0600 Subject: greybus: kill the last gbuf remnants All the code has now been adjusted such that we can do away with the old gbuf structure. Three unused references remained in "greybus.h", so those are deleted. Other than that most of the changes were done by simple global substitution. The gb_message structure incorporates the fields that were previously found its embedded gbuf structure. A few names have been changed in the process: gbuf->transfer_buffer message->buffer gbuf->transfer_buffer_size message->buffer_size gbuf->hcd_data; message->cookie Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 3 -- drivers/staging/greybus/operation.c | 58 ++++++++++++++++++------------------- drivers/staging/greybus/operation.h | 20 +++++-------- 3 files changed, 37 insertions(+), 44 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4ac7376fe815..3df2b5a60d80 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -68,7 +68,6 @@ struct greybus_host_device; struct svc_msg; -struct gbuf; /* Buffers allocated from the host driver will be aligned to this multiple */ #define GB_BUFFER_ALIGN sizeof(u32) @@ -156,8 +155,6 @@ int gb_ap_init(void); void gb_ap_exit(void); int gb_debugfs_init(void); void gb_debugfs_cleanup(void); -int gb_gbuf_init(void); -void gb_gbuf_exit(void); extern struct bus_type greybus_bus_type; extern const struct attribute_group *greybus_module_groups[]; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 254864effe27..6c082cc16457 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -72,7 +72,7 @@ static void gb_pending_operation_insert(struct gb_operation *operation) spin_unlock_irq(&gb_operations_lock); /* Store the operation id in the request header */ - header = operation->request.gbuf.transfer_buffer; + header = operation->request.buffer; header->id = cpu_to_le16(operation->id); } @@ -105,29 +105,29 @@ gb_pending_operation_find(struct gb_connection *connection, u16 id) static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) { - struct greybus_host_device *hd = message->gbuf.hd; + struct greybus_host_device *hd = message->hd; - message->gbuf.status = -EINPROGRESS; - message->gbuf.hcd_data = hd->driver->buffer_send(hd, - message->gbuf.dest_cport_id, - message->gbuf.transfer_buffer, - message->gbuf.transfer_buffer_length, + message->status = -EINPROGRESS; + message->cookie = hd->driver->buffer_send(hd, + message->dest_cport_id, + message->buffer, + message->buffer_size, gfp_mask); - if (IS_ERR(message->gbuf.hcd_data)) { - message->gbuf.status = PTR_ERR(message->gbuf.hcd_data); - message->gbuf.hcd_data = NULL; + if (IS_ERR(message->cookie)) { + message->status = PTR_ERR(message->cookie); + message->cookie = NULL; - return message->gbuf.status; + return message->status; } return 0; } static void gb_message_cancel(struct gb_message *message) { - if (message->gbuf.status != -EINPROGRESS) + if (message->status != -EINPROGRESS) return; - message->gbuf.hd->driver->buffer_cancel(message->gbuf.hcd_data); + message->hd->driver->buffer_cancel(message->cookie); } /* @@ -162,7 +162,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) struct gb_protocol *protocol = operation->connection->protocol; struct gb_operation_msg_hdr *header; - header = operation->request.gbuf.transfer_buffer; + header = operation->request.buffer; /* * If the protocol has no incoming request handler, report @@ -192,7 +192,7 @@ static void gb_operation_recv_work(struct work_struct *recv_work) bool incoming_request; operation = container_of(recv_work, struct gb_operation, recv_work); - incoming_request = operation->response.gbuf.transfer_buffer == NULL; + incoming_request = operation->response.buffer == NULL; if (incoming_request) gb_operation_request_handle(operation); gb_operation_complete(operation); @@ -251,16 +251,16 @@ static int gb_operation_message_init(struct gb_operation *operation, else dest_cport_id = CPORT_ID_BAD; - message->gbuf.transfer_buffer = hd->driver->buffer_alloc(size, gfp_flags); - if (!message->gbuf.transfer_buffer) + message->buffer = hd->driver->buffer_alloc(size, gfp_flags); + if (!message->buffer) return -ENOMEM; - message->gbuf.transfer_buffer_length = size; - message->gbuf.hd = hd; - message->gbuf.dest_cport_id = dest_cport_id; - message->gbuf.status = -EBADR; /* Initial value--means "never set" */ + message->buffer_size = size; + message->hd = hd; + message->dest_cport_id = dest_cport_id; + message->status = -EBADR; /* Initial value--means "never set" */ /* Fill in the header structure */ - header = message->gbuf.transfer_buffer; + header = message->buffer; header->size = cpu_to_le16(size); header->id = 0; /* Filled in when submitted */ header->type = type; @@ -275,9 +275,9 @@ static void gb_operation_message_exit(struct gb_message *message) { message->operation = NULL; message->payload = NULL; - message->gbuf.hd->driver->buffer_free(message->gbuf.transfer_buffer); - message->gbuf.transfer_buffer = NULL; - message->gbuf.transfer_buffer_length = 0; + message->hd->driver->buffer_free(message->buffer); + message->buffer = NULL; + message->buffer_size = 0; } /* @@ -390,7 +390,7 @@ int gb_operation_request_send(struct gb_operation *operation, * XXX * I think the order of operations is going to be * significant, and if so, we may need a mutex to surround - * setting the operation id and submitting the gbuf. + * setting the operation id and submitting the buffer. */ operation->callback = callback; gb_pending_operation_insert(operation); @@ -460,7 +460,7 @@ void gb_connection_recv(struct gb_connection *connection, cancel_delayed_work(&operation->timeout_work); gb_pending_operation_remove(operation); message = &operation->response; - if (size > message->gbuf.transfer_buffer_length) { + if (size > message->buffer_size) { operation->result = GB_OP_OVERFLOW; gb_connection_err(connection, "recv buffer too small"); return; @@ -477,7 +477,7 @@ void gb_connection_recv(struct gb_connection *connection, message = &operation->request; } - memcpy(message->gbuf.transfer_buffer, data, msg_size); + memcpy(message->buffer, data, msg_size); /* The rest will be handled in work queue context */ queue_work(gb_operation_recv_workqueue, &operation->recv_work); @@ -490,7 +490,7 @@ void gb_operation_cancel(struct gb_operation *operation) { operation->canceled = true; gb_message_cancel(&operation->request); - if (operation->response.gbuf.transfer_buffer) + if (operation->response.buffer) gb_message_cancel(&operation->response); } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index a9d4b8a1adc3..2fcb181749a9 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -24,21 +24,17 @@ enum gb_operation_status { GB_OP_TIMEOUT = 0xff, }; -struct gbuf { - struct greybus_host_device *hd; - u16 dest_cport_id; /* Destination CPort id */ - int status; - - void *transfer_buffer; - u32 transfer_buffer_length; - - void *hcd_data; /* for the HCD to track the gbuf */ -}; - struct gb_message { void *payload; struct gb_operation *operation; - struct gbuf gbuf; + struct greybus_host_device *hd; + u16 dest_cport_id; /* Destination CPort id */ + int status; + + void *buffer; + size_t buffer_size; + + void *cookie; }; /* -- cgit v1.2.3-59-g8ed1b From 3ed67aba9f3b2af83b9b9cf7cd6f7ab25de5acc2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:52 -0600 Subject: greybus: stop storing hd in message The host device pointer doesn't have to be stored in every message. It can be derived by following up the chain of pointers back to the operation's connection. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 16 +++++++++++----- drivers/staging/greybus/operation.h | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 6c082cc16457..23cf745a337a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -105,10 +105,10 @@ gb_pending_operation_find(struct gb_connection *connection, u16 id) static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) { - struct greybus_host_device *hd = message->hd; + struct gb_connection *connection = message->operation->connection; message->status = -EINPROGRESS; - message->cookie = hd->driver->buffer_send(hd, + message->cookie = connection->hd->driver->buffer_send(connection->hd, message->dest_cport_id, message->buffer, message->buffer_size, @@ -124,10 +124,13 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) static void gb_message_cancel(struct gb_message *message) { + struct greybus_host_device *hd; + if (message->status != -EINPROGRESS) return; - message->hd->driver->buffer_cancel(message->cookie); + hd = message->operation->connection->hd; + hd->driver->buffer_cancel(message->cookie); } /* @@ -255,7 +258,6 @@ static int gb_operation_message_init(struct gb_operation *operation, if (!message->buffer) return -ENOMEM; message->buffer_size = size; - message->hd = hd; message->dest_cport_id = dest_cport_id; message->status = -EBADR; /* Initial value--means "never set" */ @@ -273,9 +275,13 @@ static int gb_operation_message_init(struct gb_operation *operation, static void gb_operation_message_exit(struct gb_message *message) { + struct greybus_host_device *hd; + + hd = message->operation->connection->hd; + hd->driver->buffer_free(message->buffer); + message->operation = NULL; message->payload = NULL; - message->hd->driver->buffer_free(message->buffer); message->buffer = NULL; message->buffer_size = 0; } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 2fcb181749a9..5e068ff9f546 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -26,8 +26,8 @@ enum gb_operation_status { struct gb_message { void *payload; + struct gb_operation *operation; - struct greybus_host_device *hd; u16 dest_cport_id; /* Destination CPort id */ int status; -- cgit v1.2.3-59-g8ed1b From 1f764af77c6adb3b4035b8f41b48198f251dc7f8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:53 -0600 Subject: greybus: stop storing dest_cport_id in message We can derive the destination CPort id of any (outbound) message from the connection it's operation is associated with. So we don't need to store that information in every message. As a result, we no longer need to record it at message initialization time. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 10 ++-------- drivers/staging/greybus/operation.h | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 23cf745a337a..705b195dfe01 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -106,10 +106,11 @@ gb_pending_operation_find(struct gb_connection *connection, u16 id) static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) { struct gb_connection *connection = message->operation->connection; + u16 dest_cport_id = connection->interface_cport_id; message->status = -EINPROGRESS; message->cookie = connection->hd->driver->buffer_send(connection->hd, - message->dest_cport_id, + dest_cport_id, message->buffer, message->buffer_size, gfp_mask); @@ -236,7 +237,6 @@ static int gb_operation_message_init(struct gb_operation *operation, struct gb_message *message; struct gb_operation_msg_hdr *header; gfp_t gfp_flags = request && !outbound ? GFP_ATOMIC : GFP_KERNEL; - u16 dest_cport_id; if (size > GB_OPERATION_MESSAGE_SIZE_MAX) return -E2BIG; @@ -249,16 +249,10 @@ static int gb_operation_message_init(struct gb_operation *operation, type |= GB_OPERATION_TYPE_RESPONSE; } - if (outbound) - dest_cport_id = connection->interface_cport_id; - else - dest_cport_id = CPORT_ID_BAD; - message->buffer = hd->driver->buffer_alloc(size, gfp_flags); if (!message->buffer) return -ENOMEM; message->buffer_size = size; - message->dest_cport_id = dest_cport_id; message->status = -EBADR; /* Initial value--means "never set" */ /* Fill in the header structure */ diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 5e068ff9f546..81fd7f70b8ba 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -28,7 +28,6 @@ struct gb_message { void *payload; struct gb_operation *operation; - u16 dest_cport_id; /* Destination CPort id */ int status; void *buffer; -- cgit v1.2.3-59-g8ed1b From de80073a1768b0fb01df0e597225047fd66e8044 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 18 Nov 2014 13:26:54 -0600 Subject: greybus: pass gfp_flags for message allocation The only reason gb_operation_message_init() gets its "outbound" argument is so we can determine what allocation flags to use. Just pass the flags in directly instead. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 705b195dfe01..96f4c689e998 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -230,13 +230,12 @@ static void operation_timeout(struct work_struct *work) */ static int gb_operation_message_init(struct gb_operation *operation, u8 type, size_t size, - bool request, bool outbound) + bool request, gfp_t gfp_flags) { struct gb_connection *connection = operation->connection; struct greybus_host_device *hd = connection->hd; struct gb_message *message; struct gb_operation_msg_hdr *header; - gfp_t gfp_flags = request && !outbound ? GFP_ATOMIC : GFP_KERNEL; if (size > GB_OPERATION_MESSAGE_SIZE_MAX) return -E2BIG; @@ -311,13 +310,13 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, operation->connection = connection; ret = gb_operation_message_init(operation, type, request_size, - true, outgoing); + true, gfp_flags); if (ret) goto err_cache; if (outgoing) { ret = gb_operation_message_init(operation, type, response_size, - false, false); + false, GFP_KERNEL); if (ret) goto err_request; } -- cgit v1.2.3-59-g8ed1b From 42a946688d4b992d176a887638e7943d61a3e8f5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Nov 2014 10:36:23 -0800 Subject: greybus: vibrator-gb.c: fix up incorrect comment s/i2c/vibrator/ Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/vibrator-gb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index b9ec772b2459..9b15ae23c1ba 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -22,7 +22,7 @@ struct gb_vibrator_device { u8 version_minor; }; -/* Version of the Greybus i2c protocol we support */ +/* Version of the Greybus vibrator protocol we support */ #define GB_VIBRATOR_VERSION_MAJOR 0x00 #define GB_VIBRATOR_VERSION_MINOR 0x01 -- cgit v1.2.3-59-g8ed1b From e2dabb7c3173020a7497b18d7430a895f80ba9ce Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 19 Nov 2014 17:24:56 +0530 Subject: greybus: ap: remove extra parameter to convert_ap_message() Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 990e5c0620b9..d68221b7736c 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -249,11 +249,11 @@ static void svc_suspend(struct svc_function_suspend *suspend, dev_err(hd->parent, "Got an suspend message???\n"); } -static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg, - struct greybus_host_device *hd) +static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) { struct svc_msg *svc_msg; struct svc_msg_header *header; + struct greybus_host_device *hd = ap_msg->hd; svc_msg = (struct svc_msg *)ap_msg->data; header = &svc_msg->header; @@ -285,7 +285,7 @@ static void ap_process_event(struct work_struct *work) hd = ap_msg->hd; /* Turn the "raw" data into a real message */ - svc_msg = convert_ap_message(ap_msg, hd); + svc_msg = convert_ap_message(ap_msg); if (!svc_msg) return; -- cgit v1.2.3-59-g8ed1b From a4040ab7e68ee9b1e4750f42c93b49bc53768b0c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 19 Nov 2014 17:24:57 +0530 Subject: greybus: ap: keep all svc function routines together Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index d68221b7736c..ef64068da1b8 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -60,6 +60,24 @@ static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) } +int svc_set_route_send(struct gb_interface *interface, + struct greybus_host_device *hd) +{ + struct svc_msg *svc_msg; + + svc_msg = svc_msg_alloc(SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT); + if (!svc_msg) + return -ENOMEM; + + svc_msg->header.function_id = SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT; + svc_msg->header.message_type = SVC_MSG_DATA; + svc_msg->header.payload_length = + cpu_to_le16(sizeof(struct svc_function_unipro_set_route)); + svc_msg->management.set_route.device_id = interface->device_id; + + return svc_msg_send(svc_msg, hd); +} + static void svc_handshake(struct svc_function_handshake *handshake, int payload_length, struct greybus_host_device *hd) { @@ -104,24 +122,6 @@ static void svc_handshake(struct svc_function_handshake *handshake, (void)svc_msg_send(svc_msg, hd); } -int svc_set_route_send(struct gb_interface *interface, - struct greybus_host_device *hd) -{ - struct svc_msg *svc_msg; - - svc_msg = svc_msg_alloc(SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT); - if (!svc_msg) - return -ENOMEM; - - svc_msg->header.function_id = SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT; - svc_msg->header.message_type = SVC_MSG_DATA; - svc_msg->header.payload_length = - cpu_to_le16(sizeof(struct svc_function_unipro_set_route)); - svc_msg->management.set_route.device_id = interface->device_id; - - return svc_msg_send(svc_msg, hd); -} - static void svc_management(struct svc_function_unipro_management *management, int payload_length, struct greybus_host_device *hd) { -- cgit v1.2.3-59-g8ed1b From 64e69291d9d6c14c133910bb0d8b5072fbd4397d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 19 Nov 2014 17:24:58 +0530 Subject: greybus: Pass '*ptr' to sizeof() wherever possible Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 8 ++++---- drivers/staging/greybus/i2c-gb.c | 4 ++-- drivers/staging/greybus/manifest.c | 2 +- drivers/staging/greybus/sdio-gb.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index ef64068da1b8..a3bf146e5562 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -83,7 +83,7 @@ static void svc_handshake(struct svc_function_handshake *handshake, { struct svc_msg *svc_msg; - if (payload_length != sizeof(struct svc_function_handshake)) { + if (payload_length != sizeof(*handshake)) { dev_err(hd->parent, "Illegal size of svc handshake message %d\n", payload_length); @@ -114,7 +114,7 @@ static void svc_handshake(struct svc_function_handshake *handshake, svc_msg->header.function_id = SVC_FUNCTION_HANDSHAKE; svc_msg->header.message_type = SVC_MSG_DATA; svc_msg->header.payload_length = - cpu_to_le16(sizeof(struct svc_function_handshake)); + cpu_to_le16(sizeof(*handshake)); svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR; svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR; svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; @@ -128,7 +128,7 @@ static void svc_management(struct svc_function_unipro_management *management, struct gb_module *module; int ret; - if (payload_length != sizeof(struct svc_function_unipro_management)) { + if (payload_length != sizeof(*management)) { dev_err(hd->parent, "Illegal size of svc management message %d\n", payload_length); @@ -221,7 +221,7 @@ static void svc_power(struct svc_function_power *power, * big, we can just check the union of the whole structure to validate * the size of this message. */ - if (payload_length != sizeof(struct svc_function_power)) { + if (payload_length != sizeof(*power)) { dev_err(hd->parent, "Illegal size of svc power message %d\n", payload_length); diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 3374173b012a..c810c429d529 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -311,8 +311,8 @@ gb_i2c_transfer_request(struct gb_connection *connection, else data_out_size += (u32)msg->len; - request_size = sizeof(struct gb_i2c_transfer_request); - request_size += msg_count * sizeof(struct gb_i2c_transfer_op); + request_size = sizeof(*request); + request_size += msg_count * sizeof(*op); request_size += data_out_size; /* Response consists only of incoming data */ diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 9014611c6a7a..37540350cb75 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -81,7 +81,7 @@ static int identify_descriptor(struct greybus_descriptor *desc, size_t size) } break; case GREYBUS_TYPE_STRING: - expected_size = sizeof(struct greybus_descriptor_header); + expected_size = sizeof(*desc_header); expected_size += sizeof(struct greybus_descriptor_string); expected_size += (size_t)desc->string.length; if (desc_size < expected_size) { diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 4775ed0c7bea..3f1136566491 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -46,7 +46,7 @@ static int gb_sdio_connection_init(struct gb_connection *connection) struct mmc_host *mmc; struct gb_sdio_host *host; - mmc = mmc_alloc_host(sizeof(struct gb_sdio_host), &connection->dev); + mmc = mmc_alloc_host(sizeof(*host), &connection->dev); if (!mmc) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From c002b0ec60c5d7acc8cc7af88ed6845f3e656fc7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 19 Nov 2014 17:24:59 +0530 Subject: greybus: ap: function_id is already set by svc_msg_alloc() Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index a3bf146e5562..35367174afb4 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -69,7 +69,6 @@ int svc_set_route_send(struct gb_interface *interface, if (!svc_msg) return -ENOMEM; - svc_msg->header.function_id = SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT; svc_msg->header.message_type = SVC_MSG_DATA; svc_msg->header.payload_length = cpu_to_le16(sizeof(struct svc_function_unipro_set_route)); @@ -111,7 +110,6 @@ static void svc_handshake(struct svc_function_handshake *handshake, if (!svc_msg) return; - svc_msg->header.function_id = SVC_FUNCTION_HANDSHAKE; svc_msg->header.message_type = SVC_MSG_DATA; svc_msg->header.payload_length = cpu_to_le16(sizeof(*handshake)); -- cgit v1.2.3-59-g8ed1b From 2292bac5e76846387152d1d20b10cb79f8c1b14b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 12:27:13 -0600 Subject: greybus: explicitly mark cookies as opaque Use simple macros to mark the conversion of an URB pointer into an opaque cookie value (and vice-versa). We scramble some bits, but the main point is to make it explicit where we're returning and using opaque values. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 3404dc59a151..658506df89e5 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -16,6 +16,13 @@ #include "svc_msg.h" #include "kernel_ver.h" +/* + * Macros for making pointers explicitly opaque, such that the result + * isn't valid but also can't be mistaken for an ERR_PTR() value. + */ +#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) +#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) + /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE PAGE_SIZE @@ -241,12 +248,12 @@ static void *buffer_send(struct greybus_host_device *hd, u16 dest_cport_id, return ERR_PTR(retval); } - return urb; + return conceal_urb(urb); } static void buffer_cancel(void *cookie) { - struct urb *urb = cookie; + struct urb *urb = reveal_urb(cookie); /* * We really should be defensive and track all outstanding -- cgit v1.2.3-59-g8ed1b From c41f1651c4962e56548a5b580e60f66a724357e7 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 12:27:14 -0600 Subject: greybus: tidy up svc_in_callback() and cport_in_callback() The only use of local variable "es1" in in svc_in_callback() and cport_in_callback() is to get at its hd field. But we already have that, so we can get rid of that local variable. Also, rename the "cport" variable "cport_id" in cport_in_callback() is to match the convention used elsewhere, and make it the proper u16 type. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 658506df89e5..7745b81c893a 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -346,7 +346,6 @@ static void ap_disconnect(struct usb_interface *interface) static void svc_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; - struct es1_ap_dev *es1 = hd_to_es1(hd); struct device *dev = &urb->dev->dev; int status = check_urb_status(urb); int retval; @@ -361,7 +360,7 @@ static void svc_in_callback(struct urb *urb) /* We have a message, create a new message structure, add it to the * list, and wake up our thread that will process the messages. */ - greybus_svc_in(es1->hd, urb->transfer_buffer, urb->actual_length); + greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); exit: /* resubmit the urb to get more messages */ @@ -373,11 +372,10 @@ exit: static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; - struct es1_ap_dev *es1 = hd_to_es1(hd); struct device *dev = &urb->dev->dev; int status = check_urb_status(urb); int retval; - u8 cport; + u16 cport_id; u8 *data; if (status) { @@ -398,11 +396,11 @@ static void cport_in_callback(struct urb *urb) * the stream is "real" data */ data = urb->transfer_buffer; - cport = data[0]; + cport_id = (u16)data[0]; data = &data[1]; /* Pass this data to the greybus core */ - greybus_cport_in(es1->hd, cport, data, urb->actual_length - 1); + greybus_cport_in(hd, cport_id, data, urb->actual_length - 1); exit: /* put our urb back in the request pool */ -- cgit v1.2.3-59-g8ed1b From 6014718d4db9d48a980d70c66a7617293db633e6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 12:27:15 -0600 Subject: greybus: get rid of message status We (sort of) maintain the status of each message, but we shouldn't need to. Right now we're not using it consistently in any case. If a message fails to send, the caller will know to destroy the operation that contained it. If a message has been sent (i.e., handed to the host device layer) it'll have a non-null cookie pointer. If a does complete in error, we can update the status of the operation that contains it. That isn't happening right now but it will soon. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 17 +++++++++-------- drivers/staging/greybus/operation.h | 1 - 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 96f4c689e998..05a61d87dabe 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -107,28 +107,30 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) { struct gb_connection *connection = message->operation->connection; u16 dest_cport_id = connection->interface_cport_id; + int ret = 0; - message->status = -EINPROGRESS; message->cookie = connection->hd->driver->buffer_send(connection->hd, dest_cport_id, message->buffer, message->buffer_size, gfp_mask); if (IS_ERR(message->cookie)) { - message->status = PTR_ERR(message->cookie); + ret = PTR_ERR(message->cookie); message->cookie = NULL; - - return message->status; } - return 0; + return ret; } +/* + * Cancel a message whose buffer we have passed to the host device + * layer to be sent. + */ static void gb_message_cancel(struct gb_message *message) { struct greybus_host_device *hd; - if (message->status != -EINPROGRESS) - return; + if (!message->cookie) + return; /* Don't bother if the message isn't in flight */ hd = message->operation->connection->hd; hd->driver->buffer_cancel(message->cookie); @@ -252,7 +254,6 @@ static int gb_operation_message_init(struct gb_operation *operation, if (!message->buffer) return -ENOMEM; message->buffer_size = size; - message->status = -EBADR; /* Initial value--means "never set" */ /* Fill in the header structure */ header = message->buffer; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 81fd7f70b8ba..80ee158d74f7 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -28,7 +28,6 @@ struct gb_message { void *payload; struct gb_operation *operation; - int status; void *buffer; size_t buffer_size; -- cgit v1.2.3-59-g8ed1b From 8fc71a73363f742c70ef5d570b795583124efd29 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 12:27:16 -0600 Subject: greybus: use "operation_id" for certain values A message header contains a field "id" that is an operation id. Since the field doesn't identify the message itself, rename this field so it's clearer what it's referring to. Similarly gb_pending_operation_find() has a parameter "id" that is really an operation id, so rename that as well. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 05a61d87dabe..bd50c6e2ed2e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -48,9 +48,9 @@ static struct workqueue_struct *gb_operation_recv_workqueue; * header, and is 64-bit aligned. */ struct gb_operation_msg_hdr { - __le16 size; /* Size in bytes of header + payload */ - __le16 id; /* Operation unique id */ - __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ + __le16 size; /* Size in bytes of header + payload */ + __le16 operation_id; /* Operation unique id */ + __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ /* 3 bytes pad, must be zero (ignore when read) */ } __aligned(sizeof(u64)); @@ -73,7 +73,7 @@ static void gb_pending_operation_insert(struct gb_operation *operation) /* Store the operation id in the request header */ header = operation->request.buffer; - header->id = cpu_to_le16(operation->id); + header->operation_id = cpu_to_le16(operation->id); } static void gb_pending_operation_remove(struct gb_operation *operation) @@ -87,14 +87,14 @@ static void gb_pending_operation_remove(struct gb_operation *operation) } static struct gb_operation * -gb_pending_operation_find(struct gb_connection *connection, u16 id) +gb_pending_operation_find(struct gb_connection *connection, u16 operation_id) { struct gb_operation *operation; bool found = false; spin_lock_irq(&gb_operations_lock); list_for_each_entry(operation, &connection->pending, links) - if (operation->id == id) { + if (operation->id == operation_id) { found = true; break; } @@ -258,7 +258,7 @@ static int gb_operation_message_init(struct gb_operation *operation, /* Fill in the header structure */ header = message->buffer; header->size = cpu_to_le16(size); - header->id = 0; /* Filled in when submitted */ + header->operation_id = 0; /* Filled in when submitted */ header->type = type; message->payload = header + 1; @@ -450,9 +450,9 @@ void gb_connection_recv(struct gb_connection *connection, header = data; msg_size = le16_to_cpu(header->size); if (header->type & GB_OPERATION_TYPE_RESPONSE) { - u16 id = le16_to_cpu(header->id); + u16 operation_id = le16_to_cpu(header->operation_id); - operation = gb_pending_operation_find(connection, id); + operation = gb_pending_operation_find(connection, operation_id); if (!operation) { gb_connection_err(connection, "operation not found"); return; -- cgit v1.2.3-59-g8ed1b From d37b1db13f8b5f3ad27aff5aa487b4ea2a298aa4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 12:27:17 -0600 Subject: greybus: refactor gb_connection_recv() Define two helper functions to break down handling of a received message. One is used to handle receiving an incoming request message, the other for a response message. Three other changes are made: - We verify message size recorded in the message header does not exceed the amount of data that's arriving. - We no longer warn if a request' recorded message size differs from the number of bytes that have arrived. - We now record the operation id for an incoming request. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 122 +++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 44 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index bd50c6e2ed2e..520214bde878 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -418,29 +418,83 @@ int gb_operation_response_send(struct gb_operation *operation) } /* - * Handle data arriving on a connection. As soon as we return, the - * incoming data buffer will be reused, so we need to copy the data - * into one of our own operation message buffers. - * - * If the incoming data is an operation response message, look up - * the operation and copy the incoming data into its response - * buffer. Otherwise allocate a new operation and copy the incoming - * data into its request buffer. + * We've received data on a connection, and it doesn't look like a + * response, so we assume it's a request. * * This is called in interrupt context, so just copy the incoming - * data into the buffer and do remaining handling via a work queue. + * data into the request buffer and handle the rest via workqueue. + */ +void gb_connection_recv_request(struct gb_connection *connection, + u16 operation_id, u8 type, void *data, size_t size) +{ + struct gb_operation *operation; + + operation = gb_operation_create(connection, type, size, 0); + if (!operation) { + gb_connection_err(connection, "can't create operation"); + return; /* XXX Respond with pre-allocated ENOMEM */ + } + operation->id = operation_id; + memcpy(operation->request.buffer, data, size); + + /* The rest will be handled in work queue context */ + queue_work(gb_operation_recv_workqueue, &operation->recv_work); +} + +/* + * We've received data that appears to be an operation response + * message. Look up the operation, and record that we've received + * its repsonse. * + * This is called in interrupt context, so just copy the incoming + * data into the response buffer and handle the rest via workqueue. + */ +static void gb_connection_recv_response(struct gb_connection *connection, + u16 operation_id, void *data, size_t size) +{ + struct gb_operation *operation; + struct gb_message *message; + + operation = gb_pending_operation_find(connection, operation_id); + if (!operation) { + gb_connection_err(connection, "operation not found"); + return; + } + + cancel_delayed_work(&operation->timeout_work); + gb_pending_operation_remove(operation); + + message = &operation->response; + if (size > message->buffer_size) { + operation->result = GB_OP_OVERFLOW; + gb_connection_err(connection, "recv buffer too small"); + return; /* XXX Should still complete operation */ + } + operation->result = GB_OP_SUCCESS; /* XXX Maybe not yet? */ + + memcpy(message->buffer, data, size); + + /* The rest will be handled in work queue context */ + queue_work(gb_operation_recv_workqueue, &operation->recv_work); +} + +/* + * Handle data arriving on a connection. As soon as we return the + * supplied data buffer will be reused (so unless we do something + * with, it's effectively dropped). */ void gb_connection_recv(struct gb_connection *connection, void *data, size_t size) { struct gb_operation_msg_hdr *header; - struct gb_operation *operation; - struct gb_message *message; - u16 msg_size; + size_t msg_size; + u16 operation_id; - if (connection->state != GB_CONNECTION_STATE_ENABLED) + if (connection->state != GB_CONNECTION_STATE_ENABLED) { + gb_connection_err(connection, "dropping %zu received bytes", + size); return; + } if (size < sizeof(*header)) { gb_connection_err(connection, "message too small"); @@ -448,39 +502,19 @@ void gb_connection_recv(struct gb_connection *connection, } header = data; - msg_size = le16_to_cpu(header->size); - if (header->type & GB_OPERATION_TYPE_RESPONSE) { - u16 operation_id = le16_to_cpu(header->operation_id); - - operation = gb_pending_operation_find(connection, operation_id); - if (!operation) { - gb_connection_err(connection, "operation not found"); - return; - } - cancel_delayed_work(&operation->timeout_work); - gb_pending_operation_remove(operation); - message = &operation->response; - if (size > message->buffer_size) { - operation->result = GB_OP_OVERFLOW; - gb_connection_err(connection, "recv buffer too small"); - return; - } - operation->result = GB_OP_SUCCESS; - } else { - WARN_ON(msg_size != size); - operation = gb_operation_create(connection, header->type, - msg_size, 0); - if (!operation) { - gb_connection_err(connection, "can't create operation"); - return; - } - message = &operation->request; + msg_size = (size_t)le16_to_cpu(header->size); + if (msg_size > size) { + gb_connection_err(connection, "incomplete message"); + return; /* XXX Should still complete operation */ } - memcpy(message->buffer, data, msg_size); - - /* The rest will be handled in work queue context */ - queue_work(gb_operation_recv_workqueue, &operation->recv_work); + operation_id = le16_to_cpu(header->operation_id); + if (header->type & GB_OPERATION_TYPE_RESPONSE) + gb_connection_recv_response(connection, operation_id, + data, msg_size); + else + gb_connection_recv_request(connection, operation_id, + header->type, data, msg_size); } /* -- cgit v1.2.3-59-g8ed1b From 8abf4148030d42825d4f76f4a3cf734dbc2bcd2b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 19 Nov 2014 17:25:00 +0530 Subject: greybus: operation: don't complete operation twice Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 520214bde878..a2b27aeefb7f 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -176,14 +176,12 @@ static void gb_operation_request_handle(struct gb_operation *operation) */ if (protocol->request_recv) { protocol->request_recv(header->type, operation); - goto out; + return; } gb_connection_err(operation->connection, "unexpected incoming request type 0x%02hhx\n", header->type); operation->result = GB_OP_PROTOCOL_BAD; -out: - gb_operation_complete(operation); } /* -- cgit v1.2.3-59-g8ed1b From 0bbfe04cd945df79c52924284b2242508fef4e47 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 16:29:14 -0600 Subject: greybus: fix battery_operation() This patch fixes some problems with the battery protocol driver. First, when gb_operation_create() is called, it creates buffers of the requested sizes to hold the operation request and response messages. There is therefore no reason to allocate a local response buffer. By the time the (synchronous) gb_operation_request_send() call returns, the operation response buffer will have been filled in. (In addition, the content of local_response was not being filled before its contents were used...) Next, all the message structures are misnamed. The structures that are defined are all the content of operation response messages (not request messages). So this changes all the types names to properly reflect their role. All the local variables using these types are similarly renamed. I added a new type, gb_generic_battery_response, to be used for casting the fake_response used in battery_operation(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 92 ++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 4bd7aed0a27b..101c3de81dc0 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -54,7 +54,7 @@ struct gb_battery_proto_version_response { #define GB_BATTERY_TECH_NiCd 0x0005 #define GB_BATTERY_TECH_LiMn 0x0006 -struct gb_battery_technology_request { +struct gb_battery_technology_response { __u8 status; __le32 technology; }; @@ -66,50 +66,53 @@ struct gb_battery_technology_request { #define GB_BATTERY_STATUS_NOT_CHARGING 0x0003 #define GB_BATTERY_STATUS_FULL 0x0004 -struct gb_battery_status_request { +struct gb_battery_status_response { __u8 status; __le16 battery_status; }; -struct gb_battery_max_voltage_request { +struct gb_battery_max_voltage_response { __u8 status; __le32 max_voltage; }; -struct gb_battery_capacity_request { +struct gb_battery_capacity_response { __u8 status; __le32 capacity; }; -struct gb_battery_temperature_request { +struct gb_battery_temperature_response { __u8 status; __le32 temperature; }; -struct gb_battery_voltage_request { +struct gb_battery_voltage_response { __u8 status; __le32 voltage; }; +/* Generia response structure--prefix for all other responses */ +struct gb_generic_battery_response { + __u8 status; +}; +/* + * None of the battery operation requests have any payload. This + * function implements all of the requests by allowing the caller to + * supply a buffer into which the operation response should be + * copied. + */ static int battery_operation(struct gb_battery *gb, int type, void *response, int response_size) { struct gb_connection *connection = gb->connection; struct gb_operation *operation; - struct gb_battery_technology_request *fake_request; - u8 *local_response; + struct gb_generic_battery_response *fake_response; int ret; - local_response = kmalloc(response_size, GFP_KERNEL); - if (!local_response) - return -ENOMEM; - operation = gb_operation_create(connection, type, 0, response_size); - if (!operation) { - kfree(local_response); + if (!operation) return -ENOMEM; - } /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); @@ -123,18 +126,17 @@ static int battery_operation(struct gb_battery *gb, int type, * layout for where the status is, so cast this to a random request so * we can see the status easier. */ - fake_request = (struct gb_battery_technology_request *)local_response; - if (fake_request->status) { + fake_response = operation->response.payload; + if (fake_response->status) { gb_connection_err(connection, "version response %hhu", - fake_request->status); + fake_response->status); ret = -EIO; } else { - /* Good request, so copy to the caller's buffer */ - memcpy(response, local_response, response_size); + /* Good response, so copy to the caller's buffer */ + memcpy(response, fake_response, response_size); } out: gb_operation_destroy(operation); - kfree(local_response); return ret; } @@ -145,33 +147,33 @@ out: */ static int get_version(struct gb_battery *gb) { - struct gb_battery_proto_version_response version_request; + struct gb_battery_proto_version_response version_response; int retval; retval = battery_operation(gb, GB_BATTERY_TYPE_PROTOCOL_VERSION, - &version_request, sizeof(version_request)); + &version_response, sizeof(version_response)); if (retval) return retval; - if (version_request.major > GB_BATTERY_VERSION_MAJOR) { + if (version_response.major > GB_BATTERY_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", - version_request.major, GB_BATTERY_VERSION_MAJOR); + version_response.major, GB_BATTERY_VERSION_MAJOR); return -ENOTSUPP; } - gb->version_major = version_request.major; - gb->version_minor = version_request.minor; + gb->version_major = version_response.major; + gb->version_minor = version_response.minor; return 0; } static int get_tech(struct gb_battery *gb) { - struct gb_battery_technology_request tech_request; + struct gb_battery_technology_response tech_response; u32 technology; int retval; retval = battery_operation(gb, GB_BATTERY_TYPE_TECHNOLOGY, - &tech_request, sizeof(tech_request)); + &tech_response, sizeof(tech_response)); if (retval) return retval; @@ -180,7 +182,7 @@ static int get_tech(struct gb_battery *gb) * "identical" which should allow gcc to optomize the code away to * nothing. */ - technology = le32_to_cpu(tech_request.technology); + technology = le32_to_cpu(tech_response.technology); switch (technology) { case GB_BATTERY_TECH_NiMH: technology = POWER_SUPPLY_TECHNOLOGY_NiMH; @@ -210,12 +212,12 @@ static int get_tech(struct gb_battery *gb) static int get_status(struct gb_battery *gb) { - struct gb_battery_status_request status_request; + struct gb_battery_status_response status_response; u16 battery_status; int retval; retval = battery_operation(gb, GB_BATTERY_TYPE_STATUS, - &status_request, sizeof(status_request)); + &status_response, sizeof(status_response)); if (retval) return retval; @@ -224,7 +226,7 @@ static int get_status(struct gb_battery *gb) * "identical" which should allow gcc to optomize the code away to * nothing. */ - battery_status = le16_to_cpu(status_request.battery_status); + battery_status = le16_to_cpu(status_response.battery_status); switch (battery_status) { case GB_BATTERY_STATUS_CHARGING: battery_status = POWER_SUPPLY_STATUS_CHARGING; @@ -248,61 +250,61 @@ static int get_status(struct gb_battery *gb) static int get_max_voltage(struct gb_battery *gb) { - struct gb_battery_max_voltage_request volt_request; + struct gb_battery_max_voltage_response volt_response; u32 max_voltage; int retval; retval = battery_operation(gb, GB_BATTERY_TYPE_MAX_VOLTAGE, - &volt_request, sizeof(volt_request)); + &volt_response, sizeof(volt_response)); if (retval) return retval; - max_voltage = le32_to_cpu(volt_request.max_voltage); + max_voltage = le32_to_cpu(volt_response.max_voltage); return max_voltage; } static int get_capacity(struct gb_battery *gb) { - struct gb_battery_capacity_request capacity_request; + struct gb_battery_capacity_response capacity_response; u32 capacity; int retval; retval = battery_operation(gb, GB_BATTERY_TYPE_CAPACITY, - &capacity_request, sizeof(capacity_request)); + &capacity_response, sizeof(capacity_response)); if (retval) return retval; - capacity = le32_to_cpu(capacity_request.capacity); + capacity = le32_to_cpu(capacity_response.capacity); return capacity; } static int get_temp(struct gb_battery *gb) { - struct gb_battery_temperature_request temp_request; + struct gb_battery_temperature_response temp_response; u32 temperature; int retval; retval = battery_operation(gb, GB_BATTERY_TYPE_TEMPERATURE, - &temp_request, sizeof(temp_request)); + &temp_response, sizeof(temp_response)); if (retval) return retval; - temperature = le32_to_cpu(temp_request.temperature); + temperature = le32_to_cpu(temp_response.temperature); return temperature; } static int get_voltage(struct gb_battery *gb) { - struct gb_battery_voltage_request voltage_request; + struct gb_battery_voltage_response voltage_response; u32 voltage; int retval; retval = battery_operation(gb, GB_BATTERY_TYPE_VOLTAGE, - &voltage_request, sizeof(voltage_request)); + &voltage_response, sizeof(voltage_response)); if (retval) return retval; - voltage = le32_to_cpu(voltage_request.voltage); + voltage = le32_to_cpu(voltage_response.voltage); return voltage; } -- cgit v1.2.3-59-g8ed1b From bc091356ccbf2e08b2a8818efce043669f131e59 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 16:29:15 -0600 Subject: greybus: fix uart request_operation() This fixes a problem similar to what was found in the battery protcool driver code. There's no need to allocate a local buffer, that already set up by gb_operation_create(). Just use that instead. Change a few variable names to reflect that they hold response messages, not request messages. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index a8c342eb09d3..d01956acdedc 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -144,19 +144,12 @@ static int request_operation(struct gb_connection *connection, int type, void *response, int response_size) { struct gb_operation *operation; - struct gb_uart_simple_response *fake_request; - u8 *local_response; + struct gb_uart_simple_response *fake_response; int ret; - local_response = kmalloc(response_size, GFP_KERNEL); - if (!local_response) - return -ENOMEM; - operation = gb_operation_create(connection, type, 0, response_size); - if (!operation) { - kfree(local_response); + if (!operation) return -ENOMEM; - } /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); @@ -170,19 +163,18 @@ static int request_operation(struct gb_connection *connection, int type, * layout for where the status is, so cast this to a random request so * we can see the status easier. */ - fake_request = (struct gb_uart_simple_response *)local_response; - if (fake_request->status) { + fake_response = operation->response.payload; + if (fake_response->status) { gb_connection_err(connection, "response %hhu", - fake_request->status); + fake_response->status); ret = -EIO; } else { /* Good request, so copy to the caller's buffer */ if (response_size && response) - memcpy(response, local_response, response_size); + memcpy(response, fake_response, response_size); } out: gb_operation_destroy(operation); - kfree(local_response); return ret; } @@ -193,23 +185,23 @@ out: */ static int get_version(struct gb_tty *tty) { - struct gb_uart_proto_version_response version_request; + struct gb_uart_proto_version_response version_response; int retval; retval = request_operation(tty->connection, GB_UART_REQ_PROTOCOL_VERSION, - &version_request, sizeof(version_request)); + &version_response, sizeof(version_response)); if (retval) return retval; - if (version_request.major > GB_UART_VERSION_MAJOR) { + if (version_response.major > GB_UART_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", - version_request.major, GB_UART_VERSION_MAJOR); + version_response.major, GB_UART_VERSION_MAJOR); return -ENOTSUPP; } - tty->version_major = version_request.major; - tty->version_minor = version_request.minor; + tty->version_major = version_response.major; + tty->version_minor = version_response.minor; return 0; } -- cgit v1.2.3-59-g8ed1b From d122382f88e0a2849e758acb46140d3085b3dc61 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 17:05:14 -0600 Subject: greybus: fix vibrator request_operation() And this fixes a problem similar the last two, this time found in the vibrator protcool driver code. Change a variable name in get_version() to reflect that it holds a response message, not a request message. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/vibrator-gb.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index 9b15ae23c1ba..3e46fa665dec 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -51,19 +51,12 @@ static int request_operation(struct gb_connection *connection, int type, void *response, int response_size) { struct gb_operation *operation; - struct gb_vibrator_simple_response *fake_request; - u8 *local_response; + struct gb_vibrator_simple_response *fake_response; int ret; - local_response = kmalloc(response_size, GFP_KERNEL); - if (!local_response) - return -ENOMEM; - operation = gb_operation_create(connection, type, 0, response_size); - if (!operation) { - kfree(local_response); + if (!operation) return -ENOMEM; - } /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); @@ -77,19 +70,18 @@ static int request_operation(struct gb_connection *connection, int type, * layout for where the status is, so cast this to a random request so * we can see the status easier. */ - fake_request = (struct gb_vibrator_simple_response *)local_response; - if (fake_request->status) { + fake_response = operation->response.payload; + if (fake_response->status) { gb_connection_err(connection, "response %hhu", - fake_request->status); + fake_response->status); ret = -EIO; } else { /* Good request, so copy to the caller's buffer */ if (response_size && response) - memcpy(response, local_response, response_size); + memcpy(response, fake_response, response_size); } out: gb_operation_destroy(operation); - kfree(local_response); return ret; } @@ -101,24 +93,24 @@ out: static int get_version(struct gb_vibrator_device *vib) { struct gb_connection *connection = vib->connection; - struct gb_vibrator_proto_version_response version_request; + struct gb_vibrator_proto_version_response version_response; int retval; retval = request_operation(connection, GB_VIBRATOR_TYPE_PROTOCOL_VERSION, - &version_request, sizeof(version_request)); + &version_response, sizeof(version_response)); if (retval) return retval; - if (version_request.major > GB_VIBRATOR_VERSION_MAJOR) { + if (version_response.major > GB_VIBRATOR_VERSION_MAJOR) { dev_err(&connection->dev, "unsupported major version (%hhu > %hhu)\n", - version_request.major, GB_VIBRATOR_VERSION_MAJOR); + version_response.major, GB_VIBRATOR_VERSION_MAJOR); return -ENOTSUPP; } - vib->version_major = version_request.major; - vib->version_minor = version_request.minor; + vib->version_major = version_response.major; + vib->version_minor = version_response.minor; return 0; } -- cgit v1.2.3-59-g8ed1b From 980c7c509eb36c693576ad342a11455f530db0b9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Nov 2014 14:23:00 -0800 Subject: greybus: uart-gb: handle throttle/unthrottle properly This hooks up throttle/unthrottle to properly toggle the RTS line or do XON/XOFF if that is how the port is set up. Note, if the UART itself can handle XON/XOFF, we would need to send the correct character down to it, to have the firmware in the device set up the chip to use it automatically when needed. The odds of someone wanting to use this type of flow control is slim, so this isn't implemented at this point in time. Also fill in a few more fields in the get_serial_info ioctl, to make tools like stty(1) happier. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 47 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index d01956acdedc..460947e94a38 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -116,10 +116,7 @@ struct gb_tty { u16 cport_id; unsigned int minor; unsigned char clocal; - unsigned int throttled:1; - unsigned int throttle_req:1; bool disconnected; - int writesize; // FIXME - set this somehow. spinlock_t read_lock; spinlock_t write_lock; struct async_icount iocount; @@ -567,25 +564,39 @@ static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, static void gb_tty_throttle(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; + unsigned char stop_char; + int retval; + + if (I_IXOFF(tty)) { + stop_char = STOP_CHAR(tty); + retval = gb_tty_write(tty, &stop_char, 1); + if (retval <= 0) + return; + } + + if (tty->termios.c_cflag & CRTSCTS) { + gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; + retval = send_control(gb_tty, gb_tty->ctrlout); + } - spin_lock_irq(&gb_tty->read_lock); - gb_tty->throttle_req = 1; - spin_unlock_irq(&gb_tty->read_lock); } static void gb_tty_unthrottle(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; - unsigned int was_throttled; + unsigned char start_char; + int retval; - spin_lock_irq(&gb_tty->read_lock); - was_throttled = gb_tty->throttled; - gb_tty->throttle_req = 0; - gb_tty->throttled = 0; - spin_unlock_irq(&gb_tty->read_lock); + if (I_IXOFF(tty)) { + start_char = START_CHAR(tty); + retval = gb_tty_write(tty, &start_char, 1); + if (retval <= 0) + return; + } - if (was_throttled) { - // FIXME - send more data + if (tty->termios.c_cflag & CRTSCTS) { + gb_tty->ctrlout |= GB_UART_CTRL_RTS; + retval = send_control(gb_tty, gb_tty->ctrlout); } } @@ -598,9 +609,11 @@ static int get_serial_info(struct gb_tty *gb_tty, return -EINVAL; memset(&tmp, 0, sizeof(tmp)); - tmp.flags = ASYNC_LOW_LATENCY; - tmp.xmit_fifo_size = gb_tty->writesize; - tmp.baud_base = 0; // FIXME + tmp.flags = ASYNC_LOW_LATENCY | ASYNC_SKIP_TEST; + tmp.type = PORT_16550A; + tmp.line = gb_tty->minor; + tmp.xmit_fifo_size = 16; + tmp.baud_base = 9600; tmp.close_delay = gb_tty->port.close_delay / 10; tmp.closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; -- cgit v1.2.3-59-g8ed1b From dcec19fb1b10b62e02db9e234f0091509545971e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 17:55:01 -0600 Subject: greybus: get rid of uart request_operation() In "uart-gb.c", request_operation() function is only used by get_version(). Since it's not reused, it probably subtracts rather than adds value. So just incorporate what it does into get_version() and get rid of request_operation(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 67 ++++++++++++++------------------------- 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 460947e94a38..e68f4a24130b 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -136,15 +136,19 @@ static DEFINE_IDR(tty_minors); static DEFINE_MUTEX(table_lock); static atomic_t reference_count = ATOMIC_INIT(0); - -static int request_operation(struct gb_connection *connection, int type, - void *response, int response_size) +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_tty *tty) { struct gb_operation *operation; - struct gb_uart_simple_response *fake_response; + struct gb_uart_proto_version_response *response; int ret; - operation = gb_operation_create(connection, type, 0, response_size); + operation = gb_operation_create(tty->connection, + GB_UART_REQ_PROTOCOL_VERSION, + 0, sizeof(*response)); if (!operation) return -ENOMEM; @@ -155,20 +159,23 @@ static int request_operation(struct gb_connection *connection, int type, goto out; } - /* - * We only want to look at the status, and all requests have the same - * layout for where the status is, so cast this to a random request so - * we can see the status easier. - */ - fake_response = operation->response.payload; - if (fake_response->status) { - gb_connection_err(connection, "response %hhu", - fake_response->status); + response = operation->response.payload; + if (response->status) { + gb_connection_err(tty->connection, "response %hhu", + response->status); ret = -EIO; } else { - /* Good request, so copy to the caller's buffer */ - if (response_size && response) - memcpy(response, fake_response, response_size); + if (response->major > GB_UART_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response->major, GB_UART_VERSION_MAJOR); + ret = -ENOTSUPP; + goto out; + } + tty->version_major = response->major; + tty->version_minor = response->minor; + + pr_debug("%s: version_major = %u version_minor = %u\n", + __func__, tty->version_major, tty->version_minor); } out: gb_operation_destroy(operation); @@ -176,32 +183,6 @@ out: return ret; } -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_tty *tty) -{ - struct gb_uart_proto_version_response version_response; - int retval; - - retval = request_operation(tty->connection, - GB_UART_REQ_PROTOCOL_VERSION, - &version_response, sizeof(version_response)); - if (retval) - return retval; - - if (version_response.major > GB_UART_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - version_response.major, GB_UART_VERSION_MAJOR); - return -ENOTSUPP; - } - - tty->version_major = version_response.major; - tty->version_minor = version_response.minor; - return 0; -} - static int send_data(struct gb_tty *tty, u16 size, const u8 *data) { struct gb_connection *connection = tty->connection; -- cgit v1.2.3-59-g8ed1b From 30a2964f8455ede0f2416a3b6a28b60acc4b569c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 17:55:02 -0600 Subject: greybus: distinguish incoming from outgoing requests When we remove the mandatory status byte from response messages we will no longer be able to use a zero-sized response to indicate an operation is to be used for an incoming request. Define a new function gb_operation_create_incoming() to be used for incoming operations. Change (and rename) gb_operation_create() to be a helper that takes a Boolean to indicate which type is to be created, and use a simple wrapper to expose the outgoing operation creation routine. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index a2b27aeefb7f..8214a378efa2 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -294,13 +294,13 @@ static void gb_operation_message_exit(struct gb_message *message) * Returns a pointer to the new operation or a null pointer if an * error occurs. */ -struct gb_operation *gb_operation_create(struct gb_connection *connection, - u8 type, size_t request_size, - size_t response_size) +static struct gb_operation * +gb_operation_create_common(struct gb_connection *connection, bool outgoing, + u8 type, size_t request_size, + size_t response_size) { struct gb_operation *operation; gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC; - bool outgoing = response_size != 0; int ret; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); @@ -340,6 +340,23 @@ err_cache: return NULL; } +struct gb_operation *gb_operation_create(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size) +{ + return gb_operation_create_common(connection, true, type, + request_size, response_size); +} + +static struct gb_operation * +gb_operation_create_incoming(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size) +{ + return gb_operation_create_common(connection, false, type, + request_size, response_size); +} + /* * Destroy a previously created operation. */ @@ -427,7 +444,7 @@ void gb_connection_recv_request(struct gb_connection *connection, { struct gb_operation *operation; - operation = gb_operation_create(connection, type, size, 0); + operation = gb_operation_create_incoming(connection, type, size, 0); if (!operation) { gb_connection_err(connection, "can't create operation"); return; /* XXX Respond with pre-allocated ENOMEM */ -- cgit v1.2.3-59-g8ed1b From d30df426ffc055586ec63359aee27458af10214f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 17:55:03 -0600 Subject: greybus: send operation result in response message header Define a result byte in an operation response message header. All the protocols now define the mandatory status as the first byte in their response message. Assume that, for the moment, and save that value into the header result field (until we can get the simulator set up to handle the new protocol). Record the result from the response header as the result of the overall operation. Start enforcing the rule that we ignore all response payload (in fact, the entire message) if we see a non-zero result value. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 8214a378efa2..301102081567 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -34,14 +34,22 @@ static struct workqueue_struct *gb_operation_recv_workqueue; /* * All operation messages (both requests and responses) begin with - * a common header that encodes the size of the data (header - * included). This header also contains a unique identifier, which - * is used to keep track of in-flight operations. Finally, the - * header contains a operation type field, whose interpretation is - * dependent on what type of device lies on the other end of the - * connection. Response messages are distinguished from request - * messages by setting the high bit (0x80) in the operation type - * value. + * a header that encodes the size of the data (header included). + * This header also contains a unique identifier, which is used to + * keep track of in-flight operations. The header contains an + * operation type field, whose interpretation is dependent on what + * type of protocol is used over the connection. + * + * The high bit (0x80) of the operation type field is used to + * indicate whether the message is a request (clear) or a response + * (set). + * + * Response messages include an additional status byte, which + * communicates the result of the corresponding request. A zero + * status value means the operation completed successfully. Any + * other value indicates an error; in this case, the payload of the + * response message (if any) is ignored. The status byte must be + * zero in the header for a request message. * * The wire format for all numeric fields in the header is little * endian. Any operation-specific data begins immediately after the @@ -51,7 +59,8 @@ struct gb_operation_msg_hdr { __le16 size; /* Size in bytes of header + payload */ __le16 operation_id; /* Operation unique id */ __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ - /* 3 bytes pad, must be zero (ignore when read) */ + __u8 result; /* Result of request (in responses only) */ + /* 2 bytes pad, must be zero (ignore when read) */ } __aligned(sizeof(u64)); /* XXX Could be per-host device, per-module, or even per-connection */ @@ -469,6 +478,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, { struct gb_operation *operation; struct gb_message *message; + struct gb_operation_msg_hdr *header; operation = gb_pending_operation_find(connection, operation_id); if (!operation) { @@ -485,9 +495,13 @@ static void gb_connection_recv_response(struct gb_connection *connection, gb_connection_err(connection, "recv buffer too small"); return; /* XXX Should still complete operation */ } - operation->result = GB_OP_SUCCESS; /* XXX Maybe not yet? */ - memcpy(message->buffer, data, size); + /* Hack the status from the buffer into the header */ + header = message->buffer; + header->result = *(char *)message->payload; /* Eeew. */ + operation->result = header->result; + if (operation->result == GB_OP_SUCCESS) + memcpy(message->buffer, data, size); /* The rest will be handled in work queue context */ queue_work(gb_operation_recv_workqueue, &operation->recv_work); -- cgit v1.2.3-59-g8ed1b From bc717fcbf673b0852a474b869efb9bfd0989b012 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 17:55:04 -0600 Subject: greybus: define gb_operation_status_map() Define a common function that maps an operation status value to a Linux negative errno. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 29 +++++++++++++++++++++++++++++ drivers/staging/greybus/operation.h | 2 ++ 2 files changed, 31 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 301102081567..cfc341e6e341 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -287,6 +287,35 @@ static void gb_operation_message_exit(struct gb_message *message) message->buffer_size = 0; } +/* + * Map an enum gb_operation_status value (which is represted in a + * message as a single back a single byte) to an appropriate Linux + * negative errno. + */ +int gb_operation_status_map(u8 status) +{ + switch (status) { + case GB_OP_SUCCESS: + return 0; + case GB_OP_INVALID: + return -EINVAL; + case GB_OP_NO_MEMORY: + return -ENOMEM; + case GB_OP_INTERRUPTED: + return -EINTR; + case GB_OP_RETRY: + return -EAGAIN; + case GB_OP_PROTOCOL_BAD: + return -EPROTONOSUPPORT; + case GB_OP_OVERFLOW: + return -E2BIG; + case GB_OP_TIMEOUT: + return -ETIMEDOUT; + default: + return -EIO; + } +} + /* * Create a Greybus operation to be sent over the given connection. * The request buffer will big enough for a payload of the given diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 80ee158d74f7..6547291d8d85 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -101,6 +101,8 @@ int gb_operation_response_send(struct gb_operation *operation); void gb_operation_cancel(struct gb_operation *operation); int gb_operation_wait(struct gb_operation *operation); +int gb_operation_status_map(u8 status); + int gb_operation_init(void); void gb_operation_exit(void); -- cgit v1.2.3-59-g8ed1b From 25d0f81a0ec9263618b6e7e75879e963d438bc11 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 19 Nov 2014 17:55:05 -0600 Subject: greybus: remove status from all responses This is a pervasive change, but not really a big one. However: ============== Pay attention to this ============== If you're doing any testing with "gbsim" you need to update that program in sync with this change, because it changes the protocol used between them. ============== Pay attention to this ============== The status of a request is now recorded in the header of a response message. The previous patch put that header status byte in place, and this one removes the status byte from all the response messages. And finally, since we're modifying all these files anyway... Use gb_operation_status_map() to come up with a return code to use, given an operation response. Right now most errors simply result in -EIO getting returned. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 24 ++---- drivers/staging/greybus/gpio-gb.c | 143 ++++++++++++++-------------------- drivers/staging/greybus/i2c-gb.c | 69 +++++++--------- drivers/staging/greybus/operation.c | 5 +- drivers/staging/greybus/pwm-gb.c | 98 ++++++++++------------- drivers/staging/greybus/uart-gb.c | 66 ++++++---------- drivers/staging/greybus/vibrator-gb.c | 30 +++---- 7 files changed, 170 insertions(+), 265 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 101c3de81dc0..1c8534102287 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -40,7 +40,6 @@ struct gb_battery { #define GB_BATTERY_TYPE_VOLTAGE 0x07 struct gb_battery_proto_version_response { - __u8 status; __u8 major; __u8 minor; }; @@ -55,7 +54,6 @@ struct gb_battery_proto_version_response { #define GB_BATTERY_TECH_LiMn 0x0006 struct gb_battery_technology_response { - __u8 status; __le32 technology; }; @@ -67,35 +65,25 @@ struct gb_battery_technology_response { #define GB_BATTERY_STATUS_FULL 0x0004 struct gb_battery_status_response { - __u8 status; __le16 battery_status; }; struct gb_battery_max_voltage_response { - __u8 status; __le32 max_voltage; }; struct gb_battery_capacity_response { - __u8 status; __le32 capacity; }; struct gb_battery_temperature_response { - __u8 status; __le32 temperature; }; struct gb_battery_voltage_response { - __u8 status; __le32 voltage; }; -/* Generia response structure--prefix for all other responses */ -struct gb_generic_battery_response { - __u8 status; -}; - /* * None of the battery operation requests have any payload. This * function implements all of the requests by allowing the caller to @@ -107,7 +95,6 @@ static int battery_operation(struct gb_battery *gb, int type, { struct gb_connection *connection = gb->connection; struct gb_operation *operation; - struct gb_generic_battery_response *fake_response; int ret; operation = gb_operation_create(connection, type, 0, response_size); @@ -126,14 +113,13 @@ static int battery_operation(struct gb_battery *gb, int type, * layout for where the status is, so cast this to a random request so * we can see the status easier. */ - fake_response = operation->response.payload; - if (fake_response->status) { - gb_connection_err(connection, "version response %hhu", - fake_response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "operation result %hhu", + operation->result); } else { /* Good response, so copy to the caller's buffer */ - memcpy(response, fake_response, response_size); + memcpy(response, operation->response.payload, response_size); } out: gb_operation_destroy(operation); diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 6f4609da7f97..8ef00a7f477b 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -55,14 +55,12 @@ struct gb_gpio_controller { /* version request has no payload */ struct gb_gpio_proto_version_response { - __u8 status; __u8 major; __u8 minor; }; /* line count request has no payload */ struct gb_gpio_line_count_response { - __u8 status; __u8 count; }; @@ -70,44 +68,35 @@ struct gb_gpio_activate_request { __u8 which; }; struct gb_gpio_activate_response { - __u8 status; }; struct gb_gpio_deactivate_request { __u8 which; }; -struct gb_gpio_deactivate_response { - __u8 status; -}; +/* deactivate response has no payload */ struct gb_gpio_get_direction_request { __u8 which; }; struct gb_gpio_get_direction_response { - __u8 status; __u8 direction; }; struct gb_gpio_direction_in_request { __u8 which; }; -struct gb_gpio_direction_in_response { - __u8 status; -}; +/* direction in response has no payload */ struct gb_gpio_direction_out_request { __u8 which; __u8 value; }; -struct gb_gpio_direction_out_response { - __u8 status; -}; +/* direction out response has no payload */ struct gb_gpio_get_value_request { __u8 which; }; struct gb_gpio_get_value_response { - __u8 status; __u8 value; }; @@ -115,17 +104,13 @@ struct gb_gpio_set_value_request { __u8 which; __u8 value; }; -struct gb_gpio_set_value_response { - __u8 status; -}; +/* set value response has no payload */ struct gb_gpio_set_debounce_request { __u8 which; __le16 usec; }; -struct gb_gpio_set_debounce_response { - __u8 status; -}; +/* debounce response has no payload */ /* @@ -153,12 +138,12 @@ static int gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_co goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "version response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "version result %hhu", + operation->result); } else { + response = operation->response.payload; if (response->major > GB_GPIO_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", response->major, GB_GPIO_VERSION_MAJOR); @@ -199,12 +184,12 @@ static int gb_gpio_line_count_operation(struct gb_gpio_controller *gb_gpio_contr goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "line count response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "line count result %hhu", + operation->result); } else { + response = operation->response.payload; gb_gpio_controller->line_max = response->count; pr_debug("%s: count = %u\n", __func__, @@ -222,7 +207,6 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control struct gb_connection *connection = gb_gpio_controller->connection; struct gb_operation *operation; struct gb_gpio_activate_request *request; - struct gb_gpio_activate_response *response; int ret; if (which > gb_gpio_controller->line_max) @@ -231,7 +215,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control /* activate response has no payload */ operation = gb_operation_create(connection, GB_GPIO_TYPE_ACTIVATE, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -244,11 +228,10 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "activate response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "activate result %hhu", + operation->result); } else { gb_gpio_controller->lines[which].active = true; @@ -266,7 +249,6 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr struct gb_connection *connection = gb_gpio_controller->connection; struct gb_operation *operation; struct gb_gpio_deactivate_request *request; - struct gb_gpio_deactivate_response *response; int ret; if (which > gb_gpio_controller->line_max) @@ -275,7 +257,7 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr /* deactivate response has no payload */ operation = gb_operation_create(connection, GB_GPIO_TYPE_DEACTIVATE, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -288,11 +270,10 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "deactivate response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "deactivate result %hhu", + operation->result); } else { gb_gpio_controller->lines[which].active = false; pr_debug("%s: %u is now inactive\n", __func__, which); @@ -330,14 +311,15 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "get direction response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "get direction result %hhu", + operation->result); } else { - u8 direction = response->direction; + u8 direction; + response = operation->response.payload; + direction = response->direction; if (direction && direction != 1) pr_warn("gpio %u direction was %u (should be 0 or 1)\n", which, direction); @@ -357,7 +339,6 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con struct gb_connection *connection = gb_gpio_controller->connection; struct gb_operation *operation; struct gb_gpio_direction_in_request *request; - struct gb_gpio_direction_in_response *response; int ret; if (which > gb_gpio_controller->line_max) @@ -366,7 +347,7 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con /* direction_in response has no payload */ operation = gb_operation_create(connection, GB_GPIO_TYPE_DIRECTION_IN, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -379,11 +360,10 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "direction in response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "direction in result %hhu", + operation->result); } else { gb_gpio_controller->lines[which].direction = 1; pr_debug("%s: direction of %u is now in\n", __func__, which); @@ -400,7 +380,6 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co struct gb_connection *connection = gb_gpio_controller->connection; struct gb_operation *operation; struct gb_gpio_direction_out_request *request; - struct gb_gpio_direction_out_response *response; int ret; if (which > gb_gpio_controller->line_max) @@ -409,7 +388,7 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co /* direction_out response has no payload */ operation = gb_operation_create(connection, GB_GPIO_TYPE_DIRECTION_OUT, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -423,11 +402,10 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "direction out response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "direction out result %hhu", + operation->result); } else { gb_gpio_controller->lines[which].direction = 0; pr_debug("%s: direction of %u is now out, value %s\n", __func__, @@ -466,14 +444,15 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "get value response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "get value result %hhu", + operation->result); } else { - u8 value = response->value; + u8 value; + response = operation->response.payload; + value = response->value; if (value && value != 1) pr_warn("gpio %u value was %u (should be 0 or 1)\n", which, value); @@ -495,7 +474,6 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro struct gb_connection *connection = gb_gpio_controller->connection; struct gb_operation *operation; struct gb_gpio_set_value_request *request; - struct gb_gpio_set_value_response *response; int ret; if (which > gb_gpio_controller->line_max) @@ -504,7 +482,7 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro /* set_value response has no payload */ operation = gb_operation_create(connection, GB_GPIO_TYPE_SET_VALUE, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -518,11 +496,10 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "set value response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "set value result %hhu", + operation->result); } else { /* XXX should this set direction to out? */ gb_gpio_controller->lines[which].value = request->value; @@ -542,7 +519,6 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con struct gb_connection *connection = gb_gpio_controller->connection; struct gb_operation *operation; struct gb_gpio_set_debounce_request *request; - struct gb_gpio_set_debounce_response *response; int ret; if (which > gb_gpio_controller->line_max) @@ -551,7 +527,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con /* set_debounce response has no payload */ operation = gb_operation_create(connection, GB_GPIO_TYPE_SET_DEBOUNCE, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -565,11 +541,10 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "set debounce response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "set debounce result %hhu", + operation->result); } else { gb_gpio_controller->lines[which].debounce_usec = le16_to_cpu(request->usec); pr_debug("%s: debounce of %u is now %hu usec\n", __func__, which, diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index c810c429d529..9a090f49a203 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -43,30 +43,24 @@ struct gb_i2c_device { /* version request has no payload */ struct gb_i2c_proto_version_response { - __u8 status; __u8 major; __u8 minor; }; /* functionality request has no payload */ struct gb_i2c_functionality_response { - __u8 status; __le32 functionality; }; struct gb_i2c_timeout_request { __le16 msec; }; -struct gb_i2c_timeout_response { - __u8 status; -}; +/* timeout response has no payload */ struct gb_i2c_retries_request { __u8 retries; }; -struct gb_i2c_retries_response { - __u8 status; -}; +/* retries response has no payload */ /* * Outgoing data immediately follows the op count and ops array. @@ -89,7 +83,6 @@ struct gb_i2c_transfer_request { struct gb_i2c_transfer_op ops[0]; /* op_count of these */ }; struct gb_i2c_transfer_response { - __u8 status; __u8 data[0]; /* inbound data */ }; @@ -118,12 +111,12 @@ static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "version response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "version result %hhu", + operation->result); } else { + response = operation->response.payload; if (response->major > GB_I2C_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", response->major, GB_I2C_VERSION_MAJOR); @@ -170,12 +163,12 @@ static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "functionality response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "functionality result %hhu", + operation->result); } else { + response = operation->response.payload; functionality = le32_to_cpu(response->functionality); gb_i2c_dev->functionality = gb_i2c_functionality_map(functionality); @@ -191,11 +184,10 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) struct gb_connection *connection = gb_i2c_dev->connection; struct gb_operation *operation; struct gb_i2c_timeout_request *request; - struct gb_i2c_timeout_response *response; int ret; operation = gb_operation_create(connection, GB_I2C_TYPE_TIMEOUT, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -208,11 +200,10 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "timeout response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "timeout result %hhu", + operation->result); } else { gb_i2c_dev->timeout_msec = msec; } @@ -228,11 +219,10 @@ static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, struct gb_connection *connection = gb_i2c_dev->connection; struct gb_operation *operation; struct gb_i2c_retries_request *request; - struct gb_i2c_retries_response *response; int ret; operation = gb_operation_create(connection, GB_I2C_TYPE_RETRIES, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -245,11 +235,10 @@ static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "retries response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "retries result %hhu", + operation->result); } else { gb_i2c_dev->retries = retries; } @@ -380,16 +369,14 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, goto out; } - response = operation->response.payload; - if (response->status) { - if (response->status == GB_OP_RETRY) { - ret = -EAGAIN; - } else { - gb_connection_err(connection, "transfer response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + if (ret != -EAGAIN) { + gb_connection_err(connection, "transfer result %hhu", + operation->result); } } else { + response = operation->response.payload; gb_i2c_transfer_response(msgs, msg_count, response->data); ret = msg_count; } diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index cfc341e6e341..e6474253eae2 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -525,10 +525,11 @@ static void gb_connection_recv_response(struct gb_connection *connection, return; /* XXX Should still complete operation */ } - /* Hack the status from the buffer into the header */ + /* The status in the response is the result of the operation */ header = message->buffer; - header->result = *(char *)message->payload; /* Eeew. */ operation->result = header->result; + + /* We must ignore the payload if a bad status is returned */ if (operation->result == GB_OP_SUCCESS) memcpy(message->buffer, data, size); diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index 0f465522223a..0b66f8cab05e 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -41,20 +41,14 @@ struct gb_pwm_chip { #define GB_PWM_TYPE_DISABLE 0x08 #define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ -struct gb_pwm_simple_response { - __u8 status; -}; - /* version request has no payload */ struct gb_pwm_proto_version_response { - __u8 status; __u8 major; __u8 minor; }; /* pwm count request has no payload */ struct gb_pwm_count_response { - __u8 status; __u8 count; }; @@ -110,12 +104,12 @@ static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "version response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "version result %hhu", + operation->result); } else { + response = operation->response.payload; if (response->major > GB_PWM_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", response->major, GB_PWM_VERSION_MAJOR); @@ -151,12 +145,12 @@ static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "pwm count response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "pwm count result %hhu", + operation->result); } else + response = operation->response.payload; pwmc->pwm_max = response->count; out: gb_operation_destroy(operation); @@ -170,7 +164,6 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, struct gb_connection *connection = pwmc->connection; struct gb_operation *operation; struct gb_pwm_activate_request *request; - struct gb_pwm_simple_response *response; int ret; if (which > pwmc->pwm_max) @@ -178,7 +171,7 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, /* activate response has no payload */ operation = gb_operation_create(connection, GB_PWM_TYPE_ACTIVATE, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -191,11 +184,10 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "activate response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "activate result %hhu", + operation->result); } out: gb_operation_destroy(operation); @@ -209,7 +201,6 @@ static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, struct gb_connection *connection = pwmc->connection; struct gb_operation *operation; struct gb_pwm_deactivate_request *request; - struct gb_pwm_simple_response *response; int ret; if (which > pwmc->pwm_max) @@ -217,7 +208,7 @@ static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, /* deactivate response has no payload */ operation = gb_operation_create(connection, GB_PWM_TYPE_DEACTIVATE, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -230,11 +221,10 @@ static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "deactivate response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "deactivate result %hhu", + operation->result); } out: gb_operation_destroy(operation); @@ -248,14 +238,13 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, struct gb_connection *connection = pwmc->connection; struct gb_operation *operation; struct gb_pwm_config_request *request; - struct gb_pwm_simple_response *response; int ret; if (which > pwmc->pwm_max) return -EINVAL; operation = gb_operation_create(connection, GB_PWM_TYPE_CONFIG, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -270,11 +259,10 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "config response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "config result %hhu", + operation->result); } out: gb_operation_destroy(operation); @@ -289,14 +277,13 @@ static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, struct gb_connection *connection = pwmc->connection; struct gb_operation *operation; struct gb_pwm_polarity_request *request; - struct gb_pwm_simple_response *response; int ret; if (which > pwmc->pwm_max) return -EINVAL; operation = gb_operation_create(connection, GB_PWM_TYPE_POLARITY, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -310,11 +297,10 @@ static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "set polarity response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "set polarity result %hhu", + operation->result); } out: gb_operation_destroy(operation); @@ -328,7 +314,6 @@ static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, struct gb_connection *connection = pwmc->connection; struct gb_operation *operation; struct gb_pwm_enable_request *request; - struct gb_pwm_simple_response *response; int ret; if (which > pwmc->pwm_max) @@ -336,7 +321,7 @@ static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, /* enable response has no payload */ operation = gb_operation_create(connection, GB_PWM_TYPE_ENABLE, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -349,11 +334,10 @@ static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "enable response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "enable result %hhu", + operation->result); } out: gb_operation_destroy(operation); @@ -367,7 +351,6 @@ static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, struct gb_connection *connection = pwmc->connection; struct gb_operation *operation; struct gb_pwm_disable_request *request; - struct gb_pwm_simple_response *response; int ret; if (which > pwmc->pwm_max) @@ -375,7 +358,7 @@ static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, /* disable response has no payload */ operation = gb_operation_create(connection, GB_PWM_TYPE_DISABLE, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -388,11 +371,10 @@ static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "disable response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "disable result %hhu", + operation->result); } out: gb_operation_destroy(operation); diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index e68f4a24130b..1be00d654a6d 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -49,7 +49,6 @@ #define GB_UART_TYPE_RESPONSE 0x80 /* OR'd with rest */ struct gb_uart_proto_version_response { - __u8 status; __u8 major; __u8 minor; }; @@ -106,10 +105,6 @@ struct gb_uart_serial_state_request { __u16 control; }; -struct gb_uart_simple_response { - __u8 status; -}; - struct gb_tty { struct tty_port port; struct gb_connection *connection; @@ -159,11 +154,10 @@ static int get_version(struct gb_tty *tty) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(tty->connection, "response %hhu", - response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(tty->connection, "result %hhu", + operation->result); } else { if (response->major > GB_UART_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", @@ -188,15 +182,13 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) struct gb_connection *connection = tty->connection; struct gb_operation *operation; struct gb_uart_send_data_request *request; - struct gb_uart_simple_response *response; int retval; if (!data || !size) return 0; operation = gb_operation_create(connection, GB_UART_REQ_SEND_DATA, - sizeof(*request) + size, - sizeof(*response)); + sizeof(*request) + size, 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -211,11 +203,10 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "send data response %hhu", - response->status); - retval = -EIO; + if (operation->result) { + retval = gb_operation_status_map(operation->result); + gb_connection_err(connection, "send data result %hhu", + operation->result); } out: gb_operation_destroy(operation); @@ -229,12 +220,10 @@ static int send_line_coding(struct gb_tty *tty, struct gb_connection *connection = tty->connection; struct gb_operation *operation; struct gb_uart_set_line_coding_request *request; - struct gb_uart_simple_response *response; int retval; operation = gb_operation_create(connection, GB_UART_REQ_SET_LINE_CODING, - sizeof(*request), - sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -248,11 +237,10 @@ static int send_line_coding(struct gb_tty *tty, goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "send line coding response %hhu", - response->status); - retval = -EIO; + if (operation->result) { + retval = gb_operation_status_map(operation->result); + gb_connection_err(connection, "send line coding result %hhu", + operation->result); } out: gb_operation_destroy(operation); @@ -265,13 +253,11 @@ static int send_control(struct gb_tty *tty, u16 control) struct gb_connection *connection = tty->connection; struct gb_operation *operation; struct gb_uart_set_control_line_state_request *request; - struct gb_uart_simple_response *response; int retval; operation = gb_operation_create(connection, GB_UART_REQ_SET_CONTROL_LINE_STATE, - sizeof(*request), - sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -285,11 +271,10 @@ static int send_control(struct gb_tty *tty, u16 control) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "send control response %hhu", - response->status); - retval = -EIO; + if (operation->result) { + retval = gb_operation_status_map(operation->result); + gb_connection_err(connection, "send control result %hhu", + operation->result); } out: gb_operation_destroy(operation); @@ -302,7 +287,6 @@ static int send_break(struct gb_tty *tty, u8 state) struct gb_connection *connection = tty->connection; struct gb_operation *operation; struct gb_uart_set_break_request *request; - struct gb_uart_simple_response *response; int retval; if ((state != 0) && (state != 1)) { @@ -311,8 +295,7 @@ static int send_break(struct gb_tty *tty, u8 state) } operation = gb_operation_create(connection, GB_UART_REQ_SET_BREAK, - sizeof(*request), - sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -326,11 +309,10 @@ static int send_break(struct gb_tty *tty, u8 state) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "send break response %hhu", - response->status); - retval = -EIO; + if (operation->result) { + retval = gb_operation_status_map(operation->result); + gb_connection_err(connection, "send break result %hhu", + operation->result); } out: gb_operation_destroy(operation); diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index 3e46fa665dec..9ad3cb077f6a 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -34,7 +34,6 @@ struct gb_vibrator_device { #define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */ struct gb_vibrator_proto_version_response { - __u8 status; __u8 major; __u8 minor; }; @@ -43,15 +42,10 @@ struct gb_vibrator_on_request { __le16 timeout_ms; }; -struct gb_vibrator_simple_response { - __u8 status; -}; - static int request_operation(struct gb_connection *connection, int type, void *response, int response_size) { struct gb_operation *operation; - struct gb_vibrator_simple_response *fake_response; int ret; operation = gb_operation_create(connection, type, 0, response_size); @@ -70,15 +64,15 @@ static int request_operation(struct gb_connection *connection, int type, * layout for where the status is, so cast this to a random request so * we can see the status easier. */ - fake_response = operation->response.payload; - if (fake_response->status) { - gb_connection_err(connection, "response %hhu", - fake_response->status); - ret = -EIO; + if (operation->result) { + ret = gb_operation_status_map(operation->result); + gb_connection_err(connection, "operation result %hhu", + operation->result); } else { /* Good request, so copy to the caller's buffer */ if (response_size && response) - memcpy(response, fake_response, response_size); + memcpy(response, operation->response.payload, + response_size); } out: gb_operation_destroy(operation); @@ -119,11 +113,10 @@ static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) struct gb_connection *connection = vib->connection; struct gb_operation *operation; struct gb_vibrator_on_request *request; - struct gb_vibrator_simple_response *response; int retval; operation = gb_operation_create(connection, GB_VIBRATOR_TYPE_ON, - sizeof(*request), sizeof(*response)); + sizeof(*request), 0); if (!operation) return -ENOMEM; request = operation->request.payload; @@ -137,11 +130,10 @@ static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) goto out; } - response = operation->response.payload; - if (response->status) { - gb_connection_err(connection, "send data response %hhu", - response->status); - retval = -EIO; + if (operation->result) { + retval = gb_operation_status_map(operation->result); + gb_connection_err(connection, "send data result %hhu", + operation->result); } out: gb_operation_destroy(operation); -- cgit v1.2.3-59-g8ed1b From 9ffb4b8b398a7c420a94372abcde05a41ac23ccb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Nov 2014 16:56:13 -0800 Subject: greybus: uart-gb: fix up typo from previous patch moving the status variable around. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 1be00d654a6d..8df3bfb24a7e 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -159,6 +159,7 @@ static int get_version(struct gb_tty *tty) gb_connection_err(tty->connection, "result %hhu", operation->result); } else { + response = operation->response.payload; if (response->major > GB_UART_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", response->major, GB_UART_VERSION_MAJOR); -- cgit v1.2.3-59-g8ed1b From 4589f0666334edf220402cc4d791657e3565f9f7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Nov 2014 17:02:21 -0800 Subject: greybus: pwm-gb.c: fix up missing { } for else This was a compiler warning, which looked correct, but was trying to tell us something else... Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/pwm-gb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index 0b66f8cab05e..c92d8e262b37 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -149,9 +149,10 @@ static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) ret = gb_operation_status_map(operation->result); gb_connection_err(connection, "pwm count result %hhu", operation->result); - } else + } else { response = operation->response.payload; pwmc->pwm_max = response->count; + } out: gb_operation_destroy(operation); -- cgit v1.2.3-59-g8ed1b From 7486dfd161e4474b20e9acf5ca4c8c439cd0def6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Nov 2014 19:09:53 -0800 Subject: greybus: add -Wall to the build flags In an attempt to turn on as many options as we can to catch warnings early, let's enable -Wall. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index b254516b4392..81174b936b3a 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -23,6 +23,9 @@ KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build PWD := $(shell pwd) +# add -Wall to try to catch everything we can. +ccFlags-y := -Wall + all: module module: -- cgit v1.2.3-59-g8ed1b From 5e68995784ad7ef8410661f3970bebf272fd322f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 20 Nov 2014 15:37:05 -0600 Subject: greybus: drop a now-empty structure One structure, gb_gpio_activate_response, was not deleted even though it now has no contents. Delete it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 8ef00a7f477b..f2e2eeffbc1f 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -67,8 +67,7 @@ struct gb_gpio_line_count_response { struct gb_gpio_activate_request { __u8 which; }; -struct gb_gpio_activate_response { -}; +/* activate response has no payload */ struct gb_gpio_deactivate_request { __u8 which; -- cgit v1.2.3-59-g8ed1b From e8b48d1586abd62dd1749520dccd4bac784ce680 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 20 Nov 2014 15:37:06 -0600 Subject: greybus: fix a timeout race Whenever we send a request message we start a timer to ensure the we don't wait too long for the matching response to arrive. Currently we set up the timeout *after* sending the message, but that is subject to a race--the response could arrive (and the timeout prematurely disabled) before the timeout is even set up. Set up the timeout before sending the message. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index e6474253eae2..3e3fc73a881c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -447,13 +447,20 @@ int gb_operation_request_send(struct gb_operation *operation, */ operation->callback = callback; gb_pending_operation_insert(operation); + + /* + * We impose a time limit for requests to complete. We need + * to set the timer before we send the request though, so we + * don't lose a race with the receipt of the resposne. + */ + timeout = msecs_to_jiffies(OPERATION_TIMEOUT_DEFAULT); + schedule_delayed_work(&operation->timeout_work, timeout); + + /* All set, send the request */ ret = gb_message_send(&operation->request, GFP_KERNEL); if (ret) return ret; - /* We impose a time limit for requests to complete. */ - timeout = msecs_to_jiffies(OPERATION_TIMEOUT_DEFAULT); - schedule_delayed_work(&operation->timeout_work, timeout); if (!callback) ret = gb_operation_wait(operation); -- cgit v1.2.3-59-g8ed1b From 8d55f4c6d9a29f4b3483231f6d88458333ee36ed Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 20 Nov 2014 15:37:07 -0600 Subject: greybus: complete overflow responses If a response arrives for an operation request and the allotted buffer isn't big enough we report the error, but we don't finish processing the response. Instead, set the operation result, but then finish processing the response (no different from any other operation error). This will allow the normal completion handling to occur for this error case. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 3e3fc73a881c..d91cd5b4b65a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -526,16 +526,15 @@ static void gb_connection_recv_response(struct gb_connection *connection, gb_pending_operation_remove(operation); message = &operation->response; - if (size > message->buffer_size) { - operation->result = GB_OP_OVERFLOW; + if (size <= message->buffer_size) { + /* Transfer the operation result from the response header */ + header = message->buffer; + operation->result = header->result; + } else { gb_connection_err(connection, "recv buffer too small"); - return; /* XXX Should still complete operation */ + operation->result = GB_OP_OVERFLOW; } - /* The status in the response is the result of the operation */ - header = message->buffer; - operation->result = header->result; - /* We must ignore the payload if a bad status is returned */ if (operation->result == GB_OP_SUCCESS) memcpy(message->buffer, data, size); -- cgit v1.2.3-59-g8ed1b From 8b337308e7ff71cd6ae6d9c04260f8ada6e98c9e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 20 Nov 2014 16:09:13 -0600 Subject: greybus: have greybus allocate its own buffers Rather than having the host driver allocate the buffers that the Greybus core uses to hold its data for sending or receiving, have the host driver define what it requires those buffers to look like. Two constraints define what the host driver requires: the maximum number of bytes that the host device can send in a single request; and a statement of the "headroom" that needs to be present for use by the host device. The direct description of the headroom is that it's the extra byte the host device needs at the beginning of the "data" portion of the buffer so the ES1 driver can insert the destination CPort id. But more generally, the host driver could put other data in there as well. By stating these two parameters, Greybus can allocate the buffers it uses by itself. The host driver still allocates the buffers it uses for receiving data--the content of those are copied as needed into Greybus buffers when data arrives. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 +- drivers/staging/greybus/es1-ap-usb.c | 81 +++++++++++++++++------------------- drivers/staging/greybus/greybus.h | 6 ++- drivers/staging/greybus/operation.c | 29 +++++++++++-- 4 files changed, 68 insertions(+), 51 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 04fc5412c351..2c50dd3e2a47 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -169,8 +169,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * Validate that the driver implements all of the callbacks * so that we don't have to every time we make them. */ - if ((!driver->buffer_alloc) || (!driver->buffer_free) || - (!driver->buffer_send) || (!driver->buffer_cancel) || + if ((!driver->buffer_send) || (!driver->buffer_cancel) || (!driver->submit_svc)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); return NULL; diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 7745b81c893a..88436436fcb2 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -25,7 +25,7 @@ /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) -#define ES1_GBUF_MSG_SIZE PAGE_SIZE +#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE static const struct usb_device_id id_table[] = { /* Made up numbers for the SVC USB Bridge in ES1 */ @@ -92,47 +92,41 @@ static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) static void cport_out_callback(struct urb *urb); /* - * Allocate a buffer to be sent via UniPro. + * Buffer constraints for the host driver. + * + * A "buffer" is used to hold data to be transferred for Greybus by + * the host driver. A buffer is represented by a "buffer pointer", + * which defines a region of memory used by the host driver for + * transferring the data. When Greybus allocates a buffer, it must + * do so subject to the constraints associated with the host driver. + * These constraints are specified by two parameters: the + * headroom; and the maximum buffer size. + * + * +------------------+ + * | Host driver | \ + * | reserved area | }- headroom + * | . . . | / + * buffer pointer ---> +------------------+ + * | Buffer space for | \ + * | transferred data | }- buffer size + * | . . . | / (limited to size_max) + * +------------------+ + * + * headroom: Every buffer must have at least this much space + * *before* the buffer pointer, reserved for use by the + * host driver. I.e., ((char *)buffer - headroom) must + * point to valid memory, usable only by the host driver. + * size_max: The maximum size of a buffer (not including the + * headroom) must not exceed this. */ -static void *buffer_alloc(unsigned int size, gfp_t gfp_mask) +static void hd_buffer_constraints(struct greybus_host_device *hd) { - u8 *buffer; - - if (size > ES1_GBUF_MSG_SIZE) { - pr_err("guf was asked to be bigger than %ld!\n", - ES1_GBUF_MSG_SIZE); - } - /* - * For ES1 we need to insert a byte at the front of the data - * to indicate the destination CPort id. We only need one - * extra byte, but we allocate four extra bytes to allow the - * buffer returned to be aligned on a four-byte boundary. - * - * This is only needed for outbound data, but we handle - * buffers for inbound data the same way for consistency. - * - * XXX Do we need to indicate the destination device id too? + * Only one byte is required, but this produces a result + * that's better aligned for the user. */ - buffer = kzalloc(GB_BUFFER_ALIGN + size, gfp_mask); - if (buffer) - buffer += GB_BUFFER_ALIGN; - - return buffer; -} - -/* Free a previously-allocated buffer */ -static void buffer_free(void *buffer) -{ - u8 *allocated = buffer; - - /* Can be called with a NULL buffer on some error paths */ - if (!allocated) - return; - - /* Account for the space set aside for the prepended cport id */ - allocated -= GB_BUFFER_ALIGN; - kfree(allocated); + hd->buffer_headroom = sizeof(u32); /* For cport id */ + hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ @@ -265,8 +259,6 @@ static void buffer_cancel(void *cookie) static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), - .buffer_alloc = buffer_alloc, - .buffer_free = buffer_free, .buffer_send = buffer_send, .buffer_cancel = buffer_cancel, .submit_svc = submit_svc, @@ -493,6 +485,9 @@ static int ap_probe(struct usb_interface *interface, return -ENOMEM; } + /* Fill in the buffer allocation constraints */ + hd_buffer_constraints(hd); + es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; @@ -557,14 +552,14 @@ static int ap_probe(struct usb_interface *interface, urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) goto error; - buffer = kmalloc(ES1_GBUF_MSG_SIZE, GFP_KERNEL); + buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); if (!buffer) goto error; usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, es1->cport_in_endpoint), - buffer, ES1_GBUF_MSG_SIZE, cport_in_callback, - hd); + buffer, ES1_GBUF_MSG_SIZE_MAX, + cport_in_callback, hd); es1->cport_in_urb[i] = urb; es1->cport_in_buffer[i] = buffer; retval = usb_submit_urb(urb, GFP_KERNEL); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 3df2b5a60d80..8fda37c42a0c 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -78,8 +78,6 @@ struct svc_msg; struct greybus_host_driver { size_t hd_priv_size; - void *(*buffer_alloc)(unsigned int size, gfp_t gfp_mask); - void (*buffer_free)(void *buffer); void *(*buffer_send)(struct greybus_host_device *hd, u16 dest_cport_id, void *buffer, size_t buffer_size, gfp_t gfp_mask); void (*buffer_cancel)(void *cookie); @@ -97,6 +95,10 @@ struct greybus_host_device { struct ida cport_id_map; u8 device_id; + /* Host device buffer constraints */ + size_t buffer_headroom; + size_t buffer_size_max; + /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d91cd5b4b65a..6c66c264891e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -229,6 +229,27 @@ static void operation_timeout(struct work_struct *work) gb_operation_complete(operation); } +static void * +gb_buffer_alloc(struct greybus_host_device *hd, size_t size, gfp_t gfp_flags) +{ + u8 *buffer; + + buffer = kzalloc(hd->buffer_headroom + size, gfp_flags); + if (buffer) + buffer += hd->buffer_headroom; + + return buffer; +} + +static void +gb_buffer_free(struct greybus_host_device *hd, void *buffer) +{ + u8 *allocated = buffer; + + if (allocated) + kfree(allocated - hd->buffer_headroom); +} + /* * Allocate a buffer to be used for an operation request or response * message. For outgoing messages, both types of message contain a @@ -246,9 +267,9 @@ static int gb_operation_message_init(struct gb_operation *operation, struct gb_message *message; struct gb_operation_msg_hdr *header; - if (size > GB_OPERATION_MESSAGE_SIZE_MAX) - return -E2BIG; size += sizeof(*header); + if (size > hd->buffer_size_max) + return -E2BIG; if (request) { message = &operation->request; @@ -257,7 +278,7 @@ static int gb_operation_message_init(struct gb_operation *operation, type |= GB_OPERATION_TYPE_RESPONSE; } - message->buffer = hd->driver->buffer_alloc(size, gfp_flags); + message->buffer = gb_buffer_alloc(hd, size, gfp_flags); if (!message->buffer) return -ENOMEM; message->buffer_size = size; @@ -279,7 +300,7 @@ static void gb_operation_message_exit(struct gb_message *message) struct greybus_host_device *hd; hd = message->operation->connection->hd; - hd->driver->buffer_free(message->buffer); + gb_buffer_free(hd, message->buffer); message->operation = NULL; message->payload = NULL; -- cgit v1.2.3-59-g8ed1b From 0a4e14a882aeefc7cb1bebff240b4d1c770065a4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 20 Nov 2014 16:09:14 -0600 Subject: greybus: rename message buffer fields The beginning of an operation message always contains the message header. Rename the "buffer" field in an operation message to be "header" to reflect this. Change its type as well. The size of a message is the combined size of its header and its payload. Rename the "buffer_size" field in a message header to be simply "size", so message->size describes exactly that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 34 +++++++++++++++++----------------- drivers/staging/greybus/operation.h | 12 +++++------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 6c66c264891e..f6940a375364 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -81,7 +81,7 @@ static void gb_pending_operation_insert(struct gb_operation *operation) spin_unlock_irq(&gb_operations_lock); /* Store the operation id in the request header */ - header = operation->request.buffer; + header = operation->request.header; header->operation_id = cpu_to_le16(operation->id); } @@ -120,8 +120,8 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) message->cookie = connection->hd->driver->buffer_send(connection->hd, dest_cport_id, - message->buffer, - message->buffer_size, + message->header, + message->size, gfp_mask); if (IS_ERR(message->cookie)) { ret = PTR_ERR(message->cookie); @@ -177,7 +177,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) struct gb_protocol *protocol = operation->connection->protocol; struct gb_operation_msg_hdr *header; - header = operation->request.buffer; + header = operation->request.header; /* * If the protocol has no incoming request handler, report @@ -205,7 +205,7 @@ static void gb_operation_recv_work(struct work_struct *recv_work) bool incoming_request; operation = container_of(recv_work, struct gb_operation, recv_work); - incoming_request = operation->response.buffer == NULL; + incoming_request = operation->response.header == NULL; if (incoming_request) gb_operation_request_handle(operation); gb_operation_complete(operation); @@ -278,13 +278,13 @@ static int gb_operation_message_init(struct gb_operation *operation, type |= GB_OPERATION_TYPE_RESPONSE; } - message->buffer = gb_buffer_alloc(hd, size, gfp_flags); - if (!message->buffer) + message->header = gb_buffer_alloc(hd, size, gfp_flags); + if (!message->header) return -ENOMEM; - message->buffer_size = size; + message->size = size; /* Fill in the header structure */ - header = message->buffer; + header = message->header; header->size = cpu_to_le16(size); header->operation_id = 0; /* Filled in when submitted */ header->type = type; @@ -300,12 +300,12 @@ static void gb_operation_message_exit(struct gb_message *message) struct greybus_host_device *hd; hd = message->operation->connection->hd; - gb_buffer_free(hd, message->buffer); + gb_buffer_free(hd, message->header); message->operation = NULL; message->payload = NULL; - message->buffer = NULL; - message->buffer_size = 0; + message->header = NULL; + message->size = 0; } /* @@ -516,7 +516,7 @@ void gb_connection_recv_request(struct gb_connection *connection, return; /* XXX Respond with pre-allocated ENOMEM */ } operation->id = operation_id; - memcpy(operation->request.buffer, data, size); + memcpy(operation->request.header, data, size); /* The rest will be handled in work queue context */ queue_work(gb_operation_recv_workqueue, &operation->recv_work); @@ -547,9 +547,9 @@ static void gb_connection_recv_response(struct gb_connection *connection, gb_pending_operation_remove(operation); message = &operation->response; - if (size <= message->buffer_size) { + if (size <= message->size) { /* Transfer the operation result from the response header */ - header = message->buffer; + header = message->header; operation->result = header->result; } else { gb_connection_err(connection, "recv buffer too small"); @@ -558,7 +558,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, /* We must ignore the payload if a bad status is returned */ if (operation->result == GB_OP_SUCCESS) - memcpy(message->buffer, data, size); + memcpy(message->header, data, size); /* The rest will be handled in work queue context */ queue_work(gb_operation_recv_workqueue, &operation->recv_work); @@ -610,7 +610,7 @@ void gb_operation_cancel(struct gb_operation *operation) { operation->canceled = true; gb_message_cancel(&operation->request); - if (operation->response.buffer) + if (operation->response.header) gb_message_cancel(&operation->response); } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 6547291d8d85..38b2833f0dfc 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -25,14 +25,12 @@ enum gb_operation_status { }; struct gb_message { - void *payload; + struct gb_operation_msg_hdr *header; + void *payload; + size_t size; /* header + payload */ + struct gb_operation *operation; - struct gb_operation *operation; - - void *buffer; - size_t buffer_size; - - void *cookie; + void *cookie; }; /* -- cgit v1.2.3-59-g8ed1b From c08b1ddaeb6e8f3c22b15f80e7475c809490a716 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 20 Nov 2014 16:09:15 -0600 Subject: greybus: dynamically allocate requests and responses Have an operation's request and response messages be dynamically allocated rather than embedded in an operation. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 2 +- drivers/staging/greybus/gpio-gb.c | 24 +++++----- drivers/staging/greybus/i2c-gb.c | 12 ++--- drivers/staging/greybus/operation.c | 89 +++++++++++++++++------------------ drivers/staging/greybus/operation.h | 4 +- drivers/staging/greybus/pwm-gb.c | 16 +++---- drivers/staging/greybus/uart-gb.c | 10 ++-- drivers/staging/greybus/vibrator-gb.c | 4 +- 8 files changed, 78 insertions(+), 83 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 1c8534102287..02178f53cc5b 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -119,7 +119,7 @@ static int battery_operation(struct gb_battery *gb, int type, operation->result); } else { /* Good response, so copy to the caller's buffer */ - memcpy(response, operation->response.payload, response_size); + memcpy(response, operation->response->payload, response_size); } out: gb_operation_destroy(operation); diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index f2e2eeffbc1f..170f8aa289ca 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -142,7 +142,7 @@ static int gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_co gb_connection_err(connection, "version result %hhu", operation->result); } else { - response = operation->response.payload; + response = operation->response->payload; if (response->major > GB_GPIO_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", response->major, GB_GPIO_VERSION_MAJOR); @@ -188,7 +188,7 @@ static int gb_gpio_line_count_operation(struct gb_gpio_controller *gb_gpio_contr gb_connection_err(connection, "line count result %hhu", operation->result); } else { - response = operation->response.payload; + response = operation->response->payload; gb_gpio_controller->line_max = response->count; pr_debug("%s: count = %u\n", __func__, @@ -217,7 +217,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; /* Synchronous operation--no callback */ @@ -259,7 +259,7 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; /* Synchronous operation--no callback */ @@ -300,7 +300,7 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; /* Synchronous operation--no callback */ @@ -317,7 +317,7 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co } else { u8 direction; - response = operation->response.payload; + response = operation->response->payload; direction = response->direction; if (direction && direction != 1) pr_warn("gpio %u direction was %u (should be 0 or 1)\n", @@ -349,7 +349,7 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; /* Synchronous operation--no callback */ @@ -390,7 +390,7 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; request->value = value_high ? 1 : 0; @@ -433,7 +433,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro sizeof(*request), sizeof(*response)); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; /* Synchronous operation--no callback */ @@ -450,7 +450,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro } else { u8 value; - response = operation->response.payload; + response = operation->response->payload; value = response->value; if (value && value != 1) pr_warn("gpio %u value was %u (should be 0 or 1)\n", @@ -484,7 +484,7 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; request->value = value_high ? 1 : 0; @@ -529,7 +529,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; request->usec = cpu_to_le16(debounce_usec); diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 9a090f49a203..2a5fb822535e 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -116,7 +116,7 @@ static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) gb_connection_err(connection, "version result %hhu", operation->result); } else { - response = operation->response.payload; + response = operation->response->payload; if (response->major > GB_I2C_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", response->major, GB_I2C_VERSION_MAJOR); @@ -168,7 +168,7 @@ static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) gb_connection_err(connection, "functionality result %hhu", operation->result); } else { - response = operation->response.payload; + response = operation->response->payload; functionality = le32_to_cpu(response->functionality); gb_i2c_dev->functionality = gb_i2c_functionality_map(functionality); @@ -190,7 +190,7 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->msec = cpu_to_le16(msec); /* Synchronous operation--no callback */ @@ -225,7 +225,7 @@ static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->retries = retries; /* Synchronous operation--no callback */ @@ -310,7 +310,7 @@ gb_i2c_transfer_request(struct gb_connection *connection, if (!operation) return NULL; - request = operation->request.payload; + request = operation->request->payload; request->op_count = cpu_to_le16(op_count); /* Fill in the ops array */ op = &request->ops[0]; @@ -376,7 +376,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, operation->result); } } else { - response = operation->response.payload; + response = operation->response->payload; gb_i2c_transfer_response(msgs, msg_count, response->data); ret = msg_count; } diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f6940a375364..b02c53144cdd 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -81,7 +81,7 @@ static void gb_pending_operation_insert(struct gb_operation *operation) spin_unlock_irq(&gb_operations_lock); /* Store the operation id in the request header */ - header = operation->request.header; + header = operation->request->header; header->operation_id = cpu_to_le16(operation->id); } @@ -167,7 +167,7 @@ int gb_operation_wait(struct gb_operation *operation) ret = wait_for_completion_interruptible(&operation->completion); /* If interrupted, cancel the in-flight buffer */ if (ret < 0) - gb_message_cancel(&operation->request); + gb_message_cancel(operation->request); return ret; } @@ -177,7 +177,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) struct gb_protocol *protocol = operation->connection->protocol; struct gb_operation_msg_hdr *header; - header = operation->request.header; + header = operation->request->header; /* * If the protocol has no incoming request handler, report @@ -205,7 +205,7 @@ static void gb_operation_recv_work(struct work_struct *recv_work) bool incoming_request; operation = container_of(recv_work, struct gb_operation, recv_work); - incoming_request = operation->response.header == NULL; + incoming_request = operation->response->header == NULL; if (incoming_request) gb_operation_request_handle(operation); gb_operation_complete(operation); @@ -251,61 +251,53 @@ gb_buffer_free(struct greybus_host_device *hd, void *buffer) } /* - * Allocate a buffer to be used for an operation request or response - * message. For outgoing messages, both types of message contain a + * Allocate a message to be used for an operation request or + * response. For outgoing messages, both types of message contain a * common header, which is filled in here. Incoming requests or * responses also contain the same header, but there's no need to * initialize it here (it'll be overwritten by the incoming * message). */ -static int gb_operation_message_init(struct gb_operation *operation, - u8 type, size_t size, - bool request, gfp_t gfp_flags) +static struct gb_message * +gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, + size_t size, gfp_t gfp_flags) { - struct gb_connection *connection = operation->connection; - struct greybus_host_device *hd = connection->hd; struct gb_message *message; struct gb_operation_msg_hdr *header; size += sizeof(*header); if (size > hd->buffer_size_max) - return -E2BIG; + return NULL; - if (request) { - message = &operation->request; - } else { - message = &operation->response; - type |= GB_OPERATION_TYPE_RESPONSE; - } + message = kzalloc(sizeof(*message), gfp_flags); + if (!message) + return NULL; - message->header = gb_buffer_alloc(hd, size, gfp_flags); - if (!message->header) - return -ENOMEM; - message->size = size; + header = gb_buffer_alloc(hd, size, gfp_flags); + if (!header) { + kfree(message); + return NULL; + } /* Fill in the header structure */ - header = message->header; header->size = cpu_to_le16(size); header->operation_id = 0; /* Filled in when submitted */ header->type = type; + message->header = header; message->payload = header + 1; - message->operation = operation; + message->size = size; - return 0; + return message; } -static void gb_operation_message_exit(struct gb_message *message) +static void gb_operation_message_free(struct gb_message *message) { struct greybus_host_device *hd; hd = message->operation->connection->hd; gb_buffer_free(hd, message->header); - - message->operation = NULL; - message->payload = NULL; - message->header = NULL; - message->size = 0; + kfree(message); } /* @@ -358,25 +350,28 @@ gb_operation_create_common(struct gb_connection *connection, bool outgoing, u8 type, size_t request_size, size_t response_size) { + struct greybus_host_device *hd = connection->hd; struct gb_operation *operation; gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC; - int ret; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) return NULL; operation->connection = connection; - ret = gb_operation_message_init(operation, type, request_size, - true, gfp_flags); - if (ret) + operation->request = gb_operation_message_alloc(hd, type, request_size, + gfp_flags); + if (!operation->request) goto err_cache; + operation->request->operation = operation; if (outgoing) { - ret = gb_operation_message_init(operation, type, response_size, - false, GFP_KERNEL); - if (ret) + type |= GB_OPERATION_TYPE_RESPONSE; + operation->response = gb_operation_message_alloc(hd, type, + response_size, GFP_KERNEL); + if (!operation->response) goto err_request; + operation->response->operation = operation; } INIT_WORK(&operation->recv_work, gb_operation_recv_work); @@ -392,7 +387,7 @@ gb_operation_create_common(struct gb_connection *connection, bool outgoing, return operation; err_request: - gb_operation_message_exit(&operation->request); + gb_operation_message_free(operation->request); err_cache: kmem_cache_free(gb_operation_cache, operation); @@ -430,8 +425,8 @@ static void _gb_operation_destroy(struct kref *kref) list_del(&operation->links); spin_unlock_irq(&gb_operations_lock); - gb_operation_message_exit(&operation->response); - gb_operation_message_exit(&operation->request); + gb_operation_message_free(operation->response); + gb_operation_message_free(operation->request); kmem_cache_free(gb_operation_cache, operation); } @@ -478,7 +473,7 @@ int gb_operation_request_send(struct gb_operation *operation, schedule_delayed_work(&operation->timeout_work, timeout); /* All set, send the request */ - ret = gb_message_send(&operation->request, GFP_KERNEL); + ret = gb_message_send(operation->request, GFP_KERNEL); if (ret) return ret; @@ -516,7 +511,7 @@ void gb_connection_recv_request(struct gb_connection *connection, return; /* XXX Respond with pre-allocated ENOMEM */ } operation->id = operation_id; - memcpy(operation->request.header, data, size); + memcpy(operation->request->header, data, size); /* The rest will be handled in work queue context */ queue_work(gb_operation_recv_workqueue, &operation->recv_work); @@ -546,7 +541,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, cancel_delayed_work(&operation->timeout_work); gb_pending_operation_remove(operation); - message = &operation->response; + message = operation->response; if (size <= message->size) { /* Transfer the operation result from the response header */ header = message->header; @@ -609,9 +604,9 @@ void gb_connection_recv(struct gb_connection *connection, void gb_operation_cancel(struct gb_operation *operation) { operation->canceled = true; - gb_message_cancel(&operation->request); - if (operation->response.header) - gb_message_cancel(&operation->response); + gb_message_cancel(operation->request); + if (operation->response->header) + gb_message_cancel(operation->response); } int gb_operation_init(void) diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 38b2833f0dfc..567bb708bc12 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -64,8 +64,8 @@ struct gb_message { typedef void (*gb_operation_callback)(struct gb_operation *); struct gb_operation { struct gb_connection *connection; - struct gb_message request; - struct gb_message response; + struct gb_message *request; + struct gb_message *response; u16 id; bool canceled; diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index c92d8e262b37..d3d39be59201 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -109,7 +109,7 @@ static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) gb_connection_err(connection, "version result %hhu", operation->result); } else { - response = operation->response.payload; + response = operation->response->payload; if (response->major > GB_PWM_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", response->major, GB_PWM_VERSION_MAJOR); @@ -150,7 +150,7 @@ static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) gb_connection_err(connection, "pwm count result %hhu", operation->result); } else { - response = operation->response.payload; + response = operation->response->payload; pwmc->pwm_max = response->count; } out: @@ -175,7 +175,7 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; /* Synchronous operation--no callback */ @@ -212,7 +212,7 @@ static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; /* Synchronous operation--no callback */ @@ -248,7 +248,7 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; request->duty = duty; request->period = period; @@ -287,7 +287,7 @@ static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; request->polarity = polarity; @@ -325,7 +325,7 @@ static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; /* Synchronous operation--no callback */ @@ -362,7 +362,7 @@ static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->which = which; /* Synchronous operation--no callback */ diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 8df3bfb24a7e..7d7e223059a7 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -159,7 +159,7 @@ static int get_version(struct gb_tty *tty) gb_connection_err(tty->connection, "result %hhu", operation->result); } else { - response = operation->response.payload; + response = operation->response->payload; if (response->major > GB_UART_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", response->major, GB_UART_VERSION_MAJOR); @@ -192,7 +192,7 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) sizeof(*request) + size, 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->size = cpu_to_le16(size); memcpy(&request->data[0], data, size); @@ -227,7 +227,7 @@ static int send_line_coding(struct gb_tty *tty, sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; memcpy(&request->line_coding, line_coding, sizeof(*line_coding)); /* Synchronous operation--no callback */ @@ -261,7 +261,7 @@ static int send_control(struct gb_tty *tty, u16 control) sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->control = cpu_to_le16(control); /* Synchronous operation--no callback */ @@ -299,7 +299,7 @@ static int send_break(struct gb_tty *tty, u8 state) sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->state = state; /* Synchronous operation--no callback */ diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index 9ad3cb077f6a..b974973fc067 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -71,7 +71,7 @@ static int request_operation(struct gb_connection *connection, int type, } else { /* Good request, so copy to the caller's buffer */ if (response_size && response) - memcpy(response, operation->response.payload, + memcpy(response, operation->response->payload, response_size); } out: @@ -119,7 +119,7 @@ static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) sizeof(*request), 0); if (!operation) return -ENOMEM; - request = operation->request.payload; + request = operation->request->payload; request->timeout_ms = cpu_to_le16(timeout_ms); /* Synchronous operation--no callback */ -- cgit v1.2.3-59-g8ed1b From 87d208feb74f4297ac2677215252bf7da65e0650 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 20 Nov 2014 16:09:16 -0600 Subject: greybus: embed message buffer into message structure Embed the buffer for message data into the message structure itself. This allows us to use a single allocation for each message, and more importantly will allow us to derive the message structure describing a message from the buffer itself. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 54 ++++++++++++------------------------- drivers/staging/greybus/operation.h | 2 ++ 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b02c53144cdd..c9988fd790e3 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -229,27 +229,6 @@ static void operation_timeout(struct work_struct *work) gb_operation_complete(operation); } -static void * -gb_buffer_alloc(struct greybus_host_device *hd, size_t size, gfp_t gfp_flags) -{ - u8 *buffer; - - buffer = kzalloc(hd->buffer_headroom + size, gfp_flags); - if (buffer) - buffer += hd->buffer_headroom; - - return buffer; -} - -static void -gb_buffer_free(struct greybus_host_device *hd, void *buffer) -{ - u8 *allocated = buffer; - - if (allocated) - kfree(allocated - hd->buffer_headroom); -} - /* * Allocate a message to be used for an operation request or * response. For outgoing messages, both types of message contain a @@ -257,46 +236,47 @@ gb_buffer_free(struct greybus_host_device *hd, void *buffer) * responses also contain the same header, but there's no need to * initialize it here (it'll be overwritten by the incoming * message). + * + * Our message structure consists of: + * message structure + * headroom + * message header \_ these combined are + * message payload / the message size */ static struct gb_message * gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, - size_t size, gfp_t gfp_flags) + size_t payload_size, gfp_t gfp_flags) { struct gb_message *message; struct gb_operation_msg_hdr *header; + size_t message_size = payload_size + sizeof(*header); + size_t size; + u8 *buffer; - size += sizeof(*header); - if (size > hd->buffer_size_max) + if (message_size > hd->buffer_size_max) return NULL; - message = kzalloc(sizeof(*message), gfp_flags); + size = sizeof(*message) + hd->buffer_headroom + message_size; + message = kzalloc(size, gfp_flags); if (!message) return NULL; - - header = gb_buffer_alloc(hd, size, gfp_flags); - if (!header) { - kfree(message); - return NULL; - } + buffer = &message->buffer[0]; + header = (struct gb_operation_msg_hdr *)(buffer + hd->buffer_headroom); /* Fill in the header structure */ - header->size = cpu_to_le16(size); + header->size = cpu_to_le16(message_size); header->operation_id = 0; /* Filled in when submitted */ header->type = type; message->header = header; message->payload = header + 1; - message->size = size; + message->size = message_size; return message; } static void gb_operation_message_free(struct gb_message *message) { - struct greybus_host_device *hd; - - hd = message->operation->connection->hd; - gb_buffer_free(hd, message->header); kfree(message); } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 567bb708bc12..2fdb41c154a8 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -31,6 +31,8 @@ struct gb_message { struct gb_operation *operation; void *cookie; + + u8 buffer[]; }; /* -- cgit v1.2.3-59-g8ed1b From d98b52b04e5bd3d8c47ca9a9bffcf08a43c944c3 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 20 Nov 2014 16:09:17 -0600 Subject: greybus: define greybus_data_sent() Define greybus_data_sent(), which is a callback the host driver makes when a buffer send request has completed. The main use for this is to actively detect errors that can occur while sending. (Something like this existed at one time and was removed.) This also defines gb_hd_message_find(), which looks up a message pointer associated with a buffer sent over a given host device. This is now a pretty trival mapping. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 11 ++++++++- drivers/staging/greybus/operation.c | 44 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/operation.h | 3 +++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 88436436fcb2..f32c981d184c 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -407,9 +407,18 @@ static void cport_out_callback(struct urb *urb) struct greybus_host_device *hd = urb->context; struct es1_ap_dev *es1 = hd_to_es1(hd); unsigned long flags; - /* int status = check_urb_status(urb); */ + int status = check_urb_status(urb); + u8 *data = urb->transfer_buffer + 1; int i; + /* + * Tell the submitter that the buffer send (attempt) is + * complete, and report the status. The submitter's buffer + * starts after the one-byte CPort id we inserted. + */ + data = urb->transfer_buffer + 1; + greybus_data_sent(hd, data, status); + /* * See if this was an urb in our pool, if so mark it "free", otherwise * we need to free it ourselves. diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c9988fd790e3..74dd48a5a489 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -229,6 +229,24 @@ static void operation_timeout(struct work_struct *work) gb_operation_complete(operation); } +/* + * Given a pointer to the header in a message sent on a given host + * device, return the associated message structure. (This "header" + * is just the buffer pointer we supply to the host device for + * sending.) + */ +static struct gb_message * +gb_hd_message_find(struct greybus_host_device *hd, void *header) +{ + struct gb_message *message; + u8 *result; + + result = (u8 *)header - hd->buffer_headroom - sizeof(*message); + message = (struct gb_message *)result; + + return message; +} + /* * Allocate a message to be used for an operation request or * response. For outgoing messages, both types of message contain a @@ -473,6 +491,32 @@ int gb_operation_response_send(struct gb_operation *operation) return 0; } +/* + * This function is called when a buffer send request has completed. + * The "header" is the message header--the beginning of what we + * asked to have sent. + * + * XXX Mismatch between errno here and operation result code + */ +void +greybus_data_sent(struct greybus_host_device *hd, void *header, int status) +{ + struct gb_message *message; + struct gb_operation *operation; + + /* If there's no error, there's really nothing to do */ + if (!status) + return; /* Mark it complete? */ + + /* XXX Right now we assume we're an outgoing request */ + message = gb_hd_message_find(hd, header); + operation = message->operation; + gb_connection_err(operation->connection, "send error %d\n", status); + operation->result = status; /* XXX */ + gb_operation_complete(operation); +} +EXPORT_SYMBOL_GPL(greybus_data_sent); + /* * We've received data on a connection, and it doesn't look like a * response, so we assume it's a request. diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 2fdb41c154a8..3e5e1f570f3e 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -103,6 +103,9 @@ int gb_operation_wait(struct gb_operation *operation); int gb_operation_status_map(u8 status); +void greybus_data_sent(struct greybus_host_device *hd, + void *header, int status); + int gb_operation_init(void); void gb_operation_exit(void); -- cgit v1.2.3-59-g8ed1b From de3557d927735d14a16e44475c078d6ff3060d10 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 20 Nov 2014 16:09:18 -0600 Subject: greybus: rename greybus_cport_in() This function is associated with a host device (interface), not a CPort. Change its name to reflect that, and to match its "sent" callback counterpart. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 ++++++-- drivers/staging/greybus/connection.h | 2 +- drivers/staging/greybus/es1-ap-usb.c | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 584f49164261..8cb2af37c25b 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -29,7 +29,11 @@ struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, return connection; } -void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, +/* + * Callback from the host driver to let us know that data has been + * received on the interface. + */ +void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length) { struct gb_connection *connection; @@ -42,7 +46,7 @@ void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, } gb_connection_recv(connection, data, length); } -EXPORT_SYMBOL_GPL(greybus_cport_in); +EXPORT_SYMBOL_GPL(greybus_data_rcvd); /* * Allocate an available CPort Id for use for the host side of the diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index bcaad47aaa03..e190944508ea 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -53,7 +53,7 @@ void gb_connection_exit(struct gb_connection *connection); struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, u16 cport_id); -void greybus_cport_in(struct greybus_host_device *hd, u16 cport_id, +void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); __printf(2, 3) void gb_connection_err(struct gb_connection *connection, const char *fmt, ...); diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index f32c981d184c..3daa6eb8dd38 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -392,7 +392,7 @@ static void cport_in_callback(struct urb *urb) data = &data[1]; /* Pass this data to the greybus core */ - greybus_cport_in(hd, cport_id, data, urb->actual_length - 1); + greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); exit: /* put our urb back in the request pool */ -- cgit v1.2.3-59-g8ed1b From 696e0ccabd45d4aabf22d2b4a035c3b144a1607c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 21 Nov 2014 11:26:30 +0530 Subject: greybus: Random spell fixes Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 4 ++-- drivers/staging/greybus/connection.h | 2 +- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/es1-ap-usb.c | 2 +- drivers/staging/greybus/manifest.c | 2 +- drivers/staging/greybus/module.c | 2 +- drivers/staging/greybus/operation.c | 12 +++++------- drivers/staging/greybus/protocol.c | 4 ++-- drivers/staging/greybus/uart-gb.c | 2 +- 9 files changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 02178f53cc5b..13ade437d63c 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -165,7 +165,7 @@ static int get_tech(struct gb_battery *gb) /* * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optomize the code away to + * "identical" which should allow gcc to optimize the code away to * nothing. */ technology = le32_to_cpu(tech_response.technology); @@ -209,7 +209,7 @@ static int get_status(struct gb_battery *gb) /* * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optomize the code away to + * "identical" which should allow gcc to optimize the code away to * nothing. */ battery_status = le16_to_cpu(status_response.battery_status); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index e190944508ea..8cde114f5ac2 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -37,7 +37,7 @@ struct gb_connection { u16 op_cycle; struct list_head operations; - struct list_head pending; /* awaiting reponse */ + struct list_head pending; /* awaiting response */ void *private; }; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 2c50dd3e2a47..9203ebd2db7f 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -39,7 +39,7 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) id = gb_module_match_id(gmod, driver->id_table); if (id) return 1; - /* FIXME - Dyanmic ids? */ + /* FIXME - Dynamic ids? */ return 0; } diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 3daa6eb8dd38..bab6f259dd70 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -450,7 +450,7 @@ static void cport_out_callback(struct urb *urb) * * This protocol is "needed" due to some hardware restrictions on the * current generation of Unipro controllers. Think about it for a - * minute, this is a USB driver, talking to a Unipro bridge, impediance + * minute, this is a USB driver, talking to a Unipro bridge, impedance * mismatch is huge, yet the Unipro controller are even more * underpowered than this little USB controller. We rely on the round * trip to keep stalls in the Unipro controllers from happening so that diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 37540350cb75..28abd2ad395e 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -46,7 +46,7 @@ static void release_manifest_descriptors(void) /* * Validate the given descriptor. Its reported size must fit within - * the number of bytes reamining, and it must have a recognized + * the number of bytes remaining, and it must have a recognized * type. Check that the reported size is at least as big as what * we expect to see. (It could be bigger, perhaps for a new version * of the format.) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index f432bea3c818..f72e6aef4dc4 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -68,7 +68,7 @@ struct device_type greybus_module_type = { }; /* - * A Greybus module represents a user-replacable component on an Ara + * A Greybus module represents a user-replicable component on an Ara * phone. * * Create a gb_module structure to represent a discovered module. diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 74dd48a5a489..442046562bfb 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -146,7 +146,7 @@ static void gb_message_cancel(struct gb_message *message) } /* - * An operations's response message has arrived. If no callback was + * An operation's response message has arrived. If no callback was * supplied it was submitted for asynchronous completion, so we notify * any waiters. Otherwise we assume calling the completion is enough * and nobody else will be waiting. @@ -169,7 +169,6 @@ int gb_operation_wait(struct gb_operation *operation) if (ret < 0) gb_message_cancel(operation->request); return ret; - } static void gb_operation_request_handle(struct gb_operation *operation) @@ -299,9 +298,8 @@ static void gb_operation_message_free(struct gb_message *message) } /* - * Map an enum gb_operation_status value (which is represted in a - * message as a single back a single byte) to an appropriate Linux - * negative errno. + * Map an enum gb_operation_status value (which is represented in a + * message as a single byte) to an appropriate Linux negative errno. */ int gb_operation_status_map(u8 status) { @@ -329,7 +327,7 @@ int gb_operation_status_map(u8 status) /* * Create a Greybus operation to be sent over the given connection. - * The request buffer will big enough for a payload of the given + * The request buffer will be big enough for a payload of the given * size. Outgoing requests must specify the size of the response * buffer size, which must be sufficient to hold all expected * response data. @@ -544,7 +542,7 @@ void gb_connection_recv_request(struct gb_connection *connection, /* * We've received data that appears to be an operation response * message. Look up the operation, and record that we've received - * its repsonse. + * its response. * * This is called in interrupt context, so just copy the incoming * data into the response buffer and handle the rest via workqueue. diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 346120f8d4e3..55ef387b812f 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -38,7 +38,7 @@ static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) return NULL; } -/* Returns true if protocol was succesfully registered, false otherwise */ +/* Returns true if protocol was successfully registered, false otherwise */ bool gb_protocol_register(struct gb_protocol *protocol) { struct gb_protocol *existing; @@ -93,7 +93,7 @@ bool gb_protocol_register(struct gb_protocol *protocol) * XXX Currently this fails (and reports an error to the caller) if * XXX the protocol is currently in use. We may want to forcefully * XXX kill off a protocol and all its active users at some point. - * XXX But I think that's better handled by quescing modules that + * XXX But I think that's better handled by quiescing modules that * XXX have users and having those users drop their reference. * * Returns true if successful, false otherwise. diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 7d7e223059a7..48fad7b4c886 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -816,7 +816,7 @@ static void gb_uart_connection_exit(struct gb_connection *connection) tty_unregister_device(gb_tty_driver, gb_tty->minor); - /* FIXME - free transmit / recieve buffers */ + /* FIXME - free transmit / receive buffers */ tty_port_put(&gb_tty->port); -- cgit v1.2.3-59-g8ed1b From 23383defa8395eb34d02dd4d4fc1d95e95c5603d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Nov 2014 19:29:12 -0600 Subject: greybus: use errno for operation result An in-core operation structure tracks the progress of an operation. Currently it holds a result field that was intended to take the status value that arrives in an operation response message header. But operations can fail for reasons other than that, and it's inconvenient to try to represent those using the operation status codes. So change the operation->result field to be an int, and switch to storing negative errno values in it. Rename it "errno" to make it obvious how to interpret the value. This patch makes another change, which simplifies the protocol drivers a lot. It's being done as part of this patch because it affects all the same code as the above change does. If desired I can split this into two separate patches. If a caller makes a synchronous gb_operation_request_send() request (i.e., no callback function is supplied), and the operation request and response messages were transferred successfully, have gb_operation_request_send() return the result of the request (i.e., operation->errno). This allows the caller (or more generally, any caller of gb_request_wait() to avoid having to look at this field for every successful send. Any caller that does an asynchronous request will of course need to look at request->errno in the callback function to see the result of the operation. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 21 +--- drivers/staging/greybus/gpio-gb.c | 176 +++++++--------------------------- drivers/staging/greybus/i2c-gb.c | 75 ++++----------- drivers/staging/greybus/operation.c | 29 +++--- drivers/staging/greybus/operation.h | 5 +- drivers/staging/greybus/pwm-gb.c | 96 +++---------------- drivers/staging/greybus/uart-gb.c | 72 +++----------- drivers/staging/greybus/vibrator-gb.c | 42 +++----- 8 files changed, 114 insertions(+), 402 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 13ade437d63c..ae03869373d8 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -88,7 +88,7 @@ struct gb_battery_voltage_response { * None of the battery operation requests have any payload. This * function implements all of the requests by allowing the caller to * supply a buffer into which the operation response should be - * copied. + * copied. If there is an error, the response buffer is left alone. */ static int battery_operation(struct gb_battery *gb, int type, void *response, int response_size) @@ -103,25 +103,10 @@ static int battery_operation(struct gb_battery *gb, int type, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("version operation failed (%d)\n", ret); - goto out; - } - - /* - * We only want to look at the status, and all requests have the same - * layout for where the status is, so cast this to a random request so - * we can see the status easier. - */ - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "operation result %hhu", - operation->result); - } else { - /* Good response, so copy to the caller's buffer */ + else /* Good response, so copy to the caller's buffer */ memcpy(response, operation->response->payload, response_size); - } -out: gb_operation_destroy(operation); return ret; diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 170f8aa289ca..c6c85d8c28d2 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -116,7 +116,8 @@ struct gb_gpio_set_debounce_request { * This request only uses the connection field, and if successful, * fills in the major and minor protocol version of the target. */ -static int gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_controller) +static int +gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_controller) { struct gb_connection *connection = gb_gpio_controller->connection; struct gb_operation *operation; @@ -137,24 +138,14 @@ static int gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_co goto out; } - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "version result %hhu", - operation->result); + response = operation->response->payload; + if (response->major > GB_GPIO_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response->major, GB_GPIO_VERSION_MAJOR); + ret = -ENOTSUPP; } else { - response = operation->response->payload; - if (response->major > GB_GPIO_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response->major, GB_GPIO_VERSION_MAJOR); - ret = -ENOTSUPP; - goto out; - } gb_gpio_controller->version_major = response->major; gb_gpio_controller->version_minor = response->minor; - - pr_debug("%s: version_major = %u version_minor = %u\n", __func__, - gb_gpio_controller->version_major, - gb_gpio_controller->version_minor); } out: gb_operation_destroy(operation); @@ -180,21 +171,10 @@ static int gb_gpio_line_count_operation(struct gb_gpio_controller *gb_gpio_contr ret = gb_operation_request_send(operation, NULL); if (ret) { pr_err("line count operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "line count result %hhu", - operation->result); } else { response = operation->response->payload; gb_gpio_controller->line_max = response->count; - - pr_debug("%s: count = %u\n", __func__, - gb_gpio_controller->line_max + 1); } -out: gb_operation_destroy(operation); return ret; @@ -222,21 +202,10 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_control /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("activate operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "activate result %hhu", - operation->result); - } else { + else gb_gpio_controller->lines[which].active = true; - - pr_debug("%s: %u is now active\n", __func__, which); - } -out: gb_operation_destroy(operation); return ret; @@ -264,20 +233,10 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_contr /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("deactivate operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "deactivate result %hhu", - operation->result); - } else { + else gb_gpio_controller->lines[which].active = false; - pr_debug("%s: %u is now inactive\n", __func__, which); - } -out: gb_operation_destroy(operation); return ret; @@ -291,6 +250,7 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co struct gb_gpio_get_direction_request *request; struct gb_gpio_get_direction_response *response; int ret; + u8 direction; if (which > gb_gpio_controller->line_max) return -EINVAL; @@ -310,22 +270,12 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_co goto out; } - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "get direction result %hhu", - operation->result); - } else { - u8 direction; - - response = operation->response->payload; - direction = response->direction; - if (direction && direction != 1) - pr_warn("gpio %u direction was %u (should be 0 or 1)\n", - which, direction); - gb_gpio_controller->lines[which].direction = direction ? 1 : 0; - pr_debug("%s: direction of %u is %s\n", __func__, which, - direction ? "in" : "out"); - } + response = operation->response->payload; + direction = response->direction; + if (direction && direction != 1) + pr_warn("gpio %u direction was %u (should be 0 or 1)\n", + which, direction); + gb_gpio_controller->lines[which].direction = direction ? 1 : 0; out: gb_operation_destroy(operation); @@ -354,20 +304,10 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_con /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("direction in operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "direction in result %hhu", - operation->result); - } else { + else gb_gpio_controller->lines[which].direction = 1; - pr_debug("%s: direction of %u is now in\n", __func__, which); - } -out: gb_operation_destroy(operation); return ret; @@ -396,21 +336,10 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_co /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("direction out operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "direction out result %hhu", - operation->result); - } else { + else gb_gpio_controller->lines[which].direction = 0; - pr_debug("%s: direction of %u is now out, value %s\n", __func__, - which, value_high ? "high" : "low"); - } -out: gb_operation_destroy(operation); return ret; @@ -424,6 +353,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro struct gb_gpio_get_value_request *request; struct gb_gpio_get_value_response *response; int ret; + u8 value; if (which > gb_gpio_controller->line_max) return -EINVAL; @@ -443,24 +373,12 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_contro goto out; } - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "get value result %hhu", - operation->result); - } else { - u8 value; - - response = operation->response->payload; - value = response->value; - if (value && value != 1) - pr_warn("gpio %u value was %u (should be 0 or 1)\n", - which, value); - gb_gpio_controller->lines[which].value = value ? 1 : 0; - /* XXX should this set direction to out? */ - pr_debug("%s: value of %u is %s\n", __func__, which, - gb_gpio_controller->lines[which].value ? "high" : - "low"); - } + response = operation->response->payload; + value = response->value; + if (value && value != 1) + pr_warn("gpio %u value was %u (should be 0 or 1)\n", + which, value); + gb_gpio_controller->lines[which].value = value ? 1 : 0; out: gb_operation_destroy(operation); @@ -490,23 +408,10 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_contro /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("set value operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "set value result %hhu", - operation->result); - } else { - /* XXX should this set direction to out? */ + else /* XXX should this set direction to out? */ gb_gpio_controller->lines[which].value = request->value; - pr_debug("%s: out value of %u is now %s\n", __func__, which, - gb_gpio_controller->lines[which].value ? "high" : - "low"); - } -out: gb_operation_destroy(operation); return ret; @@ -535,21 +440,11 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_con /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("set debounce operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "set debounce result %hhu", - operation->result); - } else { - gb_gpio_controller->lines[which].debounce_usec = le16_to_cpu(request->usec); - pr_debug("%s: debounce of %u is now %hu usec\n", __func__, which, - gb_gpio_controller->lines[which].debounce_usec); - } -out: + else + gb_gpio_controller->lines[which].debounce_usec = + le16_to_cpu(request->usec); gb_operation_destroy(operation); return ret; @@ -562,7 +457,6 @@ static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) if (offset < 0 || offset >= chip->ngpio) return -EINVAL; - pr_debug("%s: passed check\n", __func__); ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); if (ret) ; /* return ret; */ diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 2a5fb822535e..6b2fd7e934e3 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -111,23 +111,16 @@ static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) goto out; } - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "version result %hhu", - operation->result); - } else { - response = operation->response->payload; - if (response->major > GB_I2C_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response->major, GB_I2C_VERSION_MAJOR); - ret = -ENOTSUPP; - goto out; - } - gb_i2c_dev->version_major = response->major; - gb_i2c_dev->version_minor = response->minor; + response = operation->response->payload; + if (response->major > GB_I2C_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response->major, GB_I2C_VERSION_MAJOR); + ret = -ENOTSUPP; + goto out; } + gb_i2c_dev->version_major = response->major; + gb_i2c_dev->version_minor = response->minor; out: - gb_operation_destroy(operation); return ret; @@ -163,16 +156,9 @@ static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) goto out; } - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "functionality result %hhu", - operation->result); - } else { - response = operation->response->payload; - functionality = le32_to_cpu(response->functionality); - gb_i2c_dev->functionality = - gb_i2c_functionality_map(functionality); - } + response = operation->response->payload; + functionality = le32_to_cpu(response->functionality); + gb_i2c_dev->functionality = gb_i2c_functionality_map(functionality); out: gb_operation_destroy(operation); @@ -195,19 +181,10 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("timeout operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "timeout result %hhu", - operation->result); - } else { + else gb_i2c_dev->timeout_msec = msec; - } -out: gb_operation_destroy(operation); return ret; @@ -230,19 +207,10 @@ static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("retries operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "retries result %hhu", - operation->result); - } else { + else gb_i2c_dev->retries = retries; - } -out: gb_operation_destroy(operation); return ret; @@ -365,22 +333,13 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); if (ret) { - pr_err("transfer operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - if (ret != -EAGAIN) { - gb_connection_err(connection, "transfer result %hhu", - operation->result); - } + if (ret != -EAGAIN) + pr_err("transfer operation failed (%d)\n", ret); } else { response = operation->response->payload; gb_i2c_transfer_response(msgs, msg_count, response->data); ret = msg_count; } -out: gb_operation_destroy(operation); return ret; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 442046562bfb..5ab1c47f5521 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -159,7 +159,11 @@ static void gb_operation_complete(struct gb_operation *operation) complete_all(&operation->completion); } -/* Wait for a submitted operation to complete */ +/* + * Wait for a submitted operation to complete. Returns -RESTARTSYS + * if the wait was interrupted. Otherwise returns the result of the + * operation. + */ int gb_operation_wait(struct gb_operation *operation) { int ret; @@ -168,6 +172,8 @@ int gb_operation_wait(struct gb_operation *operation) /* If interrupted, cancel the in-flight buffer */ if (ret < 0) gb_message_cancel(operation->request); + else + ret = operation->errno; return ret; } @@ -189,7 +195,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) gb_connection_err(operation->connection, "unexpected incoming request type 0x%02hhx\n", header->type); - operation->result = GB_OP_PROTOCOL_BAD; + operation->errno = -EPROTONOSUPPORT; } /* @@ -224,7 +230,7 @@ static void operation_timeout(struct work_struct *work) operation = container_of(work, struct gb_operation, timeout_work.work); pr_debug("%s: timeout!\n", __func__); - operation->result = GB_OP_TIMEOUT; + operation->errno = -ETIMEDOUT; gb_operation_complete(operation); } @@ -470,13 +476,10 @@ int gb_operation_request_send(struct gb_operation *operation, /* All set, send the request */ ret = gb_message_send(operation->request, GFP_KERNEL); - if (ret) + if (ret || callback) return ret; - if (!callback) - ret = gb_operation_wait(operation); - - return ret; + return gb_operation_wait(operation); } /* @@ -493,8 +496,6 @@ int gb_operation_response_send(struct gb_operation *operation) * This function is called when a buffer send request has completed. * The "header" is the message header--the beginning of what we * asked to have sent. - * - * XXX Mismatch between errno here and operation result code */ void greybus_data_sent(struct greybus_host_device *hd, void *header, int status) @@ -510,7 +511,7 @@ greybus_data_sent(struct greybus_host_device *hd, void *header, int status) message = gb_hd_message_find(hd, header); operation = message->operation; gb_connection_err(operation->connection, "send error %d\n", status); - operation->result = status; /* XXX */ + operation->errno = status; gb_operation_complete(operation); } EXPORT_SYMBOL_GPL(greybus_data_sent); @@ -567,14 +568,14 @@ static void gb_connection_recv_response(struct gb_connection *connection, if (size <= message->size) { /* Transfer the operation result from the response header */ header = message->header; - operation->result = header->result; + operation->errno = gb_operation_status_map(header->result); } else { gb_connection_err(connection, "recv buffer too small"); - operation->result = GB_OP_OVERFLOW; + operation->errno = -E2BIG; } /* We must ignore the payload if a bad status is returned */ - if (operation->result == GB_OP_SUCCESS) + if (!operation->errno) memcpy(message->header, data, size); /* The rest will be handled in work queue context */ diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 3e5e1f570f3e..33d50039327b 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -13,7 +13,7 @@ struct gb_operation; -enum gb_operation_status { +enum gb_operation_result { GB_OP_SUCCESS = 0, GB_OP_INVALID = 1, GB_OP_NO_MEMORY = 2, @@ -71,7 +71,8 @@ struct gb_operation { u16 id; bool canceled; - u8 result; + int errno; /* Operation result */ + struct work_struct recv_work; gb_operation_callback callback; /* If asynchronous */ struct completion completion; /* Used if no callback */ diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index d3d39be59201..e146b240f89e 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -104,21 +104,15 @@ static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) goto out; } - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "version result %hhu", - operation->result); - } else { - response = operation->response->payload; - if (response->major > GB_PWM_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response->major, GB_PWM_VERSION_MAJOR); - ret = -ENOTSUPP; - goto out; - } - pwmc->version_major = response->major; - pwmc->version_minor = response->minor; + response = operation->response->payload; + if (response->major > GB_PWM_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response->major, GB_PWM_VERSION_MAJOR); + ret = -ENOTSUPP; + goto out; } + pwmc->version_major = response->major; + pwmc->version_minor = response->minor; out: gb_operation_destroy(operation); @@ -142,18 +136,10 @@ static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) ret = gb_operation_request_send(operation, NULL); if (ret) { pr_err("line count operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "pwm count result %hhu", - operation->result); } else { response = operation->response->payload; pwmc->pwm_max = response->count; } -out: gb_operation_destroy(operation); return ret; @@ -180,17 +166,8 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("activate operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "activate result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return ret; @@ -217,17 +194,8 @@ static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("deactivate operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "deactivate result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return ret; @@ -255,17 +223,8 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("config operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "config result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return ret; @@ -293,17 +252,8 @@ static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("set polarity operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "set polarity result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return ret; @@ -330,17 +280,8 @@ static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("enable operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "enable result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return ret; @@ -367,17 +308,8 @@ static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("disable operation failed (%d)\n", ret); - goto out; - } - - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "disable result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return ret; diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 48fad7b4c886..e9faf310e39c 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -154,24 +154,18 @@ static int get_version(struct gb_tty *tty) goto out; } - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(tty->connection, "result %hhu", - operation->result); - } else { - response = operation->response->payload; - if (response->major > GB_UART_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response->major, GB_UART_VERSION_MAJOR); - ret = -ENOTSUPP; - goto out; - } - tty->version_major = response->major; - tty->version_minor = response->minor; - - pr_debug("%s: version_major = %u version_minor = %u\n", - __func__, tty->version_major, tty->version_minor); + response = operation->response->payload; + if (response->major > GB_UART_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response->major, GB_UART_VERSION_MAJOR); + ret = -ENOTSUPP; + goto out; } + tty->version_major = response->major; + tty->version_minor = response->minor; + + pr_debug("%s: version_major = %u version_minor = %u\n", + __func__, tty->version_major, tty->version_minor); out: gb_operation_destroy(operation); @@ -198,18 +192,9 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) /* Synchronous operation--no callback */ retval = gb_operation_request_send(operation, NULL); - if (retval) { + if (retval) dev_err(&connection->dev, "send data operation failed (%d)\n", retval); - goto out; - } - - if (operation->result) { - retval = gb_operation_status_map(operation->result); - gb_connection_err(connection, "send data result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return retval; @@ -232,18 +217,9 @@ static int send_line_coding(struct gb_tty *tty, /* Synchronous operation--no callback */ retval = gb_operation_request_send(operation, NULL); - if (retval) { + if (retval) dev_err(&connection->dev, "send line coding operation failed (%d)\n", retval); - goto out; - } - - if (operation->result) { - retval = gb_operation_status_map(operation->result); - gb_connection_err(connection, "send line coding result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return retval; @@ -266,18 +242,9 @@ static int send_control(struct gb_tty *tty, u16 control) /* Synchronous operation--no callback */ retval = gb_operation_request_send(operation, NULL); - if (retval) { + if (retval) dev_err(&connection->dev, "send control operation failed (%d)\n", retval); - goto out; - } - - if (operation->result) { - retval = gb_operation_status_map(operation->result); - gb_connection_err(connection, "send control result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return retval; @@ -304,18 +271,9 @@ static int send_break(struct gb_tty *tty, u8 state) /* Synchronous operation--no callback */ retval = gb_operation_request_send(operation, NULL); - if (retval) { + if (retval) dev_err(&connection->dev, "send break operation failed (%d)\n", retval); - goto out; - } - - if (operation->result) { - retval = gb_operation_status_map(operation->result); - gb_connection_err(connection, "send break result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return retval; diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index b974973fc067..b65a586b87c9 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -42,6 +42,13 @@ struct gb_vibrator_on_request { __le16 timeout_ms; }; +/* + * The get_version and turn_off vibrator operations have no payload. + * This function implements these requests by allowing the caller to + * supply a buffer into which the operation response should be + * copied. The turn_off operation, there is no response either. + * If there is an error, the response buffer is left alone. + */ static int request_operation(struct gb_connection *connection, int type, void *response, int response_size) { @@ -54,27 +61,11 @@ static int request_operation(struct gb_connection *connection, int type, /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); - if (ret) { + if (ret) pr_err("version operation failed (%d)\n", ret); - goto out; - } - - /* - * We only want to look at the status, and all requests have the same - * layout for where the status is, so cast this to a random request so - * we can see the status easier. - */ - if (operation->result) { - ret = gb_operation_status_map(operation->result); - gb_connection_err(connection, "operation result %hhu", - operation->result); - } else { - /* Good request, so copy to the caller's buffer */ - if (response_size && response) - memcpy(response, operation->response->payload, - response_size); - } -out: + else if (response_size && response) + memcpy(response, operation->response->payload, + response_size); gb_operation_destroy(operation); return ret; @@ -124,18 +115,9 @@ static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) /* Synchronous operation--no callback */ retval = gb_operation_request_send(operation, NULL); - if (retval) { + if (retval) dev_err(&connection->dev, "send data operation failed (%d)\n", retval); - goto out; - } - - if (operation->result) { - retval = gb_operation_status_map(operation->result); - gb_connection_err(connection, "send data result %hhu", - operation->result); - } -out: gb_operation_destroy(operation); return retval; -- cgit v1.2.3-59-g8ed1b From ee637a9b0e7452c1e3fa12f2d70bc4736a486096 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Nov 2014 19:29:13 -0600 Subject: greybus: abandon incoming requests for now Change the operation "receive workqueue" to be just the operation "workqueue". All it does is complete an operation in non-atomic context. This is all that's required for an outgoing request. Similarly, ignore any notion that a response will only exist for outgoing requests in gb_operation_cancel(). I'm doing this in the interest of getting the outgoing request path verified without the encumbrance of any preconceptions about how incoming requests need to work. When I finally turn my full attenion to incoming requests I'll adapt the code as needed. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 38 +++++++++++++++++-------------------- drivers/staging/greybus/operation.h | 2 +- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5ab1c47f5521..4e4fa8b0123c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -30,7 +30,7 @@ static struct kmem_cache *gb_operation_cache; /* Workqueue to handle Greybus operation completions. */ -static struct workqueue_struct *gb_operation_recv_workqueue; +static struct workqueue_struct *gb_operation_workqueue; /* * All operation messages (both requests and responses) begin with @@ -177,6 +177,7 @@ int gb_operation_wait(struct gb_operation *operation) return ret; } +#if 0 static void gb_operation_request_handle(struct gb_operation *operation) { struct gb_protocol *protocol = operation->connection->protocol; @@ -197,22 +198,17 @@ static void gb_operation_request_handle(struct gb_operation *operation) "unexpected incoming request type 0x%02hhx\n", header->type); operation->errno = -EPROTONOSUPPORT; } +#endif /* - * Either this operation contains an incoming request, or its - * response has arrived. An incoming request will have a null - * response buffer pointer (it is the responsibility of the request - * handler to allocate and fill in the response buffer). + * Complete an operation in non-atomic context. The operation's + * result value should have been set before queueing this. */ -static void gb_operation_recv_work(struct work_struct *recv_work) +static void gb_operation_work(struct work_struct *work) { struct gb_operation *operation; - bool incoming_request; - operation = container_of(recv_work, struct gb_operation, recv_work); - incoming_request = operation->response->header == NULL; - if (incoming_request) - gb_operation_request_handle(operation); + operation = container_of(work, struct gb_operation, work); gb_operation_complete(operation); } @@ -376,7 +372,7 @@ gb_operation_create_common(struct gb_connection *connection, bool outgoing, operation->response->operation = operation; } - INIT_WORK(&operation->recv_work, gb_operation_recv_work); + INIT_WORK(&operation->work, gb_operation_work); operation->callback = NULL; /* set at submit time */ init_completion(&operation->completion); INIT_DELAYED_WORK(&operation->timeout_work, operation_timeout); @@ -536,8 +532,9 @@ void gb_connection_recv_request(struct gb_connection *connection, operation->id = operation_id; memcpy(operation->request->header, data, size); - /* The rest will be handled in work queue context */ - queue_work(gb_operation_recv_workqueue, &operation->recv_work); + /* XXX Right now this will just complete the operation */ + operation->errno = -ENOSYS; + queue_work(gb_operation_workqueue, &operation->work); } /* @@ -579,7 +576,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, memcpy(message->header, data, size); /* The rest will be handled in work queue context */ - queue_work(gb_operation_recv_workqueue, &operation->recv_work); + queue_work(gb_operation_workqueue, &operation->work); } /* @@ -628,8 +625,7 @@ void gb_operation_cancel(struct gb_operation *operation) { operation->canceled = true; gb_message_cancel(operation->request); - if (operation->response->header) - gb_message_cancel(operation->response); + gb_message_cancel(operation->response); } int gb_operation_init(void) @@ -639,8 +635,8 @@ int gb_operation_init(void) if (!gb_operation_cache) return -ENOMEM; - gb_operation_recv_workqueue = alloc_workqueue("greybus_recv", 0, 1); - if (!gb_operation_recv_workqueue) { + gb_operation_workqueue = alloc_workqueue("greybus_operation", 0, 1); + if (!gb_operation_workqueue) { kmem_cache_destroy(gb_operation_cache); gb_operation_cache = NULL; return -ENOMEM; @@ -651,8 +647,8 @@ int gb_operation_init(void) void gb_operation_exit(void) { - destroy_workqueue(gb_operation_recv_workqueue); - gb_operation_recv_workqueue = NULL; + destroy_workqueue(gb_operation_workqueue); + gb_operation_workqueue = NULL; kmem_cache_destroy(gb_operation_cache); gb_operation_cache = NULL; } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 33d50039327b..f60884455f4b 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -73,7 +73,7 @@ struct gb_operation { int errno; /* Operation result */ - struct work_struct recv_work; + struct work_struct work; gb_operation_callback callback; /* If asynchronous */ struct completion completion; /* Used if no callback */ struct delayed_work timeout_work; -- cgit v1.2.3-59-g8ed1b From 583c3117a4d36499adfe6de186826d59f5c4d788 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Nov 2014 19:29:14 -0600 Subject: greybus: handle data send errors in workqueue The data sent callback can execute in atomic context. If an error occurred, we shouldn't be completing the operation right then and there. Instead, hand it off to the operation workqueue to complete the operation. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 4e4fa8b0123c..9ad714eb773c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -506,9 +506,8 @@ greybus_data_sent(struct greybus_host_device *hd, void *header, int status) /* XXX Right now we assume we're an outgoing request */ message = gb_hd_message_find(hd, header); operation = message->operation; - gb_connection_err(operation->connection, "send error %d\n", status); operation->errno = status; - gb_operation_complete(operation); + queue_work(gb_operation_workqueue, &operation->work); } EXPORT_SYMBOL_GPL(greybus_data_sent); -- cgit v1.2.3-59-g8ed1b From deb4b9efb30606b8cb1150b8ae1e922405f60f8d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Nov 2014 19:29:15 -0600 Subject: greybus: add a reference to pending operations Grab an extra reference to an operation before sending it. Drop that reference at the end of its completion handling. It turns out gb_operation_get() got deleted along the way, so this re-introduces it. We're assuming we only get a reference when there's at least one in existence so we don't need a semaphore to protect it. Emphasize this by *not* returning a pointer to the referenced operation. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 21 +++++++++++++++++---- drivers/staging/greybus/operation.h | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 9ad714eb773c..ab27cd94880a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -157,6 +157,7 @@ static void gb_operation_complete(struct gb_operation *operation) operation->callback(operation); else complete_all(&operation->completion); + gb_operation_put(operation); } /* @@ -409,6 +410,14 @@ gb_operation_create_incoming(struct gb_connection *connection, request_size, response_size); } +/* + * Get an additional reference on an operation. + */ +void gb_operation_get(struct gb_operation *operation) +{ + kref_get(&operation->kref); +} + /* * Destroy a previously created operation. */ @@ -429,6 +438,10 @@ static void _gb_operation_destroy(struct kref *kref) kmem_cache_free(gb_operation_cache, operation); } +/* + * Drop a reference on an operation, and destroy it when the last + * one is gone. + */ void gb_operation_put(struct gb_operation *operation) { if (!WARN_ON(!operation)) @@ -454,11 +467,11 @@ int gb_operation_request_send(struct gb_operation *operation, return -ENOTCONN; /* - * XXX - * I think the order of operations is going to be - * significant, and if so, we may need a mutex to surround - * setting the operation id and submitting the buffer. + * First, get an extra reference on the operation. + * It'll be dropped when the operation completes. */ + gb_operation_get(operation); + operation->callback = callback; gb_pending_operation_insert(operation); diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index f60884455f4b..befce156aa67 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -88,7 +88,7 @@ void gb_connection_recv(struct gb_connection *connection, struct gb_operation *gb_operation_create(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size); -struct gb_operation *gb_operation_get(struct gb_operation *operation); +void gb_operation_get(struct gb_operation *operation); void gb_operation_put(struct gb_operation *operation); static inline void gb_operation_destroy(struct gb_operation *operation) { -- cgit v1.2.3-59-g8ed1b From 0e3d0e8fad52ed478647a1a122e21c7deb7f96cf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Nov 2014 19:29:16 -0600 Subject: greybus: minor tweak in gb_connection_recv_response() Any time we queue work on the operation work queue we need to have set the operation errno first. This patch moves the assignment of that field to be immediately prior to the queue_work() call in gb_connection_recv_response(), so it is easier to see at a glance that this has been done. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index ab27cd94880a..32cd2358ab1c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -563,6 +563,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, struct gb_operation *operation; struct gb_message *message; struct gb_operation_msg_hdr *header; + int result; operation = gb_pending_operation_find(connection, operation_id); if (!operation) { @@ -577,17 +578,18 @@ static void gb_connection_recv_response(struct gb_connection *connection, if (size <= message->size) { /* Transfer the operation result from the response header */ header = message->header; - operation->errno = gb_operation_status_map(header->result); + result = gb_operation_status_map(header->result); } else { gb_connection_err(connection, "recv buffer too small"); - operation->errno = -E2BIG; + result = -E2BIG; } /* We must ignore the payload if a bad status is returned */ - if (!operation->errno) + if (!result) memcpy(message->header, data, size); /* The rest will be handled in work queue context */ + operation->errno = result; queue_work(gb_operation_workqueue, &operation->work); } -- cgit v1.2.3-59-g8ed1b From f68c05c0216e09ed93b12894747d8583dc5b8f8e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Nov 2014 19:29:17 -0600 Subject: greybus: cancel operation on timeout If an operation times out, we need to cancel whatever message it has in-flight. Do that instead of completing the operation, in the timeout handler. When the in-flight request message is canceled its completion function will lead to the proper completion of the operation. Change gb_operation_cancel() so it takes the errno that it's supposed to assign as the result of the operation. Note that we want to preserve the original -ETIMEDOUT error, so don't overwrite the operation result value if it has already been set. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/operation.c | 19 ++++++------------- drivers/staging/greybus/operation.h | 3 +-- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 8cb2af37c25b..6503546ccc01 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -212,7 +212,7 @@ void gb_connection_destroy(struct gb_connection *connection) if (WARN_ON(!list_empty(&connection->operations))) { list_for_each_entry_safe(operation, next, &connection->operations, links) - gb_operation_cancel(operation); + gb_operation_cancel(operation, -ESHUTDOWN); } spin_lock_irq(&gb_connections_lock); list_del(&connection->interface_links); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 32cd2358ab1c..dc12e6df043d 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -215,20 +215,13 @@ static void gb_operation_work(struct work_struct *work) /* * Timeout call for the operation. - * - * If this fires, something went wrong, so mark the result as timed out, and - * run the completion handler, which (hopefully) should clean up the operation - * properly. */ -static void operation_timeout(struct work_struct *work) +static void gb_operation_timeout(struct work_struct *work) { struct gb_operation *operation; operation = container_of(work, struct gb_operation, timeout_work.work); - pr_debug("%s: timeout!\n", __func__); - - operation->errno = -ETIMEDOUT; - gb_operation_complete(operation); + gb_operation_cancel(operation, -ETIMEDOUT); } /* @@ -376,7 +369,7 @@ gb_operation_create_common(struct gb_connection *connection, bool outgoing, INIT_WORK(&operation->work, gb_operation_work); operation->callback = NULL; /* set at submit time */ init_completion(&operation->completion); - INIT_DELAYED_WORK(&operation->timeout_work, operation_timeout); + INIT_DELAYED_WORK(&operation->timeout_work, gb_operation_timeout); kref_init(&operation->kref); spin_lock_irq(&gb_operations_lock); @@ -633,11 +626,11 @@ void gb_connection_recv(struct gb_connection *connection, } /* - * Cancel an operation. + * Cancel an operation, and record the given error to indicate why. */ -void gb_operation_cancel(struct gb_operation *operation) +void gb_operation_cancel(struct gb_operation *operation, int errno) { - operation->canceled = true; + operation->errno = errno; gb_message_cancel(operation->request); gb_message_cancel(operation->response); } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index befce156aa67..d24e5e0d18f0 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -69,7 +69,6 @@ struct gb_operation { struct gb_message *request; struct gb_message *response; u16 id; - bool canceled; int errno; /* Operation result */ @@ -99,7 +98,7 @@ int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback); int gb_operation_response_send(struct gb_operation *operation); -void gb_operation_cancel(struct gb_operation *operation); +void gb_operation_cancel(struct gb_operation *operation, int errno); int gb_operation_wait(struct gb_operation *operation); int gb_operation_status_map(u8 status); -- cgit v1.2.3-59-g8ed1b From 7035833f081cefc3953a6a72a61767967f59a59e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Nov 2014 19:29:18 -0600 Subject: greybus: cancel whole operation on interrupt Cancel the operation--not just the request message--if waiting for a synchronous operation to complete is interrupted. Return the operation result (which in that case will be -EINTR). The cancelation will result in the normal operation completion path being taken before returning. Make gb_operation_wait() private, since it's only ever used for for synchronous operations. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 15 ++++++--------- drivers/staging/greybus/operation.h | 1 - 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index dc12e6df043d..f4d984f68055 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -161,21 +161,18 @@ static void gb_operation_complete(struct gb_operation *operation) } /* - * Wait for a submitted operation to complete. Returns -RESTARTSYS - * if the wait was interrupted. Otherwise returns the result of the - * operation. + * Wait for a submitted operation to complete. Returns the result + * of the operation; this will be -EINTR if the wait was interrupted. */ -int gb_operation_wait(struct gb_operation *operation) +static int gb_operation_wait(struct gb_operation *operation) { int ret; ret = wait_for_completion_interruptible(&operation->completion); - /* If interrupted, cancel the in-flight buffer */ if (ret < 0) - gb_message_cancel(operation->request); - else - ret = operation->errno; - return ret; + gb_operation_cancel(operation, -EINTR); + + return operation->errno; } #if 0 diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index d24e5e0d18f0..c391b28a4ca7 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -99,7 +99,6 @@ int gb_operation_request_send(struct gb_operation *operation, int gb_operation_response_send(struct gb_operation *operation); void gb_operation_cancel(struct gb_operation *operation, int errno); -int gb_operation_wait(struct gb_operation *operation); int gb_operation_status_map(u8 status); -- cgit v1.2.3-59-g8ed1b From 2cf72a233a5d29bee380e89d325cde422a0c6bac Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Nov 2014 19:29:19 -0600 Subject: greybus: kill gb_operation_wait() When a caller wants an operation to complete synchronously, there is generally no need for any other threads to wait for the operation's completion. So here's no need for gb_operation_wait() to be available for synchronous requests. At the moment, all operations are done synchronously. Knowing that, get rid of the public gb_operation_wait() function, and open-code it in gb_operation_request_send(). The public wait function can be re-implemented when it's really needed. With that function gone, the only waiter for the completion of an operation is the submitter itself, and only then if it's synchronous. So rather than complete_all(), we can simply use complete() to signal the submitter. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f4d984f68055..032973cf27a9 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -156,25 +156,10 @@ static void gb_operation_complete(struct gb_operation *operation) if (operation->callback) operation->callback(operation); else - complete_all(&operation->completion); + complete(&operation->completion); gb_operation_put(operation); } -/* - * Wait for a submitted operation to complete. Returns the result - * of the operation; this will be -EINTR if the wait was interrupted. - */ -static int gb_operation_wait(struct gb_operation *operation) -{ - int ret; - - ret = wait_for_completion_interruptible(&operation->completion); - if (ret < 0) - gb_operation_cancel(operation, -EINTR); - - return operation->errno; -} - #if 0 static void gb_operation_request_handle(struct gb_operation *operation) { @@ -478,7 +463,12 @@ int gb_operation_request_send(struct gb_operation *operation, if (ret || callback) return ret; - return gb_operation_wait(operation); + /* Cancel the operation if interrupted */ + ret = wait_for_completion_interruptible(&operation->completion); + if (ret < 0) + gb_operation_cancel(operation, -EINTR); + + return operation->errno; } /* -- cgit v1.2.3-59-g8ed1b From 10c69399043351e5c68f07bedd14fe2fdbf32cb4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Nov 2014 19:29:20 -0600 Subject: greybus: rework synchronous operation completion The only time we need a completion signaled on a request is when the request provided no callback function. In that case, we wait for a completion on behalf of the caller. If an interrupt occurs, we attempt to cancel the message that's been sent, but we don't actually complete the operation as required. Instead of simply waiting for the completion, put in place a special callback function for the synchronous operation. The only job the callback has is to signal completion, allowing the waiter to know it's done. This means gb_operation_complete() will always have a non-null callback pointer, so it becomes a simple wrapper, and we can get rid of it and invoke the callback directly, in gb_operation_work(). Be defensive by checking for a null callback pointer, and reset it to NULL once it's been called. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 42 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 032973cf27a9..180d02856225 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -145,21 +145,6 @@ static void gb_message_cancel(struct gb_message *message) hd->driver->buffer_cancel(message->cookie); } -/* - * An operation's response message has arrived. If no callback was - * supplied it was submitted for asynchronous completion, so we notify - * any waiters. Otherwise we assume calling the completion is enough - * and nobody else will be waiting. - */ -static void gb_operation_complete(struct gb_operation *operation) -{ - if (operation->callback) - operation->callback(operation); - else - complete(&operation->completion); - gb_operation_put(operation); -} - #if 0 static void gb_operation_request_handle(struct gb_operation *operation) { @@ -192,7 +177,12 @@ static void gb_operation_work(struct work_struct *work) struct gb_operation *operation; operation = container_of(work, struct gb_operation, work); - gb_operation_complete(operation); + if (WARN_ON(!operation->callback)) + return; + + operation->callback(operation); + operation->callback = NULL; + gb_operation_put(operation); } /* @@ -423,14 +413,22 @@ void gb_operation_put(struct gb_operation *operation) kref_put(&operation->kref, _gb_operation_destroy); } +/* Tell the requester we're done */ +static void gb_operation_sync_callback(struct gb_operation *operation) +{ + complete(&operation->completion); +} + /* * Send an operation request message. The caller has filled in * any payload so the request message is ready to go. If non-null, * the callback function supplied will be called when the response - * message has arrived indicating the operation is complete. A null + * message has arrived indicating the operation is complete. In + * that case, the callback function is responsible for extracting + * the result of the operation from operation->errno if desired, + * and dropping the final reference to the operation. A null * callback function is used for a synchronous request; return from - * this function won't occur until the operation is complete (or an - * interrupt occurs). + * this function won't occur until the operation is complete. */ int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback) @@ -447,7 +445,11 @@ int gb_operation_request_send(struct gb_operation *operation, */ gb_operation_get(operation); - operation->callback = callback; + /* A null callback pointer means synchronous return */ + if (callback) + operation->callback = callback; + else + operation->callback = gb_operation_sync_callback; gb_pending_operation_insert(operation); /* -- cgit v1.2.3-59-g8ed1b From 10aa801d331609c38a58e0c4e73fd31103ffa230 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 24 Nov 2014 11:19:13 -0800 Subject: greybus: operation: create gb_operation_sync for sending "simple" messages Everyone keeps doing the same create/send/destroy logic all over the place, so abstract that out to a simple function that can handle any arbritrary request and/or response. This will let us save lots of duplicated logic in the protocol drivers. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/operation.c | 54 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/operation.h | 4 +++ 2 files changed, 58 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 180d02856225..35fcb882bfde 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -624,6 +624,60 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) gb_message_cancel(operation->response); } +/** + * gb_operation_sync: implement a "simple" synchronous gb operation. + * @connection: the Greybus connection to send this to + * @type: the type of operation to send + * @request: pointer to a memory buffer to copy the request from + * @request_size: size of @request + * @response: pointer to a memory buffer to copy the response to + * @response_size: the size of @response. + * + * This function implements a simple synchronous Greybus operation. It sends + * the provided operation request and waits (sleeps) until the corresponding + * operation response message has been successfully received, or an error + * occurs. @request and @response are buffers to hold the request and response + * data respectively, and if they are not NULL, their size must be specified in + * @request_size and @response_size. + * + * If a response payload is to come back, and @response is not NULL, + * @response_size number of bytes will be copied into @response if the operation + * is successful. + * + * If there is an error, the response buffer is left alone. + */ +int gb_operation_sync(struct gb_connection *connection, int type, + void *request, int request_size, + void *response, int response_size) +{ + struct gb_operation *operation; + int ret; + + if ((response_size && !response) || + (request_size && !request)) + return -EINVAL; + + operation = gb_operation_create(connection, type, + request_size, response_size); + if (!operation) + return -ENOMEM; + + if (request_size) + memcpy(&operation->request->payload, request, request_size); + + /* Synchronous operation--no callback */ + ret = gb_operation_request_send(operation, NULL); + if (ret) + pr_err("version operation failed (%d)\n", ret); + else + if (response_size) + memcpy(response, operation->response->payload, + response_size); + gb_operation_destroy(operation); + + return ret; +} + int gb_operation_init(void) { gb_operation_cache = kmem_cache_create("gb_operation_cache", diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index c391b28a4ca7..bc5c1641e1ac 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -105,6 +105,10 @@ int gb_operation_status_map(u8 status); void greybus_data_sent(struct greybus_host_device *hd, void *header, int status); +int gb_operation_sync(struct gb_connection *connection, int type, + void *request, int request_size, + void *response, int response_size); + int gb_operation_init(void); void gb_operation_exit(void); -- cgit v1.2.3-59-g8ed1b From d5671a6a24066dbf717ed83c143df7145377756c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 23 Nov 2014 17:45:19 -0800 Subject: greybus: battery-gb: convert to use gb_operation_sync This converts the battery protocol driver to use gb_operation_sync, removing the hand-rolled version of the same function. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/battery-gb.c | 52 +++++++++++------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index ae03869373d8..d28be04f5c68 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -84,34 +84,6 @@ struct gb_battery_voltage_response { __le32 voltage; }; -/* - * None of the battery operation requests have any payload. This - * function implements all of the requests by allowing the caller to - * supply a buffer into which the operation response should be - * copied. If there is an error, the response buffer is left alone. - */ -static int battery_operation(struct gb_battery *gb, int type, - void *response, int response_size) -{ - struct gb_connection *connection = gb->connection; - struct gb_operation *operation; - int ret; - - operation = gb_operation_create(connection, type, 0, response_size); - if (!operation) - return -ENOMEM; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("version operation failed (%d)\n", ret); - else /* Good response, so copy to the caller's buffer */ - memcpy(response, operation->response->payload, response_size); - gb_operation_destroy(operation); - - return ret; -} - /* * This request only uses the connection field, and if successful, * fills in the major and minor protocol version of the target. @@ -121,7 +93,9 @@ static int get_version(struct gb_battery *gb) struct gb_battery_proto_version_response version_response; int retval; - retval = battery_operation(gb, GB_BATTERY_TYPE_PROTOCOL_VERSION, + retval = gb_operation_sync(gb->connection, + GB_BATTERY_TYPE_PROTOCOL_VERSION, + NULL, 0, &version_response, sizeof(version_response)); if (retval) return retval; @@ -143,7 +117,8 @@ static int get_tech(struct gb_battery *gb) u32 technology; int retval; - retval = battery_operation(gb, GB_BATTERY_TYPE_TECHNOLOGY, + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY, + NULL, 0, &tech_response, sizeof(tech_response)); if (retval) return retval; @@ -187,7 +162,8 @@ static int get_status(struct gb_battery *gb) u16 battery_status; int retval; - retval = battery_operation(gb, GB_BATTERY_TYPE_STATUS, + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS, + NULL, 0, &status_response, sizeof(status_response)); if (retval) return retval; @@ -225,7 +201,8 @@ static int get_max_voltage(struct gb_battery *gb) u32 max_voltage; int retval; - retval = battery_operation(gb, GB_BATTERY_TYPE_MAX_VOLTAGE, + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE, + NULL, 0, &volt_response, sizeof(volt_response)); if (retval) return retval; @@ -240,8 +217,9 @@ static int get_capacity(struct gb_battery *gb) u32 capacity; int retval; - retval = battery_operation(gb, GB_BATTERY_TYPE_CAPACITY, - &capacity_response, sizeof(capacity_response)); + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_CAPACITY, + NULL, 0, &capacity_response, + sizeof(capacity_response)); if (retval) return retval; @@ -255,7 +233,8 @@ static int get_temp(struct gb_battery *gb) u32 temperature; int retval; - retval = battery_operation(gb, GB_BATTERY_TYPE_TEMPERATURE, + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE, + NULL, 0, &temp_response, sizeof(temp_response)); if (retval) return retval; @@ -270,7 +249,8 @@ static int get_voltage(struct gb_battery *gb) u32 voltage; int retval; - retval = battery_operation(gb, GB_BATTERY_TYPE_VOLTAGE, + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE, + NULL, 0, &voltage_response, sizeof(voltage_response)); if (retval) return retval; -- cgit v1.2.3-59-g8ed1b From ac3d24930226fac39ea6023577a03b9b5da4d5f6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 23 Nov 2014 17:45:20 -0800 Subject: greybus: vibrator-gb: convert to use gb_operation_sync This converts the vibrator protocol driver to use gb_operation_sync, removing the hand-rolled version of the same function, as well as removing an open-coded version for a request when turning on the vibrator motor. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/vibrator-gb.c | 60 +++++------------------------------ 1 file changed, 8 insertions(+), 52 deletions(-) diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index b65a586b87c9..d00301dce8f0 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -42,35 +42,6 @@ struct gb_vibrator_on_request { __le16 timeout_ms; }; -/* - * The get_version and turn_off vibrator operations have no payload. - * This function implements these requests by allowing the caller to - * supply a buffer into which the operation response should be - * copied. The turn_off operation, there is no response either. - * If there is an error, the response buffer is left alone. - */ -static int request_operation(struct gb_connection *connection, int type, - void *response, int response_size) -{ - struct gb_operation *operation; - int ret; - - operation = gb_operation_create(connection, type, 0, response_size); - if (!operation) - return -ENOMEM; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("version operation failed (%d)\n", ret); - else if (response_size && response) - memcpy(response, operation->response->payload, - response_size); - gb_operation_destroy(operation); - - return ret; -} - /* * This request only uses the connection field, and if successful, * fills in the major and minor protocol version of the target. @@ -81,8 +52,9 @@ static int get_version(struct gb_vibrator_device *vib) struct gb_vibrator_proto_version_response version_response; int retval; - retval = request_operation(connection, + retval = gb_operation_sync(connection, GB_VIBRATOR_TYPE_PROTOCOL_VERSION, + NULL, 0, &version_response, sizeof(version_response)); if (retval) return retval; @@ -101,33 +73,17 @@ static int get_version(struct gb_vibrator_device *vib) static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) { - struct gb_connection *connection = vib->connection; - struct gb_operation *operation; - struct gb_vibrator_on_request *request; - int retval; - - operation = gb_operation_create(connection, GB_VIBRATOR_TYPE_ON, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->timeout_ms = cpu_to_le16(timeout_ms); + struct gb_vibrator_on_request request; - /* Synchronous operation--no callback */ - retval = gb_operation_request_send(operation, NULL); - if (retval) - dev_err(&connection->dev, - "send data operation failed (%d)\n", retval); - gb_operation_destroy(operation); - - return retval; + request.timeout_ms = cpu_to_le16(timeout_ms); + return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, + &request, sizeof(request), NULL, 0); } static int turn_off(struct gb_vibrator_device *vib) { - struct gb_connection *connection = vib->connection; - - return request_operation(connection, GB_VIBRATOR_TYPE_OFF, NULL, 0); + return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, + NULL, 0, NULL, 0); } static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, -- cgit v1.2.3-59-g8ed1b From e51f1d1a7711e2d68922dab08e2edc13fea8925a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 23 Nov 2014 17:45:21 -0800 Subject: greybus: uart-gb: convert to use gb_operation_sync This converts the UART protocol driver to use gb_operation_sync, removing lots of places where the create/send/destroy pattern was being used to send greybus messages. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/uart-gb.c | 130 ++++++++++---------------------------- 1 file changed, 33 insertions(+), 97 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index e9faf310e39c..10d00b775bf3 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -137,146 +137,82 @@ static atomic_t reference_count = ATOMIC_INIT(0); */ static int get_version(struct gb_tty *tty) { - struct gb_operation *operation; - struct gb_uart_proto_version_response *response; + struct gb_uart_proto_version_response response; int ret; - operation = gb_operation_create(tty->connection, - GB_UART_REQ_PROTOCOL_VERSION, - 0, sizeof(*response)); - if (!operation) - return -ENOMEM; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) { - pr_err("version operation failed (%d)\n", ret); - goto out; - } + ret = gb_operation_sync(tty->connection, + GB_UART_REQ_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; - response = operation->response->payload; - if (response->major > GB_UART_VERSION_MAJOR) { + if (response.major > GB_UART_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", - response->major, GB_UART_VERSION_MAJOR); - ret = -ENOTSUPP; - goto out; + response.major, GB_UART_VERSION_MAJOR); + return -ENOTSUPP; } - tty->version_major = response->major; - tty->version_minor = response->minor; + tty->version_major = response.major; + tty->version_minor = response.minor; pr_debug("%s: version_major = %u version_minor = %u\n", __func__, tty->version_major, tty->version_minor); -out: - gb_operation_destroy(operation); - - return ret; + return 0; } static int send_data(struct gb_tty *tty, u16 size, const u8 *data) { - struct gb_connection *connection = tty->connection; - struct gb_operation *operation; struct gb_uart_send_data_request *request; int retval; if (!data || !size) return 0; - operation = gb_operation_create(connection, GB_UART_REQ_SEND_DATA, - sizeof(*request) + size, 0); - if (!operation) + request = kmalloc(sizeof(*request) + size, GFP_KERNEL); + if (!request) return -ENOMEM; - request = operation->request->payload; + request->size = cpu_to_le16(size); memcpy(&request->data[0], data, size); + retval = gb_operation_sync(tty->connection, GB_UART_REQ_SEND_DATA, + request, sizeof(*request) + size, NULL, 0); - /* Synchronous operation--no callback */ - retval = gb_operation_request_send(operation, NULL); - if (retval) - dev_err(&connection->dev, - "send data operation failed (%d)\n", retval); - gb_operation_destroy(operation); - + kfree(request); return retval; } static int send_line_coding(struct gb_tty *tty, struct gb_serial_line_coding *line_coding) { - struct gb_connection *connection = tty->connection; - struct gb_operation *operation; - struct gb_uart_set_line_coding_request *request; - int retval; - - operation = gb_operation_create(connection, GB_UART_REQ_SET_LINE_CODING, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - memcpy(&request->line_coding, line_coding, sizeof(*line_coding)); - - /* Synchronous operation--no callback */ - retval = gb_operation_request_send(operation, NULL); - if (retval) - dev_err(&connection->dev, - "send line coding operation failed (%d)\n", retval); - gb_operation_destroy(operation); + struct gb_uart_set_line_coding_request request; - return retval; + memcpy(&request.line_coding, line_coding, sizeof(*line_coding)); + return gb_operation_sync(tty->connection, GB_UART_REQ_SET_LINE_CODING, + &request, sizeof(request), NULL, 0); } static int send_control(struct gb_tty *tty, u16 control) { - struct gb_connection *connection = tty->connection; - struct gb_operation *operation; - struct gb_uart_set_control_line_state_request *request; - int retval; - - operation = gb_operation_create(connection, - GB_UART_REQ_SET_CONTROL_LINE_STATE, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->control = cpu_to_le16(control); + struct gb_uart_set_control_line_state_request request; - /* Synchronous operation--no callback */ - retval = gb_operation_request_send(operation, NULL); - if (retval) - dev_err(&connection->dev, - "send control operation failed (%d)\n", retval); - gb_operation_destroy(operation); - - return retval; + request.control = cpu_to_le16(control); + return gb_operation_sync(tty->connection, + GB_UART_REQ_SET_CONTROL_LINE_STATE, + &request, sizeof(request), NULL, 0); } static int send_break(struct gb_tty *tty, u8 state) { - struct gb_connection *connection = tty->connection; - struct gb_operation *operation; - struct gb_uart_set_break_request *request; - int retval; + struct gb_uart_set_break_request request; if ((state != 0) && (state != 1)) { - dev_err(&connection->dev, "invalid break state of %d\n", state); + dev_err(&tty->connection->dev, + "invalid break state of %d\n", state); return -EINVAL; } - operation = gb_operation_create(connection, GB_UART_REQ_SET_BREAK, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->state = state; - - /* Synchronous operation--no callback */ - retval = gb_operation_request_send(operation, NULL); - if (retval) - dev_err(&connection->dev, - "send break operation failed (%d)\n", retval); - gb_operation_destroy(operation); - - return retval; + request.state = state; + return gb_operation_sync(tty->connection, GB_UART_REQ_SET_BREAK, + &request, sizeof(request), NULL, 0); } -- cgit v1.2.3-59-g8ed1b From 7d5bbb173559328cdd63a729870baac69d3631f6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 23 Nov 2014 17:45:22 -0800 Subject: greybus: gpio-gb: convert to use gb_operation_sync This converts the GPIO protocol driver to use gb_operation_sync, removing lots of places where the create/send/destroy pattern was being used to send greybus messages. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/gpio-gb.c | 338 +++++++++++--------------------------- 1 file changed, 97 insertions(+), 241 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index c6c85d8c28d2..09d77c1e6c07 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -116,337 +116,193 @@ struct gb_gpio_set_debounce_request { * This request only uses the connection field, and if successful, * fills in the major and minor protocol version of the target. */ -static int -gb_gpio_proto_version_operation(struct gb_gpio_controller *gb_gpio_controller) +static int gb_gpio_proto_version_operation(struct gb_gpio_controller *ggc) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_proto_version_response *response; + struct gb_gpio_proto_version_response response; int ret; - /* protocol version request has no payload */ - operation = gb_operation_create(connection, - GB_GPIO_TYPE_PROTOCOL_VERSION, - 0, sizeof(*response)); - if (!operation) - return -ENOMEM; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) { - pr_err("version operation failed (%d)\n", ret); - goto out; - } + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; - response = operation->response->payload; - if (response->major > GB_GPIO_VERSION_MAJOR) { + if (response.major > GB_GPIO_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", - response->major, GB_GPIO_VERSION_MAJOR); - ret = -ENOTSUPP; - } else { - gb_gpio_controller->version_major = response->major; - gb_gpio_controller->version_minor = response->minor; + response.major, GB_GPIO_VERSION_MAJOR); + return -ENOTSUPP; } -out: - gb_operation_destroy(operation); - - return ret; + ggc->version_major = response.major; + ggc->version_minor = response.minor; + return 0; } -static int gb_gpio_line_count_operation(struct gb_gpio_controller *gb_gpio_controller) +static int gb_gpio_line_count_operation(struct gb_gpio_controller *ggc) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_line_count_response *response; + struct gb_gpio_line_count_response response; int ret; - /* line count request has no payload */ - operation = gb_operation_create(connection, - GB_GPIO_TYPE_LINE_COUNT, - 0, sizeof(*response)); - if (!operation) - return -ENOMEM; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) { - pr_err("line count operation failed (%d)\n", ret); - } else { - response = operation->response->payload; - gb_gpio_controller->line_max = response->count; - } - gb_operation_destroy(operation); - + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_LINE_COUNT, + NULL, 0, &response, sizeof(response)); + if (!ret) + ggc->line_max = response.count; return ret; } -static int gb_gpio_activate_operation(struct gb_gpio_controller *gb_gpio_controller, - u8 which) +static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_activate_request *request; + struct gb_gpio_activate_request request; int ret; - if (which > gb_gpio_controller->line_max) + if (which > ggc->line_max) return -EINVAL; - /* activate response has no payload */ - operation = gb_operation_create(connection, - GB_GPIO_TYPE_ACTIVATE, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("activate operation failed (%d)\n", ret); - else - gb_gpio_controller->lines[which].active = true; - gb_operation_destroy(operation); - + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_ACTIVATE, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].active = true; return ret; } -static int gb_gpio_deactivate_operation(struct gb_gpio_controller *gb_gpio_controller, +static int gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, u8 which) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_deactivate_request *request; + struct gb_gpio_deactivate_request request; int ret; - if (which > gb_gpio_controller->line_max) + if (which > ggc->line_max) return -EINVAL; - /* deactivate response has no payload */ - operation = gb_operation_create(connection, - GB_GPIO_TYPE_DEACTIVATE, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("deactivate operation failed (%d)\n", ret); - else - gb_gpio_controller->lines[which].active = false; - gb_operation_destroy(operation); - + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DEACTIVATE, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].active = false; return ret; } -static int gb_gpio_get_direction_operation(struct gb_gpio_controller *gb_gpio_controller, +static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, u8 which) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_get_direction_request *request; - struct gb_gpio_get_direction_response *response; + struct gb_gpio_get_direction_request request; + struct gb_gpio_get_direction_response response; int ret; u8 direction; - if (which > gb_gpio_controller->line_max) + if (which > ggc->line_max) return -EINVAL; - operation = gb_operation_create(connection, - GB_GPIO_TYPE_GET_DIRECTION, - sizeof(*request), sizeof(*response)); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) { - pr_err("get direction operation failed (%d)\n", ret); - goto out; - } + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_DIRECTION, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) + return ret; - response = operation->response->payload; - direction = response->direction; + direction = response.direction; if (direction && direction != 1) pr_warn("gpio %u direction was %u (should be 0 or 1)\n", which, direction); - gb_gpio_controller->lines[which].direction = direction ? 1 : 0; -out: - gb_operation_destroy(operation); - - return ret; + ggc->lines[which].direction = direction ? 1 : 0; + return 0; } -static int gb_gpio_direction_in_operation(struct gb_gpio_controller *gb_gpio_controller, +static int gb_gpio_direction_in_operation(struct gb_gpio_controller *ggc, u8 which) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_direction_in_request *request; + struct gb_gpio_direction_in_request request; int ret; - if (which > gb_gpio_controller->line_max) + if (which > ggc->line_max) return -EINVAL; - /* direction_in response has no payload */ - operation = gb_operation_create(connection, - GB_GPIO_TYPE_DIRECTION_IN, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("direction in operation failed (%d)\n", ret); - else - gb_gpio_controller->lines[which].direction = 1; - gb_operation_destroy(operation); - + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_IN, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].direction = 1; return ret; } -static int gb_gpio_direction_out_operation(struct gb_gpio_controller *gb_gpio_controller, +static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, u8 which, bool value_high) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_direction_out_request *request; + struct gb_gpio_direction_out_request request; int ret; - if (which > gb_gpio_controller->line_max) + if (which > ggc->line_max) return -EINVAL; - /* direction_out response has no payload */ - operation = gb_operation_create(connection, - GB_GPIO_TYPE_DIRECTION_OUT, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - request->value = value_high ? 1 : 0; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("direction out operation failed (%d)\n", ret); - else - gb_gpio_controller->lines[which].direction = 0; - gb_operation_destroy(operation); - + request.which = which; + request.value = value_high ? 1 : 0; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_OUT, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].direction = 0; return ret; } -static int gb_gpio_get_value_operation(struct gb_gpio_controller *gb_gpio_controller, +static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, u8 which) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_get_value_request *request; - struct gb_gpio_get_value_response *response; + struct gb_gpio_get_value_request request; + struct gb_gpio_get_value_response response; int ret; u8 value; - if (which > gb_gpio_controller->line_max) + if (which > ggc->line_max) return -EINVAL; - operation = gb_operation_create(connection, - GB_GPIO_TYPE_GET_VALUE, - sizeof(*request), sizeof(*response)); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) { - pr_err("get value operation failed (%d)\n", ret); - goto out; - } + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_VALUE, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) + return ret; - response = operation->response->payload; - value = response->value; + value = response.value; if (value && value != 1) pr_warn("gpio %u value was %u (should be 0 or 1)\n", which, value); - gb_gpio_controller->lines[which].value = value ? 1 : 0; -out: - gb_operation_destroy(operation); - - return ret; + ggc->lines[which].value = value ? 1 : 0; + return 0; } -static int gb_gpio_set_value_operation(struct gb_gpio_controller *gb_gpio_controller, +static int gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, u8 which, bool value_high) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_set_value_request *request; + struct gb_gpio_set_value_request request; int ret; - if (which > gb_gpio_controller->line_max) + if (which > ggc->line_max) return -EINVAL; - /* set_value response has no payload */ - operation = gb_operation_create(connection, - GB_GPIO_TYPE_SET_VALUE, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - request->value = value_high ? 1 : 0; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("set value operation failed (%d)\n", ret); - else /* XXX should this set direction to out? */ - gb_gpio_controller->lines[which].value = request->value; - gb_operation_destroy(operation); - + request.which = which; + request.value = value_high ? 1 : 0; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, + &request, sizeof(request), NULL, 0); + if (!ret) { + /* XXX should this set direction to out? */ + ggc->lines[which].value = request.value; + } return ret; } -static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *gb_gpio_controller, +static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, u8 which, u16 debounce_usec) { - struct gb_connection *connection = gb_gpio_controller->connection; - struct gb_operation *operation; - struct gb_gpio_set_debounce_request *request; + struct gb_gpio_set_debounce_request request; int ret; - if (which > gb_gpio_controller->line_max) + if (which > ggc->line_max) return -EINVAL; - /* set_debounce response has no payload */ - operation = gb_operation_create(connection, - GB_GPIO_TYPE_SET_DEBOUNCE, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - request->usec = cpu_to_le16(debounce_usec); - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("set debounce operation failed (%d)\n", ret); - else - gb_gpio_controller->lines[which].debounce_usec = - le16_to_cpu(request->usec); - gb_operation_destroy(operation); - + request.which = which; + request.usec = cpu_to_le16(debounce_usec); + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_DEBOUNCE, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].debounce_usec = debounce_usec; return ret; } -- cgit v1.2.3-59-g8ed1b From 5a8b8314db138b9cc59f710a91b291cbe1486001 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 23 Nov 2014 17:45:23 -0800 Subject: greybus: i2c-gb: convert to use gb_operation_sync This converts the I2C protocol driver to use gb_operation_sync, removing lots of places where the create/send/destroy pattern was being used to send greybus messages. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/i2c-gb.c | 102 +++++++++++---------------------------- 1 file changed, 28 insertions(+), 74 deletions(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 6b2fd7e934e3..19a7df91565a 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -92,38 +92,23 @@ struct gb_i2c_transfer_response { */ static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) { - struct gb_connection *connection = gb_i2c_dev->connection; - struct gb_operation *operation; - struct gb_i2c_proto_version_response *response; + struct gb_i2c_proto_version_response response; int ret; - /* A protocol version request has no payload */ - operation = gb_operation_create(connection, - GB_I2C_TYPE_PROTOCOL_VERSION, - 0, sizeof(*response)); - if (!operation) - return -ENOMEM; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) { - pr_err("version operation failed (%d)\n", ret); - goto out; - } + ret = gb_operation_sync(gb_i2c_dev->connection, + GB_I2C_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; - response = operation->response->payload; - if (response->major > GB_I2C_VERSION_MAJOR) { + if (response.major > GB_I2C_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", - response->major, GB_I2C_VERSION_MAJOR); - ret = -ENOTSUPP; - goto out; + response.major, GB_I2C_VERSION_MAJOR); + return -ENOTSUPP; } - gb_i2c_dev->version_major = response->major; - gb_i2c_dev->version_minor = response->minor; -out: - gb_operation_destroy(operation); - - return ret; + gb_i2c_dev->version_major = response.major; + gb_i2c_dev->version_minor = response.minor; + return 0; } /* @@ -136,56 +121,34 @@ static u32 gb_i2c_functionality_map(u32 gb_i2c_functionality) static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) { - struct gb_connection *connection = gb_i2c_dev->connection; - struct gb_operation *operation; - struct gb_i2c_functionality_response *response; + struct gb_i2c_functionality_response response; u32 functionality; int ret; - /* A functionality request has no payload */ - operation = gb_operation_create(connection, - GB_I2C_TYPE_FUNCTIONALITY, - 0, sizeof(*response)); - if (!operation) - return -ENOMEM; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) { - pr_err("functionality operation failed (%d)\n", ret); - goto out; - } + ret = gb_operation_sync(gb_i2c_dev->connection, + GB_I2C_TYPE_FUNCTIONALITY, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; - response = operation->response->payload; - functionality = le32_to_cpu(response->functionality); + functionality = le32_to_cpu(response.functionality); gb_i2c_dev->functionality = gb_i2c_functionality_map(functionality); -out: - gb_operation_destroy(operation); - return ret; + return 0; } static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) { - struct gb_connection *connection = gb_i2c_dev->connection; - struct gb_operation *operation; - struct gb_i2c_timeout_request *request; + struct gb_i2c_timeout_request request; int ret; - operation = gb_operation_create(connection, GB_I2C_TYPE_TIMEOUT, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->msec = cpu_to_le16(msec); - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); + request.msec = cpu_to_le16(msec); + ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_TIMEOUT, + &request, sizeof(request), NULL, 0); if (ret) pr_err("timeout operation failed (%d)\n", ret); else gb_i2c_dev->timeout_msec = msec; - gb_operation_destroy(operation); return ret; } @@ -193,25 +156,16 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, u8 retries) { - struct gb_connection *connection = gb_i2c_dev->connection; - struct gb_operation *operation; - struct gb_i2c_retries_request *request; + struct gb_i2c_retries_request request; int ret; - operation = gb_operation_create(connection, GB_I2C_TYPE_RETRIES, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->retries = retries; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); + request.retries = retries; + ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_RETRIES, + &request, sizeof(request), NULL, 0); if (ret) pr_err("retries operation failed (%d)\n", ret); else gb_i2c_dev->retries = retries; - gb_operation_destroy(operation); return ret; } -- cgit v1.2.3-59-g8ed1b From bf2329fd7d2795b9ce7146ca9e7092a7f507ce59 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 23 Nov 2014 17:45:24 -0800 Subject: greybus: pwm-gb: convert to use gb_operation_sync This converts the PWM protocol driver to use gb_operation_sync, removing lots of places where the create/send/destroy pattern was being used to send greybus messages. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/pwm-gb.c | 206 +++++++++------------------------------ 1 file changed, 45 insertions(+), 161 deletions(-) diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index e146b240f89e..bd1379f4400f 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -85,234 +85,118 @@ struct gb_pwm_disable_request { */ static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) { - struct gb_connection *connection = pwmc->connection; - struct gb_operation *operation; - struct gb_pwm_proto_version_response *response; + struct gb_pwm_proto_version_response response; int ret; - /* protocol version request has no payload */ - operation = gb_operation_create(connection, - GB_PWM_TYPE_PROTOCOL_VERSION, - 0, sizeof(*response)); - if (!operation) - return -ENOMEM; + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) { - pr_err("version operation failed (%d)\n", ret); - goto out; - } + if (ret) + return ret; - response = operation->response->payload; - if (response->major > GB_PWM_VERSION_MAJOR) { + if (response.major > GB_PWM_VERSION_MAJOR) { pr_err("unsupported major version (%hhu > %hhu)\n", - response->major, GB_PWM_VERSION_MAJOR); - ret = -ENOTSUPP; - goto out; + response.major, GB_PWM_VERSION_MAJOR); + return -ENOTSUPP; } - pwmc->version_major = response->major; - pwmc->version_minor = response->minor; -out: - gb_operation_destroy(operation); - - return ret; + pwmc->version_major = response.major; + pwmc->version_minor = response.minor; + return 0; } static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) { - struct gb_connection *connection = pwmc->connection; - struct gb_operation *operation; - struct gb_pwm_count_response *response; + struct gb_pwm_count_response response; int ret; - /* pwm count request has no payload */ - operation = gb_operation_create(connection, GB_PWM_TYPE_PWM_COUNT, - 0, sizeof(*response)); - if (!operation) - return -ENOMEM; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) { - pr_err("line count operation failed (%d)\n", ret); - } else { - response = operation->response->payload; - pwmc->pwm_max = response->count; - } - gb_operation_destroy(operation); - - return ret; + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PWM_COUNT, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + pwmc->pwm_max = response.count; + return 0; } static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, u8 which) { - struct gb_connection *connection = pwmc->connection; - struct gb_operation *operation; - struct gb_pwm_activate_request *request; - int ret; + struct gb_pwm_activate_request request; if (which > pwmc->pwm_max) return -EINVAL; - /* activate response has no payload */ - operation = gb_operation_create(connection, GB_PWM_TYPE_ACTIVATE, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("activate operation failed (%d)\n", ret); - gb_operation_destroy(operation); - - return ret; + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ACTIVATE, + &request, sizeof(request), NULL, 0); } static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, u8 which) { - struct gb_connection *connection = pwmc->connection; - struct gb_operation *operation; - struct gb_pwm_deactivate_request *request; - int ret; + struct gb_pwm_deactivate_request request; if (which > pwmc->pwm_max) return -EINVAL; - /* deactivate response has no payload */ - operation = gb_operation_create(connection, GB_PWM_TYPE_DEACTIVATE, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("deactivate operation failed (%d)\n", ret); - gb_operation_destroy(operation); - - return ret; + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DEACTIVATE, + &request, sizeof(request), NULL, 0); } static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, u8 which, u32 duty, u32 period) { - struct gb_connection *connection = pwmc->connection; - struct gb_operation *operation; - struct gb_pwm_config_request *request; - int ret; + struct gb_pwm_config_request request; if (which > pwmc->pwm_max) return -EINVAL; - operation = gb_operation_create(connection, GB_PWM_TYPE_CONFIG, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - request->duty = duty; - request->period = period; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("config operation failed (%d)\n", ret); - gb_operation_destroy(operation); - - return ret; + request.which = which; + request.duty = duty; + request.period = period; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, + &request, sizeof(request), NULL, 0); } static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, u8 which, u8 polarity) { - struct gb_connection *connection = pwmc->connection; - struct gb_operation *operation; - struct gb_pwm_polarity_request *request; - int ret; + struct gb_pwm_polarity_request request; if (which > pwmc->pwm_max) return -EINVAL; - operation = gb_operation_create(connection, GB_PWM_TYPE_POLARITY, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - request->polarity = polarity; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("set polarity operation failed (%d)\n", ret); - gb_operation_destroy(operation); - - return ret; + request.which = which; + request.polarity = polarity; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_POLARITY, + &request, sizeof(request), NULL, 0); } static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, u8 which) { - struct gb_connection *connection = pwmc->connection; - struct gb_operation *operation; - struct gb_pwm_enable_request *request; - int ret; + struct gb_pwm_enable_request request; if (which > pwmc->pwm_max) return -EINVAL; - /* enable response has no payload */ - operation = gb_operation_create(connection, GB_PWM_TYPE_ENABLE, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("enable operation failed (%d)\n", ret); - gb_operation_destroy(operation); - - return ret; + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ENABLE, + &request, sizeof(request), NULL, 0); } static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, u8 which) { - struct gb_connection *connection = pwmc->connection; - struct gb_operation *operation; - struct gb_pwm_disable_request *request; - int ret; + struct gb_pwm_disable_request request; if (which > pwmc->pwm_max) return -EINVAL; - /* disable response has no payload */ - operation = gb_operation_create(connection, GB_PWM_TYPE_DISABLE, - sizeof(*request), 0); - if (!operation) - return -ENOMEM; - request = operation->request->payload; - request->which = which; - - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); - if (ret) - pr_err("disable operation failed (%d)\n", ret); - gb_operation_destroy(operation); - - return ret; + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DISABLE, + &request, sizeof(request), NULL, 0); } static int gb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) -- cgit v1.2.3-59-g8ed1b From ecc25a7f58d9be842f591800f13f932396fb72af Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 24 Nov 2014 09:25:24 +0530 Subject: greybus: es1-ap-usb: don't use 'es1' after it is freed greybus_remove_hd() will free memory allocated to 'es1' and so using it after the routine has returned isn't right. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index bab6f259dd70..204784329716 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -293,6 +293,7 @@ static int check_urb_status(struct urb *urb) static void ap_disconnect(struct usb_interface *interface) { struct es1_ap_dev *es1; + struct usb_device *udev; int i; es1 = usb_get_intfdata(interface); @@ -329,9 +330,10 @@ static void ap_disconnect(struct usb_interface *interface) es1->svc_buffer = NULL; usb_set_intfdata(interface, NULL); + udev = es1->usb_dev; greybus_remove_hd(es1->hd); - usb_put_dev(es1->usb_dev); + usb_put_dev(udev); } /* Callback for when we get a SVC message */ -- cgit v1.2.3-59-g8ed1b From 9f240f20caf93090d82949d59d467dcde341560b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 24 Nov 2014 13:52:25 -0800 Subject: greybus: uart-gb: clean up send_line_coding We always pass the same option to send_line_coding() for the line_coding structure, which is already in the struct gb_tty variable, so just remove the second parameter as it's not needed. This logic came from the cdc-acm.c driver, where it's also not needed anymore, I'll go fix up that later on when I get a chance. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 10d00b775bf3..6432c64fbd60 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -180,12 +180,12 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) return retval; } -static int send_line_coding(struct gb_tty *tty, - struct gb_serial_line_coding *line_coding) +static int send_line_coding(struct gb_tty *tty) { struct gb_uart_set_line_coding_request request; - memcpy(&request.line_coding, line_coding, sizeof(*line_coding)); + memcpy(&request.line_coding, &tty->line_coding, + sizeof(tty->line_coding)); return gb_operation_sync(tty->connection, GB_UART_REQ_SET_LINE_CODING, &request, sizeof(request), NULL, 0); } @@ -384,7 +384,7 @@ static void gb_tty_set_termios(struct tty_struct *tty, if (memcpy(&gb_tty->line_coding, &newline, sizeof(newline))) { memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); - send_line_coding(gb_tty, &gb_tty->line_coding); + send_line_coding(gb_tty); } } @@ -668,7 +668,7 @@ static int gb_uart_connection_init(struct gb_connection *connection) gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; gb_tty->line_coding.data = 8; - send_line_coding(gb_tty, &gb_tty->line_coding); + send_line_coding(gb_tty); tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, &connection->dev); -- cgit v1.2.3-59-g8ed1b From ba986b5ab97f59669cc5d174a8a7359b8600e2e5 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Nov 2014 11:33:13 -0600 Subject: greybus: encapsulate operation result access Hide the setting and getting of the operation result (stored in operation->errno) behind a pair of accessor functions. Only the operation core should be setting the result, but operations that complete asynchronously will need access to the result so expose the function that provides that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 33 ++++++++++++++++++++++----------- drivers/staging/greybus/operation.h | 2 ++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 35fcb882bfde..f42e6d29f827 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -66,6 +66,16 @@ struct gb_operation_msg_hdr { /* XXX Could be per-host device, per-module, or even per-connection */ static DEFINE_SPINLOCK(gb_operations_lock); +static void gb_operation_result_set(struct gb_operation *operation, int result) +{ + operation->errno = result; +} + +int gb_operation_result(struct gb_operation *operation) +{ + return operation->errno; +} + static void gb_pending_operation_insert(struct gb_operation *operation) { struct gb_connection *connection = operation->connection; @@ -164,7 +174,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) gb_connection_err(operation->connection, "unexpected incoming request type 0x%02hhx\n", header->type); - operation->errno = -EPROTONOSUPPORT; + gb_operation_result_set(operation, -EPROTONOSUPPORT); } #endif @@ -424,11 +434,12 @@ static void gb_operation_sync_callback(struct gb_operation *operation) * any payload so the request message is ready to go. If non-null, * the callback function supplied will be called when the response * message has arrived indicating the operation is complete. In - * that case, the callback function is responsible for extracting - * the result of the operation from operation->errno if desired, - * and dropping the final reference to the operation. A null - * callback function is used for a synchronous request; return from - * this function won't occur until the operation is complete. + * that case, the callback function is responsible for fetching the + * result of the operation using gb_operation_result() if desired, + * and dropping the final reference to (i.e., destroying) the + * operation. A null callback function is used for a synchronous + * request; in that case return from this function won't occur until + * the operation is complete. */ int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback) @@ -470,7 +481,7 @@ int gb_operation_request_send(struct gb_operation *operation, if (ret < 0) gb_operation_cancel(operation, -EINTR); - return operation->errno; + return gb_operation_result(operation); } /* @@ -501,7 +512,7 @@ greybus_data_sent(struct greybus_host_device *hd, void *header, int status) /* XXX Right now we assume we're an outgoing request */ message = gb_hd_message_find(hd, header); operation = message->operation; - operation->errno = status; + gb_operation_result_set(operation, status); queue_work(gb_operation_workqueue, &operation->work); } EXPORT_SYMBOL_GPL(greybus_data_sent); @@ -527,7 +538,7 @@ void gb_connection_recv_request(struct gb_connection *connection, memcpy(operation->request->header, data, size); /* XXX Right now this will just complete the operation */ - operation->errno = -ENOSYS; + gb_operation_result_set(operation, -ENOSYS); queue_work(gb_operation_workqueue, &operation->work); } @@ -571,7 +582,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, memcpy(message->header, data, size); /* The rest will be handled in work queue context */ - operation->errno = result; + gb_operation_result_set(operation, result); queue_work(gb_operation_workqueue, &operation->work); } @@ -619,7 +630,7 @@ void gb_connection_recv(struct gb_connection *connection, */ void gb_operation_cancel(struct gb_operation *operation, int errno) { - operation->errno = errno; + gb_operation_result_set(operation, errno); gb_message_cancel(operation->request); gb_message_cancel(operation->response); } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index bc5c1641e1ac..7f835d2e8f35 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -84,6 +84,8 @@ struct gb_operation { void gb_connection_recv(struct gb_connection *connection, void *data, size_t size); +int gb_operation_result(struct gb_operation *operation); + struct gb_operation *gb_operation_create(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size); -- cgit v1.2.3-59-g8ed1b From abe9a3006fd95d34e69617b65824613e503b9b77 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Nov 2014 11:33:14 -0600 Subject: greybus: first operation error prevails If an operation already has an error result recorded, don't overwrite it with a new error code. In order to ensure a request completes exactly once, return a Boolean indicating whether setting the result was successful. If two threads are racing to complete an operation (for example if a slow-but-normal response message arrives at the same time timeout processing commences) only the one that sets the final result will finish its activity. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f42e6d29f827..49b0f19b4e50 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -66,9 +66,12 @@ struct gb_operation_msg_hdr { /* XXX Could be per-host device, per-module, or even per-connection */ static DEFINE_SPINLOCK(gb_operations_lock); -static void gb_operation_result_set(struct gb_operation *operation, int result) +static bool gb_operation_result_set(struct gb_operation *operation, int result) { + if (operation->errno) + return false; operation->errno = result; + return true; } int gb_operation_result(struct gb_operation *operation) @@ -174,7 +177,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) gb_connection_err(operation->connection, "unexpected incoming request type 0x%02hhx\n", header->type); - gb_operation_result_set(operation, -EPROTONOSUPPORT); + (void)gb_operation_result_set(operation, -EPROTONOSUPPORT); } #endif @@ -512,8 +515,8 @@ greybus_data_sent(struct greybus_host_device *hd, void *header, int status) /* XXX Right now we assume we're an outgoing request */ message = gb_hd_message_find(hd, header); operation = message->operation; - gb_operation_result_set(operation, status); - queue_work(gb_operation_workqueue, &operation->work); + if (gb_operation_result_set(operation, status)) + queue_work(gb_operation_workqueue, &operation->work); } EXPORT_SYMBOL_GPL(greybus_data_sent); @@ -538,8 +541,8 @@ void gb_connection_recv_request(struct gb_connection *connection, memcpy(operation->request->header, data, size); /* XXX Right now this will just complete the operation */ - gb_operation_result_set(operation, -ENOSYS); - queue_work(gb_operation_workqueue, &operation->work); + if (gb_operation_result_set(operation, -ENOSYS)) + queue_work(gb_operation_workqueue, &operation->work); } /* @@ -582,8 +585,8 @@ static void gb_connection_recv_response(struct gb_connection *connection, memcpy(message->header, data, size); /* The rest will be handled in work queue context */ - gb_operation_result_set(operation, result); - queue_work(gb_operation_workqueue, &operation->work); + if (gb_operation_result_set(operation, result)) + queue_work(gb_operation_workqueue, &operation->work); } /* @@ -630,9 +633,10 @@ void gb_connection_recv(struct gb_connection *connection, */ void gb_operation_cancel(struct gb_operation *operation, int errno) { - gb_operation_result_set(operation, errno); - gb_message_cancel(operation->request); - gb_message_cancel(operation->response); + if (gb_operation_result_set(operation, errno)) { + gb_message_cancel(operation->request); + gb_message_cancel(operation->response); + } } /** -- cgit v1.2.3-59-g8ed1b From 3deb37d4ad04c6cb18564f5af2c88c10fa6bfc76 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Nov 2014 11:33:15 -0600 Subject: greybus: use special operation result valus This is more or less re-implementing this commit: 96f95d4 greybus: update gbuf status for completion handlers But this time we're doing this for an operation, not the gbuf. Define an initial operation result value (-EBADR) to signify that no valid result has been set. Nobody should ever set that value after the operation is initially created. Since only the operation core code sets the result, an attempt to set -EBADR would be a bug. Define another known operation result value (-EINPROGRESS) for an outgoing operation whose request has been sent but whose response has not yet been successfully received. This should the first (non-initial) result value set, and it should happen exactly once. Any other attempt to set this value once set would be a bug. Finally, once the request message is in flight, the result value will be set exactly once more, to indicate the final result of the operation. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 49b0f19b4e50..82fd7e98074c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -66,17 +66,43 @@ struct gb_operation_msg_hdr { /* XXX Could be per-host device, per-module, or even per-connection */ static DEFINE_SPINLOCK(gb_operations_lock); +/* + * Set an operation's result. Initially an outgoing operation's + * errno value is -EBADR. If no error occurs before sending the + * request message the only valid value operation->errno can be + * set to is -EINPROGRESS, indicating the request has been (or + * rather is about to be) sent. At that point nobody should + * be looking at the result until the reponse arrives. + * + * The first time the result gets set after the request has been + * sent, that result "sticks." That is, if two concurrent threads + * race to set the result, the first one wins. The return value + * tells the caller whether its result was recorded; if not the + * has nothing more to do. + */ static bool gb_operation_result_set(struct gb_operation *operation, int result) { - if (operation->errno) + if (WARN_ON(result == -EBADR)) + return false; + + if (result == -EINPROGRESS) { + if (WARN_ON(operation->errno != -EBADR)) + return false; + } else if (operation->errno != -EINPROGRESS) { return false; + } operation->errno = result; + return true; } int gb_operation_result(struct gb_operation *operation) { - return operation->errno; + int result = operation->errno; + + WARN_ON(result == -EINPROGRESS); + + return result; } static void gb_pending_operation_insert(struct gb_operation *operation) @@ -350,6 +376,7 @@ gb_operation_create_common(struct gb_connection *connection, bool outgoing, goto err_request; operation->response->operation = operation; } + operation->errno = -EBADR; /* Initial value--means "never set" */ INIT_WORK(&operation->work, gb_operation_work); operation->callback = NULL; /* set at submit time */ @@ -475,6 +502,7 @@ int gb_operation_request_send(struct gb_operation *operation, schedule_delayed_work(&operation->timeout_work, timeout); /* All set, send the request */ + gb_operation_result_set(operation, -EINPROGRESS); ret = gb_message_send(operation->request, GFP_KERNEL); if (ret || callback) return ret; -- cgit v1.2.3-59-g8ed1b From 1a365154c24a3fd48b96ad25cb67a483124d56ad Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Nov 2014 13:06:44 -0600 Subject: greybus: fix some error codes Change the message result values used in two cases. First, use -EMSGSIZE rather than -E2BIG to represent a message that is larger than the buffer intended to hold it. That is the proper code for this situation. Second, use -ECANCELED rather than -EINTR for an operation that has been canceled. The definition of that error is literally "Operation Canceled" so it seems like the right thing to do. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 82fd7e98074c..5e5c0977f35d 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -324,7 +324,7 @@ int gb_operation_status_map(u8 status) case GB_OP_PROTOCOL_BAD: return -EPROTONOSUPPORT; case GB_OP_OVERFLOW: - return -E2BIG; + return -EMSGSIZE; case GB_OP_TIMEOUT: return -ETIMEDOUT; default: @@ -510,7 +510,7 @@ int gb_operation_request_send(struct gb_operation *operation, /* Cancel the operation if interrupted */ ret = wait_for_completion_interruptible(&operation->completion); if (ret < 0) - gb_operation_cancel(operation, -EINTR); + gb_operation_cancel(operation, -ECANCELED); return gb_operation_result(operation); } @@ -605,7 +605,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, result = gb_operation_status_map(header->result); } else { gb_connection_err(connection, "recv buffer too small"); - result = -E2BIG; + result = -EMSGSIZE; } /* We must ignore the payload if a bad status is returned */ -- cgit v1.2.3-59-g8ed1b From aa3a4d12093b818ac2b9fe3e0454ae0090201254 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Nov 2014 13:06:45 -0600 Subject: greybus: enforce receive buffer size When an operation is created its receive buffer size is specified. In all current cases, the size supplied for the receive buffer is exactly the size that should be returned. In other words, if any fewer than that many bytes arrived in a response, it would be an error. So tighten the check on the number of bytes arriving for a response message, ensuring that the number of bytes received is *exactly the same* as the number of bytes available (rather than just less than). We'll expand our interpretation of of -EMSGSIZE to mean "wrong message size" rather than just "message too long." If we someday encounter an actual case where we want to be able to successfully receive something less than the full receive buffer we can adjust the code to handle that (and give it a way to tell the receiver how many bytes are present). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5e5c0977f35d..c3864bde5200 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -599,12 +599,13 @@ static void gb_connection_recv_response(struct gb_connection *connection, gb_pending_operation_remove(operation); message = operation->response; - if (size <= message->size) { + if (size == message->size) { /* Transfer the operation result from the response header */ header = message->header; result = gb_operation_status_map(header->result); } else { - gb_connection_err(connection, "recv buffer too small"); + gb_connection_err(connection, "bad message size (%zu != %zu)", + size, message->size); result = -EMSGSIZE; } -- cgit v1.2.3-59-g8ed1b From 894cbc31360102fe51babdb82be69885f317843b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Nov 2014 16:54:02 -0600 Subject: greybus: update operation result atomically An operation result can be set both in and out of interrupt context. For example, a response message could be arriving at the same time a timeout of the operation is getting processed. We therefore need to ensure the result is accessed atomically. Protect updates to the errno field using the operations spinlock. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c3864bde5200..b4ef82015750 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -82,18 +82,32 @@ static DEFINE_SPINLOCK(gb_operations_lock); */ static bool gb_operation_result_set(struct gb_operation *operation, int result) { + int prev; + + /* Nobody should be setting -EBADR */ if (WARN_ON(result == -EBADR)) return false; + /* Are we sending the request message? */ if (result == -EINPROGRESS) { - if (WARN_ON(operation->errno != -EBADR)) - return false; - } else if (operation->errno != -EINPROGRESS) { - return false; + /* Yes, but verify the result has not already been set */ + spin_lock_irq(&gb_operations_lock); + prev = operation->errno; + if (prev == -EBADR) + operation->errno = result; + spin_unlock_irq(&gb_operations_lock); + + return !WARN_ON(prev != -EBADR); } - operation->errno = result; - return true; + /* Trying to set final status; only the first one succeeds */ + spin_lock_irq(&gb_operations_lock); + prev = operation->errno; + if (prev == -EINPROGRESS) + operation->errno = result; + spin_unlock_irq(&gb_operations_lock); + + return prev == -EINPROGRESS; } int gb_operation_result(struct gb_operation *operation) -- cgit v1.2.3-59-g8ed1b From f34541d7e823bff1be8e0d497e01dcbaf9c65edb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Nov 2014 16:54:03 -0600 Subject: greybus: ignore a null cookie when canceling buffer It's possible for an in-flight buffer to be recorded as sent *after* a thread has begin the process of canceling it. In that case the Greybus message cookie will be set to NULL, and that value can end up getting passed to buffer_cancel(). Change buffer_cancel() so it properly handles (ignores) a null cookie pointer. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 204784329716..1832d0f41b8a 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -245,16 +245,22 @@ static void *buffer_send(struct greybus_host_device *hd, u16 dest_cport_id, return conceal_urb(urb); } +/* + * The cookie value supplied is the value that buffer_send() + * returned to its caller. It identifies the buffer that should be + * canceled. This function must also handle (which is to say, + * ignore) a null cookie value. + */ static void buffer_cancel(void *cookie) { - struct urb *urb = reveal_urb(cookie); /* * We really should be defensive and track all outstanding * (sent) buffers rather than trusting the cookie provided * is valid. For the time being, this will do. */ - usb_kill_urb(urb); + if (cookie) + usb_kill_urb(reveal_urb(cookie)); } static struct greybus_host_driver es1_driver = { -- cgit v1.2.3-59-g8ed1b From 43cdae5c3cbc000146cccbbfc651105feba9525e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Nov 2014 16:54:04 -0600 Subject: greybus: protect cookie with a mutex When a Greybus message is sent, the host driver supplies a cookie for Greybus to use to identify the sent message in the event it needs to be canceled. The cookie will be non-null while the message is in flight, and a null pointer otherwise. There are two problems with this, which arise out of the fact that a message can be canceled at any time--even concurrent with it getting sent (such as when Greybus is getting shut down). First, the host driver's buffer_send method can return an error value, which is non-null but not a valid cookie. So we need to ensure such a bogus cookie is never used to cancel a message. Second, we can't resolve that problem by assigning message->cookie only after we've determined it's not an error. The instant buffer_send() returns, the message may well be in flight and *should* be canceled at shutdown, so we need the cookie value to reflect that. In order to avoid these problems, protect access to a message's cookie value with a mutex. A spin lock can't be used because the window that needs protecting covers code that can block. We reset the cookie value to NULL as soon as the host driver has notified us it has been sent (or failed to). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 40 ++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b4ef82015750..226565d4503a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -32,6 +32,9 @@ static struct kmem_cache *gb_operation_cache; /* Workqueue to handle Greybus operation completions. */ static struct workqueue_struct *gb_operation_workqueue; +/* Protects the cookie representing whether a message is in flight */ +static DEFINE_MUTEX(gb_message_mutex); + /* * All operation messages (both requests and responses) begin with * a header that encodes the size of the data (header included). @@ -170,16 +173,20 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) struct gb_connection *connection = message->operation->connection; u16 dest_cport_id = connection->interface_cport_id; int ret = 0; + void *cookie; - message->cookie = connection->hd->driver->buffer_send(connection->hd, + mutex_lock(&gb_message_mutex); + cookie = connection->hd->driver->buffer_send(connection->hd, dest_cport_id, message->header, message->size, gfp_mask); - if (IS_ERR(message->cookie)) { - ret = PTR_ERR(message->cookie); - message->cookie = NULL; - } + if (IS_ERR(cookie)) + ret = PTR_ERR(cookie); + else + message->cookie = cookie; + mutex_unlock(&gb_message_mutex); + return ret; } @@ -189,13 +196,14 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) */ static void gb_message_cancel(struct gb_message *message) { - struct greybus_host_device *hd; - - if (!message->cookie) - return; /* Don't bother if the message isn't in flight */ + mutex_lock(&gb_message_mutex); + if (message->cookie) { + struct greybus_host_device *hd; - hd = message->operation->connection->hd; - hd->driver->buffer_cancel(message->cookie); + hd = message->operation->connection->hd; + hd->driver->buffer_cancel(message->cookie); + } + mutex_unlock(&gb_message_mutex); } #if 0 @@ -550,12 +558,16 @@ greybus_data_sent(struct greybus_host_device *hd, void *header, int status) struct gb_message *message; struct gb_operation *operation; - /* If there's no error, there's really nothing to do */ + /* XXX Right now we assume we're an outgoing request */ + message = gb_hd_message_find(hd, header); + + /* Record that the message is no longer in flight */ + message->cookie = NULL; + + /* If there's no error, there's really nothing more to do */ if (!status) return; /* Mark it complete? */ - /* XXX Right now we assume we're an outgoing request */ - message = gb_hd_message_find(hd, header); operation = message->operation; if (gb_operation_result_set(operation, status)) queue_work(gb_operation_workqueue, &operation->work); -- cgit v1.2.3-59-g8ed1b From 615772aace9c87bdcae038cea37c69c85373dba4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 25 Nov 2014 16:59:21 -0800 Subject: greybus: usb-gb: import a "buildable" version of the usb-gb.c driver Based on Fabien's original driver, this version is converted (mostly) to the new greybus operation apis. Lots of things still to do, not the least being hooking up proper responses... Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 3 +- drivers/staging/greybus/protocol.c | 5 + drivers/staging/greybus/protocol.h | 3 + drivers/staging/greybus/usb-gb.c | 393 +++++++++++++++++++++++++++++++++++++ 4 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/greybus/usb-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 81174b936b3a..4fa9b3f29e57 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -14,7 +14,8 @@ greybus-y := core.o \ sdio-gb.o \ uart-gb.o \ battery-gb.o \ - vibrator-gb.o + vibrator-gb.o \ + usb-gb.o obj-m += greybus.o obj-m += es1-ap-usb.o diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 55ef387b812f..31ba172c76b2 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -193,11 +193,16 @@ bool gb_protocol_init(void) pr_err("error initializing vibrator protocol\n"); ret = false; } + if (!gb_usb_protocol_init()) { + pr_err("error initializing usb protocol\n"); + ret = false; + } return ret; } void gb_protocol_exit(void) { + gb_usb_protocol_exit(); gb_vibrator_protocol_exit(); gb_sdio_protocol_exit(); gb_uart_protocol_exit(); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 69a40079a2c5..0ced63e9df91 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -67,6 +67,9 @@ extern void gb_sdio_protocol_exit(void); extern bool gb_vibrator_protocol_init(void); extern void gb_vibrator_protocol_exit(void); +extern bool gb_usb_protocol_init(void); +extern void gb_usb_protocol_exit(void); + bool gb_protocol_init(void); void gb_protocol_exit(void); diff --git a/drivers/staging/greybus/usb-gb.c b/drivers/staging/greybus/usb-gb.c new file mode 100644 index 000000000000..ab4e093b62af --- /dev/null +++ b/drivers/staging/greybus/usb-gb.c @@ -0,0 +1,393 @@ +/* + * USB host driver for the Greybus "generic" USB module. + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + * + */ +#include +#include +#include +#include +#include + +#include "greybus.h" + +/* Version of the Greybus USB protocol we support */ +#define GB_USB_VERSION_MAJOR 0x00 +#define GB_USB_VERSION_MINOR 0x01 + +/* Greybus USB request types */ +#define GB_USB_TYPE_INVALID 0x00 +#define GB_USB_TYPE_PROTOCOL_VERSION 0x01 +#define GB_USB_TYPE_HCD_STOP 0x02 +#define GB_USB_TYPE_HCD_START 0x03 +#define GB_USB_TYPE_URB_ENQUEUE 0x04 +#define GB_USB_TYPE_URB_DEQUEUE 0x05 +#define GB_USB_TYPE_ENDPOINT_DISABLE 0x06 +#define GB_USB_TYPE_HUB_CONTROL 0x07 +#define GB_USB_TYPE_GET_FRAME_NUMBER 0x08 +#define GB_USB_TYPE_HUB_STATUS_DATA 0x09 + +struct gb_usb_proto_version_response { + __u8 major; + __u8 minor; +}; + +struct gb_usb_urb_enqueue_request { + __le32 pipe; + __le32 transfer_flags; + __le32 transfer_buffer_length; + __le32 maxpacket; + __le32 interval; + __le64 hcpriv_ep; + __le32 number_of_packets; + u8 setup_packet[8]; + u8 payload[0]; +}; + +struct gb_usb_urb_dequeue_request { + __le64 hcpriv_ep; +}; + +struct gb_usb_endpoint_disable_request { + __le64 hcpriv; +}; + +struct gb_usb_hub_control_request { + __le16 typeReq; + __le16 wValue; + __le16 wIndex; + __le16 wLength; +}; + +struct gb_usb_hub_control_response { + u8 buf[0]; +}; + +struct gb_usb_header { + __le16 size; + __le16 id; + __u8 type; +}; + +struct gb_usb_hub_status { + __le32 status; + __le16 buf_size; + u8 buf[0]; +}; + +static struct gb_usb_hub_status *hub_status; // FIXME!!! +static DEFINE_SPINLOCK(hub_status_lock); +static atomic_t frame_number; // FIXME!!! + +struct gb_usb_device { + struct gb_connection *connection; + + struct usb_hcd *hcd; + u8 version_major; + u8 version_minor; +}; + +#define to_gb_usb_device(d) ((struct gb_usb_device*) d->hcd_priv) + +static int get_version(struct gb_usb_device *dev) +{ + struct gb_usb_proto_version_response response; + int ret; + + ret = gb_operation_sync(dev->connection, + GB_USB_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + if (response.major > GB_USB_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_USB_VERSION_MAJOR); + return -ENOTSUPP; + } + dev->version_major = response.major; + dev->version_minor = response.minor; + return 0; +} + +static void hcd_stop(struct usb_hcd *hcd) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + int ret; + + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, + NULL, 0, NULL, 0); + if (ret) + dev_err(&dev->connection->dev, "HCD stop failed '%d'\n", ret); +} + +static int hcd_start(struct usb_hcd *hcd) +{ + struct usb_bus *bus = hcd_to_bus(hcd); + struct gb_usb_device *dev = to_gb_usb_device(hcd); + int ret; + + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, + NULL, 0, NULL, 0); + if (ret) { + dev_err(&dev->connection->dev, "HCD start failed '%d'\n", ret); + return ret; + } + + hcd->state = HC_STATE_RUNNING; + if (bus->root_hub) + usb_hcd_resume_root_hub(hcd); + return 0; +} + +static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_usb_urb_enqueue_request *request; + struct gb_operation *operation; + int ret; + + operation = gb_operation_create(dev->connection, + GB_USB_TYPE_URB_ENQUEUE, + sizeof(*request) + + urb->transfer_buffer_length, 0); + if (!operation) + return -ENODEV; + + request = operation->request->payload; + request->pipe = cpu_to_le32(urb->pipe); + request->transfer_flags = cpu_to_le32(urb->transfer_flags); + request->transfer_buffer_length = cpu_to_le32(urb->transfer_buffer_length); + request->interval = cpu_to_le32(urb->interval); + request->hcpriv_ep = cpu_to_le64(urb->ep->hcpriv); + request->number_of_packets = cpu_to_le32(urb->number_of_packets); + + memcpy(request->setup_packet, urb->setup_packet, 8); + memcpy(&request->payload, urb->transfer_buffer, + urb->transfer_buffer_length); + + ret = gb_operation_request_send(operation, NULL); + gb_operation_destroy(operation); + + return ret; +} + +static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_usb_urb_dequeue_request request; + int ret; + + urb->ep->hcpriv = NULL; + request.hcpriv_ep = cpu_to_le64(urb->hcpriv); + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_URB_DEQUEUE, + &request, sizeof(request), NULL, 0); + urb->hcpriv = NULL; + return ret; +} + +static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_usb_endpoint_disable_request request; + int ret; + + request.hcpriv = cpu_to_le64(ep->hcpriv); + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_ENDPOINT_DISABLE, + &request, sizeof(request), NULL, 0); + ep->hcpriv = NULL; +} + +static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ +} + +static int get_frame_number(struct usb_hcd *hcd) +{ + return atomic_read(&frame_number); +} + +static int hub_status_data(struct usb_hcd *hcd, char *buf) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&hub_status_lock, flags); + memcpy(buf, hub_status->buf, le16_to_cpu(hub_status->buf_size)); + retval = le32_to_cpu(hub_status->status); + spin_unlock_irqrestore(&hub_status_lock, flags); + + return retval; +} + +static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, + char *buf, u16 wLength) +{ + struct gb_usb_hub_control_request request; + struct gb_usb_device *dev = to_gb_usb_device(hcd); + int ret; + + request.typeReq = cpu_to_le16(typeReq); + request.wValue = cpu_to_le16(wValue); + request.wIndex = cpu_to_le16(wIndex); + request.wLength = cpu_to_le16(wLength); + + // FIXME - buf needs to come back in struct gb_usb_hub_control_response + // for some types of requests, depending on typeReq. Do we do this in a + // "generic" way, or only ask for a response for the ones we "know" need + // a response (a small subset of all valid typeReq, thankfully.) + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HUB_CONTROL, + &request, sizeof(request), NULL, 0); + + return ret; +} + +static struct hc_driver usb_gb_hc_driver = { + .description = "greybus_usb", + .product_desc = "GB-Bridge USB Controller", /* TODO: Get this from GPB ?*/ + .flags = HCD_MEMORY | HCD_USB2, /* FIXME: Get this from GPB */ + .hcd_priv_size = sizeof(struct gb_usb_device), + + .start = hcd_start, + .stop = hcd_stop, + .urb_enqueue = urb_enqueue, + .urb_dequeue = urb_dequeue, + .endpoint_disable = endpoint_disable, + .endpoint_reset = endpoint_reset, + .get_frame_number = get_frame_number, + .hub_status_data = hub_status_data, + .hub_control = hub_control, +}; + +#if 0 +static inline void gb_usb_handle_get_frame_number(struct gbuf *gbuf) +{ + __le32 frame_num; + const size_t packet_size = sizeof(struct gb_usb_header) + + sizeof(frame_num); + struct gb_usb_header* hdr = gbuf->transfer_buffer; + + if (le16_to_cpu(hdr->size) != packet_size) { + pr_err("%s(): dropping packet too small\n", __func__); + return; + } + + frame_num = (__le32) ((char*) gbuf->transfer_buffer + + sizeof(struct gb_usb_header)); + atomic_set(&frame_number, le32_to_cpu(frame_num)); +} + +static inline void gb_usb_handle_hubs_status_data(struct gbuf *gbuf) +{ + struct gb_usb_hub_status *new_hubstatus, *hubstatus; + struct gb_usb_header* hdr = gbuf->transfer_buffer; + const size_t min_packet_size = sizeof(struct gb_usb_header) + + sizeof(struct gb_usb_hub_status); + unsigned long flags; + + if (le16_to_cpu(hdr->size) < min_packet_size) { + pr_err("%s(): dropping packet too small\n", __func__); + return; + } + + hubstatus = (struct gb_usb_hub_status*) ((char*) gbuf->transfer_buffer + + sizeof(struct gb_usb_header)); + + if (le16_to_cpu(hdr->size) != min_packet_size + hubstatus->buf_size) { + pr_err("%s(): invalid packet size, dropping packet\n", + __func__); + return; + } + + new_hubstatus = kmalloc(hubstatus->buf_size, GFP_KERNEL); + memcpy(&new_hubstatus, hubstatus, hubstatus->buf_size); + + spin_lock_irqsave(&hub_status_lock, flags); + hubstatus = hub_status; + hub_status = new_hubstatus; + spin_unlock_irqrestore(&hub_status_lock, flags); + + kfree(hubstatus); +} + +static void gb_usb_in_handler(struct gbuf *gbuf) +{ + struct gb_usb_header* hdr = gbuf->transfer_buffer; + + switch (hdr->type) { + case GB_USB_TYPE_GET_FRAME_NUMBER: + gb_usb_handle_get_frame_number(gbuf); + break; + + case GB_USB_TYPE_HUB_STATUS_DATA: + gb_usb_handle_hubs_status_data(gbuf); + break; + } +} +#endif + +static int gb_usb_connection_init(struct gb_connection *connection) +{ + struct device *dev = &connection->dev; + struct gb_usb_device *gb_usb_dev; + + int retval; + + gb_usb_dev = kzalloc(sizeof(*gb_usb_dev), GFP_KERNEL); + if (!gb_usb_dev) + return -ENOMEM; + + gb_usb_dev->connection = connection; + + /* Check for compatible protocol version */ + retval = get_version(gb_usb_dev); + if (retval) + goto error_create_hcd; + + gb_usb_dev->hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); + if (!gb_usb_dev->hcd) { + retval = -ENODEV; + goto error_create_hcd; + } + + gb_usb_dev->hcd->has_tt = 1; + gb_usb_dev->hcd->hcd_priv[0] = (unsigned long) gb_usb_dev; + + retval = usb_add_hcd(gb_usb_dev->hcd, 0, 0); + if (retval) + goto error_add_hcd; + + return 0; +error_add_hcd: + usb_put_hcd(gb_usb_dev->hcd); +error_create_hcd: + kfree(gb_usb_dev); + return retval; +} + +static void gb_usb_connection_exit(struct gb_connection *connection) +{ + // FIXME - tear everything down! +} + +static struct gb_protocol usb_protocol = { + .id = GREYBUS_PROTOCOL_USB, + .major = 0, + .minor = 1, + .connection_init = gb_usb_connection_init, + .connection_exit = gb_usb_connection_exit, + .request_recv = NULL, /* FIXME we have requests!!! */ +}; + +bool gb_usb_protocol_init(void) +{ + return gb_protocol_register(&usb_protocol); +} + +void gb_usb_protocol_exit(void) +{ + gb_protocol_deregister(&usb_protocol); +} -- cgit v1.2.3-59-g8ed1b From 94b15d7613e861563b9f06fac2c273ca8e2fc269 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 1 Dec 2014 07:53:06 -0600 Subject: greybus: use outgoing flag when creating operation In gb_operation_create_common(), a zero response size is still being used to determine whether to use GFP_KERNEL or GFP_ATOMIC when allocating a message. Use the value of the "outgoing" parameter to decide this instead. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 226565d4503a..7617410f7c5a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -377,7 +377,7 @@ gb_operation_create_common(struct gb_connection *connection, bool outgoing, { struct greybus_host_device *hd = connection->hd; struct gb_operation *operation; - gfp_t gfp_flags = response_size ? GFP_KERNEL : GFP_ATOMIC; + gfp_t gfp_flags = outgoing ? GFP_KERNEL : GFP_ATOMIC; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) -- cgit v1.2.3-59-g8ed1b From ab3cf8dc7db6755d216173b04043b0e0cd24415b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 1 Dec 2014 07:53:07 -0600 Subject: greybus: enforce max representable message size We represent the size of a message using a 16-bit field. It's possible for a host driver to advertise a maximum message size that's bigger than that. If that happens, reduce the host device's maximum buffer size to the maximum we can represent the first time a message is allocated. This information is actually only used by the Greybus code, but because we're modifying a value that's "owned" by the host driver, issue a warning when this limit is being imposed Ensure (at build time) that our own definition is sane as well. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 7617410f7c5a..8a023cbbf511 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -299,6 +299,12 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, size_t size; u8 *buffer; + if (hd->buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) { + pr_warn("limiting buffer size to %u\n", + GB_OPERATION_MESSAGE_SIZE_MAX); + hd->buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX; + } + if (message_size > hd->buffer_size_max) return NULL; @@ -750,6 +756,9 @@ int gb_operation_sync(struct gb_connection *connection, int type, int gb_operation_init(void) { + BUILD_BUG_ON(GB_OPERATION_MESSAGE_SIZE_MAX > + U16_MAX - sizeof(struct gb_operation_msg_hdr)); + gb_operation_cache = kmem_cache_create("gb_operation_cache", sizeof(struct gb_operation), 0, 0, NULL); if (!gb_operation_cache) -- cgit v1.2.3-59-g8ed1b From 2fb2d2a73f27bdd90a86d14c143e000e95d3c9d2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 1 Dec 2014 07:53:08 -0600 Subject: greybus: define -EILSEQ to mean implementation error Reserve operation result code -EILSEQ to represent that the code that implements an operation is broken. This is used (initially) for any attempt to set the result to -EBADR (which is reserved for an operation in initial state), or for an attempt to set the result of an operation that is *not* in initial state to -EINPROGRESS. Note that we still use -EIO gb_operation_status_map() to represent a gb_operation_result value that isn't recognized. In gb_operation_result(), warn if operation->errno is -EBADR. That is another value that indicates the operation is not in a state where it's valid to query an operation's result. Update a bunch of comments above gb_operation_result_set() to explain constraints on operation->errno. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 59 +++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 8a023cbbf511..905c6de9e20e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -70,44 +70,70 @@ struct gb_operation_msg_hdr { static DEFINE_SPINLOCK(gb_operations_lock); /* - * Set an operation's result. Initially an outgoing operation's - * errno value is -EBADR. If no error occurs before sending the - * request message the only valid value operation->errno can be - * set to is -EINPROGRESS, indicating the request has been (or - * rather is about to be) sent. At that point nobody should - * be looking at the result until the reponse arrives. + * Set an operation's result. + * + * Initially an outgoing operation's errno value is -EBADR. + * If no error occurs before sending the request message the only + * valid value operation->errno can be set to is -EINPROGRESS, + * indicating the request has been (or rather is about to be) sent. + * At that point nobody should be looking at the result until the + * reponse arrives. * * The first time the result gets set after the request has been * sent, that result "sticks." That is, if two concurrent threads * race to set the result, the first one wins. The return value * tells the caller whether its result was recorded; if not the - * has nothing more to do. + * caller has nothing more to do. + * + * The result value -EILSEQ is reserved to signal an implementation + * error; if it's ever observed, the code performing the request has + * done something fundamentally wrong. It is an error to try to set + * the result to -EBADR, and attempts to do so result in a warning, + * and -EILSEQ is used instead. Similarly, the only valid result + * value to set for an operation in initial state is -EINPROGRESS. + * Attempts to do otherwise will also record a (successful) -EILSEQ + * operation result. */ static bool gb_operation_result_set(struct gb_operation *operation, int result) { int prev; - /* Nobody should be setting -EBADR */ - if (WARN_ON(result == -EBADR)) - return false; - - /* Are we sending the request message? */ if (result == -EINPROGRESS) { - /* Yes, but verify the result has not already been set */ + /* + * -EINPROGRESS is used to indicate the request is + * in flight. It should be the first result value + * set after the initial -EBADR. Issue a warning + * and record an implementation error if it's + * set at any other time. + */ spin_lock_irq(&gb_operations_lock); prev = operation->errno; if (prev == -EBADR) operation->errno = result; + else + operation->errno = -EILSEQ; spin_unlock_irq(&gb_operations_lock); + WARN_ON(prev != -EBADR); - return !WARN_ON(prev != -EBADR); + return true; } - /* Trying to set final status; only the first one succeeds */ + /* + * The first result value set after a request has been sent + * will be the final result of the operation. Subsequent + * attempts to set the result are ignored. + * + * Note that -EBADR is a reserved "initial state" result + * value. Attempts to set this value result in a warning, + * and the result code is set to -EILSEQ instead. + */ + if (WARN_ON(result == -EBADR)) + result = -EILSEQ; /* Nobody should be setting -EBADR */ + spin_lock_irq(&gb_operations_lock); prev = operation->errno; if (prev == -EINPROGRESS) - operation->errno = result; + operation->errno = result; /* First and final result */ spin_unlock_irq(&gb_operations_lock); return prev == -EINPROGRESS; @@ -117,6 +143,7 @@ int gb_operation_result(struct gb_operation *operation) { int result = operation->errno; + WARN_ON(result == -EBADR); WARN_ON(result == -EINPROGRESS); return result; -- cgit v1.2.3-59-g8ed1b From 57248face3894f0b3e97ff1c9e9e8b8b22033ec3 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 1 Dec 2014 07:53:09 -0600 Subject: greybus: renumber operation result values Define a new operation status GB_OP_MALFUNCTION, which will be used to represent that something unexpected happened while handling an operation. This is intended as an indication similar to a BUG() call--whatever went wrong should *never* happen and because it's unexpected we need to treat it as a fatal error. Define another new operation status GB_OP_UNKNOWN_ERROR, which will represent the case where an operation ended in error, but the error was not recognized to be properly represented by one of the other status values. Renumber the operation status values, defining those that are produced by core operations code ahead of those that are more likely to come from operation handlers. Represent the values in hexadecimal to emphasize that they must be represented with 8 bits. The Use 0xff for GB_OP_MALFUNCTION instead of GB_OP_TIMEOUT; the latter is special, but a malfunction is in a class by itself. Reorder the cases in gb_operation_status_map() to match their numeric order. Map GB_OP_UNKNOWN_ERROR to -EIO in gb_operation_status_map(). Map GB_OP_MALFUNCTION to -EILSEQ in gb_operation_status_map(), since that value is used to represent an implementation error. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 19 +++++++++++-------- drivers/staging/greybus/operation.h | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 905c6de9e20e..0841ab271496 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -368,20 +368,23 @@ int gb_operation_status_map(u8 status) switch (status) { case GB_OP_SUCCESS: return 0; - case GB_OP_INVALID: - return -EINVAL; - case GB_OP_NO_MEMORY: - return -ENOMEM; case GB_OP_INTERRUPTED: return -EINTR; - case GB_OP_RETRY: - return -EAGAIN; + case GB_OP_TIMEOUT: + return -ETIMEDOUT; + case GB_OP_NO_MEMORY: + return -ENOMEM; case GB_OP_PROTOCOL_BAD: return -EPROTONOSUPPORT; case GB_OP_OVERFLOW: return -EMSGSIZE; - case GB_OP_TIMEOUT: - return -ETIMEDOUT; + case GB_OP_INVALID: + return -EINVAL; + case GB_OP_RETRY: + return -EAGAIN; + case GB_OP_MALFUNCTION: + return -EILSEQ; + case GB_OP_UNKNOWN_ERROR: default: return -EIO; } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 7f835d2e8f35..feff8238f7cf 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -14,14 +14,16 @@ struct gb_operation; enum gb_operation_result { - GB_OP_SUCCESS = 0, - GB_OP_INVALID = 1, - GB_OP_NO_MEMORY = 2, - GB_OP_INTERRUPTED = 3, - GB_OP_RETRY = 4, - GB_OP_PROTOCOL_BAD = 5, - GB_OP_OVERFLOW = 6, - GB_OP_TIMEOUT = 0xff, + GB_OP_SUCCESS = 0x00, + GB_OP_INTERRUPTED = 0x01, + GB_OP_TIMEOUT = 0x02, + GB_OP_NO_MEMORY = 0x03, + GB_OP_PROTOCOL_BAD = 0x04, + GB_OP_OVERFLOW = 0x05, + GB_OP_INVALID = 0x06, + GB_OP_RETRY = 0x07, + GB_OP_UNKNOWN_ERROR = 0xfe, + GB_OP_MALFUNCTION = 0xff, }; struct gb_message { -- cgit v1.2.3-59-g8ed1b From e413614b01763a84940168f253426fca89cdc38c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 1 Dec 2014 07:53:10 -0600 Subject: greybus: drop gfp_mask from gb_message_send() We will only send messages from process context. Drop the gfp_mask parameter from gb_message_send(), and just supply GFP_KERNEL to the host driver's buffer_send method. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 0841ab271496..75900d3129be 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -195,7 +195,7 @@ gb_pending_operation_find(struct gb_connection *connection, u16 operation_id) return found ? operation : NULL; } -static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) +static int gb_message_send(struct gb_message *message) { struct gb_connection *connection = message->operation->connection; u16 dest_cport_id = connection->interface_cport_id; @@ -207,7 +207,7 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp_mask) dest_cport_id, message->header, message->size, - gfp_mask); + GFP_KERNEL); if (IS_ERR(cookie)) ret = PTR_ERR(cookie); else @@ -561,7 +561,7 @@ int gb_operation_request_send(struct gb_operation *operation, /* All set, send the request */ gb_operation_result_set(operation, -EINPROGRESS); - ret = gb_message_send(operation->request, GFP_KERNEL); + ret = gb_message_send(operation->request); if (ret || callback) return ret; -- cgit v1.2.3-59-g8ed1b From e5fbc07360f1ed9da423abc2ab96a12f2d7a7632 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 1 Dec 2014 07:53:11 -0600 Subject: greybus: always drop reference in gb_operation_work() Currently we issue a warning in gb_operation_work() if an operation has no callback function defined. But we return without dropping the reference to the operation as we should. Stop warning if there's no callback, call it only if it's defined, and always drop the operation reference before returning. This means we're now treating a NULL callback pointer as a normal condition. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 75900d3129be..aaac03733fd4 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -265,11 +265,10 @@ static void gb_operation_work(struct work_struct *work) struct gb_operation *operation; operation = container_of(work, struct gb_operation, work); - if (WARN_ON(!operation->callback)) - return; - - operation->callback(operation); - operation->callback = NULL; + if (operation->callback) { + operation->callback(operation); + operation->callback = NULL; + } gb_operation_put(operation); } -- cgit v1.2.3-59-g8ed1b From 85a0442893444c044c0dde979c0372d7369ab45f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Dec 2014 20:42:20 -0800 Subject: greybus: operation: fix up sparse warning gb_connection_recv_request should be static, so mark it as such. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index aaac03733fd4..d973b57dc693 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -616,8 +616,9 @@ EXPORT_SYMBOL_GPL(greybus_data_sent); * This is called in interrupt context, so just copy the incoming * data into the request buffer and handle the rest via workqueue. */ -void gb_connection_recv_request(struct gb_connection *connection, - u16 operation_id, u8 type, void *data, size_t size) +static void gb_connection_recv_request(struct gb_connection *connection, + u16 operation_id, u8 type, void *data, + size_t size) { struct gb_operation *operation; -- cgit v1.2.3-59-g8ed1b From 34db1f91e674ba8c7df674ac6692c2604ea1ece5 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:28 -0600 Subject: greybus: move copy of incoming request data Currently incoming request data is copied into a request message buffer in gb_connection_recv_request(). Move that--along with the assignment of the message id--into gb_operation_create_incoming(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d973b57dc693..1e0ce7d98618 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -465,11 +465,19 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, static struct gb_operation * gb_operation_create_incoming(struct gb_connection *connection, - u8 type, size_t request_size, - size_t response_size) + u16 operation_id, u8 type, + void *data, size_t request_size) { - return gb_operation_create_common(connection, false, type, - request_size, response_size); + struct gb_operation *operation; + + operation = gb_operation_create_common(connection, false, type, + request_size, 0); + if (operation) { + operation->id = operation_id; + memcpy(operation->request->header, data, request_size); + } + + return operation; } /* @@ -622,13 +630,12 @@ static void gb_connection_recv_request(struct gb_connection *connection, { struct gb_operation *operation; - operation = gb_operation_create_incoming(connection, type, size, 0); + operation = gb_operation_create_incoming(connection, operation_id, + type, data, size); if (!operation) { gb_connection_err(connection, "can't create operation"); return; /* XXX Respond with pre-allocated ENOMEM */ } - operation->id = operation_id; - memcpy(operation->request->header, data, size); /* XXX Right now this will just complete the operation */ if (gb_operation_result_set(operation, -ENOSYS)) -- cgit v1.2.3-59-g8ed1b From f71e1cc1944d0a82aff032acd9bb25dff5187d37 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:29 -0600 Subject: greybus: short message is OK for errors We enforce a rule that a response message must completely fill the buffer that's been allocated to hold it. However, if an error occurs, the payload is off limits, so we should allow a short message to convey an error result. Change gb_connection_recv_response() to require the right message size only if there's no error. One other thing: The arriving data is only being copied into the response buffer if the request was successful. That means the response message header is assumed to have been initialized. That isn't a valid assumption. So change it so that if an error is seen, the header portion of the message is copied into the response buffer--but only the header. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 1e0ce7d98618..2fd60ccb7018 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -655,7 +655,6 @@ static void gb_connection_recv_response(struct gb_connection *connection, { struct gb_operation *operation; struct gb_message *message; - struct gb_operation_msg_hdr *header; int result; operation = gb_pending_operation_find(connection, operation_id); @@ -668,19 +667,17 @@ static void gb_connection_recv_response(struct gb_connection *connection, gb_pending_operation_remove(operation); message = operation->response; - if (size == message->size) { - /* Transfer the operation result from the response header */ - header = message->header; - result = gb_operation_status_map(header->result); - } else { + result = gb_operation_status_map(message->header->result); + if (!result && size != message->size) { gb_connection_err(connection, "bad message size (%zu != %zu)", size, message->size); result = -EMSGSIZE; } /* We must ignore the payload if a bad status is returned */ - if (!result) - memcpy(message->header, data, size); + if (result) + size = sizeof(*message->header); + memcpy(message->header, data, size); /* The rest will be handled in work queue context */ if (gb_operation_result_set(operation, result)) -- cgit v1.2.3-59-g8ed1b From 64ce39a3463776a6ccf6770f588a265bca2468ca Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:30 -0600 Subject: greybus: pass result in gb_connection_recv_response() Pass the operation result to gb_connection_recv_response() as a parameter. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 2fd60ccb7018..c4898f61b1f7 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -651,11 +651,11 @@ static void gb_connection_recv_request(struct gb_connection *connection, * data into the response buffer and handle the rest via workqueue. */ static void gb_connection_recv_response(struct gb_connection *connection, - u16 operation_id, void *data, size_t size) + u16 operation_id, u8 result, void *data, size_t size) { struct gb_operation *operation; struct gb_message *message; - int result; + int errno = gb_operation_status_map(result); operation = gb_pending_operation_find(connection, operation_id); if (!operation) { @@ -667,20 +667,19 @@ static void gb_connection_recv_response(struct gb_connection *connection, gb_pending_operation_remove(operation); message = operation->response; - result = gb_operation_status_map(message->header->result); - if (!result && size != message->size) { + if (!errno && size != message->size) { gb_connection_err(connection, "bad message size (%zu != %zu)", size, message->size); - result = -EMSGSIZE; + errno = -EMSGSIZE; } /* We must ignore the payload if a bad status is returned */ - if (result) + if (errno) size = sizeof(*message->header); memcpy(message->header, data, size); /* The rest will be handled in work queue context */ - if (gb_operation_result_set(operation, result)) + if (gb_operation_result_set(operation, errno)) queue_work(gb_operation_workqueue, &operation->work); } @@ -717,7 +716,7 @@ void gb_connection_recv(struct gb_connection *connection, operation_id = le16_to_cpu(header->operation_id); if (header->type & GB_OPERATION_TYPE_RESPONSE) gb_connection_recv_response(connection, operation_id, - data, msg_size); + header->result, data, msg_size); else gb_connection_recv_request(connection, operation_id, header->type, data, msg_size); -- cgit v1.2.3-59-g8ed1b From 55f66a88dbd718c2133ac759eee4ff51f516bf45 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:31 -0600 Subject: greybus: enforce non-zero operation type requirement The operation type 0x00 is reserved as an explicitly invalid operation type in all protocols. Enforce this. Add a check for callers who erroneously have the RESPONSE message type flag set in the operation type passed in gb_operation_create(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c4898f61b1f7..53fffb190dbf 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -455,10 +455,23 @@ err_cache: return NULL; } +/* + * Create a new operation associated with the given connection. The + * request and response sizes provided are the number of bytes + * required to hold the request/response payload only. Both of + * these are allowed to be 0. Note that 0x00 is reserved as an + * invalid operation type for all protocols, and this is enforced + * here. + */ struct gb_operation *gb_operation_create(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size) { + if (WARN_ON_ONCE(!type)) + return NULL; + if (WARN_ON_ONCE(type & GB_OPERATION_TYPE_RESPONSE)) + type &= ~GB_OPERATION_TYPE_RESPONSE; + return gb_operation_create_common(connection, true, type, request_size, response_size); } -- cgit v1.2.3-59-g8ed1b From ea64cd9a5e83605bdb6374b48d3aa84f0d08abde Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:32 -0600 Subject: greybus: use operation type 0 to signal incoming data When incoming data is going to be handled as a request, we create a new operation whose request buffer will hold the received data. There is no need to initialize the message header in such a request buffer because it will be immediately overwritten. Use operation type value of 0x00 in gb_operation_create_common() to signal that we are creating an incoming operation, and therefore do not need to initialize the request message header. This allows us to get rid of the Boolean "outgoing" parameter. As a result, we can stop supplying the "type" parameter to both gb_operation_create_incoming() and gb_connection_recv_request(). Update the header comments for gb_operation_message_alloc() and gb_operation_create_common(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 88 +++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 53fffb190dbf..f474e8fea1b4 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -302,12 +302,14 @@ gb_hd_message_find(struct greybus_host_device *hd, void *header) } /* - * Allocate a message to be used for an operation request or - * response. For outgoing messages, both types of message contain a - * common header, which is filled in here. Incoming requests or - * responses also contain the same header, but there's no need to - * initialize it here (it'll be overwritten by the incoming - * message). + * Allocate a message to be used for an operation request or response. + * Both types of message contain a common header. The request message + * for an outgoing operation is outbound, as is the response message + * for an incoming operation. The message header for an outbound + * message is partially initialized here. + * + * The headers for inbound messages don't need to be initialized; + * they'll be filled in by arriving data. * * Our message structure consists of: * message structure @@ -341,15 +343,31 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, buffer = &message->buffer[0]; header = (struct gb_operation_msg_hdr *)(buffer + hd->buffer_headroom); - /* Fill in the header structure */ - header->size = cpu_to_le16(message_size); - header->operation_id = 0; /* Filled in when submitted */ - header->type = type; - message->header = header; message->payload = header + 1; message->size = message_size; + /* + * The type supplied for incoming message buffers will be + * 0x00. Such buffers will be overwritten by arriving data + * so there's no need to initialize the message header. + */ + if (type) { + /* + * For a request, the operation id gets filled in + * when the message is sent. For a response, it + * will be copied from the request by the caller. + * + * The result field in a request message must be + * zero. It will be set just prior to sending for + * a response. + */ + header->size = cpu_to_le16(message_size); + header->operation_id = 0; + header->type = type; + header->result = 0; + } + return message; } @@ -392,27 +410,32 @@ int gb_operation_status_map(u8 status) /* * Create a Greybus operation to be sent over the given connection. * The request buffer will be big enough for a payload of the given - * size. Outgoing requests must specify the size of the response - * buffer size, which must be sufficient to hold all expected - * response data. + * size. + * + * For outgoing requests, the request message's header will be + * initialized with the type of the request and the message size. + * Outgoing operations must also specify the response buffer size, + * which must be sufficient to hold all expected response data. The + * response message header will eventually be overwritten, so there's + * no need to initialize it here. * - * Incoming requests will supply a response size of 0, and in that - * case no response buffer is allocated. (A response always - * includes a status byte, so 0 is not a valid size.) Whatever - * handles the operation request is responsible for allocating the - * response buffer. + * Request messages for incoming operations can arrive in interrupt + * context, so they must be allocated with GFP_ATOMIC. In this case + * the request buffer will be immediately overwritten, so there is + * no need to initialize the message header. Responsibility for + * allocating a response buffer lies with the incoming request + * handler for a protocol. So we don't allocate that here. * * Returns a pointer to the new operation or a null pointer if an * error occurs. */ static struct gb_operation * -gb_operation_create_common(struct gb_connection *connection, bool outgoing, - u8 type, size_t request_size, - size_t response_size) +gb_operation_create_common(struct gb_connection *connection, u8 type, + size_t request_size, size_t response_size) { struct greybus_host_device *hd = connection->hd; struct gb_operation *operation; - gfp_t gfp_flags = outgoing ? GFP_KERNEL : GFP_ATOMIC; + gfp_t gfp_flags = type ? GFP_KERNEL : GFP_ATOMIC; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) @@ -425,7 +448,8 @@ gb_operation_create_common(struct gb_connection *connection, bool outgoing, goto err_cache; operation->request->operation = operation; - if (outgoing) { + /* Allocate the response buffer for outgoing operations */ + if (type) { type |= GB_OPERATION_TYPE_RESPONSE; operation->response = gb_operation_message_alloc(hd, type, response_size, GFP_KERNEL); @@ -472,21 +496,19 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, if (WARN_ON_ONCE(type & GB_OPERATION_TYPE_RESPONSE)) type &= ~GB_OPERATION_TYPE_RESPONSE; - return gb_operation_create_common(connection, true, type, + return gb_operation_create_common(connection, type, request_size, response_size); } static struct gb_operation * -gb_operation_create_incoming(struct gb_connection *connection, - u16 operation_id, u8 type, +gb_operation_create_incoming(struct gb_connection *connection, u16 id, void *data, size_t request_size) { struct gb_operation *operation; - operation = gb_operation_create_common(connection, false, type, - request_size, 0); + operation = gb_operation_create_common(connection, 0, request_size, 0); if (operation) { - operation->id = operation_id; + operation->id = id; memcpy(operation->request->header, data, request_size); } @@ -638,13 +660,13 @@ EXPORT_SYMBOL_GPL(greybus_data_sent); * data into the request buffer and handle the rest via workqueue. */ static void gb_connection_recv_request(struct gb_connection *connection, - u16 operation_id, u8 type, void *data, + u16 operation_id, void *data, size_t size) { struct gb_operation *operation; operation = gb_operation_create_incoming(connection, operation_id, - type, data, size); + data, size); if (!operation) { gb_connection_err(connection, "can't create operation"); return; /* XXX Respond with pre-allocated ENOMEM */ @@ -732,7 +754,7 @@ void gb_connection_recv(struct gb_connection *connection, header->result, data, msg_size); else gb_connection_recv_request(connection, operation_id, - header->type, data, msg_size); + data, msg_size); } /* -- cgit v1.2.3-59-g8ed1b From dc779229b538f1b5cd5d20a5afdfdfb4c83e5429 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:33 -0600 Subject: greybus: introduce gb_operation_message_init() Separate the allocation of a message structure from its basic initialization. This will allow very common fixed-size operation response buffers to be allocated from a slab cache. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 66 ++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f474e8fea1b4..d335bd2454ae 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -301,6 +301,43 @@ gb_hd_message_find(struct greybus_host_device *hd, void *header) return message; } +static void gb_operation_message_init(struct greybus_host_device *hd, + struct gb_message *message, u16 operation_id, + size_t message_size, u8 type) +{ + struct gb_operation_msg_hdr *header; + u8 *buffer; + + BUG_ON(message_size < sizeof(*header)); + buffer = &message->buffer[0]; + header = (struct gb_operation_msg_hdr *)(buffer + hd->buffer_headroom); + + message->header = header; + message->payload = header + 1; + message->size = message_size; + + /* + * The type supplied for incoming message buffers will be + * 0x00. Such buffers will be overwritten by arriving data + * so there's no need to initialize the message header. + */ + if (type) { + /* + * For a request, the operation id gets filled in + * when the message is sent. For a response, it + * will be copied from the request by the caller. + * + * The result field in a request message must be + * zero. It will be set just prior to sending for + * a response. + */ + header->size = cpu_to_le16(message_size); + header->operation_id = 0; + header->type = type; + header->result = 0; + } +} + /* * Allocate a message to be used for an operation request or response. * Both types of message contain a common header. The request message @@ -325,7 +362,6 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, struct gb_operation_msg_hdr *header; size_t message_size = payload_size + sizeof(*header); size_t size; - u8 *buffer; if (hd->buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) { pr_warn("limiting buffer size to %u\n", @@ -340,33 +376,9 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, message = kzalloc(size, gfp_flags); if (!message) return NULL; - buffer = &message->buffer[0]; - header = (struct gb_operation_msg_hdr *)(buffer + hd->buffer_headroom); - message->header = header; - message->payload = header + 1; - message->size = message_size; - - /* - * The type supplied for incoming message buffers will be - * 0x00. Such buffers will be overwritten by arriving data - * so there's no need to initialize the message header. - */ - if (type) { - /* - * For a request, the operation id gets filled in - * when the message is sent. For a response, it - * will be copied from the request by the caller. - * - * The result field in a request message must be - * zero. It will be set just prior to sending for - * a response. - */ - header->size = cpu_to_le16(message_size); - header->operation_id = 0; - header->type = type; - header->result = 0; - } + /* Initialize the message. Operation id is filled in later. */ + gb_operation_message_init(hd, message, 0, message_size, type); return message; } -- cgit v1.2.3-59-g8ed1b From 835fb5e4985de46aafb7fbcf975663d375e7b4b4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:34 -0600 Subject: greybus: enforce a buffer headroom maximum size Define a maximum size that a host device can use for its private area ahead of the payload space used by Greybus in a message buffer. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 1 + drivers/staging/greybus/greybus.h | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 1832d0f41b8a..f551907ced2d 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -127,6 +127,7 @@ static void hd_buffer_constraints(struct greybus_host_device *hd) */ hd->buffer_headroom = sizeof(u32); /* For cport id */ hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; + BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 8fda37c42a0c..11f4e5529d9a 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -69,6 +69,14 @@ struct greybus_host_device; struct svc_msg; +/* + * When the Greybus code allocates a buffer it sets aside bytes + * prior to the beginning of the payload area for the host device's + * exclusive use. The size is specified by hd->buffer_headroom, and + * which can't be greater than GB_BUFFER_HEADROOM_MAX. + */ +#define GB_BUFFER_HEADROOM_MAX sizeof(u64) + /* Buffers allocated from the host driver will be aligned to this multiple */ #define GB_BUFFER_ALIGN sizeof(u32) -- cgit v1.2.3-59-g8ed1b From 0cffcac3051fa1447d8a452ab5e0029bbe100777 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:35 -0600 Subject: greybus: create a slab cache for simple messages A large number of request and response message types have no payload. Such "simple" messages have a known, fixed maximum size, so we can preallocate and use a pool (slab cache) of them. Here are two benefits to doing this: - There can be (small) performance and memory utilization benefits to using a slab cache. - Error responses can be sent with no payload; the cache is likely to have a free entry to use for an error response even in a low memory situation. The plan here is that an incoming request handler that has no response payload to fill will not need to allocate a response message. If no message has been allocated when a response is to be sent, one will be allocated from the cache by the core code. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 60 ++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d335bd2454ae..2a6f361b877f 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -28,6 +28,7 @@ #define GB_OPERATION_MESSAGE_SIZE_MAX 4096 static struct kmem_cache *gb_operation_cache; +static struct kmem_cache *gb_simple_message_cache; /* Workqueue to handle Greybus operation completions. */ static struct workqueue_struct *gb_operation_workqueue; @@ -369,11 +370,19 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, hd->buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX; } - if (message_size > hd->buffer_size_max) - return NULL; + /* Allocate the message. Use the slab cache for simple messages */ + if (payload_size) { + if (message_size > hd->buffer_size_max) { + pr_warn("requested message size too big (%zu > %zu)\n", + message_size, hd->buffer_size_max); + return NULL; + } - size = sizeof(*message) + hd->buffer_headroom + message_size; - message = kzalloc(size, gfp_flags); + size = sizeof(*message) + hd->buffer_headroom + message_size; + message = kzalloc(size, gfp_flags); + } else { + message = kmem_cache_zalloc(gb_simple_message_cache, gfp_flags); + } if (!message) return NULL; @@ -385,7 +394,10 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, static void gb_operation_message_free(struct gb_message *message) { - kfree(message); + if (message->size > sizeof(message->header)) + kfree(message); + else + kmem_cache_free(gb_simple_message_cache, message); } /* @@ -836,22 +848,46 @@ int gb_operation_sync(struct gb_connection *connection, int type, int gb_operation_init(void) { + size_t size; + BUILD_BUG_ON(GB_OPERATION_MESSAGE_SIZE_MAX > U16_MAX - sizeof(struct gb_operation_msg_hdr)); + /* + * A message structure with consists of: + * - the message structure itself + * - the headroom set aside for the host device + * - the message header + * - space for the message payload + * Messages with no payload are a fairly common case and + * have a known fixed maximum size, so we use a slab cache + * for them. + */ + size = sizeof(struct gb_message) + GB_BUFFER_HEADROOM_MAX + + sizeof(struct gb_operation_msg_hdr); + gb_simple_message_cache = kmem_cache_create("gb_simple_message_cache", + size, 0, 0, NULL); + if (!gb_simple_message_cache) + return -ENOMEM; + gb_operation_cache = kmem_cache_create("gb_operation_cache", sizeof(struct gb_operation), 0, 0, NULL); if (!gb_operation_cache) - return -ENOMEM; + goto err_simple; gb_operation_workqueue = alloc_workqueue("greybus_operation", 0, 1); - if (!gb_operation_workqueue) { - kmem_cache_destroy(gb_operation_cache); - gb_operation_cache = NULL; - return -ENOMEM; - } + if (!gb_operation_workqueue) + goto err_operation; return 0; +err_operation: + kmem_cache_destroy(gb_operation_cache); + gb_operation_cache = NULL; +err_simple: + kmem_cache_destroy(gb_simple_message_cache); + gb_simple_message_cache = NULL; + + return -ENOMEM; } void gb_operation_exit(void) @@ -860,4 +896,6 @@ void gb_operation_exit(void) gb_operation_workqueue = NULL; kmem_cache_destroy(gb_operation_cache); gb_operation_cache = NULL; + kmem_cache_destroy(gb_simple_message_cache); + gb_simple_message_cache = NULL; } -- cgit v1.2.3-59-g8ed1b From d2d2c0fe70ff09510f56bd341e2ab415b7b5e947 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:36 -0600 Subject: greybus: set result in gb_operation_response_send() Change gb_operation_response_send() so it takes an errno to assign as an operation's result. This emphasizes that setting the result should be the last thing done to an incoming operation before sending its response. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 7 ++++++- drivers/staging/greybus/operation.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 2a6f361b877f..c80eb33f5884 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -642,8 +642,13 @@ int gb_operation_request_send(struct gb_operation *operation, /* * Send a response for an incoming operation request. */ -int gb_operation_response_send(struct gb_operation *operation) +int gb_operation_response_send(struct gb_operation *operation, int errno) { + /* Record the result */ + if (!gb_operation_result_set(operation, errno)) { + pr_err("request result already set\n"); + return -EIO; /* Shouldn't happen */ + } gb_operation_destroy(operation); return 0; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index feff8238f7cf..ed344f8de2a7 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -100,7 +100,7 @@ static inline void gb_operation_destroy(struct gb_operation *operation) int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback); -int gb_operation_response_send(struct gb_operation *operation); +int gb_operation_response_send(struct gb_operation *operation, int errno); void gb_operation_cancel(struct gb_operation *operation, int errno); -- cgit v1.2.3-59-g8ed1b From d4a1ff674dfbd120930dbd011773b3ae79385449 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:37 -0600 Subject: greybus: activate incoming request handling Un-comment gb_operation_request_handle(), which was recently disabled to avoid distraction. In gb_connection_recv_request(), activate handling incoming requests by defining gb_operation_request_handle() as an incoming operation's callback function. Incoming operation requests have Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 59 +++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c80eb33f5884..8ffc54bacba1 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -234,7 +234,6 @@ static void gb_message_cancel(struct gb_message *message) mutex_unlock(&gb_message_mutex); } -#if 0 static void gb_operation_request_handle(struct gb_operation *operation) { struct gb_protocol *protocol = operation->connection->protocol; @@ -253,13 +252,21 @@ static void gb_operation_request_handle(struct gb_operation *operation) gb_connection_err(operation->connection, "unexpected incoming request type 0x%02hhx\n", header->type); - (void)gb_operation_result_set(operation, -EPROTONOSUPPORT); + if (gb_operation_result_set(operation, -EPROTONOSUPPORT)) + queue_work(gb_operation_workqueue, &operation->work); + else + WARN(true, "failed to mark request bad\n"); } -#endif /* - * Complete an operation in non-atomic context. The operation's - * result value should have been set before queueing this. + * Complete an operation in non-atomic context. For incoming + * requests, the callback function is the request handler, and + * the operation result should be -EINPROGRESS at this point. + * + * For outgoing requests, the operation result value should have + * been set before queueing this. The operation callback function + * allows the original requester to know the request has completed + * and its result is available. */ static void gb_operation_work(struct work_struct *work) { @@ -665,19 +672,29 @@ greybus_data_sent(struct greybus_host_device *hd, void *header, int status) struct gb_message *message; struct gb_operation *operation; - /* XXX Right now we assume we're an outgoing request */ + /* Get the message and record that it is no longer in flight */ message = gb_hd_message_find(hd, header); - - /* Record that the message is no longer in flight */ message->cookie = NULL; - /* If there's no error, there's really nothing more to do */ - if (!status) - return; /* Mark it complete? */ - + /* + * If the message was a response, we just need to drop our + * reference to the operation. If an error occurred, report + * it. + * + * For requests, if there's no error, there's nothing more + * to do until the response arrives. If an error occurred + * attempting to send it, record that as the result of + * the operation and schedule its completion. + */ operation = message->operation; - if (gb_operation_result_set(operation, status)) - queue_work(gb_operation_workqueue, &operation->work); + if (message == operation->response) { + if (status) + pr_err("error %d sending response\n", status); + gb_operation_put(operation); + } else if (status) { + if (gb_operation_result_set(operation, status)) + queue_work(gb_operation_workqueue, &operation->work); + } } EXPORT_SYMBOL_GPL(greybus_data_sent); @@ -701,8 +718,18 @@ static void gb_connection_recv_request(struct gb_connection *connection, return; /* XXX Respond with pre-allocated ENOMEM */ } - /* XXX Right now this will just complete the operation */ - if (gb_operation_result_set(operation, -ENOSYS)) + /* + * Incoming requests are handled by arranging for the + * request handler to be the operation's callback function. + * + * The last thing the handler does is send a response + * message. The callback function is then cleared (in + * gb_operation_work()). The original reference to the + * operation will be dropped when the response has been + * sent. + */ + operation->callback = gb_operation_request_handle; + if (gb_operation_result_set(operation, -EINPROGRESS)) queue_work(gb_operation_workqueue, &operation->work); } -- cgit v1.2.3-59-g8ed1b From 0c90fff4e35c1322a52709f17d630431c7deb931 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:38 -0600 Subject: greybus: introduce gb_operation_errno_map() Define gb_operation_errno_map(), which maps an operation->errno into the u8 value that represents it in the status field of an operation response header. It'll be used in an upcoming patch. Make gb_operation_status_map() a private function. It's not used outside "operation.c" and I don't believe it ever should be. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 36 +++++++++++++++++++++++++++++++++++- drivers/staging/greybus/operation.h | 2 -- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 8ffc54bacba1..f3246818d777 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -411,7 +411,7 @@ static void gb_operation_message_free(struct gb_message *message) * Map an enum gb_operation_status value (which is represented in a * message as a single byte) to an appropriate Linux negative errno. */ -int gb_operation_status_map(u8 status) +static int gb_operation_status_map(u8 status) { switch (status) { case GB_OP_SUCCESS: @@ -438,6 +438,39 @@ int gb_operation_status_map(u8 status) } } +/* + * Map a Linux errno value (from operation->errno) into the value + * that should represent it in a response message status sent + * over the wire. Returns an enum gb_operation_status value (which + * is represented in a message as a single byte). + */ +static u8 gb_operation_errno_map(int errno) +{ + switch (errno) { + case 0: + return GB_OP_SUCCESS; + case -EINTR: + return GB_OP_INTERRUPTED; + case -ETIMEDOUT: + return GB_OP_TIMEOUT; + case -ENOMEM: + return GB_OP_NO_MEMORY; + case -EPROTONOSUPPORT: + return GB_OP_PROTOCOL_BAD; + case -EMSGSIZE: + return GB_OP_OVERFLOW; /* Could be underflow too */ + case -EINVAL: + return GB_OP_INVALID; + case -EAGAIN: + return GB_OP_RETRY; + case -EILSEQ: + return GB_OP_MALFUNCTION; + case -EIO: + default: + return GB_OP_UNKNOWN_ERROR; + } +} + /* * Create a Greybus operation to be sent over the given connection. * The request buffer will be big enough for a payload of the given @@ -656,6 +689,7 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) pr_err("request result already set\n"); return -EIO; /* Shouldn't happen */ } + (void)gb_operation_errno_map; /* avoid a build warning */ gb_operation_destroy(operation); return 0; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index ed344f8de2a7..adaec7c43ea9 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -104,8 +104,6 @@ int gb_operation_response_send(struct gb_operation *operation, int errno); void gb_operation_cancel(struct gb_operation *operation, int errno); -int gb_operation_status_map(u8 status); - void greybus_data_sent(struct greybus_host_device *hd, void *header, int status); -- cgit v1.2.3-59-g8ed1b From 82e26f73b2a8ee4acc2507494430aa5774da2b74 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 08:30:39 -0600 Subject: greybus: send operation response messages Define a helper function gb_operation_response_alloc() and use it to allocate the response buffer for outgoing operations in gb_operation_create_common(. Use it also in gb_operation_response_send() if the caller has not allocated a response buffer. Once a response buffer is allocated, fill in its result code and send it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 60 ++++++++++++++++++++++++++++++------- drivers/staging/greybus/operation.h | 3 ++ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f3246818d777..1409d31f48c0 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -471,6 +471,34 @@ static u8 gb_operation_errno_map(int errno) } } +bool gb_operation_response_alloc(struct gb_operation *operation, + size_t response_size) +{ + struct greybus_host_device *hd = operation->connection->hd; + struct gb_operation_msg_hdr *request_header; + struct gb_message *response; + u8 type; + + request_header = operation->request->header; + type = request_header->type | GB_OPERATION_TYPE_RESPONSE; + response = gb_operation_message_alloc(hd, type, response_size, + GFP_KERNEL); + if (!response) + return false; + response->operation = operation; + + /* + * Size and type get initialized when the message is + * allocated. The errno will be set before sending. All + * that's left is the operation id, which we copy from the + * request message header (as-is, in little-endian order). + */ + response->header->operation_id = request_header->operation_id; + operation->response = response; + + return true; +} + /* * Create a Greybus operation to be sent over the given connection. * The request buffer will be big enough for a payload of the given @@ -513,14 +541,9 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, operation->request->operation = operation; /* Allocate the response buffer for outgoing operations */ - if (type) { - type |= GB_OPERATION_TYPE_RESPONSE; - operation->response = gb_operation_message_alloc(hd, type, - response_size, GFP_KERNEL); - if (!operation->response) + if (type) + if (!gb_operation_response_alloc(operation, response_size)) goto err_request; - operation->response->operation = operation; - } operation->errno = -EBADR; /* Initial value--means "never set" */ INIT_WORK(&operation->work, gb_operation_work); @@ -680,7 +703,13 @@ int gb_operation_request_send(struct gb_operation *operation, } /* - * Send a response for an incoming operation request. + * Send a response for an incoming operation request. A non-zero + * errno indicates a failed operation. + * + * If there is any response payload, the incoming request handler is + * responsible for allocating the response message. Otherwise the + * it can simply supply the result errno; this function will + * allocate the response message if necessary. */ int gb_operation_response_send(struct gb_operation *operation, int errno) { @@ -689,10 +718,19 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) pr_err("request result already set\n"); return -EIO; /* Shouldn't happen */ } - (void)gb_operation_errno_map; /* avoid a build warning */ - gb_operation_destroy(operation); - return 0; + if (!operation->response) { + if (!gb_operation_response_alloc(operation, 0)) { + pr_err("error allocating response\n"); + /* XXX Respond with pre-allocated -ENOMEM? */ + return -ENOMEM; + } + } + + /* Fill in the response header and send it */ + operation->response->header->result = gb_operation_errno_map(errno); + + return gb_message_send(operation->response); } /* diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index adaec7c43ea9..c60decb40539 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -98,6 +98,9 @@ static inline void gb_operation_destroy(struct gb_operation *operation) gb_operation_put(operation); } +bool gb_operation_response_alloc(struct gb_operation *operation, + size_t response_size); + int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback); int gb_operation_response_send(struct gb_operation *operation, int errno); -- cgit v1.2.3-59-g8ed1b From 583d233fa9d6fffd028af5219bcb5753bcb7b5ef Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 15:48:09 -0600 Subject: greybus: use little-endian in PWM requests The PWM config request defines two 32-bit values using u32. All over-the-wire values have to be in little-endian format. Fix this. Signed-off-by: Alex Elder Acked-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/pwm-gb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index bd1379f4400f..9678b6431760 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -62,8 +62,8 @@ struct gb_pwm_deactivate_request { struct gb_pwm_config_request { __u8 which; - __u32 duty; - __u32 period; + __le32 duty; + __le32 period; }; struct gb_pwm_polarity_request { @@ -152,8 +152,8 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, return -EINVAL; request.which = which; - request.duty = duty; - request.period = period; + request.duty = cpu_to_le32(duty); + request.period = cpu_to_le32(period); return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, &request, sizeof(request), NULL, 0); } -- cgit v1.2.3-59-g8ed1b From 6cd6ec55f4940bcddeed65e4eebb6190a64a4eb7 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 17:03:51 -0600 Subject: greybus: fix a bug in gb_operation_sync() The memcpy of request data into the request payload was copying the data into the wrong location. Fix that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 1409d31f48c0..d3ba65849f2b 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -935,7 +935,7 @@ int gb_operation_sync(struct gb_connection *connection, int type, return -ENOMEM; if (request_size) - memcpy(&operation->request->payload, request, request_size); + memcpy(operation->request->payload, request, request_size); /* Synchronous operation--no callback */ ret = gb_operation_request_send(operation, NULL); -- cgit v1.2.3-59-g8ed1b From c939c2f8fe85a8099d553ae5c12b9a24ef735d05 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 2 Dec 2014 17:25:11 -0600 Subject: greybus: define the invalid operation type symbolically Use a symbolic constant (rather than just "0") to represent an explicitly invalid operation type. The protocols have all reserved that value for that purpose--this just makes it explicit in the core code (since we now leverage its existence). Fix the code so it uses the new symbolic value. Define it in "operation.h" for all to see. Move the common definition of the GB_OPERATION_TYPE_RESPONSE flag mask there as well. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 27 ++++++++++++++++----------- drivers/staging/greybus/operation.h | 12 ++++++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d3ba65849f2b..b2ebbe42a45b 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -13,12 +13,6 @@ #include "greybus.h" -/* - * The top bit of the type in an operation message header indicates - * whether the message is a request (bit clear) or response (bit set) - */ -#define GB_OPERATION_TYPE_RESPONSE 0x80 - #define OPERATION_TIMEOUT_DEFAULT 1000 /* milliseconds */ /* @@ -329,7 +323,7 @@ static void gb_operation_message_init(struct greybus_host_device *hd, * 0x00. Such buffers will be overwritten by arriving data * so there's no need to initialize the message header. */ - if (type) { + if (type != GB_OPERATION_TYPE_INVALID) { /* * For a request, the operation id gets filled in * when the message is sent. For a response, it @@ -527,8 +521,17 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, { struct greybus_host_device *hd = connection->hd; struct gb_operation *operation; - gfp_t gfp_flags = type ? GFP_KERNEL : GFP_ATOMIC; + gfp_t gfp_flags; + /* + * An incoming request will pass an invalid operation type, + * because the header will get overwritten anyway. These + * occur in interrupt context, so we must use GFP_ATOMIC. + */ + if (type == GB_OPERATION_TYPE_INVALID) + gfp_flags = GFP_ATOMIC; + else + gfp_flags = GFP_KERNEL; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) return NULL; @@ -541,7 +544,7 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, operation->request->operation = operation; /* Allocate the response buffer for outgoing operations */ - if (type) + if (type != GB_OPERATION_TYPE_INVALID) if (!gb_operation_response_alloc(operation, response_size)) goto err_request; operation->errno = -EBADR; /* Initial value--means "never set" */ @@ -578,7 +581,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size) { - if (WARN_ON_ONCE(!type)) + if (WARN_ON_ONCE(type == GB_OPERATION_TYPE_INVALID)) return NULL; if (WARN_ON_ONCE(type & GB_OPERATION_TYPE_RESPONSE)) type &= ~GB_OPERATION_TYPE_RESPONSE; @@ -593,7 +596,9 @@ gb_operation_create_incoming(struct gb_connection *connection, u16 id, { struct gb_operation *operation; - operation = gb_operation_create_common(connection, 0, request_size, 0); + operation = gb_operation_create_common(connection, + GB_OPERATION_TYPE_INVALID, + request_size, 0); if (operation) { operation->id = id; memcpy(operation->request->header, data, request_size); diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index c60decb40539..40b2e7d68b87 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -13,6 +13,18 @@ struct gb_operation; +/* + * No protocol may define an operation that has numeric value 0x00. + * It is reserved as an explicitly invalid value. + */ +#define GB_OPERATION_TYPE_INVALID ((u8)0x00) + +/* + * The top bit of the type in an operation message header indicates + * whether the message is a request (bit clear) or response (bit set) + */ +#define GB_OPERATION_TYPE_RESPONSE ((u8)0x80) + enum gb_operation_result { GB_OP_SUCCESS = 0x00, GB_OP_INTERRUPTED = 0x01, -- cgit v1.2.3-59-g8ed1b From 0ba02c4d1675c426f38f3beb6b6cb1a3d9aeff97 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 08:35:06 -0600 Subject: greybus: don't use 0 as an operation id Stop allowing 0x0000 to be used as an operation id. That id will be reserved for use by operations that will return no response message. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b2ebbe42a45b..c0e206db9ed7 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -151,10 +151,11 @@ static void gb_pending_operation_insert(struct gb_operation *operation) /* * Assign the operation's id and move it into its - * connection's pending list. + * connection's pending list. Zero is a reserved operation + * id. */ spin_lock_irq(&gb_operations_lock); - operation->id = ++connection->op_cycle; + operation->id = ++connection->op_cycle % U16_MAX + 1; list_move_tail(&operation->links, &connection->pending); spin_unlock_irq(&gb_operations_lock); -- cgit v1.2.3-59-g8ed1b From afb2e1342e75b55be8834efc5b1c77f654572df8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 08:35:07 -0600 Subject: greybus: get rid of pending operations list A connection has two lists of operations, and an operation is always on one or the other of them. One of them contains the operations that are currently "in flight". We really don't expect to have very many in-flight operations on any given connection (in fact, at the moment it's always exactly one). So there's no significant performance benefit to keeping these in a separate list. An in-flight operation can also be distinguished by its errno field holding -EINPROGRESS. Get rid of the pending list, and search all operations rather than the pending list when looking up a response message's operation. Rename gb_pending_operation_find() accordingly. There's no longer any need to remove operations from the pending list, and the insertion function no longer has anything to do with a pending list. Just open code what was the insertion function (it now has only to do with assigning the operation id). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 1 - drivers/staging/greybus/connection.h | 1 - drivers/staging/greybus/operation.c | 52 +++++++++++------------------------- drivers/staging/greybus/operation.h | 2 +- 4 files changed, 17 insertions(+), 39 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 6503546ccc01..7fbfcdc22307 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -192,7 +192,6 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, spin_unlock_irq(&gb_connections_lock); INIT_LIST_HEAD(&connection->operations); - INIT_LIST_HEAD(&connection->pending); return connection; } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 8cde114f5ac2..035fced12edc 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -37,7 +37,6 @@ struct gb_connection { u16 op_cycle; struct list_head operations; - struct list_head pending; /* awaiting response */ void *private; }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c0e206db9ed7..6ed1d479b117 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -144,44 +144,14 @@ int gb_operation_result(struct gb_operation *operation) return result; } -static void gb_pending_operation_insert(struct gb_operation *operation) -{ - struct gb_connection *connection = operation->connection; - struct gb_operation_msg_hdr *header; - - /* - * Assign the operation's id and move it into its - * connection's pending list. Zero is a reserved operation - * id. - */ - spin_lock_irq(&gb_operations_lock); - operation->id = ++connection->op_cycle % U16_MAX + 1; - list_move_tail(&operation->links, &connection->pending); - spin_unlock_irq(&gb_operations_lock); - - /* Store the operation id in the request header */ - header = operation->request->header; - header->operation_id = cpu_to_le16(operation->id); -} - -static void gb_pending_operation_remove(struct gb_operation *operation) -{ - struct gb_connection *connection = operation->connection; - - /* Take us off of the list of pending operations */ - spin_lock_irq(&gb_operations_lock); - list_move_tail(&operation->links, &connection->operations); - spin_unlock_irq(&gb_operations_lock); -} - static struct gb_operation * -gb_pending_operation_find(struct gb_connection *connection, u16 operation_id) +gb_operation_find(struct gb_connection *connection, u16 operation_id) { struct gb_operation *operation; bool found = false; spin_lock_irq(&gb_operations_lock); - list_for_each_entry(operation, &connection->pending, links) + list_for_each_entry(operation, &connection->operations, links) if (operation->id == operation_id) { found = true; break; @@ -667,10 +637,12 @@ static void gb_operation_sync_callback(struct gb_operation *operation) int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback) { + struct gb_connection *connection = operation->connection; + struct gb_operation_msg_hdr *header; unsigned long timeout; int ret; - if (operation->connection->state != GB_CONNECTION_STATE_ENABLED) + if (connection->state != GB_CONNECTION_STATE_ENABLED) return -ENOTCONN; /* @@ -684,7 +656,16 @@ int gb_operation_request_send(struct gb_operation *operation, operation->callback = callback; else operation->callback = gb_operation_sync_callback; - gb_pending_operation_insert(operation); + + /* + * Assign the operation's id, and store it in the request header. + * Zero is a reserved operation id. + */ + spin_lock_irq(&gb_operations_lock); + operation->id = ++connection->op_cycle % U16_MAX + 1; + spin_unlock_irq(&gb_operations_lock); + header = operation->request->header; + header->operation_id = cpu_to_le16(operation->id); /* * We impose a time limit for requests to complete. We need @@ -826,14 +807,13 @@ static void gb_connection_recv_response(struct gb_connection *connection, struct gb_message *message; int errno = gb_operation_status_map(result); - operation = gb_pending_operation_find(connection, operation_id); + operation = gb_operation_find(connection, operation_id); if (!operation) { gb_connection_err(connection, "operation not found"); return; } cancel_delayed_work(&operation->timeout_work); - gb_pending_operation_remove(operation); message = operation->response; if (!errno && size != message->size) { diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 40b2e7d68b87..c73d9b92b5c5 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -92,7 +92,7 @@ struct gb_operation { struct delayed_work timeout_work; struct kref kref; - struct list_head links; /* connection->{operations,pending} */ + struct list_head links; /* connection->operations */ }; void gb_connection_recv(struct gb_connection *connection, -- cgit v1.2.3-59-g8ed1b From 4afb7fd0154753711e7bc68790f6f5de8dbed39e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 08:35:08 -0600 Subject: greybus: make op_cycle atomic (again) There's no need to protect updating a connections operation id cycle counter with the operations spinlock. That spinlock protects connection lists, which do not interact with the cycle counter. All that we require is that it gets updated atomically, and we can express that requirement in its type. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 1 + drivers/staging/greybus/connection.h | 2 +- drivers/staging/greybus/operation.c | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7fbfcdc22307..e59a7778c02a 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -191,6 +191,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, list_add_tail(&connection->interface_links, &interface->connections); spin_unlock_irq(&gb_connections_lock); + atomic_set(&connection->op_cycle, 0); INIT_LIST_HEAD(&connection->operations); return connection; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 035fced12edc..7568161e5dcb 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -35,7 +35,7 @@ struct gb_connection { enum gb_connection_state state; - u16 op_cycle; + atomic_t op_cycle; struct list_head operations; void *private; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 6ed1d479b117..15a6e3b24a7c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -640,6 +640,7 @@ int gb_operation_request_send(struct gb_operation *operation, struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; unsigned long timeout; + unsigned int cycle; int ret; if (connection->state != GB_CONNECTION_STATE_ENABLED) @@ -661,9 +662,8 @@ int gb_operation_request_send(struct gb_operation *operation, * Assign the operation's id, and store it in the request header. * Zero is a reserved operation id. */ - spin_lock_irq(&gb_operations_lock); - operation->id = ++connection->op_cycle % U16_MAX + 1; - spin_unlock_irq(&gb_operations_lock); + cycle = (unsigned int)atomic_inc_return(&connection->op_cycle); + operation->id = (u16)(cycle % U16_MAX + 1); header = operation->request->header; header->operation_id = cpu_to_le16(operation->id); -- cgit v1.2.3-59-g8ed1b From c25572ca943a1f8c2db7a8063c166d12480bb69d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 08:35:09 -0600 Subject: greybus: introduce gb_operation_request_send_sync() Define a new function used to initiate a synchronous operation. It sends the operation request message and doesn't return until the response has been received and/or the operation's result has been set. This gets rid of the convention that a null callback pointer signifies a synchronous operation. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c-gb.c | 2 +- drivers/staging/greybus/operation.c | 33 +++++++++++++++++++++++---------- drivers/staging/greybus/operation.h | 1 + drivers/staging/greybus/usb-gb.c | 2 +- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 19a7df91565a..bd1bada3b372 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -285,7 +285,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, return -ENOMEM; /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); + ret = gb_operation_request_send_sync(operation); if (ret) { if (ret != -EAGAIN) pr_err("transfer operation failed (%d)\n", ret); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 15a6e3b24a7c..c62f20072b3e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -641,7 +641,6 @@ int gb_operation_request_send(struct gb_operation *operation, struct gb_operation_msg_hdr *header; unsigned long timeout; unsigned int cycle; - int ret; if (connection->state != GB_CONNECTION_STATE_ENABLED) return -ENOTCONN; @@ -652,11 +651,12 @@ int gb_operation_request_send(struct gb_operation *operation, */ gb_operation_get(operation); - /* A null callback pointer means synchronous return */ - if (callback) - operation->callback = callback; - else - operation->callback = gb_operation_sync_callback; + /* + * Record the callback function, which is executed in + * non-atomic (workqueue) context when the final result + * of an operation has been set. + */ + operation->callback = callback; /* * Assign the operation's id, and store it in the request header. @@ -677,8 +677,22 @@ int gb_operation_request_send(struct gb_operation *operation, /* All set, send the request */ gb_operation_result_set(operation, -EINPROGRESS); - ret = gb_message_send(operation->request); - if (ret || callback) + + return gb_message_send(operation->request); +} + +/* + * Send a synchronous operation. This function is expected to + * block, returning only when the response has arrived, (or when an + * error is detected. The return value is the result of the + * operation. + */ +int gb_operation_request_send_sync(struct gb_operation *operation) +{ + int ret; + + ret = gb_operation_request_send(operation, gb_operation_sync_callback); + if (ret) return ret; /* Cancel the operation if interrupted */ @@ -923,8 +937,7 @@ int gb_operation_sync(struct gb_connection *connection, int type, if (request_size) memcpy(operation->request->payload, request, request_size); - /* Synchronous operation--no callback */ - ret = gb_operation_request_send(operation, NULL); + ret = gb_operation_request_send_sync(operation); if (ret) pr_err("version operation failed (%d)\n", ret); else diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index c73d9b92b5c5..3415e8b56eb6 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -115,6 +115,7 @@ bool gb_operation_response_alloc(struct gb_operation *operation, int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback); +int gb_operation_request_send_sync(struct gb_operation *operation); int gb_operation_response_send(struct gb_operation *operation, int errno); void gb_operation_cancel(struct gb_operation *operation, int errno); diff --git a/drivers/staging/greybus/usb-gb.c b/drivers/staging/greybus/usb-gb.c index ab4e093b62af..e5da72a60b89 100644 --- a/drivers/staging/greybus/usb-gb.c +++ b/drivers/staging/greybus/usb-gb.c @@ -169,7 +169,7 @@ static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) memcpy(&request->payload, urb->transfer_buffer, urb->transfer_buffer_length); - ret = gb_operation_request_send(operation, NULL); + ret = gb_operation_request_send_sync(operation); gb_operation_destroy(operation); return ret; -- cgit v1.2.3-59-g8ed1b From 62749a056ab48994956cf14abcd5622db4a4a0cb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 12:27:41 -0600 Subject: greybus: fix an error message The error message printed by gb_operation_sync() if the operation fails is wrong. Fix it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c62f20072b3e..109b94fc26b7 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -939,7 +939,7 @@ int gb_operation_sync(struct gb_connection *connection, int type, ret = gb_operation_request_send_sync(operation); if (ret) - pr_err("version operation failed (%d)\n", ret); + pr_err("synchronous operation failed (%d)\n", ret); else if (response_size) memcpy(response, operation->response->payload, -- cgit v1.2.3-59-g8ed1b From 93bbe859b75616ebdc33722c3b30cf36e74161a8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 12:27:42 -0600 Subject: greybus: set up connection->private properly The connection->private pointer should refer to a protocol-specific data structure. Change two protocol drivers (USB and vibrator) so they now set this. In addition, because the setup routine may need access to the data structure, the private pointer should be set early--as early as possible. Make the UART, i2c, and GPIO protocol drivers set the private pointer earlier. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 2 +- drivers/staging/greybus/i2c-gb.c | 3 +-- drivers/staging/greybus/pwm-gb.c | 2 +- drivers/staging/greybus/uart-gb.c | 3 +-- drivers/staging/greybus/usb-gb.c | 1 + drivers/staging/greybus/vibrator-gb.c | 1 + 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 09d77c1e6c07..c573ccec40aa 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -472,6 +472,7 @@ static int gb_gpio_connection_init(struct gb_connection *connection) if (!gb_gpio_controller) return -ENOMEM; gb_gpio_controller->connection = connection; + connection->private = gb_gpio_controller; ret = gb_gpio_controller_setup(gb_gpio_controller); if (ret) @@ -502,7 +503,6 @@ static int gb_gpio_connection_init(struct gb_connection *connection) pr_err("Failed to register GPIO\n"); return ret; } - connection->private = gb_gpio_controller; return 0; out_err: diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index bd1bada3b372..8936389f5e2e 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -377,6 +377,7 @@ static int gb_i2c_connection_init(struct gb_connection *connection) return -ENOMEM; gb_i2c_dev->connection = connection; /* refcount? */ + connection->private = gb_i2c_dev; ret = gb_i2c_device_setup(gb_i2c_dev); if (ret) @@ -399,8 +400,6 @@ static int gb_i2c_connection_init(struct gb_connection *connection) if (ret) goto out_err; - connection->private = gb_i2c_dev; - return 0; out_err: /* kref_put(gb_i2c_dev->connection) */ diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index 9678b6431760..c505f1d2d676 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -266,6 +266,7 @@ static int gb_pwm_connection_init(struct gb_connection *connection) if (!pwmc) return -ENOMEM; pwmc->connection = connection; + connection->private = pwmc; /* Check for compatible protocol version */ ret = gb_pwm_proto_version_operation(pwmc); @@ -290,7 +291,6 @@ static int gb_pwm_connection_init(struct gb_connection *connection) pr_err("Failed to register PWM\n"); return ret; } - connection->private = pwmc; return 0; out_err: diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 6432c64fbd60..fad8635a8335 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -637,6 +637,7 @@ static int gb_uart_connection_init(struct gb_connection *connection) if (!gb_tty) return -ENOMEM; gb_tty->connection = connection; + connection->private = gb_tty; /* Check for compatible protocol version */ retval = get_version(gb_tty); @@ -659,8 +660,6 @@ static int gb_uart_connection_init(struct gb_connection *connection) init_waitqueue_head(&gb_tty->wioctl); mutex_init(&gb_tty->mutex); - connection->private = gb_tty; - send_control(gb_tty, gb_tty->ctrlout); /* initialize the uart to be 9600n81 */ diff --git a/drivers/staging/greybus/usb-gb.c b/drivers/staging/greybus/usb-gb.c index e5da72a60b89..b3092ed1a191 100644 --- a/drivers/staging/greybus/usb-gb.c +++ b/drivers/staging/greybus/usb-gb.c @@ -341,6 +341,7 @@ static int gb_usb_connection_init(struct gb_connection *connection) return -ENOMEM; gb_usb_dev->connection = connection; + connection->private = gb_usb_dev; /* Check for compatible protocol version */ retval = get_version(gb_usb_dev); diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index d00301dce8f0..8970c69ea893 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -139,6 +139,7 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) return -ENOMEM; vib->connection = connection; + connection->private = vib; retval = get_version(vib); if (retval) -- cgit v1.2.3-59-g8ed1b From 7a9366aa1e8645c12c0050b417358089c216d14f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 12:27:43 -0600 Subject: greybus: don't let i2c code assume non-null payload pointer This is in preparation for an upcoming patch, which makes the payload pointer be NULL when a message has zero bytes of payload. It ensures a null payload pointer never gets dereferenced. To do this we pass the response structure to gb_i2c_transfer_response() rather than just its data, and if it's null, returning immediately. Rearrange the logic in gb_i2c_transfer_operation() a bit. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c-gb.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 8936389f5e2e..f63b60e50181 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -258,11 +258,15 @@ gb_i2c_transfer_request(struct gb_connection *connection, } static void gb_i2c_transfer_response(struct i2c_msg *msgs, u32 msg_count, - void *data) + struct gb_i2c_transfer_response *response) { struct i2c_msg *msg = msgs; + u8 *data; u32 i; + if (!response) + return; + data = response->data; for (i = 0; i < msg_count; i++) { if (msg->flags & I2C_M_RD) { memcpy(msg->buf, data, msg->len); @@ -276,7 +280,6 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, struct i2c_msg *msgs, u32 msg_count) { struct gb_connection *connection = gb_i2c_dev->connection; - struct gb_i2c_transfer_response *response; struct gb_operation *operation; int ret; @@ -284,15 +287,15 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, if (!operation) return -ENOMEM; - /* Synchronous operation--no callback */ ret = gb_operation_request_send_sync(operation); - if (ret) { - if (ret != -EAGAIN) - pr_err("transfer operation failed (%d)\n", ret); - } else { + if (!ret) { + struct gb_i2c_transfer_response *response; + response = operation->response->payload; - gb_i2c_transfer_response(msgs, msg_count, response->data); + gb_i2c_transfer_response(msgs, msg_count, response); ret = msg_count; + } else if (ret != -EAGAIN) { + pr_err("transfer operation failed (%d)\n", ret); } gb_operation_destroy(operation); -- cgit v1.2.3-59-g8ed1b From 7cfa699556731c0c7d93793c419eb83f37107de2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 12:27:44 -0600 Subject: greybus: only record message payload size An asynchronous operation will want to know how big the response message it receives is. Rather than require the sender to record that information, expose a new field "payload_size" available to the protocol code for this purpose. An operation message consists of a header and a payload. The size of the message can be derived from the size of the payload, so record only the payload size and not the size of the whole message. Reorder the fields in a message structure. Update the description of the message header structure. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 34 ++++++++++++++++++---------------- drivers/staging/greybus/operation.h | 13 +++++++++---- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 109b94fc26b7..6a1d3e663547 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -32,15 +32,13 @@ static DEFINE_MUTEX(gb_message_mutex); /* * All operation messages (both requests and responses) begin with - * a header that encodes the size of the data (header included). - * This header also contains a unique identifier, which is used to - * keep track of in-flight operations. The header contains an + * a header that encodes the size of the message (header included). + * This header also contains a unique identifier, that associates a + * response message with its operation. The header contains an * operation type field, whose interpretation is dependent on what - * type of protocol is used over the connection. - * - * The high bit (0x80) of the operation type field is used to - * indicate whether the message is a request (clear) or a response - * (set). + * type of protocol is used over the connection. The high bit + * (0x80) of the operation type field is used to indicate whether + * the message is a request (clear) or a response (set). * * Response messages include an additional status byte, which * communicates the result of the corresponding request. A zero @@ -163,6 +161,7 @@ gb_operation_find(struct gb_connection *connection, u16 operation_id) static int gb_message_send(struct gb_message *message) { + size_t message_size = sizeof(*message->header) + message->payload_size; struct gb_connection *connection = message->operation->connection; u16 dest_cport_id = connection->interface_cport_id; int ret = 0; @@ -172,7 +171,7 @@ static int gb_message_send(struct gb_message *message) cookie = connection->hd->driver->buffer_send(connection->hd, dest_cport_id, message->header, - message->size, + message_size, GFP_KERNEL); if (IS_ERR(cookie)) ret = PTR_ERR(cookie); @@ -276,18 +275,17 @@ gb_hd_message_find(struct greybus_host_device *hd, void *header) static void gb_operation_message_init(struct greybus_host_device *hd, struct gb_message *message, u16 operation_id, - size_t message_size, u8 type) + size_t payload_size, u8 type) { struct gb_operation_msg_hdr *header; u8 *buffer; - BUG_ON(message_size < sizeof(*header)); buffer = &message->buffer[0]; header = (struct gb_operation_msg_hdr *)(buffer + hd->buffer_headroom); message->header = header; message->payload = header + 1; - message->size = message_size; + message->payload_size = payload_size; /* * The type supplied for incoming message buffers will be @@ -295,6 +293,8 @@ static void gb_operation_message_init(struct greybus_host_device *hd, * so there's no need to initialize the message header. */ if (type != GB_OPERATION_TYPE_INVALID) { + u16 message_size = (u16)(sizeof(*header) + payload_size); + /* * For a request, the operation id gets filled in * when the message is sent. For a response, it @@ -359,14 +359,14 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, return NULL; /* Initialize the message. Operation id is filled in later. */ - gb_operation_message_init(hd, message, 0, message_size, type); + gb_operation_message_init(hd, message, 0, payload_size, type); return message; } static void gb_operation_message_free(struct gb_message *message) { - if (message->size > sizeof(message->header)) + if (message->payload_size) kfree(message); else kmem_cache_free(gb_simple_message_cache, message); @@ -820,6 +820,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, struct gb_operation *operation; struct gb_message *message; int errno = gb_operation_status_map(result); + size_t message_size; operation = gb_operation_find(connection, operation_id); if (!operation) { @@ -830,9 +831,10 @@ static void gb_connection_recv_response(struct gb_connection *connection, cancel_delayed_work(&operation->timeout_work); message = operation->response; - if (!errno && size != message->size) { + message_size = sizeof(*message->header) + message->payload_size; + if (!errno && size != message_size) { gb_connection_err(connection, "bad message size (%zu != %zu)", - size, message->size); + size, message_size); errno = -EMSGSIZE; } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 3415e8b56eb6..a79e88a3b314 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -38,13 +38,18 @@ enum gb_operation_result { GB_OP_MALFUNCTION = 0xff, }; +/* + * Protocol code should only examine the payload and payload_size + * fields. All other fields are intended to be private to the + * operations core code. + */ struct gb_message { - struct gb_operation_msg_hdr *header; - void *payload; - size_t size; /* header + payload */ struct gb_operation *operation; - void *cookie; + struct gb_operation_msg_hdr *header; + + void *payload; + size_t payload_size; u8 buffer[]; }; -- cgit v1.2.3-59-g8ed1b From 746e0ef95ade8dd6d8633679a87ab573b5e1f69e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 12:27:45 -0600 Subject: greybus: use null pointer for empty payload Currently message->payload always points to the address immediately following the header in a message. If the payload length is 0, this is not a valid pointer. Change the code to assign a null pointer to the payload in this case. I have verified that no code dereferences the payload pointer unless the payload is known to have non-zero size. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 6a1d3e663547..6197167a67d8 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -284,7 +284,7 @@ static void gb_operation_message_init(struct greybus_host_device *hd, header = (struct gb_operation_msg_hdr *)(buffer + hd->buffer_headroom); message->header = header; - message->payload = header + 1; + message->payload = payload_size ? header + 1 : NULL; message->payload_size = payload_size; /* -- cgit v1.2.3-59-g8ed1b From 82b5e3feb71482fe63f3c62d81a1528a890dfe74 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 3 Dec 2014 12:27:46 -0600 Subject: greybus: record type in operation structure I've gone back and forth on this, but now that I'm looking at asynchronous operations I know that the asynchronous callback will want to know what type of operation it is handling, and right now that's only available in the message header. So record an operation's type in the operation structure, and use it in a few spots where the header type was being used previously. Pass the type to gb_operation_create_incoming() so it can fill it in after the operation has been created. Clean up the crap comments above the definition of the operation structure. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 32 +++++++++++++++++-------------- drivers/staging/greybus/operation.h | 38 +++++++++++++++---------------------- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 6197167a67d8..046ed2a99f45 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -13,6 +13,7 @@ #include "greybus.h" +/* The default amount of time a request is given to complete */ #define OPERATION_TIMEOUT_DEFAULT 1000 /* milliseconds */ /* @@ -59,7 +60,10 @@ struct gb_operation_msg_hdr { /* 2 bytes pad, must be zero (ignore when read) */ } __aligned(sizeof(u64)); -/* XXX Could be per-host device, per-module, or even per-connection */ +/* + * Protects access to connection operations lists, as well as + * updates to operation->errno. + */ static DEFINE_SPINLOCK(gb_operations_lock); /* @@ -201,21 +205,18 @@ static void gb_message_cancel(struct gb_message *message) static void gb_operation_request_handle(struct gb_operation *operation) { struct gb_protocol *protocol = operation->connection->protocol; - struct gb_operation_msg_hdr *header; - - header = operation->request->header; /* * If the protocol has no incoming request handler, report * an error and mark the request bad. */ if (protocol->request_recv) { - protocol->request_recv(header->type, operation); + protocol->request_recv(operation->type, operation); return; } gb_connection_err(operation->connection, - "unexpected incoming request type 0x%02hhx\n", header->type); + "unexpected incoming request type 0x%02hhx\n", operation->type); if (gb_operation_result_set(operation, -EPROTONOSUPPORT)) queue_work(gb_operation_workqueue, &operation->work); else @@ -444,8 +445,7 @@ bool gb_operation_response_alloc(struct gb_operation *operation, struct gb_message *response; u8 type; - request_header = operation->request->header; - type = request_header->type | GB_OPERATION_TYPE_RESPONSE; + type = operation->type | GB_OPERATION_TYPE_RESPONSE; response = gb_operation_message_alloc(hd, type, response_size, GFP_KERNEL); if (!response) @@ -458,6 +458,7 @@ bool gb_operation_response_alloc(struct gb_operation *operation, * that's left is the operation id, which we copy from the * request message header (as-is, in little-endian order). */ + request_header = operation->request->header; response->header->operation_id = request_header->operation_id; operation->response = response; @@ -515,9 +516,11 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, operation->request->operation = operation; /* Allocate the response buffer for outgoing operations */ - if (type != GB_OPERATION_TYPE_INVALID) + if (type != GB_OPERATION_TYPE_INVALID) { if (!gb_operation_response_alloc(operation, response_size)) goto err_request; + operation->type = type; + } operation->errno = -EBADR; /* Initial value--means "never set" */ INIT_WORK(&operation->work, gb_operation_work); @@ -563,7 +566,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, static struct gb_operation * gb_operation_create_incoming(struct gb_connection *connection, u16 id, - void *data, size_t request_size) + u8 type, void *data, size_t request_size) { struct gb_operation *operation; @@ -572,6 +575,7 @@ gb_operation_create_incoming(struct gb_connection *connection, u16 id, request_size, 0); if (operation) { operation->id = id; + operation->type = type; memcpy(operation->request->header, data, request_size); } @@ -779,13 +783,13 @@ EXPORT_SYMBOL_GPL(greybus_data_sent); * data into the request buffer and handle the rest via workqueue. */ static void gb_connection_recv_request(struct gb_connection *connection, - u16 operation_id, void *data, - size_t size) + u16 operation_id, u8 type, + void *data, size_t size) { struct gb_operation *operation; operation = gb_operation_create_incoming(connection, operation_id, - data, size); + type, data, size); if (!operation) { gb_connection_err(connection, "can't create operation"); return; /* XXX Respond with pre-allocated ENOMEM */ @@ -884,7 +888,7 @@ void gb_connection_recv(struct gb_connection *connection, header->result, data, msg_size); else gb_connection_recv_request(connection, operation_id, - data, msg_size); + header->type, data, msg_size); } /* diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index a79e88a3b314..a173aa9feb17 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -56,39 +56,31 @@ struct gb_message { /* * A Greybus operation is a remote procedure call performed over a - * connection between the AP and a function on Greybus module. - * Every operation consists of a request message sent to the other - * end of the connection coupled with a reply returned to the - * sender. - * - * The state for managing active requests on a connection is held in - * the connection structure. + * connection between two UniPro interfaces. * - * YADA YADA + * Every operation consists of a request message sent to the other + * end of the connection coupled with a reply message returned to + * the sender. Every operation has a type, whose interpretation is + * dependent on the protocol associated with the connection. * - * submitting each request and providing its matching response to - * the caller when it arrives. Operations normally complete - * asynchronously, and when an operation's response arrives its - * callback function is executed. The callback pointer is supplied - * at the time the operation is submitted; a null callback pointer - * causes synchronous operation--the caller is blocked until - * the response arrives. In addition, it is possible to await - * the completion of a submitted asynchronous operation. + * Only four things in an operation structure are intended to be + * directly usable by protocol handlers: the operation's connection + * pointer; the operation type; the request message payload (and + * size); and the response message payload (and size). Note that a + * message with a 0-byte payload has a null message payload pointer. * - * A Greybus device operation includes a Greybus buffer to hold the - * data sent to the device. The only field within a Greybus - * operation that should be used by a caller is the payload pointer, - * which should be used to populate the request data. This pointer - * is guaranteed to be 64-bit aligned. - * XXX and callback? + * In addition, every operation has a result, which is an errno + * value. Protocol handlers access the operation result using + * gb_operation_result(). */ typedef void (*gb_operation_callback)(struct gb_operation *); struct gb_operation { struct gb_connection *connection; struct gb_message *request; struct gb_message *response; - u16 id; + u8 type; + u16 id; int errno; /* Operation result */ struct work_struct work; -- cgit v1.2.3-59-g8ed1b From 6b7dff889c129a310136ce1aad334e4abec13743 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 8 Dec 2014 17:45:10 -0500 Subject: greybus: battery-gb.c: add new functions from Greybus spec document. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index d28be04f5c68..f469a8d48e91 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -35,9 +35,12 @@ struct gb_battery { #define GB_BATTERY_TYPE_TECHNOLOGY 0x02 #define GB_BATTERY_TYPE_STATUS 0x03 #define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 -#define GB_BATTERY_TYPE_CAPACITY 0x05 +#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05 #define GB_BATTERY_TYPE_TEMPERATURE 0x06 #define GB_BATTERY_TYPE_VOLTAGE 0x07 +#define GB_BATTERY_TYPE_CURRENT 0x08 +#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX +#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX struct gb_battery_proto_version_response { __u8 major; @@ -211,13 +214,14 @@ static int get_max_voltage(struct gb_battery *gb) return max_voltage; } -static int get_capacity(struct gb_battery *gb) +static int get_percent_capacity(struct gb_battery *gb) { struct gb_battery_capacity_response capacity_response; u32 capacity; int retval; - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_CAPACITY, + retval = gb_operation_sync(gb->connection, + GB_BATTERY_TYPE_PERCENT_CAPACITY, NULL, 0, &capacity_response, sizeof(capacity_response)); if (retval) @@ -279,7 +283,7 @@ static int get_property(struct power_supply *b, break; case POWER_SUPPLY_PROP_CAPACITY: - val->intval = get_capacity(gb); + val->intval = get_percent_capacity(gb); break; case POWER_SUPPLY_PROP_TEMP: -- cgit v1.2.3-59-g8ed1b From 5f474d49436134e0f36d7bc3fb10babebc5e824f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 9 Dec 2014 14:57:04 -0500 Subject: greybus: greybus_manifest.h: add FIXME for version The version field is going to go away, but after the demo, not before. Note that in the header file. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index e2ec55888914..f44f3376f4da 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -71,7 +71,7 @@ enum greybus_class_type { struct greybus_descriptor_module { __le16 vendor; __le16 product; - __le16 version; + __le16 version; // TODO - remove after Dec demo. __u8 vendor_stringid; __u8 product_stringid; __le64 unique_id; -- cgit v1.2.3-59-g8ed1b From aa26351d0bb5b3ecf9bccacc9b05209b3d2af8ca Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 10 Dec 2014 08:43:33 -0600 Subject: greybus: define GB_OP_NONEXISTENT The i2c protocol needs a way to indicate an i2c device doesn't exist (which is not necessarily an error). Define GB_OP_NONEXISTENT to indicate this, and updating the status<->errno mapping functions accordingly. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 4 ++++ drivers/staging/greybus/operation.h | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 046ed2a99f45..742eccc4b989 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -396,6 +396,8 @@ static int gb_operation_status_map(u8 status) return -EINVAL; case GB_OP_RETRY: return -EAGAIN; + case GB_OP_NONEXISTENT: + return -ENODEV; case GB_OP_MALFUNCTION: return -EILSEQ; case GB_OP_UNKNOWN_ERROR: @@ -431,6 +433,8 @@ static u8 gb_operation_errno_map(int errno) return GB_OP_RETRY; case -EILSEQ: return GB_OP_MALFUNCTION; + case -ENODEV: + return GB_OP_NONEXISTENT; case -EIO: default: return GB_OP_UNKNOWN_ERROR; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index a173aa9feb17..1ade52bb1168 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -34,6 +34,7 @@ enum gb_operation_result { GB_OP_OVERFLOW = 0x05, GB_OP_INVALID = 0x06, GB_OP_RETRY = 0x07, + GB_OP_NONEXISTENT = 0x08, GB_OP_UNKNOWN_ERROR = 0xfe, GB_OP_MALFUNCTION = 0xff, }; -- cgit v1.2.3-59-g8ed1b From 7de3e650fb736bf7c40e20f5dc866c544376119c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 10 Dec 2014 14:50:48 -0600 Subject: greybus: ENODEV can be an expected error too When probing for i2c devices, a read transfer operation can be used. In this case, it is expected that some devices will not be found, so ENODEV is an expected failure. Don't issue a warning if the return value is -ENODEV. Note: I anticipate we might have to be more precise in identifying this specific case, but for now this eliminates a bogus warning when probing i2c devices. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c-gb.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index f63b60e50181..4ef6ea1230a5 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -276,6 +276,14 @@ static void gb_i2c_transfer_response(struct i2c_msg *msgs, u32 msg_count, } } +/* + * Some i2c transfer operations return results that are expected. + */ +static bool gb_i2c_expected_transfer_error(int errno) +{ + return errno == -EAGAIN || errno == -ENODEV; +} + static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, struct i2c_msg *msgs, u32 msg_count) { @@ -294,7 +302,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, response = operation->response->payload; gb_i2c_transfer_response(msgs, msg_count, response); ret = msg_count; - } else if (ret != -EAGAIN) { + } else if (!gb_i2c_expected_transfer_error(ret)) { pr_err("transfer operation failed (%d)\n", ret); } gb_operation_destroy(operation); -- cgit v1.2.3-59-g8ed1b From 0a9c4d70d372a85252c00a94cd45622b657397be Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 11 Dec 2014 16:48:38 -0600 Subject: greybus: switch cport id used for sends In talking with Perry today I learned that the CPort id expected to supplied over the HSIC interface to the APB is different from the way I understood it. My understanding was that the CPort id to supply always specified the CPort id on the other end of a connection. However, Perry says the mapping between local CPort id and remote CPort id (and device id) is done by the host UniPro interface. So whether sending or receiving data, the CPort id that the Greybus code should supply to the AP Bridge is the one representing the AP side of a connection. This patch fixes this. The receive side already used that CPort id; it's only the sending code that needed to be changed. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1-ap-usb.c | 15 +++++++-------- drivers/staging/greybus/operation.c | 3 +-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index f551907ced2d..277699758973 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -190,7 +190,7 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) * error otherwise. If the caller wishes to cancel the in-flight * buffer, it must supply the returned cookie to the cancel routine. */ -static void *buffer_send(struct greybus_host_device *hd, u16 dest_cport_id, +static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, void *buffer, size_t buffer_size, gfp_t gfp_mask) { struct es1_ap_dev *es1 = hd_to_es1(hd); @@ -216,17 +216,16 @@ static void *buffer_send(struct greybus_host_device *hd, u16 dest_cport_id, * of where the data should be sent. Do one last check of * the target CPort id before filling it in. */ - if (dest_cport_id == CPORT_ID_BAD) { + if (cport_id == CPORT_ID_BAD) { pr_err("request to send inbound data buffer\n"); return ERR_PTR(-EINVAL); } - if (dest_cport_id > (u16)U8_MAX) { - pr_err("dest_cport_id (%hd) is out of range for ES1\n", - dest_cport_id); + if (cport_id > (u16)U8_MAX) { + pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); return ERR_PTR(-EINVAL); } /* OK, the destination is fine; record it in the transfer buffer */ - *transfer_buffer = dest_cport_id; + *transfer_buffer = cport_id; /* Find a free urb */ urb = next_free_urb(es1, gfp_mask); @@ -393,8 +392,8 @@ static void cport_in_callback(struct urb *urb) } /* - * The CPort number is the first byte of the data stream, the rest of - * the stream is "real" data + * Our CPort number is the first byte of the data stream, + * the rest of the stream is "real" data */ data = urb->transfer_buffer; cport_id = (u16)data[0]; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 742eccc4b989..6c815d29502c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -167,13 +167,12 @@ static int gb_message_send(struct gb_message *message) { size_t message_size = sizeof(*message->header) + message->payload_size; struct gb_connection *connection = message->operation->connection; - u16 dest_cport_id = connection->interface_cport_id; int ret = 0; void *cookie; mutex_lock(&gb_message_mutex); cookie = connection->hd->driver->buffer_send(connection->hd, - dest_cport_id, + connection->hd_cport_id, message->header, message_size, GFP_KERNEL); -- cgit v1.2.3-59-g8ed1b From 3763f960a497fed19666dfd3366e28d6bc12b2c2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:10:52 -0500 Subject: greybus: uart-gb.c: don't include module.h No need to specifically include the greybus module.h here, greybus.h already does so and we will be renaming it soon. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart-gb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index fad8635a8335..8599da2b7956 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -28,7 +28,6 @@ #include #include "greybus.h" -#include "module.h" #define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ #define GB_NAME "ttyGB" -- cgit v1.2.3-59-g8ed1b From 708c126695611f2028aaa152398c75a03aeb1344 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:10:53 -0500 Subject: greybus: module versions: remove them We removed the module version from the spec, so remove them from the code as well. It's still in the manifest as we need to sync with gbsim / firmware when we do that, which will happen sometime in the next weeks. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 1 - drivers/staging/greybus/module.h | 1 - drivers/staging/greybus/sysfs.c | 2 -- 3 files changed, 4 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 28abd2ad395e..57cd5944b2b6 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -273,7 +273,6 @@ static bool gb_manifest_parse_module(struct gb_module *gmod, gmod->vendor = le16_to_cpu(desc_module->vendor); gmod->product = le16_to_cpu(desc_module->product); - gmod->version = le16_to_cpu(desc_module->version); gmod->unique_id = le64_to_cpu(desc_module->unique_id); /* Release the module descriptor, now that we're done with it */ diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 9e5358bae00a..facc8ba0fa40 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -23,7 +23,6 @@ struct gb_module { /* Information taken from the manifest module descriptor */ u16 vendor; u16 product; - u16 version; char *vendor_string; char *product_string; u64 unique_id; diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 44b0c707a5c4..f9d56e188ec4 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -32,7 +32,6 @@ static DEVICE_ATTR_RO(module_##field) gb_module_attr(vendor, x); gb_module_attr(product, x); -gb_module_attr(version, x); gb_module_attr(unique_id, llX); gb_module_attr(vendor_string, s); gb_module_attr(product_string, s); @@ -40,7 +39,6 @@ gb_module_attr(product_string, s); static struct attribute *module_attrs[] = { &dev_attr_module_vendor.attr, &dev_attr_module_product.attr, - &dev_attr_module_version.attr, &dev_attr_module_unique_id.attr, &dev_attr_module_vendor_string.attr, &dev_attr_module_product_string.attr, -- cgit v1.2.3-59-g8ed1b From 1cd9ba1477f25e9137403162092452f176dd30b4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:10:54 -0500 Subject: greybus: module: remove obsolete gb_tty pointer We aren't using this anymore, so remove gb_tty from struct gb_module. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 2 -- drivers/staging/greybus/module.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index f72e6aef4dc4..c424a5ac25e7 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -132,8 +132,6 @@ void gb_module_destroy(struct gb_module *gmod) list_del(&gmod->links); spin_unlock_irq(&gb_modules_lock); - /* XXX Do something with gmod->gb_tty */ - gb_interface_destroy(gmod); kfree(gmod->product_string); diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index facc8ba0fa40..2fdca57398d2 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -28,8 +28,6 @@ struct gb_module { u64 unique_id; struct greybus_host_device *hd; - - struct gb_tty *gb_tty; }; #define to_gb_module(d) container_of(d, struct gb_module, dev) -- cgit v1.2.3-59-g8ed1b From e50522209a2e86ffac2d8c9a99c0979914cff5dd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:10:55 -0500 Subject: greybus: interface_block: rename module.[c|h] to interface_block.[c|h] "modules" in the driver model here, are really "interface blocks" as that is what they are physically tied to. So rename the files before we start changing the code to make it obvious what is going on. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/interface_block.c | 201 ++++++++++++++++++++++++++++++ drivers/staging/greybus/interface_block.h | 55 ++++++++ drivers/staging/greybus/module.c | 201 ------------------------------ drivers/staging/greybus/module.h | 55 -------- 6 files changed, 258 insertions(+), 258 deletions(-) create mode 100644 drivers/staging/greybus/interface_block.c create mode 100644 drivers/staging/greybus/interface_block.h delete mode 100644 drivers/staging/greybus/module.c delete mode 100644 drivers/staging/greybus/module.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 4fa9b3f29e57..41186b7bb2e4 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -3,7 +3,7 @@ greybus-y := core.o \ debugfs.o \ ap.o \ manifest.o \ - module.o \ + interface_block.o \ interface.o \ connection.o \ protocol.o \ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 11f4e5529d9a..101b71168491 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -23,7 +23,7 @@ #include "greybus_id.h" #include "greybus_manifest.h" #include "manifest.h" -#include "module.h" +#include "interface_block.h" #include "interface.h" #include "connection.h" #include "protocol.h" diff --git a/drivers/staging/greybus/interface_block.c b/drivers/staging/greybus/interface_block.c new file mode 100644 index 000000000000..c424a5ac25e7 --- /dev/null +++ b/drivers/staging/greybus/interface_block.c @@ -0,0 +1,201 @@ +/* + * Greybus modules + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +/* XXX This could be per-host device */ +static DEFINE_SPINLOCK(gb_modules_lock); + +static int gb_module_match_one_id(struct gb_module *gmod, + const struct greybus_module_id *id) +{ + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && + (id->vendor != gmod->vendor)) + return 0; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && + (id->product != gmod->product)) + return 0; + + if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && + (id->unique_id != gmod->unique_id)) + return 0; + + return 1; +} + +const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, + const struct greybus_module_id *id) +{ + if (id == NULL) + return NULL; + + for (; id->vendor || id->product || id->unique_id || + id->driver_info; id++) { + if (gb_module_match_one_id(gmod, id)) + return id; + } + + return NULL; +} + +struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) +{ + struct gb_module *module; + + list_for_each_entry(module, &hd->modules, links) + if (module->module_id == module_id) + return module; + + return NULL; +} + +static void greybus_module_release(struct device *dev) +{ + struct gb_module *gmod = to_gb_module(dev); + + kfree(gmod); +} + +struct device_type greybus_module_type = { + .name = "greybus_module", + .release = greybus_module_release, +}; + +/* + * A Greybus module represents a user-replicable component on an Ara + * phone. + * + * Create a gb_module structure to represent a discovered module. + * The position within the Endo is encoded in the "module_id" argument. + * Returns a pointer to the new module or a null pointer if a + * failure occurs due to memory exhaustion. + */ +struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) +{ + struct gb_module *gmod; + int retval; + + gmod = gb_module_find(hd, module_id); + if (gmod) { + dev_err(hd->parent, "Duplicate module id %d will not be created\n", + module_id); + return NULL; + } + + gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); + if (!gmod) + return NULL; + + gmod->hd = hd; /* XXX refcount? */ + gmod->module_id = module_id; + INIT_LIST_HEAD(&gmod->interfaces); + + gmod->dev.parent = hd->parent; + gmod->dev.bus = &greybus_bus_type; + gmod->dev.type = &greybus_module_type; + gmod->dev.groups = greybus_module_groups; + gmod->dev.dma_mask = hd->parent->dma_mask; + device_initialize(&gmod->dev); + dev_set_name(&gmod->dev, "%d", module_id); + + retval = device_add(&gmod->dev); + if (retval) { + pr_err("failed to add module device for id 0x%02hhx\n", + module_id); + put_device(&gmod->dev); + kfree(gmod); + return NULL; + } + + spin_lock_irq(&gb_modules_lock); + list_add_tail(&gmod->links, &hd->modules); + spin_unlock_irq(&gb_modules_lock); + + return gmod; +} + +/* + * Tear down a previously set up module. + */ +void gb_module_destroy(struct gb_module *gmod) +{ + if (WARN_ON(!gmod)) + return; + + spin_lock_irq(&gb_modules_lock); + list_del(&gmod->links); + spin_unlock_irq(&gb_modules_lock); + + gb_interface_destroy(gmod); + + kfree(gmod->product_string); + kfree(gmod->vendor_string); + /* kref_put(module->hd); */ + + device_del(&gmod->dev); +} + +/** + * gb_add_module + * + * Pass in a buffer that _should_ contain a Greybus module manifest + * and register a greybus device structure with the kernel core. + */ +void gb_add_module(struct greybus_host_device *hd, u8 module_id, + u8 *data, int size) +{ + struct gb_module *gmod; + + gmod = gb_module_create(hd, module_id); + if (!gmod) { + dev_err(hd->parent, "failed to create module\n"); + return; + } + + /* + * Parse the manifest and build up our data structures + * representing what's in it. + */ + if (!gb_manifest_parse(gmod, data, size)) { + dev_err(hd->parent, "manifest error\n"); + goto err_module; + } + + /* + * XXX + * We've successfully parsed the manifest. Now we need to + * allocate CPort Id's for connecting to the CPorts found on + * other modules. For each of these, establish a connection + * between the local and remote CPorts (including + * configuring the switch to allow them to communicate). + */ + + return; + +err_module: + gb_module_destroy(gmod); +} + +void gb_remove_module(struct greybus_host_device *hd, u8 module_id) +{ + struct gb_module *gmod = gb_module_find(hd, module_id); + + if (gmod) + gb_module_destroy(gmod); + else + dev_err(hd->parent, "module id %d not found\n", module_id); +} + +void gb_remove_modules(struct greybus_host_device *hd) +{ + struct gb_module *gmod, *temp; + + list_for_each_entry_safe(gmod, temp, &hd->modules, links) + gb_module_destroy(gmod); +} diff --git a/drivers/staging/greybus/interface_block.h b/drivers/staging/greybus/interface_block.h new file mode 100644 index 000000000000..2fdca57398d2 --- /dev/null +++ b/drivers/staging/greybus/interface_block.h @@ -0,0 +1,55 @@ +/* + * Greybus modules + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __MODULE_H +#define __MODULE_H + +/* Increase these values if needed */ +#define MAX_CPORTS_PER_MODULE 10 +#define MAX_STRINGS_PER_MODULE 10 + +struct gb_module { + struct device dev; + + struct list_head interfaces; + struct list_head links; /* greybus_host_device->modules */ + u8 module_id; /* Physical location within the Endo */ + + /* Information taken from the manifest module descriptor */ + u16 vendor; + u16 product; + char *vendor_string; + char *product_string; + u64 unique_id; + + struct greybus_host_device *hd; +}; +#define to_gb_module(d) container_of(d, struct gb_module, dev) + +static inline void +gb_module_set_drvdata(struct gb_module *gmod, void *data) +{ + dev_set_drvdata(&gmod->dev, data); +} + +static inline void *gb_module_get_drvdata(struct gb_module *gmod) +{ + return dev_get_drvdata(&gmod->dev); +} + +const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, + const struct greybus_module_id *id); + +struct gb_module *gb_module_create(struct greybus_host_device *hd, + u8 module_id); +void gb_module_destroy(struct gb_module *module); + +struct gb_module *gb_module_find(struct greybus_host_device *hd, + u8 module_id); + +#endif /* __MODULE_H */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c deleted file mode 100644 index c424a5ac25e7..000000000000 --- a/drivers/staging/greybus/module.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Greybus modules - * - * Copyright 2014 Google Inc. - * - * Released under the GPLv2 only. - */ - -#include "greybus.h" - -/* XXX This could be per-host device */ -static DEFINE_SPINLOCK(gb_modules_lock); - -static int gb_module_match_one_id(struct gb_module *gmod, - const struct greybus_module_id *id) -{ - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && - (id->vendor != gmod->vendor)) - return 0; - - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && - (id->product != gmod->product)) - return 0; - - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && - (id->unique_id != gmod->unique_id)) - return 0; - - return 1; -} - -const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, - const struct greybus_module_id *id) -{ - if (id == NULL) - return NULL; - - for (; id->vendor || id->product || id->unique_id || - id->driver_info; id++) { - if (gb_module_match_one_id(gmod, id)) - return id; - } - - return NULL; -} - -struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) -{ - struct gb_module *module; - - list_for_each_entry(module, &hd->modules, links) - if (module->module_id == module_id) - return module; - - return NULL; -} - -static void greybus_module_release(struct device *dev) -{ - struct gb_module *gmod = to_gb_module(dev); - - kfree(gmod); -} - -struct device_type greybus_module_type = { - .name = "greybus_module", - .release = greybus_module_release, -}; - -/* - * A Greybus module represents a user-replicable component on an Ara - * phone. - * - * Create a gb_module structure to represent a discovered module. - * The position within the Endo is encoded in the "module_id" argument. - * Returns a pointer to the new module or a null pointer if a - * failure occurs due to memory exhaustion. - */ -struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) -{ - struct gb_module *gmod; - int retval; - - gmod = gb_module_find(hd, module_id); - if (gmod) { - dev_err(hd->parent, "Duplicate module id %d will not be created\n", - module_id); - return NULL; - } - - gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); - if (!gmod) - return NULL; - - gmod->hd = hd; /* XXX refcount? */ - gmod->module_id = module_id; - INIT_LIST_HEAD(&gmod->interfaces); - - gmod->dev.parent = hd->parent; - gmod->dev.bus = &greybus_bus_type; - gmod->dev.type = &greybus_module_type; - gmod->dev.groups = greybus_module_groups; - gmod->dev.dma_mask = hd->parent->dma_mask; - device_initialize(&gmod->dev); - dev_set_name(&gmod->dev, "%d", module_id); - - retval = device_add(&gmod->dev); - if (retval) { - pr_err("failed to add module device for id 0x%02hhx\n", - module_id); - put_device(&gmod->dev); - kfree(gmod); - return NULL; - } - - spin_lock_irq(&gb_modules_lock); - list_add_tail(&gmod->links, &hd->modules); - spin_unlock_irq(&gb_modules_lock); - - return gmod; -} - -/* - * Tear down a previously set up module. - */ -void gb_module_destroy(struct gb_module *gmod) -{ - if (WARN_ON(!gmod)) - return; - - spin_lock_irq(&gb_modules_lock); - list_del(&gmod->links); - spin_unlock_irq(&gb_modules_lock); - - gb_interface_destroy(gmod); - - kfree(gmod->product_string); - kfree(gmod->vendor_string); - /* kref_put(module->hd); */ - - device_del(&gmod->dev); -} - -/** - * gb_add_module - * - * Pass in a buffer that _should_ contain a Greybus module manifest - * and register a greybus device structure with the kernel core. - */ -void gb_add_module(struct greybus_host_device *hd, u8 module_id, - u8 *data, int size) -{ - struct gb_module *gmod; - - gmod = gb_module_create(hd, module_id); - if (!gmod) { - dev_err(hd->parent, "failed to create module\n"); - return; - } - - /* - * Parse the manifest and build up our data structures - * representing what's in it. - */ - if (!gb_manifest_parse(gmod, data, size)) { - dev_err(hd->parent, "manifest error\n"); - goto err_module; - } - - /* - * XXX - * We've successfully parsed the manifest. Now we need to - * allocate CPort Id's for connecting to the CPorts found on - * other modules. For each of these, establish a connection - * between the local and remote CPorts (including - * configuring the switch to allow them to communicate). - */ - - return; - -err_module: - gb_module_destroy(gmod); -} - -void gb_remove_module(struct greybus_host_device *hd, u8 module_id) -{ - struct gb_module *gmod = gb_module_find(hd, module_id); - - if (gmod) - gb_module_destroy(gmod); - else - dev_err(hd->parent, "module id %d not found\n", module_id); -} - -void gb_remove_modules(struct greybus_host_device *hd) -{ - struct gb_module *gmod, *temp; - - list_for_each_entry_safe(gmod, temp, &hd->modules, links) - gb_module_destroy(gmod); -} diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h deleted file mode 100644 index 2fdca57398d2..000000000000 --- a/drivers/staging/greybus/module.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Greybus modules - * - * Copyright 2014 Google Inc. - * - * Released under the GPLv2 only. - */ - -#ifndef __MODULE_H -#define __MODULE_H - -/* Increase these values if needed */ -#define MAX_CPORTS_PER_MODULE 10 -#define MAX_STRINGS_PER_MODULE 10 - -struct gb_module { - struct device dev; - - struct list_head interfaces; - struct list_head links; /* greybus_host_device->modules */ - u8 module_id; /* Physical location within the Endo */ - - /* Information taken from the manifest module descriptor */ - u16 vendor; - u16 product; - char *vendor_string; - char *product_string; - u64 unique_id; - - struct greybus_host_device *hd; -}; -#define to_gb_module(d) container_of(d, struct gb_module, dev) - -static inline void -gb_module_set_drvdata(struct gb_module *gmod, void *data) -{ - dev_set_drvdata(&gmod->dev, data); -} - -static inline void *gb_module_get_drvdata(struct gb_module *gmod) -{ - return dev_get_drvdata(&gmod->dev); -} - -const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, - const struct greybus_module_id *id); - -struct gb_module *gb_module_create(struct greybus_host_device *hd, - u8 module_id); -void gb_module_destroy(struct gb_module *module); - -struct gb_module *gb_module_find(struct greybus_host_device *hd, - u8 module_id); - -#endif /* __MODULE_H */ -- cgit v1.2.3-59-g8ed1b From 4ec7b07915523aa19ec31ec7e07cb8d903d39526 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:10:56 -0500 Subject: greybus: interface_block: s/gb_module/gb_interface_block/ Rename struct gb_module to struct gb_interface_block It's a complex rename, some functions got their name changed where needed, but primarily this change is focused on the structure and where it is used. Future changes will clean up the remaining usages of the term "module" in individual changes, this one spanned the whole subsystem so do it all at once. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 10 +-- drivers/staging/greybus/battery-gb.c | 2 +- drivers/staging/greybus/connection.c | 4 +- drivers/staging/greybus/core.c | 24 +++--- drivers/staging/greybus/greybus.h | 14 ++-- drivers/staging/greybus/interface.c | 32 ++++---- drivers/staging/greybus/interface.h | 10 +-- drivers/staging/greybus/interface_block.c | 122 +++++++++++++++--------------- drivers/staging/greybus/interface_block.h | 35 ++++----- drivers/staging/greybus/manifest.c | 34 ++++----- drivers/staging/greybus/manifest.h | 4 +- drivers/staging/greybus/sysfs.c | 4 +- 12 files changed, 149 insertions(+), 146 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 35367174afb4..89debab75621 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -123,7 +123,7 @@ static void svc_handshake(struct svc_function_handshake *handshake, static void svc_management(struct svc_function_unipro_management *management, int payload_length, struct greybus_host_device *hd) { - struct gb_module *module; + struct gb_interface_block *gb_ib; int ret; if (payload_length != sizeof(*management)) { @@ -138,18 +138,18 @@ static void svc_management(struct svc_function_unipro_management *management, hd->device_id = management->ap_id.device_id; break; case SVC_MANAGEMENT_LINK_UP: - module = gb_module_find(hd, management->link_up.module_id); - if (!module) { + gb_ib = gb_ib_find(hd, management->link_up.module_id); + if (!gb_ib) { dev_err(hd->parent, "Module ID %d not found\n", management->link_up.module_id); return; } - ret = gb_interface_init(module, + ret = gb_interface_init(gb_ib, management->link_up.interface_id, management->link_up.device_id); if (ret) dev_err(hd->parent, "error %d initializing " - "module %hhu interface %hhu\n", + "interface block %hhu interface %hhu\n", ret, management->link_up.module_id, management->link_up.interface_id); break; diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index f469a8d48e91..e4a5a85f7670 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -340,7 +340,7 @@ static int gb_battery_connection_init(struct gb_connection *connection) b->num_properties = ARRAY_SIZE(battery_props), b->get_property = get_property, - retval = power_supply_register(&connection->interface->gmod->dev, b); + retval = power_supply_register(&connection->interface->gb_ib->dev, b); if (retval) { kfree(gb); return retval; diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index e59a7778c02a..7d5f4616264c 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -155,7 +155,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, return NULL; } - hd = interface->gmod->hd; + hd = interface->gb_ib->hd; connection->hd = hd; if (!gb_connection_hd_cport_id_alloc(connection)) { gb_protocol_put(connection->protocol); @@ -236,7 +236,7 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) vaf.va = &args; pr_err("greybus: [%hhu:%hhu:%hu]: %pV\n", - connection->interface->gmod->module_id, + connection->interface->gb_ib->module_id, connection->interface->id, connection->interface_cport_id, &vaf); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9203ebd2db7f..31460abd5673 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -33,10 +33,10 @@ EXPORT_SYMBOL_GPL(greybus_disabled); static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(drv); - struct gb_module *gmod = to_gb_module(dev); + struct gb_interface_block *gb_ib = to_gb_interface_block(dev); const struct greybus_module_id *id; - id = gb_module_match_id(gmod, driver->id_table); + id = gb_ib_match_id(gb_ib, driver->id_table); if (id) return 1; /* FIXME - Dynamic ids? */ @@ -45,19 +45,19 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct gb_module *gmod = NULL; + struct gb_interface_block *gb_ib = NULL; struct gb_interface *interface = NULL; struct gb_connection *connection = NULL; - if (is_gb_module(dev)) { - gmod = to_gb_module(dev); + if (is_gb_interface_block(dev)) { + gb_ib = to_gb_interface_block(dev); } else if (is_gb_interface(dev)) { interface = to_gb_interface(dev); - gmod = interface->gmod; + gb_ib = interface->gb_ib; } else if (is_gb_connection(dev)) { connection = to_gb_connection(dev); interface = connection->interface; - gmod = interface->gmod; + gb_ib = interface->gb_ib; } else { dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n"); return -EINVAL; @@ -93,16 +93,16 @@ struct bus_type greybus_bus_type = { static int greybus_probe(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct gb_module *gmod = to_gb_module(dev); + struct gb_interface_block *gb_ib = to_gb_interface_block(dev); const struct greybus_module_id *id; int retval; /* match id */ - id = gb_module_match_id(gmod, driver->id_table); + id = gb_ib_match_id(gb_ib, driver->id_table); if (!id) return -ENODEV; - retval = driver->probe(gmod, id); + retval = driver->probe(gb_ib, id); if (retval) return retval; @@ -112,9 +112,9 @@ static int greybus_probe(struct device *dev) static int greybus_remove(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct gb_module *gmod = to_gb_module(dev); + struct gb_interface_block *gb_ib = to_gb_interface_block(dev); - driver->disconnect(gmod); + driver->disconnect(gb_ib); return 0; } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 101b71168491..84516cb60a95 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -118,12 +118,12 @@ void greybus_remove_hd(struct greybus_host_device *hd); struct greybus_driver { const char *name; - int (*probe)(struct gb_module *gmod, + int (*probe)(struct gb_interface_block *gb_ib, const struct greybus_module_id *id); - void (*disconnect)(struct gb_module *gmod); + void (*disconnect)(struct gb_interface_block *gb_ib); - int (*suspend)(struct gb_module *gmod, pm_message_t message); - int (*resume)(struct gb_module *gmod); + int (*suspend)(struct gb_interface_block *gb_ib, pm_message_t message); + int (*resume)(struct gb_interface_block *gb_ib); const struct greybus_module_id *id_table; @@ -175,13 +175,13 @@ void gb_uart_device_exit(struct gb_connection *connection); int svc_set_route_send(struct gb_interface *interface, struct greybus_host_device *hd); -extern struct device_type greybus_module_type; +extern struct device_type greybus_interface_block_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_connection_type; -static inline int is_gb_module(const struct device *dev) +static inline int is_gb_interface_block(const struct device *dev) { - return dev->type == &greybus_module_type; + return dev->type == &greybus_interface_block_type; } static inline int is_gb_interface(const struct device *dev) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 38c104fc8675..0108100aea64 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -52,7 +52,7 @@ static DEFINE_SPINLOCK(gb_interfaces_lock); * pointer if a failure occurs due to memory exhaustion. */ struct gb_interface * -gb_interface_create(struct gb_module *gmod, u8 interface_id) +gb_interface_create(struct gb_interface_block *gb_ib, u8 interface_id) { struct gb_interface *interface; int retval; @@ -61,19 +61,19 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) if (!interface) return NULL; - interface->gmod = gmod; + interface->gb_ib = gb_ib; interface->id = interface_id; interface->device_id = 0xff; /* Invalid device id to start with */ INIT_LIST_HEAD(&interface->connections); /* Build up the interface device structures and register it with the * driver core */ - interface->dev.parent = &gmod->dev; + interface->dev.parent = &gb_ib->dev; interface->dev.bus = &greybus_bus_type; interface->dev.type = &greybus_interface_type; interface->dev.groups = interface_groups; device_initialize(&interface->dev); - dev_set_name(&interface->dev, "%d:%d", gmod->module_id, interface_id); + dev_set_name(&interface->dev, "%d:%d", gb_ib->module_id, interface_id); retval = device_add(&interface->dev); if (retval) { @@ -85,7 +85,7 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) } spin_lock_irq(&gb_interfaces_lock); - list_add_tail(&interface->links, &gmod->interfaces); + list_add_tail(&interface->links, &gb_ib->interfaces); spin_unlock_irq(&gb_interfaces_lock); return interface; @@ -94,16 +94,16 @@ gb_interface_create(struct gb_module *gmod, u8 interface_id) /* * Tear down a previously set up interface. */ -void gb_interface_destroy(struct gb_module *gmod) +void gb_interface_destroy(struct gb_interface_block *gb_ib) { struct gb_interface *interface; struct gb_interface *temp; - if (WARN_ON(!gmod)) + if (WARN_ON(!gb_ib)) return; spin_lock_irq(&gb_interfaces_lock); - list_for_each_entry_safe(interface, temp, &gmod->interfaces, links) { + list_for_each_entry_safe(interface, temp, &gb_ib->interfaces, links) { list_del(&interface->links); gb_interface_connections_exit(interface); device_del(&interface->dev); @@ -111,28 +111,28 @@ void gb_interface_destroy(struct gb_module *gmod) spin_unlock_irq(&gb_interfaces_lock); } -int gb_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) +int gb_interface_init(struct gb_interface_block *gb_ib, u8 interface_id, u8 device_id) { struct gb_interface *interface; int ret; - interface = gb_interface_find(gmod, interface_id); + interface = gb_interface_find(gb_ib, interface_id); if (!interface) { - dev_err(gmod->hd->parent, "module %hhu not found\n", + dev_err(gb_ib->hd->parent, "module %hhu not found\n", interface_id); return -ENOENT; } interface->device_id = device_id; - ret = svc_set_route_send(interface, gmod->hd); + ret = svc_set_route_send(interface, gb_ib->hd); if (ret) { - dev_err(gmod->hd->parent, "failed to set route (%d)\n", ret); + dev_err(gb_ib->hd->parent, "failed to set route (%d)\n", ret); return ret; } ret = gb_interface_connections_init(interface); if (ret) { - dev_err(gmod->hd->parent, "module interface init error %d\n", + dev_err(gb_ib->hd->parent, "module interface init error %d\n", ret); /* XXX clear route */ return ret; @@ -141,13 +141,13 @@ int gb_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id) return 0; } -struct gb_interface *gb_interface_find(struct gb_module *module, +struct gb_interface *gb_interface_find(struct gb_interface_block *gb_ib, u8 interface_id) { struct gb_interface *interface; spin_lock_irq(&gb_interfaces_lock); - list_for_each_entry(interface, &module->interfaces, links) + list_for_each_entry(interface, &gb_ib->interfaces, links) if (interface->id == interface_id) { spin_unlock_irq(&gb_interfaces_lock); return interface; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index bfd1781786ac..2f435fd3f206 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -13,7 +13,7 @@ struct gb_interface { struct device dev; - struct gb_module *gmod; + struct gb_interface_block *gb_ib; u8 id; u8 device_id; struct list_head connections; @@ -22,11 +22,11 @@ struct gb_interface { }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) -struct gb_interface *gb_interface_create(struct gb_module *gmod, u8 module_id); -void gb_interface_destroy(struct gb_module *gmod); -int gb_interface_init(struct gb_module *gmod, u8 module_id, u8 device_id); +struct gb_interface *gb_interface_create(struct gb_interface_block *gb_ib, u8 module_id); +void gb_interface_destroy(struct gb_interface_block *gb_ib); +int gb_interface_init(struct gb_interface_block *gb_ib, u8 module_id, u8 device_id); -struct gb_interface *gb_interface_find(struct gb_module *gmod, u8 interface_id); +struct gb_interface *gb_interface_find(struct gb_interface_block *gb_ib, u8 interface_id); int gb_interface_connections_init(struct gb_interface *interface); void gb_interface_connections_exit(struct gb_interface *interface); diff --git a/drivers/staging/greybus/interface_block.c b/drivers/staging/greybus/interface_block.c index c424a5ac25e7..10b3f509773a 100644 --- a/drivers/staging/greybus/interface_block.c +++ b/drivers/staging/greybus/interface_block.c @@ -11,25 +11,25 @@ /* XXX This could be per-host device */ static DEFINE_SPINLOCK(gb_modules_lock); -static int gb_module_match_one_id(struct gb_module *gmod, +static int gb_module_match_one_id(struct gb_interface_block *gb_ib, const struct greybus_module_id *id) { if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && - (id->vendor != gmod->vendor)) + (id->vendor != gb_ib->vendor)) return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && - (id->product != gmod->product)) + (id->product != gb_ib->product)) return 0; if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && - (id->unique_id != gmod->unique_id)) + (id->unique_id != gb_ib->unique_id)) return 0; return 1; } -const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, +const struct greybus_module_id *gb_ib_match_id(struct gb_interface_block *gb_ib, const struct greybus_module_id *id) { if (id == NULL) @@ -37,108 +37,110 @@ const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, for (; id->vendor || id->product || id->unique_id || id->driver_info; id++) { - if (gb_module_match_one_id(gmod, id)) + if (gb_module_match_one_id(gb_ib, id)) return id; } return NULL; } -struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) +struct gb_interface_block *gb_ib_find(struct greybus_host_device *hd, u8 module_id) { - struct gb_module *module; + struct gb_interface_block *gb_ib; - list_for_each_entry(module, &hd->modules, links) - if (module->module_id == module_id) - return module; + list_for_each_entry(gb_ib, &hd->modules, links) + if (gb_ib->module_id == module_id) + return gb_ib; return NULL; } -static void greybus_module_release(struct device *dev) +static void greybus_ib_release(struct device *dev) { - struct gb_module *gmod = to_gb_module(dev); + struct gb_interface_block *gb_ib = to_gb_interface_block(dev); - kfree(gmod); + kfree(gb_ib); } -struct device_type greybus_module_type = { - .name = "greybus_module", - .release = greybus_module_release, +struct device_type greybus_interface_block_type = { + .name = "greybus_interface_block", + .release = greybus_ib_release, }; /* * A Greybus module represents a user-replicable component on an Ara - * phone. + * phone. An interface block is the physical connection on that module. A + * module may have more than one interface block. * - * Create a gb_module structure to represent a discovered module. + * Create a gb_interface_block structure to represent a discovered module. * The position within the Endo is encoded in the "module_id" argument. * Returns a pointer to the new module or a null pointer if a * failure occurs due to memory exhaustion. */ -struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id) +static struct gb_interface_block *gb_ib_create(struct greybus_host_device *hd, + u8 module_id) { - struct gb_module *gmod; + struct gb_interface_block *gb_ib; int retval; - gmod = gb_module_find(hd, module_id); - if (gmod) { + gb_ib = gb_ib_find(hd, module_id); + if (gb_ib) { dev_err(hd->parent, "Duplicate module id %d will not be created\n", module_id); return NULL; } - gmod = kzalloc(sizeof(*gmod), GFP_KERNEL); - if (!gmod) + gb_ib = kzalloc(sizeof(*gb_ib), GFP_KERNEL); + if (!gb_ib) return NULL; - gmod->hd = hd; /* XXX refcount? */ - gmod->module_id = module_id; - INIT_LIST_HEAD(&gmod->interfaces); + gb_ib->hd = hd; /* XXX refcount? */ + gb_ib->module_id = module_id; + INIT_LIST_HEAD(&gb_ib->interfaces); - gmod->dev.parent = hd->parent; - gmod->dev.bus = &greybus_bus_type; - gmod->dev.type = &greybus_module_type; - gmod->dev.groups = greybus_module_groups; - gmod->dev.dma_mask = hd->parent->dma_mask; - device_initialize(&gmod->dev); - dev_set_name(&gmod->dev, "%d", module_id); + gb_ib->dev.parent = hd->parent; + gb_ib->dev.bus = &greybus_bus_type; + gb_ib->dev.type = &greybus_interface_block_type; + gb_ib->dev.groups = greybus_module_groups; + gb_ib->dev.dma_mask = hd->parent->dma_mask; + device_initialize(&gb_ib->dev); + dev_set_name(&gb_ib->dev, "%d", module_id); - retval = device_add(&gmod->dev); + retval = device_add(&gb_ib->dev); if (retval) { pr_err("failed to add module device for id 0x%02hhx\n", module_id); - put_device(&gmod->dev); - kfree(gmod); + put_device(&gb_ib->dev); + kfree(gb_ib); return NULL; } spin_lock_irq(&gb_modules_lock); - list_add_tail(&gmod->links, &hd->modules); + list_add_tail(&gb_ib->links, &hd->modules); spin_unlock_irq(&gb_modules_lock); - return gmod; + return gb_ib; } /* * Tear down a previously set up module. */ -void gb_module_destroy(struct gb_module *gmod) +static void gb_ib_destroy(struct gb_interface_block *gb_ib) { - if (WARN_ON(!gmod)) + if (WARN_ON(!gb_ib)) return; spin_lock_irq(&gb_modules_lock); - list_del(&gmod->links); + list_del(&gb_ib->links); spin_unlock_irq(&gb_modules_lock); - gb_interface_destroy(gmod); + gb_interface_destroy(gb_ib); - kfree(gmod->product_string); - kfree(gmod->vendor_string); + kfree(gb_ib->product_string); + kfree(gb_ib->vendor_string); /* kref_put(module->hd); */ - device_del(&gmod->dev); + device_del(&gb_ib->dev); } /** @@ -150,11 +152,11 @@ void gb_module_destroy(struct gb_module *gmod) void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size) { - struct gb_module *gmod; + struct gb_interface_block *gb_ib; - gmod = gb_module_create(hd, module_id); - if (!gmod) { - dev_err(hd->parent, "failed to create module\n"); + gb_ib = gb_ib_create(hd, module_id); + if (!gb_ib) { + dev_err(hd->parent, "failed to create interface block\n"); return; } @@ -162,7 +164,7 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, * Parse the manifest and build up our data structures * representing what's in it. */ - if (!gb_manifest_parse(gmod, data, size)) { + if (!gb_manifest_parse(gb_ib, data, size)) { dev_err(hd->parent, "manifest error\n"); goto err_module; } @@ -179,23 +181,23 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, return; err_module: - gb_module_destroy(gmod); + gb_ib_destroy(gb_ib); } void gb_remove_module(struct greybus_host_device *hd, u8 module_id) { - struct gb_module *gmod = gb_module_find(hd, module_id); + struct gb_interface_block *gb_ib = gb_ib_find(hd, module_id); - if (gmod) - gb_module_destroy(gmod); + if (gb_ib) + gb_ib_destroy(gb_ib); else - dev_err(hd->parent, "module id %d not found\n", module_id); + dev_err(hd->parent, "interface block id %d not found\n", module_id); } void gb_remove_modules(struct greybus_host_device *hd) { - struct gb_module *gmod, *temp; + struct gb_interface_block *gb_ib, *temp; - list_for_each_entry_safe(gmod, temp, &hd->modules, links) - gb_module_destroy(gmod); + list_for_each_entry_safe(gb_ib, temp, &hd->modules, links) + gb_ib_destroy(gb_ib); } diff --git a/drivers/staging/greybus/interface_block.h b/drivers/staging/greybus/interface_block.h index 2fdca57398d2..b751ce45a24b 100644 --- a/drivers/staging/greybus/interface_block.h +++ b/drivers/staging/greybus/interface_block.h @@ -1,19 +1,21 @@ /* - * Greybus modules + * Greybus Interface Block code * * Copyright 2014 Google Inc. * * Released under the GPLv2 only. */ -#ifndef __MODULE_H -#define __MODULE_H +#ifndef __INTERFACE_BLOCK_H +#define __INTERFACE_BLOCK_H /* Increase these values if needed */ #define MAX_CPORTS_PER_MODULE 10 #define MAX_STRINGS_PER_MODULE 10 -struct gb_module { + +/* Greybus "public" definitions" */ +struct gb_interface_block { struct device dev; struct list_head interfaces; @@ -29,27 +31,26 @@ struct gb_module { struct greybus_host_device *hd; }; -#define to_gb_module(d) container_of(d, struct gb_module, dev) +#define to_gb_interface_block(d) container_of(d, struct gb_interface_block, dev) static inline void -gb_module_set_drvdata(struct gb_module *gmod, void *data) +gb_interface_block_set_drvdata(struct gb_interface_block *gb_ib, void *data) { - dev_set_drvdata(&gmod->dev, data); + dev_set_drvdata(&gb_ib->dev, data); } -static inline void *gb_module_get_drvdata(struct gb_module *gmod) +static inline void * +gb_interface_block_get_drvdata(struct gb_interface_block *gb_ib) { - return dev_get_drvdata(&gmod->dev); + return dev_get_drvdata(&gb_ib->dev); } -const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod, - const struct greybus_module_id *id); +/* Greybus "private" definitions */ -struct gb_module *gb_module_create(struct greybus_host_device *hd, - u8 module_id); -void gb_module_destroy(struct gb_module *module); +const struct greybus_module_id *gb_ib_match_id(struct gb_interface_block *gb_ib, + const struct greybus_module_id *id); -struct gb_module *gb_module_find(struct greybus_host_device *hd, - u8 module_id); +struct gb_interface_block *gb_ib_find(struct greybus_host_device *hd, + u8 module_id); -#endif /* __MODULE_H */ +#endif /* __INTERFACE_BLOCK_H */ diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 57cd5944b2b6..67aa92796291 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -217,7 +217,7 @@ static u32 gb_manifest_parse_cports(struct gb_interface *interface) * structures. Returns the number of interfaces set up for the * given module. */ -static u32 gb_manifest_parse_interfaces(struct gb_module *gmod) +static u32 gb_manifest_parse_interfaces(struct gb_interface_block *gb_ib) { u32 count = 0; @@ -239,7 +239,7 @@ static u32 gb_manifest_parse_interfaces(struct gb_module *gmod) /* Found one. Set up its interface structure*/ desc_interface = descriptor->data; - interface = gb_interface_create(gmod, desc_interface->id); + interface = gb_interface_create(gb_ib, desc_interface->id); if (!interface) return 0; /* Error */ @@ -256,41 +256,41 @@ static u32 gb_manifest_parse_interfaces(struct gb_module *gmod) return count; } -static bool gb_manifest_parse_module(struct gb_module *gmod, +static bool gb_manifest_parse_module(struct gb_interface_block *gb_ib, struct manifest_desc *module_desc) { struct greybus_descriptor_module *desc_module = module_desc->data; /* Handle the strings first--they can fail */ - gmod->vendor_string = gb_string_get(desc_module->vendor_stringid); - if (IS_ERR(gmod->vendor_string)) + gb_ib->vendor_string = gb_string_get(desc_module->vendor_stringid); + if (IS_ERR(gb_ib->vendor_string)) return false; - gmod->product_string = gb_string_get(desc_module->product_stringid); - if (IS_ERR(gmod->product_string)) { + gb_ib->product_string = gb_string_get(desc_module->product_stringid); + if (IS_ERR(gb_ib->product_string)) { goto out_free_vendor_string; } - gmod->vendor = le16_to_cpu(desc_module->vendor); - gmod->product = le16_to_cpu(desc_module->product); - gmod->unique_id = le64_to_cpu(desc_module->unique_id); + gb_ib->vendor = le16_to_cpu(desc_module->vendor); + gb_ib->product = le16_to_cpu(desc_module->product); + gb_ib->unique_id = le64_to_cpu(desc_module->unique_id); /* Release the module descriptor, now that we're done with it */ release_manifest_descriptor(module_desc); /* A module must have at least one interface descriptor */ - if (!gb_manifest_parse_interfaces(gmod)) { + if (!gb_manifest_parse_interfaces(gb_ib)) { pr_err("manifest interface descriptors not valid\n"); goto out_err; } return true; out_err: - kfree(gmod->product_string); - gmod->product_string = NULL; + kfree(gb_ib->product_string); + gb_ib->product_string = NULL; out_free_vendor_string: - kfree(gmod->vendor_string); - gmod->vendor_string = NULL; + kfree(gb_ib->vendor_string); + gb_ib->vendor_string = NULL; return false; } @@ -318,7 +318,7 @@ out_free_vendor_string: * * Returns true if parsing was successful, false otherwise. */ -bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) +bool gb_manifest_parse(struct gb_interface_block *gb_ib, void *data, size_t size) { struct greybus_manifest *manifest; struct greybus_manifest_header *header; @@ -388,7 +388,7 @@ bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size) } /* Parse the module manifest, starting with the module descriptor */ - result = gb_manifest_parse_module(gmod, module_desc); + result = gb_manifest_parse_module(gb_ib, module_desc); /* * We really should have no remaining descriptors, but we diff --git a/drivers/staging/greybus/manifest.h b/drivers/staging/greybus/manifest.h index a1fe2c1281ad..7c82a45d080e 100644 --- a/drivers/staging/greybus/manifest.h +++ b/drivers/staging/greybus/manifest.h @@ -9,7 +9,7 @@ #ifndef __MANIFEST_H #define __MANIFEST_H -struct gb_module; -bool gb_manifest_parse(struct gb_module *gmod, void *data, size_t size); +struct gb_interface_block; +bool gb_manifest_parse(struct gb_interface_block *gb_ib, void *data, size_t size); #endif /* __MANIFEST_H */ diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index f9d56e188ec4..28d1b8d83873 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -25,8 +25,8 @@ static ssize_t module_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_module *gmod = to_gb_module(dev); \ - return sprintf(buf, "%"#type"\n", gmod->field); \ + struct gb_interface_block *gb_ib = to_gb_interface_block(dev); \ + return sprintf(buf, "%"#type"\n", gb_ib->field); \ } \ static DEVICE_ATTR_RO(module_##field) -- cgit v1.2.3-59-g8ed1b From 8ede6e36dfa88a4c0a0e66f34fb177af3beaab8f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:10:57 -0500 Subject: greybus: interface_block: rename the sysfs files to not have 'module' in them The sysfs files for an interface block should not have 'module' in them. This was a hold-over from when we thought we were going to have all attributes of a "module" in one directory. Remove the prefix as it's not needed, and is confusing considering modules can not have strings or any of these attributes. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/interface_block.c | 2 +- drivers/staging/greybus/sysfs.c | 40 +++++++++++++++---------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 84516cb60a95..632b1cd2a60b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -167,7 +167,7 @@ int gb_debugfs_init(void); void gb_debugfs_cleanup(void); extern struct bus_type greybus_bus_type; -extern const struct attribute_group *greybus_module_groups[]; +extern const struct attribute_group *greybus_interface_block_groups[]; int gb_uart_device_init(struct gb_connection *connection); void gb_uart_device_exit(struct gb_connection *connection); diff --git a/drivers/staging/greybus/interface_block.c b/drivers/staging/greybus/interface_block.c index 10b3f509773a..7b18f4855cb5 100644 --- a/drivers/staging/greybus/interface_block.c +++ b/drivers/staging/greybus/interface_block.c @@ -101,7 +101,7 @@ static struct gb_interface_block *gb_ib_create(struct greybus_host_device *hd, gb_ib->dev.parent = hd->parent; gb_ib->dev.bus = &greybus_bus_type; gb_ib->dev.type = &greybus_interface_block_type; - gb_ib->dev.groups = greybus_module_groups; + gb_ib->dev.groups = greybus_interface_block_groups; gb_ib->dev.dma_mask = hd->parent->dma_mask; device_initialize(&gb_ib->dev); dev_set_name(&gb_ib->dev, "%d", module_id); diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c index 28d1b8d83873..ae44c895760f 100644 --- a/drivers/staging/greybus/sysfs.c +++ b/drivers/staging/greybus/sysfs.c @@ -20,37 +20,37 @@ #include "kernel_ver.h" /* Module fields */ -#define gb_module_attr(field, type) \ -static ssize_t module_##field##_show(struct device *dev, \ +#define gb_ib_attr(field, type) \ +static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_interface_block *gb_ib = to_gb_interface_block(dev); \ return sprintf(buf, "%"#type"\n", gb_ib->field); \ } \ -static DEVICE_ATTR_RO(module_##field) - -gb_module_attr(vendor, x); -gb_module_attr(product, x); -gb_module_attr(unique_id, llX); -gb_module_attr(vendor_string, s); -gb_module_attr(product_string, s); - -static struct attribute *module_attrs[] = { - &dev_attr_module_vendor.attr, - &dev_attr_module_product.attr, - &dev_attr_module_unique_id.attr, - &dev_attr_module_vendor_string.attr, - &dev_attr_module_product_string.attr, +static DEVICE_ATTR_RO(field) + +gb_ib_attr(vendor, x); +gb_ib_attr(product, x); +gb_ib_attr(unique_id, llX); +gb_ib_attr(vendor_string, s); +gb_ib_attr(product_string, s); + +static struct attribute *interface_block_attrs[] = { + &dev_attr_vendor.attr, + &dev_attr_product.attr, + &dev_attr_unique_id.attr, + &dev_attr_vendor_string.attr, + &dev_attr_product_string.attr, NULL, }; -static struct attribute_group module_attr_grp = { - .attrs = module_attrs, +static struct attribute_group interface_block_attr_grp = { + .attrs = interface_block_attrs, }; -const struct attribute_group *greybus_module_groups[] = { - &module_attr_grp, +const struct attribute_group *greybus_interface_block_groups[] = { + &interface_block_attr_grp, NULL, }; -- cgit v1.2.3-59-g8ed1b From fc2a8fbec71fe12d58154191cc10775e2881ef1b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:10:58 -0500 Subject: greybus: sysfs documentation: Document the greybus interface block sysfs files. Documentation, what, really? Yes. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs-bus-greybus | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 drivers/staging/greybus/Documentation/sysfs-bus-greybus diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus new file mode 100644 index 000000000000..cd1d812d388e --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -0,0 +1,35 @@ +What: /sys/bus/greybus/device/.../product +Date: December 2014 +KernelVersion: 3.XX +Contact: Greg Kroah-Hartman +Description: + Product ID of a Greybus interface block. + +What: /sys/bus/greybus/device/.../product_string +Date: December 2014 +KernelVersion: 3.XX +Contact: Greg Kroah-Hartman +Description: + Product ID string of a Greybus interface block. + +What: /sys/bus/greybus/device/.../unique_id +Date: December 2014 +KernelVersion: 3.XX +Contact: Greg Kroah-Hartman +Description: + Unique ID of a Greybus interface block. + +What: /sys/bus/greybus/device/.../vendor +Date: December 2014 +KernelVersion: 3.XX +Contact: Greg Kroah-Hartman +Description: + Vendor ID of a Greybus interface block. + +What: /sys/bus/greybus/device/.../vendor_string +Date: December 2014 +KernelVersion: 3.XX +Contact: Greg Kroah-Hartman +Description: + Vendor ID string of a Greybus interface block. + -- cgit v1.2.3-59-g8ed1b From ab88eb58c7a56ded2fd3ec4b9184075ff415cad3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:10:59 -0500 Subject: greybus: interface_block: move sysfs files into the interface_block.c file No need to keep these out in sysfs.c, move them into the interface_block.c file so that we can see them easier, and remove some variable definitions by taking advantage of the attribute group macro. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 - drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/interface_block.c | 32 ++++++++++++++++-- drivers/staging/greybus/sysfs.c | 56 ------------------------------- 4 files changed, 30 insertions(+), 60 deletions(-) delete mode 100644 drivers/staging/greybus/sysfs.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 41186b7bb2e4..29aa4d665877 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,5 +1,4 @@ greybus-y := core.o \ - sysfs.o \ debugfs.o \ ap.o \ manifest.o \ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 632b1cd2a60b..b32dd6118e30 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -167,7 +167,6 @@ int gb_debugfs_init(void); void gb_debugfs_cleanup(void); extern struct bus_type greybus_bus_type; -extern const struct attribute_group *greybus_interface_block_groups[]; int gb_uart_device_init(struct gb_connection *connection); void gb_uart_device_exit(struct gb_connection *connection); diff --git a/drivers/staging/greybus/interface_block.c b/drivers/staging/greybus/interface_block.c index 7b18f4855cb5..3f173aef01c5 100644 --- a/drivers/staging/greybus/interface_block.c +++ b/drivers/staging/greybus/interface_block.c @@ -1,5 +1,5 @@ /* - * Greybus modules + * Greybus interface block code * * Copyright 2014 Google Inc. * @@ -8,6 +8,34 @@ #include "greybus.h" +/* interface block sysfs attributes */ +#define gb_ib_attr(field, type) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct gb_interface_block *gb_ib = to_gb_interface_block(dev); \ + return sprintf(buf, "%"#type"\n", gb_ib->field); \ +} \ +static DEVICE_ATTR_RO(field) + +gb_ib_attr(vendor, x); +gb_ib_attr(product, x); +gb_ib_attr(unique_id, llX); +gb_ib_attr(vendor_string, s); +gb_ib_attr(product_string, s); + +static struct attribute *interface_block_attrs[] = { + &dev_attr_vendor.attr, + &dev_attr_product.attr, + &dev_attr_unique_id.attr, + &dev_attr_vendor_string.attr, + &dev_attr_product_string.attr, + NULL, +}; +ATTRIBUTE_GROUPS(interface_block); + + /* XXX This could be per-host device */ static DEFINE_SPINLOCK(gb_modules_lock); @@ -101,7 +129,7 @@ static struct gb_interface_block *gb_ib_create(struct greybus_host_device *hd, gb_ib->dev.parent = hd->parent; gb_ib->dev.bus = &greybus_bus_type; gb_ib->dev.type = &greybus_interface_block_type; - gb_ib->dev.groups = greybus_interface_block_groups; + gb_ib->dev.groups = interface_block_groups; gb_ib->dev.dma_mask = hd->parent->dma_mask; device_initialize(&gb_ib->dev); dev_set_name(&gb_ib->dev, "%d", module_id); diff --git a/drivers/staging/greybus/sysfs.c b/drivers/staging/greybus/sysfs.c deleted file mode 100644 index ae44c895760f..000000000000 --- a/drivers/staging/greybus/sysfs.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Greybus sysfs file functions - * - * Copyright 2014 Google Inc. - * - * Released under the GPLv2 only. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#include "greybus.h" -#include "kernel_ver.h" - -/* Module fields */ -#define gb_ib_attr(field, type) \ -static ssize_t field##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - struct gb_interface_block *gb_ib = to_gb_interface_block(dev); \ - return sprintf(buf, "%"#type"\n", gb_ib->field); \ -} \ -static DEVICE_ATTR_RO(field) - -gb_ib_attr(vendor, x); -gb_ib_attr(product, x); -gb_ib_attr(unique_id, llX); -gb_ib_attr(vendor_string, s); -gb_ib_attr(product_string, s); - -static struct attribute *interface_block_attrs[] = { - &dev_attr_vendor.attr, - &dev_attr_product.attr, - &dev_attr_unique_id.attr, - &dev_attr_vendor_string.attr, - &dev_attr_product_string.attr, - NULL, -}; - -static struct attribute_group interface_block_attr_grp = { - .attrs = interface_block_attrs, -}; - -const struct attribute_group *greybus_interface_block_groups[] = { - &interface_block_attr_grp, - NULL, -}; - -- cgit v1.2.3-59-g8ed1b From a500a8a6ef283f47efaddde8c8b7213a76a731c5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:11:00 -0500 Subject: greybus: connection: document the sysfs files Document what the sysfs files are for connections, so that people have a chance to understand what they can be used for. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- .../staging/greybus/Documentation/sysfs-bus-greybus | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index cd1d812d388e..b734c674b4f9 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -33,3 +33,23 @@ Contact: Greg Kroah-Hartman Description: Vendor ID string of a Greybus interface block. +What: /sys/bus/greybus/device/.../state +Date: December 2014 +KernelVersion: 3.XX +Contact: Greg Kroah-Hartman +Description: + The current state of a Greybus connection. + + It will be one of the following values: + 0 - invalid + 1 - disabled + 2 - enabled + 3 - error + 4 - destroying + +What: /sys/bus/greybus/device/.../protocol_id +Date: December 2014 +KernelVersion: 3.XX +Contact: Greg Kroah-Hartman +Description: + The protocol id of a Greybus connection. -- cgit v1.2.3-59-g8ed1b From 640f13ecc879163dd45c213fbbef8ffc825d95bb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:11:01 -0500 Subject: greybus: interface: document sysfs files Yes, an interface has a device id sysfs file, so we need to document it. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index b734c674b4f9..2491201848c7 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -53,3 +53,10 @@ KernelVersion: 3.XX Contact: Greg Kroah-Hartman Description: The protocol id of a Greybus connection. + +What: /sys/bus/greybus/device/.../device_id +Date: December 2014 +KernelVersion: 3.XX +Contact: Greg Kroah-Hartman +Description: + The device id of a Greybus interface. -- cgit v1.2.3-59-g8ed1b From 2f0c8aa4e6710a939da64412da0bdd7bb9aa5557 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Dec 2014 17:11:02 -0500 Subject: greybus: driver matching: Greybus drivers bind to interface blocks, not modules Because of this, rename greybus_module_id to greybus_interface_block_id. We still need to add a way for a "class" driver to be bound to an interface, but for now, all we really need is the vendor/product pair as the GP Bridge interface block is going to be our main user. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 ++-- drivers/staging/greybus/greybus.h | 4 ++-- drivers/staging/greybus/greybus_id.h | 10 +++++----- drivers/staging/greybus/interface_block.c | 17 +++++++++-------- drivers/staging/greybus/interface_block.h | 5 +++-- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 31460abd5673..66c54d7a0fcf 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -34,7 +34,7 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(drv); struct gb_interface_block *gb_ib = to_gb_interface_block(dev); - const struct greybus_module_id *id; + const struct greybus_interface_block_id *id; id = gb_ib_match_id(gb_ib, driver->id_table); if (id) @@ -94,7 +94,7 @@ static int greybus_probe(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); struct gb_interface_block *gb_ib = to_gb_interface_block(dev); - const struct greybus_module_id *id; + const struct greybus_interface_block_id *id; int retval; /* match id */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index b32dd6118e30..95567b853647 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -119,13 +119,13 @@ struct greybus_driver { const char *name; int (*probe)(struct gb_interface_block *gb_ib, - const struct greybus_module_id *id); + const struct greybus_interface_block_id *id); void (*disconnect)(struct gb_interface_block *gb_ib); int (*suspend)(struct gb_interface_block *gb_ib, pm_message_t message); int (*resume)(struct gb_interface_block *gb_ib); - const struct greybus_module_id *id_table; + const struct greybus_interface_block_id *id_table; struct device_driver driver; }; diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index c6cd2e8fa7c1..da70aab54ab0 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -9,7 +9,7 @@ #include -struct greybus_module_id { +struct greybus_interface_block_id { __u16 match_flags; __u16 vendor; __u16 product; @@ -18,9 +18,9 @@ struct greybus_module_id { kernel_ulong_t driver_info __aligned(sizeof(kernel_ulong_t)); }; -/* Used to match the greybus_module_id */ -#define GREYBUS_DEVICE_ID_MATCH_VENDOR BIT(0) -#define GREYBUS_DEVICE_ID_MATCH_PRODUCT BIT(1) -#define GREYBUS_DEVICE_ID_MATCH_SERIAL BIT(2) +/* Used to match the greybus_interface_block_id */ +#define GREYBUS_ID_MATCH_VENDOR BIT(0) +#define GREYBUS_ID_MATCH_PRODUCT BIT(1) +#define GREYBUS_ID_MATCH_SERIAL BIT(2) #endif /* __LINUX_GREYBUS_H */ diff --git a/drivers/staging/greybus/interface_block.c b/drivers/staging/greybus/interface_block.c index 3f173aef01c5..882763df70b7 100644 --- a/drivers/staging/greybus/interface_block.c +++ b/drivers/staging/greybus/interface_block.c @@ -39,33 +39,34 @@ ATTRIBUTE_GROUPS(interface_block); /* XXX This could be per-host device */ static DEFINE_SPINLOCK(gb_modules_lock); -static int gb_module_match_one_id(struct gb_interface_block *gb_ib, - const struct greybus_module_id *id) +static int gb_ib_match_one_id(struct gb_interface_block *gb_ib, + const struct greybus_interface_block_id *id) { - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) && + if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && (id->vendor != gb_ib->vendor)) return 0; - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) && + if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && (id->product != gb_ib->product)) return 0; - if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) && + if ((id->match_flags & GREYBUS_ID_MATCH_SERIAL) && (id->unique_id != gb_ib->unique_id)) return 0; return 1; } -const struct greybus_module_id *gb_ib_match_id(struct gb_interface_block *gb_ib, - const struct greybus_module_id *id) +const struct greybus_interface_block_id * +gb_ib_match_id(struct gb_interface_block *gb_ib, + const struct greybus_interface_block_id *id) { if (id == NULL) return NULL; for (; id->vendor || id->product || id->unique_id || id->driver_info; id++) { - if (gb_module_match_one_id(gb_ib, id)) + if (gb_ib_match_one_id(gb_ib, id)) return id; } diff --git a/drivers/staging/greybus/interface_block.h b/drivers/staging/greybus/interface_block.h index b751ce45a24b..7a166fdf93a7 100644 --- a/drivers/staging/greybus/interface_block.h +++ b/drivers/staging/greybus/interface_block.h @@ -47,8 +47,9 @@ gb_interface_block_get_drvdata(struct gb_interface_block *gb_ib) /* Greybus "private" definitions */ -const struct greybus_module_id *gb_ib_match_id(struct gb_interface_block *gb_ib, - const struct greybus_module_id *id); +const struct greybus_interface_block_id * + gb_ib_match_id(struct gb_interface_block *gb_ib, + const struct greybus_interface_block_id *id); struct gb_interface_block *gb_ib_find(struct greybus_host_device *hd, u8 module_id); -- cgit v1.2.3-59-g8ed1b From a46e96719dc9180e80fe0cb81f4cdd3757b1e9cd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 12 Dec 2014 12:08:42 -0600 Subject: greybus: add Linaro copyrights I was asked to add a Linaro copyright to all Greybus source files that anyone at Linaro has modified. This patch does that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 1 + drivers/staging/greybus/battery-gb.c | 1 + drivers/staging/greybus/connection.c | 1 + drivers/staging/greybus/connection.h | 1 + drivers/staging/greybus/core.c | 1 + drivers/staging/greybus/debugfs.c | 1 + drivers/staging/greybus/es1-ap-usb.c | 1 + drivers/staging/greybus/gpio-gb.c | 1 + drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/i2c-gb.c | 1 + drivers/staging/greybus/interface.c | 1 + drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/interface_block.c | 1 + drivers/staging/greybus/interface_block.h | 1 + drivers/staging/greybus/kernel_ver.h | 1 + drivers/staging/greybus/manifest.c | 1 + drivers/staging/greybus/manifest.h | 1 + drivers/staging/greybus/operation.c | 1 + drivers/staging/greybus/operation.h | 1 + drivers/staging/greybus/protocol.c | 1 + drivers/staging/greybus/protocol.h | 1 + drivers/staging/greybus/sdio-gb.c | 1 + drivers/staging/greybus/svc_msg.h | 1 + drivers/staging/greybus/uart-gb.c | 1 + drivers/staging/greybus/usb-gb.c | 1 + drivers/staging/greybus/vibrator-gb.c | 1 + 27 files changed, 27 insertions(+) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 89debab75621..4fa2ad9b3b0a 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -2,6 +2,7 @@ * Greybus "AP" message loop handling * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index e4a5a85f7670..3ed7639b2cad 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -2,6 +2,7 @@ * Battery driver for a Greybus module. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7d5f4616264c..1658fdb5f10b 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -2,6 +2,7 @@ * Greybus connections * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 7568161e5dcb..5c3fad3f46eb 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -2,6 +2,7 @@ * Greybus connections * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 66c54d7a0fcf..032710ced7ce 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -2,6 +2,7 @@ * Greybus "Core" * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c index 4755a36d0522..770b7c0857d3 100644 --- a/drivers/staging/greybus/debugfs.c +++ b/drivers/staging/greybus/debugfs.c @@ -2,6 +2,7 @@ * Greybus debugfs code * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 277699758973..41f56da747c5 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -2,6 +2,7 @@ * Greybus "AP" USB driver * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index c573ccec40aa..152ba50d1693 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -2,6 +2,7 @@ * GPIO Greybus driver. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 95567b853647..8216c9365503 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -2,6 +2,7 @@ * Greybus driver and device API * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index f44f3376f4da..deff5c71d3fd 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -5,6 +5,7 @@ * details on these values and structures. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 and BSD licenses. */ diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 4ef6ea1230a5..491fac4feaf3 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -2,6 +2,7 @@ * I2C bridge driver for the Greybus "generic" I2C module. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 0108100aea64..742781ceb135 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -2,6 +2,7 @@ * Greybus interfaces * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 2f435fd3f206..c0c66b851c5d 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -2,6 +2,7 @@ * Greybus interfaces * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/interface_block.c b/drivers/staging/greybus/interface_block.c index 882763df70b7..ab4c833ef0a0 100644 --- a/drivers/staging/greybus/interface_block.c +++ b/drivers/staging/greybus/interface_block.c @@ -2,6 +2,7 @@ * Greybus interface block code * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/interface_block.h b/drivers/staging/greybus/interface_block.h index 7a166fdf93a7..03da567c7ac9 100644 --- a/drivers/staging/greybus/interface_block.h +++ b/drivers/staging/greybus/interface_block.h @@ -2,6 +2,7 @@ * Greybus Interface Block code * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index a39ceafb9d9b..66c27132e4ac 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -2,6 +2,7 @@ * Greybus kernel "version" glue logic. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. * diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 67aa92796291..01a69a3bfa02 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -2,6 +2,7 @@ * Greybus module manifest parsing * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/manifest.h b/drivers/staging/greybus/manifest.h index 7c82a45d080e..a8316a0399f9 100644 --- a/drivers/staging/greybus/manifest.h +++ b/drivers/staging/greybus/manifest.h @@ -2,6 +2,7 @@ * Greybus module manifest parsing * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 6c815d29502c..a057c83c90f8 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -2,6 +2,7 @@ * Greybus operations * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 1ade52bb1168..e6da8276619e 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -2,6 +2,7 @@ * Greybus operations * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 31ba172c76b2..4ca3ae533354 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -2,6 +2,7 @@ * Greybus protocol handling * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 0ced63e9df91..7d33b20b1808 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -2,6 +2,7 @@ * Greybus protocol handling * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 3f1136566491..8de7dc467b31 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -2,6 +2,7 @@ * SD/MMC Greybus driver. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 5b545129e63d..1f8c4d574536 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -5,6 +5,7 @@ * details on these values and structures. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 and BSD license. */ diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 8599da2b7956..faba00ef1c22 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -2,6 +2,7 @@ * UART driver for the Greybus "generic" UART module. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. * diff --git a/drivers/staging/greybus/usb-gb.c b/drivers/staging/greybus/usb-gb.c index b3092ed1a191..2b5f69e59a46 100644 --- a/drivers/staging/greybus/usb-gb.c +++ b/drivers/staging/greybus/usb-gb.c @@ -2,6 +2,7 @@ * USB host driver for the Greybus "generic" USB module. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. * diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index 8970c69ea893..fb48112692cf 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -2,6 +2,7 @@ * Greybus Vibrator protocol driver. * * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ -- cgit v1.2.3-59-g8ed1b From 3bdec69931d889f2ee5dccafbc8cee6a364bb249 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 12 Dec 2014 17:10:16 -0500 Subject: greybus: bundle: rename interface.[c|h] to bundle.[c|h] We are renameing the "interface" term to "bundle" so rename the files before we start changing structure names to make it easier for people to see what really is happening in the changes. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/bundle.c | 186 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/bundle.h | 35 +++++++ drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/interface.c | 186 ------------------------------------ drivers/staging/greybus/interface.h | 35 ------- 6 files changed, 223 insertions(+), 223 deletions(-) create mode 100644 drivers/staging/greybus/bundle.c create mode 100644 drivers/staging/greybus/bundle.h delete mode 100644 drivers/staging/greybus/interface.c delete mode 100644 drivers/staging/greybus/interface.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 29aa4d665877..08cdaf1affb8 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -3,7 +3,7 @@ greybus-y := core.o \ ap.o \ manifest.o \ interface_block.o \ - interface.o \ + bundle.o \ connection.o \ protocol.o \ operation.o \ diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c new file mode 100644 index 000000000000..742781ceb135 --- /dev/null +++ b/drivers/staging/greybus/bundle.c @@ -0,0 +1,186 @@ +/* + * Greybus interfaces + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_interface *interface = to_gb_interface(dev); + + return sprintf(buf, "%d", interface->device_id); +} +static DEVICE_ATTR_RO(device_id); + +static struct attribute *interface_attrs[] = { + &dev_attr_device_id.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(interface); + +static void gb_interface_release(struct device *dev) +{ + struct gb_interface *interface = to_gb_interface(dev); + + kfree(interface); +} + +struct device_type greybus_interface_type = { + .name = "greybus_interface", + .release = gb_interface_release, +}; + + +/* XXX This could be per-host device or per-module */ +static DEFINE_SPINLOCK(gb_interfaces_lock); + +/* + * A Greybus interface represents a UniPro device present on a + * module. For Project Ara, each active Interface Block on a module + * implements a UniPro device, and therefore a Greybus interface. A + * Greybus module has at least one interface, but can have two (or + * even more). + * + * Create a gb_interface structure to represent a discovered + * interface. Returns a pointer to the new interface or a null + * pointer if a failure occurs due to memory exhaustion. + */ +struct gb_interface * +gb_interface_create(struct gb_interface_block *gb_ib, u8 interface_id) +{ + struct gb_interface *interface; + int retval; + + interface = kzalloc(sizeof(*interface), GFP_KERNEL); + if (!interface) + return NULL; + + interface->gb_ib = gb_ib; + interface->id = interface_id; + interface->device_id = 0xff; /* Invalid device id to start with */ + INIT_LIST_HEAD(&interface->connections); + + /* Build up the interface device structures and register it with the + * driver core */ + interface->dev.parent = &gb_ib->dev; + interface->dev.bus = &greybus_bus_type; + interface->dev.type = &greybus_interface_type; + interface->dev.groups = interface_groups; + device_initialize(&interface->dev); + dev_set_name(&interface->dev, "%d:%d", gb_ib->module_id, interface_id); + + retval = device_add(&interface->dev); + if (retval) { + pr_err("failed to add interface device for id 0x%02hhx\n", + interface_id); + put_device(&interface->dev); + kfree(interface); + return NULL; + } + + spin_lock_irq(&gb_interfaces_lock); + list_add_tail(&interface->links, &gb_ib->interfaces); + spin_unlock_irq(&gb_interfaces_lock); + + return interface; +} + +/* + * Tear down a previously set up interface. + */ +void gb_interface_destroy(struct gb_interface_block *gb_ib) +{ + struct gb_interface *interface; + struct gb_interface *temp; + + if (WARN_ON(!gb_ib)) + return; + + spin_lock_irq(&gb_interfaces_lock); + list_for_each_entry_safe(interface, temp, &gb_ib->interfaces, links) { + list_del(&interface->links); + gb_interface_connections_exit(interface); + device_del(&interface->dev); + } + spin_unlock_irq(&gb_interfaces_lock); +} + +int gb_interface_init(struct gb_interface_block *gb_ib, u8 interface_id, u8 device_id) +{ + struct gb_interface *interface; + int ret; + + interface = gb_interface_find(gb_ib, interface_id); + if (!interface) { + dev_err(gb_ib->hd->parent, "module %hhu not found\n", + interface_id); + return -ENOENT; + } + interface->device_id = device_id; + + ret = svc_set_route_send(interface, gb_ib->hd); + if (ret) { + dev_err(gb_ib->hd->parent, "failed to set route (%d)\n", ret); + return ret; + } + + ret = gb_interface_connections_init(interface); + if (ret) { + dev_err(gb_ib->hd->parent, "module interface init error %d\n", + ret); + /* XXX clear route */ + return ret; + } + + return 0; +} + +struct gb_interface *gb_interface_find(struct gb_interface_block *gb_ib, + u8 interface_id) +{ + struct gb_interface *interface; + + spin_lock_irq(&gb_interfaces_lock); + list_for_each_entry(interface, &gb_ib->interfaces, links) + if (interface->id == interface_id) { + spin_unlock_irq(&gb_interfaces_lock); + return interface; + } + spin_unlock_irq(&gb_interfaces_lock); + + return NULL; +} + +int gb_interface_connections_init(struct gb_interface *interface) +{ + struct gb_connection *connection; + int ret = 0; + + list_for_each_entry(connection, &interface->connections, + interface_links) { + ret = gb_connection_init(connection); + if (ret) + break; + } + + return ret; +} + +void gb_interface_connections_exit(struct gb_interface *interface) +{ + struct gb_connection *connection; + struct gb_connection *next; + + list_for_each_entry_safe(connection, next, &interface->connections, + interface_links) { + gb_connection_exit(connection); + gb_connection_destroy(connection); + } +} diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h new file mode 100644 index 000000000000..c0c66b851c5d --- /dev/null +++ b/drivers/staging/greybus/bundle.h @@ -0,0 +1,35 @@ +/* + * Greybus interfaces + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __INTERFACE_H +#define __INTERFACE_H + +#include + +struct gb_interface { + struct device dev; + struct gb_interface_block *gb_ib; + u8 id; + u8 device_id; + struct list_head connections; + + struct list_head links; /* module->interfaces */ +}; +#define to_gb_interface(d) container_of(d, struct gb_interface, dev) + +struct gb_interface *gb_interface_create(struct gb_interface_block *gb_ib, u8 module_id); +void gb_interface_destroy(struct gb_interface_block *gb_ib); +int gb_interface_init(struct gb_interface_block *gb_ib, u8 module_id, u8 device_id); + +struct gb_interface *gb_interface_find(struct gb_interface_block *gb_ib, u8 interface_id); + +int gb_interface_connections_init(struct gb_interface *interface); +void gb_interface_connections_exit(struct gb_interface *interface); + +#endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 8216c9365503..f243dd2c1835 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -25,7 +25,7 @@ #include "greybus_manifest.h" #include "manifest.h" #include "interface_block.h" -#include "interface.h" +#include "bundle.h" #include "connection.h" #include "protocol.h" #include "operation.h" diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c deleted file mode 100644 index 742781ceb135..000000000000 --- a/drivers/staging/greybus/interface.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Greybus interfaces - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include "greybus.h" - -static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct gb_interface *interface = to_gb_interface(dev); - - return sprintf(buf, "%d", interface->device_id); -} -static DEVICE_ATTR_RO(device_id); - -static struct attribute *interface_attrs[] = { - &dev_attr_device_id.attr, - NULL, -}; - -ATTRIBUTE_GROUPS(interface); - -static void gb_interface_release(struct device *dev) -{ - struct gb_interface *interface = to_gb_interface(dev); - - kfree(interface); -} - -struct device_type greybus_interface_type = { - .name = "greybus_interface", - .release = gb_interface_release, -}; - - -/* XXX This could be per-host device or per-module */ -static DEFINE_SPINLOCK(gb_interfaces_lock); - -/* - * A Greybus interface represents a UniPro device present on a - * module. For Project Ara, each active Interface Block on a module - * implements a UniPro device, and therefore a Greybus interface. A - * Greybus module has at least one interface, but can have two (or - * even more). - * - * Create a gb_interface structure to represent a discovered - * interface. Returns a pointer to the new interface or a null - * pointer if a failure occurs due to memory exhaustion. - */ -struct gb_interface * -gb_interface_create(struct gb_interface_block *gb_ib, u8 interface_id) -{ - struct gb_interface *interface; - int retval; - - interface = kzalloc(sizeof(*interface), GFP_KERNEL); - if (!interface) - return NULL; - - interface->gb_ib = gb_ib; - interface->id = interface_id; - interface->device_id = 0xff; /* Invalid device id to start with */ - INIT_LIST_HEAD(&interface->connections); - - /* Build up the interface device structures and register it with the - * driver core */ - interface->dev.parent = &gb_ib->dev; - interface->dev.bus = &greybus_bus_type; - interface->dev.type = &greybus_interface_type; - interface->dev.groups = interface_groups; - device_initialize(&interface->dev); - dev_set_name(&interface->dev, "%d:%d", gb_ib->module_id, interface_id); - - retval = device_add(&interface->dev); - if (retval) { - pr_err("failed to add interface device for id 0x%02hhx\n", - interface_id); - put_device(&interface->dev); - kfree(interface); - return NULL; - } - - spin_lock_irq(&gb_interfaces_lock); - list_add_tail(&interface->links, &gb_ib->interfaces); - spin_unlock_irq(&gb_interfaces_lock); - - return interface; -} - -/* - * Tear down a previously set up interface. - */ -void gb_interface_destroy(struct gb_interface_block *gb_ib) -{ - struct gb_interface *interface; - struct gb_interface *temp; - - if (WARN_ON(!gb_ib)) - return; - - spin_lock_irq(&gb_interfaces_lock); - list_for_each_entry_safe(interface, temp, &gb_ib->interfaces, links) { - list_del(&interface->links); - gb_interface_connections_exit(interface); - device_del(&interface->dev); - } - spin_unlock_irq(&gb_interfaces_lock); -} - -int gb_interface_init(struct gb_interface_block *gb_ib, u8 interface_id, u8 device_id) -{ - struct gb_interface *interface; - int ret; - - interface = gb_interface_find(gb_ib, interface_id); - if (!interface) { - dev_err(gb_ib->hd->parent, "module %hhu not found\n", - interface_id); - return -ENOENT; - } - interface->device_id = device_id; - - ret = svc_set_route_send(interface, gb_ib->hd); - if (ret) { - dev_err(gb_ib->hd->parent, "failed to set route (%d)\n", ret); - return ret; - } - - ret = gb_interface_connections_init(interface); - if (ret) { - dev_err(gb_ib->hd->parent, "module interface init error %d\n", - ret); - /* XXX clear route */ - return ret; - } - - return 0; -} - -struct gb_interface *gb_interface_find(struct gb_interface_block *gb_ib, - u8 interface_id) -{ - struct gb_interface *interface; - - spin_lock_irq(&gb_interfaces_lock); - list_for_each_entry(interface, &gb_ib->interfaces, links) - if (interface->id == interface_id) { - spin_unlock_irq(&gb_interfaces_lock); - return interface; - } - spin_unlock_irq(&gb_interfaces_lock); - - return NULL; -} - -int gb_interface_connections_init(struct gb_interface *interface) -{ - struct gb_connection *connection; - int ret = 0; - - list_for_each_entry(connection, &interface->connections, - interface_links) { - ret = gb_connection_init(connection); - if (ret) - break; - } - - return ret; -} - -void gb_interface_connections_exit(struct gb_interface *interface) -{ - struct gb_connection *connection; - struct gb_connection *next; - - list_for_each_entry_safe(connection, next, &interface->connections, - interface_links) { - gb_connection_exit(connection); - gb_connection_destroy(connection); - } -} diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h deleted file mode 100644 index c0c66b851c5d..000000000000 --- a/drivers/staging/greybus/interface.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Greybus interfaces - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __INTERFACE_H -#define __INTERFACE_H - -#include - -struct gb_interface { - struct device dev; - struct gb_interface_block *gb_ib; - u8 id; - u8 device_id; - struct list_head connections; - - struct list_head links; /* module->interfaces */ -}; -#define to_gb_interface(d) container_of(d, struct gb_interface, dev) - -struct gb_interface *gb_interface_create(struct gb_interface_block *gb_ib, u8 module_id); -void gb_interface_destroy(struct gb_interface_block *gb_ib); -int gb_interface_init(struct gb_interface_block *gb_ib, u8 module_id, u8 device_id); - -struct gb_interface *gb_interface_find(struct gb_interface_block *gb_ib, u8 interface_id); - -int gb_interface_connections_init(struct gb_interface *interface); -void gb_interface_connections_exit(struct gb_interface *interface); - -#endif /* __INTERFACE_H */ -- cgit v1.2.3-59-g8ed1b From f9b1df64a9f98e9c2fe38bec86b11740743e3661 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 12 Dec 2014 17:10:18 -0500 Subject: greybus: es1-ap-usb: don't protest when getting -EPROTO USB errors -EPROTO happens when devices are starting to go away in a system, or there is something wrong on the USB connection. Either way, it's safe to resubmit the urb for this error, don't complain to userspace about this, as the user will see this for every device removed, which looks scary, but means nothing. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/es1-ap-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c index 41f56da747c5..27416e440ede 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -352,7 +352,7 @@ static void svc_in_callback(struct urb *urb) int retval; if (status) { - if (status == -EAGAIN) + if ((status == -EAGAIN) || (status == -EPROTO)) goto exit; dev_err(dev, "urb svc in error %d (dropped)\n", status); return; @@ -380,7 +380,7 @@ static void cport_in_callback(struct urb *urb) u8 *data; if (status) { - if (status == -EAGAIN) + if ((status == -EAGAIN) || (status == -EPROTO)) goto exit; dev_err(dev, "urb cport in error %d (dropped)\n", status); return; -- cgit v1.2.3-59-g8ed1b From 1db0a5ff3a32c78082e7ef0264e71aa8afa3be96 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 12 Dec 2014 17:10:17 -0500 Subject: greybus: bundle: s/gb_interface/gb_bundle/g Rename struct gb_interface to struct gb_bundle It's a lot of renaming, some structures got renamed and also some fields, but the goal was to rename things to make sense with the new naming of how the system is put together in the 'driver model' view. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs-bus-greybus | 2 +- drivers/staging/greybus/ap.c | 8 +- drivers/staging/greybus/battery-gb.c | 2 +- drivers/staging/greybus/bundle.c | 155 ++++++++++----------- drivers/staging/greybus/bundle.h | 27 ++-- drivers/staging/greybus/connection.c | 24 ++-- drivers/staging/greybus/connection.h | 8 +- drivers/staging/greybus/core.c | 16 +-- drivers/staging/greybus/greybus.h | 8 +- drivers/staging/greybus/greybus_manifest.h | 4 +- drivers/staging/greybus/interface_block.c | 2 +- drivers/staging/greybus/manifest.c | 40 +++--- 12 files changed, 145 insertions(+), 151 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 2491201848c7..748ea3eed6fd 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -59,4 +59,4 @@ Date: December 2014 KernelVersion: 3.XX Contact: Greg Kroah-Hartman Description: - The device id of a Greybus interface. + The device id of a Greybus bundle. diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 4fa2ad9b3b0a..a5ac2bba26ba 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -61,7 +61,7 @@ static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) } -int svc_set_route_send(struct gb_interface *interface, +int svc_set_route_send(struct gb_bundle *bundle, struct greybus_host_device *hd) { struct svc_msg *svc_msg; @@ -73,7 +73,7 @@ int svc_set_route_send(struct gb_interface *interface, svc_msg->header.message_type = SVC_MSG_DATA; svc_msg->header.payload_length = cpu_to_le16(sizeof(struct svc_function_unipro_set_route)); - svc_msg->management.set_route.device_id = interface->device_id; + svc_msg->management.set_route.device_id = bundle->device_id; return svc_msg_send(svc_msg, hd); } @@ -145,12 +145,12 @@ static void svc_management(struct svc_function_unipro_management *management, management->link_up.module_id); return; } - ret = gb_interface_init(gb_ib, + ret = gb_bundle_init(gb_ib, management->link_up.interface_id, management->link_up.device_id); if (ret) dev_err(hd->parent, "error %d initializing " - "interface block %hhu interface %hhu\n", + "interface block %hhu bundle %hhu\n", ret, management->link_up.module_id, management->link_up.interface_id); break; diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 3ed7639b2cad..83cb64245da7 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -341,7 +341,7 @@ static int gb_battery_connection_init(struct gb_connection *connection) b->num_properties = ARRAY_SIZE(battery_props), b->get_property = get_property, - retval = power_supply_register(&connection->interface->gb_ib->dev, b); + retval = power_supply_register(&connection->bundle->gb_ib->dev, b); if (retval) { kfree(gb); return retval; diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 742781ceb135..885461598c94 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -1,5 +1,5 @@ /* - * Greybus interfaces + * Greybus bundles * * Copyright 2014 Google Inc. * Copyright 2014 Linaro Ltd. @@ -9,131 +9,128 @@ #include "greybus.h" +static void gb_bundle_connections_exit(struct gb_bundle *bundle); +static int gb_bundle_connections_init(struct gb_bundle *bundle); + + static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gb_interface *interface = to_gb_interface(dev); + struct gb_bundle *bundle = to_gb_bundle(dev); - return sprintf(buf, "%d", interface->device_id); + return sprintf(buf, "%d", bundle->device_id); } static DEVICE_ATTR_RO(device_id); -static struct attribute *interface_attrs[] = { +static struct attribute *bundle_attrs[] = { &dev_attr_device_id.attr, NULL, }; -ATTRIBUTE_GROUPS(interface); +ATTRIBUTE_GROUPS(bundle); -static void gb_interface_release(struct device *dev) +static void gb_bundle_release(struct device *dev) { - struct gb_interface *interface = to_gb_interface(dev); + struct gb_bundle *bundle = to_gb_bundle(dev); - kfree(interface); + kfree(bundle); } -struct device_type greybus_interface_type = { - .name = "greybus_interface", - .release = gb_interface_release, +struct device_type greybus_bundle_type = { + .name = "greybus_bundle", + .release = gb_bundle_release, }; /* XXX This could be per-host device or per-module */ -static DEFINE_SPINLOCK(gb_interfaces_lock); +static DEFINE_SPINLOCK(gb_bundles_lock); /* - * A Greybus interface represents a UniPro device present on a - * module. For Project Ara, each active Interface Block on a module - * implements a UniPro device, and therefore a Greybus interface. A - * Greybus module has at least one interface, but can have two (or - * even more). - * - * Create a gb_interface structure to represent a discovered - * interface. Returns a pointer to the new interface or a null + * Create a gb_bundle structure to represent a discovered + * bundle. Returns a pointer to the new bundle or a null * pointer if a failure occurs due to memory exhaustion. */ -struct gb_interface * -gb_interface_create(struct gb_interface_block *gb_ib, u8 interface_id) +struct gb_bundle *gb_bundle_create(struct gb_interface_block *gb_ib, u8 interface_id) { - struct gb_interface *interface; + struct gb_bundle *bundle; int retval; - interface = kzalloc(sizeof(*interface), GFP_KERNEL); - if (!interface) + bundle = kzalloc(sizeof(*bundle), GFP_KERNEL); + if (!bundle) return NULL; - interface->gb_ib = gb_ib; - interface->id = interface_id; - interface->device_id = 0xff; /* Invalid device id to start with */ - INIT_LIST_HEAD(&interface->connections); + bundle->gb_ib = gb_ib; + bundle->id = interface_id; + bundle->device_id = 0xff; /* Invalid device id to start with */ + INIT_LIST_HEAD(&bundle->connections); - /* Build up the interface device structures and register it with the + /* Build up the bundle device structures and register it with the * driver core */ - interface->dev.parent = &gb_ib->dev; - interface->dev.bus = &greybus_bus_type; - interface->dev.type = &greybus_interface_type; - interface->dev.groups = interface_groups; - device_initialize(&interface->dev); - dev_set_name(&interface->dev, "%d:%d", gb_ib->module_id, interface_id); - - retval = device_add(&interface->dev); + bundle->dev.parent = &gb_ib->dev; + bundle->dev.bus = &greybus_bus_type; + bundle->dev.type = &greybus_bundle_type; + bundle->dev.groups = bundle_groups; + device_initialize(&bundle->dev); + dev_set_name(&bundle->dev, "%d:%d", gb_ib->module_id, interface_id); + + retval = device_add(&bundle->dev); if (retval) { - pr_err("failed to add interface device for id 0x%02hhx\n", + pr_err("failed to add bundle device for id 0x%02hhx\n", interface_id); - put_device(&interface->dev); - kfree(interface); + put_device(&bundle->dev); + kfree(bundle); return NULL; } - spin_lock_irq(&gb_interfaces_lock); - list_add_tail(&interface->links, &gb_ib->interfaces); - spin_unlock_irq(&gb_interfaces_lock); + spin_lock_irq(&gb_bundles_lock); + list_add_tail(&bundle->links, &gb_ib->interfaces); + spin_unlock_irq(&gb_bundles_lock); - return interface; + return bundle; } /* - * Tear down a previously set up interface. + * Tear down a previously set up bundle. */ -void gb_interface_destroy(struct gb_interface_block *gb_ib) +void gb_bundle_destroy(struct gb_interface_block *gb_ib) { - struct gb_interface *interface; - struct gb_interface *temp; + struct gb_bundle *bundle; + struct gb_bundle *temp; if (WARN_ON(!gb_ib)) return; - spin_lock_irq(&gb_interfaces_lock); - list_for_each_entry_safe(interface, temp, &gb_ib->interfaces, links) { - list_del(&interface->links); - gb_interface_connections_exit(interface); - device_del(&interface->dev); + spin_lock_irq(&gb_bundles_lock); + list_for_each_entry_safe(bundle, temp, &gb_ib->interfaces, links) { + list_del(&bundle->links); + gb_bundle_connections_exit(bundle); + device_del(&bundle->dev); } - spin_unlock_irq(&gb_interfaces_lock); + spin_unlock_irq(&gb_bundles_lock); } -int gb_interface_init(struct gb_interface_block *gb_ib, u8 interface_id, u8 device_id) +int gb_bundle_init(struct gb_interface_block *gb_ib, u8 bundle_id, u8 device_id) { - struct gb_interface *interface; + struct gb_bundle *bundle; int ret; - interface = gb_interface_find(gb_ib, interface_id); - if (!interface) { - dev_err(gb_ib->hd->parent, "module %hhu not found\n", - interface_id); + bundle = gb_bundle_find(gb_ib, bundle_id); + if (!bundle) { + dev_err(gb_ib->hd->parent, "bundle %hhu not found\n", + bundle_id); return -ENOENT; } - interface->device_id = device_id; + bundle->device_id = device_id; - ret = svc_set_route_send(interface, gb_ib->hd); + ret = svc_set_route_send(bundle, gb_ib->hd); if (ret) { dev_err(gb_ib->hd->parent, "failed to set route (%d)\n", ret); return ret; } - ret = gb_interface_connections_init(interface); + ret = gb_bundle_connections_init(bundle); if (ret) { - dev_err(gb_ib->hd->parent, "module interface init error %d\n", + dev_err(gb_ib->hd->parent, "interface bundle init error %d\n", ret); /* XXX clear route */ return ret; @@ -142,29 +139,27 @@ int gb_interface_init(struct gb_interface_block *gb_ib, u8 interface_id, u8 devi return 0; } -struct gb_interface *gb_interface_find(struct gb_interface_block *gb_ib, - u8 interface_id) +struct gb_bundle *gb_bundle_find(struct gb_interface_block *gb_ib, u8 bundle_id) { - struct gb_interface *interface; + struct gb_bundle *bundle; - spin_lock_irq(&gb_interfaces_lock); - list_for_each_entry(interface, &gb_ib->interfaces, links) - if (interface->id == interface_id) { - spin_unlock_irq(&gb_interfaces_lock); - return interface; + spin_lock_irq(&gb_bundles_lock); + list_for_each_entry(bundle, &gb_ib->interfaces, links) + if (bundle->id == bundle_id) { + spin_unlock_irq(&gb_bundles_lock); + return bundle; } - spin_unlock_irq(&gb_interfaces_lock); + spin_unlock_irq(&gb_bundles_lock); return NULL; } -int gb_interface_connections_init(struct gb_interface *interface) +static int gb_bundle_connections_init(struct gb_bundle *bundle) { struct gb_connection *connection; int ret = 0; - list_for_each_entry(connection, &interface->connections, - interface_links) { + list_for_each_entry(connection, &bundle->connections, bundle_links) { ret = gb_connection_init(connection); if (ret) break; @@ -173,13 +168,13 @@ int gb_interface_connections_init(struct gb_interface *interface) return ret; } -void gb_interface_connections_exit(struct gb_interface *interface) +static void gb_bundle_connections_exit(struct gb_bundle *bundle) { struct gb_connection *connection; struct gb_connection *next; - list_for_each_entry_safe(connection, next, &interface->connections, - interface_links) { + list_for_each_entry_safe(connection, next, &bundle->connections, + bundle_links) { gb_connection_exit(connection); gb_connection_destroy(connection); } diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index c0c66b851c5d..ad14a47d64a8 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -1,5 +1,5 @@ /* - * Greybus interfaces + * Greybus bundles * * Copyright 2014 Google Inc. * Copyright 2014 Linaro Ltd. @@ -7,29 +7,28 @@ * Released under the GPLv2 only. */ -#ifndef __INTERFACE_H -#define __INTERFACE_H +#ifndef __BUNDLE_H +#define __BUNDLE_H #include -struct gb_interface { +/* Greybus "public" definitions" */ +struct gb_bundle { struct device dev; struct gb_interface_block *gb_ib; u8 id; u8 device_id; struct list_head connections; - struct list_head links; /* module->interfaces */ + struct list_head links; /* interface->bundles */ }; -#define to_gb_interface(d) container_of(d, struct gb_interface, dev) +#define to_gb_bundle(d) container_of(d, struct gb_bundle, dev) -struct gb_interface *gb_interface_create(struct gb_interface_block *gb_ib, u8 module_id); -void gb_interface_destroy(struct gb_interface_block *gb_ib); -int gb_interface_init(struct gb_interface_block *gb_ib, u8 module_id, u8 device_id); +/* Greybus "private" definitions" */ +struct gb_bundle *gb_bundle_create(struct gb_interface_block *gb_ib, u8 module_id); +void gb_bundle_destroy(struct gb_interface_block *gb_ib); +int gb_bundle_init(struct gb_interface_block *gb_ib, u8 module_id, u8 device_id); -struct gb_interface *gb_interface_find(struct gb_interface_block *gb_ib, u8 interface_id); +struct gb_bundle *gb_bundle_find(struct gb_interface_block *gb_ib, u8 bundle_id); -int gb_interface_connections_init(struct gb_interface *interface); -void gb_interface_connections_exit(struct gb_interface *interface); - -#endif /* __INTERFACE_H */ +#endif /* __BUNDLE_H */ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1658fdb5f10b..2a54b3e067ba 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -32,7 +32,7 @@ struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, /* * Callback from the host driver to let us know that data has been - * received on the interface. + * received on the bundle. */ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length) @@ -135,7 +135,7 @@ struct device_type greybus_connection_type = { * Returns a pointer to the new connection if successful, or a null * pointer otherwise. */ -struct gb_connection *gb_connection_create(struct gb_interface *interface, +struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id) { struct gb_connection *connection; @@ -156,7 +156,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, return NULL; } - hd = interface->gb_ib->hd; + hd = bundle->gb_ib->hd; connection->hd = hd; if (!gb_connection_hd_cport_id_alloc(connection)) { gb_protocol_put(connection->protocol); @@ -164,17 +164,17 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, return NULL; } - connection->interface = interface; - connection->interface_cport_id = cport_id; + connection->bundle = bundle; + connection->bundle_cport_id = cport_id; connection->state = GB_CONNECTION_STATE_DISABLED; - connection->dev.parent = &interface->dev; + connection->dev.parent = &bundle->dev; connection->dev.bus = &greybus_bus_type; connection->dev.type = &greybus_connection_type; connection->dev.groups = connection_groups; device_initialize(&connection->dev); dev_set_name(&connection->dev, "%s:%d", - dev_name(&interface->dev), cport_id); + dev_name(&bundle->dev), cport_id); retval = device_add(&connection->dev); if (retval) { @@ -189,7 +189,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, spin_lock_irq(&gb_connections_lock); list_add_tail(&connection->hd_links, &hd->connections); - list_add_tail(&connection->interface_links, &interface->connections); + list_add_tail(&connection->bundle_links, &bundle->connections); spin_unlock_irq(&gb_connections_lock); atomic_set(&connection->op_cycle, 0); @@ -216,7 +216,7 @@ void gb_connection_destroy(struct gb_connection *connection) gb_operation_cancel(operation, -ESHUTDOWN); } spin_lock_irq(&gb_connections_lock); - list_del(&connection->interface_links); + list_del(&connection->bundle_links); list_del(&connection->hd_links); spin_unlock_irq(&gb_connections_lock); @@ -237,9 +237,9 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) vaf.va = &args; pr_err("greybus: [%hhu:%hhu:%hu]: %pV\n", - connection->interface->gb_ib->module_id, - connection->interface->id, - connection->interface_cport_id, &vaf); + connection->bundle->gb_ib->module_id, + connection->bundle->id, + connection->bundle_cport_id, &vaf); va_end(args); } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 5c3fad3f46eb..caf52b8ef676 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -24,13 +24,13 @@ enum gb_connection_state { struct gb_connection { struct greybus_host_device *hd; - struct gb_interface *interface; + struct gb_bundle *bundle; struct device dev; u16 hd_cport_id; - u16 interface_cport_id; + u16 bundle_cport_id; struct list_head hd_links; - struct list_head interface_links; + struct list_head bundle_links; struct gb_protocol *protocol; @@ -43,7 +43,7 @@ struct gb_connection { }; #define to_gb_connection(d) container_of(d, struct gb_connection, dev) -struct gb_connection *gb_connection_create(struct gb_interface *interface, +struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id); void gb_connection_destroy(struct gb_connection *connection); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 032710ced7ce..eef28e1afaf1 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -47,18 +47,18 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { struct gb_interface_block *gb_ib = NULL; - struct gb_interface *interface = NULL; + struct gb_bundle *bundle = NULL; struct gb_connection *connection = NULL; if (is_gb_interface_block(dev)) { gb_ib = to_gb_interface_block(dev); - } else if (is_gb_interface(dev)) { - interface = to_gb_interface(dev); - gb_ib = interface->gb_ib; + } else if (is_gb_bundle(dev)) { + bundle = to_gb_bundle(dev); + gb_ib = bundle->gb_ib; } else if (is_gb_connection(dev)) { connection = to_gb_connection(dev); - interface = connection->interface; - gb_ib = interface->gb_ib; + bundle = connection->bundle; + gb_ib = bundle->gb_ib; } else { dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n"); return -EINVAL; @@ -70,9 +70,9 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } - if (interface) { + if (bundle) { // FIXME - // add a uevent that can "load" a interface type + // add a uevent that can "load" a bundle type // This is what we need to bind a driver to so use the info // in gmod here as well return 0; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f243dd2c1835..230cc2859b27 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -172,11 +172,11 @@ extern struct bus_type greybus_bus_type; int gb_uart_device_init(struct gb_connection *connection); void gb_uart_device_exit(struct gb_connection *connection); -int svc_set_route_send(struct gb_interface *interface, +int svc_set_route_send(struct gb_bundle *bundle, struct greybus_host_device *hd); extern struct device_type greybus_interface_block_type; -extern struct device_type greybus_interface_type; +extern struct device_type greybus_bundle_type; extern struct device_type greybus_connection_type; static inline int is_gb_interface_block(const struct device *dev) @@ -184,9 +184,9 @@ static inline int is_gb_interface_block(const struct device *dev) return dev->type == &greybus_interface_block_type; } -static inline int is_gb_interface(const struct device *dev) +static inline int is_gb_bundle(const struct device *dev) { - return dev->type == &greybus_interface_type; + return dev->type == &greybus_bundle_type; } static inline int is_gb_connection(const struct device *dev) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index deff5c71d3fd..398630ce8ac4 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -106,13 +106,13 @@ struct greybus_descriptor_interface { }; /* - * A CPort descriptor indicates the id of the interface within the + * A CPort descriptor indicates the id of the bundle within the * module it's associated with, along with the CPort id used to * address the CPort. The protocol id defines the format of messages * exchanged using the CPort. */ struct greybus_descriptor_cport { - __u8 interface; + __u8 bundle; __le16 id; __u8 protocol_id; /* enum greybus_protocol */ }; diff --git a/drivers/staging/greybus/interface_block.c b/drivers/staging/greybus/interface_block.c index ab4c833ef0a0..ee51613ab92d 100644 --- a/drivers/staging/greybus/interface_block.c +++ b/drivers/staging/greybus/interface_block.c @@ -164,7 +164,7 @@ static void gb_ib_destroy(struct gb_interface_block *gb_ib) list_del(&gb_ib->links); spin_unlock_irq(&gb_modules_lock); - gb_interface_destroy(gb_ib); + gb_bundle_destroy(gb_ib); kfree(gb_ib->product_string); kfree(gb_ib->vendor_string); diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 01a69a3bfa02..7661ca74acf3 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -172,10 +172,10 @@ static char *gb_string_get(u8 string_id) /* * Find cport descriptors in the manifest and set up data structures - * for the functions that use them. Returns the number of interfaces - * set up for the given module, or 0 if there is an error. + * for the functions that use them. Returns the number of bundles + * set up for the given interface, or 0 if there is an error. */ -static u32 gb_manifest_parse_cports(struct gb_interface *interface) +static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) { u32 count = 0; @@ -190,7 +190,7 @@ static u32 gb_manifest_parse_cports(struct gb_interface *interface) list_for_each_entry(descriptor, &manifest_descs, links) { if (descriptor->type == GREYBUS_TYPE_CPORT) { desc_cport = descriptor->data; - if (desc_cport->interface == interface->id) { + if (desc_cport->bundle == bundle->id) { found = true; break; } @@ -202,7 +202,7 @@ static u32 gb_manifest_parse_cports(struct gb_interface *interface) /* Found one. Set up its function structure */ protocol_id = desc_cport->protocol_id; cport_id = le16_to_cpu(desc_cport->id); - if (!gb_connection_create(interface, cport_id, protocol_id)) + if (!gb_connection_create(bundle, cport_id, protocol_id)) return 0; /* Error */ count++; @@ -214,21 +214,21 @@ static u32 gb_manifest_parse_cports(struct gb_interface *interface) } /* - * Find interface descriptors in the manifest and set up their data - * structures. Returns the number of interfaces set up for the + * Find bundle descriptors in the manifest and set up their data + * structures. Returns the number of bundles set up for the * given module. */ -static u32 gb_manifest_parse_interfaces(struct gb_interface_block *gb_ib) +static u32 gb_manifest_parse_bundles(struct gb_interface_block *gb_ib) { u32 count = 0; while (true) { struct manifest_desc *descriptor; struct greybus_descriptor_interface *desc_interface; - struct gb_interface *interface; + struct gb_bundle *bundle; bool found = false; - /* Find an interface descriptor */ + /* Find an bundle descriptor */ list_for_each_entry(descriptor, &manifest_descs, links) { if (descriptor->type == GREYBUS_TYPE_INTERFACE) { found = true; @@ -238,19 +238,19 @@ static u32 gb_manifest_parse_interfaces(struct gb_interface_block *gb_ib) if (!found) break; - /* Found one. Set up its interface structure*/ + /* Found one. Set up its bundle structure*/ desc_interface = descriptor->data; - interface = gb_interface_create(gb_ib, desc_interface->id); - if (!interface) + bundle = gb_bundle_create(gb_ib, desc_interface->id); + if (!bundle) return 0; /* Error */ - /* Now go set up this interface's functions and cports */ - if (!gb_manifest_parse_cports(interface)) + /* Now go set up this bundle's functions and cports */ + if (!gb_manifest_parse_cports(bundle)) return 0; /* Error parsing cports */ count++; - /* Done with this interface descriptor */ + /* Done with this bundle descriptor */ release_manifest_descriptor(descriptor); } @@ -279,9 +279,9 @@ static bool gb_manifest_parse_module(struct gb_interface_block *gb_ib, /* Release the module descriptor, now that we're done with it */ release_manifest_descriptor(module_desc); - /* A module must have at least one interface descriptor */ - if (!gb_manifest_parse_interfaces(gb_ib)) { - pr_err("manifest interface descriptors not valid\n"); + /* An interface must have at least one bundle descriptor */ + if (!gb_manifest_parse_bundles(gb_ib)) { + pr_err("manifest bundle descriptors not valid\n"); goto out_err; } @@ -314,7 +314,7 @@ out_free_vendor_string: * information it contains, and then remove that descriptor (and any * string descriptors it refers to) from further consideration. * - * After that we look for the module's interfaces--there must be at + * After that we look for the interface block's bundles--there must be at * least one of those. * * Returns true if parsing was successful, false otherwise. -- cgit v1.2.3-59-g8ed1b From e232b791d5a4c597626fbbd02f613a8a5474d7c4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Dec 2014 14:56:29 -0800 Subject: greybus: interface_block.c: rename the "interfaces" list "bundles" Alex pointed out one rename I missed previously, this fixes up the interface_block list of bundles name. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 6 +++--- drivers/staging/greybus/interface_block.c | 2 +- drivers/staging/greybus/interface_block.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 885461598c94..bad6cf6d17fc 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -83,7 +83,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface_block *gb_ib, u8 interfac } spin_lock_irq(&gb_bundles_lock); - list_add_tail(&bundle->links, &gb_ib->interfaces); + list_add_tail(&bundle->links, &gb_ib->bundles); spin_unlock_irq(&gb_bundles_lock); return bundle; @@ -101,7 +101,7 @@ void gb_bundle_destroy(struct gb_interface_block *gb_ib) return; spin_lock_irq(&gb_bundles_lock); - list_for_each_entry_safe(bundle, temp, &gb_ib->interfaces, links) { + list_for_each_entry_safe(bundle, temp, &gb_ib->bundles, links) { list_del(&bundle->links); gb_bundle_connections_exit(bundle); device_del(&bundle->dev); @@ -144,7 +144,7 @@ struct gb_bundle *gb_bundle_find(struct gb_interface_block *gb_ib, u8 bundle_id) struct gb_bundle *bundle; spin_lock_irq(&gb_bundles_lock); - list_for_each_entry(bundle, &gb_ib->interfaces, links) + list_for_each_entry(bundle, &gb_ib->bundles, links) if (bundle->id == bundle_id) { spin_unlock_irq(&gb_bundles_lock); return bundle; diff --git a/drivers/staging/greybus/interface_block.c b/drivers/staging/greybus/interface_block.c index ee51613ab92d..f47c8fc18433 100644 --- a/drivers/staging/greybus/interface_block.c +++ b/drivers/staging/greybus/interface_block.c @@ -126,7 +126,7 @@ static struct gb_interface_block *gb_ib_create(struct greybus_host_device *hd, gb_ib->hd = hd; /* XXX refcount? */ gb_ib->module_id = module_id; - INIT_LIST_HEAD(&gb_ib->interfaces); + INIT_LIST_HEAD(&gb_ib->bundles); gb_ib->dev.parent = hd->parent; gb_ib->dev.bus = &greybus_bus_type; diff --git a/drivers/staging/greybus/interface_block.h b/drivers/staging/greybus/interface_block.h index 03da567c7ac9..e0846d8ee84f 100644 --- a/drivers/staging/greybus/interface_block.h +++ b/drivers/staging/greybus/interface_block.h @@ -19,7 +19,7 @@ struct gb_interface_block { struct device dev; - struct list_head interfaces; + struct list_head bundles; struct list_head links; /* greybus_host_device->modules */ u8 module_id; /* Physical location within the Endo */ -- cgit v1.2.3-59-g8ed1b From a93938a23d4d0210d06ea106a132bcab491d3885 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Dec 2014 14:56:30 -0800 Subject: greybus: rename interface_block.[c|h] -> interface.[c|h] Interface_block is being renamed to interface, so move the file first. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/interface.c | 233 ++++++++++++++++++++++++++++++ drivers/staging/greybus/interface.h | 58 ++++++++ drivers/staging/greybus/interface_block.c | 233 ------------------------------ drivers/staging/greybus/interface_block.h | 58 -------- 6 files changed, 293 insertions(+), 293 deletions(-) create mode 100644 drivers/staging/greybus/interface.c create mode 100644 drivers/staging/greybus/interface.h delete mode 100644 drivers/staging/greybus/interface_block.c delete mode 100644 drivers/staging/greybus/interface_block.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 08cdaf1affb8..6c0b0ca5be38 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -2,7 +2,7 @@ greybus-y := core.o \ debugfs.o \ ap.o \ manifest.o \ - interface_block.o \ + interface.o \ bundle.o \ connection.o \ protocol.o \ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 230cc2859b27..e0c5eb7bd13f 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -24,7 +24,7 @@ #include "greybus_id.h" #include "greybus_manifest.h" #include "manifest.h" -#include "interface_block.h" +#include "interface.h" #include "bundle.h" #include "connection.h" #include "protocol.h" diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c new file mode 100644 index 000000000000..f47c8fc18433 --- /dev/null +++ b/drivers/staging/greybus/interface.c @@ -0,0 +1,233 @@ +/* + * Greybus interface block code + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +/* interface block sysfs attributes */ +#define gb_ib_attr(field, type) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct gb_interface_block *gb_ib = to_gb_interface_block(dev); \ + return sprintf(buf, "%"#type"\n", gb_ib->field); \ +} \ +static DEVICE_ATTR_RO(field) + +gb_ib_attr(vendor, x); +gb_ib_attr(product, x); +gb_ib_attr(unique_id, llX); +gb_ib_attr(vendor_string, s); +gb_ib_attr(product_string, s); + +static struct attribute *interface_block_attrs[] = { + &dev_attr_vendor.attr, + &dev_attr_product.attr, + &dev_attr_unique_id.attr, + &dev_attr_vendor_string.attr, + &dev_attr_product_string.attr, + NULL, +}; +ATTRIBUTE_GROUPS(interface_block); + + +/* XXX This could be per-host device */ +static DEFINE_SPINLOCK(gb_modules_lock); + +static int gb_ib_match_one_id(struct gb_interface_block *gb_ib, + const struct greybus_interface_block_id *id) +{ + if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && + (id->vendor != gb_ib->vendor)) + return 0; + + if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && + (id->product != gb_ib->product)) + return 0; + + if ((id->match_flags & GREYBUS_ID_MATCH_SERIAL) && + (id->unique_id != gb_ib->unique_id)) + return 0; + + return 1; +} + +const struct greybus_interface_block_id * +gb_ib_match_id(struct gb_interface_block *gb_ib, + const struct greybus_interface_block_id *id) +{ + if (id == NULL) + return NULL; + + for (; id->vendor || id->product || id->unique_id || + id->driver_info; id++) { + if (gb_ib_match_one_id(gb_ib, id)) + return id; + } + + return NULL; +} + +struct gb_interface_block *gb_ib_find(struct greybus_host_device *hd, u8 module_id) +{ + struct gb_interface_block *gb_ib; + + list_for_each_entry(gb_ib, &hd->modules, links) + if (gb_ib->module_id == module_id) + return gb_ib; + + return NULL; +} + +static void greybus_ib_release(struct device *dev) +{ + struct gb_interface_block *gb_ib = to_gb_interface_block(dev); + + kfree(gb_ib); +} + +struct device_type greybus_interface_block_type = { + .name = "greybus_interface_block", + .release = greybus_ib_release, +}; + +/* + * A Greybus module represents a user-replicable component on an Ara + * phone. An interface block is the physical connection on that module. A + * module may have more than one interface block. + * + * Create a gb_interface_block structure to represent a discovered module. + * The position within the Endo is encoded in the "module_id" argument. + * Returns a pointer to the new module or a null pointer if a + * failure occurs due to memory exhaustion. + */ +static struct gb_interface_block *gb_ib_create(struct greybus_host_device *hd, + u8 module_id) +{ + struct gb_interface_block *gb_ib; + int retval; + + gb_ib = gb_ib_find(hd, module_id); + if (gb_ib) { + dev_err(hd->parent, "Duplicate module id %d will not be created\n", + module_id); + return NULL; + } + + gb_ib = kzalloc(sizeof(*gb_ib), GFP_KERNEL); + if (!gb_ib) + return NULL; + + gb_ib->hd = hd; /* XXX refcount? */ + gb_ib->module_id = module_id; + INIT_LIST_HEAD(&gb_ib->bundles); + + gb_ib->dev.parent = hd->parent; + gb_ib->dev.bus = &greybus_bus_type; + gb_ib->dev.type = &greybus_interface_block_type; + gb_ib->dev.groups = interface_block_groups; + gb_ib->dev.dma_mask = hd->parent->dma_mask; + device_initialize(&gb_ib->dev); + dev_set_name(&gb_ib->dev, "%d", module_id); + + retval = device_add(&gb_ib->dev); + if (retval) { + pr_err("failed to add module device for id 0x%02hhx\n", + module_id); + put_device(&gb_ib->dev); + kfree(gb_ib); + return NULL; + } + + spin_lock_irq(&gb_modules_lock); + list_add_tail(&gb_ib->links, &hd->modules); + spin_unlock_irq(&gb_modules_lock); + + return gb_ib; +} + +/* + * Tear down a previously set up module. + */ +static void gb_ib_destroy(struct gb_interface_block *gb_ib) +{ + if (WARN_ON(!gb_ib)) + return; + + spin_lock_irq(&gb_modules_lock); + list_del(&gb_ib->links); + spin_unlock_irq(&gb_modules_lock); + + gb_bundle_destroy(gb_ib); + + kfree(gb_ib->product_string); + kfree(gb_ib->vendor_string); + /* kref_put(module->hd); */ + + device_del(&gb_ib->dev); +} + +/** + * gb_add_module + * + * Pass in a buffer that _should_ contain a Greybus module manifest + * and register a greybus device structure with the kernel core. + */ +void gb_add_module(struct greybus_host_device *hd, u8 module_id, + u8 *data, int size) +{ + struct gb_interface_block *gb_ib; + + gb_ib = gb_ib_create(hd, module_id); + if (!gb_ib) { + dev_err(hd->parent, "failed to create interface block\n"); + return; + } + + /* + * Parse the manifest and build up our data structures + * representing what's in it. + */ + if (!gb_manifest_parse(gb_ib, data, size)) { + dev_err(hd->parent, "manifest error\n"); + goto err_module; + } + + /* + * XXX + * We've successfully parsed the manifest. Now we need to + * allocate CPort Id's for connecting to the CPorts found on + * other modules. For each of these, establish a connection + * between the local and remote CPorts (including + * configuring the switch to allow them to communicate). + */ + + return; + +err_module: + gb_ib_destroy(gb_ib); +} + +void gb_remove_module(struct greybus_host_device *hd, u8 module_id) +{ + struct gb_interface_block *gb_ib = gb_ib_find(hd, module_id); + + if (gb_ib) + gb_ib_destroy(gb_ib); + else + dev_err(hd->parent, "interface block id %d not found\n", module_id); +} + +void gb_remove_modules(struct greybus_host_device *hd) +{ + struct gb_interface_block *gb_ib, *temp; + + list_for_each_entry_safe(gb_ib, temp, &hd->modules, links) + gb_ib_destroy(gb_ib); +} diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h new file mode 100644 index 000000000000..5c7cebc17e2e --- /dev/null +++ b/drivers/staging/greybus/interface.h @@ -0,0 +1,58 @@ +/* + * Greybus Interface Block code + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __INTERFACE_H +#define __INTERFACE_H + +/* Increase these values if needed */ +#define MAX_CPORTS_PER_MODULE 10 +#define MAX_STRINGS_PER_MODULE 10 + + +/* Greybus "public" definitions" */ +struct gb_interface_block { + struct device dev; + + struct list_head bundles; + struct list_head links; /* greybus_host_device->modules */ + u8 module_id; /* Physical location within the Endo */ + + /* Information taken from the manifest module descriptor */ + u16 vendor; + u16 product; + char *vendor_string; + char *product_string; + u64 unique_id; + + struct greybus_host_device *hd; +}; +#define to_gb_interface_block(d) container_of(d, struct gb_interface_block, dev) + +static inline void +gb_interface_block_set_drvdata(struct gb_interface_block *gb_ib, void *data) +{ + dev_set_drvdata(&gb_ib->dev, data); +} + +static inline void * +gb_interface_block_get_drvdata(struct gb_interface_block *gb_ib) +{ + return dev_get_drvdata(&gb_ib->dev); +} + +/* Greybus "private" definitions */ + +const struct greybus_interface_block_id * + gb_ib_match_id(struct gb_interface_block *gb_ib, + const struct greybus_interface_block_id *id); + +struct gb_interface_block *gb_ib_find(struct greybus_host_device *hd, + u8 module_id); + +#endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/interface_block.c b/drivers/staging/greybus/interface_block.c deleted file mode 100644 index f47c8fc18433..000000000000 --- a/drivers/staging/greybus/interface_block.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Greybus interface block code - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include "greybus.h" - -/* interface block sysfs attributes */ -#define gb_ib_attr(field, type) \ -static ssize_t field##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - struct gb_interface_block *gb_ib = to_gb_interface_block(dev); \ - return sprintf(buf, "%"#type"\n", gb_ib->field); \ -} \ -static DEVICE_ATTR_RO(field) - -gb_ib_attr(vendor, x); -gb_ib_attr(product, x); -gb_ib_attr(unique_id, llX); -gb_ib_attr(vendor_string, s); -gb_ib_attr(product_string, s); - -static struct attribute *interface_block_attrs[] = { - &dev_attr_vendor.attr, - &dev_attr_product.attr, - &dev_attr_unique_id.attr, - &dev_attr_vendor_string.attr, - &dev_attr_product_string.attr, - NULL, -}; -ATTRIBUTE_GROUPS(interface_block); - - -/* XXX This could be per-host device */ -static DEFINE_SPINLOCK(gb_modules_lock); - -static int gb_ib_match_one_id(struct gb_interface_block *gb_ib, - const struct greybus_interface_block_id *id) -{ - if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && - (id->vendor != gb_ib->vendor)) - return 0; - - if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && - (id->product != gb_ib->product)) - return 0; - - if ((id->match_flags & GREYBUS_ID_MATCH_SERIAL) && - (id->unique_id != gb_ib->unique_id)) - return 0; - - return 1; -} - -const struct greybus_interface_block_id * -gb_ib_match_id(struct gb_interface_block *gb_ib, - const struct greybus_interface_block_id *id) -{ - if (id == NULL) - return NULL; - - for (; id->vendor || id->product || id->unique_id || - id->driver_info; id++) { - if (gb_ib_match_one_id(gb_ib, id)) - return id; - } - - return NULL; -} - -struct gb_interface_block *gb_ib_find(struct greybus_host_device *hd, u8 module_id) -{ - struct gb_interface_block *gb_ib; - - list_for_each_entry(gb_ib, &hd->modules, links) - if (gb_ib->module_id == module_id) - return gb_ib; - - return NULL; -} - -static void greybus_ib_release(struct device *dev) -{ - struct gb_interface_block *gb_ib = to_gb_interface_block(dev); - - kfree(gb_ib); -} - -struct device_type greybus_interface_block_type = { - .name = "greybus_interface_block", - .release = greybus_ib_release, -}; - -/* - * A Greybus module represents a user-replicable component on an Ara - * phone. An interface block is the physical connection on that module. A - * module may have more than one interface block. - * - * Create a gb_interface_block structure to represent a discovered module. - * The position within the Endo is encoded in the "module_id" argument. - * Returns a pointer to the new module or a null pointer if a - * failure occurs due to memory exhaustion. - */ -static struct gb_interface_block *gb_ib_create(struct greybus_host_device *hd, - u8 module_id) -{ - struct gb_interface_block *gb_ib; - int retval; - - gb_ib = gb_ib_find(hd, module_id); - if (gb_ib) { - dev_err(hd->parent, "Duplicate module id %d will not be created\n", - module_id); - return NULL; - } - - gb_ib = kzalloc(sizeof(*gb_ib), GFP_KERNEL); - if (!gb_ib) - return NULL; - - gb_ib->hd = hd; /* XXX refcount? */ - gb_ib->module_id = module_id; - INIT_LIST_HEAD(&gb_ib->bundles); - - gb_ib->dev.parent = hd->parent; - gb_ib->dev.bus = &greybus_bus_type; - gb_ib->dev.type = &greybus_interface_block_type; - gb_ib->dev.groups = interface_block_groups; - gb_ib->dev.dma_mask = hd->parent->dma_mask; - device_initialize(&gb_ib->dev); - dev_set_name(&gb_ib->dev, "%d", module_id); - - retval = device_add(&gb_ib->dev); - if (retval) { - pr_err("failed to add module device for id 0x%02hhx\n", - module_id); - put_device(&gb_ib->dev); - kfree(gb_ib); - return NULL; - } - - spin_lock_irq(&gb_modules_lock); - list_add_tail(&gb_ib->links, &hd->modules); - spin_unlock_irq(&gb_modules_lock); - - return gb_ib; -} - -/* - * Tear down a previously set up module. - */ -static void gb_ib_destroy(struct gb_interface_block *gb_ib) -{ - if (WARN_ON(!gb_ib)) - return; - - spin_lock_irq(&gb_modules_lock); - list_del(&gb_ib->links); - spin_unlock_irq(&gb_modules_lock); - - gb_bundle_destroy(gb_ib); - - kfree(gb_ib->product_string); - kfree(gb_ib->vendor_string); - /* kref_put(module->hd); */ - - device_del(&gb_ib->dev); -} - -/** - * gb_add_module - * - * Pass in a buffer that _should_ contain a Greybus module manifest - * and register a greybus device structure with the kernel core. - */ -void gb_add_module(struct greybus_host_device *hd, u8 module_id, - u8 *data, int size) -{ - struct gb_interface_block *gb_ib; - - gb_ib = gb_ib_create(hd, module_id); - if (!gb_ib) { - dev_err(hd->parent, "failed to create interface block\n"); - return; - } - - /* - * Parse the manifest and build up our data structures - * representing what's in it. - */ - if (!gb_manifest_parse(gb_ib, data, size)) { - dev_err(hd->parent, "manifest error\n"); - goto err_module; - } - - /* - * XXX - * We've successfully parsed the manifest. Now we need to - * allocate CPort Id's for connecting to the CPorts found on - * other modules. For each of these, establish a connection - * between the local and remote CPorts (including - * configuring the switch to allow them to communicate). - */ - - return; - -err_module: - gb_ib_destroy(gb_ib); -} - -void gb_remove_module(struct greybus_host_device *hd, u8 module_id) -{ - struct gb_interface_block *gb_ib = gb_ib_find(hd, module_id); - - if (gb_ib) - gb_ib_destroy(gb_ib); - else - dev_err(hd->parent, "interface block id %d not found\n", module_id); -} - -void gb_remove_modules(struct greybus_host_device *hd) -{ - struct gb_interface_block *gb_ib, *temp; - - list_for_each_entry_safe(gb_ib, temp, &hd->modules, links) - gb_ib_destroy(gb_ib); -} diff --git a/drivers/staging/greybus/interface_block.h b/drivers/staging/greybus/interface_block.h deleted file mode 100644 index e0846d8ee84f..000000000000 --- a/drivers/staging/greybus/interface_block.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Greybus Interface Block code - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __INTERFACE_BLOCK_H -#define __INTERFACE_BLOCK_H - -/* Increase these values if needed */ -#define MAX_CPORTS_PER_MODULE 10 -#define MAX_STRINGS_PER_MODULE 10 - - -/* Greybus "public" definitions" */ -struct gb_interface_block { - struct device dev; - - struct list_head bundles; - struct list_head links; /* greybus_host_device->modules */ - u8 module_id; /* Physical location within the Endo */ - - /* Information taken from the manifest module descriptor */ - u16 vendor; - u16 product; - char *vendor_string; - char *product_string; - u64 unique_id; - - struct greybus_host_device *hd; -}; -#define to_gb_interface_block(d) container_of(d, struct gb_interface_block, dev) - -static inline void -gb_interface_block_set_drvdata(struct gb_interface_block *gb_ib, void *data) -{ - dev_set_drvdata(&gb_ib->dev, data); -} - -static inline void * -gb_interface_block_get_drvdata(struct gb_interface_block *gb_ib) -{ - return dev_get_drvdata(&gb_ib->dev); -} - -/* Greybus "private" definitions */ - -const struct greybus_interface_block_id * - gb_ib_match_id(struct gb_interface_block *gb_ib, - const struct greybus_interface_block_id *id); - -struct gb_interface_block *gb_ib_find(struct greybus_host_device *hd, - u8 module_id); - -#endif /* __INTERFACE_BLOCK_H */ -- cgit v1.2.3-59-g8ed1b From 4ab9b3c24b009fdc55465977153c5deffd31b0c0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Dec 2014 14:56:31 -0800 Subject: greybus: interface: s/gb_interface_block/gb_interface/g Rename struct gb_interface_block to struct gb_interface Lots of renaming, and variable renames as well (gb_ib->intf), but all should be sane with regards to the new naming scheme we are using. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 12 +-- drivers/staging/greybus/battery-gb.c | 2 +- drivers/staging/greybus/bundle.c | 33 ++++---- drivers/staging/greybus/bundle.h | 10 +-- drivers/staging/greybus/connection.c | 4 +- drivers/staging/greybus/core.c | 24 +++--- drivers/staging/greybus/greybus.h | 14 +-- drivers/staging/greybus/interface.c | 159 ++++++++++++++++++----------------- drivers/staging/greybus/interface.h | 23 +++-- drivers/staging/greybus/manifest.c | 38 ++++----- drivers/staging/greybus/manifest.h | 4 +- drivers/staging/greybus/svc_msg.h | 9 +- 12 files changed, 165 insertions(+), 167 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index a5ac2bba26ba..f9c63e43acd7 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -124,7 +124,7 @@ static void svc_handshake(struct svc_function_handshake *handshake, static void svc_management(struct svc_function_unipro_management *management, int payload_length, struct greybus_host_device *hd) { - struct gb_interface_block *gb_ib; + struct gb_interface *intf; int ret; if (payload_length != sizeof(*management)) { @@ -139,18 +139,18 @@ static void svc_management(struct svc_function_unipro_management *management, hd->device_id = management->ap_id.device_id; break; case SVC_MANAGEMENT_LINK_UP: - gb_ib = gb_ib_find(hd, management->link_up.module_id); - if (!gb_ib) { + intf = gb_interface_find(hd, management->link_up.module_id); + if (!intf) { dev_err(hd->parent, "Module ID %d not found\n", management->link_up.module_id); return; } - ret = gb_bundle_init(gb_ib, + ret = gb_bundle_init(intf, management->link_up.interface_id, management->link_up.device_id); if (ret) - dev_err(hd->parent, "error %d initializing " - "interface block %hhu bundle %hhu\n", + dev_err(hd->parent, + "error %d initializing interface %hhu bundle %hhu\n", ret, management->link_up.module_id, management->link_up.interface_id); break; diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 83cb64245da7..df1d7ee08199 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -341,7 +341,7 @@ static int gb_battery_connection_init(struct gb_connection *connection) b->num_properties = ARRAY_SIZE(battery_props), b->get_property = get_property, - retval = power_supply_register(&connection->bundle->gb_ib->dev, b); + retval = power_supply_register(&connection->bundle->intf->dev, b); if (retval) { kfree(gb); return retval; diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index bad6cf6d17fc..2ac67a242c0f 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -50,7 +50,7 @@ static DEFINE_SPINLOCK(gb_bundles_lock); * bundle. Returns a pointer to the new bundle or a null * pointer if a failure occurs due to memory exhaustion. */ -struct gb_bundle *gb_bundle_create(struct gb_interface_block *gb_ib, u8 interface_id) +struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 interface_id) { struct gb_bundle *bundle; int retval; @@ -59,19 +59,19 @@ struct gb_bundle *gb_bundle_create(struct gb_interface_block *gb_ib, u8 interfac if (!bundle) return NULL; - bundle->gb_ib = gb_ib; + bundle->intf = intf; bundle->id = interface_id; bundle->device_id = 0xff; /* Invalid device id to start with */ INIT_LIST_HEAD(&bundle->connections); /* Build up the bundle device structures and register it with the * driver core */ - bundle->dev.parent = &gb_ib->dev; + bundle->dev.parent = &intf->dev; bundle->dev.bus = &greybus_bus_type; bundle->dev.type = &greybus_bundle_type; bundle->dev.groups = bundle_groups; device_initialize(&bundle->dev); - dev_set_name(&bundle->dev, "%d:%d", gb_ib->module_id, interface_id); + dev_set_name(&bundle->dev, "%d:%d", intf->module_id, interface_id); retval = device_add(&bundle->dev); if (retval) { @@ -83,7 +83,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface_block *gb_ib, u8 interfac } spin_lock_irq(&gb_bundles_lock); - list_add_tail(&bundle->links, &gb_ib->bundles); + list_add_tail(&bundle->links, &intf->bundles); spin_unlock_irq(&gb_bundles_lock); return bundle; @@ -92,16 +92,16 @@ struct gb_bundle *gb_bundle_create(struct gb_interface_block *gb_ib, u8 interfac /* * Tear down a previously set up bundle. */ -void gb_bundle_destroy(struct gb_interface_block *gb_ib) +void gb_bundle_destroy(struct gb_interface *intf) { struct gb_bundle *bundle; struct gb_bundle *temp; - if (WARN_ON(!gb_ib)) + if (WARN_ON(!intf)) return; spin_lock_irq(&gb_bundles_lock); - list_for_each_entry_safe(bundle, temp, &gb_ib->bundles, links) { + list_for_each_entry_safe(bundle, temp, &intf->bundles, links) { list_del(&bundle->links); gb_bundle_connections_exit(bundle); device_del(&bundle->dev); @@ -109,28 +109,27 @@ void gb_bundle_destroy(struct gb_interface_block *gb_ib) spin_unlock_irq(&gb_bundles_lock); } -int gb_bundle_init(struct gb_interface_block *gb_ib, u8 bundle_id, u8 device_id) +int gb_bundle_init(struct gb_interface *intf, u8 bundle_id, u8 device_id) { struct gb_bundle *bundle; int ret; - bundle = gb_bundle_find(gb_ib, bundle_id); + bundle = gb_bundle_find(intf, bundle_id); if (!bundle) { - dev_err(gb_ib->hd->parent, "bundle %hhu not found\n", - bundle_id); + dev_err(intf->hd->parent, "bundle %hhu not found\n", bundle_id); return -ENOENT; } bundle->device_id = device_id; - ret = svc_set_route_send(bundle, gb_ib->hd); + ret = svc_set_route_send(bundle, intf->hd); if (ret) { - dev_err(gb_ib->hd->parent, "failed to set route (%d)\n", ret); + dev_err(intf->hd->parent, "failed to set route (%d)\n", ret); return ret; } ret = gb_bundle_connections_init(bundle); if (ret) { - dev_err(gb_ib->hd->parent, "interface bundle init error %d\n", + dev_err(intf->hd->parent, "interface bundle init error %d\n", ret); /* XXX clear route */ return ret; @@ -139,12 +138,12 @@ int gb_bundle_init(struct gb_interface_block *gb_ib, u8 bundle_id, u8 device_id) return 0; } -struct gb_bundle *gb_bundle_find(struct gb_interface_block *gb_ib, u8 bundle_id) +struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id) { struct gb_bundle *bundle; spin_lock_irq(&gb_bundles_lock); - list_for_each_entry(bundle, &gb_ib->bundles, links) + list_for_each_entry(bundle, &intf->bundles, links) if (bundle->id == bundle_id) { spin_unlock_irq(&gb_bundles_lock); return bundle; diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index ad14a47d64a8..c3c66faac6fc 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -15,7 +15,7 @@ /* Greybus "public" definitions" */ struct gb_bundle { struct device dev; - struct gb_interface_block *gb_ib; + struct gb_interface *intf; u8 id; u8 device_id; struct list_head connections; @@ -25,10 +25,10 @@ struct gb_bundle { #define to_gb_bundle(d) container_of(d, struct gb_bundle, dev) /* Greybus "private" definitions" */ -struct gb_bundle *gb_bundle_create(struct gb_interface_block *gb_ib, u8 module_id); -void gb_bundle_destroy(struct gb_interface_block *gb_ib); -int gb_bundle_init(struct gb_interface_block *gb_ib, u8 module_id, u8 device_id); +struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 module_id); +void gb_bundle_destroy(struct gb_interface *intf); +int gb_bundle_init(struct gb_interface *intf, u8 module_id, u8 device_id); -struct gb_bundle *gb_bundle_find(struct gb_interface_block *gb_ib, u8 bundle_id); +struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id); #endif /* __BUNDLE_H */ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2a54b3e067ba..2d61ee788218 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -156,7 +156,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, return NULL; } - hd = bundle->gb_ib->hd; + hd = bundle->intf->hd; connection->hd = hd; if (!gb_connection_hd_cport_id_alloc(connection)) { gb_protocol_put(connection->protocol); @@ -237,7 +237,7 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) vaf.va = &args; pr_err("greybus: [%hhu:%hhu:%hu]: %pV\n", - connection->bundle->gb_ib->module_id, + connection->bundle->intf->module_id, connection->bundle->id, connection->bundle_cport_id, &vaf); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index eef28e1afaf1..a0e0af5c23d2 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -34,10 +34,10 @@ EXPORT_SYMBOL_GPL(greybus_disabled); static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(drv); - struct gb_interface_block *gb_ib = to_gb_interface_block(dev); + struct gb_interface *intf = to_gb_interface(dev); const struct greybus_interface_block_id *id; - id = gb_ib_match_id(gb_ib, driver->id_table); + id = gb_interface_match_id(intf, driver->id_table); if (id) return 1; /* FIXME - Dynamic ids? */ @@ -46,19 +46,19 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct gb_interface_block *gb_ib = NULL; + struct gb_interface *intf = NULL; struct gb_bundle *bundle = NULL; struct gb_connection *connection = NULL; - if (is_gb_interface_block(dev)) { - gb_ib = to_gb_interface_block(dev); + if (is_gb_interface(dev)) { + intf = to_gb_interface(dev); } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); - gb_ib = bundle->gb_ib; + intf = bundle->intf; } else if (is_gb_connection(dev)) { connection = to_gb_connection(dev); bundle = connection->bundle; - gb_ib = bundle->gb_ib; + intf = bundle->intf; } else { dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n"); return -EINVAL; @@ -94,16 +94,16 @@ struct bus_type greybus_bus_type = { static int greybus_probe(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct gb_interface_block *gb_ib = to_gb_interface_block(dev); + struct gb_interface *intf = to_gb_interface(dev); const struct greybus_interface_block_id *id; int retval; /* match id */ - id = gb_ib_match_id(gb_ib, driver->id_table); + id = gb_interface_match_id(intf, driver->id_table); if (!id) return -ENODEV; - retval = driver->probe(gb_ib, id); + retval = driver->probe(intf, id); if (retval) return retval; @@ -113,9 +113,9 @@ static int greybus_probe(struct device *dev) static int greybus_remove(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct gb_interface_block *gb_ib = to_gb_interface_block(dev); + struct gb_interface *intf = to_gb_interface(dev); - driver->disconnect(gb_ib); + driver->disconnect(intf); return 0; } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index e0c5eb7bd13f..bca0e6682eef 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -119,12 +119,12 @@ void greybus_remove_hd(struct greybus_host_device *hd); struct greybus_driver { const char *name; - int (*probe)(struct gb_interface_block *gb_ib, + int (*probe)(struct gb_interface *intf, const struct greybus_interface_block_id *id); - void (*disconnect)(struct gb_interface_block *gb_ib); + void (*disconnect)(struct gb_interface *intf); - int (*suspend)(struct gb_interface_block *gb_ib, pm_message_t message); - int (*resume)(struct gb_interface_block *gb_ib); + int (*suspend)(struct gb_interface *intf, pm_message_t message); + int (*resume)(struct gb_interface *intf); const struct greybus_interface_block_id *id_table; @@ -175,13 +175,13 @@ void gb_uart_device_exit(struct gb_connection *connection); int svc_set_route_send(struct gb_bundle *bundle, struct greybus_host_device *hd); -extern struct device_type greybus_interface_block_type; +extern struct device_type greybus_interface_type; extern struct device_type greybus_bundle_type; extern struct device_type greybus_connection_type; -static inline int is_gb_interface_block(const struct device *dev) +static inline int is_gb_interface(const struct device *dev) { - return dev->type == &greybus_interface_block_type; + return dev->type == &greybus_interface_type; } static inline int is_gb_bundle(const struct device *dev) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index f47c8fc18433..f2c935439a9b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -1,5 +1,5 @@ /* - * Greybus interface block code + * Greybus interface code * * Copyright 2014 Google Inc. * Copyright 2014 Linaro Ltd. @@ -9,24 +9,24 @@ #include "greybus.h" -/* interface block sysfs attributes */ -#define gb_ib_attr(field, type) \ -static ssize_t field##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ +/* interface sysfs attributes */ +#define gb_interface_attr(field, type) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ - struct gb_interface_block *gb_ib = to_gb_interface_block(dev); \ - return sprintf(buf, "%"#type"\n", gb_ib->field); \ + struct gb_interface *intf = to_gb_interface(dev); \ + return sprintf(buf, "%"#type"\n", intf->field); \ } \ static DEVICE_ATTR_RO(field) -gb_ib_attr(vendor, x); -gb_ib_attr(product, x); -gb_ib_attr(unique_id, llX); -gb_ib_attr(vendor_string, s); -gb_ib_attr(product_string, s); +gb_interface_attr(vendor, x); +gb_interface_attr(product, x); +gb_interface_attr(unique_id, llX); +gb_interface_attr(vendor_string, s); +gb_interface_attr(product_string, s); -static struct attribute *interface_block_attrs[] = { +static struct attribute *interface_attrs[] = { &dev_attr_vendor.attr, &dev_attr_product.attr, &dev_attr_unique_id.attr, @@ -34,143 +34,144 @@ static struct attribute *interface_block_attrs[] = { &dev_attr_product_string.attr, NULL, }; -ATTRIBUTE_GROUPS(interface_block); +ATTRIBUTE_GROUPS(interface); /* XXX This could be per-host device */ static DEFINE_SPINLOCK(gb_modules_lock); -static int gb_ib_match_one_id(struct gb_interface_block *gb_ib, - const struct greybus_interface_block_id *id) +static int gb_interface_match_one_id(struct gb_interface *intf, + const struct greybus_interface_block_id *id) { if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && - (id->vendor != gb_ib->vendor)) + (id->vendor != intf->vendor)) return 0; if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && - (id->product != gb_ib->product)) + (id->product != intf->product)) return 0; if ((id->match_flags & GREYBUS_ID_MATCH_SERIAL) && - (id->unique_id != gb_ib->unique_id)) + (id->unique_id != intf->unique_id)) return 0; return 1; } const struct greybus_interface_block_id * -gb_ib_match_id(struct gb_interface_block *gb_ib, - const struct greybus_interface_block_id *id) +gb_interface_match_id(struct gb_interface *intf, + const struct greybus_interface_block_id *id) { if (id == NULL) return NULL; for (; id->vendor || id->product || id->unique_id || id->driver_info; id++) { - if (gb_ib_match_one_id(gb_ib, id)) + if (gb_interface_match_one_id(intf, id)) return id; } return NULL; } -struct gb_interface_block *gb_ib_find(struct greybus_host_device *hd, u8 module_id) +struct gb_interface *gb_interface_find(struct greybus_host_device *hd, + u8 module_id) { - struct gb_interface_block *gb_ib; + struct gb_interface *intf; - list_for_each_entry(gb_ib, &hd->modules, links) - if (gb_ib->module_id == module_id) - return gb_ib; + list_for_each_entry(intf, &hd->modules, links) + if (intf->module_id == module_id) + return intf; return NULL; } -static void greybus_ib_release(struct device *dev) +static void greybus_interface_release(struct device *dev) { - struct gb_interface_block *gb_ib = to_gb_interface_block(dev); + struct gb_interface *intf = to_gb_interface(dev); - kfree(gb_ib); + kfree(intf); } -struct device_type greybus_interface_block_type = { - .name = "greybus_interface_block", - .release = greybus_ib_release, +struct device_type greybus_interface_type = { + .name = "greybus_interface", + .release = greybus_interface_release, }; /* * A Greybus module represents a user-replicable component on an Ara - * phone. An interface block is the physical connection on that module. A - * module may have more than one interface block. + * phone. An interface is the physical connection on that module. A + * module may have more than one interface. * - * Create a gb_interface_block structure to represent a discovered module. + * Create a gb_interface structure to represent a discovered module. * The position within the Endo is encoded in the "module_id" argument. * Returns a pointer to the new module or a null pointer if a * failure occurs due to memory exhaustion. */ -static struct gb_interface_block *gb_ib_create(struct greybus_host_device *hd, - u8 module_id) +static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, + u8 module_id) { - struct gb_interface_block *gb_ib; + struct gb_interface *intf; int retval; - gb_ib = gb_ib_find(hd, module_id); - if (gb_ib) { + intf = gb_interface_find(hd, module_id); + if (intf) { dev_err(hd->parent, "Duplicate module id %d will not be created\n", module_id); return NULL; } - gb_ib = kzalloc(sizeof(*gb_ib), GFP_KERNEL); - if (!gb_ib) + intf = kzalloc(sizeof(*intf), GFP_KERNEL); + if (!intf) return NULL; - gb_ib->hd = hd; /* XXX refcount? */ - gb_ib->module_id = module_id; - INIT_LIST_HEAD(&gb_ib->bundles); + intf->hd = hd; /* XXX refcount? */ + intf->module_id = module_id; + INIT_LIST_HEAD(&intf->bundles); - gb_ib->dev.parent = hd->parent; - gb_ib->dev.bus = &greybus_bus_type; - gb_ib->dev.type = &greybus_interface_block_type; - gb_ib->dev.groups = interface_block_groups; - gb_ib->dev.dma_mask = hd->parent->dma_mask; - device_initialize(&gb_ib->dev); - dev_set_name(&gb_ib->dev, "%d", module_id); + intf->dev.parent = hd->parent; + intf->dev.bus = &greybus_bus_type; + intf->dev.type = &greybus_interface_type; + intf->dev.groups = interface_groups; + intf->dev.dma_mask = hd->parent->dma_mask; + device_initialize(&intf->dev); + dev_set_name(&intf->dev, "%d", module_id); - retval = device_add(&gb_ib->dev); + retval = device_add(&intf->dev); if (retval) { pr_err("failed to add module device for id 0x%02hhx\n", module_id); - put_device(&gb_ib->dev); - kfree(gb_ib); + put_device(&intf->dev); + kfree(intf); return NULL; } spin_lock_irq(&gb_modules_lock); - list_add_tail(&gb_ib->links, &hd->modules); + list_add_tail(&intf->links, &hd->modules); spin_unlock_irq(&gb_modules_lock); - return gb_ib; + return intf; } /* * Tear down a previously set up module. */ -static void gb_ib_destroy(struct gb_interface_block *gb_ib) +static void gb_interface_destroy(struct gb_interface *intf) { - if (WARN_ON(!gb_ib)) + if (WARN_ON(!intf)) return; spin_lock_irq(&gb_modules_lock); - list_del(&gb_ib->links); + list_del(&intf->links); spin_unlock_irq(&gb_modules_lock); - gb_bundle_destroy(gb_ib); + gb_bundle_destroy(intf); - kfree(gb_ib->product_string); - kfree(gb_ib->vendor_string); + kfree(intf->product_string); + kfree(intf->vendor_string); /* kref_put(module->hd); */ - device_del(&gb_ib->dev); + device_del(&intf->dev); } /** @@ -182,11 +183,11 @@ static void gb_ib_destroy(struct gb_interface_block *gb_ib) void gb_add_module(struct greybus_host_device *hd, u8 module_id, u8 *data, int size) { - struct gb_interface_block *gb_ib; + struct gb_interface *intf; - gb_ib = gb_ib_create(hd, module_id); - if (!gb_ib) { - dev_err(hd->parent, "failed to create interface block\n"); + intf = gb_interface_create(hd, module_id); + if (!intf) { + dev_err(hd->parent, "failed to create interface\n"); return; } @@ -194,7 +195,7 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, * Parse the manifest and build up our data structures * representing what's in it. */ - if (!gb_manifest_parse(gb_ib, data, size)) { + if (!gb_manifest_parse(intf, data, size)) { dev_err(hd->parent, "manifest error\n"); goto err_module; } @@ -211,23 +212,23 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, return; err_module: - gb_ib_destroy(gb_ib); + gb_interface_destroy(intf); } void gb_remove_module(struct greybus_host_device *hd, u8 module_id) { - struct gb_interface_block *gb_ib = gb_ib_find(hd, module_id); + struct gb_interface *intf = gb_interface_find(hd, module_id); - if (gb_ib) - gb_ib_destroy(gb_ib); + if (intf) + gb_interface_destroy(intf); else - dev_err(hd->parent, "interface block id %d not found\n", module_id); + dev_err(hd->parent, "interface id %d not found\n", module_id); } void gb_remove_modules(struct greybus_host_device *hd) { - struct gb_interface_block *gb_ib, *temp; + struct gb_interface *intf, *temp; - list_for_each_entry_safe(gb_ib, temp, &hd->modules, links) - gb_ib_destroy(gb_ib); + list_for_each_entry_safe(intf, temp, &hd->modules, links) + gb_interface_destroy(intf); } diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 5c7cebc17e2e..52adc83dd19f 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -16,7 +16,7 @@ /* Greybus "public" definitions" */ -struct gb_interface_block { +struct gb_interface { struct device dev; struct list_head bundles; @@ -32,27 +32,26 @@ struct gb_interface_block { struct greybus_host_device *hd; }; -#define to_gb_interface_block(d) container_of(d, struct gb_interface_block, dev) +#define to_gb_interface(d) container_of(d, struct gb_interface, dev) -static inline void -gb_interface_block_set_drvdata(struct gb_interface_block *gb_ib, void *data) +static inline void gb_interface_set_drvdata(struct gb_interface *intf, + void *data) { - dev_set_drvdata(&gb_ib->dev, data); + dev_set_drvdata(&intf->dev, data); } -static inline void * -gb_interface_block_get_drvdata(struct gb_interface_block *gb_ib) +static inline void * gb_interface__get_drvdata(struct gb_interface *intf) { - return dev_get_drvdata(&gb_ib->dev); + return dev_get_drvdata(&intf->dev); } /* Greybus "private" definitions */ const struct greybus_interface_block_id * - gb_ib_match_id(struct gb_interface_block *gb_ib, - const struct greybus_interface_block_id *id); + gb_interface_match_id(struct gb_interface *intf, + const struct greybus_interface_block_id *id); -struct gb_interface_block *gb_ib_find(struct greybus_host_device *hd, - u8 module_id); +struct gb_interface *gb_interface_find(struct greybus_host_device *hd, + u8 module_id); #endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 7661ca74acf3..f4cd422c72ad 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -218,7 +218,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) * structures. Returns the number of bundles set up for the * given module. */ -static u32 gb_manifest_parse_bundles(struct gb_interface_block *gb_ib) +static u32 gb_manifest_parse_bundles(struct gb_interface *intf) { u32 count = 0; @@ -240,7 +240,7 @@ static u32 gb_manifest_parse_bundles(struct gb_interface_block *gb_ib) /* Found one. Set up its bundle structure*/ desc_interface = descriptor->data; - bundle = gb_bundle_create(gb_ib, desc_interface->id); + bundle = gb_bundle_create(intf, desc_interface->id); if (!bundle) return 0; /* Error */ @@ -257,41 +257,41 @@ static u32 gb_manifest_parse_bundles(struct gb_interface_block *gb_ib) return count; } -static bool gb_manifest_parse_module(struct gb_interface_block *gb_ib, - struct manifest_desc *module_desc) +static bool gb_manifest_parse_module(struct gb_interface *intf, + struct manifest_desc *module_desc) { struct greybus_descriptor_module *desc_module = module_desc->data; /* Handle the strings first--they can fail */ - gb_ib->vendor_string = gb_string_get(desc_module->vendor_stringid); - if (IS_ERR(gb_ib->vendor_string)) + intf->vendor_string = gb_string_get(desc_module->vendor_stringid); + if (IS_ERR(intf->vendor_string)) return false; - gb_ib->product_string = gb_string_get(desc_module->product_stringid); - if (IS_ERR(gb_ib->product_string)) { + intf->product_string = gb_string_get(desc_module->product_stringid); + if (IS_ERR(intf->product_string)) { goto out_free_vendor_string; } - gb_ib->vendor = le16_to_cpu(desc_module->vendor); - gb_ib->product = le16_to_cpu(desc_module->product); - gb_ib->unique_id = le64_to_cpu(desc_module->unique_id); + intf->vendor = le16_to_cpu(desc_module->vendor); + intf->product = le16_to_cpu(desc_module->product); + intf->unique_id = le64_to_cpu(desc_module->unique_id); /* Release the module descriptor, now that we're done with it */ release_manifest_descriptor(module_desc); /* An interface must have at least one bundle descriptor */ - if (!gb_manifest_parse_bundles(gb_ib)) { + if (!gb_manifest_parse_bundles(intf)) { pr_err("manifest bundle descriptors not valid\n"); goto out_err; } return true; out_err: - kfree(gb_ib->product_string); - gb_ib->product_string = NULL; + kfree(intf->product_string); + intf->product_string = NULL; out_free_vendor_string: - kfree(gb_ib->vendor_string); - gb_ib->vendor_string = NULL; + kfree(intf->vendor_string); + intf->vendor_string = NULL; return false; } @@ -314,12 +314,12 @@ out_free_vendor_string: * information it contains, and then remove that descriptor (and any * string descriptors it refers to) from further consideration. * - * After that we look for the interface block's bundles--there must be at + * After that we look for the interface's bundles--there must be at * least one of those. * * Returns true if parsing was successful, false otherwise. */ -bool gb_manifest_parse(struct gb_interface_block *gb_ib, void *data, size_t size) +bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) { struct greybus_manifest *manifest; struct greybus_manifest_header *header; @@ -389,7 +389,7 @@ bool gb_manifest_parse(struct gb_interface_block *gb_ib, void *data, size_t size } /* Parse the module manifest, starting with the module descriptor */ - result = gb_manifest_parse_module(gb_ib, module_desc); + result = gb_manifest_parse_module(intf, module_desc); /* * We really should have no remaining descriptors, but we diff --git a/drivers/staging/greybus/manifest.h b/drivers/staging/greybus/manifest.h index a8316a0399f9..90fb62df8063 100644 --- a/drivers/staging/greybus/manifest.h +++ b/drivers/staging/greybus/manifest.h @@ -10,7 +10,7 @@ #ifndef __MANIFEST_H #define __MANIFEST_H -struct gb_interface_block; -bool gb_manifest_parse(struct gb_interface_block *gb_ib, void *data, size_t size); +struct gb_interface; +bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size); #endif /* __MANIFEST_H */ diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 1f8c4d574536..471baa59078e 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -83,9 +83,8 @@ enum svc_function_hotplug_event { }; /* XXX - * Does a hotplug come from module insertion, or from detection - * of each interface block (UniPro device) in a module? Assume - * the former for now. + * Does a hotplug come from module insertion, or from detection of each + * interface (UniPro device) in a module? Assume the former for now. */ struct svc_function_hotplug { __u8 hotplug_event; /* enum svc_function_hotplug_event */ @@ -116,7 +115,7 @@ struct svc_function_power_battery_status_request { }; /* XXX - * Each interface block carries power, so it's possible these things + * Each interface carries power, so it's possible these things * are associated with each UniPro device and not just the module. * For now it's safe to assume it's per-module. */ @@ -145,7 +144,7 @@ enum svc_function_suspend_command_type { SVC_SUSPEND_FIXME_2 = 0x01, }; -/* We'll want independent control for multi-interface block modules */ +/* We'll want independent control for multi-interface modules */ struct svc_function_suspend { __u8 suspend_command_type; /* enum function_suspend_command_type */ __u8 device_id; -- cgit v1.2.3-59-g8ed1b From edb0e0b5a17d28c7207197f415c41ab25e75b5d5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Dec 2014 14:56:32 -0800 Subject: greybus: interface: rename greybus_interface_block_id to greybus_interface_id This moves the id structure name to not have "block" in it, as that doesn't make sense anymore with the renaming of the gb_interface structure. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 ++-- drivers/staging/greybus/greybus.h | 4 ++-- drivers/staging/greybus/greybus_id.h | 4 ++-- drivers/staging/greybus/interface.c | 6 +++--- drivers/staging/greybus/interface.h | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index a0e0af5c23d2..96410cbdc0b6 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -35,7 +35,7 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(drv); struct gb_interface *intf = to_gb_interface(dev); - const struct greybus_interface_block_id *id; + const struct greybus_interface_id *id; id = gb_interface_match_id(intf, driver->id_table); if (id) @@ -95,7 +95,7 @@ static int greybus_probe(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); struct gb_interface *intf = to_gb_interface(dev); - const struct greybus_interface_block_id *id; + const struct greybus_interface_id *id; int retval; /* match id */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index bca0e6682eef..4db595674f2b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -120,13 +120,13 @@ struct greybus_driver { const char *name; int (*probe)(struct gb_interface *intf, - const struct greybus_interface_block_id *id); + const struct greybus_interface_id *id); void (*disconnect)(struct gb_interface *intf); int (*suspend)(struct gb_interface *intf, pm_message_t message); int (*resume)(struct gb_interface *intf); - const struct greybus_interface_block_id *id_table; + const struct greybus_interface_id *id_table; struct device_driver driver; }; diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index da70aab54ab0..53da8e71d674 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -9,7 +9,7 @@ #include -struct greybus_interface_block_id { +struct greybus_interface_id { __u16 match_flags; __u16 vendor; __u16 product; @@ -18,7 +18,7 @@ struct greybus_interface_block_id { kernel_ulong_t driver_info __aligned(sizeof(kernel_ulong_t)); }; -/* Used to match the greybus_interface_block_id */ +/* Used to match the greybus_interface_id */ #define GREYBUS_ID_MATCH_VENDOR BIT(0) #define GREYBUS_ID_MATCH_PRODUCT BIT(1) #define GREYBUS_ID_MATCH_SERIAL BIT(2) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index f2c935439a9b..d840ae66aab3 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -41,7 +41,7 @@ ATTRIBUTE_GROUPS(interface); static DEFINE_SPINLOCK(gb_modules_lock); static int gb_interface_match_one_id(struct gb_interface *intf, - const struct greybus_interface_block_id *id) + const struct greybus_interface_id *id) { if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && (id->vendor != intf->vendor)) @@ -58,9 +58,9 @@ static int gb_interface_match_one_id(struct gb_interface *intf, return 1; } -const struct greybus_interface_block_id * +const struct greybus_interface_id * gb_interface_match_id(struct gb_interface *intf, - const struct greybus_interface_block_id *id) + const struct greybus_interface_id *id) { if (id == NULL) return NULL; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 52adc83dd19f..f6f16df04730 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -47,9 +47,9 @@ static inline void * gb_interface__get_drvdata(struct gb_interface *intf) /* Greybus "private" definitions */ -const struct greybus_interface_block_id * +const struct greybus_interface_id * gb_interface_match_id(struct gb_interface *intf, - const struct greybus_interface_block_id *id); + const struct greybus_interface_id *id); struct gb_interface *gb_interface_find(struct greybus_host_device *hd, u8 module_id); -- cgit v1.2.3-59-g8ed1b From 426fa950595af017f5e96ff8bde972e06605ed34 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Dec 2014 14:56:33 -0800 Subject: greybus: interface.h: remove unused #defines MAX_CPORTS_PER_MODULE and MAX_STRINGS_PER_MODULE are not used anywhere anymore, so remove them lest someone thing we have limits. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/interface.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index f6f16df04730..75f813537895 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -10,11 +10,6 @@ #ifndef __INTERFACE_H #define __INTERFACE_H -/* Increase these values if needed */ -#define MAX_CPORTS_PER_MODULE 10 -#define MAX_STRINGS_PER_MODULE 10 - - /* Greybus "public" definitions" */ struct gb_interface { struct device dev; -- cgit v1.2.3-59-g8ed1b From 0e51032f381738e3e854febc627936e1c4d8deda Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Dec 2014 14:56:34 -0800 Subject: greybus: greybus_id.h: minor typo fixes Align up the BIT() #defines and properly comment the include block define. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_id.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index 53da8e71d674..3c462ef7ba6c 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -20,7 +20,7 @@ struct greybus_interface_id { /* Used to match the greybus_interface_id */ #define GREYBUS_ID_MATCH_VENDOR BIT(0) -#define GREYBUS_ID_MATCH_PRODUCT BIT(1) +#define GREYBUS_ID_MATCH_PRODUCT BIT(1) #define GREYBUS_ID_MATCH_SERIAL BIT(2) -#endif /* __LINUX_GREYBUS_H */ +#endif /* __LINUX_GREYBUS_ID_H */ -- cgit v1.2.3-59-g8ed1b From 13e6aacf60ad6791268238b989a081edfe47e7d2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Dec 2014 14:56:35 -0800 Subject: greybus: interface: we really are creating/destroying interfaces not modules. rename gb_add_module -> gb_add_interface rename gb_remove_modules -> gb_remove_interfaces rename gb_remove_module -> gb_remove_interface And move the function prototypes to interface.h, where they belong. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 6 +++--- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/greybus.h | 7 ------- drivers/staging/greybus/interface.c | 14 +++++++------- drivers/staging/greybus/interface.h | 6 ++++++ 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index f9c63e43acd7..e69566b119cf 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -175,8 +175,8 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, return; } dev_dbg(hd->parent, "module id %d added\n", module_id); - gb_add_module(hd, module_id, hotplug->data, - payload_length - 0x02); + gb_add_interface(hd, module_id, hotplug->data, + payload_length - 0x02); break; case SVC_HOTUNPLUG_EVENT: @@ -189,7 +189,7 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, return; } dev_dbg(hd->parent, "module id %d removed\n", module_id); - gb_remove_module(hd, module_id); + gb_remove_interface(hd, module_id); break; default: diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 96410cbdc0b6..3e00055bf4ae 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -195,7 +195,7 @@ void greybus_remove_hd(struct greybus_host_device *hd) { /* Tear down all modules that happen to be associated with this host * controller */ - gb_remove_modules(hd); + gb_remove_interfaces(hd); kref_put_mutex(&hd->kref, free_hd, &hd_mutex); } EXPORT_SYMBOL_GPL(greybus_remove_hd); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4db595674f2b..707189286dd9 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -154,13 +154,6 @@ void greybus_deregister(struct greybus_driver *driver); int greybus_disabled(void); -/* Internal functions to gb module, move to internal .h file eventually. */ - -void gb_add_module(struct greybus_host_device *hd, u8 module_id, - u8 *data, int size); -void gb_remove_module(struct greybus_host_device *hd, u8 module_id); -void gb_remove_modules(struct greybus_host_device *hd); - int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); int gb_ap_init(void); void gb_ap_exit(void); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index d840ae66aab3..67b14df8a179 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -175,13 +175,13 @@ static void gb_interface_destroy(struct gb_interface *intf) } /** - * gb_add_module + * gb_add_interface * * Pass in a buffer that _should_ contain a Greybus module manifest * and register a greybus device structure with the kernel core. */ -void gb_add_module(struct greybus_host_device *hd, u8 module_id, - u8 *data, int size) +void gb_add_interface(struct greybus_host_device *hd, u8 module_id, + u8 *data, int size) { struct gb_interface *intf; @@ -197,7 +197,7 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, */ if (!gb_manifest_parse(intf, data, size)) { dev_err(hd->parent, "manifest error\n"); - goto err_module; + goto err_parse; } /* @@ -211,11 +211,11 @@ void gb_add_module(struct greybus_host_device *hd, u8 module_id, return; -err_module: +err_parse: gb_interface_destroy(intf); } -void gb_remove_module(struct greybus_host_device *hd, u8 module_id) +void gb_remove_interface(struct greybus_host_device *hd, u8 module_id) { struct gb_interface *intf = gb_interface_find(hd, module_id); @@ -225,7 +225,7 @@ void gb_remove_module(struct greybus_host_device *hd, u8 module_id) dev_err(hd->parent, "interface id %d not found\n", module_id); } -void gb_remove_modules(struct greybus_host_device *hd) +void gb_remove_interfaces(struct greybus_host_device *hd) { struct gb_interface *intf, *temp; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 75f813537895..70dad49ec00b 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -49,4 +49,10 @@ const struct greybus_interface_id * struct gb_interface *gb_interface_find(struct greybus_host_device *hd, u8 module_id); +void gb_add_interface(struct greybus_host_device *hd, u8 module_id, + u8 *data, int size); +void gb_remove_interface(struct greybus_host_device *hd, u8 module_id); +void gb_remove_interfaces(struct greybus_host_device *hd); + + #endif /* __INTERFACE_H */ -- cgit v1.2.3-59-g8ed1b From 1cd56a80969d7c5cfc74d847c6f81cc2ab8dc829 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Dec 2014 14:56:36 -0800 Subject: greybus: greybus_host_device: rename modules -> interfaces This is really a list of interfaces, not modules, so rename it so that we don't get confused when we really do add modules to the whole system later on. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/interface.c | 6 +++--- drivers/staging/greybus/interface.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 3e00055bf4ae..c90f74c7a25e 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -183,7 +183,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver kref_init(&hd->kref); hd->parent = parent; hd->driver = driver; - INIT_LIST_HEAD(&hd->modules); + INIT_LIST_HEAD(&hd->interfaces); INIT_LIST_HEAD(&hd->connections); ida_init(&hd->cport_id_map); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 707189286dd9..ced329af2a2d 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -99,7 +99,7 @@ struct greybus_host_device { struct device *parent; const struct greybus_host_driver *driver; - struct list_head modules; + struct list_head interfaces; struct list_head connections; struct ida cport_id_map; u8 device_id; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 67b14df8a179..d4ec7884c01e 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -79,7 +79,7 @@ struct gb_interface *gb_interface_find(struct greybus_host_device *hd, { struct gb_interface *intf; - list_for_each_entry(intf, &hd->modules, links) + list_for_each_entry(intf, &hd->interfaces, links) if (intf->module_id == module_id) return intf; @@ -147,7 +147,7 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, } spin_lock_irq(&gb_modules_lock); - list_add_tail(&intf->links, &hd->modules); + list_add_tail(&intf->links, &hd->interfaces); spin_unlock_irq(&gb_modules_lock); return intf; @@ -229,6 +229,6 @@ void gb_remove_interfaces(struct greybus_host_device *hd) { struct gb_interface *intf, *temp; - list_for_each_entry_safe(intf, temp, &hd->modules, links) + list_for_each_entry_safe(intf, temp, &hd->interfaces, links) gb_interface_destroy(intf); } diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 70dad49ec00b..5dd2c20dc286 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -15,7 +15,7 @@ struct gb_interface { struct device dev; struct list_head bundles; - struct list_head links; /* greybus_host_device->modules */ + struct list_head links; /* greybus_host_device->interfaces */ u8 module_id; /* Physical location within the Endo */ /* Information taken from the manifest module descriptor */ -- cgit v1.2.3-59-g8ed1b From 4901175f282792450f899b86b2d5d0048d6c11b2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Dec 2014 14:56:37 -0800 Subject: greybus: interface: rename gb_modules_lock -> gb_interfaces_lock It's a local interface lock, not a modules lock, so rename it. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index d4ec7884c01e..96897f27a550 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -38,7 +38,7 @@ ATTRIBUTE_GROUPS(interface); /* XXX This could be per-host device */ -static DEFINE_SPINLOCK(gb_modules_lock); +static DEFINE_SPINLOCK(gb_interfaces_lock); static int gb_interface_match_one_id(struct gb_interface *intf, const struct greybus_interface_id *id) @@ -146,9 +146,9 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, return NULL; } - spin_lock_irq(&gb_modules_lock); + spin_lock_irq(&gb_interfaces_lock); list_add_tail(&intf->links, &hd->interfaces); - spin_unlock_irq(&gb_modules_lock); + spin_unlock_irq(&gb_interfaces_lock); return intf; } @@ -161,9 +161,9 @@ static void gb_interface_destroy(struct gb_interface *intf) if (WARN_ON(!intf)) return; - spin_lock_irq(&gb_modules_lock); + spin_lock_irq(&gb_interfaces_lock); list_del(&intf->links); - spin_unlock_irq(&gb_modules_lock); + spin_unlock_irq(&gb_interfaces_lock); gb_bundle_destroy(intf); -- cgit v1.2.3-59-g8ed1b From df671553cbe286e885e61b61f8c126e034854a89 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 21 Dec 2014 14:10:26 -0800 Subject: greybus: add module support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modules in the greybus system sit above the interface, so insert them early in the sysfs tree. We dynamically create them when we have an interface that references a module, as we don't get a "module create" message directly. They also dynamically go away when the last interface associated with a module is removed. Naming scheme for modules/interfaces/bundles/connections is bumped up by one ':', and now looks like the following: /sys/bus/greybus $ tree . ├── devices │   ├── 7 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7 │   ├── 7:7 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7/7:7 │   ├── 7:7:0 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7/7:7/7:7:0 │   └── 7:7:0:1 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7/7:7/7:7:0/7:7:0:1 ├── drivers ├── drivers_autoprobe ├── drivers_probe └── uevent 6 directories, 3 files /sys/bus/greybus $ grep . devices/*/uevent devices/7/uevent:DEVTYPE=greybus_module devices/7:7/uevent:DEVTYPE=greybus_interface devices/7:7:0/uevent:DEVTYPE=greybus_bundle devices/7:7:0:1/uevent:DEVTYPE=greybus_connection We still have some "confusion" about interface ids and module ids, which will be cleaned up later when the svc control protocol changes die down, right now we just name a module after the interface as we don't have any modules that have multiple interfaces in our systems. This has been tested with gbsim. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/bundle.c | 2 +- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/core.c | 5 +- drivers/staging/greybus/greybus.h | 7 ++ drivers/staging/greybus/interface.c | 23 ++++-- drivers/staging/greybus/interface.h | 3 +- drivers/staging/greybus/kernel_ver.h | 10 +++ drivers/staging/greybus/module.c | 140 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/module.h | 28 +++++++ 10 files changed, 211 insertions(+), 10 deletions(-) create mode 100644 drivers/staging/greybus/module.c create mode 100644 drivers/staging/greybus/module.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 6c0b0ca5be38..0d3977d87cd2 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -2,6 +2,7 @@ greybus-y := core.o \ debugfs.o \ ap.o \ manifest.o \ + module.o \ interface.o \ bundle.o \ connection.o \ diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 2ac67a242c0f..28a82229adeb 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -71,7 +71,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 interface_id) bundle->dev.type = &greybus_bundle_type; bundle->dev.groups = bundle_groups; device_initialize(&bundle->dev); - dev_set_name(&bundle->dev, "%d:%d", intf->module_id, interface_id); + dev_set_name(&bundle->dev, "%s:%d", dev_name(&intf->dev), interface_id); retval = device_add(&bundle->dev); if (retval) { diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2d61ee788218..3f786bf53798 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -237,7 +237,7 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) vaf.va = &args; pr_err("greybus: [%hhu:%hhu:%hu]: %pV\n", - connection->bundle->intf->module_id, + connection->bundle->intf->module->module_id, connection->bundle->id, connection->bundle_cport_id, &vaf); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index c90f74c7a25e..f6ca89a6b88c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -46,11 +46,14 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { + struct gb_module *module = NULL; struct gb_interface *intf = NULL; struct gb_bundle *bundle = NULL; struct gb_connection *connection = NULL; - if (is_gb_interface(dev)) { + if (is_gb_module(dev)) { + module = to_gb_module(dev); + } else if (is_gb_interface(dev)) { intf = to_gb_interface(dev); } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index ced329af2a2d..68382b383390 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -24,6 +24,7 @@ #include "greybus_id.h" #include "greybus_manifest.h" #include "manifest.h" +#include "module.h" #include "interface.h" #include "bundle.h" #include "connection.h" @@ -168,10 +169,16 @@ void gb_uart_device_exit(struct gb_connection *connection); int svc_set_route_send(struct gb_bundle *bundle, struct greybus_host_device *hd); +extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_bundle_type; extern struct device_type greybus_connection_type; +static inline int is_gb_module(const struct device *dev) +{ + return dev->type == &greybus_module_type; +} + static inline int is_gb_interface(const struct device *dev) { return dev->type == &greybus_interface_type; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 96897f27a550..4b502c69fce5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -74,13 +74,15 @@ gb_interface_match_id(struct gb_interface *intf, return NULL; } +// FIXME, odds are you don't want to call this function, rework the caller to +// not need it please. struct gb_interface *gb_interface_find(struct greybus_host_device *hd, u8 module_id) { struct gb_interface *intf; list_for_each_entry(intf, &hd->interfaces, links) - if (intf->module_id == module_id) + if (intf->module->module_id == module_id) return intf; return NULL; @@ -105,43 +107,51 @@ struct device_type greybus_interface_type = { * * Create a gb_interface structure to represent a discovered module. * The position within the Endo is encoded in the "module_id" argument. - * Returns a pointer to the new module or a null pointer if a + * Returns a pointer to the new interfce or a null pointer if a * failure occurs due to memory exhaustion. */ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, u8 module_id) { + struct gb_module *module; struct gb_interface *intf; int retval; + u8 interface_id = module_id; - intf = gb_interface_find(hd, module_id); + // FIXME we need an interface id here to check for this properly! + intf = gb_interface_find(hd, interface_id); if (intf) { dev_err(hd->parent, "Duplicate module id %d will not be created\n", module_id); return NULL; } + module = gb_module_find_or_create(hd, module_id); + if (!module) + return NULL; + intf = kzalloc(sizeof(*intf), GFP_KERNEL); if (!intf) return NULL; intf->hd = hd; /* XXX refcount? */ - intf->module_id = module_id; + intf->module = module; INIT_LIST_HEAD(&intf->bundles); - intf->dev.parent = hd->parent; + intf->dev.parent = &module->dev; intf->dev.bus = &greybus_bus_type; intf->dev.type = &greybus_interface_type; intf->dev.groups = interface_groups; intf->dev.dma_mask = hd->parent->dma_mask; device_initialize(&intf->dev); - dev_set_name(&intf->dev, "%d", module_id); + dev_set_name(&intf->dev, "%s:%d", dev_name(&module->dev), interface_id); retval = device_add(&intf->dev); if (retval) { pr_err("failed to add module device for id 0x%02hhx\n", module_id); put_device(&intf->dev); + put_device(&module->dev); kfree(intf); return NULL; } @@ -169,6 +179,7 @@ static void gb_interface_destroy(struct gb_interface *intf) kfree(intf->product_string); kfree(intf->vendor_string); + put_device(&intf->module->dev); /* kref_put(module->hd); */ device_del(&intf->dev); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 5dd2c20dc286..fd5001e908bf 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -16,7 +16,7 @@ struct gb_interface { struct list_head bundles; struct list_head links; /* greybus_host_device->interfaces */ - u8 module_id; /* Physical location within the Endo */ + u8 interface_id; /* Physical location within the Endo */ /* Information taken from the manifest module descriptor */ u16 vendor; @@ -25,6 +25,7 @@ struct gb_interface { char *product_string; u64 unique_id; + struct gb_module *module; struct greybus_host_device *hd; }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 66c27132e4ac..f0010a865c22 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -21,6 +21,11 @@ } #endif +#ifndef __ATTR_RW +#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \ + _name##_show, _name##_store) +#endif + #ifndef DEVICE_ATTR_RO #define DEVICE_ATTR_RO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RO(_name) @@ -31,6 +36,11 @@ struct device_attribute dev_attr_##_name = __ATTR_WO(_name) #endif +#ifndef DEVICE_ATTR_RW +#define DEVICE_ATTR_RW(_name) \ + struct device_attribute dev_attr_##_name = __ATTR_RW(_name) +#endif + #ifndef U8_MAX #define U8_MAX ((u8)~0U) #endif /* ! U8_MAX */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c new file mode 100644 index 000000000000..625e2d436073 --- /dev/null +++ b/drivers/staging/greybus/module.c @@ -0,0 +1,140 @@ +/* + * Greybus module code + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + + +/* + * List of modules in the system. We really should just walk the list the + * driver core provides us, but as we have lots of different things on the same + * "bus" at the same time, a single list of modules is simplest for now. + */ +static DEFINE_SPINLOCK(gb_modules_lock); +static LIST_HEAD(module_list); + +/* module sysfs attributes */ +#define gb_module_attr(field, type) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct gb_module *module = to_gb_module(dev); \ + return sprintf(buf, "%"#type"\n", module->field); \ +} \ +static DEVICE_ATTR_RO(field) + +// FIXME, do we really need this attribute? +gb_module_attr(module_id, x); + +static ssize_t epm_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + // FIXME, implement something here + return sprintf(buf, "1\n"); +} + +static ssize_t epm_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + // FIXME, implement something here. + return 0; +} +static DEVICE_ATTR_RW(epm); + +static struct attribute *module_attrs[] = { + &dev_attr_module_id.attr, + &dev_attr_epm.attr, + NULL, +}; +ATTRIBUTE_GROUPS(module); + +static void greybus_module_release(struct device *dev) +{ + struct gb_module *module = to_gb_module(dev); + + spin_lock(&gb_modules_lock); + list_del(&module->list); + spin_unlock(&gb_modules_lock); + + kfree(module); +} + +struct device_type greybus_module_type = { + .name = "greybus_module", + .release = greybus_module_release, +}; + +/* + * Search the list of modules in the system. If one is found, return it, with + * the reference count incremented. + */ +static struct gb_module *gb_module_find(u8 module_id) +{ + struct gb_module *module; + + spin_lock(&gb_modules_lock); + list_for_each_entry(module, &module_list, list) { + if (module->module_id == module_id) { + get_device(&module->dev); + goto exit; + } + } + module = NULL; +exit: + spin_unlock(&gb_modules_lock); + return module; +} + +static struct gb_module *gb_module_create(struct greybus_host_device *hd, + u8 module_id) +{ + struct gb_module *module; + int retval; + + module = kzalloc(sizeof(*module), GFP_KERNEL); + if (!module) + return NULL; + + module->module_id = module_id; + module->dev.parent = hd->parent; + module->dev.bus = &greybus_bus_type; + module->dev.type = &greybus_module_type; + module->dev.groups = module_groups; + module->dev.dma_mask = hd->parent->dma_mask; + device_initialize(&module->dev); + dev_set_name(&module->dev, "%d", module_id); + + retval = device_add(&module->dev); + if (retval) { + pr_err("failed to add module device for id 0x%02hhx\n", + module_id); + put_device(&module->dev); + kfree(module); + return NULL; + } + + spin_lock(&gb_modules_lock); + list_add_tail(&module->list, &module_list); + spin_unlock(&gb_modules_lock); + + return module; +} + +struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd, + u8 module_id) +{ + struct gb_module *module; + + module = gb_module_find(module_id); + if (module) + return module; + + return gb_module_create(hd, module_id); +} + diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h new file mode 100644 index 000000000000..9ca7c2899a3f --- /dev/null +++ b/drivers/staging/greybus/module.h @@ -0,0 +1,28 @@ +/* + * Greybus module code + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __MODULE_H +#define __MODULE_H + +/* Greybus "public" definitions" */ +struct gb_module { + struct device dev; + + struct list_head list; + u8 module_id; /* Physical location within the Endo */ +}; +#define to_gb_module(d) container_of(d, struct gb_module, dev) + +struct greybus_host_device; + +/* Greybus "private" definitions */ +struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd, + u8 module_id); + + +#endif /* __MODULE_H */ -- cgit v1.2.3-59-g8ed1b From 86cad66677942601941c2b516ece2f5741123eda Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Dec 2014 15:16:50 -0800 Subject: greybus: interface: remove global manifest_descs list The list was global and had no locking. It's not like we were ever parsing more than one manifest at the same time right now, but we might in the future. And we really want this to be local to the interface itself, for future work redoing how to bind protocols to bundles, so move the list to the interface structure. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 1 + drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/manifest.c | 39 +++++++++++++++++++------------------ 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 4b502c69fce5..d2b2e3df33fa 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -137,6 +137,7 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, intf->hd = hd; /* XXX refcount? */ intf->module = module; INIT_LIST_HEAD(&intf->bundles); + INIT_LIST_HEAD(&intf->manifest_descs); intf->dev.parent = &module->dev; intf->dev.bus = &greybus_bus_type; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index fd5001e908bf..f5b0cef9311b 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -16,6 +16,7 @@ struct gb_interface { struct list_head bundles; struct list_head links; /* greybus_host_device->interfaces */ + struct list_head manifest_descs; u8 interface_id; /* Physical location within the Endo */ /* Information taken from the manifest module descriptor */ diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index f4cd422c72ad..8b61b3486a3d 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -28,20 +28,18 @@ struct manifest_desc { enum greybus_descriptor_type type; }; -static LIST_HEAD(manifest_descs); - static void release_manifest_descriptor(struct manifest_desc *descriptor) { list_del(&descriptor->links); kfree(descriptor); } -static void release_manifest_descriptors(void) +static void release_manifest_descriptors(struct gb_interface *intf) { struct manifest_desc *descriptor; struct manifest_desc *next; - list_for_each_entry_safe(descriptor, next, &manifest_descs, links) + list_for_each_entry_safe(descriptor, next, &intf->manifest_descs, links) release_manifest_descriptor(descriptor); } @@ -55,7 +53,8 @@ static void release_manifest_descriptors(void) * Returns the number of bytes consumed by the descriptor, or a * negative errno. */ -static int identify_descriptor(struct greybus_descriptor *desc, size_t size) +static int identify_descriptor(struct gb_interface *intf, + struct greybus_descriptor *desc, size_t size) { struct greybus_descriptor_header *desc_header = &desc->header; struct manifest_desc *descriptor; @@ -116,7 +115,7 @@ static int identify_descriptor(struct greybus_descriptor *desc, size_t size) descriptor->size = desc_size; descriptor->data = (u8 *)desc + sizeof(*desc_header); descriptor->type = desc_header->type; - list_add_tail(&descriptor->links, &manifest_descs); + list_add_tail(&descriptor->links, &intf->manifest_descs); return desc_size; } @@ -132,7 +131,7 @@ static int identify_descriptor(struct greybus_descriptor *desc, size_t size) * Otherwise returns a pointer to a newly-allocated copy of the * descriptor string, or an error-coded pointer on failure. */ -static char *gb_string_get(u8 string_id) +static char *gb_string_get(struct gb_interface *intf, u8 string_id) { struct greybus_descriptor_string *desc_string; struct manifest_desc *descriptor; @@ -143,7 +142,7 @@ static char *gb_string_get(u8 string_id) if (!string_id) return NULL; - list_for_each_entry(descriptor, &manifest_descs, links) { + list_for_each_entry(descriptor, &intf->manifest_descs, links) { if (descriptor->type != GREYBUS_TYPE_STRING) continue; @@ -175,7 +174,8 @@ static char *gb_string_get(u8 string_id) * for the functions that use them. Returns the number of bundles * set up for the given interface, or 0 if there is an error. */ -static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) +static u32 gb_manifest_parse_cports(struct gb_interface *intf, + struct gb_bundle *bundle) { u32 count = 0; @@ -187,7 +187,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) bool found = false; /* Find a cport descriptor */ - list_for_each_entry(descriptor, &manifest_descs, links) { + list_for_each_entry(descriptor, &intf->manifest_descs, links) { if (descriptor->type == GREYBUS_TYPE_CPORT) { desc_cport = descriptor->data; if (desc_cport->bundle == bundle->id) { @@ -229,7 +229,7 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) bool found = false; /* Find an bundle descriptor */ - list_for_each_entry(descriptor, &manifest_descs, links) { + list_for_each_entry(descriptor, &intf->manifest_descs, links) { if (descriptor->type == GREYBUS_TYPE_INTERFACE) { found = true; break; @@ -245,7 +245,7 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) return 0; /* Error */ /* Now go set up this bundle's functions and cports */ - if (!gb_manifest_parse_cports(bundle)) + if (!gb_manifest_parse_cports(intf, bundle)) return 0; /* Error parsing cports */ count++; @@ -263,11 +263,12 @@ static bool gb_manifest_parse_module(struct gb_interface *intf, struct greybus_descriptor_module *desc_module = module_desc->data; /* Handle the strings first--they can fail */ - intf->vendor_string = gb_string_get(desc_module->vendor_stringid); + intf->vendor_string = gb_string_get(intf, desc_module->vendor_stringid); if (IS_ERR(intf->vendor_string)) return false; - intf->product_string = gb_string_get(desc_module->product_stringid); + intf->product_string = gb_string_get(intf, + desc_module->product_stringid); if (IS_ERR(intf->product_string)) { goto out_free_vendor_string; } @@ -331,7 +332,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) bool result; /* Manifest descriptor list should be empty here */ - if (WARN_ON(!list_empty(&manifest_descs))) + if (WARN_ON(!list_empty(&intf->manifest_descs))) return false; /* we have to have at _least_ the manifest header */ @@ -364,7 +365,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) while (size) { int desc_size; - desc_size = identify_descriptor(desc, size); + desc_size = identify_descriptor(intf, desc, size); if (desc_size <= 0) { if (!desc_size) pr_err("zero-sized manifest descriptor\n"); @@ -376,7 +377,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) } /* There must be a single module descriptor */ - list_for_each_entry(descriptor, &manifest_descs, links) { + list_for_each_entry(descriptor, &intf->manifest_descs, links) { if (descriptor->type == GREYBUS_TYPE_MODULE) if (!found++) module_desc = descriptor; @@ -395,10 +396,10 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) * We really should have no remaining descriptors, but we * don't know what newer format manifests might leave. */ - if (result && !list_empty(&manifest_descs)) + if (result && !list_empty(&intf->manifest_descs)) pr_info("excess descriptors in module manifest\n"); out: - release_manifest_descriptors(); + release_manifest_descriptors(intf); return result; } -- cgit v1.2.3-59-g8ed1b From 7c7d5b9a942eea5ae450487d47a95c1d0f9a4e64 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Dec 2014 15:16:51 -0800 Subject: greybus: protocol: switch gb_protocol_register() to return an int We will want to return this value as a return value for module_init() and bool does not play well with module_init(). So make it a "real" error value and return int and fix up all callers of the function. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery-gb.c | 2 +- drivers/staging/greybus/gpio-gb.c | 2 +- drivers/staging/greybus/i2c-gb.c | 2 +- drivers/staging/greybus/protocol.c | 28 +++++++++++++++------------- drivers/staging/greybus/protocol.h | 20 ++++++++++---------- drivers/staging/greybus/pwm-gb.c | 2 +- drivers/staging/greybus/sdio-gb.c | 2 +- drivers/staging/greybus/uart-gb.c | 2 +- drivers/staging/greybus/usb-gb.c | 2 +- drivers/staging/greybus/vibrator-gb.c | 2 +- 10 files changed, 33 insertions(+), 31 deletions(-) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index df1d7ee08199..f6b0dd681430 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -367,7 +367,7 @@ static struct gb_protocol battery_protocol = { .request_recv = NULL, /* no incoming requests */ }; -bool gb_battery_protocol_init(void) +int gb_battery_protocol_init(void) { return gb_protocol_register(&battery_protocol); } diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 152ba50d1693..cf60a4a96b29 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -532,7 +532,7 @@ static struct gb_protocol gpio_protocol = { .request_recv = NULL, /* no incoming requests */ }; -bool gb_gpio_protocol_init(void) +int gb_gpio_protocol_init(void) { return gb_protocol_register(&gpio_protocol); } diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 491fac4feaf3..b78de6b4be10 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -438,7 +438,7 @@ static struct gb_protocol i2c_protocol = { .request_recv = NULL, /* no incoming requests */ }; -bool gb_i2c_protocol_init(void) +int gb_i2c_protocol_init(void) { return gb_protocol_register(&i2c_protocol); } diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 4ca3ae533354..227c531fa903 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -39,8 +39,7 @@ static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) return NULL; } -/* Returns true if protocol was successfully registered, false otherwise */ -bool gb_protocol_register(struct gb_protocol *protocol) +int gb_protocol_register(struct gb_protocol *protocol) { struct gb_protocol *existing; u8 id = protocol->id; @@ -75,7 +74,7 @@ bool gb_protocol_register(struct gb_protocol *protocol) /* A matching protocol has already been registered */ spin_unlock_irq(&gb_protocols_lock); - return false; + return -EEXIST; } /* @@ -85,7 +84,7 @@ bool gb_protocol_register(struct gb_protocol *protocol) list_add_tail(&protocol->links, &existing->links); spin_unlock_irq(&gb_protocols_lock); - return true; + return 0; } /* @@ -99,10 +98,13 @@ bool gb_protocol_register(struct gb_protocol *protocol) * * Returns true if successful, false otherwise. */ -bool gb_protocol_deregister(struct gb_protocol *protocol) +int gb_protocol_deregister(struct gb_protocol *protocol) { u8 protocol_count = 0; + if (!protocol) + return 0; + spin_lock_irq(&gb_protocols_lock); protocol = _gb_protocol_find(protocol->id, protocol->major, protocol->minor); @@ -166,35 +168,35 @@ bool gb_protocol_init(void) { bool ret = true; - if (!gb_battery_protocol_init()) { + if (gb_battery_protocol_init()) { pr_err("error initializing battery protocol\n"); ret = false; } - if (!gb_gpio_protocol_init()) { + if (gb_gpio_protocol_init()) { pr_err("error initializing gpio protocol\n"); ret = false; } - if (!gb_i2c_protocol_init()) { + if (gb_i2c_protocol_init()) { pr_err("error initializing i2c protocol\n"); ret = false; } - if (!gb_pwm_protocol_init()) { + if (gb_pwm_protocol_init()) { pr_err("error initializing pwm protocol\n"); ret = false; } - if (!gb_uart_protocol_init()) { + if (gb_uart_protocol_init()) { pr_err("error initializing uart protocol\n"); ret = false; } - if (!gb_sdio_protocol_init()) { + if (gb_sdio_protocol_init()) { pr_err("error initializing sdio protocol\n"); ret = false; } - if (!gb_vibrator_protocol_init()) { + if (gb_vibrator_protocol_init()) { pr_err("error initializing vibrator protocol\n"); ret = false; } - if (!gb_usb_protocol_init()) { + if (gb_usb_protocol_init()) { pr_err("error initializing usb protocol\n"); ret = false; } diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 7d33b20b1808..49214d6a9af0 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -36,8 +36,8 @@ struct gb_protocol { gb_request_recv_t request_recv; }; -bool gb_protocol_register(struct gb_protocol *protocol); -bool gb_protocol_deregister(struct gb_protocol *protocol); +int gb_protocol_register(struct gb_protocol *protocol); +int gb_protocol_deregister(struct gb_protocol *protocol); struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); void gb_protocol_put(struct gb_protocol *protocol); @@ -47,28 +47,28 @@ void gb_protocol_put(struct gb_protocol *protocol); * Declared here for now. They could be added via modules, or maybe * just use initcalls (which level?). */ -extern bool gb_battery_protocol_init(void); +extern int gb_battery_protocol_init(void); extern void gb_battery_protocol_exit(void); -extern bool gb_gpio_protocol_init(void); +extern int gb_gpio_protocol_init(void); extern void gb_gpio_protocol_exit(void); -extern bool gb_i2c_protocol_init(void); +extern int gb_i2c_protocol_init(void); extern void gb_i2c_protocol_exit(void); -extern bool gb_pwm_protocol_init(void); +extern int gb_pwm_protocol_init(void); extern void gb_pwm_protocol_exit(void); -extern bool gb_uart_protocol_init(void); +extern int gb_uart_protocol_init(void); extern void gb_uart_protocol_exit(void); -extern bool gb_sdio_protocol_init(void); +extern int gb_sdio_protocol_init(void); extern void gb_sdio_protocol_exit(void); -extern bool gb_vibrator_protocol_init(void); +extern int gb_vibrator_protocol_init(void); extern void gb_vibrator_protocol_exit(void); -extern bool gb_usb_protocol_init(void); +extern int gb_usb_protocol_init(void); extern void gb_usb_protocol_exit(void); bool gb_protocol_init(void); diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index c505f1d2d676..5f02e7802949 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -319,7 +319,7 @@ static struct gb_protocol pwm_protocol = { .request_recv = NULL, /* no incoming requests */ }; -bool gb_pwm_protocol_init(void) +int gb_pwm_protocol_init(void) { return gb_protocol_register(&pwm_protocol); } diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 8de7dc467b31..13293a6f358e 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -87,7 +87,7 @@ static struct gb_protocol sdio_protocol = { .request_recv = NULL, /* no incoming requests */ }; -bool gb_sdio_protocol_init(void) +int gb_sdio_protocol_init(void) { return gb_protocol_register(&sdio_protocol); } diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index faba00ef1c22..825de7462f7b 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -775,7 +775,7 @@ static struct gb_protocol uart_protocol = { .request_recv = NULL, /* FIXME we have 2 types of requests!!! */ }; -bool gb_uart_protocol_init(void) +int gb_uart_protocol_init(void) { return gb_protocol_register(&uart_protocol); } diff --git a/drivers/staging/greybus/usb-gb.c b/drivers/staging/greybus/usb-gb.c index 2b5f69e59a46..b3ba50b502ec 100644 --- a/drivers/staging/greybus/usb-gb.c +++ b/drivers/staging/greybus/usb-gb.c @@ -384,7 +384,7 @@ static struct gb_protocol usb_protocol = { .request_recv = NULL, /* FIXME we have requests!!! */ }; -bool gb_usb_protocol_init(void) +int gb_usb_protocol_init(void) { return gb_protocol_register(&usb_protocol); } diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index fb48112692cf..3ef7d06160bb 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -205,7 +205,7 @@ static struct gb_protocol vibrator_protocol = { .request_recv = NULL, /* no incoming requests */ }; -bool gb_vibrator_protocol_init(void) +int gb_vibrator_protocol_init(void) { int retval; -- cgit v1.2.3-59-g8ed1b From df469a942301c9bf4d939e1139c1a435079b288a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Dec 2014 15:16:52 -0800 Subject: greybus: export needed symbols for protocols Protocol handlers need some greybus symbols, so export them so that they can be built outside of the greybus core. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 1 + drivers/staging/greybus/operation.c | 4 ++++ drivers/staging/greybus/protocol.c | 2 ++ 3 files changed, 7 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3f786bf53798..191df5377bc3 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -243,6 +243,7 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) va_end(args); } +EXPORT_SYMBOL_GPL(gb_connection_err); int gb_connection_init(struct gb_connection *connection) { diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index a057c83c90f8..087e0cc14a13 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -567,6 +567,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, return gb_operation_create_common(connection, type, request_size, response_size); } +EXPORT_SYMBOL_GPL(gb_operation_create); static struct gb_operation * gb_operation_create_incoming(struct gb_connection *connection, u16 id, @@ -623,6 +624,7 @@ void gb_operation_put(struct gb_operation *operation) if (!WARN_ON(!operation)) kref_put(&operation->kref, _gb_operation_destroy); } +EXPORT_SYMBOL_GPL(gb_operation_put); /* Tell the requester we're done */ static void gb_operation_sync_callback(struct gb_operation *operation) @@ -710,6 +712,7 @@ int gb_operation_request_send_sync(struct gb_operation *operation) return gb_operation_result(operation); } +EXPORT_SYMBOL_GPL(gb_operation_request_send_sync); /* * Send a response for an incoming operation request. A non-zero @@ -958,6 +961,7 @@ int gb_operation_sync(struct gb_connection *connection, int type, return ret; } +EXPORT_SYMBOL_GPL(gb_operation_sync); int gb_operation_init(void) { diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 227c531fa903..7536a30e5b90 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -86,6 +86,7 @@ int gb_protocol_register(struct gb_protocol *protocol) return 0; } +EXPORT_SYMBOL_GPL(gb_protocol_register); /* * De-register a previously registered protocol. @@ -117,6 +118,7 @@ int gb_protocol_deregister(struct gb_protocol *protocol) return protocol && !protocol_count; } +EXPORT_SYMBOL_GPL(gb_protocol_deregister); /* Returns the requested protocol if available, or a null pointer */ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) -- cgit v1.2.3-59-g8ed1b From fb69cb506c16d38de2c3b62ea4cf6e8728bd4348 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Dec 2014 15:16:53 -0800 Subject: greybus: protocol: split binding of prototcols to connections out of init When adding a new protocol to the system, walk all bundles and try to hook up any connections that do not have a protocol already. This sets the stage to allow for protocols to be loaded at any time, not just before the device is seen in the system. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 30 ++++++++++++++++++++++ drivers/staging/greybus/bundle.h | 1 + drivers/staging/greybus/connection.c | 48 ++++++++++++++++++++++++++++-------- drivers/staging/greybus/connection.h | 5 ++++ drivers/staging/greybus/operation.c | 3 +++ drivers/staging/greybus/protocol.c | 6 +++++ 6 files changed, 83 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 28a82229adeb..973ea39dc407 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -45,6 +45,36 @@ struct device_type greybus_bundle_type = { /* XXX This could be per-host device or per-module */ static DEFINE_SPINLOCK(gb_bundles_lock); +static int __bundle_bind_protocols(struct device *dev, void *data) +{ + struct gb_bundle *bundle; + struct gb_connection *connection; + + if (!is_gb_bundle(dev)) + return 0; + + bundle = to_gb_bundle(dev); + + list_for_each_entry(connection, &bundle->connections, bundle_links) { + gb_connection_bind_protocol(connection); + } + + return 0; +} + +/* + * Walk all bundles in the system, and see if any connections are not bound to a + * specific prototcol. If they are not, then try to find one for it and bind it + * to it. + * + * This is called after registering a new protocol. + */ +void gb_bundle_bind_protocols(void) +{ + bus_for_each_dev(&greybus_bus_type, NULL, NULL, + __bundle_bind_protocols); +} + /* * Create a gb_bundle structure to represent a discovered * bundle. Returns a pointer to the new bundle or a null diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index c3c66faac6fc..e11d456430ef 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -30,5 +30,6 @@ void gb_bundle_destroy(struct gb_interface *intf); int gb_bundle_init(struct gb_interface *intf, u8 module_id, u8 device_id); struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id); +void gb_bundle_bind_protocols(void); #endif /* __BUNDLE_H */ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 191df5377bc3..2f70837ff4fe 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -124,6 +124,32 @@ struct device_type greybus_connection_type = { .release = gb_connection_release, }; + +void gb_connection_bind_protocol(struct gb_connection *connection) +{ + struct gb_bundle *bundle; + struct gb_protocol *protocol; + + /* If we already have a protocol bound here, just return */ + if (connection->protocol) + return; + + protocol = gb_protocol_get(connection->protocol_id, + connection->major, + connection->minor); + if (!protocol) + return; + connection->protocol = protocol; + + /* + * If we have a valid device_id for the bundle, then we have an active + * device, so bring up the connection at the same time. + * */ + bundle = connection->bundle; + if (bundle->device_id != 0xff) + gb_connection_init(connection); +} + /* * Set up a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -148,13 +174,9 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, if (!connection) return NULL; - /* XXX Will have to establish connections to get version */ - connection->protocol = gb_protocol_get(protocol_id, major, minor); - if (!connection->protocol) { - pr_err("protocol 0x%02hhx not found\n", protocol_id); - kfree(connection); - return NULL; - } + connection->protocol_id = protocol_id; + connection->major = major; + connection->minor = minor; hd = bundle->intf->hd; connection->hd = hd; @@ -187,6 +209,12 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, return NULL; } + /* XXX Will have to establish connections to get version */ + gb_connection_bind_protocol(connection); + if (!connection->protocol) + dev_warn(&bundle->dev, + "protocol 0x%02hhx handler not found\n", protocol_id); + spin_lock_irq(&gb_connections_lock); list_add_tail(&connection->hd_links, &hd->connections); list_add_tail(&connection->bundle_links, &bundle->connections); @@ -250,8 +278,8 @@ int gb_connection_init(struct gb_connection *connection) int ret; if (!connection->protocol) { - gb_connection_err(connection, "uninitialized connection"); - return -EIO; + dev_warn(&connection->dev, "init without protocol.\n"); + return 0; } /* Need to enable the connection to initialize it */ @@ -266,7 +294,7 @@ int gb_connection_init(struct gb_connection *connection) void gb_connection_exit(struct gb_connection *connection) { if (!connection->protocol) { - gb_connection_err(connection, "uninitialized connection"); + dev_warn(&connection->dev, "exit without protocol.\n"); return; } connection->state = GB_CONNECTION_STATE_DESTROYING; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index caf52b8ef676..b07df79f9d86 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -33,6 +33,9 @@ struct gb_connection { struct list_head bundle_links; struct gb_protocol *protocol; + u8 protocol_id; + u8 major; + u8 minor; enum gb_connection_state state; @@ -58,4 +61,6 @@ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, __printf(2, 3) void gb_connection_err(struct gb_connection *connection, const char *fmt, ...); +void gb_connection_bind_protocol(struct gb_connection *connection); + #endif /* __CONNECTION_H */ diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 087e0cc14a13..44cfd5057e71 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -206,6 +206,9 @@ static void gb_operation_request_handle(struct gb_operation *operation) { struct gb_protocol *protocol = operation->connection->protocol; + if (!protocol) + return; + /* * If the protocol has no incoming request handler, report * an error and mark the request bad. diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 7536a30e5b90..2527532b0514 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -84,6 +84,12 @@ int gb_protocol_register(struct gb_protocol *protocol) list_add_tail(&protocol->links, &existing->links); spin_unlock_irq(&gb_protocols_lock); + /* + * Go try to bind any unbound connections, as we have a + * new protocol in the system + */ + gb_bundle_bind_protocols(); + return 0; } EXPORT_SYMBOL_GPL(gb_protocol_register); -- cgit v1.2.3-59-g8ed1b From 900ceba9e480f49bb8fef688994b4fbdb1688b77 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Dec 2014 15:16:54 -0800 Subject: greybus: i2c-gb: split out into a stand-alone kernel module. This splits the i2c-gb protocol into a stand-alone kernel module. It's not going to stay in this fashion for long, this was done to test the "can a protcol be loaded later" logic. Future refactoring is going to move the gpbridge protocols to a separate kernel module, where this protocol is going to live. But for now, split it out, it is good to test with, and shows a bug in gbsim at the moment. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/i2c-gb.c | 10 ++-------- drivers/staging/greybus/protocol.c | 5 ----- drivers/staging/greybus/protocol.h | 15 ++++++++++++--- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 0d3977d87cd2..6ce00c2b6e97 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -8,7 +8,6 @@ greybus-y := core.o \ connection.o \ protocol.o \ operation.o \ - i2c-gb.o \ gpio-gb.o \ pwm-gb.o \ sdio-gb.o \ @@ -18,6 +17,7 @@ greybus-y := core.o \ usb-gb.o obj-m += greybus.o +obj-m += i2c-gb.o obj-m += es1-ap-usb.o KERNELVER ?= $(shell uname -r) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index b78de6b4be10..d430beac9d5c 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -438,12 +438,6 @@ static struct gb_protocol i2c_protocol = { .request_recv = NULL, /* no incoming requests */ }; -int gb_i2c_protocol_init(void) -{ - return gb_protocol_register(&i2c_protocol); -} +gb_protocol_driver(&i2c_protocol); -void gb_i2c_protocol_exit(void) -{ - gb_protocol_deregister(&i2c_protocol); -} +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 2527532b0514..ee8ee3e15f8e 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -184,10 +184,6 @@ bool gb_protocol_init(void) pr_err("error initializing gpio protocol\n"); ret = false; } - if (gb_i2c_protocol_init()) { - pr_err("error initializing i2c protocol\n"); - ret = false; - } if (gb_pwm_protocol_init()) { pr_err("error initializing pwm protocol\n"); ret = false; @@ -217,7 +213,6 @@ void gb_protocol_exit(void) gb_vibrator_protocol_exit(); gb_sdio_protocol_exit(); gb_uart_protocol_exit(); - gb_i2c_protocol_exit(); gb_gpio_protocol_exit(); gb_battery_protocol_exit(); } diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 49214d6a9af0..e2555b75e334 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -53,9 +53,6 @@ extern void gb_battery_protocol_exit(void); extern int gb_gpio_protocol_init(void); extern void gb_gpio_protocol_exit(void); -extern int gb_i2c_protocol_init(void); -extern void gb_i2c_protocol_exit(void); - extern int gb_pwm_protocol_init(void); extern void gb_pwm_protocol_exit(void); @@ -74,4 +71,16 @@ extern void gb_usb_protocol_exit(void); bool gb_protocol_init(void); void gb_protocol_exit(void); +#define gb_protocol_driver(__protocol) \ +static int __init protocol_init(void) \ +{ \ + return gb_protocol_register(__protocol); \ +} \ +module_init(protocol_init); \ +static void __exit protocol_exit(void) \ +{ \ + gb_protocol_deregister(__protocol); \ +} \ +module_exit(protocol_exit); + #endif /* __PROTOCOL_H */ -- cgit v1.2.3-59-g8ed1b From 1b6ea0db016941a0919c17839c1772cceb1955c1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Dec 2014 13:01:39 -0800 Subject: greybus: bundle: create GB_DEVICE_ID_BAD Use a "name" for when we don't have a valid device id yet, instead of a magic value of 0xff. Reported-by: Alex Elder Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/bundle.c | 4 +++- drivers/staging/greybus/bundle.h | 2 ++ drivers/staging/greybus/connection.c | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 973ea39dc407..96153c54a687 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -91,9 +91,11 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 interface_id) bundle->intf = intf; bundle->id = interface_id; - bundle->device_id = 0xff; /* Invalid device id to start with */ INIT_LIST_HEAD(&bundle->connections); + /* Invalid device id to start with */ + bundle->device_id = GB_DEVICE_ID_BAD; + /* Build up the bundle device structures and register it with the * driver core */ bundle->dev.parent = &intf->dev; diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index e11d456430ef..62969cf0fa1f 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -24,6 +24,8 @@ struct gb_bundle { }; #define to_gb_bundle(d) container_of(d, struct gb_bundle, dev) +#define GB_DEVICE_ID_BAD 0xff + /* Greybus "private" definitions" */ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 module_id); void gb_bundle_destroy(struct gb_interface *intf); diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2f70837ff4fe..82b03c348239 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -146,7 +146,7 @@ void gb_connection_bind_protocol(struct gb_connection *connection) * device, so bring up the connection at the same time. * */ bundle = connection->bundle; - if (bundle->device_id != 0xff) + if (bundle->device_id != GB_DEVICE_ID_BAD) gb_connection_init(connection); } -- cgit v1.2.3-59-g8ed1b From 12a5dfc9acf690504e7266a8f310702bc9e6872c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Dec 2014 13:01:40 -0800 Subject: greybus: protocol: add a module owner to a protocol Now that protocols can be in a module, we need to reference count them to lock them into memory so they can't be removed while in use. So add a module owner structure, and have it automatically be assigned when registering the protocol. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/protocol.c | 17 ++++++++++++----- drivers/staging/greybus/protocol.h | 6 +++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index ee8ee3e15f8e..c6c0fd3ebea3 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -39,13 +39,15 @@ static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) return NULL; } -int gb_protocol_register(struct gb_protocol *protocol) +int __gb_protocol_register(struct gb_protocol *protocol, struct module *module) { struct gb_protocol *existing; u8 id = protocol->id; u8 major = protocol->major; u8 minor = protocol->minor; + protocol->owner = module; + /* * The protocols list is sorted first by protocol id (low to * high), then by major version (high to low), and finally @@ -92,7 +94,7 @@ int gb_protocol_register(struct gb_protocol *protocol) return 0; } -EXPORT_SYMBOL_GPL(gb_protocol_register); +EXPORT_SYMBOL_GPL(__gb_protocol_register); /* * De-register a previously registered protocol. @@ -135,9 +137,13 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) spin_lock_irq(&gb_protocols_lock); protocol = _gb_protocol_find(id, major, minor); if (protocol) { - protocol_count = protocol->count; - if (protocol_count != U8_MAX) - protocol->count++; + if (!try_module_get(protocol->owner)) { + protocol = NULL; + } else { + protocol_count = protocol->count; + if (protocol_count != U8_MAX) + protocol->count++; + } } spin_unlock_irq(&gb_protocols_lock); @@ -163,6 +169,7 @@ void gb_protocol_put(struct gb_protocol *protocol) protocol_count = protocol->count; if (protocol_count) protocol->count--; + module_put(protocol->owner); } spin_unlock_irq(&gb_protocols_lock); if (protocol) diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index e2555b75e334..62f024dd71bd 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -34,11 +34,15 @@ struct gb_protocol { gb_connection_init_t connection_init; gb_connection_exit_t connection_exit; gb_request_recv_t request_recv; + struct module *owner; }; -int gb_protocol_register(struct gb_protocol *protocol); +int __gb_protocol_register(struct gb_protocol *protocol, struct module *module); int gb_protocol_deregister(struct gb_protocol *protocol); +#define gb_protocol_register(protocol) \ + __gb_protocol_register(protocol, THIS_MODULE) + struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); void gb_protocol_put(struct gb_protocol *protocol); -- cgit v1.2.3-59-g8ed1b From 66b676fd88a681b149e98c462e2c0337c2848770 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Dec 2014 13:01:41 -0800 Subject: greybus: vibrator-gb: move vibrator protocol to a stand-alone module. We can't use the gb_protocol_driver() macro here as we need to do some init and exit logic when loading and removing, so "open code" the module init and exit functions. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/protocol.c | 5 ----- drivers/staging/greybus/protocol.h | 3 --- drivers/staging/greybus/vibrator-gb.c | 9 +++++++-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 6ce00c2b6e97..f7284680f6e0 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -13,11 +13,11 @@ greybus-y := core.o \ sdio-gb.o \ uart-gb.o \ battery-gb.o \ - vibrator-gb.o \ usb-gb.o obj-m += greybus.o obj-m += i2c-gb.o +obj-m += vibrator-gb.o obj-m += es1-ap-usb.o KERNELVER ?= $(shell uname -r) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index c6c0fd3ebea3..0d3c3367dab7 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -203,10 +203,6 @@ bool gb_protocol_init(void) pr_err("error initializing sdio protocol\n"); ret = false; } - if (gb_vibrator_protocol_init()) { - pr_err("error initializing vibrator protocol\n"); - ret = false; - } if (gb_usb_protocol_init()) { pr_err("error initializing usb protocol\n"); ret = false; @@ -217,7 +213,6 @@ bool gb_protocol_init(void) void gb_protocol_exit(void) { gb_usb_protocol_exit(); - gb_vibrator_protocol_exit(); gb_sdio_protocol_exit(); gb_uart_protocol_exit(); gb_gpio_protocol_exit(); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 62f024dd71bd..7b1c09c941b1 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -66,9 +66,6 @@ extern void gb_uart_protocol_exit(void); extern int gb_sdio_protocol_init(void); extern void gb_sdio_protocol_exit(void); -extern int gb_vibrator_protocol_init(void); -extern void gb_vibrator_protocol_exit(void); - extern int gb_usb_protocol_init(void); extern void gb_usb_protocol_exit(void); diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index 3ef7d06160bb..71c96d3da9ed 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -205,7 +205,7 @@ static struct gb_protocol vibrator_protocol = { .request_recv = NULL, /* no incoming requests */ }; -int gb_vibrator_protocol_init(void) +static __init int protocol_init(void) { int retval; @@ -216,8 +216,13 @@ int gb_vibrator_protocol_init(void) return gb_protocol_register(&vibrator_protocol); } -void gb_vibrator_protocol_exit(void) +static __exit void protocol_exit(void) { gb_protocol_deregister(&vibrator_protocol); class_unregister(&vibrator_class); } + +module_init(protocol_init); +module_exit(protocol_exit); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 7dd26263248c6c567c05772959d4d6a21240daa8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Dec 2014 13:01:42 -0800 Subject: greybus: battery-gb: move the battery protocol out to a stand-alone module This moves the battery class protocol to be a stand-alone kernel module. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/battery-gb.c | 10 ++-------- drivers/staging/greybus/protocol.c | 5 ----- drivers/staging/greybus/protocol.h | 3 --- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index f7284680f6e0..79cb193e453b 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -12,12 +12,12 @@ greybus-y := core.o \ pwm-gb.o \ sdio-gb.o \ uart-gb.o \ - battery-gb.o \ usb-gb.o obj-m += greybus.o obj-m += i2c-gb.o obj-m += vibrator-gb.o +obj-m += battery-gb.o obj-m += es1-ap-usb.o KERNELVER ?= $(shell uname -r) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index f6b0dd681430..7ce37377bca5 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -367,12 +367,6 @@ static struct gb_protocol battery_protocol = { .request_recv = NULL, /* no incoming requests */ }; -int gb_battery_protocol_init(void) -{ - return gb_protocol_register(&battery_protocol); -} +gb_protocol_driver(&battery_protocol); -void gb_battery_protocol_exit(void) -{ - gb_protocol_deregister(&battery_protocol); -} +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 0d3c3367dab7..e32f4ac8c7f2 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -183,10 +183,6 @@ bool gb_protocol_init(void) { bool ret = true; - if (gb_battery_protocol_init()) { - pr_err("error initializing battery protocol\n"); - ret = false; - } if (gb_gpio_protocol_init()) { pr_err("error initializing gpio protocol\n"); ret = false; @@ -216,5 +212,4 @@ void gb_protocol_exit(void) gb_sdio_protocol_exit(); gb_uart_protocol_exit(); gb_gpio_protocol_exit(); - gb_battery_protocol_exit(); } diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 7b1c09c941b1..1acc61594461 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -51,9 +51,6 @@ void gb_protocol_put(struct gb_protocol *protocol); * Declared here for now. They could be added via modules, or maybe * just use initcalls (which level?). */ -extern int gb_battery_protocol_init(void); -extern void gb_battery_protocol_exit(void); - extern int gb_gpio_protocol_init(void); extern void gb_gpio_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From e1308c1fb675878ace7a489c50b2ae52c598d294 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Dec 2014 13:01:43 -0800 Subject: greybus: gpb: Create a "GP Bridge" kernel module This bundles together the existing GP Bridged PHY protocols that were part of the Greybus core: USB, UART, SDIO, PWM, and GPIO. This is now a stand-alone kernel module. More logic will be moving here in the future to handle bridged devices. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 9 +++-- drivers/staging/greybus/core.c | 10 ------ drivers/staging/greybus/gpb.c | 70 ++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/protocol.c | 35 ------------------- drivers/staging/greybus/protocol.h | 3 -- 5 files changed, 76 insertions(+), 51 deletions(-) create mode 100644 drivers/staging/greybus/gpb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 79cb193e453b..5d73aede91df 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -7,14 +7,17 @@ greybus-y := core.o \ bundle.o \ connection.o \ protocol.o \ - operation.o \ - gpio-gb.o \ - pwm-gb.o \ + operation.o + +gpbridge-y := gpb.o \ sdio-gb.o \ uart-gb.o \ + pwm-gb.o \ + gpio-gb.o \ usb-gb.o obj-m += greybus.o +obj-m += gpbridge.o obj-m += i2c-gb.o obj-m += vibrator-gb.o obj-m += battery-gb.o diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index f6ca89a6b88c..ee8bba59faab 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -233,17 +233,8 @@ static int __init gb_init(void) goto error_operation; } - if (!gb_protocol_init()) { - /* This only fails for duplicate protocol registration */ - retval = -EEXIST; - pr_err("gb_protocol_init failed\n"); - goto error_protocol; - } - return 0; /* Success */ -error_protocol: - gb_operation_exit(); error_operation: gb_ap_exit(); error_ap: @@ -256,7 +247,6 @@ error_bus: static void __exit gb_exit(void) { - gb_protocol_exit(); gb_operation_exit(); gb_ap_exit(); bus_unregister(&greybus_bus_type); diff --git a/drivers/staging/greybus/gpb.c b/drivers/staging/greybus/gpb.c new file mode 100644 index 000000000000..0c4f46c4d458 --- /dev/null +++ b/drivers/staging/greybus/gpb.c @@ -0,0 +1,70 @@ +/* + * Greybus GP Bridge driver + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "greybus.h" + + +static int __init gpbridge_init(void) +{ + if (gb_gpio_protocol_init()) { + pr_err("error initializing gpio protocol\n"); + goto error_gpio; + } + if (gb_pwm_protocol_init()) { + pr_err("error initializing pwm protocol\n"); + goto error_pwm; + } + if (gb_uart_protocol_init()) { + pr_err("error initializing uart protocol\n"); + goto error_uart; + } + if (gb_sdio_protocol_init()) { + pr_err("error initializing sdio protocol\n"); + goto error_sdio; + } + if (gb_usb_protocol_init()) { + pr_err("error initializing usb protocol\n"); + goto error_usb; + } + return 0; + +error_usb: + gb_sdio_protocol_exit(); +error_sdio: + gb_uart_protocol_exit(); +error_uart: + gb_pwm_protocol_exit(); +error_pwm: + gb_gpio_protocol_exit(); +error_gpio: + return -EPROTO; +} + +static void __exit gpbridge_exit(void) +{ + gb_usb_protocol_exit(); + gb_sdio_protocol_exit(); + gb_uart_protocol_exit(); + gb_pwm_protocol_exit(); + gb_gpio_protocol_exit(); +} + +module_init(gpbridge_init); +module_exit(gpbridge_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index e32f4ac8c7f2..654adbc0b787 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -178,38 +178,3 @@ void gb_protocol_put(struct gb_protocol *protocol) pr_err("protocol id %hhu version %hhu.%hhu not found\n", protocol->id, major, minor); } - -bool gb_protocol_init(void) -{ - bool ret = true; - - if (gb_gpio_protocol_init()) { - pr_err("error initializing gpio protocol\n"); - ret = false; - } - if (gb_pwm_protocol_init()) { - pr_err("error initializing pwm protocol\n"); - ret = false; - } - if (gb_uart_protocol_init()) { - pr_err("error initializing uart protocol\n"); - ret = false; - } - if (gb_sdio_protocol_init()) { - pr_err("error initializing sdio protocol\n"); - ret = false; - } - if (gb_usb_protocol_init()) { - pr_err("error initializing usb protocol\n"); - ret = false; - } - return ret; -} - -void gb_protocol_exit(void) -{ - gb_usb_protocol_exit(); - gb_sdio_protocol_exit(); - gb_uart_protocol_exit(); - gb_gpio_protocol_exit(); -} diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 1acc61594461..1f6fd74a946f 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -66,9 +66,6 @@ extern void gb_sdio_protocol_exit(void); extern int gb_usb_protocol_init(void); extern void gb_usb_protocol_exit(void); -bool gb_protocol_init(void); -void gb_protocol_exit(void); - #define gb_protocol_driver(__protocol) \ static int __init protocol_init(void) \ { \ -- cgit v1.2.3-59-g8ed1b From 2c07817e72450e6fab7b49c3cbb35799bc418880 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Dec 2014 13:01:44 -0800 Subject: greybus: i2c-gb: move i2c protocol into the gpbridge driver The i2c protocol belongs in the gpbridge driver, so move it there. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/gpb.c | 7 +++++++ drivers/staging/greybus/i2c-gb.c | 10 ++++++++-- drivers/staging/greybus/protocol.h | 3 +++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 5d73aede91df..8e6551b08a93 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -14,11 +14,11 @@ gpbridge-y := gpb.o \ uart-gb.o \ pwm-gb.o \ gpio-gb.o \ + i2c-gb.o \ usb-gb.o obj-m += greybus.o obj-m += gpbridge.o -obj-m += i2c-gb.o obj-m += vibrator-gb.o obj-m += battery-gb.o obj-m += es1-ap-usb.o diff --git a/drivers/staging/greybus/gpb.c b/drivers/staging/greybus/gpb.c index 0c4f46c4d458..0776df65e4b6 100644 --- a/drivers/staging/greybus/gpb.c +++ b/drivers/staging/greybus/gpb.c @@ -41,8 +41,14 @@ static int __init gpbridge_init(void) pr_err("error initializing usb protocol\n"); goto error_usb; } + if (gb_i2c_protocol_init()) { + pr_err("error initializing usb protocol\n"); + goto error_i2c; + } return 0; +error_i2c: + gb_usb_protocol_exit(); error_usb: gb_sdio_protocol_exit(); error_sdio: @@ -57,6 +63,7 @@ error_gpio: static void __exit gpbridge_exit(void) { + gb_i2c_protocol_exit(); gb_usb_protocol_exit(); gb_sdio_protocol_exit(); gb_uart_protocol_exit(); diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index d430beac9d5c..b78de6b4be10 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -438,6 +438,12 @@ static struct gb_protocol i2c_protocol = { .request_recv = NULL, /* no incoming requests */ }; -gb_protocol_driver(&i2c_protocol); +int gb_i2c_protocol_init(void) +{ + return gb_protocol_register(&i2c_protocol); +} -MODULE_LICENSE("GPL v2"); +void gb_i2c_protocol_exit(void) +{ + gb_protocol_deregister(&i2c_protocol); +} diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 1f6fd74a946f..2d4fcfac8052 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -66,6 +66,9 @@ extern void gb_sdio_protocol_exit(void); extern int gb_usb_protocol_init(void); extern void gb_usb_protocol_exit(void); +extern int gb_i2c_protocol_init(void); +extern void gb_i2c_protocol_exit(void); + #define gb_protocol_driver(__protocol) \ static int __init protocol_init(void) \ { \ -- cgit v1.2.3-59-g8ed1b From e5646710c1836abb038415d3009f1c72d6794b77 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Dec 2014 13:01:46 -0800 Subject: greybus: module: get rid of global list of modules Use the list that the driver core keeps of our structure, no need to duplicate it with a local list as well. This gets rid of a static lock too, always a nice thing to do. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/module.c | 49 ++++++++++++++++++---------------------- drivers/staging/greybus/module.h | 2 -- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 625e2d436073..56a55fea107b 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -10,14 +10,6 @@ #include "greybus.h" -/* - * List of modules in the system. We really should just walk the list the - * driver core provides us, but as we have lots of different things on the same - * "bus" at the same time, a single list of modules is simplest for now. - */ -static DEFINE_SPINLOCK(gb_modules_lock); -static LIST_HEAD(module_list); - /* module sysfs attributes */ #define gb_module_attr(field, type) \ static ssize_t field##_show(struct device *dev, \ @@ -58,10 +50,6 @@ static void greybus_module_release(struct device *dev) { struct gb_module *module = to_gb_module(dev); - spin_lock(&gb_modules_lock); - list_del(&module->list); - spin_unlock(&gb_modules_lock); - kfree(module); } @@ -70,24 +58,35 @@ struct device_type greybus_module_type = { .release = greybus_module_release, }; +static int module_find(struct device *dev, void *data) +{ + struct gb_module *module; + u8 *module_id = data; + + if (!is_gb_module(dev)) + return 0; + + module = to_gb_module(dev); + if (module->module_id == *module_id) + return 1; + + return 0; +} + /* * Search the list of modules in the system. If one is found, return it, with * the reference count incremented. */ static struct gb_module *gb_module_find(u8 module_id) { - struct gb_module *module; + struct device *dev; + struct gb_module *module = NULL; + + dev = bus_find_device(&greybus_bus_type, NULL, + &module_id, module_find); + if (dev) + module = to_gb_module(dev); - spin_lock(&gb_modules_lock); - list_for_each_entry(module, &module_list, list) { - if (module->module_id == module_id) { - get_device(&module->dev); - goto exit; - } - } - module = NULL; -exit: - spin_unlock(&gb_modules_lock); return module; } @@ -119,10 +118,6 @@ static struct gb_module *gb_module_create(struct greybus_host_device *hd, return NULL; } - spin_lock(&gb_modules_lock); - list_add_tail(&module->list, &module_list); - spin_unlock(&gb_modules_lock); - return module; } diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 9ca7c2899a3f..75a8818de1c5 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -12,8 +12,6 @@ /* Greybus "public" definitions" */ struct gb_module { struct device dev; - - struct list_head list; u8 module_id; /* Physical location within the Endo */ }; #define to_gb_module(d) container_of(d, struct gb_module, dev) -- cgit v1.2.3-59-g8ed1b From 7422a1ec2e208762cacd3c3368e6313046b3e3d5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Dec 2014 13:01:45 -0800 Subject: greybus: protocol: name protocols. We want to be able to "blame" a protocol for things at times, so give them a name we can refer to them by. Announce when they are added or removed from the system so we have a chance to know what is going on in the kernel logs. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/battery-gb.c | 1 + drivers/staging/greybus/gpio-gb.c | 1 + drivers/staging/greybus/i2c-gb.c | 1 + drivers/staging/greybus/protocol.c | 7 +++++++ drivers/staging/greybus/protocol.h | 1 + drivers/staging/greybus/pwm-gb.c | 1 + drivers/staging/greybus/sdio-gb.c | 1 + drivers/staging/greybus/uart-gb.c | 1 + drivers/staging/greybus/usb-gb.c | 1 + drivers/staging/greybus/vibrator-gb.c | 1 + 10 files changed, 16 insertions(+) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c index 7ce37377bca5..2130d73cbfde 100644 --- a/drivers/staging/greybus/battery-gb.c +++ b/drivers/staging/greybus/battery-gb.c @@ -359,6 +359,7 @@ static void gb_battery_connection_exit(struct gb_connection *connection) } static struct gb_protocol battery_protocol = { + .name = "battery", .id = GREYBUS_PROTOCOL_BATTERY, .major = 0, .minor = 1, diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index cf60a4a96b29..5fcd01814126 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -524,6 +524,7 @@ static void gb_gpio_connection_exit(struct gb_connection *connection) } static struct gb_protocol gpio_protocol = { + .name = "gpio", .id = GREYBUS_PROTOCOL_GPIO, .major = 0, .minor = 1, diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index b78de6b4be10..3b86258d2dcf 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -430,6 +430,7 @@ static void gb_i2c_connection_exit(struct gb_connection *connection) } static struct gb_protocol i2c_protocol = { + .name = "i2c", .id = GREYBUS_PROTOCOL_I2C, .major = 0, .minor = 1, diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 654adbc0b787..4d6ffdd04ab8 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -7,6 +7,8 @@ * Released under the GPLv2 only. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "greybus.h" /* Global list of registered protocols */ @@ -86,6 +88,8 @@ int __gb_protocol_register(struct gb_protocol *protocol, struct module *module) list_add_tail(&protocol->links, &existing->links); spin_unlock_irq(&gb_protocols_lock); + pr_info("Registered %s protocol.\n", protocol->name); + /* * Go try to bind any unbound connections, as we have a * new protocol in the system @@ -124,6 +128,9 @@ int gb_protocol_deregister(struct gb_protocol *protocol) } spin_unlock_irq(&gb_protocols_lock); + if (protocol) + pr_info("Deregistered %s protocol.\n", protocol->name); + return protocol && !protocol_count; } EXPORT_SYMBOL_GPL(gb_protocol_deregister); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 2d4fcfac8052..8bda524a4d6d 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -35,6 +35,7 @@ struct gb_protocol { gb_connection_exit_t connection_exit; gb_request_recv_t request_recv; struct module *owner; + char *name; }; int __gb_protocol_register(struct gb_protocol *protocol, struct module *module); diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c index 5f02e7802949..91f7b87a1cae 100644 --- a/drivers/staging/greybus/pwm-gb.c +++ b/drivers/staging/greybus/pwm-gb.c @@ -311,6 +311,7 @@ static void gb_pwm_connection_exit(struct gb_connection *connection) } static struct gb_protocol pwm_protocol = { + .name = "pwm", .id = GREYBUS_PROTOCOL_PWM, .major = 0, .minor = 1, diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c index 13293a6f358e..d324846d09ab 100644 --- a/drivers/staging/greybus/sdio-gb.c +++ b/drivers/staging/greybus/sdio-gb.c @@ -79,6 +79,7 @@ static void gb_sdio_connection_exit(struct gb_connection *connection) } static struct gb_protocol sdio_protocol = { + .name = "sdio", .id = GREYBUS_PROTOCOL_SDIO, .major = 0, .minor = 1, diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c index 825de7462f7b..032062019d20 100644 --- a/drivers/staging/greybus/uart-gb.c +++ b/drivers/staging/greybus/uart-gb.c @@ -767,6 +767,7 @@ static void gb_tty_exit(void) } static struct gb_protocol uart_protocol = { + .name = "uart", .id = GREYBUS_PROTOCOL_UART, .major = 0, .minor = 1, diff --git a/drivers/staging/greybus/usb-gb.c b/drivers/staging/greybus/usb-gb.c index b3ba50b502ec..010ef9ee831f 100644 --- a/drivers/staging/greybus/usb-gb.c +++ b/drivers/staging/greybus/usb-gb.c @@ -376,6 +376,7 @@ static void gb_usb_connection_exit(struct gb_connection *connection) } static struct gb_protocol usb_protocol = { + .name = "usb", .id = GREYBUS_PROTOCOL_USB, .major = 0, .minor = 1, diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c index 71c96d3da9ed..c85d95085040 100644 --- a/drivers/staging/greybus/vibrator-gb.c +++ b/drivers/staging/greybus/vibrator-gb.c @@ -197,6 +197,7 @@ static void gb_vibrator_connection_exit(struct gb_connection *connection) } static struct gb_protocol vibrator_protocol = { + .name = "vibrator", .id = GREYBUS_PROTOCOL_VIBRATOR, .major = 0, .minor = 1, -- cgit v1.2.3-59-g8ed1b From 23ad7bb96bb59e1f91a95a494cca303a8f7d300a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Dec 2014 13:12:10 -0800 Subject: greybus: protocol: fix oops when no protocol is assigned When removing a connection with no protocol assigned to it, the kernel oopses as we always thought protocols were always there. Fix that problem, oopses are bad. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/protocol.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 4d6ffdd04ab8..562401d77d6d 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -165,10 +165,16 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) void gb_protocol_put(struct gb_protocol *protocol) { - u8 major = protocol->major; - u8 minor = protocol->minor; + u8 major; + u8 minor; u8 protocol_count; + if (!protocol) + return; + + major = protocol->major; + minor = protocol->minor; + spin_lock_irq(&gb_protocols_lock); protocol = _gb_protocol_find(protocol->id, protocol->major, protocol->minor); -- cgit v1.2.3-59-g8ed1b From 88e70a6846dbc3cb94d3dd2bb488bc743a1f14f1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Dec 2014 16:09:26 -0800 Subject: greybus: sysfs: put a \n at the end of all sysfs files Right now some sysfs attributes have \n and some do not, so fix that and put \n at the end of all of them to make it easier to parse things properly in userspace. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 2 +- drivers/staging/greybus/connection.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 96153c54a687..d0206602eacd 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -18,7 +18,7 @@ static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, { struct gb_bundle *bundle = to_gb_bundle(dev); - return sprintf(buf, "%d", bundle->device_id); + return sprintf(buf, "%d\n", bundle->device_id); } static DEVICE_ATTR_RO(device_id); diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 82b03c348239..c805022c50bc 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -91,7 +91,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, { struct gb_connection *connection = to_gb_connection(dev); - return sprintf(buf, "%d", connection->state); + return sprintf(buf, "%d\n", connection->state); } static DEVICE_ATTR_RO(state); @@ -100,7 +100,7 @@ protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_connection *connection = to_gb_connection(dev); - return sprintf(buf, "%d", connection->protocol->id); + return sprintf(buf, "%d\n", connection->protocol->id); } static DEVICE_ATTR_RO(protocol_id); -- cgit v1.2.3-59-g8ed1b From e2ed07f1e6926d5b6da8bc29c1ec552ecdb7085b Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Wed, 14 Jan 2015 10:23:49 +0100 Subject: greybus: i2c-gb: fix bad message size in gb_i2c The data_in_size variable was set to 1 for the status byte. But now, the status byte has move to header. Then, the status byte is "allocated" twice and cause bad message size error. Signed-off-by: Alexandre Bailon Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c-gb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c index 3b86258d2dcf..c967ae3161c5 100644 --- a/drivers/staging/greybus/i2c-gb.c +++ b/drivers/staging/greybus/i2c-gb.c @@ -199,7 +199,7 @@ gb_i2c_transfer_request(struct gb_connection *connection, struct gb_i2c_transfer_op *op; struct i2c_msg *msg; u32 data_out_size = 0; - u32 data_in_size = 1; /* Response begins with a status byte */ + u32 data_in_size = 0; size_t request_size; void *data; u16 op_count; -- cgit v1.2.3-59-g8ed1b From 025677def8717bd34dc1b554ade30b3b07626cab Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Jan 2015 14:55:24 -0800 Subject: greybus: Makefile: provide install target Provide an install Makefile target for those that want to install the kernel modules. Signed-off-by: Greg Kroah-Hartman -- v3: resend to list, somehow this thread got taken private and v2 never made it there. v2: add -a option to depmod, thanks to Mitchell --- drivers/staging/greybus/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 8e6551b08a93..bdb175ca810c 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -46,3 +46,7 @@ clean: coccicheck: $(MAKE) -C $(KERNELDIR) M=$(PWD) coccicheck +install: module + mkdir -p /lib/modules/$(KERNELVER)/kernel/drivers/greybus/ + cp -f *.ko /lib/modules/$(KERNELVER)/kernel/drivers/greybus/ + depmod -a $(KERNELVER) -- cgit v1.2.3-59-g8ed1b From 71479f6c6ba19320ff1f5acb74e1a7fd28abd346 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Jan 2015 17:07:59 -0800 Subject: greybus: rename gpbridge.ko to gb-phy.ko This module provides the Bridged PHY protocols, so name the thing properly. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index bdb175ca810c..24000e93de9f 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -9,7 +9,7 @@ greybus-y := core.o \ protocol.o \ operation.o -gpbridge-y := gpb.o \ +gb-phy-y := gpb.o \ sdio-gb.o \ uart-gb.o \ pwm-gb.o \ @@ -18,7 +18,7 @@ gpbridge-y := gpb.o \ usb-gb.o obj-m += greybus.o -obj-m += gpbridge.o +obj-m += gb-phy.o obj-m += vibrator-gb.o obj-m += battery-gb.o obj-m += es1-ap-usb.o -- cgit v1.2.3-59-g8ed1b From 419a8cf1816a9fa73071e6020847bbb81e4b9cca Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Jan 2015 17:08:00 -0800 Subject: greybus: rename vibrator-gb.ko to gb-vibrator.ko Use the "gb" prefix for module names, not a suffix. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/gb-vibrator.c | 229 ++++++++++++++++++++++++++++++++++ drivers/staging/greybus/vibrator-gb.c | 229 ---------------------------------- 3 files changed, 230 insertions(+), 230 deletions(-) create mode 100644 drivers/staging/greybus/gb-vibrator.c delete mode 100644 drivers/staging/greybus/vibrator-gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 24000e93de9f..c4bab94f69d9 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -19,7 +19,7 @@ gb-phy-y := gpb.o \ obj-m += greybus.o obj-m += gb-phy.o -obj-m += vibrator-gb.o +obj-m += gb-vibrator.o obj-m += battery-gb.o obj-m += es1-ap-usb.o diff --git a/drivers/staging/greybus/gb-vibrator.c b/drivers/staging/greybus/gb-vibrator.c new file mode 100644 index 000000000000..c85d95085040 --- /dev/null +++ b/drivers/staging/greybus/gb-vibrator.c @@ -0,0 +1,229 @@ +/* + * Greybus Vibrator protocol driver. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include +#include +#include "greybus.h" + +struct gb_vibrator_device { + struct gb_connection *connection; + struct device *dev; + int minor; /* vibrator minor number */ + u8 version_major; + u8 version_minor; +}; + +/* Version of the Greybus vibrator protocol we support */ +#define GB_VIBRATOR_VERSION_MAJOR 0x00 +#define GB_VIBRATOR_VERSION_MINOR 0x01 + +/* Greybus Vibrator request types */ +#define GB_VIBRATOR_TYPE_INVALID 0x00 +#define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01 +#define GB_VIBRATOR_TYPE_ON 0x02 +#define GB_VIBRATOR_TYPE_OFF 0x03 +#define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +struct gb_vibrator_proto_version_response { + __u8 major; + __u8 minor; +}; + +struct gb_vibrator_on_request { + __le16 timeout_ms; +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_vibrator_device *vib) +{ + struct gb_connection *connection = vib->connection; + struct gb_vibrator_proto_version_response version_response; + int retval; + + retval = gb_operation_sync(connection, + GB_VIBRATOR_TYPE_PROTOCOL_VERSION, + NULL, 0, + &version_response, sizeof(version_response)); + if (retval) + return retval; + + if (version_response.major > GB_VIBRATOR_VERSION_MAJOR) { + dev_err(&connection->dev, + "unsupported major version (%hhu > %hhu)\n", + version_response.major, GB_VIBRATOR_VERSION_MAJOR); + return -ENOTSUPP; + } + + vib->version_major = version_response.major; + vib->version_minor = version_response.minor; + return 0; +} + +static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) +{ + struct gb_vibrator_on_request request; + + request.timeout_ms = cpu_to_le16(timeout_ms); + return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, + &request, sizeof(request), NULL, 0); +} + +static int turn_off(struct gb_vibrator_device *vib) +{ + return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, + NULL, 0, NULL, 0); +} + +static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gb_vibrator_device *vib = dev_get_drvdata(dev); + unsigned long val; + int retval; + + retval = kstrtoul(buf, 10, &val); + if (retval < 0) { + dev_err(dev, "could not parse timeout value %d\n", retval); + return retval; + } + + if (val < 0) + return -EINVAL; + if (val) + retval = turn_on(vib, (u16)val); + else + retval = turn_off(vib); + if (retval) + return retval; + + return count; +} +static DEVICE_ATTR_WO(timeout); + +static struct attribute *vibrator_attrs[] = { + &dev_attr_timeout.attr, + NULL, +}; +ATTRIBUTE_GROUPS(vibrator); + +static struct class vibrator_class = { + .name = "vibrator", + .owner = THIS_MODULE, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + .dev_groups = vibrator_groups, +#endif +}; + +static DEFINE_IDR(minors); + +static int gb_vibrator_connection_init(struct gb_connection *connection) +{ + struct gb_vibrator_device *vib; + struct device *dev; + int retval; + + vib = kzalloc(sizeof(*vib), GFP_KERNEL); + if (!vib) + return -ENOMEM; + + vib->connection = connection; + connection->private = vib; + + retval = get_version(vib); + if (retval) + goto error; + + /* + * For now we create a device in sysfs for the vibrator, but odds are + * there is a "real" device somewhere in the kernel for this, but I + * can't find it at the moment... + */ + vib->minor = idr_alloc(&minors, vib, 0, 0, GFP_KERNEL); + if (vib->minor < 0) { + retval = vib->minor; + goto error; + } + dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib, + "vibrator%d", vib->minor); + if (IS_ERR(dev)) { + retval = -EINVAL; + goto error; + } + vib->dev = dev; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) + /* + * Newer kernels handle this in a race-free manner, by the dev_groups + * field in the struct class up above. But for older kernels, we need + * to "open code this :( + */ + retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); + if (retval) { + device_unregister(dev); + goto error; + } +#endif + + return 0; + +error: + kfree(vib); + return retval; +} + +static void gb_vibrator_connection_exit(struct gb_connection *connection) +{ + struct gb_vibrator_device *vib = connection->private; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) + sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); +#endif + idr_remove(&minors, vib->minor); + device_unregister(vib->dev); + kfree(vib); +} + +static struct gb_protocol vibrator_protocol = { + .name = "vibrator", + .id = GREYBUS_PROTOCOL_VIBRATOR, + .major = 0, + .minor = 1, + .connection_init = gb_vibrator_connection_init, + .connection_exit = gb_vibrator_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +static __init int protocol_init(void) +{ + int retval; + + retval = class_register(&vibrator_class); + if (retval) + return retval; + + return gb_protocol_register(&vibrator_protocol); +} + +static __exit void protocol_exit(void) +{ + gb_protocol_deregister(&vibrator_protocol); + class_unregister(&vibrator_class); +} + +module_init(protocol_init); +module_exit(protocol_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/vibrator-gb.c b/drivers/staging/greybus/vibrator-gb.c deleted file mode 100644 index c85d95085040..000000000000 --- a/drivers/staging/greybus/vibrator-gb.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Greybus Vibrator protocol driver. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include -#include -#include "greybus.h" - -struct gb_vibrator_device { - struct gb_connection *connection; - struct device *dev; - int minor; /* vibrator minor number */ - u8 version_major; - u8 version_minor; -}; - -/* Version of the Greybus vibrator protocol we support */ -#define GB_VIBRATOR_VERSION_MAJOR 0x00 -#define GB_VIBRATOR_VERSION_MINOR 0x01 - -/* Greybus Vibrator request types */ -#define GB_VIBRATOR_TYPE_INVALID 0x00 -#define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01 -#define GB_VIBRATOR_TYPE_ON 0x02 -#define GB_VIBRATOR_TYPE_OFF 0x03 -#define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -struct gb_vibrator_proto_version_response { - __u8 major; - __u8 minor; -}; - -struct gb_vibrator_on_request { - __le16 timeout_ms; -}; - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_vibrator_device *vib) -{ - struct gb_connection *connection = vib->connection; - struct gb_vibrator_proto_version_response version_response; - int retval; - - retval = gb_operation_sync(connection, - GB_VIBRATOR_TYPE_PROTOCOL_VERSION, - NULL, 0, - &version_response, sizeof(version_response)); - if (retval) - return retval; - - if (version_response.major > GB_VIBRATOR_VERSION_MAJOR) { - dev_err(&connection->dev, - "unsupported major version (%hhu > %hhu)\n", - version_response.major, GB_VIBRATOR_VERSION_MAJOR); - return -ENOTSUPP; - } - - vib->version_major = version_response.major; - vib->version_minor = version_response.minor; - return 0; -} - -static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) -{ - struct gb_vibrator_on_request request; - - request.timeout_ms = cpu_to_le16(timeout_ms); - return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, - &request, sizeof(request), NULL, 0); -} - -static int turn_off(struct gb_vibrator_device *vib) -{ - return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, - NULL, 0, NULL, 0); -} - -static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct gb_vibrator_device *vib = dev_get_drvdata(dev); - unsigned long val; - int retval; - - retval = kstrtoul(buf, 10, &val); - if (retval < 0) { - dev_err(dev, "could not parse timeout value %d\n", retval); - return retval; - } - - if (val < 0) - return -EINVAL; - if (val) - retval = turn_on(vib, (u16)val); - else - retval = turn_off(vib); - if (retval) - return retval; - - return count; -} -static DEVICE_ATTR_WO(timeout); - -static struct attribute *vibrator_attrs[] = { - &dev_attr_timeout.attr, - NULL, -}; -ATTRIBUTE_GROUPS(vibrator); - -static struct class vibrator_class = { - .name = "vibrator", - .owner = THIS_MODULE, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) - .dev_groups = vibrator_groups, -#endif -}; - -static DEFINE_IDR(minors); - -static int gb_vibrator_connection_init(struct gb_connection *connection) -{ - struct gb_vibrator_device *vib; - struct device *dev; - int retval; - - vib = kzalloc(sizeof(*vib), GFP_KERNEL); - if (!vib) - return -ENOMEM; - - vib->connection = connection; - connection->private = vib; - - retval = get_version(vib); - if (retval) - goto error; - - /* - * For now we create a device in sysfs for the vibrator, but odds are - * there is a "real" device somewhere in the kernel for this, but I - * can't find it at the moment... - */ - vib->minor = idr_alloc(&minors, vib, 0, 0, GFP_KERNEL); - if (vib->minor < 0) { - retval = vib->minor; - goto error; - } - dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib, - "vibrator%d", vib->minor); - if (IS_ERR(dev)) { - retval = -EINVAL; - goto error; - } - vib->dev = dev; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) - /* - * Newer kernels handle this in a race-free manner, by the dev_groups - * field in the struct class up above. But for older kernels, we need - * to "open code this :( - */ - retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); - if (retval) { - device_unregister(dev); - goto error; - } -#endif - - return 0; - -error: - kfree(vib); - return retval; -} - -static void gb_vibrator_connection_exit(struct gb_connection *connection) -{ - struct gb_vibrator_device *vib = connection->private; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) - sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); -#endif - idr_remove(&minors, vib->minor); - device_unregister(vib->dev); - kfree(vib); -} - -static struct gb_protocol vibrator_protocol = { - .name = "vibrator", - .id = GREYBUS_PROTOCOL_VIBRATOR, - .major = 0, - .minor = 1, - .connection_init = gb_vibrator_connection_init, - .connection_exit = gb_vibrator_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -static __init int protocol_init(void) -{ - int retval; - - retval = class_register(&vibrator_class); - if (retval) - return retval; - - return gb_protocol_register(&vibrator_protocol); -} - -static __exit void protocol_exit(void) -{ - gb_protocol_deregister(&vibrator_protocol); - class_unregister(&vibrator_class); -} - -module_init(protocol_init); -module_exit(protocol_exit); - -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 347fedb70dfd29a159832a1d6a8b46bfb25b89dd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Jan 2015 17:08:01 -0800 Subject: greybus: rename battery-gb.c to gb-battery.c Use the "gb" prefix for module names, not a suffix. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/battery-gb.c | 373 ----------------------------------- drivers/staging/greybus/gb-battery.c | 373 +++++++++++++++++++++++++++++++++++ 3 files changed, 374 insertions(+), 374 deletions(-) delete mode 100644 drivers/staging/greybus/battery-gb.c create mode 100644 drivers/staging/greybus/gb-battery.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index c4bab94f69d9..1734480d8dcd 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -20,7 +20,7 @@ gb-phy-y := gpb.o \ obj-m += greybus.o obj-m += gb-phy.o obj-m += gb-vibrator.o -obj-m += battery-gb.o +obj-m += gb-battery.o obj-m += es1-ap-usb.o KERNELVER ?= $(shell uname -r) diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c deleted file mode 100644 index 2130d73cbfde..000000000000 --- a/drivers/staging/greybus/battery-gb.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Battery driver for a Greybus module. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include "greybus.h" - -struct gb_battery { - struct power_supply bat; - // FIXME - // we will want to keep the battery stats in here as we will be getting - // updates from the SVC "on the fly" so we don't have to always go ask - // the battery for some information. Hopefully... - struct gb_connection *connection; - u8 version_major; - u8 version_minor; - -}; -#define to_gb_battery(x) container_of(x, struct gb_battery, bat) - -/* Version of the Greybus battery protocol we support */ -#define GB_BATTERY_VERSION_MAJOR 0x00 -#define GB_BATTERY_VERSION_MINOR 0x01 - -/* Greybus battery request types */ -#define GB_BATTERY_TYPE_INVALID 0x00 -#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01 -#define GB_BATTERY_TYPE_TECHNOLOGY 0x02 -#define GB_BATTERY_TYPE_STATUS 0x03 -#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 -#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05 -#define GB_BATTERY_TYPE_TEMPERATURE 0x06 -#define GB_BATTERY_TYPE_VOLTAGE 0x07 -#define GB_BATTERY_TYPE_CURRENT 0x08 -#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX -#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX - -struct gb_battery_proto_version_response { - __u8 major; - __u8 minor; -}; - -/* Should match up with battery types in linux/power_supply.h */ -#define GB_BATTERY_TECH_UNKNOWN 0x0000 -#define GB_BATTERY_TECH_NiMH 0x0001 -#define GB_BATTERY_TECH_LION 0x0002 -#define GB_BATTERY_TECH_LIPO 0x0003 -#define GB_BATTERY_TECH_LiFe 0x0004 -#define GB_BATTERY_TECH_NiCd 0x0005 -#define GB_BATTERY_TECH_LiMn 0x0006 - -struct gb_battery_technology_response { - __le32 technology; -}; - -/* Should match up with battery status in linux/power_supply.h */ -#define GB_BATTERY_STATUS_UNKNOWN 0x0000 -#define GB_BATTERY_STATUS_CHARGING 0x0001 -#define GB_BATTERY_STATUS_DISCHARGING 0x0002 -#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003 -#define GB_BATTERY_STATUS_FULL 0x0004 - -struct gb_battery_status_response { - __le16 battery_status; -}; - -struct gb_battery_max_voltage_response { - __le32 max_voltage; -}; - -struct gb_battery_capacity_response { - __le32 capacity; -}; - -struct gb_battery_temperature_response { - __le32 temperature; -}; - -struct gb_battery_voltage_response { - __le32 voltage; -}; - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_battery *gb) -{ - struct gb_battery_proto_version_response version_response; - int retval; - - retval = gb_operation_sync(gb->connection, - GB_BATTERY_TYPE_PROTOCOL_VERSION, - NULL, 0, - &version_response, sizeof(version_response)); - if (retval) - return retval; - - if (version_response.major > GB_BATTERY_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - version_response.major, GB_BATTERY_VERSION_MAJOR); - return -ENOTSUPP; - } - - gb->version_major = version_response.major; - gb->version_minor = version_response.minor; - return 0; -} - -static int get_tech(struct gb_battery *gb) -{ - struct gb_battery_technology_response tech_response; - u32 technology; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY, - NULL, 0, - &tech_response, sizeof(tech_response)); - if (retval) - return retval; - - /* - * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optimize the code away to - * nothing. - */ - technology = le32_to_cpu(tech_response.technology); - switch (technology) { - case GB_BATTERY_TECH_NiMH: - technology = POWER_SUPPLY_TECHNOLOGY_NiMH; - break; - case GB_BATTERY_TECH_LION: - technology = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case GB_BATTERY_TECH_LIPO: - technology = POWER_SUPPLY_TECHNOLOGY_LIPO; - break; - case GB_BATTERY_TECH_LiFe: - technology = POWER_SUPPLY_TECHNOLOGY_LiFe; - break; - case GB_BATTERY_TECH_NiCd: - technology = POWER_SUPPLY_TECHNOLOGY_NiCd; - break; - case GB_BATTERY_TECH_LiMn: - technology = POWER_SUPPLY_TECHNOLOGY_LiMn; - break; - case GB_BATTERY_TECH_UNKNOWN: - default: - technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; - break; - } - return technology; -} - -static int get_status(struct gb_battery *gb) -{ - struct gb_battery_status_response status_response; - u16 battery_status; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS, - NULL, 0, - &status_response, sizeof(status_response)); - if (retval) - return retval; - - /* - * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optimize the code away to - * nothing. - */ - battery_status = le16_to_cpu(status_response.battery_status); - switch (battery_status) { - case GB_BATTERY_STATUS_CHARGING: - battery_status = POWER_SUPPLY_STATUS_CHARGING; - break; - case GB_BATTERY_STATUS_DISCHARGING: - battery_status = POWER_SUPPLY_STATUS_DISCHARGING; - break; - case GB_BATTERY_STATUS_NOT_CHARGING: - battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; - break; - case GB_BATTERY_STATUS_FULL: - battery_status = POWER_SUPPLY_STATUS_FULL; - break; - case GB_BATTERY_STATUS_UNKNOWN: - default: - battery_status = POWER_SUPPLY_STATUS_UNKNOWN; - break; - } - return battery_status; -} - -static int get_max_voltage(struct gb_battery *gb) -{ - struct gb_battery_max_voltage_response volt_response; - u32 max_voltage; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE, - NULL, 0, - &volt_response, sizeof(volt_response)); - if (retval) - return retval; - - max_voltage = le32_to_cpu(volt_response.max_voltage); - return max_voltage; -} - -static int get_percent_capacity(struct gb_battery *gb) -{ - struct gb_battery_capacity_response capacity_response; - u32 capacity; - int retval; - - retval = gb_operation_sync(gb->connection, - GB_BATTERY_TYPE_PERCENT_CAPACITY, - NULL, 0, &capacity_response, - sizeof(capacity_response)); - if (retval) - return retval; - - capacity = le32_to_cpu(capacity_response.capacity); - return capacity; -} - -static int get_temp(struct gb_battery *gb) -{ - struct gb_battery_temperature_response temp_response; - u32 temperature; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE, - NULL, 0, - &temp_response, sizeof(temp_response)); - if (retval) - return retval; - - temperature = le32_to_cpu(temp_response.temperature); - return temperature; -} - -static int get_voltage(struct gb_battery *gb) -{ - struct gb_battery_voltage_response voltage_response; - u32 voltage; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE, - NULL, 0, - &voltage_response, sizeof(voltage_response)); - if (retval) - return retval; - - voltage = le32_to_cpu(voltage_response.voltage); - return voltage; -} - -static int get_property(struct power_supply *b, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct gb_battery *gb = to_gb_battery(b); - - switch (psp) { - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = get_tech(gb); - break; - - case POWER_SUPPLY_PROP_STATUS: - val->intval = get_status(gb); - break; - - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = get_max_voltage(gb); - break; - - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = get_percent_capacity(gb); - break; - - case POWER_SUPPLY_PROP_TEMP: - val->intval = get_temp(gb); - break; - - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = get_voltage(gb); - break; - - default: - return -EINVAL; - } - - return 0; -} - -// FIXME - verify this list, odds are some can be removed and others added. -static enum power_supply_property battery_props[] = { - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_VOLTAGE_NOW, -}; - -static int gb_battery_connection_init(struct gb_connection *connection) -{ - struct gb_battery *gb; - struct power_supply *b; - int retval; - - gb = kzalloc(sizeof(*gb), GFP_KERNEL); - if (!gb) - return -ENOMEM; - - gb->connection = connection; - connection->private = gb; - - /* Check the version */ - retval = get_version(gb); - if (retval) { - kfree(gb); - return retval; - } - - b = &gb->bat; - // FIXME - get a better (i.e. unique) name - // FIXME - anything else needs to be set? - b->name = "gb_battery"; - b->type = POWER_SUPPLY_TYPE_BATTERY, - b->properties = battery_props, - b->num_properties = ARRAY_SIZE(battery_props), - b->get_property = get_property, - - retval = power_supply_register(&connection->bundle->intf->dev, b); - if (retval) { - kfree(gb); - return retval; - } - - return 0; -} - -static void gb_battery_connection_exit(struct gb_connection *connection) -{ - struct gb_battery *gb = connection->private; - - power_supply_unregister(&gb->bat); - kfree(gb); -} - -static struct gb_protocol battery_protocol = { - .name = "battery", - .id = GREYBUS_PROTOCOL_BATTERY, - .major = 0, - .minor = 1, - .connection_init = gb_battery_connection_init, - .connection_exit = gb_battery_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -gb_protocol_driver(&battery_protocol); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/gb-battery.c b/drivers/staging/greybus/gb-battery.c new file mode 100644 index 000000000000..2130d73cbfde --- /dev/null +++ b/drivers/staging/greybus/gb-battery.c @@ -0,0 +1,373 @@ +/* + * Battery driver for a Greybus module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_battery { + struct power_supply bat; + // FIXME + // we will want to keep the battery stats in here as we will be getting + // updates from the SVC "on the fly" so we don't have to always go ask + // the battery for some information. Hopefully... + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + +}; +#define to_gb_battery(x) container_of(x, struct gb_battery, bat) + +/* Version of the Greybus battery protocol we support */ +#define GB_BATTERY_VERSION_MAJOR 0x00 +#define GB_BATTERY_VERSION_MINOR 0x01 + +/* Greybus battery request types */ +#define GB_BATTERY_TYPE_INVALID 0x00 +#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01 +#define GB_BATTERY_TYPE_TECHNOLOGY 0x02 +#define GB_BATTERY_TYPE_STATUS 0x03 +#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 +#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05 +#define GB_BATTERY_TYPE_TEMPERATURE 0x06 +#define GB_BATTERY_TYPE_VOLTAGE 0x07 +#define GB_BATTERY_TYPE_CURRENT 0x08 +#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX +#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX + +struct gb_battery_proto_version_response { + __u8 major; + __u8 minor; +}; + +/* Should match up with battery types in linux/power_supply.h */ +#define GB_BATTERY_TECH_UNKNOWN 0x0000 +#define GB_BATTERY_TECH_NiMH 0x0001 +#define GB_BATTERY_TECH_LION 0x0002 +#define GB_BATTERY_TECH_LIPO 0x0003 +#define GB_BATTERY_TECH_LiFe 0x0004 +#define GB_BATTERY_TECH_NiCd 0x0005 +#define GB_BATTERY_TECH_LiMn 0x0006 + +struct gb_battery_technology_response { + __le32 technology; +}; + +/* Should match up with battery status in linux/power_supply.h */ +#define GB_BATTERY_STATUS_UNKNOWN 0x0000 +#define GB_BATTERY_STATUS_CHARGING 0x0001 +#define GB_BATTERY_STATUS_DISCHARGING 0x0002 +#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003 +#define GB_BATTERY_STATUS_FULL 0x0004 + +struct gb_battery_status_response { + __le16 battery_status; +}; + +struct gb_battery_max_voltage_response { + __le32 max_voltage; +}; + +struct gb_battery_capacity_response { + __le32 capacity; +}; + +struct gb_battery_temperature_response { + __le32 temperature; +}; + +struct gb_battery_voltage_response { + __le32 voltage; +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_battery *gb) +{ + struct gb_battery_proto_version_response version_response; + int retval; + + retval = gb_operation_sync(gb->connection, + GB_BATTERY_TYPE_PROTOCOL_VERSION, + NULL, 0, + &version_response, sizeof(version_response)); + if (retval) + return retval; + + if (version_response.major > GB_BATTERY_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + version_response.major, GB_BATTERY_VERSION_MAJOR); + return -ENOTSUPP; + } + + gb->version_major = version_response.major; + gb->version_minor = version_response.minor; + return 0; +} + +static int get_tech(struct gb_battery *gb) +{ + struct gb_battery_technology_response tech_response; + u32 technology; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY, + NULL, 0, + &tech_response, sizeof(tech_response)); + if (retval) + return retval; + + /* + * Map greybus values to power_supply values. Hopefully these are + * "identical" which should allow gcc to optimize the code away to + * nothing. + */ + technology = le32_to_cpu(tech_response.technology); + switch (technology) { + case GB_BATTERY_TECH_NiMH: + technology = POWER_SUPPLY_TECHNOLOGY_NiMH; + break; + case GB_BATTERY_TECH_LION: + technology = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case GB_BATTERY_TECH_LIPO: + technology = POWER_SUPPLY_TECHNOLOGY_LIPO; + break; + case GB_BATTERY_TECH_LiFe: + technology = POWER_SUPPLY_TECHNOLOGY_LiFe; + break; + case GB_BATTERY_TECH_NiCd: + technology = POWER_SUPPLY_TECHNOLOGY_NiCd; + break; + case GB_BATTERY_TECH_LiMn: + technology = POWER_SUPPLY_TECHNOLOGY_LiMn; + break; + case GB_BATTERY_TECH_UNKNOWN: + default: + technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + break; + } + return technology; +} + +static int get_status(struct gb_battery *gb) +{ + struct gb_battery_status_response status_response; + u16 battery_status; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS, + NULL, 0, + &status_response, sizeof(status_response)); + if (retval) + return retval; + + /* + * Map greybus values to power_supply values. Hopefully these are + * "identical" which should allow gcc to optimize the code away to + * nothing. + */ + battery_status = le16_to_cpu(status_response.battery_status); + switch (battery_status) { + case GB_BATTERY_STATUS_CHARGING: + battery_status = POWER_SUPPLY_STATUS_CHARGING; + break; + case GB_BATTERY_STATUS_DISCHARGING: + battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case GB_BATTERY_STATUS_NOT_CHARGING: + battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case GB_BATTERY_STATUS_FULL: + battery_status = POWER_SUPPLY_STATUS_FULL; + break; + case GB_BATTERY_STATUS_UNKNOWN: + default: + battery_status = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + return battery_status; +} + +static int get_max_voltage(struct gb_battery *gb) +{ + struct gb_battery_max_voltage_response volt_response; + u32 max_voltage; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE, + NULL, 0, + &volt_response, sizeof(volt_response)); + if (retval) + return retval; + + max_voltage = le32_to_cpu(volt_response.max_voltage); + return max_voltage; +} + +static int get_percent_capacity(struct gb_battery *gb) +{ + struct gb_battery_capacity_response capacity_response; + u32 capacity; + int retval; + + retval = gb_operation_sync(gb->connection, + GB_BATTERY_TYPE_PERCENT_CAPACITY, + NULL, 0, &capacity_response, + sizeof(capacity_response)); + if (retval) + return retval; + + capacity = le32_to_cpu(capacity_response.capacity); + return capacity; +} + +static int get_temp(struct gb_battery *gb) +{ + struct gb_battery_temperature_response temp_response; + u32 temperature; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE, + NULL, 0, + &temp_response, sizeof(temp_response)); + if (retval) + return retval; + + temperature = le32_to_cpu(temp_response.temperature); + return temperature; +} + +static int get_voltage(struct gb_battery *gb) +{ + struct gb_battery_voltage_response voltage_response; + u32 voltage; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE, + NULL, 0, + &voltage_response, sizeof(voltage_response)); + if (retval) + return retval; + + voltage = le32_to_cpu(voltage_response.voltage); + return voltage; +} + +static int get_property(struct power_supply *b, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct gb_battery *gb = to_gb_battery(b); + + switch (psp) { + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = get_tech(gb); + break; + + case POWER_SUPPLY_PROP_STATUS: + val->intval = get_status(gb); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = get_max_voltage(gb); + break; + + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = get_percent_capacity(gb); + break; + + case POWER_SUPPLY_PROP_TEMP: + val->intval = get_temp(gb); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = get_voltage(gb); + break; + + default: + return -EINVAL; + } + + return 0; +} + +// FIXME - verify this list, odds are some can be removed and others added. +static enum power_supply_property battery_props[] = { + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static int gb_battery_connection_init(struct gb_connection *connection) +{ + struct gb_battery *gb; + struct power_supply *b; + int retval; + + gb = kzalloc(sizeof(*gb), GFP_KERNEL); + if (!gb) + return -ENOMEM; + + gb->connection = connection; + connection->private = gb; + + /* Check the version */ + retval = get_version(gb); + if (retval) { + kfree(gb); + return retval; + } + + b = &gb->bat; + // FIXME - get a better (i.e. unique) name + // FIXME - anything else needs to be set? + b->name = "gb_battery"; + b->type = POWER_SUPPLY_TYPE_BATTERY, + b->properties = battery_props, + b->num_properties = ARRAY_SIZE(battery_props), + b->get_property = get_property, + + retval = power_supply_register(&connection->bundle->intf->dev, b); + if (retval) { + kfree(gb); + return retval; + } + + return 0; +} + +static void gb_battery_connection_exit(struct gb_connection *connection) +{ + struct gb_battery *gb = connection->private; + + power_supply_unregister(&gb->bat); + kfree(gb); +} + +static struct gb_protocol battery_protocol = { + .name = "battery", + .id = GREYBUS_PROTOCOL_BATTERY, + .major = 0, + .minor = 1, + .connection_init = gb_battery_connection_init, + .connection_exit = gb_battery_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +gb_protocol_driver(&battery_protocol); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 60a7ad7cae9d095d2de55aac7c05f258f89b0ad5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Jan 2015 17:08:02 -0800 Subject: greybus: rename es1-ap-usb.ko to gb-es1.ko Use the "gb-" prefix for the ES1 Host controller driver. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/es1-ap-usb.c | 617 ----------------------------------- drivers/staging/greybus/gb-es1.c | 617 +++++++++++++++++++++++++++++++++++ 3 files changed, 618 insertions(+), 618 deletions(-) delete mode 100644 drivers/staging/greybus/es1-ap-usb.c create mode 100644 drivers/staging/greybus/gb-es1.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1734480d8dcd..5a7c58deb92a 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -21,7 +21,7 @@ obj-m += greybus.o obj-m += gb-phy.o obj-m += gb-vibrator.o obj-m += gb-battery.o -obj-m += es1-ap-usb.o +obj-m += gb-es1.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c deleted file mode 100644 index 27416e440ede..000000000000 --- a/drivers/staging/greybus/es1-ap-usb.c +++ /dev/null @@ -1,617 +0,0 @@ -/* - * Greybus "AP" USB driver - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ -#include -#include -#include -#include -#include -#include - -#include "greybus.h" -#include "svc_msg.h" -#include "kernel_ver.h" - -/* - * Macros for making pointers explicitly opaque, such that the result - * isn't valid but also can't be mistaken for an ERR_PTR() value. - */ -#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) -#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) - -/* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) -#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE - -static const struct usb_device_id id_table[] = { - /* Made up numbers for the SVC USB Bridge in ES1 */ - { USB_DEVICE(0xffff, 0x0001) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -/* - * Number of CPort IN urbs in flight at any point in time. - * Adjust if we are having stalls in the USB buffer due to not enough urbs in - * flight. - */ -#define NUM_CPORT_IN_URB 4 - -/* Number of CPort OUT urbs in flight at any point in time. - * Adjust if we get messages saying we are out of urbs in the system log. - */ -#define NUM_CPORT_OUT_URB 8 - -/** - * es1_ap_dev - ES1 USB Bridge to AP structure - * @usb_dev: pointer to the USB device we are. - * @usb_intf: pointer to the USB interface we are bound to. - * @hd: pointer to our greybus_host_device structure - * @control_endpoint: endpoint to send data to SVC - * @svc_endpoint: endpoint for SVC data in - * @cport_in_endpoint: bulk in endpoint for CPort data - * @cport-out_endpoint: bulk out endpoint for CPort data - * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint - * @svc_urb: urb for SVC messages coming in on @svc_endpoint - * @cport_in_urb: array of urbs for the CPort in messages - * @cport_in_buffer: array of buffers for the @cport_in_urb urbs - * @cport_out_urb: array of urbs for the CPort out messages - * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or - * not. - * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" - */ -struct es1_ap_dev { - struct usb_device *usb_dev; - struct usb_interface *usb_intf; - struct greybus_host_device *hd; - - __u8 control_endpoint; - __u8 svc_endpoint; - __u8 cport_in_endpoint; - __u8 cport_out_endpoint; - - u8 *svc_buffer; - struct urb *svc_urb; - - struct urb *cport_in_urb[NUM_CPORT_IN_URB]; - u8 *cport_in_buffer[NUM_CPORT_IN_URB]; - struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; - bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; - spinlock_t cport_out_urb_lock; -}; - -static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) -{ - return (struct es1_ap_dev *)&hd->hd_priv; -} - -static void cport_out_callback(struct urb *urb); - -/* - * Buffer constraints for the host driver. - * - * A "buffer" is used to hold data to be transferred for Greybus by - * the host driver. A buffer is represented by a "buffer pointer", - * which defines a region of memory used by the host driver for - * transferring the data. When Greybus allocates a buffer, it must - * do so subject to the constraints associated with the host driver. - * These constraints are specified by two parameters: the - * headroom; and the maximum buffer size. - * - * +------------------+ - * | Host driver | \ - * | reserved area | }- headroom - * | . . . | / - * buffer pointer ---> +------------------+ - * | Buffer space for | \ - * | transferred data | }- buffer size - * | . . . | / (limited to size_max) - * +------------------+ - * - * headroom: Every buffer must have at least this much space - * *before* the buffer pointer, reserved for use by the - * host driver. I.e., ((char *)buffer - headroom) must - * point to valid memory, usable only by the host driver. - * size_max: The maximum size of a buffer (not including the - * headroom) must not exceed this. - */ -static void hd_buffer_constraints(struct greybus_host_device *hd) -{ - /* - * Only one byte is required, but this produces a result - * that's better aligned for the user. - */ - hd->buffer_headroom = sizeof(u32); /* For cport id */ - hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; - BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); -} - -#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ -static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - int retval; - - /* SVC messages go down our control pipe */ - retval = usb_control_msg(es1->usb_dev, - usb_sndctrlpipe(es1->usb_dev, - es1->control_endpoint), - 0x01, /* vendor request AP message */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, 0x00, - (char *)svc_msg, - sizeof(*svc_msg), - ES1_TIMEOUT); - if (retval != sizeof(*svc_msg)) - return retval; - - return 0; -} - -static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) -{ - struct urb *urb = NULL; - unsigned long flags; - int i; - - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - - /* Look in our pool of allocated urbs first, as that's the "fastest" */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (es1->cport_out_urb_busy[i] == false) { - es1->cport_out_urb_busy[i] = true; - urb = es1->cport_out_urb[i]; - break; - } - } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - if (urb) - return urb; - - /* - * Crap, pool is empty, complain to the syslog and go allocate one - * dynamically as we have to succeed. - */ - dev_err(&es1->usb_dev->dev, - "No free CPort OUT urbs, having to dynamically allocate one!\n"); - urb = usb_alloc_urb(0, gfp_mask); - if (!urb) - return NULL; - - return urb; -} - -/* - * Returns an opaque cookie value if successful, or a pointer coded - * error otherwise. If the caller wishes to cancel the in-flight - * buffer, it must supply the returned cookie to the cancel routine. - */ -static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, - void *buffer, size_t buffer_size, gfp_t gfp_mask) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; - u8 *transfer_buffer = buffer; - int transfer_buffer_size; - int retval; - struct urb *urb; - - if (!buffer) { - pr_err("null buffer supplied to send\n"); - return ERR_PTR(-EINVAL); - } - if (buffer_size > (size_t)INT_MAX) { - pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); - return ERR_PTR(-EINVAL); - } - transfer_buffer--; - transfer_buffer_size = buffer_size + 1; - - /* - * The data actually transferred will include an indication - * of where the data should be sent. Do one last check of - * the target CPort id before filling it in. - */ - if (cport_id == CPORT_ID_BAD) { - pr_err("request to send inbound data buffer\n"); - return ERR_PTR(-EINVAL); - } - if (cport_id > (u16)U8_MAX) { - pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); - return ERR_PTR(-EINVAL); - } - /* OK, the destination is fine; record it in the transfer buffer */ - *transfer_buffer = cport_id; - - /* Find a free urb */ - urb = next_free_urb(es1, gfp_mask); - if (!urb) - return ERR_PTR(-ENOMEM); - - usb_fill_bulk_urb(urb, udev, - usb_sndbulkpipe(udev, es1->cport_out_endpoint), - transfer_buffer, transfer_buffer_size, - cport_out_callback, hd); - retval = usb_submit_urb(urb, gfp_mask); - if (retval) { - pr_err("error %d submitting URB\n", retval); - return ERR_PTR(retval); - } - - return conceal_urb(urb); -} - -/* - * The cookie value supplied is the value that buffer_send() - * returned to its caller. It identifies the buffer that should be - * canceled. This function must also handle (which is to say, - * ignore) a null cookie value. - */ -static void buffer_cancel(void *cookie) -{ - - /* - * We really should be defensive and track all outstanding - * (sent) buffers rather than trusting the cookie provided - * is valid. For the time being, this will do. - */ - if (cookie) - usb_kill_urb(reveal_urb(cookie)); -} - -static struct greybus_host_driver es1_driver = { - .hd_priv_size = sizeof(struct es1_ap_dev), - .buffer_send = buffer_send, - .buffer_cancel = buffer_cancel, - .submit_svc = submit_svc, -}; - -/* Common function to report consistent warnings based on URB status */ -static int check_urb_status(struct urb *urb) -{ - struct device *dev = &urb->dev->dev; - int status = urb->status; - - switch (status) { - case 0: - return 0; - - case -EOVERFLOW: - dev_err(dev, "%s: overflow actual length is %d\n", - __func__, urb->actual_length); - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - case -EILSEQ: - case -EPROTO: - /* device is gone, stop sending */ - return status; - } - dev_err(dev, "%s: unknown status %d\n", __func__, status); - - return -EAGAIN; -} - -static void ap_disconnect(struct usb_interface *interface) -{ - struct es1_ap_dev *es1; - struct usb_device *udev; - int i; - - es1 = usb_get_intfdata(interface); - if (!es1) - return; - - /* Tear down everything! */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb = es1->cport_out_urb[i]; - - if (!urb) - break; - usb_kill_urb(urb); - usb_free_urb(urb); - es1->cport_out_urb[i] = NULL; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ - } - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb = es1->cport_in_urb[i]; - - if (!urb) - break; - usb_kill_urb(urb); - usb_free_urb(urb); - kfree(es1->cport_in_buffer[i]); - es1->cport_in_buffer[i] = NULL; - } - - usb_kill_urb(es1->svc_urb); - usb_free_urb(es1->svc_urb); - es1->svc_urb = NULL; - kfree(es1->svc_buffer); - es1->svc_buffer = NULL; - - usb_set_intfdata(interface, NULL); - udev = es1->usb_dev; - greybus_remove_hd(es1->hd); - - usb_put_dev(udev); -} - -/* Callback for when we get a SVC message */ -static void svc_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb svc in error %d (dropped)\n", status); - return; - } - - /* We have a message, create a new message structure, add it to the - * list, and wake up our thread that will process the messages. - */ - greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); - -exit: - /* resubmit the urb to get more messages */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "Can not submit urb for AP data: %d\n", retval); -} - -static void cport_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - u16 cport_id; - u8 *data; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb cport in error %d (dropped)\n", status); - return; - } - - /* The size has to be at least one, for the cport id */ - if (!urb->actual_length) { - dev_err(dev, "%s: no cport id in input buffer?\n", __func__); - goto exit; - } - - /* - * Our CPort number is the first byte of the data stream, - * the rest of the stream is "real" data - */ - data = urb->transfer_buffer; - cport_id = (u16)data[0]; - data = &data[1]; - - /* Pass this data to the greybus core */ - greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); - -exit: - /* put our urb back in the request pool */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "%s: error %d in submitting urb.\n", - __func__, retval); -} - -static void cport_out_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct es1_ap_dev *es1 = hd_to_es1(hd); - unsigned long flags; - int status = check_urb_status(urb); - u8 *data = urb->transfer_buffer + 1; - int i; - - /* - * Tell the submitter that the buffer send (attempt) is - * complete, and report the status. The submitter's buffer - * starts after the one-byte CPort id we inserted. - */ - data = urb->transfer_buffer + 1; - greybus_data_sent(hd, data, status); - - /* - * See if this was an urb in our pool, if so mark it "free", otherwise - * we need to free it ourselves. - */ - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (urb == es1->cport_out_urb[i]) { - es1->cport_out_urb_busy[i] = false; - urb = NULL; - break; - } - } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - - /* If urb is not NULL, then we need to free this urb */ - usb_free_urb(urb); - - /* - * Rest assured Greg, this craziness is getting fixed. - * - * Yes, you are right, we aren't telling anyone that the urb finished. - * "That's crazy! How does this all even work?" you might be saying. - * The "magic" is the idea that greybus works on the "operation" level, - * not the "send a buffer" level. All operations are "round-trip" with - * a response from the device that the operation finished, or it will - * time out. Because of that, we don't care that this urb finished, or - * failed, or did anything else, as higher levels of the protocol stack - * will handle completions and timeouts and the rest. - * - * This protocol is "needed" due to some hardware restrictions on the - * current generation of Unipro controllers. Think about it for a - * minute, this is a USB driver, talking to a Unipro bridge, impedance - * mismatch is huge, yet the Unipro controller are even more - * underpowered than this little USB controller. We rely on the round - * trip to keep stalls in the Unipro controllers from happening so that - * we can keep data flowing properly, no matter how slow it might be. - * - * Once again, a wonderful bus protocol cut down in its prime by a naive - * controller chip. We dream of the day we have a "real" HCD for - * Unipro. Until then, we suck it up and make the hardware work, as - * that's the job of the firmware and kernel. - * - */ -} - -/* - * The ES1 USB Bridge device contains 4 endpoints - * 1 Control - usual USB stuff + AP -> SVC messages - * 1 Interrupt IN - SVC -> AP messages - * 1 Bulk IN - CPort data in - * 1 Bulk OUT - CPort data out - */ -static int ap_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct es1_ap_dev *es1; - struct greybus_host_device *hd; - struct usb_device *udev; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - bool int_in_found = false; - bool bulk_in_found = false; - bool bulk_out_found = false; - int retval = -ENOMEM; - int i; - u8 svc_interval = 0; - - udev = usb_get_dev(interface_to_usbdev(interface)); - - hd = greybus_create_hd(&es1_driver, &udev->dev); - if (!hd) { - usb_put_dev(udev); - return -ENOMEM; - } - - /* Fill in the buffer allocation constraints */ - hd_buffer_constraints(hd); - - es1 = hd_to_es1(hd); - es1->hd = hd; - es1->usb_intf = interface; - es1->usb_dev = udev; - spin_lock_init(&es1->cport_out_urb_lock); - usb_set_intfdata(interface, es1); - - /* Control endpoint is the pipe to talk to this AP, so save it off */ - endpoint = &udev->ep0.desc; - es1->control_endpoint = endpoint->bEndpointAddress; - - /* find all 3 of our endpoints */ - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (usb_endpoint_is_int_in(endpoint)) { - es1->svc_endpoint = endpoint->bEndpointAddress; - svc_interval = endpoint->bInterval; - int_in_found = true; - } else if (usb_endpoint_is_bulk_in(endpoint)) { - es1->cport_in_endpoint = endpoint->bEndpointAddress; - bulk_in_found = true; - } else if (usb_endpoint_is_bulk_out(endpoint)) { - es1->cport_out_endpoint = endpoint->bEndpointAddress; - bulk_out_found = true; - } else { - dev_err(&udev->dev, - "Unknown endpoint type found, address %x\n", - endpoint->bEndpointAddress); - } - } - if ((int_in_found == false) || - (bulk_in_found == false) || - (bulk_out_found == false)) { - dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); - goto error; - } - - /* Create our buffer and URB to get SVC messages, and start it up */ - es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); - if (!es1->svc_buffer) - goto error; - - es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!es1->svc_urb) - goto error; - - usb_fill_int_urb(es1->svc_urb, udev, - usb_rcvintpipe(udev, es1->svc_endpoint), - es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, - hd, svc_interval); - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; - - /* Allocate buffers for our cport in messages and start them up */ - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb; - u8 *buffer; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - goto error; - buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); - if (!buffer) - goto error; - - usb_fill_bulk_urb(urb, udev, - usb_rcvbulkpipe(udev, es1->cport_in_endpoint), - buffer, ES1_GBUF_MSG_SIZE_MAX, - cport_in_callback, hd); - es1->cport_in_urb[i] = urb; - es1->cport_in_buffer[i] = buffer; - retval = usb_submit_urb(urb, GFP_KERNEL); - if (retval) - goto error; - } - - /* Allocate urbs for our CPort OUT messages */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - goto error; - - es1->cport_out_urb[i] = urb; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ - } - - return 0; -error: - ap_disconnect(interface); - - return retval; -} - -static struct usb_driver es1_ap_driver = { - .name = "es1_ap_driver", - .probe = ap_probe, - .disconnect = ap_disconnect, - .id_table = id_table, -}; - -module_usb_driver(es1_ap_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/gb-es1.c b/drivers/staging/greybus/gb-es1.c new file mode 100644 index 000000000000..27416e440ede --- /dev/null +++ b/drivers/staging/greybus/gb-es1.c @@ -0,0 +1,617 @@ +/* + * Greybus "AP" USB driver + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include +#include +#include +#include +#include +#include + +#include "greybus.h" +#include "svc_msg.h" +#include "kernel_ver.h" + +/* + * Macros for making pointers explicitly opaque, such that the result + * isn't valid but also can't be mistaken for an ERR_PTR() value. + */ +#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) +#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) + +/* Memory sizes for the buffers sent to/from the ES1 controller */ +#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) +#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE + +static const struct usb_device_id id_table[] = { + /* Made up numbers for the SVC USB Bridge in ES1 */ + { USB_DEVICE(0xffff, 0x0001) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* + * Number of CPort IN urbs in flight at any point in time. + * Adjust if we are having stalls in the USB buffer due to not enough urbs in + * flight. + */ +#define NUM_CPORT_IN_URB 4 + +/* Number of CPort OUT urbs in flight at any point in time. + * Adjust if we get messages saying we are out of urbs in the system log. + */ +#define NUM_CPORT_OUT_URB 8 + +/** + * es1_ap_dev - ES1 USB Bridge to AP structure + * @usb_dev: pointer to the USB device we are. + * @usb_intf: pointer to the USB interface we are bound to. + * @hd: pointer to our greybus_host_device structure + * @control_endpoint: endpoint to send data to SVC + * @svc_endpoint: endpoint for SVC data in + * @cport_in_endpoint: bulk in endpoint for CPort data + * @cport-out_endpoint: bulk out endpoint for CPort data + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint + * @svc_urb: urb for SVC messages coming in on @svc_endpoint + * @cport_in_urb: array of urbs for the CPort in messages + * @cport_in_buffer: array of buffers for the @cport_in_urb urbs + * @cport_out_urb: array of urbs for the CPort out messages + * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or + * not. + * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" + */ +struct es1_ap_dev { + struct usb_device *usb_dev; + struct usb_interface *usb_intf; + struct greybus_host_device *hd; + + __u8 control_endpoint; + __u8 svc_endpoint; + __u8 cport_in_endpoint; + __u8 cport_out_endpoint; + + u8 *svc_buffer; + struct urb *svc_urb; + + struct urb *cport_in_urb[NUM_CPORT_IN_URB]; + u8 *cport_in_buffer[NUM_CPORT_IN_URB]; + struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; + bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; + spinlock_t cport_out_urb_lock; +}; + +static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) +{ + return (struct es1_ap_dev *)&hd->hd_priv; +} + +static void cport_out_callback(struct urb *urb); + +/* + * Buffer constraints for the host driver. + * + * A "buffer" is used to hold data to be transferred for Greybus by + * the host driver. A buffer is represented by a "buffer pointer", + * which defines a region of memory used by the host driver for + * transferring the data. When Greybus allocates a buffer, it must + * do so subject to the constraints associated with the host driver. + * These constraints are specified by two parameters: the + * headroom; and the maximum buffer size. + * + * +------------------+ + * | Host driver | \ + * | reserved area | }- headroom + * | . . . | / + * buffer pointer ---> +------------------+ + * | Buffer space for | \ + * | transferred data | }- buffer size + * | . . . | / (limited to size_max) + * +------------------+ + * + * headroom: Every buffer must have at least this much space + * *before* the buffer pointer, reserved for use by the + * host driver. I.e., ((char *)buffer - headroom) must + * point to valid memory, usable only by the host driver. + * size_max: The maximum size of a buffer (not including the + * headroom) must not exceed this. + */ +static void hd_buffer_constraints(struct greybus_host_device *hd) +{ + /* + * Only one byte is required, but this produces a result + * that's better aligned for the user. + */ + hd->buffer_headroom = sizeof(u32); /* For cport id */ + hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; + BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); +} + +#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + int retval; + + /* SVC messages go down our control pipe */ + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + 0x01, /* vendor request AP message */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + (char *)svc_msg, + sizeof(*svc_msg), + ES1_TIMEOUT); + if (retval != sizeof(*svc_msg)) + return retval; + + return 0; +} + +static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) +{ + struct urb *urb = NULL; + unsigned long flags; + int i; + + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + + /* Look in our pool of allocated urbs first, as that's the "fastest" */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (es1->cport_out_urb_busy[i] == false) { + es1->cport_out_urb_busy[i] = true; + urb = es1->cport_out_urb[i]; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + if (urb) + return urb; + + /* + * Crap, pool is empty, complain to the syslog and go allocate one + * dynamically as we have to succeed. + */ + dev_err(&es1->usb_dev->dev, + "No free CPort OUT urbs, having to dynamically allocate one!\n"); + urb = usb_alloc_urb(0, gfp_mask); + if (!urb) + return NULL; + + return urb; +} + +/* + * Returns an opaque cookie value if successful, or a pointer coded + * error otherwise. If the caller wishes to cancel the in-flight + * buffer, it must supply the returned cookie to the cancel routine. + */ +static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, + void *buffer, size_t buffer_size, gfp_t gfp_mask) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + u8 *transfer_buffer = buffer; + int transfer_buffer_size; + int retval; + struct urb *urb; + + if (!buffer) { + pr_err("null buffer supplied to send\n"); + return ERR_PTR(-EINVAL); + } + if (buffer_size > (size_t)INT_MAX) { + pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); + return ERR_PTR(-EINVAL); + } + transfer_buffer--; + transfer_buffer_size = buffer_size + 1; + + /* + * The data actually transferred will include an indication + * of where the data should be sent. Do one last check of + * the target CPort id before filling it in. + */ + if (cport_id == CPORT_ID_BAD) { + pr_err("request to send inbound data buffer\n"); + return ERR_PTR(-EINVAL); + } + if (cport_id > (u16)U8_MAX) { + pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); + return ERR_PTR(-EINVAL); + } + /* OK, the destination is fine; record it in the transfer buffer */ + *transfer_buffer = cport_id; + + /* Find a free urb */ + urb = next_free_urb(es1, gfp_mask); + if (!urb) + return ERR_PTR(-ENOMEM); + + usb_fill_bulk_urb(urb, udev, + usb_sndbulkpipe(udev, es1->cport_out_endpoint), + transfer_buffer, transfer_buffer_size, + cport_out_callback, hd); + retval = usb_submit_urb(urb, gfp_mask); + if (retval) { + pr_err("error %d submitting URB\n", retval); + return ERR_PTR(retval); + } + + return conceal_urb(urb); +} + +/* + * The cookie value supplied is the value that buffer_send() + * returned to its caller. It identifies the buffer that should be + * canceled. This function must also handle (which is to say, + * ignore) a null cookie value. + */ +static void buffer_cancel(void *cookie) +{ + + /* + * We really should be defensive and track all outstanding + * (sent) buffers rather than trusting the cookie provided + * is valid. For the time being, this will do. + */ + if (cookie) + usb_kill_urb(reveal_urb(cookie)); +} + +static struct greybus_host_driver es1_driver = { + .hd_priv_size = sizeof(struct es1_ap_dev), + .buffer_send = buffer_send, + .buffer_cancel = buffer_cancel, + .submit_svc = submit_svc, +}; + +/* Common function to report consistent warnings based on URB status */ +static int check_urb_status(struct urb *urb) +{ + struct device *dev = &urb->dev->dev; + int status = urb->status; + + switch (status) { + case 0: + return 0; + + case -EOVERFLOW: + dev_err(dev, "%s: overflow actual length is %d\n", + __func__, urb->actual_length); + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EILSEQ: + case -EPROTO: + /* device is gone, stop sending */ + return status; + } + dev_err(dev, "%s: unknown status %d\n", __func__, status); + + return -EAGAIN; +} + +static void ap_disconnect(struct usb_interface *interface) +{ + struct es1_ap_dev *es1; + struct usb_device *udev; + int i; + + es1 = usb_get_intfdata(interface); + if (!es1) + return; + + /* Tear down everything! */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + struct urb *urb = es1->cport_out_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + es1->cport_out_urb[i] = NULL; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ + } + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb = es1->cport_in_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + kfree(es1->cport_in_buffer[i]); + es1->cport_in_buffer[i] = NULL; + } + + usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); + es1->svc_urb = NULL; + kfree(es1->svc_buffer); + es1->svc_buffer = NULL; + + usb_set_intfdata(interface, NULL); + udev = es1->usb_dev; + greybus_remove_hd(es1->hd); + + usb_put_dev(udev); +} + +/* Callback for when we get a SVC message */ +static void svc_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb svc in error %d (dropped)\n", status); + return; + } + + /* We have a message, create a new message structure, add it to the + * list, and wake up our thread that will process the messages. + */ + greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); + +exit: + /* resubmit the urb to get more messages */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "Can not submit urb for AP data: %d\n", retval); +} + +static void cport_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + u16 cport_id; + u8 *data; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb cport in error %d (dropped)\n", status); + return; + } + + /* The size has to be at least one, for the cport id */ + if (!urb->actual_length) { + dev_err(dev, "%s: no cport id in input buffer?\n", __func__); + goto exit; + } + + /* + * Our CPort number is the first byte of the data stream, + * the rest of the stream is "real" data + */ + data = urb->transfer_buffer; + cport_id = (u16)data[0]; + data = &data[1]; + + /* Pass this data to the greybus core */ + greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); + +exit: + /* put our urb back in the request pool */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "%s: error %d in submitting urb.\n", + __func__, retval); +} + +static void cport_out_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct es1_ap_dev *es1 = hd_to_es1(hd); + unsigned long flags; + int status = check_urb_status(urb); + u8 *data = urb->transfer_buffer + 1; + int i; + + /* + * Tell the submitter that the buffer send (attempt) is + * complete, and report the status. The submitter's buffer + * starts after the one-byte CPort id we inserted. + */ + data = urb->transfer_buffer + 1; + greybus_data_sent(hd, data, status); + + /* + * See if this was an urb in our pool, if so mark it "free", otherwise + * we need to free it ourselves. + */ + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (urb == es1->cport_out_urb[i]) { + es1->cport_out_urb_busy[i] = false; + urb = NULL; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + + /* If urb is not NULL, then we need to free this urb */ + usb_free_urb(urb); + + /* + * Rest assured Greg, this craziness is getting fixed. + * + * Yes, you are right, we aren't telling anyone that the urb finished. + * "That's crazy! How does this all even work?" you might be saying. + * The "magic" is the idea that greybus works on the "operation" level, + * not the "send a buffer" level. All operations are "round-trip" with + * a response from the device that the operation finished, or it will + * time out. Because of that, we don't care that this urb finished, or + * failed, or did anything else, as higher levels of the protocol stack + * will handle completions and timeouts and the rest. + * + * This protocol is "needed" due to some hardware restrictions on the + * current generation of Unipro controllers. Think about it for a + * minute, this is a USB driver, talking to a Unipro bridge, impedance + * mismatch is huge, yet the Unipro controller are even more + * underpowered than this little USB controller. We rely on the round + * trip to keep stalls in the Unipro controllers from happening so that + * we can keep data flowing properly, no matter how slow it might be. + * + * Once again, a wonderful bus protocol cut down in its prime by a naive + * controller chip. We dream of the day we have a "real" HCD for + * Unipro. Until then, we suck it up and make the hardware work, as + * that's the job of the firmware and kernel. + * + */ +} + +/* + * The ES1 USB Bridge device contains 4 endpoints + * 1 Control - usual USB stuff + AP -> SVC messages + * 1 Interrupt IN - SVC -> AP messages + * 1 Bulk IN - CPort data in + * 1 Bulk OUT - CPort data out + */ +static int ap_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct es1_ap_dev *es1; + struct greybus_host_device *hd; + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + bool int_in_found = false; + bool bulk_in_found = false; + bool bulk_out_found = false; + int retval = -ENOMEM; + int i; + u8 svc_interval = 0; + + udev = usb_get_dev(interface_to_usbdev(interface)); + + hd = greybus_create_hd(&es1_driver, &udev->dev); + if (!hd) { + usb_put_dev(udev); + return -ENOMEM; + } + + /* Fill in the buffer allocation constraints */ + hd_buffer_constraints(hd); + + es1 = hd_to_es1(hd); + es1->hd = hd; + es1->usb_intf = interface; + es1->usb_dev = udev; + spin_lock_init(&es1->cport_out_urb_lock); + usb_set_intfdata(interface, es1); + + /* Control endpoint is the pipe to talk to this AP, so save it off */ + endpoint = &udev->ep0.desc; + es1->control_endpoint = endpoint->bEndpointAddress; + + /* find all 3 of our endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) { + es1->svc_endpoint = endpoint->bEndpointAddress; + svc_interval = endpoint->bInterval; + int_in_found = true; + } else if (usb_endpoint_is_bulk_in(endpoint)) { + es1->cport_in_endpoint = endpoint->bEndpointAddress; + bulk_in_found = true; + } else if (usb_endpoint_is_bulk_out(endpoint)) { + es1->cport_out_endpoint = endpoint->bEndpointAddress; + bulk_out_found = true; + } else { + dev_err(&udev->dev, + "Unknown endpoint type found, address %x\n", + endpoint->bEndpointAddress); + } + } + if ((int_in_found == false) || + (bulk_in_found == false) || + (bulk_out_found == false)) { + dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); + goto error; + } + + /* Create our buffer and URB to get SVC messages, and start it up */ + es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); + if (!es1->svc_buffer) + goto error; + + es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!es1->svc_urb) + goto error; + + usb_fill_int_urb(es1->svc_urb, udev, + usb_rcvintpipe(udev, es1->svc_endpoint), + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, + hd, svc_interval); + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + + /* Allocate buffers for our cport in messages and start them up */ + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb; + u8 *buffer; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error; + buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); + if (!buffer) + goto error; + + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, es1->cport_in_endpoint), + buffer, ES1_GBUF_MSG_SIZE_MAX, + cport_in_callback, hd); + es1->cport_in_urb[i] = urb; + es1->cport_in_buffer[i] = buffer; + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) + goto error; + } + + /* Allocate urbs for our CPort OUT messages */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + struct urb *urb; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error; + + es1->cport_out_urb[i] = urb; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ + } + + return 0; +error: + ap_disconnect(interface); + + return retval; +} + +static struct usb_driver es1_ap_driver = { + .name = "es1_ap_driver", + .probe = ap_probe, + .disconnect = ap_disconnect, + .id_table = id_table, +}; + +module_usb_driver(es1_ap_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From c86117a8a8b4aaff67a65dc9a415352f48ef45d0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Jan 2015 17:08:03 -0800 Subject: greybus: Move the es1_ap_desc.c file to Documentation directory This .c file isn't needed by the kernel driver, it's there for firmware developers only, so just move it into the Documentation directory to reduce confusion. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- .../staging/greybus/Documentation/es1_ap_desc.c | 70 ++++++++++++++++++++++ drivers/staging/greybus/es1_ap_desc.c | 70 ---------------------- 2 files changed, 70 insertions(+), 70 deletions(-) create mode 100644 drivers/staging/greybus/Documentation/es1_ap_desc.c delete mode 100644 drivers/staging/greybus/es1_ap_desc.c diff --git a/drivers/staging/greybus/Documentation/es1_ap_desc.c b/drivers/staging/greybus/Documentation/es1_ap_desc.c new file mode 100644 index 000000000000..1502089ec29c --- /dev/null +++ b/drivers/staging/greybus/Documentation/es1_ap_desc.c @@ -0,0 +1,70 @@ +/* ES1 AP Bridge Chip USB descriptor definitions */ + +static const u8 es1_dev_descriptor[] = { + 0x12, /* __u8 bLength */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, 0x02 /* __le16 bcdUSB v2.0 */ + 0x00, /* __u8 bDeviceClass */ + 0x00, /* __u8 bDeviceClass */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x40, /* __u8 bMaxPacketSize0; 2^64 = 512 Bytes */ + + 0xff, 0xff, /* __le16 idVendor; 0xffff made up for now */ + 0x01, 0x00, /* __le16 idProduct; 0x0001 made up for now */ + 0x01, 0x00, /* __le16 bcdDevice; ES1 */ + + 0x03, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +static const u8 es1_config_descriptor[] = { + /* one configuration */ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, 0x00, /* __le16 wTotalLength; */ + 0x01, /* __u8 bNumInterfaces; (1) */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0xc0, /* __u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* one interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x03, /* __u8 if_bNumEndpoints; */ + 0xff, /* __u8 if_bInterfaceClass; Vendor-specific */ + 0xff, /* __u8 if_bInterfaceSubClass; Vendor-specific */ + 0xff, /* __u8 if_bInterfaceProtocol; Vendor-specific */ + 0x00, /* __u8 if_iInterface; */ + + /* three endpoints */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ + 0x40, /* __u8 ep_bInterval; 64ms */ + + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x82, /* __u8 ep_bEndpointAddress; IN Endpoint 2 */ + 0x02, /* __u8 ep_bmAttributes; Bulk */ + 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ + 0x40 /* __u8 ep_bInterval; */ + + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x02, /* __u8 ep_bEndpointAddress; Out Endpoint 2 */ + 0x02, /* __u8 ep_bmAttributes; Bulk */ + 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ + 0x40 /* __u8 ep_bInterval; */ +}; diff --git a/drivers/staging/greybus/es1_ap_desc.c b/drivers/staging/greybus/es1_ap_desc.c deleted file mode 100644 index 1502089ec29c..000000000000 --- a/drivers/staging/greybus/es1_ap_desc.c +++ /dev/null @@ -1,70 +0,0 @@ -/* ES1 AP Bridge Chip USB descriptor definitions */ - -static const u8 es1_dev_descriptor[] = { - 0x12, /* __u8 bLength */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x00, 0x02 /* __le16 bcdUSB v2.0 */ - 0x00, /* __u8 bDeviceClass */ - 0x00, /* __u8 bDeviceClass */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x40, /* __u8 bMaxPacketSize0; 2^64 = 512 Bytes */ - - 0xff, 0xff, /* __le16 idVendor; 0xffff made up for now */ - 0x01, 0x00, /* __le16 idProduct; 0x0001 made up for now */ - 0x01, 0x00, /* __le16 bcdDevice; ES1 */ - - 0x03, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -static const u8 es1_config_descriptor[] = { - /* one configuration */ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, 0x00, /* __le16 wTotalLength; */ - 0x01, /* __u8 bNumInterfaces; (1) */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0xc0, /* __u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* one interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x03, /* __u8 if_bNumEndpoints; */ - 0xff, /* __u8 if_bInterfaceClass; Vendor-specific */ - 0xff, /* __u8 if_bInterfaceSubClass; Vendor-specific */ - 0xff, /* __u8 if_bInterfaceProtocol; Vendor-specific */ - 0x00, /* __u8 if_iInterface; */ - - /* three endpoints */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ - 0x40, /* __u8 ep_bInterval; 64ms */ - - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x82, /* __u8 ep_bEndpointAddress; IN Endpoint 2 */ - 0x02, /* __u8 ep_bmAttributes; Bulk */ - 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ - 0x40 /* __u8 ep_bInterval; */ - - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x02, /* __u8 ep_bEndpointAddress; Out Endpoint 2 */ - 0x02, /* __u8 ep_bmAttributes; Bulk */ - 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ - 0x40 /* __u8 ep_bInterval; */ -}; -- cgit v1.2.3-59-g8ed1b From 4e2b07e9ab8f8ddc7f6dbb6617ddb2bc704d15e9 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Thu, 15 Jan 2015 21:18:26 -0500 Subject: greybus: gb-vibrator: remove useless if in timeout_store() val is an unsigned long so there is no point in checking if it is less than zero. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gb-vibrator.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/gb-vibrator.c b/drivers/staging/greybus/gb-vibrator.c index c85d95085040..b5332df7039c 100644 --- a/drivers/staging/greybus/gb-vibrator.c +++ b/drivers/staging/greybus/gb-vibrator.c @@ -100,8 +100,6 @@ static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, return retval; } - if (val < 0) - return -EINVAL; if (val) retval = turn_on(vib, (u16)val); else -- cgit v1.2.3-59-g8ed1b From 9d677cf604212313f41097e2f542e1923d717086 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Thu, 15 Jan 2015 21:18:27 -0500 Subject: greybus: Add FIXME warnings for possible NULL dereferences Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 1 + drivers/staging/greybus/protocol.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 44cfd5057e71..f014906c0259 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -742,6 +742,7 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) } } + /* FIXME operation->response could still be NULL here */ /* Fill in the response header and send it */ operation->response->header->result = gb_operation_errno_map(errno); diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 562401d77d6d..b8ae70799f81 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -188,6 +188,9 @@ void gb_protocol_put(struct gb_protocol *protocol) if (protocol) WARN_ON(!protocol_count); else + /* FIXME a different message is needed since this one + * will result in a NULL dereference + */ pr_err("protocol id %hhu version %hhu.%hhu not found\n", protocol->id, major, minor); } -- cgit v1.2.3-59-g8ed1b From 76590b1e882f4c275df695ae0641fb2d059cd9c3 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Fri, 16 Jan 2015 13:57:32 -0500 Subject: greybus: gpio-gb: remove checks for negative offset variable offset is defined as unsigned so there is no point checking for negative values of offset. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio-gb.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c index 5fcd01814126..4997588e2617 100644 --- a/drivers/staging/greybus/gpio-gb.c +++ b/drivers/staging/greybus/gpio-gb.c @@ -312,7 +312,7 @@ static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); int ret; - if (offset < 0 || offset >= chip->ngpio) + if (offset >= chip->ngpio) return -EINVAL; ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); if (ret) @@ -325,7 +325,7 @@ static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); int ret; - if (offset < 0 || offset >= chip->ngpio) { + if (offset >= chip->ngpio) { pr_err("bad offset %u supplied (must be 0..%u)\n", offset, chip->ngpio - 1); return; @@ -341,7 +341,7 @@ static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) u8 which; int ret; - if (offset < 0 || offset >= chip->ngpio) + if (offset >= chip->ngpio) return -EINVAL; which = (u8)offset; ret = gb_gpio_get_direction_operation(gb_gpio_controller, which); @@ -355,7 +355,7 @@ static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); int ret; - if (offset < 0 || offset >= chip->ngpio) + if (offset >= chip->ngpio) return -EINVAL; ret = gb_gpio_direction_in_operation(gb_gpio_controller, (u8)offset); if (ret) @@ -369,7 +369,7 @@ static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); int ret; - if (offset < 0 || offset >= chip->ngpio) + if (offset >= chip->ngpio) return -EINVAL; ret = gb_gpio_direction_out_operation(gb_gpio_controller, (u8)offset, !!value); if (ret) @@ -383,7 +383,7 @@ static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) u8 which; int ret; - if (offset < 0 || offset >= chip->ngpio) + if (offset >= chip->ngpio) return -EINVAL; which = (u8)offset; ret = gb_gpio_get_value_operation(gb_gpio_controller, which); @@ -414,7 +414,7 @@ static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, u16 usec; int ret; - if (offset < 0 || offset >= chip->ngpio) + if (offset >= chip->ngpio) return -EINVAL; if (debounce > (unsigned int)U16_MAX) return -EINVAL; @@ -428,7 +428,7 @@ static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - if (offset < 0 || offset >= chip->ngpio) + if (offset >= chip->ngpio) return -EINVAL; return 0; /* XXX */ -- cgit v1.2.3-59-g8ed1b From 7bad4e85b8f988a182cce7949591923e091a509e Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Wed, 14 Jan 2015 16:19:26 -0500 Subject: greybus: gb_operation: replace timeout workqueue If an operation is issued and the response never comes back, gb_operation_timeout() cancels the operation but never wakes up the waiter in gb_operation_request_send(). This patch removes the timeout workqueue and changes the request wait to wait_for_completion_interruptible_timeout(), with timeout set to OPERATION_TIMEOUT_DEFAULT. Signed-off-by: Perry Hung Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 34 +++++++++------------------------- drivers/staging/greybus/operation.h | 1 - 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f014906c0259..0ee44751fb62 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -248,16 +248,6 @@ static void gb_operation_work(struct work_struct *work) gb_operation_put(operation); } -/* - * Timeout call for the operation. - */ -static void gb_operation_timeout(struct work_struct *work) -{ - struct gb_operation *operation; - - operation = container_of(work, struct gb_operation, timeout_work.work); - gb_operation_cancel(operation, -ETIMEDOUT); -} /* * Given a pointer to the header in a message sent on a given host @@ -533,7 +523,6 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, INIT_WORK(&operation->work, gb_operation_work); operation->callback = NULL; /* set at submit time */ init_completion(&operation->completion); - INIT_DELAYED_WORK(&operation->timeout_work, gb_operation_timeout); kref_init(&operation->kref); spin_lock_irq(&gb_operations_lock); @@ -652,7 +641,6 @@ int gb_operation_request_send(struct gb_operation *operation, { struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; - unsigned long timeout; unsigned int cycle; if (connection->state != GB_CONNECTION_STATE_ENABLED) @@ -680,14 +668,6 @@ int gb_operation_request_send(struct gb_operation *operation, header = operation->request->header; header->operation_id = cpu_to_le16(operation->id); - /* - * We impose a time limit for requests to complete. We need - * to set the timer before we send the request though, so we - * don't lose a race with the receipt of the resposne. - */ - timeout = msecs_to_jiffies(OPERATION_TIMEOUT_DEFAULT); - schedule_delayed_work(&operation->timeout_work, timeout); - /* All set, send the request */ gb_operation_result_set(operation, -EINPROGRESS); @@ -703,15 +683,21 @@ int gb_operation_request_send(struct gb_operation *operation, int gb_operation_request_send_sync(struct gb_operation *operation) { int ret; + unsigned long timeout; ret = gb_operation_request_send(operation, gb_operation_sync_callback); if (ret) return ret; - /* Cancel the operation if interrupted */ - ret = wait_for_completion_interruptible(&operation->completion); - if (ret < 0) + timeout = msecs_to_jiffies(OPERATION_TIMEOUT_DEFAULT); + ret = wait_for_completion_interruptible_timeout(&operation->completion, timeout); + if (ret < 0) { + /* Cancel the operation if interrupted */ gb_operation_cancel(operation, -ECANCELED); + } else if (ret == 0) { + /* Cancel the operation if op timed out */ + gb_operation_cancel(operation, -ETIMEDOUT); + } return gb_operation_result(operation); } @@ -843,8 +829,6 @@ static void gb_connection_recv_response(struct gb_connection *connection, return; } - cancel_delayed_work(&operation->timeout_work); - message = operation->response; message_size = sizeof(*message->header) + message->payload_size; if (!errno && size != message_size) { diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index e6da8276619e..6784f9832c8e 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -88,7 +88,6 @@ struct gb_operation { struct work_struct work; gb_operation_callback callback; /* If asynchronous */ struct completion completion; /* Used if no callback */ - struct delayed_work timeout_work; struct kref kref; struct list_head links; /* connection->operations */ -- cgit v1.2.3-59-g8ed1b From a1f2e40b1a30c2b8a2dfc40d859550081195b57a Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Wed, 14 Jan 2015 16:19:27 -0500 Subject: greybus: gb_operation: drop operation refcount on cancel An extra reference is taken out on an operation in gb_operation_request_send(). If the response never arrives, we need to put back the reference. Signed-off-by: Perry Hung Tested-by: Mitchell Tasman Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 0ee44751fb62..82ff306d15e3 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -895,6 +895,7 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) gb_message_cancel(operation->request); gb_message_cancel(operation->response); } + gb_operation_put(operation); } /** -- cgit v1.2.3-59-g8ed1b From b976266051569557377ce63984b1562a8651838c Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 20 Jan 2015 16:31:45 +0000 Subject: greybus: es1: remove useless statement just return the result of usb_alloc_urb up, no need to rededunt check for NULL Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gb-es1.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/gb-es1.c b/drivers/staging/greybus/gb-es1.c index 27416e440ede..f44c26d21a20 100644 --- a/drivers/staging/greybus/gb-es1.c +++ b/drivers/staging/greybus/gb-es1.c @@ -180,8 +180,6 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) dev_err(&es1->usb_dev->dev, "No free CPort OUT urbs, having to dynamically allocate one!\n"); urb = usb_alloc_urb(0, gfp_mask); - if (!urb) - return NULL; return urb; } -- cgit v1.2.3-59-g8ed1b From 0008d9d0ad0a2e854f2f3f61d58cdc44d98760ff Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 20 Jan 2015 16:38:44 +0000 Subject: greybus: es1: release urb on error path if error is return when submiting the urb, we need to make sure to release the urb from the pool, or from the dinamicly allocated. As in it, factor out the free code and create the free_urb function. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gb-es1.c | 43 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/gb-es1.c b/drivers/staging/greybus/gb-es1.c index f44c26d21a20..96c7c816b590 100644 --- a/drivers/staging/greybus/gb-es1.c +++ b/drivers/staging/greybus/gb-es1.c @@ -184,6 +184,28 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) return urb; } +static void free_urb(struct es1_ap_dev *es1, struct urb *urb) +{ + unsigned long flags; + int i; + /* + * See if this was an urb in our pool, if so mark it "free", otherwise + * we need to free it ourselves. + */ + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (urb == es1->cport_out_urb[i]) { + es1->cport_out_urb_busy[i] = false; + urb = NULL; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + + /* If urb is not NULL, then we need to free this urb */ + usb_free_urb(urb); +} + /* * Returns an opaque cookie value if successful, or a pointer coded * error otherwise. If the caller wishes to cancel the in-flight @@ -238,6 +260,7 @@ static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); + free_urb(es1, urb); return ERR_PTR(retval); } @@ -413,10 +436,8 @@ static void cport_out_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; struct es1_ap_dev *es1 = hd_to_es1(hd); - unsigned long flags; int status = check_urb_status(urb); u8 *data = urb->transfer_buffer + 1; - int i; /* * Tell the submitter that the buffer send (attempt) is @@ -426,23 +447,7 @@ static void cport_out_callback(struct urb *urb) data = urb->transfer_buffer + 1; greybus_data_sent(hd, data, status); - /* - * See if this was an urb in our pool, if so mark it "free", otherwise - * we need to free it ourselves. - */ - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (urb == es1->cport_out_urb[i]) { - es1->cport_out_urb_busy[i] = false; - urb = NULL; - break; - } - } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - - /* If urb is not NULL, then we need to free this urb */ - usb_free_urb(urb); - + free_urb(es1, urb); /* * Rest assured Greg, this craziness is getting fixed. * -- cgit v1.2.3-59-g8ed1b From 580c47d8b4643730be2aef344562953377239ea3 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 20 Jan 2015 17:26:18 +0000 Subject: greybus: protocol: dedup protocol find code in protocol register replace the protocol find code with the call to the already existing function. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index b8ae70799f81..12bbc2f2191f 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -20,6 +20,13 @@ static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) { struct gb_protocol *protocol; + /* + * The protocols list is sorted first by protocol id (low to + * high), then by major version (high to low), and finally + * by minor version (high to low). Searching only by + * protocol id will produce the newest implemented version + * of the protocol. + */ list_for_each_entry(protocol, &gb_protocols, links) { if (protocol->id < id) continue; @@ -50,34 +57,12 @@ int __gb_protocol_register(struct gb_protocol *protocol, struct module *module) protocol->owner = module; - /* - * The protocols list is sorted first by protocol id (low to - * high), then by major version (high to low), and finally - * by minor version (high to low). Searching only by - * protocol id will produce the newest implemented version - * of the protocol. - */ spin_lock_irq(&gb_protocols_lock); - list_for_each_entry(existing, &gb_protocols, links) { - if (existing->id < id) - continue; - if (existing->id > id) - break; - - if (existing->major > major) - continue; - if (existing->major < major) - break; - - if (existing->minor > minor) - continue; - if (existing->minor < minor) - break; - - /* A matching protocol has already been registered */ + /* check if the protocol already wos registered */ + existing = _gb_protocol_find(id, major, minor); + if (existing) { spin_unlock_irq(&gb_protocols_lock); - return -EEXIST; } -- cgit v1.2.3-59-g8ed1b From 2ec515bfff5b13c4efd0d260683b1213f3069ab4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 Jan 2015 09:47:21 +0800 Subject: greybus: es1: no need to assign a variable we return directly In next_free_urb(), just return usb_alloc_urb(), don't waste the time assigning it to a local variable that we then return. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gb-es1.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/gb-es1.c b/drivers/staging/greybus/gb-es1.c index 96c7c816b590..2ec5d7b403a3 100644 --- a/drivers/staging/greybus/gb-es1.c +++ b/drivers/staging/greybus/gb-es1.c @@ -179,9 +179,7 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) */ dev_err(&es1->usb_dev->dev, "No free CPort OUT urbs, having to dynamically allocate one!\n"); - urb = usb_alloc_urb(0, gfp_mask); - - return urb; + return usb_alloc_urb(0, gfp_mask); } static void free_urb(struct es1_ap_dev *es1, struct urb *urb) -- cgit v1.2.3-59-g8ed1b From f587027e793cf8947c7cc408a2167db2b8218b15 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 Jan 2015 10:24:15 +0800 Subject: greybus: es2: add ES2 Greybus Host driver This is just a copy of ES1 for now, things will start to diverge soon. Any common functionality will be factored out over time. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/gb-es2.c | 618 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 619 insertions(+) create mode 100644 drivers/staging/greybus/gb-es2.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 5a7c58deb92a..a5d5470974ba 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -22,6 +22,7 @@ obj-m += gb-phy.o obj-m += gb-vibrator.o obj-m += gb-battery.o obj-m += gb-es1.o +obj-m += gb-es2.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/gb-es2.c b/drivers/staging/greybus/gb-es2.c new file mode 100644 index 000000000000..4154cce52a16 --- /dev/null +++ b/drivers/staging/greybus/gb-es2.c @@ -0,0 +1,618 @@ +/* + * Greybus "AP" USB driver for "ES2" controller chips + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include +#include +#include +#include +#include +#include + +#include "greybus.h" +#include "svc_msg.h" +#include "kernel_ver.h" + +/* + * Macros for making pointers explicitly opaque, such that the result + * isn't valid but also can't be mistaken for an ERR_PTR() value. + */ +#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) +#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) + +/* Memory sizes for the buffers sent to/from the ES1 controller */ +#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) +#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE + +static const struct usb_device_id id_table[] = { + /* Made up numbers for the SVC USB Bridge in ES1 */ + { USB_DEVICE(0xffff, 0x0001) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* + * Number of CPort IN urbs in flight at any point in time. + * Adjust if we are having stalls in the USB buffer due to not enough urbs in + * flight. + */ +#define NUM_CPORT_IN_URB 4 + +/* Number of CPort OUT urbs in flight at any point in time. + * Adjust if we get messages saying we are out of urbs in the system log. + */ +#define NUM_CPORT_OUT_URB 8 + +/** + * es1_ap_dev - ES1 USB Bridge to AP structure + * @usb_dev: pointer to the USB device we are. + * @usb_intf: pointer to the USB interface we are bound to. + * @hd: pointer to our greybus_host_device structure + * @control_endpoint: endpoint to send data to SVC + * @svc_endpoint: endpoint for SVC data in + * @cport_in_endpoint: bulk in endpoint for CPort data + * @cport-out_endpoint: bulk out endpoint for CPort data + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint + * @svc_urb: urb for SVC messages coming in on @svc_endpoint + * @cport_in_urb: array of urbs for the CPort in messages + * @cport_in_buffer: array of buffers for the @cport_in_urb urbs + * @cport_out_urb: array of urbs for the CPort out messages + * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or + * not. + * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" + */ +struct es1_ap_dev { + struct usb_device *usb_dev; + struct usb_interface *usb_intf; + struct greybus_host_device *hd; + + __u8 control_endpoint; + __u8 svc_endpoint; + __u8 cport_in_endpoint; + __u8 cport_out_endpoint; + + u8 *svc_buffer; + struct urb *svc_urb; + + struct urb *cport_in_urb[NUM_CPORT_IN_URB]; + u8 *cport_in_buffer[NUM_CPORT_IN_URB]; + struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; + bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; + spinlock_t cport_out_urb_lock; +}; + +static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) +{ + return (struct es1_ap_dev *)&hd->hd_priv; +} + +static void cport_out_callback(struct urb *urb); + +/* + * Buffer constraints for the host driver. + * + * A "buffer" is used to hold data to be transferred for Greybus by + * the host driver. A buffer is represented by a "buffer pointer", + * which defines a region of memory used by the host driver for + * transferring the data. When Greybus allocates a buffer, it must + * do so subject to the constraints associated with the host driver. + * These constraints are specified by two parameters: the + * headroom; and the maximum buffer size. + * + * +------------------+ + * | Host driver | \ + * | reserved area | }- headroom + * | . . . | / + * buffer pointer ---> +------------------+ + * | Buffer space for | \ + * | transferred data | }- buffer size + * | . . . | / (limited to size_max) + * +------------------+ + * + * headroom: Every buffer must have at least this much space + * *before* the buffer pointer, reserved for use by the + * host driver. I.e., ((char *)buffer - headroom) must + * point to valid memory, usable only by the host driver. + * size_max: The maximum size of a buffer (not including the + * headroom) must not exceed this. + */ +static void hd_buffer_constraints(struct greybus_host_device *hd) +{ + /* + * Only one byte is required, but this produces a result + * that's better aligned for the user. + */ + hd->buffer_headroom = sizeof(u32); /* For cport id */ + hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; + BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); +} + +#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + int retval; + + /* SVC messages go down our control pipe */ + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + 0x01, /* vendor request AP message */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + (char *)svc_msg, + sizeof(*svc_msg), + ES1_TIMEOUT); + if (retval != sizeof(*svc_msg)) + return retval; + + return 0; +} + +static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) +{ + struct urb *urb = NULL; + unsigned long flags; + int i; + + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + + /* Look in our pool of allocated urbs first, as that's the "fastest" */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (es1->cport_out_urb_busy[i] == false) { + es1->cport_out_urb_busy[i] = true; + urb = es1->cport_out_urb[i]; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + if (urb) + return urb; + + /* + * Crap, pool is empty, complain to the syslog and go allocate one + * dynamically as we have to succeed. + */ + dev_err(&es1->usb_dev->dev, + "No free CPort OUT urbs, having to dynamically allocate one!\n"); + return usb_alloc_urb(0, gfp_mask); +} + +static void free_urb(struct es1_ap_dev *es1, struct urb *urb) +{ + unsigned long flags; + int i; + /* + * See if this was an urb in our pool, if so mark it "free", otherwise + * we need to free it ourselves. + */ + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (urb == es1->cport_out_urb[i]) { + es1->cport_out_urb_busy[i] = false; + urb = NULL; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + + /* If urb is not NULL, then we need to free this urb */ + usb_free_urb(urb); +} + +/* + * Returns an opaque cookie value if successful, or a pointer coded + * error otherwise. If the caller wishes to cancel the in-flight + * buffer, it must supply the returned cookie to the cancel routine. + */ +static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, + void *buffer, size_t buffer_size, gfp_t gfp_mask) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + u8 *transfer_buffer = buffer; + int transfer_buffer_size; + int retval; + struct urb *urb; + + if (!buffer) { + pr_err("null buffer supplied to send\n"); + return ERR_PTR(-EINVAL); + } + if (buffer_size > (size_t)INT_MAX) { + pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); + return ERR_PTR(-EINVAL); + } + transfer_buffer--; + transfer_buffer_size = buffer_size + 1; + + /* + * The data actually transferred will include an indication + * of where the data should be sent. Do one last check of + * the target CPort id before filling it in. + */ + if (cport_id == CPORT_ID_BAD) { + pr_err("request to send inbound data buffer\n"); + return ERR_PTR(-EINVAL); + } + if (cport_id > (u16)U8_MAX) { + pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); + return ERR_PTR(-EINVAL); + } + /* OK, the destination is fine; record it in the transfer buffer */ + *transfer_buffer = cport_id; + + /* Find a free urb */ + urb = next_free_urb(es1, gfp_mask); + if (!urb) + return ERR_PTR(-ENOMEM); + + usb_fill_bulk_urb(urb, udev, + usb_sndbulkpipe(udev, es1->cport_out_endpoint), + transfer_buffer, transfer_buffer_size, + cport_out_callback, hd); + retval = usb_submit_urb(urb, gfp_mask); + if (retval) { + pr_err("error %d submitting URB\n", retval); + free_urb(es1, urb); + return ERR_PTR(retval); + } + + return conceal_urb(urb); +} + +/* + * The cookie value supplied is the value that buffer_send() + * returned to its caller. It identifies the buffer that should be + * canceled. This function must also handle (which is to say, + * ignore) a null cookie value. + */ +static void buffer_cancel(void *cookie) +{ + + /* + * We really should be defensive and track all outstanding + * (sent) buffers rather than trusting the cookie provided + * is valid. For the time being, this will do. + */ + if (cookie) + usb_kill_urb(reveal_urb(cookie)); +} + +static struct greybus_host_driver es1_driver = { + .hd_priv_size = sizeof(struct es1_ap_dev), + .buffer_send = buffer_send, + .buffer_cancel = buffer_cancel, + .submit_svc = submit_svc, +}; + +/* Common function to report consistent warnings based on URB status */ +static int check_urb_status(struct urb *urb) +{ + struct device *dev = &urb->dev->dev; + int status = urb->status; + + switch (status) { + case 0: + return 0; + + case -EOVERFLOW: + dev_err(dev, "%s: overflow actual length is %d\n", + __func__, urb->actual_length); + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EILSEQ: + case -EPROTO: + /* device is gone, stop sending */ + return status; + } + dev_err(dev, "%s: unknown status %d\n", __func__, status); + + return -EAGAIN; +} + +static void ap_disconnect(struct usb_interface *interface) +{ + struct es1_ap_dev *es1; + struct usb_device *udev; + int i; + + es1 = usb_get_intfdata(interface); + if (!es1) + return; + + /* Tear down everything! */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + struct urb *urb = es1->cport_out_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + es1->cport_out_urb[i] = NULL; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ + } + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb = es1->cport_in_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + kfree(es1->cport_in_buffer[i]); + es1->cport_in_buffer[i] = NULL; + } + + usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); + es1->svc_urb = NULL; + kfree(es1->svc_buffer); + es1->svc_buffer = NULL; + + usb_set_intfdata(interface, NULL); + udev = es1->usb_dev; + greybus_remove_hd(es1->hd); + + usb_put_dev(udev); +} + +/* Callback for when we get a SVC message */ +static void svc_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb svc in error %d (dropped)\n", status); + return; + } + + /* We have a message, create a new message structure, add it to the + * list, and wake up our thread that will process the messages. + */ + greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); + +exit: + /* resubmit the urb to get more messages */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "Can not submit urb for AP data: %d\n", retval); +} + +static void cport_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + u16 cport_id; + u8 *data; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb cport in error %d (dropped)\n", status); + return; + } + + /* The size has to be at least one, for the cport id */ + if (!urb->actual_length) { + dev_err(dev, "%s: no cport id in input buffer?\n", __func__); + goto exit; + } + + /* + * Our CPort number is the first byte of the data stream, + * the rest of the stream is "real" data + */ + data = urb->transfer_buffer; + cport_id = (u16)data[0]; + data = &data[1]; + + /* Pass this data to the greybus core */ + greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); + +exit: + /* put our urb back in the request pool */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "%s: error %d in submitting urb.\n", + __func__, retval); +} + +static void cport_out_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct es1_ap_dev *es1 = hd_to_es1(hd); + int status = check_urb_status(urb); + u8 *data = urb->transfer_buffer + 1; + + /* + * Tell the submitter that the buffer send (attempt) is + * complete, and report the status. The submitter's buffer + * starts after the one-byte CPort id we inserted. + */ + data = urb->transfer_buffer + 1; + greybus_data_sent(hd, data, status); + + free_urb(es1, urb); + /* + * Rest assured Greg, this craziness is getting fixed. + * + * Yes, you are right, we aren't telling anyone that the urb finished. + * "That's crazy! How does this all even work?" you might be saying. + * The "magic" is the idea that greybus works on the "operation" level, + * not the "send a buffer" level. All operations are "round-trip" with + * a response from the device that the operation finished, or it will + * time out. Because of that, we don't care that this urb finished, or + * failed, or did anything else, as higher levels of the protocol stack + * will handle completions and timeouts and the rest. + * + * This protocol is "needed" due to some hardware restrictions on the + * current generation of Unipro controllers. Think about it for a + * minute, this is a USB driver, talking to a Unipro bridge, impedance + * mismatch is huge, yet the Unipro controller are even more + * underpowered than this little USB controller. We rely on the round + * trip to keep stalls in the Unipro controllers from happening so that + * we can keep data flowing properly, no matter how slow it might be. + * + * Once again, a wonderful bus protocol cut down in its prime by a naive + * controller chip. We dream of the day we have a "real" HCD for + * Unipro. Until then, we suck it up and make the hardware work, as + * that's the job of the firmware and kernel. + * + */ +} + +/* + * The ES1 USB Bridge device contains 4 endpoints + * 1 Control - usual USB stuff + AP -> SVC messages + * 1 Interrupt IN - SVC -> AP messages + * 1 Bulk IN - CPort data in + * 1 Bulk OUT - CPort data out + */ +static int ap_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct es1_ap_dev *es1; + struct greybus_host_device *hd; + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + bool int_in_found = false; + bool bulk_in_found = false; + bool bulk_out_found = false; + int retval = -ENOMEM; + int i; + u8 svc_interval = 0; + + udev = usb_get_dev(interface_to_usbdev(interface)); + + hd = greybus_create_hd(&es1_driver, &udev->dev); + if (!hd) { + usb_put_dev(udev); + return -ENOMEM; + } + + /* Fill in the buffer allocation constraints */ + hd_buffer_constraints(hd); + + es1 = hd_to_es1(hd); + es1->hd = hd; + es1->usb_intf = interface; + es1->usb_dev = udev; + spin_lock_init(&es1->cport_out_urb_lock); + usb_set_intfdata(interface, es1); + + /* Control endpoint is the pipe to talk to this AP, so save it off */ + endpoint = &udev->ep0.desc; + es1->control_endpoint = endpoint->bEndpointAddress; + + /* find all 3 of our endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) { + es1->svc_endpoint = endpoint->bEndpointAddress; + svc_interval = endpoint->bInterval; + int_in_found = true; + } else if (usb_endpoint_is_bulk_in(endpoint)) { + es1->cport_in_endpoint = endpoint->bEndpointAddress; + bulk_in_found = true; + } else if (usb_endpoint_is_bulk_out(endpoint)) { + es1->cport_out_endpoint = endpoint->bEndpointAddress; + bulk_out_found = true; + } else { + dev_err(&udev->dev, + "Unknown endpoint type found, address %x\n", + endpoint->bEndpointAddress); + } + } + if ((int_in_found == false) || + (bulk_in_found == false) || + (bulk_out_found == false)) { + dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); + goto error; + } + + /* Create our buffer and URB to get SVC messages, and start it up */ + es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); + if (!es1->svc_buffer) + goto error; + + es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!es1->svc_urb) + goto error; + + usb_fill_int_urb(es1->svc_urb, udev, + usb_rcvintpipe(udev, es1->svc_endpoint), + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, + hd, svc_interval); + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + + /* Allocate buffers for our cport in messages and start them up */ + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb; + u8 *buffer; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error; + buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); + if (!buffer) + goto error; + + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, es1->cport_in_endpoint), + buffer, ES1_GBUF_MSG_SIZE_MAX, + cport_in_callback, hd); + es1->cport_in_urb[i] = urb; + es1->cport_in_buffer[i] = buffer; + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) + goto error; + } + + /* Allocate urbs for our CPort OUT messages */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + struct urb *urb; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error; + + es1->cport_out_urb[i] = urb; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ + } + + return 0; +error: + ap_disconnect(interface); + + return retval; +} + +static struct usb_driver es1_ap_driver = { + .name = "es1_ap_driver", + .probe = ap_probe, + .disconnect = ap_disconnect, + .id_table = id_table, +}; + +module_usb_driver(es1_ap_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From 5357cf323110ee4a3f4a12870618eca28672c7b9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Jan 2015 16:10:40 +0530 Subject: greybus: Remove "-gb" suffix from .c files Some files are prefixed with "gb-" and some are suffixed with "-gb". The rationale behind the first one is that the modules would be named so, i.e. gb-*.ko. But there is no reason to keep the "-gb" suffix in the second case. Remove the unnecessary suffix. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 12 +- drivers/staging/greybus/gpio-gb.c | 544 -------------------------- drivers/staging/greybus/gpio.c | 544 ++++++++++++++++++++++++++ drivers/staging/greybus/i2c-gb.c | 450 ---------------------- drivers/staging/greybus/i2c.c | 450 ++++++++++++++++++++++ drivers/staging/greybus/pwm-gb.c | 331 ---------------- drivers/staging/greybus/pwm.c | 331 ++++++++++++++++ drivers/staging/greybus/sdio-gb.c | 99 ----- drivers/staging/greybus/sdio.c | 99 +++++ drivers/staging/greybus/uart-gb.c | 787 -------------------------------------- drivers/staging/greybus/uart.c | 787 ++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/usb-gb.c | 396 ------------------- drivers/staging/greybus/usb.c | 396 +++++++++++++++++++ 13 files changed, 2613 insertions(+), 2613 deletions(-) delete mode 100644 drivers/staging/greybus/gpio-gb.c create mode 100644 drivers/staging/greybus/gpio.c delete mode 100644 drivers/staging/greybus/i2c-gb.c create mode 100644 drivers/staging/greybus/i2c.c delete mode 100644 drivers/staging/greybus/pwm-gb.c create mode 100644 drivers/staging/greybus/pwm.c delete mode 100644 drivers/staging/greybus/sdio-gb.c create mode 100644 drivers/staging/greybus/sdio.c delete mode 100644 drivers/staging/greybus/uart-gb.c create mode 100644 drivers/staging/greybus/uart.c delete mode 100644 drivers/staging/greybus/usb-gb.c create mode 100644 drivers/staging/greybus/usb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index a5d5470974ba..55b4a37c58fb 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -10,12 +10,12 @@ greybus-y := core.o \ operation.o gb-phy-y := gpb.o \ - sdio-gb.o \ - uart-gb.o \ - pwm-gb.o \ - gpio-gb.o \ - i2c-gb.o \ - usb-gb.o + sdio.o \ + uart.o \ + pwm.o \ + gpio.o \ + i2c.o \ + usb.o obj-m += greybus.o obj-m += gb-phy.o diff --git a/drivers/staging/greybus/gpio-gb.c b/drivers/staging/greybus/gpio-gb.c deleted file mode 100644 index 4997588e2617..000000000000 --- a/drivers/staging/greybus/gpio-gb.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * GPIO Greybus driver. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include "greybus.h" - -struct gb_gpio_line { - /* The following has to be an array of line_max entries */ - /* --> make them just a flags field */ - u8 active: 1, - direction: 1, /* 0 = output, 1 = input */ - value: 1; /* 0 = low, 1 = high */ - u16 debounce_usec; -}; - -struct gb_gpio_controller { - struct gb_connection *connection; - u8 version_major; - u8 version_minor; - u8 line_max; /* max line number */ - struct gb_gpio_line *lines; - - struct gpio_chip chip; -}; -#define gpio_chip_to_gb_gpio_controller(chip) \ - container_of(chip, struct gb_gpio_controller, chip) - -/* Version of the Greybus GPIO protocol we support */ -#define GB_GPIO_VERSION_MAJOR 0x00 -#define GB_GPIO_VERSION_MINOR 0x01 - -/* Greybus GPIO request types */ -#define GB_GPIO_TYPE_INVALID 0x00 -#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 -#define GB_GPIO_TYPE_LINE_COUNT 0x02 -#define GB_GPIO_TYPE_ACTIVATE 0x03 -#define GB_GPIO_TYPE_DEACTIVATE 0x04 -#define GB_GPIO_TYPE_GET_DIRECTION 0x05 -#define GB_GPIO_TYPE_DIRECTION_IN 0x06 -#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 -#define GB_GPIO_TYPE_GET_VALUE 0x08 -#define GB_GPIO_TYPE_SET_VALUE 0x09 -#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a -#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ - -/* version request has no payload */ -struct gb_gpio_proto_version_response { - __u8 major; - __u8 minor; -}; - -/* line count request has no payload */ -struct gb_gpio_line_count_response { - __u8 count; -}; - -struct gb_gpio_activate_request { - __u8 which; -}; -/* activate response has no payload */ - -struct gb_gpio_deactivate_request { - __u8 which; -}; -/* deactivate response has no payload */ - -struct gb_gpio_get_direction_request { - __u8 which; -}; -struct gb_gpio_get_direction_response { - __u8 direction; -}; - -struct gb_gpio_direction_in_request { - __u8 which; -}; -/* direction in response has no payload */ - -struct gb_gpio_direction_out_request { - __u8 which; - __u8 value; -}; -/* direction out response has no payload */ - -struct gb_gpio_get_value_request { - __u8 which; -}; -struct gb_gpio_get_value_response { - __u8 value; -}; - -struct gb_gpio_set_value_request { - __u8 which; - __u8 value; -}; -/* set value response has no payload */ - -struct gb_gpio_set_debounce_request { - __u8 which; - __le16 usec; -}; -/* debounce response has no payload */ - - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int gb_gpio_proto_version_operation(struct gb_gpio_controller *ggc) -{ - struct gb_gpio_proto_version_response response; - int ret; - - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_GPIO_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_GPIO_VERSION_MAJOR); - return -ENOTSUPP; - } - ggc->version_major = response.major; - ggc->version_minor = response.minor; - return 0; -} - -static int gb_gpio_line_count_operation(struct gb_gpio_controller *ggc) -{ - struct gb_gpio_line_count_response response; - int ret; - - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_LINE_COUNT, - NULL, 0, &response, sizeof(response)); - if (!ret) - ggc->line_max = response.count; - return ret; -} - -static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) -{ - struct gb_gpio_activate_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_ACTIVATE, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].active = true; - return ret; -} - -static int gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, - u8 which) -{ - struct gb_gpio_deactivate_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DEACTIVATE, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].active = false; - return ret; -} - -static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, - u8 which) -{ - struct gb_gpio_get_direction_request request; - struct gb_gpio_get_direction_response response; - int ret; - u8 direction; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_DIRECTION, - &request, sizeof(request), - &response, sizeof(response)); - if (ret) - return ret; - - direction = response.direction; - if (direction && direction != 1) - pr_warn("gpio %u direction was %u (should be 0 or 1)\n", - which, direction); - ggc->lines[which].direction = direction ? 1 : 0; - return 0; -} - -static int gb_gpio_direction_in_operation(struct gb_gpio_controller *ggc, - u8 which) -{ - struct gb_gpio_direction_in_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_IN, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].direction = 1; - return ret; -} - -static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, - u8 which, bool value_high) -{ - struct gb_gpio_direction_out_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - request.value = value_high ? 1 : 0; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_OUT, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].direction = 0; - return ret; -} - -static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, - u8 which) -{ - struct gb_gpio_get_value_request request; - struct gb_gpio_get_value_response response; - int ret; - u8 value; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_VALUE, - &request, sizeof(request), - &response, sizeof(response)); - if (ret) - return ret; - - value = response.value; - if (value && value != 1) - pr_warn("gpio %u value was %u (should be 0 or 1)\n", - which, value); - ggc->lines[which].value = value ? 1 : 0; - return 0; -} - -static int gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, - u8 which, bool value_high) -{ - struct gb_gpio_set_value_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - request.value = value_high ? 1 : 0; - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, - &request, sizeof(request), NULL, 0); - if (!ret) { - /* XXX should this set direction to out? */ - ggc->lines[which].value = request.value; - } - return ret; -} - -static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, - u8 which, u16 debounce_usec) -{ - struct gb_gpio_set_debounce_request request; - int ret; - - if (which > ggc->line_max) - return -EINVAL; - - request.which = which; - request.usec = cpu_to_le16(debounce_usec); - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_DEBOUNCE, - &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].debounce_usec = debounce_usec; - return ret; -} - -static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); - if (ret) - ; /* return ret; */ - return 0; -} - -static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset >= chip->ngpio) { - pr_err("bad offset %u supplied (must be 0..%u)\n", - offset, chip->ngpio - 1); - return; - } - ret = gb_gpio_deactivate_operation(gb_gpio_controller, (u8)offset); - if (ret) - ; /* return ret; */ -} - -static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u8 which; - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - which = (u8)offset; - ret = gb_gpio_get_direction_operation(gb_gpio_controller, which); - if (ret) - ; /* return ret; */ - return gb_gpio_controller->lines[which].direction ? 1 : 0; -} - -static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - ret = gb_gpio_direction_in_operation(gb_gpio_controller, (u8)offset); - if (ret) - ; /* return ret; */ - return 0; -} - -static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - ret = gb_gpio_direction_out_operation(gb_gpio_controller, (u8)offset, !!value); - if (ret) - ; /* return ret; */ - return 0; -} - -static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u8 which; - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - which = (u8)offset; - ret = gb_gpio_get_value_operation(gb_gpio_controller, which); - if (ret) - return ret; - return (int)gb_gpio_controller->lines[which].value; -} - -static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - int ret; - - if (offset < 0 || offset >= chip->ngpio) { - pr_err("bad offset %u supplied (must be 0..%u)\n", - offset, chip->ngpio - 1); - return; - } - ret = gb_gpio_set_value_operation(gb_gpio_controller, (u8)offset, !!value); - if (ret) - ; /* return ret; */ -} - -static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, - unsigned debounce) -{ - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); - u16 usec; - int ret; - - if (offset >= chip->ngpio) - return -EINVAL; - if (debounce > (unsigned int)U16_MAX) - return -EINVAL; - usec = (u8)debounce; - ret = gb_gpio_set_debounce_operation(gb_gpio_controller, (u8)offset, usec); - if (ret) - ; /* return ret; */ - - return 0; /* XXX */ -} - -static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - if (offset >= chip->ngpio) - return -EINVAL; - - return 0; /* XXX */ -} - -static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) -{ - return; /* XXX */ -} - -static int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controller) -{ - u32 line_count; - size_t size; - int ret; - - /* First thing we need to do is check the version */ - ret = gb_gpio_proto_version_operation(gb_gpio_controller); - if (ret) - ; /* return ret; */ - - /* Now find out how many lines there are */ - ret = gb_gpio_line_count_operation(gb_gpio_controller); - if (ret) - ; /* return ret; */ - line_count = (u32)gb_gpio_controller->line_max + 1; - size = line_count * sizeof(*gb_gpio_controller->lines); - gb_gpio_controller->lines = kzalloc(size, GFP_KERNEL); - if (!gb_gpio_controller->lines) - return -ENOMEM; - - return ret; -} - -static int gb_gpio_connection_init(struct gb_connection *connection) -{ - struct gb_gpio_controller *gb_gpio_controller; - struct gpio_chip *gpio; - int ret; - - gb_gpio_controller = kzalloc(sizeof(*gb_gpio_controller), GFP_KERNEL); - if (!gb_gpio_controller) - return -ENOMEM; - gb_gpio_controller->connection = connection; - connection->private = gb_gpio_controller; - - ret = gb_gpio_controller_setup(gb_gpio_controller); - if (ret) - goto out_err; - - gpio = &gb_gpio_controller->chip; - - gpio->label = "greybus_gpio"; - gpio->owner = THIS_MODULE; /* XXX Module get? */ - - gpio->request = gb_gpio_request; - gpio->free = gb_gpio_free; - gpio->get_direction = gb_gpio_get_direction; - gpio->direction_input = gb_gpio_direction_input; - gpio->direction_output = gb_gpio_direction_output; - gpio->get = gb_gpio_get; - gpio->set = gb_gpio_set; - gpio->set_debounce = gb_gpio_set_debounce; - gpio->to_irq = gb_gpio_to_irq; - gpio->dbg_show = gb_gpio_dbg_show; - - gpio->base = -1; /* Allocate base dynamically */ - gpio->ngpio = gb_gpio_controller->line_max + 1; - gpio->can_sleep = true; /* XXX */ - - ret = gpiochip_add(gpio); - if (ret) { - pr_err("Failed to register GPIO\n"); - return ret; - } - - return 0; -out_err: - kfree(gb_gpio_controller); - return ret; -} - -static void gb_gpio_connection_exit(struct gb_connection *connection) -{ - struct gb_gpio_controller *gb_gpio_controller = connection->private; - - if (!gb_gpio_controller) - return; - - gb_gpiochip_remove(&gb_gpio_controller->chip); - /* kref_put(gb_gpio_controller->connection) */ - kfree(gb_gpio_controller); -} - -static struct gb_protocol gpio_protocol = { - .name = "gpio", - .id = GREYBUS_PROTOCOL_GPIO, - .major = 0, - .minor = 1, - .connection_init = gb_gpio_connection_init, - .connection_exit = gb_gpio_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -int gb_gpio_protocol_init(void) -{ - return gb_protocol_register(&gpio_protocol); -} - -void gb_gpio_protocol_exit(void) -{ - gb_protocol_deregister(&gpio_protocol); -} diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c new file mode 100644 index 000000000000..4997588e2617 --- /dev/null +++ b/drivers/staging/greybus/gpio.c @@ -0,0 +1,544 @@ +/* + * GPIO Greybus driver. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_gpio_line { + /* The following has to be an array of line_max entries */ + /* --> make them just a flags field */ + u8 active: 1, + direction: 1, /* 0 = output, 1 = input */ + value: 1; /* 0 = low, 1 = high */ + u16 debounce_usec; +}; + +struct gb_gpio_controller { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + u8 line_max; /* max line number */ + struct gb_gpio_line *lines; + + struct gpio_chip chip; +}; +#define gpio_chip_to_gb_gpio_controller(chip) \ + container_of(chip, struct gb_gpio_controller, chip) + +/* Version of the Greybus GPIO protocol we support */ +#define GB_GPIO_VERSION_MAJOR 0x00 +#define GB_GPIO_VERSION_MINOR 0x01 + +/* Greybus GPIO request types */ +#define GB_GPIO_TYPE_INVALID 0x00 +#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 +#define GB_GPIO_TYPE_LINE_COUNT 0x02 +#define GB_GPIO_TYPE_ACTIVATE 0x03 +#define GB_GPIO_TYPE_DEACTIVATE 0x04 +#define GB_GPIO_TYPE_GET_DIRECTION 0x05 +#define GB_GPIO_TYPE_DIRECTION_IN 0x06 +#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 +#define GB_GPIO_TYPE_GET_VALUE 0x08 +#define GB_GPIO_TYPE_SET_VALUE 0x09 +#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a +#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ + +/* version request has no payload */ +struct gb_gpio_proto_version_response { + __u8 major; + __u8 minor; +}; + +/* line count request has no payload */ +struct gb_gpio_line_count_response { + __u8 count; +}; + +struct gb_gpio_activate_request { + __u8 which; +}; +/* activate response has no payload */ + +struct gb_gpio_deactivate_request { + __u8 which; +}; +/* deactivate response has no payload */ + +struct gb_gpio_get_direction_request { + __u8 which; +}; +struct gb_gpio_get_direction_response { + __u8 direction; +}; + +struct gb_gpio_direction_in_request { + __u8 which; +}; +/* direction in response has no payload */ + +struct gb_gpio_direction_out_request { + __u8 which; + __u8 value; +}; +/* direction out response has no payload */ + +struct gb_gpio_get_value_request { + __u8 which; +}; +struct gb_gpio_get_value_response { + __u8 value; +}; + +struct gb_gpio_set_value_request { + __u8 which; + __u8 value; +}; +/* set value response has no payload */ + +struct gb_gpio_set_debounce_request { + __u8 which; + __le16 usec; +}; +/* debounce response has no payload */ + + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int gb_gpio_proto_version_operation(struct gb_gpio_controller *ggc) +{ + struct gb_gpio_proto_version_response response; + int ret; + + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + if (response.major > GB_GPIO_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_GPIO_VERSION_MAJOR); + return -ENOTSUPP; + } + ggc->version_major = response.major; + ggc->version_minor = response.minor; + return 0; +} + +static int gb_gpio_line_count_operation(struct gb_gpio_controller *ggc) +{ + struct gb_gpio_line_count_response response; + int ret; + + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_LINE_COUNT, + NULL, 0, &response, sizeof(response)); + if (!ret) + ggc->line_max = response.count; + return ret; +} + +static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) +{ + struct gb_gpio_activate_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_ACTIVATE, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].active = true; + return ret; +} + +static int gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, + u8 which) +{ + struct gb_gpio_deactivate_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DEACTIVATE, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].active = false; + return ret; +} + +static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, + u8 which) +{ + struct gb_gpio_get_direction_request request; + struct gb_gpio_get_direction_response response; + int ret; + u8 direction; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_DIRECTION, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) + return ret; + + direction = response.direction; + if (direction && direction != 1) + pr_warn("gpio %u direction was %u (should be 0 or 1)\n", + which, direction); + ggc->lines[which].direction = direction ? 1 : 0; + return 0; +} + +static int gb_gpio_direction_in_operation(struct gb_gpio_controller *ggc, + u8 which) +{ + struct gb_gpio_direction_in_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_IN, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].direction = 1; + return ret; +} + +static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, + u8 which, bool value_high) +{ + struct gb_gpio_direction_out_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + request.value = value_high ? 1 : 0; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_OUT, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].direction = 0; + return ret; +} + +static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, + u8 which) +{ + struct gb_gpio_get_value_request request; + struct gb_gpio_get_value_response response; + int ret; + u8 value; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_VALUE, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) + return ret; + + value = response.value; + if (value && value != 1) + pr_warn("gpio %u value was %u (should be 0 or 1)\n", + which, value); + ggc->lines[which].value = value ? 1 : 0; + return 0; +} + +static int gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, + u8 which, bool value_high) +{ + struct gb_gpio_set_value_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + request.value = value_high ? 1 : 0; + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, + &request, sizeof(request), NULL, 0); + if (!ret) { + /* XXX should this set direction to out? */ + ggc->lines[which].value = request.value; + } + return ret; +} + +static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, + u8 which, u16 debounce_usec) +{ + struct gb_gpio_set_debounce_request request; + int ret; + + if (which > ggc->line_max) + return -EINVAL; + + request.which = which; + request.usec = cpu_to_le16(debounce_usec); + ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_DEBOUNCE, + &request, sizeof(request), NULL, 0); + if (!ret) + ggc->lines[which].debounce_usec = debounce_usec; + return ret; +} + +static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); + if (ret) + ; /* return ret; */ + return 0; +} + +static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset >= chip->ngpio) { + pr_err("bad offset %u supplied (must be 0..%u)\n", + offset, chip->ngpio - 1); + return; + } + ret = gb_gpio_deactivate_operation(gb_gpio_controller, (u8)offset); + if (ret) + ; /* return ret; */ +} + +static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + which = (u8)offset; + ret = gb_gpio_get_direction_operation(gb_gpio_controller, which); + if (ret) + ; /* return ret; */ + return gb_gpio_controller->lines[which].direction ? 1 : 0; +} + +static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + ret = gb_gpio_direction_in_operation(gb_gpio_controller, (u8)offset); + if (ret) + ; /* return ret; */ + return 0; +} + +static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + ret = gb_gpio_direction_out_operation(gb_gpio_controller, (u8)offset, !!value); + if (ret) + ; /* return ret; */ + return 0; +} + +static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u8 which; + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + which = (u8)offset; + ret = gb_gpio_get_value_operation(gb_gpio_controller, which); + if (ret) + return ret; + return (int)gb_gpio_controller->lines[which].value; +} + +static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + int ret; + + if (offset < 0 || offset >= chip->ngpio) { + pr_err("bad offset %u supplied (must be 0..%u)\n", + offset, chip->ngpio - 1); + return; + } + ret = gb_gpio_set_value_operation(gb_gpio_controller, (u8)offset, !!value); + if (ret) + ; /* return ret; */ +} + +static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + u16 usec; + int ret; + + if (offset >= chip->ngpio) + return -EINVAL; + if (debounce > (unsigned int)U16_MAX) + return -EINVAL; + usec = (u8)debounce; + ret = gb_gpio_set_debounce_operation(gb_gpio_controller, (u8)offset, usec); + if (ret) + ; /* return ret; */ + + return 0; /* XXX */ +} + +static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= chip->ngpio) + return -EINVAL; + + return 0; /* XXX */ +} + +static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + return; /* XXX */ +} + +static int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controller) +{ + u32 line_count; + size_t size; + int ret; + + /* First thing we need to do is check the version */ + ret = gb_gpio_proto_version_operation(gb_gpio_controller); + if (ret) + ; /* return ret; */ + + /* Now find out how many lines there are */ + ret = gb_gpio_line_count_operation(gb_gpio_controller); + if (ret) + ; /* return ret; */ + line_count = (u32)gb_gpio_controller->line_max + 1; + size = line_count * sizeof(*gb_gpio_controller->lines); + gb_gpio_controller->lines = kzalloc(size, GFP_KERNEL); + if (!gb_gpio_controller->lines) + return -ENOMEM; + + return ret; +} + +static int gb_gpio_connection_init(struct gb_connection *connection) +{ + struct gb_gpio_controller *gb_gpio_controller; + struct gpio_chip *gpio; + int ret; + + gb_gpio_controller = kzalloc(sizeof(*gb_gpio_controller), GFP_KERNEL); + if (!gb_gpio_controller) + return -ENOMEM; + gb_gpio_controller->connection = connection; + connection->private = gb_gpio_controller; + + ret = gb_gpio_controller_setup(gb_gpio_controller); + if (ret) + goto out_err; + + gpio = &gb_gpio_controller->chip; + + gpio->label = "greybus_gpio"; + gpio->owner = THIS_MODULE; /* XXX Module get? */ + + gpio->request = gb_gpio_request; + gpio->free = gb_gpio_free; + gpio->get_direction = gb_gpio_get_direction; + gpio->direction_input = gb_gpio_direction_input; + gpio->direction_output = gb_gpio_direction_output; + gpio->get = gb_gpio_get; + gpio->set = gb_gpio_set; + gpio->set_debounce = gb_gpio_set_debounce; + gpio->to_irq = gb_gpio_to_irq; + gpio->dbg_show = gb_gpio_dbg_show; + + gpio->base = -1; /* Allocate base dynamically */ + gpio->ngpio = gb_gpio_controller->line_max + 1; + gpio->can_sleep = true; /* XXX */ + + ret = gpiochip_add(gpio); + if (ret) { + pr_err("Failed to register GPIO\n"); + return ret; + } + + return 0; +out_err: + kfree(gb_gpio_controller); + return ret; +} + +static void gb_gpio_connection_exit(struct gb_connection *connection) +{ + struct gb_gpio_controller *gb_gpio_controller = connection->private; + + if (!gb_gpio_controller) + return; + + gb_gpiochip_remove(&gb_gpio_controller->chip); + /* kref_put(gb_gpio_controller->connection) */ + kfree(gb_gpio_controller); +} + +static struct gb_protocol gpio_protocol = { + .name = "gpio", + .id = GREYBUS_PROTOCOL_GPIO, + .major = 0, + .minor = 1, + .connection_init = gb_gpio_connection_init, + .connection_exit = gb_gpio_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +int gb_gpio_protocol_init(void) +{ + return gb_protocol_register(&gpio_protocol); +} + +void gb_gpio_protocol_exit(void) +{ + gb_protocol_deregister(&gpio_protocol); +} diff --git a/drivers/staging/greybus/i2c-gb.c b/drivers/staging/greybus/i2c-gb.c deleted file mode 100644 index c967ae3161c5..000000000000 --- a/drivers/staging/greybus/i2c-gb.c +++ /dev/null @@ -1,450 +0,0 @@ -/* - * I2C bridge driver for the Greybus "generic" I2C module. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include - -#include "greybus.h" - -struct gb_i2c_device { - struct gb_connection *connection; - u8 version_major; - u8 version_minor; - - u32 functionality; - u16 timeout_msec; - u8 retries; - - struct i2c_adapter adapter; -}; - -/* Version of the Greybus i2c protocol we support */ -#define GB_I2C_VERSION_MAJOR 0x00 -#define GB_I2C_VERSION_MINOR 0x01 - -/* Greybus i2c request types */ -#define GB_I2C_TYPE_INVALID 0x00 -#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 -#define GB_I2C_TYPE_FUNCTIONALITY 0x02 -#define GB_I2C_TYPE_TIMEOUT 0x03 -#define GB_I2C_TYPE_RETRIES 0x04 -#define GB_I2C_TYPE_TRANSFER 0x05 -#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -#define GB_I2C_RETRIES_DEFAULT 3 -#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ - -/* version request has no payload */ -struct gb_i2c_proto_version_response { - __u8 major; - __u8 minor; -}; - -/* functionality request has no payload */ -struct gb_i2c_functionality_response { - __le32 functionality; -}; - -struct gb_i2c_timeout_request { - __le16 msec; -}; -/* timeout response has no payload */ - -struct gb_i2c_retries_request { - __u8 retries; -}; -/* retries response has no payload */ - -/* - * Outgoing data immediately follows the op count and ops array. - * The data for each write (master -> slave) op in the array is sent - * in order, with no (e.g. pad) bytes separating them. - * - * Short reads cause the entire transfer request to fail So response - * payload consists only of bytes read, and the number of bytes is - * exactly what was specified in the corresponding op. Like - * outgoing data, the incoming data is in order and contiguous. - */ -struct gb_i2c_transfer_op { - __le16 addr; - __le16 flags; - __le16 size; -}; - -struct gb_i2c_transfer_request { - __le16 op_count; - struct gb_i2c_transfer_op ops[0]; /* op_count of these */ -}; -struct gb_i2c_transfer_response { - __u8 data[0]; /* inbound data */ -}; - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) -{ - struct gb_i2c_proto_version_response response; - int ret; - - ret = gb_operation_sync(gb_i2c_dev->connection, - GB_I2C_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_I2C_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_I2C_VERSION_MAJOR); - return -ENOTSUPP; - } - gb_i2c_dev->version_major = response.major; - gb_i2c_dev->version_minor = response.minor; - return 0; -} - -/* - * Map Greybus i2c functionality bits into Linux ones - */ -static u32 gb_i2c_functionality_map(u32 gb_i2c_functionality) -{ - return gb_i2c_functionality; /* All bits the same for now */ -} - -static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) -{ - struct gb_i2c_functionality_response response; - u32 functionality; - int ret; - - ret = gb_operation_sync(gb_i2c_dev->connection, - GB_I2C_TYPE_FUNCTIONALITY, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - functionality = le32_to_cpu(response.functionality); - gb_i2c_dev->functionality = gb_i2c_functionality_map(functionality); - - return 0; -} - -static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) -{ - struct gb_i2c_timeout_request request; - int ret; - - request.msec = cpu_to_le16(msec); - ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_TIMEOUT, - &request, sizeof(request), NULL, 0); - if (ret) - pr_err("timeout operation failed (%d)\n", ret); - else - gb_i2c_dev->timeout_msec = msec; - - return ret; -} - -static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, - u8 retries) -{ - struct gb_i2c_retries_request request; - int ret; - - request.retries = retries; - ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_RETRIES, - &request, sizeof(request), NULL, 0); - if (ret) - pr_err("retries operation failed (%d)\n", ret); - else - gb_i2c_dev->retries = retries; - - return ret; -} - - -/* - * Map Linux i2c_msg flags into Greybus i2c transfer op flags. - */ -static u16 gb_i2c_transfer_op_flags_map(u16 flags) -{ - return flags; /* All flags the same for now */ -} - -static void -gb_i2c_fill_transfer_op(struct gb_i2c_transfer_op *op, struct i2c_msg *msg) -{ - u16 flags = gb_i2c_transfer_op_flags_map(msg->flags); - - op->addr = cpu_to_le16(msg->addr); - op->flags = cpu_to_le16(flags); - op->size = cpu_to_le16(msg->len); -} - -static struct gb_operation * -gb_i2c_transfer_request(struct gb_connection *connection, - struct i2c_msg *msgs, u32 msg_count) -{ - struct gb_i2c_transfer_request *request; - struct gb_operation *operation; - struct gb_i2c_transfer_op *op; - struct i2c_msg *msg; - u32 data_out_size = 0; - u32 data_in_size = 0; - size_t request_size; - void *data; - u16 op_count; - u32 i; - - if (msg_count > (u32)U16_MAX) { - gb_connection_err(connection, "msg_count (%u) too big", - msg_count); - return NULL; - } - op_count = (u16)msg_count; - - /* - * In addition to space for all message descriptors we need - * to have enough to hold all outbound message data. - */ - msg = msgs; - for (i = 0; i < msg_count; i++, msg++) - if (msg->flags & I2C_M_RD) - data_in_size += (u32)msg->len; - else - data_out_size += (u32)msg->len; - - request_size = sizeof(*request); - request_size += msg_count * sizeof(*op); - request_size += data_out_size; - - /* Response consists only of incoming data */ - operation = gb_operation_create(connection, GB_I2C_TYPE_TRANSFER, - request_size, data_in_size); - if (!operation) - return NULL; - - request = operation->request->payload; - request->op_count = cpu_to_le16(op_count); - /* Fill in the ops array */ - op = &request->ops[0]; - msg = msgs; - for (i = 0; i < msg_count; i++) - gb_i2c_fill_transfer_op(op++, msg++); - - if (!data_out_size) - return operation; - - /* Copy over the outgoing data; it starts after the last op */ - data = op; - msg = msgs; - for (i = 0; i < msg_count; i++) { - if (!(msg->flags & I2C_M_RD)) { - memcpy(data, msg->buf, msg->len); - data += msg->len; - } - msg++; - } - - return operation; -} - -static void gb_i2c_transfer_response(struct i2c_msg *msgs, u32 msg_count, - struct gb_i2c_transfer_response *response) -{ - struct i2c_msg *msg = msgs; - u8 *data; - u32 i; - - if (!response) - return; - data = response->data; - for (i = 0; i < msg_count; i++) { - if (msg->flags & I2C_M_RD) { - memcpy(msg->buf, data, msg->len); - data += msg->len; - } - msg++; - } -} - -/* - * Some i2c transfer operations return results that are expected. - */ -static bool gb_i2c_expected_transfer_error(int errno) -{ - return errno == -EAGAIN || errno == -ENODEV; -} - -static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, - struct i2c_msg *msgs, u32 msg_count) -{ - struct gb_connection *connection = gb_i2c_dev->connection; - struct gb_operation *operation; - int ret; - - operation = gb_i2c_transfer_request(connection, msgs, msg_count); - if (!operation) - return -ENOMEM; - - ret = gb_operation_request_send_sync(operation); - if (!ret) { - struct gb_i2c_transfer_response *response; - - response = operation->response->payload; - gb_i2c_transfer_response(msgs, msg_count, response); - ret = msg_count; - } else if (!gb_i2c_expected_transfer_error(ret)) { - pr_err("transfer operation failed (%d)\n", ret); - } - gb_operation_destroy(operation); - - return ret; -} - -static int gb_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, - int msg_count) -{ - struct gb_i2c_device *gb_i2c_dev; - - gb_i2c_dev = i2c_get_adapdata(adap); - - return gb_i2c_transfer_operation(gb_i2c_dev, msgs, msg_count); -} - -#if 0 -/* Later */ -static int gb_i2c_smbus_xfer(struct i2c_adapter *adap, - u16 addr, unsigned short flags, char read_write, - u8 command, int size, union i2c_smbus_data *data) -{ - struct gb_i2c_device *gb_i2c_dev; - - gb_i2c_dev = i2c_get_adapdata(adap); - - return 0; -} -#endif - -static u32 gb_i2c_functionality(struct i2c_adapter *adap) -{ - struct gb_i2c_device *gb_i2c_dev = i2c_get_adapdata(adap); - - return gb_i2c_dev->functionality; -} - -static const struct i2c_algorithm gb_i2c_algorithm = { - .master_xfer = gb_i2c_master_xfer, - /* .smbus_xfer = gb_i2c_smbus_xfer, */ - .functionality = gb_i2c_functionality, -}; - -/* - * Do initial setup of the i2c device. This includes verifying we - * can support it (based on the protocol version it advertises). - * If that's OK, we get and cached its functionality bits, and - * set up the retry count and timeout. - * - * Note: gb_i2c_dev->connection is assumed to have been valid. - */ -static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) -{ - int ret; - - /* First thing we need to do is check the version */ - ret = gb_i2c_proto_version_operation(gb_i2c_dev); - if (ret) - return ret; - - /* Assume the functionality never changes, just get it once */ - ret = gb_i2c_functionality_operation(gb_i2c_dev); - if (ret) - return ret; - - /* Set up our default retry count and timeout */ - ret = gb_i2c_retries_operation(gb_i2c_dev, GB_I2C_RETRIES_DEFAULT); - if (ret) - return ret; - - return gb_i2c_timeout_operation(gb_i2c_dev, GB_I2C_TIMEOUT_DEFAULT); -} - -static int gb_i2c_connection_init(struct gb_connection *connection) -{ - struct gb_i2c_device *gb_i2c_dev; - struct i2c_adapter *adapter; - int ret; - - gb_i2c_dev = kzalloc(sizeof(*gb_i2c_dev), GFP_KERNEL); - if (!gb_i2c_dev) - return -ENOMEM; - - gb_i2c_dev->connection = connection; /* refcount? */ - connection->private = gb_i2c_dev; - - ret = gb_i2c_device_setup(gb_i2c_dev); - if (ret) - goto out_err; - - /* Looks good; up our i2c adapter */ - adapter = &gb_i2c_dev->adapter; - adapter->owner = THIS_MODULE; - adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; - adapter->algo = &gb_i2c_algorithm; - /* adapter->algo_data = what? */ - adapter->timeout = gb_i2c_dev->timeout_msec * HZ / 1000; - adapter->retries = gb_i2c_dev->retries; - - adapter->dev.parent = &connection->dev; - snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); - i2c_set_adapdata(adapter, gb_i2c_dev); - - ret = i2c_add_adapter(adapter); - if (ret) - goto out_err; - - return 0; -out_err: - /* kref_put(gb_i2c_dev->connection) */ - kfree(gb_i2c_dev); - - return ret; -} - -static void gb_i2c_connection_exit(struct gb_connection *connection) -{ - struct gb_i2c_device *gb_i2c_dev = connection->private; - - i2c_del_adapter(&gb_i2c_dev->adapter); - /* kref_put(gb_i2c_dev->connection) */ - kfree(gb_i2c_dev); -} - -static struct gb_protocol i2c_protocol = { - .name = "i2c", - .id = GREYBUS_PROTOCOL_I2C, - .major = 0, - .minor = 1, - .connection_init = gb_i2c_connection_init, - .connection_exit = gb_i2c_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -int gb_i2c_protocol_init(void) -{ - return gb_protocol_register(&i2c_protocol); -} - -void gb_i2c_protocol_exit(void) -{ - gb_protocol_deregister(&i2c_protocol); -} diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c new file mode 100644 index 000000000000..c967ae3161c5 --- /dev/null +++ b/drivers/staging/greybus/i2c.c @@ -0,0 +1,450 @@ +/* + * I2C bridge driver for the Greybus "generic" I2C module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include + +#include "greybus.h" + +struct gb_i2c_device { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + + u32 functionality; + u16 timeout_msec; + u8 retries; + + struct i2c_adapter adapter; +}; + +/* Version of the Greybus i2c protocol we support */ +#define GB_I2C_VERSION_MAJOR 0x00 +#define GB_I2C_VERSION_MINOR 0x01 + +/* Greybus i2c request types */ +#define GB_I2C_TYPE_INVALID 0x00 +#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2C_TYPE_FUNCTIONALITY 0x02 +#define GB_I2C_TYPE_TIMEOUT 0x03 +#define GB_I2C_TYPE_RETRIES 0x04 +#define GB_I2C_TYPE_TRANSFER 0x05 +#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +#define GB_I2C_RETRIES_DEFAULT 3 +#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ + +/* version request has no payload */ +struct gb_i2c_proto_version_response { + __u8 major; + __u8 minor; +}; + +/* functionality request has no payload */ +struct gb_i2c_functionality_response { + __le32 functionality; +}; + +struct gb_i2c_timeout_request { + __le16 msec; +}; +/* timeout response has no payload */ + +struct gb_i2c_retries_request { + __u8 retries; +}; +/* retries response has no payload */ + +/* + * Outgoing data immediately follows the op count and ops array. + * The data for each write (master -> slave) op in the array is sent + * in order, with no (e.g. pad) bytes separating them. + * + * Short reads cause the entire transfer request to fail So response + * payload consists only of bytes read, and the number of bytes is + * exactly what was specified in the corresponding op. Like + * outgoing data, the incoming data is in order and contiguous. + */ +struct gb_i2c_transfer_op { + __le16 addr; + __le16 flags; + __le16 size; +}; + +struct gb_i2c_transfer_request { + __le16 op_count; + struct gb_i2c_transfer_op ops[0]; /* op_count of these */ +}; +struct gb_i2c_transfer_response { + __u8 data[0]; /* inbound data */ +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) +{ + struct gb_i2c_proto_version_response response; + int ret; + + ret = gb_operation_sync(gb_i2c_dev->connection, + GB_I2C_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + if (response.major > GB_I2C_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_I2C_VERSION_MAJOR); + return -ENOTSUPP; + } + gb_i2c_dev->version_major = response.major; + gb_i2c_dev->version_minor = response.minor; + return 0; +} + +/* + * Map Greybus i2c functionality bits into Linux ones + */ +static u32 gb_i2c_functionality_map(u32 gb_i2c_functionality) +{ + return gb_i2c_functionality; /* All bits the same for now */ +} + +static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) +{ + struct gb_i2c_functionality_response response; + u32 functionality; + int ret; + + ret = gb_operation_sync(gb_i2c_dev->connection, + GB_I2C_TYPE_FUNCTIONALITY, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + functionality = le32_to_cpu(response.functionality); + gb_i2c_dev->functionality = gb_i2c_functionality_map(functionality); + + return 0; +} + +static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) +{ + struct gb_i2c_timeout_request request; + int ret; + + request.msec = cpu_to_le16(msec); + ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_TIMEOUT, + &request, sizeof(request), NULL, 0); + if (ret) + pr_err("timeout operation failed (%d)\n", ret); + else + gb_i2c_dev->timeout_msec = msec; + + return ret; +} + +static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, + u8 retries) +{ + struct gb_i2c_retries_request request; + int ret; + + request.retries = retries; + ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_RETRIES, + &request, sizeof(request), NULL, 0); + if (ret) + pr_err("retries operation failed (%d)\n", ret); + else + gb_i2c_dev->retries = retries; + + return ret; +} + + +/* + * Map Linux i2c_msg flags into Greybus i2c transfer op flags. + */ +static u16 gb_i2c_transfer_op_flags_map(u16 flags) +{ + return flags; /* All flags the same for now */ +} + +static void +gb_i2c_fill_transfer_op(struct gb_i2c_transfer_op *op, struct i2c_msg *msg) +{ + u16 flags = gb_i2c_transfer_op_flags_map(msg->flags); + + op->addr = cpu_to_le16(msg->addr); + op->flags = cpu_to_le16(flags); + op->size = cpu_to_le16(msg->len); +} + +static struct gb_operation * +gb_i2c_transfer_request(struct gb_connection *connection, + struct i2c_msg *msgs, u32 msg_count) +{ + struct gb_i2c_transfer_request *request; + struct gb_operation *operation; + struct gb_i2c_transfer_op *op; + struct i2c_msg *msg; + u32 data_out_size = 0; + u32 data_in_size = 0; + size_t request_size; + void *data; + u16 op_count; + u32 i; + + if (msg_count > (u32)U16_MAX) { + gb_connection_err(connection, "msg_count (%u) too big", + msg_count); + return NULL; + } + op_count = (u16)msg_count; + + /* + * In addition to space for all message descriptors we need + * to have enough to hold all outbound message data. + */ + msg = msgs; + for (i = 0; i < msg_count; i++, msg++) + if (msg->flags & I2C_M_RD) + data_in_size += (u32)msg->len; + else + data_out_size += (u32)msg->len; + + request_size = sizeof(*request); + request_size += msg_count * sizeof(*op); + request_size += data_out_size; + + /* Response consists only of incoming data */ + operation = gb_operation_create(connection, GB_I2C_TYPE_TRANSFER, + request_size, data_in_size); + if (!operation) + return NULL; + + request = operation->request->payload; + request->op_count = cpu_to_le16(op_count); + /* Fill in the ops array */ + op = &request->ops[0]; + msg = msgs; + for (i = 0; i < msg_count; i++) + gb_i2c_fill_transfer_op(op++, msg++); + + if (!data_out_size) + return operation; + + /* Copy over the outgoing data; it starts after the last op */ + data = op; + msg = msgs; + for (i = 0; i < msg_count; i++) { + if (!(msg->flags & I2C_M_RD)) { + memcpy(data, msg->buf, msg->len); + data += msg->len; + } + msg++; + } + + return operation; +} + +static void gb_i2c_transfer_response(struct i2c_msg *msgs, u32 msg_count, + struct gb_i2c_transfer_response *response) +{ + struct i2c_msg *msg = msgs; + u8 *data; + u32 i; + + if (!response) + return; + data = response->data; + for (i = 0; i < msg_count; i++) { + if (msg->flags & I2C_M_RD) { + memcpy(msg->buf, data, msg->len); + data += msg->len; + } + msg++; + } +} + +/* + * Some i2c transfer operations return results that are expected. + */ +static bool gb_i2c_expected_transfer_error(int errno) +{ + return errno == -EAGAIN || errno == -ENODEV; +} + +static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, + struct i2c_msg *msgs, u32 msg_count) +{ + struct gb_connection *connection = gb_i2c_dev->connection; + struct gb_operation *operation; + int ret; + + operation = gb_i2c_transfer_request(connection, msgs, msg_count); + if (!operation) + return -ENOMEM; + + ret = gb_operation_request_send_sync(operation); + if (!ret) { + struct gb_i2c_transfer_response *response; + + response = operation->response->payload; + gb_i2c_transfer_response(msgs, msg_count, response); + ret = msg_count; + } else if (!gb_i2c_expected_transfer_error(ret)) { + pr_err("transfer operation failed (%d)\n", ret); + } + gb_operation_destroy(operation); + + return ret; +} + +static int gb_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int msg_count) +{ + struct gb_i2c_device *gb_i2c_dev; + + gb_i2c_dev = i2c_get_adapdata(adap); + + return gb_i2c_transfer_operation(gb_i2c_dev, msgs, msg_count); +} + +#if 0 +/* Later */ +static int gb_i2c_smbus_xfer(struct i2c_adapter *adap, + u16 addr, unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct gb_i2c_device *gb_i2c_dev; + + gb_i2c_dev = i2c_get_adapdata(adap); + + return 0; +} +#endif + +static u32 gb_i2c_functionality(struct i2c_adapter *adap) +{ + struct gb_i2c_device *gb_i2c_dev = i2c_get_adapdata(adap); + + return gb_i2c_dev->functionality; +} + +static const struct i2c_algorithm gb_i2c_algorithm = { + .master_xfer = gb_i2c_master_xfer, + /* .smbus_xfer = gb_i2c_smbus_xfer, */ + .functionality = gb_i2c_functionality, +}; + +/* + * Do initial setup of the i2c device. This includes verifying we + * can support it (based on the protocol version it advertises). + * If that's OK, we get and cached its functionality bits, and + * set up the retry count and timeout. + * + * Note: gb_i2c_dev->connection is assumed to have been valid. + */ +static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) +{ + int ret; + + /* First thing we need to do is check the version */ + ret = gb_i2c_proto_version_operation(gb_i2c_dev); + if (ret) + return ret; + + /* Assume the functionality never changes, just get it once */ + ret = gb_i2c_functionality_operation(gb_i2c_dev); + if (ret) + return ret; + + /* Set up our default retry count and timeout */ + ret = gb_i2c_retries_operation(gb_i2c_dev, GB_I2C_RETRIES_DEFAULT); + if (ret) + return ret; + + return gb_i2c_timeout_operation(gb_i2c_dev, GB_I2C_TIMEOUT_DEFAULT); +} + +static int gb_i2c_connection_init(struct gb_connection *connection) +{ + struct gb_i2c_device *gb_i2c_dev; + struct i2c_adapter *adapter; + int ret; + + gb_i2c_dev = kzalloc(sizeof(*gb_i2c_dev), GFP_KERNEL); + if (!gb_i2c_dev) + return -ENOMEM; + + gb_i2c_dev->connection = connection; /* refcount? */ + connection->private = gb_i2c_dev; + + ret = gb_i2c_device_setup(gb_i2c_dev); + if (ret) + goto out_err; + + /* Looks good; up our i2c adapter */ + adapter = &gb_i2c_dev->adapter; + adapter->owner = THIS_MODULE; + adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adapter->algo = &gb_i2c_algorithm; + /* adapter->algo_data = what? */ + adapter->timeout = gb_i2c_dev->timeout_msec * HZ / 1000; + adapter->retries = gb_i2c_dev->retries; + + adapter->dev.parent = &connection->dev; + snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); + i2c_set_adapdata(adapter, gb_i2c_dev); + + ret = i2c_add_adapter(adapter); + if (ret) + goto out_err; + + return 0; +out_err: + /* kref_put(gb_i2c_dev->connection) */ + kfree(gb_i2c_dev); + + return ret; +} + +static void gb_i2c_connection_exit(struct gb_connection *connection) +{ + struct gb_i2c_device *gb_i2c_dev = connection->private; + + i2c_del_adapter(&gb_i2c_dev->adapter); + /* kref_put(gb_i2c_dev->connection) */ + kfree(gb_i2c_dev); +} + +static struct gb_protocol i2c_protocol = { + .name = "i2c", + .id = GREYBUS_PROTOCOL_I2C, + .major = 0, + .minor = 1, + .connection_init = gb_i2c_connection_init, + .connection_exit = gb_i2c_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +int gb_i2c_protocol_init(void) +{ + return gb_protocol_register(&i2c_protocol); +} + +void gb_i2c_protocol_exit(void) +{ + gb_protocol_deregister(&i2c_protocol); +} diff --git a/drivers/staging/greybus/pwm-gb.c b/drivers/staging/greybus/pwm-gb.c deleted file mode 100644 index 91f7b87a1cae..000000000000 --- a/drivers/staging/greybus/pwm-gb.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * PWM Greybus driver. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include "greybus.h" - -struct gb_pwm_chip { - struct gb_connection *connection; - u8 version_major; - u8 version_minor; - u8 pwm_max; /* max pwm number */ - - struct pwm_chip chip; - struct pwm_chip *pwm; -}; -#define pwm_chip_to_gb_pwm_chip(chip) \ - container_of(chip, struct gb_pwm_chip, chip) - -/* Version of the Greybus PWM protocol we support */ -#define GB_PWM_VERSION_MAJOR 0x00 -#define GB_PWM_VERSION_MINOR 0x01 - -/* Greybus PWM request types */ -#define GB_PWM_TYPE_INVALID 0x00 -#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 -#define GB_PWM_TYPE_PWM_COUNT 0x02 -#define GB_PWM_TYPE_ACTIVATE 0x03 -#define GB_PWM_TYPE_DEACTIVATE 0x04 -#define GB_PWM_TYPE_CONFIG 0x05 -#define GB_PWM_TYPE_POLARITY 0x06 -#define GB_PWM_TYPE_ENABLE 0x07 -#define GB_PWM_TYPE_DISABLE 0x08 -#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -/* version request has no payload */ -struct gb_pwm_proto_version_response { - __u8 major; - __u8 minor; -}; - -/* pwm count request has no payload */ -struct gb_pwm_count_response { - __u8 count; -}; - -struct gb_pwm_activate_request { - __u8 which; -}; - -struct gb_pwm_deactivate_request { - __u8 which; -}; - -struct gb_pwm_config_request { - __u8 which; - __le32 duty; - __le32 period; -}; - -struct gb_pwm_polarity_request { - __u8 which; - __u8 polarity; -}; - -struct gb_pwm_enable_request { - __u8 which; -}; - -struct gb_pwm_disable_request { - __u8 which; -}; - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) -{ - struct gb_pwm_proto_version_response response; - int ret; - - ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - - if (ret) - return ret; - - if (response.major > GB_PWM_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_PWM_VERSION_MAJOR); - return -ENOTSUPP; - } - pwmc->version_major = response.major; - pwmc->version_minor = response.minor; - return 0; -} - -static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) -{ - struct gb_pwm_count_response response; - int ret; - - ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PWM_COUNT, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - pwmc->pwm_max = response.count; - return 0; -} - -static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, - u8 which) -{ - struct gb_pwm_activate_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ACTIVATE, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, - u8 which) -{ - struct gb_pwm_deactivate_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DEACTIVATE, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, - u8 which, u32 duty, u32 period) -{ - struct gb_pwm_config_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - request.duty = cpu_to_le32(duty); - request.period = cpu_to_le32(period); - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, - &request, sizeof(request), NULL, 0); -} - - -static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, - u8 which, u8 polarity) -{ - struct gb_pwm_polarity_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - request.polarity = polarity; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_POLARITY, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, - u8 which) -{ - struct gb_pwm_enable_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ENABLE, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, - u8 which) -{ - struct gb_pwm_disable_request request; - - if (which > pwmc->pwm_max) - return -EINVAL; - - request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DISABLE, - &request, sizeof(request), NULL, 0); -} - -static int gb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - return gb_pwm_activate_operation(pwmc, pwm->hwpwm); -}; - -static void gb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - if (test_bit(PWMF_ENABLED, &pwm->flags)) - dev_warn(chip->dev, "freeing PWM device without disabling\n"); - - gb_pwm_deactivate_operation(pwmc, pwm->hwpwm); -} - -static int gb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - return gb_pwm_config_operation(pwmc, pwm->hwpwm, duty_ns, period_ns); -}; - -static int gb_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - return gb_pwm_set_polarity_operation(pwmc, pwm->hwpwm, polarity); -}; - -static int gb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - return gb_pwm_enable_operation(pwmc, pwm->hwpwm); -}; - -static void gb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - - gb_pwm_disable_operation(pwmc, pwm->hwpwm); -}; - -static const struct pwm_ops gb_pwm_ops = { - .request = gb_pwm_request, - .free = gb_pwm_free, - .config = gb_pwm_config, - .set_polarity = gb_pwm_set_polarity, - .enable = gb_pwm_enable, - .disable = gb_pwm_disable, - .owner = THIS_MODULE, -}; - -static int gb_pwm_connection_init(struct gb_connection *connection) -{ - struct gb_pwm_chip *pwmc; - struct pwm_chip *pwm; - int ret; - - pwmc = kzalloc(sizeof(*pwmc), GFP_KERNEL); - if (!pwmc) - return -ENOMEM; - pwmc->connection = connection; - connection->private = pwmc; - - /* Check for compatible protocol version */ - ret = gb_pwm_proto_version_operation(pwmc); - if (ret) - goto out_err; - - /* Query number of pwms present */ - ret = gb_pwm_count_operation(pwmc); - if (ret) - goto out_err; - - pwm = &pwmc->chip; - - pwm->dev = &connection->dev; - pwm->ops = &gb_pwm_ops; - pwm->base = -1; /* Allocate base dynamically */ - pwm->npwm = pwmc->pwm_max + 1; - pwm->can_sleep = true; /* FIXME */ - - ret = pwmchip_add(pwm); - if (ret) { - pr_err("Failed to register PWM\n"); - return ret; - } - - return 0; -out_err: - kfree(pwmc); - return ret; -} - -static void gb_pwm_connection_exit(struct gb_connection *connection) -{ - struct gb_pwm_chip *pwmc = connection->private; - - if (!pwmc) - return; - - pwmchip_remove(&pwmc->chip); - /* kref_put(pwmc->connection) */ - kfree(pwmc); -} - -static struct gb_protocol pwm_protocol = { - .name = "pwm", - .id = GREYBUS_PROTOCOL_PWM, - .major = 0, - .minor = 1, - .connection_init = gb_pwm_connection_init, - .connection_exit = gb_pwm_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -int gb_pwm_protocol_init(void) -{ - return gb_protocol_register(&pwm_protocol); -} - -void gb_pwm_protocol_exit(void) -{ - gb_protocol_deregister(&pwm_protocol); -} diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c new file mode 100644 index 000000000000..91f7b87a1cae --- /dev/null +++ b/drivers/staging/greybus/pwm.c @@ -0,0 +1,331 @@ +/* + * PWM Greybus driver. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_pwm_chip { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + u8 pwm_max; /* max pwm number */ + + struct pwm_chip chip; + struct pwm_chip *pwm; +}; +#define pwm_chip_to_gb_pwm_chip(chip) \ + container_of(chip, struct gb_pwm_chip, chip) + +/* Version of the Greybus PWM protocol we support */ +#define GB_PWM_VERSION_MAJOR 0x00 +#define GB_PWM_VERSION_MINOR 0x01 + +/* Greybus PWM request types */ +#define GB_PWM_TYPE_INVALID 0x00 +#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 +#define GB_PWM_TYPE_PWM_COUNT 0x02 +#define GB_PWM_TYPE_ACTIVATE 0x03 +#define GB_PWM_TYPE_DEACTIVATE 0x04 +#define GB_PWM_TYPE_CONFIG 0x05 +#define GB_PWM_TYPE_POLARITY 0x06 +#define GB_PWM_TYPE_ENABLE 0x07 +#define GB_PWM_TYPE_DISABLE 0x08 +#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +/* version request has no payload */ +struct gb_pwm_proto_version_response { + __u8 major; + __u8 minor; +}; + +/* pwm count request has no payload */ +struct gb_pwm_count_response { + __u8 count; +}; + +struct gb_pwm_activate_request { + __u8 which; +}; + +struct gb_pwm_deactivate_request { + __u8 which; +}; + +struct gb_pwm_config_request { + __u8 which; + __le32 duty; + __le32 period; +}; + +struct gb_pwm_polarity_request { + __u8 which; + __u8 polarity; +}; + +struct gb_pwm_enable_request { + __u8 which; +}; + +struct gb_pwm_disable_request { + __u8 which; +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) +{ + struct gb_pwm_proto_version_response response; + int ret; + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + + if (ret) + return ret; + + if (response.major > GB_PWM_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_PWM_VERSION_MAJOR); + return -ENOTSUPP; + } + pwmc->version_major = response.major; + pwmc->version_minor = response.minor; + return 0; +} + +static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) +{ + struct gb_pwm_count_response response; + int ret; + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PWM_COUNT, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + pwmc->pwm_max = response.count; + return 0; +} + +static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_pwm_activate_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ACTIVATE, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_pwm_deactivate_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DEACTIVATE, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, + u8 which, u32 duty, u32 period) +{ + struct gb_pwm_config_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + request.duty = cpu_to_le32(duty); + request.period = cpu_to_le32(period); + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, + &request, sizeof(request), NULL, 0); +} + + +static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, + u8 which, u8 polarity) +{ + struct gb_pwm_polarity_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + request.polarity = polarity; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_POLARITY, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_pwm_enable_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ENABLE, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, + u8 which) +{ + struct gb_pwm_disable_request request; + + if (which > pwmc->pwm_max) + return -EINVAL; + + request.which = which; + return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DISABLE, + &request, sizeof(request), NULL, 0); +} + +static int gb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_activate_operation(pwmc, pwm->hwpwm); +}; + +static void gb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + if (test_bit(PWMF_ENABLED, &pwm->flags)) + dev_warn(chip->dev, "freeing PWM device without disabling\n"); + + gb_pwm_deactivate_operation(pwmc, pwm->hwpwm); +} + +static int gb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_config_operation(pwmc, pwm->hwpwm, duty_ns, period_ns); +}; + +static int gb_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_set_polarity_operation(pwmc, pwm->hwpwm, polarity); +}; + +static int gb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + return gb_pwm_enable_operation(pwmc, pwm->hwpwm); +}; + +static void gb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); + + gb_pwm_disable_operation(pwmc, pwm->hwpwm); +}; + +static const struct pwm_ops gb_pwm_ops = { + .request = gb_pwm_request, + .free = gb_pwm_free, + .config = gb_pwm_config, + .set_polarity = gb_pwm_set_polarity, + .enable = gb_pwm_enable, + .disable = gb_pwm_disable, + .owner = THIS_MODULE, +}; + +static int gb_pwm_connection_init(struct gb_connection *connection) +{ + struct gb_pwm_chip *pwmc; + struct pwm_chip *pwm; + int ret; + + pwmc = kzalloc(sizeof(*pwmc), GFP_KERNEL); + if (!pwmc) + return -ENOMEM; + pwmc->connection = connection; + connection->private = pwmc; + + /* Check for compatible protocol version */ + ret = gb_pwm_proto_version_operation(pwmc); + if (ret) + goto out_err; + + /* Query number of pwms present */ + ret = gb_pwm_count_operation(pwmc); + if (ret) + goto out_err; + + pwm = &pwmc->chip; + + pwm->dev = &connection->dev; + pwm->ops = &gb_pwm_ops; + pwm->base = -1; /* Allocate base dynamically */ + pwm->npwm = pwmc->pwm_max + 1; + pwm->can_sleep = true; /* FIXME */ + + ret = pwmchip_add(pwm); + if (ret) { + pr_err("Failed to register PWM\n"); + return ret; + } + + return 0; +out_err: + kfree(pwmc); + return ret; +} + +static void gb_pwm_connection_exit(struct gb_connection *connection) +{ + struct gb_pwm_chip *pwmc = connection->private; + + if (!pwmc) + return; + + pwmchip_remove(&pwmc->chip); + /* kref_put(pwmc->connection) */ + kfree(pwmc); +} + +static struct gb_protocol pwm_protocol = { + .name = "pwm", + .id = GREYBUS_PROTOCOL_PWM, + .major = 0, + .minor = 1, + .connection_init = gb_pwm_connection_init, + .connection_exit = gb_pwm_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +int gb_pwm_protocol_init(void) +{ + return gb_protocol_register(&pwm_protocol); +} + +void gb_pwm_protocol_exit(void) +{ + gb_protocol_deregister(&pwm_protocol); +} diff --git a/drivers/staging/greybus/sdio-gb.c b/drivers/staging/greybus/sdio-gb.c deleted file mode 100644 index d324846d09ab..000000000000 --- a/drivers/staging/greybus/sdio-gb.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SD/MMC Greybus driver. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include - -#include "greybus.h" - -struct gb_sdio_host { - struct gb_connection *connection; - struct mmc_host *mmc; - struct mmc_request *mrq; - // FIXME - some lock? -}; - -static void gb_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - // FIXME - do something here... -} - -static void gb_sd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - // FIXME - do something here... -} - -static int gb_sd_get_ro(struct mmc_host *mmc) -{ - // FIXME - do something here... - return 0; -} - -static const struct mmc_host_ops gb_sd_ops = { - .request = gb_sd_request, - .set_ios = gb_sd_set_ios, - .get_ro = gb_sd_get_ro, -}; - -static int gb_sdio_connection_init(struct gb_connection *connection) -{ - struct mmc_host *mmc; - struct gb_sdio_host *host; - - mmc = mmc_alloc_host(sizeof(*host), &connection->dev); - if (!mmc) - return -ENOMEM; - - host = mmc_priv(mmc); - host->mmc = mmc; - - mmc->ops = &gb_sd_ops; - // FIXME - set up size limits we can handle. - // FIXME - register the host controller. - - host->connection = connection; - connection->private = host; - return 0; -} - -static void gb_sdio_connection_exit(struct gb_connection *connection) -{ - struct mmc_host *mmc; - struct gb_sdio_host *host; - - host = connection->private; - if (!host) - return; - - mmc = host->mmc; - mmc_remove_host(mmc); - mmc_free_host(mmc); - connection->private = NULL; -} - -static struct gb_protocol sdio_protocol = { - .name = "sdio", - .id = GREYBUS_PROTOCOL_SDIO, - .major = 0, - .minor = 1, - .connection_init = gb_sdio_connection_init, - .connection_exit = gb_sdio_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -int gb_sdio_protocol_init(void) -{ - return gb_protocol_register(&sdio_protocol); -} - -void gb_sdio_protocol_exit(void) -{ - gb_protocol_deregister(&sdio_protocol); -} diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c new file mode 100644 index 000000000000..d324846d09ab --- /dev/null +++ b/drivers/staging/greybus/sdio.c @@ -0,0 +1,99 @@ +/* + * SD/MMC Greybus driver. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include + +#include "greybus.h" + +struct gb_sdio_host { + struct gb_connection *connection; + struct mmc_host *mmc; + struct mmc_request *mrq; + // FIXME - some lock? +}; + +static void gb_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + // FIXME - do something here... +} + +static void gb_sd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + // FIXME - do something here... +} + +static int gb_sd_get_ro(struct mmc_host *mmc) +{ + // FIXME - do something here... + return 0; +} + +static const struct mmc_host_ops gb_sd_ops = { + .request = gb_sd_request, + .set_ios = gb_sd_set_ios, + .get_ro = gb_sd_get_ro, +}; + +static int gb_sdio_connection_init(struct gb_connection *connection) +{ + struct mmc_host *mmc; + struct gb_sdio_host *host; + + mmc = mmc_alloc_host(sizeof(*host), &connection->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->mmc = mmc; + + mmc->ops = &gb_sd_ops; + // FIXME - set up size limits we can handle. + // FIXME - register the host controller. + + host->connection = connection; + connection->private = host; + return 0; +} + +static void gb_sdio_connection_exit(struct gb_connection *connection) +{ + struct mmc_host *mmc; + struct gb_sdio_host *host; + + host = connection->private; + if (!host) + return; + + mmc = host->mmc; + mmc_remove_host(mmc); + mmc_free_host(mmc); + connection->private = NULL; +} + +static struct gb_protocol sdio_protocol = { + .name = "sdio", + .id = GREYBUS_PROTOCOL_SDIO, + .major = 0, + .minor = 1, + .connection_init = gb_sdio_connection_init, + .connection_exit = gb_sdio_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +int gb_sdio_protocol_init(void) +{ + return gb_protocol_register(&sdio_protocol); +} + +void gb_sdio_protocol_exit(void) +{ + gb_protocol_deregister(&sdio_protocol); +} diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c deleted file mode 100644 index 032062019d20..000000000000 --- a/drivers/staging/greybus/uart-gb.c +++ /dev/null @@ -1,787 +0,0 @@ -/* - * UART driver for the Greybus "generic" UART module. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - * - * Heavily based on drivers/usb/class/cdc-acm.c and - * drivers/usb/serial/usb-serial.c. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "greybus.h" - -#define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ -#define GB_NAME "ttyGB" - -/* Version of the Greybus PWM protocol we support */ -#define GB_UART_VERSION_MAJOR 0x00 -#define GB_UART_VERSION_MINOR 0x01 - -/* Greybus UART request types */ -#define GB_UART_REQ_INVALID 0x00 -#define GB_UART_REQ_PROTOCOL_VERSION 0x01 -#define GB_UART_REQ_SEND_DATA 0x02 -#define GB_UART_REQ_RECEIVE_DATA 0x03 /* Unsolicited data */ -#define GB_UART_REQ_SET_LINE_CODING 0x04 -#define GB_UART_REQ_SET_CONTROL_LINE_STATE 0x05 -#define GB_UART_REQ_SET_BREAK 0x06 -#define GB_UART_REQ_SERIAL_STATE 0x07 /* Unsolicited data */ -#define GB_UART_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -struct gb_uart_proto_version_response { - __u8 major; - __u8 minor; -}; - -struct gb_uart_send_data_request { - __le16 size; - __u8 data[0]; -}; - -struct gb_serial_line_coding { - __le32 rate; - __u8 format; -#define GB_SERIAL_1_STOP_BITS 0 -#define GB_SERIAL_1_5_STOP_BITS 1 -#define GB_SERIAL_2_STOP_BITS 2 - - __u8 parity; -#define GB_SERIAL_NO_PARITY 0 -#define GB_SERIAL_ODD_PARITY 1 -#define GB_SERIAL_EVEN_PARITY 2 -#define GB_SERIAL_MARK_PARITY 3 -#define GB_SERIAL_SPACE_PARITY 4 - - __u8 data; -} __attribute__ ((packed)); - -struct gb_uart_set_line_coding_request { - struct gb_serial_line_coding line_coding; -}; - -/* output control lines */ -#define GB_UART_CTRL_DTR 0x01 -#define GB_UART_CTRL_RTS 0x02 - -struct gb_uart_set_control_line_state_request { - __le16 control; -}; - -struct gb_uart_set_break_request { - __u8 state; -}; - -/* input control lines and line errors */ -#define GB_UART_CTRL_DCD 0x01 -#define GB_UART_CTRL_DSR 0x02 -#define GB_UART_CTRL_BRK 0x04 -#define GB_UART_CTRL_RI 0x08 - -#define GB_UART_CTRL_FRAMING 0x10 -#define GB_UART_CTRL_PARITY 0x20 -#define GB_UART_CTRL_OVERRUN 0x40 - -struct gb_uart_serial_state_request { - __u16 control; -}; - -struct gb_tty { - struct tty_port port; - struct gb_connection *connection; - u16 cport_id; - unsigned int minor; - unsigned char clocal; - bool disconnected; - spinlock_t read_lock; - spinlock_t write_lock; - struct async_icount iocount; - struct async_icount oldcount; - wait_queue_head_t wioctl; - struct mutex mutex; - u8 version_major; - u8 version_minor; - unsigned int ctrlin; /* input control lines */ - unsigned int ctrlout; /* output control lines */ - struct gb_serial_line_coding line_coding; -}; - - -static struct tty_driver *gb_tty_driver; -static DEFINE_IDR(tty_minors); -static DEFINE_MUTEX(table_lock); -static atomic_t reference_count = ATOMIC_INIT(0); - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_tty *tty) -{ - struct gb_uart_proto_version_response response; - int ret; - - ret = gb_operation_sync(tty->connection, - GB_UART_REQ_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_UART_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_UART_VERSION_MAJOR); - return -ENOTSUPP; - } - tty->version_major = response.major; - tty->version_minor = response.minor; - - pr_debug("%s: version_major = %u version_minor = %u\n", - __func__, tty->version_major, tty->version_minor); - return 0; -} - -static int send_data(struct gb_tty *tty, u16 size, const u8 *data) -{ - struct gb_uart_send_data_request *request; - int retval; - - if (!data || !size) - return 0; - - request = kmalloc(sizeof(*request) + size, GFP_KERNEL); - if (!request) - return -ENOMEM; - - request->size = cpu_to_le16(size); - memcpy(&request->data[0], data, size); - retval = gb_operation_sync(tty->connection, GB_UART_REQ_SEND_DATA, - request, sizeof(*request) + size, NULL, 0); - - kfree(request); - return retval; -} - -static int send_line_coding(struct gb_tty *tty) -{ - struct gb_uart_set_line_coding_request request; - - memcpy(&request.line_coding, &tty->line_coding, - sizeof(tty->line_coding)); - return gb_operation_sync(tty->connection, GB_UART_REQ_SET_LINE_CODING, - &request, sizeof(request), NULL, 0); -} - -static int send_control(struct gb_tty *tty, u16 control) -{ - struct gb_uart_set_control_line_state_request request; - - request.control = cpu_to_le16(control); - return gb_operation_sync(tty->connection, - GB_UART_REQ_SET_CONTROL_LINE_STATE, - &request, sizeof(request), NULL, 0); -} - -static int send_break(struct gb_tty *tty, u8 state) -{ - struct gb_uart_set_break_request request; - - if ((state != 0) && (state != 1)) { - dev_err(&tty->connection->dev, - "invalid break state of %d\n", state); - return -EINVAL; - } - - request.state = state; - return gb_operation_sync(tty->connection, GB_UART_REQ_SET_BREAK, - &request, sizeof(request), NULL, 0); -} - - -static struct gb_tty *get_gb_by_minor(unsigned minor) -{ - struct gb_tty *gb_tty; - - mutex_lock(&table_lock); - gb_tty = idr_find(&tty_minors, minor); - if (gb_tty) { - mutex_lock(&gb_tty->mutex); - if (gb_tty->disconnected) { - mutex_unlock(&gb_tty->mutex); - gb_tty = NULL; - } else { - tty_port_get(&gb_tty->port); - mutex_unlock(&gb_tty->mutex); - } - } - mutex_unlock(&table_lock); - return gb_tty; -} - -static int alloc_minor(struct gb_tty *gb_tty) -{ - int minor; - - mutex_lock(&table_lock); - minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL); - mutex_unlock(&table_lock); - if (minor >= 0) - gb_tty->minor = minor; - return minor; -} - -static void release_minor(struct gb_tty *gb_tty) -{ - int minor = gb_tty->minor; - - gb_tty->minor = 0; /* Maybe should use an invalid value instead */ - mutex_lock(&table_lock); - idr_remove(&tty_minors, minor); - mutex_unlock(&table_lock); -} - -static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct gb_tty *gb_tty; - int retval; - - gb_tty = get_gb_by_minor(tty->index); - if (!gb_tty) - return -ENODEV; - - retval = tty_standard_install(driver, tty); - if (retval) - goto error; - - tty->driver_data = gb_tty; - return 0; -error: - tty_port_put(&gb_tty->port); - return retval; -} - -static int gb_tty_open(struct tty_struct *tty, struct file *file) -{ - struct gb_tty *gb_tty = tty->driver_data; - - return tty_port_open(&gb_tty->port, tty, file); -} - -static void gb_tty_close(struct tty_struct *tty, struct file *file) -{ - struct gb_tty *gb_tty = tty->driver_data; - - tty_port_close(&gb_tty->port, tty, file); -} - -static void gb_tty_cleanup(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - - tty_port_put(&gb_tty->port); -} - -static void gb_tty_hangup(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - - tty_port_hangup(&gb_tty->port); -} - -static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, - int count) -{ - struct gb_tty *gb_tty = tty->driver_data; - - return send_data(gb_tty, count, buf); -} - -static int gb_tty_write_room(struct tty_struct *tty) -{ -// struct gb_tty *gb_tty = tty->driver_data; - - // FIXME - how much do we want to say we have room for? - return 0; -} - -static int gb_tty_chars_in_buffer(struct tty_struct *tty) -{ -// struct gb_tty *gb_tty = tty->driver_data; - - // FIXME - how many left to send? - return 0; -} - -static int gb_tty_break_ctl(struct tty_struct *tty, int state) -{ - struct gb_tty *gb_tty = tty->driver_data; - - return send_break(gb_tty, state ? 1 : 0); -} - -static void gb_tty_set_termios(struct tty_struct *tty, - struct ktermios *termios_old) -{ - struct gb_tty *gb_tty = tty->driver_data; - struct ktermios *termios = &tty->termios; - struct gb_serial_line_coding newline; - int newctrl = gb_tty->ctrlout; - - newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); - newline.format = termios->c_cflag & CSTOPB ? 2 : 0; - newline.parity = termios->c_cflag & PARENB ? - (termios->c_cflag & PARODD ? 1 : 2) + - (termios->c_cflag & CMSPAR ? 2 : 0) : 0; - - switch (termios->c_cflag & CSIZE) { - case CS5: - newline.data = 5; - break; - case CS6: - newline.data = 6; - break; - case CS7: - newline.data = 7; - break; - case CS8: - default: - newline.data = 8; - break; - } - - /* FIXME: needs to clear unsupported bits in the termios */ - gb_tty->clocal = ((termios->c_cflag & CLOCAL) != 0); - - if (C_BAUD(tty) == B0) { - newline.rate = gb_tty->line_coding.rate; - newctrl &= GB_UART_CTRL_DTR; - } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { - newctrl |= GB_UART_CTRL_DTR; - } - - if (newctrl != gb_tty->ctrlout) { - gb_tty->ctrlout = newctrl; - send_control(gb_tty, newctrl); - } - - if (memcpy(&gb_tty->line_coding, &newline, sizeof(newline))) { - memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); - send_line_coding(gb_tty); - } -} - -static int gb_tty_tiocmget(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - - return (gb_tty->ctrlout & GB_UART_CTRL_DTR ? TIOCM_DTR : 0) | - (gb_tty->ctrlout & GB_UART_CTRL_RTS ? TIOCM_RTS : 0) | - (gb_tty->ctrlin & GB_UART_CTRL_DSR ? TIOCM_DSR : 0) | - (gb_tty->ctrlin & GB_UART_CTRL_RI ? TIOCM_RI : 0) | - (gb_tty->ctrlin & GB_UART_CTRL_DCD ? TIOCM_CD : 0) | - TIOCM_CTS; -} - -static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, - unsigned int clear) -{ - struct gb_tty *gb_tty = tty->driver_data; - unsigned int newctrl = gb_tty->ctrlout; - - set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | - (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); - clear = (clear & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | - (clear & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); - - newctrl = (newctrl & ~clear) | set; - if (gb_tty->ctrlout == newctrl) - return 0; - - gb_tty->ctrlout = newctrl; - return send_control(gb_tty, newctrl); -} - -static void gb_tty_throttle(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - unsigned char stop_char; - int retval; - - if (I_IXOFF(tty)) { - stop_char = STOP_CHAR(tty); - retval = gb_tty_write(tty, &stop_char, 1); - if (retval <= 0) - return; - } - - if (tty->termios.c_cflag & CRTSCTS) { - gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; - retval = send_control(gb_tty, gb_tty->ctrlout); - } - -} - -static void gb_tty_unthrottle(struct tty_struct *tty) -{ - struct gb_tty *gb_tty = tty->driver_data; - unsigned char start_char; - int retval; - - if (I_IXOFF(tty)) { - start_char = START_CHAR(tty); - retval = gb_tty_write(tty, &start_char, 1); - if (retval <= 0) - return; - } - - if (tty->termios.c_cflag & CRTSCTS) { - gb_tty->ctrlout |= GB_UART_CTRL_RTS; - retval = send_control(gb_tty, gb_tty->ctrlout); - } -} - -static int get_serial_info(struct gb_tty *gb_tty, - struct serial_struct __user *info) -{ - struct serial_struct tmp; - - if (!info) - return -EINVAL; - - memset(&tmp, 0, sizeof(tmp)); - tmp.flags = ASYNC_LOW_LATENCY | ASYNC_SKIP_TEST; - tmp.type = PORT_16550A; - tmp.line = gb_tty->minor; - tmp.xmit_fifo_size = 16; - tmp.baud_base = 9600; - tmp.close_delay = gb_tty->port.close_delay / 10; - tmp.closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; - - if (copy_to_user(info, &tmp, sizeof(tmp))) - return -EFAULT; - return 0; -} - -static int set_serial_info(struct gb_tty *gb_tty, - struct serial_struct __user *newinfo) -{ - struct serial_struct new_serial; - unsigned int closing_wait; - unsigned int close_delay; - int retval = 0; - - if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) - return -EFAULT; - - close_delay = new_serial.close_delay * 10; - closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; - - mutex_lock(&gb_tty->port.mutex); - if (!capable(CAP_SYS_ADMIN)) { - if ((close_delay != gb_tty->port.close_delay) || - (closing_wait != gb_tty->port.closing_wait)) - retval = -EPERM; - else - retval = -EOPNOTSUPP; - } else { - gb_tty->port.close_delay = close_delay; - gb_tty->port.closing_wait = closing_wait; - } - mutex_unlock(&gb_tty->port.mutex); - return retval; -} - -static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) -{ - int retval = 0; - DECLARE_WAITQUEUE(wait, current); - struct async_icount old; - struct async_icount new; - - if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) - return -EINVAL; - - do { - spin_lock_irq(&gb_tty->read_lock); - old = gb_tty->oldcount; - new = gb_tty->iocount; - gb_tty->oldcount = new; - spin_unlock_irq(&gb_tty->read_lock); - - if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) - break; - if ((arg & TIOCM_CD) && (old.dcd != new.dcd)) - break; - if ((arg & TIOCM_RI) && (old.rng != new.rng)) - break; - - add_wait_queue(&gb_tty->wioctl, &wait); - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - remove_wait_queue(&gb_tty->wioctl, &wait); - if (gb_tty->disconnected) { - if (arg & TIOCM_CD) - break; - retval = -ENODEV; - } else if (signal_pending(current)) { - retval = -ERESTARTSYS; - } - } while (!retval); - - return retval; -} - -static int get_serial_usage(struct gb_tty *gb_tty, - struct serial_icounter_struct __user *count) -{ - struct serial_icounter_struct icount; - int retval = 0; - - memset(&icount, 0, sizeof(icount)); - icount.dsr = gb_tty->iocount.dsr; - icount.rng = gb_tty->iocount.rng; - icount.dcd = gb_tty->iocount.dcd; - icount.frame = gb_tty->iocount.frame; - icount.overrun = gb_tty->iocount.overrun; - icount.parity = gb_tty->iocount.parity; - icount.brk = gb_tty->iocount.brk; - - if (copy_to_user(count, &icount, sizeof(icount)) > 0) - retval = -EFAULT; - - return retval; -} - -static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct gb_tty *gb_tty = tty->driver_data; - - switch (cmd) { - case TIOCGSERIAL: - return get_serial_info(gb_tty, - (struct serial_struct __user *)arg); - case TIOCSSERIAL: - return set_serial_info(gb_tty, - (struct serial_struct __user *)arg); - case TIOCMIWAIT: - return wait_serial_change(gb_tty, arg); - case TIOCGICOUNT: - return get_serial_usage(gb_tty, - (struct serial_icounter_struct __user *)arg); - } - - return -ENOIOCTLCMD; -} - - -static const struct tty_operations gb_ops = { - .install = gb_tty_install, - .open = gb_tty_open, - .close = gb_tty_close, - .cleanup = gb_tty_cleanup, - .hangup = gb_tty_hangup, - .write = gb_tty_write, - .write_room = gb_tty_write_room, - .ioctl = gb_tty_ioctl, - .throttle = gb_tty_throttle, - .unthrottle = gb_tty_unthrottle, - .chars_in_buffer = gb_tty_chars_in_buffer, - .break_ctl = gb_tty_break_ctl, - .set_termios = gb_tty_set_termios, - .tiocmget = gb_tty_tiocmget, - .tiocmset = gb_tty_tiocmset, -}; - - -static int gb_tty_init(void); -static void gb_tty_exit(void); - -static int gb_uart_connection_init(struct gb_connection *connection) -{ - struct gb_tty *gb_tty; - struct device *tty_dev; - int retval; - int minor; - - /* First time here, initialize the tty structures */ - if (atomic_inc_return(&reference_count) == 1) { - retval = gb_tty_init(); - if (retval) { - atomic_dec(&reference_count); - return retval; - } - } - - gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); - if (!gb_tty) - return -ENOMEM; - gb_tty->connection = connection; - connection->private = gb_tty; - - /* Check for compatible protocol version */ - retval = get_version(gb_tty); - if (retval) - goto error_version; - - minor = alloc_minor(gb_tty); - if (minor < 0) { - if (minor == -ENOSPC) { - dev_err(&connection->dev, - "no more free minor numbers\n"); - return -ENODEV; - } - return minor; - } - - gb_tty->minor = minor; - spin_lock_init(&gb_tty->write_lock); - spin_lock_init(&gb_tty->read_lock); - init_waitqueue_head(&gb_tty->wioctl); - mutex_init(&gb_tty->mutex); - - send_control(gb_tty, gb_tty->ctrlout); - - /* initialize the uart to be 9600n81 */ - gb_tty->line_coding.rate = cpu_to_le32(9600); - gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; - gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; - gb_tty->line_coding.data = 8; - send_line_coding(gb_tty); - - tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, - &connection->dev); - if (IS_ERR(tty_dev)) { - retval = PTR_ERR(tty_dev); - goto error; - } - - return 0; -error: - release_minor(gb_tty); -error_version: - connection->private = NULL; - kfree(gb_tty); - return retval; -} - -static void gb_uart_connection_exit(struct gb_connection *connection) -{ - struct gb_tty *gb_tty = connection->private; - struct tty_struct *tty; - - if (!gb_tty) - return; - - mutex_lock(&gb_tty->mutex); - gb_tty->disconnected = true; - - wake_up_all(&gb_tty->wioctl); - connection->private = NULL; - mutex_unlock(&gb_tty->mutex); - - tty = tty_port_tty_get(&gb_tty->port); - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } - /* FIXME - stop all traffic */ - - tty_unregister_device(gb_tty_driver, gb_tty->minor); - - /* FIXME - free transmit / receive buffers */ - - tty_port_put(&gb_tty->port); - - kfree(gb_tty); - - /* If last device is gone, tear down the tty structures */ - if (atomic_dec_return(&reference_count) == 0) - gb_tty_exit(); -} - -static int gb_tty_init(void) -{ - int retval = 0; - - gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); - if (IS_ERR(gb_tty_driver)) { - pr_err("Can not allocate tty driver\n"); - retval = -ENOMEM; - goto fail_unregister_dev; - } - - gb_tty_driver->driver_name = "gb"; - gb_tty_driver->name = GB_NAME; - gb_tty_driver->major = 0; - gb_tty_driver->minor_start = 0; - gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; - gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - gb_tty_driver->init_termios = tty_std_termios; - gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty_set_operations(gb_tty_driver, &gb_ops); - - retval = tty_register_driver(gb_tty_driver); - if (retval) { - pr_err("Can not register tty driver: %d\n", retval); - goto fail_put_gb_tty; - } - - return 0; - -fail_put_gb_tty: - put_tty_driver(gb_tty_driver); -fail_unregister_dev: - return retval; -} - -static void gb_tty_exit(void) -{ - int major = MAJOR(gb_tty_driver->major); - int minor = gb_tty_driver->minor_start; - - tty_unregister_driver(gb_tty_driver); - put_tty_driver(gb_tty_driver); - unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); -} - -static struct gb_protocol uart_protocol = { - .name = "uart", - .id = GREYBUS_PROTOCOL_UART, - .major = 0, - .minor = 1, - .connection_init = gb_uart_connection_init, - .connection_exit = gb_uart_connection_exit, - .request_recv = NULL, /* FIXME we have 2 types of requests!!! */ -}; - -int gb_uart_protocol_init(void) -{ - return gb_protocol_register(&uart_protocol); -} - -void gb_uart_protocol_exit(void) -{ - gb_protocol_deregister(&uart_protocol); -} diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c new file mode 100644 index 000000000000..032062019d20 --- /dev/null +++ b/drivers/staging/greybus/uart.c @@ -0,0 +1,787 @@ +/* + * UART driver for the Greybus "generic" UART module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + * + * Heavily based on drivers/usb/class/cdc-acm.c and + * drivers/usb/serial/usb-serial.c. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "greybus.h" + +#define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ +#define GB_NAME "ttyGB" + +/* Version of the Greybus PWM protocol we support */ +#define GB_UART_VERSION_MAJOR 0x00 +#define GB_UART_VERSION_MINOR 0x01 + +/* Greybus UART request types */ +#define GB_UART_REQ_INVALID 0x00 +#define GB_UART_REQ_PROTOCOL_VERSION 0x01 +#define GB_UART_REQ_SEND_DATA 0x02 +#define GB_UART_REQ_RECEIVE_DATA 0x03 /* Unsolicited data */ +#define GB_UART_REQ_SET_LINE_CODING 0x04 +#define GB_UART_REQ_SET_CONTROL_LINE_STATE 0x05 +#define GB_UART_REQ_SET_BREAK 0x06 +#define GB_UART_REQ_SERIAL_STATE 0x07 /* Unsolicited data */ +#define GB_UART_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +struct gb_uart_proto_version_response { + __u8 major; + __u8 minor; +}; + +struct gb_uart_send_data_request { + __le16 size; + __u8 data[0]; +}; + +struct gb_serial_line_coding { + __le32 rate; + __u8 format; +#define GB_SERIAL_1_STOP_BITS 0 +#define GB_SERIAL_1_5_STOP_BITS 1 +#define GB_SERIAL_2_STOP_BITS 2 + + __u8 parity; +#define GB_SERIAL_NO_PARITY 0 +#define GB_SERIAL_ODD_PARITY 1 +#define GB_SERIAL_EVEN_PARITY 2 +#define GB_SERIAL_MARK_PARITY 3 +#define GB_SERIAL_SPACE_PARITY 4 + + __u8 data; +} __attribute__ ((packed)); + +struct gb_uart_set_line_coding_request { + struct gb_serial_line_coding line_coding; +}; + +/* output control lines */ +#define GB_UART_CTRL_DTR 0x01 +#define GB_UART_CTRL_RTS 0x02 + +struct gb_uart_set_control_line_state_request { + __le16 control; +}; + +struct gb_uart_set_break_request { + __u8 state; +}; + +/* input control lines and line errors */ +#define GB_UART_CTRL_DCD 0x01 +#define GB_UART_CTRL_DSR 0x02 +#define GB_UART_CTRL_BRK 0x04 +#define GB_UART_CTRL_RI 0x08 + +#define GB_UART_CTRL_FRAMING 0x10 +#define GB_UART_CTRL_PARITY 0x20 +#define GB_UART_CTRL_OVERRUN 0x40 + +struct gb_uart_serial_state_request { + __u16 control; +}; + +struct gb_tty { + struct tty_port port; + struct gb_connection *connection; + u16 cport_id; + unsigned int minor; + unsigned char clocal; + bool disconnected; + spinlock_t read_lock; + spinlock_t write_lock; + struct async_icount iocount; + struct async_icount oldcount; + wait_queue_head_t wioctl; + struct mutex mutex; + u8 version_major; + u8 version_minor; + unsigned int ctrlin; /* input control lines */ + unsigned int ctrlout; /* output control lines */ + struct gb_serial_line_coding line_coding; +}; + + +static struct tty_driver *gb_tty_driver; +static DEFINE_IDR(tty_minors); +static DEFINE_MUTEX(table_lock); +static atomic_t reference_count = ATOMIC_INIT(0); + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_tty *tty) +{ + struct gb_uart_proto_version_response response; + int ret; + + ret = gb_operation_sync(tty->connection, + GB_UART_REQ_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + if (response.major > GB_UART_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_UART_VERSION_MAJOR); + return -ENOTSUPP; + } + tty->version_major = response.major; + tty->version_minor = response.minor; + + pr_debug("%s: version_major = %u version_minor = %u\n", + __func__, tty->version_major, tty->version_minor); + return 0; +} + +static int send_data(struct gb_tty *tty, u16 size, const u8 *data) +{ + struct gb_uart_send_data_request *request; + int retval; + + if (!data || !size) + return 0; + + request = kmalloc(sizeof(*request) + size, GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->size = cpu_to_le16(size); + memcpy(&request->data[0], data, size); + retval = gb_operation_sync(tty->connection, GB_UART_REQ_SEND_DATA, + request, sizeof(*request) + size, NULL, 0); + + kfree(request); + return retval; +} + +static int send_line_coding(struct gb_tty *tty) +{ + struct gb_uart_set_line_coding_request request; + + memcpy(&request.line_coding, &tty->line_coding, + sizeof(tty->line_coding)); + return gb_operation_sync(tty->connection, GB_UART_REQ_SET_LINE_CODING, + &request, sizeof(request), NULL, 0); +} + +static int send_control(struct gb_tty *tty, u16 control) +{ + struct gb_uart_set_control_line_state_request request; + + request.control = cpu_to_le16(control); + return gb_operation_sync(tty->connection, + GB_UART_REQ_SET_CONTROL_LINE_STATE, + &request, sizeof(request), NULL, 0); +} + +static int send_break(struct gb_tty *tty, u8 state) +{ + struct gb_uart_set_break_request request; + + if ((state != 0) && (state != 1)) { + dev_err(&tty->connection->dev, + "invalid break state of %d\n", state); + return -EINVAL; + } + + request.state = state; + return gb_operation_sync(tty->connection, GB_UART_REQ_SET_BREAK, + &request, sizeof(request), NULL, 0); +} + + +static struct gb_tty *get_gb_by_minor(unsigned minor) +{ + struct gb_tty *gb_tty; + + mutex_lock(&table_lock); + gb_tty = idr_find(&tty_minors, minor); + if (gb_tty) { + mutex_lock(&gb_tty->mutex); + if (gb_tty->disconnected) { + mutex_unlock(&gb_tty->mutex); + gb_tty = NULL; + } else { + tty_port_get(&gb_tty->port); + mutex_unlock(&gb_tty->mutex); + } + } + mutex_unlock(&table_lock); + return gb_tty; +} + +static int alloc_minor(struct gb_tty *gb_tty) +{ + int minor; + + mutex_lock(&table_lock); + minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL); + mutex_unlock(&table_lock); + if (minor >= 0) + gb_tty->minor = minor; + return minor; +} + +static void release_minor(struct gb_tty *gb_tty) +{ + int minor = gb_tty->minor; + + gb_tty->minor = 0; /* Maybe should use an invalid value instead */ + mutex_lock(&table_lock); + idr_remove(&tty_minors, minor); + mutex_unlock(&table_lock); +} + +static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct gb_tty *gb_tty; + int retval; + + gb_tty = get_gb_by_minor(tty->index); + if (!gb_tty) + return -ENODEV; + + retval = tty_standard_install(driver, tty); + if (retval) + goto error; + + tty->driver_data = gb_tty; + return 0; +error: + tty_port_put(&gb_tty->port); + return retval; +} + +static int gb_tty_open(struct tty_struct *tty, struct file *file) +{ + struct gb_tty *gb_tty = tty->driver_data; + + return tty_port_open(&gb_tty->port, tty, file); +} + +static void gb_tty_close(struct tty_struct *tty, struct file *file) +{ + struct gb_tty *gb_tty = tty->driver_data; + + tty_port_close(&gb_tty->port, tty, file); +} + +static void gb_tty_cleanup(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + tty_port_put(&gb_tty->port); +} + +static void gb_tty_hangup(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + tty_port_hangup(&gb_tty->port); +} + +static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct gb_tty *gb_tty = tty->driver_data; + + return send_data(gb_tty, count, buf); +} + +static int gb_tty_write_room(struct tty_struct *tty) +{ +// struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - how much do we want to say we have room for? + return 0; +} + +static int gb_tty_chars_in_buffer(struct tty_struct *tty) +{ +// struct gb_tty *gb_tty = tty->driver_data; + + // FIXME - how many left to send? + return 0; +} + +static int gb_tty_break_ctl(struct tty_struct *tty, int state) +{ + struct gb_tty *gb_tty = tty->driver_data; + + return send_break(gb_tty, state ? 1 : 0); +} + +static void gb_tty_set_termios(struct tty_struct *tty, + struct ktermios *termios_old) +{ + struct gb_tty *gb_tty = tty->driver_data; + struct ktermios *termios = &tty->termios; + struct gb_serial_line_coding newline; + int newctrl = gb_tty->ctrlout; + + newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); + newline.format = termios->c_cflag & CSTOPB ? 2 : 0; + newline.parity = termios->c_cflag & PARENB ? + (termios->c_cflag & PARODD ? 1 : 2) + + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; + + switch (termios->c_cflag & CSIZE) { + case CS5: + newline.data = 5; + break; + case CS6: + newline.data = 6; + break; + case CS7: + newline.data = 7; + break; + case CS8: + default: + newline.data = 8; + break; + } + + /* FIXME: needs to clear unsupported bits in the termios */ + gb_tty->clocal = ((termios->c_cflag & CLOCAL) != 0); + + if (C_BAUD(tty) == B0) { + newline.rate = gb_tty->line_coding.rate; + newctrl &= GB_UART_CTRL_DTR; + } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { + newctrl |= GB_UART_CTRL_DTR; + } + + if (newctrl != gb_tty->ctrlout) { + gb_tty->ctrlout = newctrl; + send_control(gb_tty, newctrl); + } + + if (memcpy(&gb_tty->line_coding, &newline, sizeof(newline))) { + memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); + send_line_coding(gb_tty); + } +} + +static int gb_tty_tiocmget(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + + return (gb_tty->ctrlout & GB_UART_CTRL_DTR ? TIOCM_DTR : 0) | + (gb_tty->ctrlout & GB_UART_CTRL_RTS ? TIOCM_RTS : 0) | + (gb_tty->ctrlin & GB_UART_CTRL_DSR ? TIOCM_DSR : 0) | + (gb_tty->ctrlin & GB_UART_CTRL_RI ? TIOCM_RI : 0) | + (gb_tty->ctrlin & GB_UART_CTRL_DCD ? TIOCM_CD : 0) | + TIOCM_CTS; +} + +static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, + unsigned int clear) +{ + struct gb_tty *gb_tty = tty->driver_data; + unsigned int newctrl = gb_tty->ctrlout; + + set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | + (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); + clear = (clear & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | + (clear & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); + + newctrl = (newctrl & ~clear) | set; + if (gb_tty->ctrlout == newctrl) + return 0; + + gb_tty->ctrlout = newctrl; + return send_control(gb_tty, newctrl); +} + +static void gb_tty_throttle(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + unsigned char stop_char; + int retval; + + if (I_IXOFF(tty)) { + stop_char = STOP_CHAR(tty); + retval = gb_tty_write(tty, &stop_char, 1); + if (retval <= 0) + return; + } + + if (tty->termios.c_cflag & CRTSCTS) { + gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; + retval = send_control(gb_tty, gb_tty->ctrlout); + } + +} + +static void gb_tty_unthrottle(struct tty_struct *tty) +{ + struct gb_tty *gb_tty = tty->driver_data; + unsigned char start_char; + int retval; + + if (I_IXOFF(tty)) { + start_char = START_CHAR(tty); + retval = gb_tty_write(tty, &start_char, 1); + if (retval <= 0) + return; + } + + if (tty->termios.c_cflag & CRTSCTS) { + gb_tty->ctrlout |= GB_UART_CTRL_RTS; + retval = send_control(gb_tty, gb_tty->ctrlout); + } +} + +static int get_serial_info(struct gb_tty *gb_tty, + struct serial_struct __user *info) +{ + struct serial_struct tmp; + + if (!info) + return -EINVAL; + + memset(&tmp, 0, sizeof(tmp)); + tmp.flags = ASYNC_LOW_LATENCY | ASYNC_SKIP_TEST; + tmp.type = PORT_16550A; + tmp.line = gb_tty->minor; + tmp.xmit_fifo_size = 16; + tmp.baud_base = 9600; + tmp.close_delay = gb_tty->port.close_delay / 10; + tmp.closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; + + if (copy_to_user(info, &tmp, sizeof(tmp))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct gb_tty *gb_tty, + struct serial_struct __user *newinfo) +{ + struct serial_struct new_serial; + unsigned int closing_wait; + unsigned int close_delay; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + close_delay = new_serial.close_delay * 10; + closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; + + mutex_lock(&gb_tty->port.mutex); + if (!capable(CAP_SYS_ADMIN)) { + if ((close_delay != gb_tty->port.close_delay) || + (closing_wait != gb_tty->port.closing_wait)) + retval = -EPERM; + else + retval = -EOPNOTSUPP; + } else { + gb_tty->port.close_delay = close_delay; + gb_tty->port.closing_wait = closing_wait; + } + mutex_unlock(&gb_tty->port.mutex); + return retval; +} + +static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) +{ + int retval = 0; + DECLARE_WAITQUEUE(wait, current); + struct async_icount old; + struct async_icount new; + + if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) + return -EINVAL; + + do { + spin_lock_irq(&gb_tty->read_lock); + old = gb_tty->oldcount; + new = gb_tty->iocount; + gb_tty->oldcount = new; + spin_unlock_irq(&gb_tty->read_lock); + + if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) + break; + if ((arg & TIOCM_CD) && (old.dcd != new.dcd)) + break; + if ((arg & TIOCM_RI) && (old.rng != new.rng)) + break; + + add_wait_queue(&gb_tty->wioctl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + remove_wait_queue(&gb_tty->wioctl, &wait); + if (gb_tty->disconnected) { + if (arg & TIOCM_CD) + break; + retval = -ENODEV; + } else if (signal_pending(current)) { + retval = -ERESTARTSYS; + } + } while (!retval); + + return retval; +} + +static int get_serial_usage(struct gb_tty *gb_tty, + struct serial_icounter_struct __user *count) +{ + struct serial_icounter_struct icount; + int retval = 0; + + memset(&icount, 0, sizeof(icount)); + icount.dsr = gb_tty->iocount.dsr; + icount.rng = gb_tty->iocount.rng; + icount.dcd = gb_tty->iocount.dcd; + icount.frame = gb_tty->iocount.frame; + icount.overrun = gb_tty->iocount.overrun; + icount.parity = gb_tty->iocount.parity; + icount.brk = gb_tty->iocount.brk; + + if (copy_to_user(count, &icount, sizeof(icount)) > 0) + retval = -EFAULT; + + return retval; +} + +static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg) +{ + struct gb_tty *gb_tty = tty->driver_data; + + switch (cmd) { + case TIOCGSERIAL: + return get_serial_info(gb_tty, + (struct serial_struct __user *)arg); + case TIOCSSERIAL: + return set_serial_info(gb_tty, + (struct serial_struct __user *)arg); + case TIOCMIWAIT: + return wait_serial_change(gb_tty, arg); + case TIOCGICOUNT: + return get_serial_usage(gb_tty, + (struct serial_icounter_struct __user *)arg); + } + + return -ENOIOCTLCMD; +} + + +static const struct tty_operations gb_ops = { + .install = gb_tty_install, + .open = gb_tty_open, + .close = gb_tty_close, + .cleanup = gb_tty_cleanup, + .hangup = gb_tty_hangup, + .write = gb_tty_write, + .write_room = gb_tty_write_room, + .ioctl = gb_tty_ioctl, + .throttle = gb_tty_throttle, + .unthrottle = gb_tty_unthrottle, + .chars_in_buffer = gb_tty_chars_in_buffer, + .break_ctl = gb_tty_break_ctl, + .set_termios = gb_tty_set_termios, + .tiocmget = gb_tty_tiocmget, + .tiocmset = gb_tty_tiocmset, +}; + + +static int gb_tty_init(void); +static void gb_tty_exit(void); + +static int gb_uart_connection_init(struct gb_connection *connection) +{ + struct gb_tty *gb_tty; + struct device *tty_dev; + int retval; + int minor; + + /* First time here, initialize the tty structures */ + if (atomic_inc_return(&reference_count) == 1) { + retval = gb_tty_init(); + if (retval) { + atomic_dec(&reference_count); + return retval; + } + } + + gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); + if (!gb_tty) + return -ENOMEM; + gb_tty->connection = connection; + connection->private = gb_tty; + + /* Check for compatible protocol version */ + retval = get_version(gb_tty); + if (retval) + goto error_version; + + minor = alloc_minor(gb_tty); + if (minor < 0) { + if (minor == -ENOSPC) { + dev_err(&connection->dev, + "no more free minor numbers\n"); + return -ENODEV; + } + return minor; + } + + gb_tty->minor = minor; + spin_lock_init(&gb_tty->write_lock); + spin_lock_init(&gb_tty->read_lock); + init_waitqueue_head(&gb_tty->wioctl); + mutex_init(&gb_tty->mutex); + + send_control(gb_tty, gb_tty->ctrlout); + + /* initialize the uart to be 9600n81 */ + gb_tty->line_coding.rate = cpu_to_le32(9600); + gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; + gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; + gb_tty->line_coding.data = 8; + send_line_coding(gb_tty); + + tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, + &connection->dev); + if (IS_ERR(tty_dev)) { + retval = PTR_ERR(tty_dev); + goto error; + } + + return 0; +error: + release_minor(gb_tty); +error_version: + connection->private = NULL; + kfree(gb_tty); + return retval; +} + +static void gb_uart_connection_exit(struct gb_connection *connection) +{ + struct gb_tty *gb_tty = connection->private; + struct tty_struct *tty; + + if (!gb_tty) + return; + + mutex_lock(&gb_tty->mutex); + gb_tty->disconnected = true; + + wake_up_all(&gb_tty->wioctl); + connection->private = NULL; + mutex_unlock(&gb_tty->mutex); + + tty = tty_port_tty_get(&gb_tty->port); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + /* FIXME - stop all traffic */ + + tty_unregister_device(gb_tty_driver, gb_tty->minor); + + /* FIXME - free transmit / receive buffers */ + + tty_port_put(&gb_tty->port); + + kfree(gb_tty); + + /* If last device is gone, tear down the tty structures */ + if (atomic_dec_return(&reference_count) == 0) + gb_tty_exit(); +} + +static int gb_tty_init(void) +{ + int retval = 0; + + gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); + if (IS_ERR(gb_tty_driver)) { + pr_err("Can not allocate tty driver\n"); + retval = -ENOMEM; + goto fail_unregister_dev; + } + + gb_tty_driver->driver_name = "gb"; + gb_tty_driver->name = GB_NAME; + gb_tty_driver->major = 0; + gb_tty_driver->minor_start = 0; + gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + gb_tty_driver->init_termios = tty_std_termios; + gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty_set_operations(gb_tty_driver, &gb_ops); + + retval = tty_register_driver(gb_tty_driver); + if (retval) { + pr_err("Can not register tty driver: %d\n", retval); + goto fail_put_gb_tty; + } + + return 0; + +fail_put_gb_tty: + put_tty_driver(gb_tty_driver); +fail_unregister_dev: + return retval; +} + +static void gb_tty_exit(void) +{ + int major = MAJOR(gb_tty_driver->major); + int minor = gb_tty_driver->minor_start; + + tty_unregister_driver(gb_tty_driver); + put_tty_driver(gb_tty_driver); + unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); +} + +static struct gb_protocol uart_protocol = { + .name = "uart", + .id = GREYBUS_PROTOCOL_UART, + .major = 0, + .minor = 1, + .connection_init = gb_uart_connection_init, + .connection_exit = gb_uart_connection_exit, + .request_recv = NULL, /* FIXME we have 2 types of requests!!! */ +}; + +int gb_uart_protocol_init(void) +{ + return gb_protocol_register(&uart_protocol); +} + +void gb_uart_protocol_exit(void) +{ + gb_protocol_deregister(&uart_protocol); +} diff --git a/drivers/staging/greybus/usb-gb.c b/drivers/staging/greybus/usb-gb.c deleted file mode 100644 index 010ef9ee831f..000000000000 --- a/drivers/staging/greybus/usb-gb.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * USB host driver for the Greybus "generic" USB module. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - * - */ -#include -#include -#include -#include -#include - -#include "greybus.h" - -/* Version of the Greybus USB protocol we support */ -#define GB_USB_VERSION_MAJOR 0x00 -#define GB_USB_VERSION_MINOR 0x01 - -/* Greybus USB request types */ -#define GB_USB_TYPE_INVALID 0x00 -#define GB_USB_TYPE_PROTOCOL_VERSION 0x01 -#define GB_USB_TYPE_HCD_STOP 0x02 -#define GB_USB_TYPE_HCD_START 0x03 -#define GB_USB_TYPE_URB_ENQUEUE 0x04 -#define GB_USB_TYPE_URB_DEQUEUE 0x05 -#define GB_USB_TYPE_ENDPOINT_DISABLE 0x06 -#define GB_USB_TYPE_HUB_CONTROL 0x07 -#define GB_USB_TYPE_GET_FRAME_NUMBER 0x08 -#define GB_USB_TYPE_HUB_STATUS_DATA 0x09 - -struct gb_usb_proto_version_response { - __u8 major; - __u8 minor; -}; - -struct gb_usb_urb_enqueue_request { - __le32 pipe; - __le32 transfer_flags; - __le32 transfer_buffer_length; - __le32 maxpacket; - __le32 interval; - __le64 hcpriv_ep; - __le32 number_of_packets; - u8 setup_packet[8]; - u8 payload[0]; -}; - -struct gb_usb_urb_dequeue_request { - __le64 hcpriv_ep; -}; - -struct gb_usb_endpoint_disable_request { - __le64 hcpriv; -}; - -struct gb_usb_hub_control_request { - __le16 typeReq; - __le16 wValue; - __le16 wIndex; - __le16 wLength; -}; - -struct gb_usb_hub_control_response { - u8 buf[0]; -}; - -struct gb_usb_header { - __le16 size; - __le16 id; - __u8 type; -}; - -struct gb_usb_hub_status { - __le32 status; - __le16 buf_size; - u8 buf[0]; -}; - -static struct gb_usb_hub_status *hub_status; // FIXME!!! -static DEFINE_SPINLOCK(hub_status_lock); -static atomic_t frame_number; // FIXME!!! - -struct gb_usb_device { - struct gb_connection *connection; - - struct usb_hcd *hcd; - u8 version_major; - u8 version_minor; -}; - -#define to_gb_usb_device(d) ((struct gb_usb_device*) d->hcd_priv) - -static int get_version(struct gb_usb_device *dev) -{ - struct gb_usb_proto_version_response response; - int ret; - - ret = gb_operation_sync(dev->connection, - GB_USB_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_USB_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_USB_VERSION_MAJOR); - return -ENOTSUPP; - } - dev->version_major = response.major; - dev->version_minor = response.minor; - return 0; -} - -static void hcd_stop(struct usb_hcd *hcd) -{ - struct gb_usb_device *dev = to_gb_usb_device(hcd); - int ret; - - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, - NULL, 0, NULL, 0); - if (ret) - dev_err(&dev->connection->dev, "HCD stop failed '%d'\n", ret); -} - -static int hcd_start(struct usb_hcd *hcd) -{ - struct usb_bus *bus = hcd_to_bus(hcd); - struct gb_usb_device *dev = to_gb_usb_device(hcd); - int ret; - - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, - NULL, 0, NULL, 0); - if (ret) { - dev_err(&dev->connection->dev, "HCD start failed '%d'\n", ret); - return ret; - } - - hcd->state = HC_STATE_RUNNING; - if (bus->root_hub) - usb_hcd_resume_root_hub(hcd); - return 0; -} - -static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) -{ - struct gb_usb_device *dev = to_gb_usb_device(hcd); - struct gb_usb_urb_enqueue_request *request; - struct gb_operation *operation; - int ret; - - operation = gb_operation_create(dev->connection, - GB_USB_TYPE_URB_ENQUEUE, - sizeof(*request) + - urb->transfer_buffer_length, 0); - if (!operation) - return -ENODEV; - - request = operation->request->payload; - request->pipe = cpu_to_le32(urb->pipe); - request->transfer_flags = cpu_to_le32(urb->transfer_flags); - request->transfer_buffer_length = cpu_to_le32(urb->transfer_buffer_length); - request->interval = cpu_to_le32(urb->interval); - request->hcpriv_ep = cpu_to_le64(urb->ep->hcpriv); - request->number_of_packets = cpu_to_le32(urb->number_of_packets); - - memcpy(request->setup_packet, urb->setup_packet, 8); - memcpy(&request->payload, urb->transfer_buffer, - urb->transfer_buffer_length); - - ret = gb_operation_request_send_sync(operation); - gb_operation_destroy(operation); - - return ret; -} - -static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -{ - struct gb_usb_device *dev = to_gb_usb_device(hcd); - struct gb_usb_urb_dequeue_request request; - int ret; - - urb->ep->hcpriv = NULL; - request.hcpriv_ep = cpu_to_le64(urb->hcpriv); - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_URB_DEQUEUE, - &request, sizeof(request), NULL, 0); - urb->hcpriv = NULL; - return ret; -} - -static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ - struct gb_usb_device *dev = to_gb_usb_device(hcd); - struct gb_usb_endpoint_disable_request request; - int ret; - - request.hcpriv = cpu_to_le64(ep->hcpriv); - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_ENDPOINT_DISABLE, - &request, sizeof(request), NULL, 0); - ep->hcpriv = NULL; -} - -static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ -} - -static int get_frame_number(struct usb_hcd *hcd) -{ - return atomic_read(&frame_number); -} - -static int hub_status_data(struct usb_hcd *hcd, char *buf) -{ - int retval; - unsigned long flags; - - spin_lock_irqsave(&hub_status_lock, flags); - memcpy(buf, hub_status->buf, le16_to_cpu(hub_status->buf_size)); - retval = le32_to_cpu(hub_status->status); - spin_unlock_irqrestore(&hub_status_lock, flags); - - return retval; -} - -static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, - char *buf, u16 wLength) -{ - struct gb_usb_hub_control_request request; - struct gb_usb_device *dev = to_gb_usb_device(hcd); - int ret; - - request.typeReq = cpu_to_le16(typeReq); - request.wValue = cpu_to_le16(wValue); - request.wIndex = cpu_to_le16(wIndex); - request.wLength = cpu_to_le16(wLength); - - // FIXME - buf needs to come back in struct gb_usb_hub_control_response - // for some types of requests, depending on typeReq. Do we do this in a - // "generic" way, or only ask for a response for the ones we "know" need - // a response (a small subset of all valid typeReq, thankfully.) - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HUB_CONTROL, - &request, sizeof(request), NULL, 0); - - return ret; -} - -static struct hc_driver usb_gb_hc_driver = { - .description = "greybus_usb", - .product_desc = "GB-Bridge USB Controller", /* TODO: Get this from GPB ?*/ - .flags = HCD_MEMORY | HCD_USB2, /* FIXME: Get this from GPB */ - .hcd_priv_size = sizeof(struct gb_usb_device), - - .start = hcd_start, - .stop = hcd_stop, - .urb_enqueue = urb_enqueue, - .urb_dequeue = urb_dequeue, - .endpoint_disable = endpoint_disable, - .endpoint_reset = endpoint_reset, - .get_frame_number = get_frame_number, - .hub_status_data = hub_status_data, - .hub_control = hub_control, -}; - -#if 0 -static inline void gb_usb_handle_get_frame_number(struct gbuf *gbuf) -{ - __le32 frame_num; - const size_t packet_size = sizeof(struct gb_usb_header) + - sizeof(frame_num); - struct gb_usb_header* hdr = gbuf->transfer_buffer; - - if (le16_to_cpu(hdr->size) != packet_size) { - pr_err("%s(): dropping packet too small\n", __func__); - return; - } - - frame_num = (__le32) ((char*) gbuf->transfer_buffer + - sizeof(struct gb_usb_header)); - atomic_set(&frame_number, le32_to_cpu(frame_num)); -} - -static inline void gb_usb_handle_hubs_status_data(struct gbuf *gbuf) -{ - struct gb_usb_hub_status *new_hubstatus, *hubstatus; - struct gb_usb_header* hdr = gbuf->transfer_buffer; - const size_t min_packet_size = sizeof(struct gb_usb_header) + - sizeof(struct gb_usb_hub_status); - unsigned long flags; - - if (le16_to_cpu(hdr->size) < min_packet_size) { - pr_err("%s(): dropping packet too small\n", __func__); - return; - } - - hubstatus = (struct gb_usb_hub_status*) ((char*) gbuf->transfer_buffer - + sizeof(struct gb_usb_header)); - - if (le16_to_cpu(hdr->size) != min_packet_size + hubstatus->buf_size) { - pr_err("%s(): invalid packet size, dropping packet\n", - __func__); - return; - } - - new_hubstatus = kmalloc(hubstatus->buf_size, GFP_KERNEL); - memcpy(&new_hubstatus, hubstatus, hubstatus->buf_size); - - spin_lock_irqsave(&hub_status_lock, flags); - hubstatus = hub_status; - hub_status = new_hubstatus; - spin_unlock_irqrestore(&hub_status_lock, flags); - - kfree(hubstatus); -} - -static void gb_usb_in_handler(struct gbuf *gbuf) -{ - struct gb_usb_header* hdr = gbuf->transfer_buffer; - - switch (hdr->type) { - case GB_USB_TYPE_GET_FRAME_NUMBER: - gb_usb_handle_get_frame_number(gbuf); - break; - - case GB_USB_TYPE_HUB_STATUS_DATA: - gb_usb_handle_hubs_status_data(gbuf); - break; - } -} -#endif - -static int gb_usb_connection_init(struct gb_connection *connection) -{ - struct device *dev = &connection->dev; - struct gb_usb_device *gb_usb_dev; - - int retval; - - gb_usb_dev = kzalloc(sizeof(*gb_usb_dev), GFP_KERNEL); - if (!gb_usb_dev) - return -ENOMEM; - - gb_usb_dev->connection = connection; - connection->private = gb_usb_dev; - - /* Check for compatible protocol version */ - retval = get_version(gb_usb_dev); - if (retval) - goto error_create_hcd; - - gb_usb_dev->hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); - if (!gb_usb_dev->hcd) { - retval = -ENODEV; - goto error_create_hcd; - } - - gb_usb_dev->hcd->has_tt = 1; - gb_usb_dev->hcd->hcd_priv[0] = (unsigned long) gb_usb_dev; - - retval = usb_add_hcd(gb_usb_dev->hcd, 0, 0); - if (retval) - goto error_add_hcd; - - return 0; -error_add_hcd: - usb_put_hcd(gb_usb_dev->hcd); -error_create_hcd: - kfree(gb_usb_dev); - return retval; -} - -static void gb_usb_connection_exit(struct gb_connection *connection) -{ - // FIXME - tear everything down! -} - -static struct gb_protocol usb_protocol = { - .name = "usb", - .id = GREYBUS_PROTOCOL_USB, - .major = 0, - .minor = 1, - .connection_init = gb_usb_connection_init, - .connection_exit = gb_usb_connection_exit, - .request_recv = NULL, /* FIXME we have requests!!! */ -}; - -int gb_usb_protocol_init(void) -{ - return gb_protocol_register(&usb_protocol); -} - -void gb_usb_protocol_exit(void) -{ - gb_protocol_deregister(&usb_protocol); -} diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c new file mode 100644 index 000000000000..010ef9ee831f --- /dev/null +++ b/drivers/staging/greybus/usb.c @@ -0,0 +1,396 @@ +/* + * USB host driver for the Greybus "generic" USB module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + * + */ +#include +#include +#include +#include +#include + +#include "greybus.h" + +/* Version of the Greybus USB protocol we support */ +#define GB_USB_VERSION_MAJOR 0x00 +#define GB_USB_VERSION_MINOR 0x01 + +/* Greybus USB request types */ +#define GB_USB_TYPE_INVALID 0x00 +#define GB_USB_TYPE_PROTOCOL_VERSION 0x01 +#define GB_USB_TYPE_HCD_STOP 0x02 +#define GB_USB_TYPE_HCD_START 0x03 +#define GB_USB_TYPE_URB_ENQUEUE 0x04 +#define GB_USB_TYPE_URB_DEQUEUE 0x05 +#define GB_USB_TYPE_ENDPOINT_DISABLE 0x06 +#define GB_USB_TYPE_HUB_CONTROL 0x07 +#define GB_USB_TYPE_GET_FRAME_NUMBER 0x08 +#define GB_USB_TYPE_HUB_STATUS_DATA 0x09 + +struct gb_usb_proto_version_response { + __u8 major; + __u8 minor; +}; + +struct gb_usb_urb_enqueue_request { + __le32 pipe; + __le32 transfer_flags; + __le32 transfer_buffer_length; + __le32 maxpacket; + __le32 interval; + __le64 hcpriv_ep; + __le32 number_of_packets; + u8 setup_packet[8]; + u8 payload[0]; +}; + +struct gb_usb_urb_dequeue_request { + __le64 hcpriv_ep; +}; + +struct gb_usb_endpoint_disable_request { + __le64 hcpriv; +}; + +struct gb_usb_hub_control_request { + __le16 typeReq; + __le16 wValue; + __le16 wIndex; + __le16 wLength; +}; + +struct gb_usb_hub_control_response { + u8 buf[0]; +}; + +struct gb_usb_header { + __le16 size; + __le16 id; + __u8 type; +}; + +struct gb_usb_hub_status { + __le32 status; + __le16 buf_size; + u8 buf[0]; +}; + +static struct gb_usb_hub_status *hub_status; // FIXME!!! +static DEFINE_SPINLOCK(hub_status_lock); +static atomic_t frame_number; // FIXME!!! + +struct gb_usb_device { + struct gb_connection *connection; + + struct usb_hcd *hcd; + u8 version_major; + u8 version_minor; +}; + +#define to_gb_usb_device(d) ((struct gb_usb_device*) d->hcd_priv) + +static int get_version(struct gb_usb_device *dev) +{ + struct gb_usb_proto_version_response response; + int ret; + + ret = gb_operation_sync(dev->connection, + GB_USB_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + if (response.major > GB_USB_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + response.major, GB_USB_VERSION_MAJOR); + return -ENOTSUPP; + } + dev->version_major = response.major; + dev->version_minor = response.minor; + return 0; +} + +static void hcd_stop(struct usb_hcd *hcd) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + int ret; + + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, + NULL, 0, NULL, 0); + if (ret) + dev_err(&dev->connection->dev, "HCD stop failed '%d'\n", ret); +} + +static int hcd_start(struct usb_hcd *hcd) +{ + struct usb_bus *bus = hcd_to_bus(hcd); + struct gb_usb_device *dev = to_gb_usb_device(hcd); + int ret; + + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, + NULL, 0, NULL, 0); + if (ret) { + dev_err(&dev->connection->dev, "HCD start failed '%d'\n", ret); + return ret; + } + + hcd->state = HC_STATE_RUNNING; + if (bus->root_hub) + usb_hcd_resume_root_hub(hcd); + return 0; +} + +static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_usb_urb_enqueue_request *request; + struct gb_operation *operation; + int ret; + + operation = gb_operation_create(dev->connection, + GB_USB_TYPE_URB_ENQUEUE, + sizeof(*request) + + urb->transfer_buffer_length, 0); + if (!operation) + return -ENODEV; + + request = operation->request->payload; + request->pipe = cpu_to_le32(urb->pipe); + request->transfer_flags = cpu_to_le32(urb->transfer_flags); + request->transfer_buffer_length = cpu_to_le32(urb->transfer_buffer_length); + request->interval = cpu_to_le32(urb->interval); + request->hcpriv_ep = cpu_to_le64(urb->ep->hcpriv); + request->number_of_packets = cpu_to_le32(urb->number_of_packets); + + memcpy(request->setup_packet, urb->setup_packet, 8); + memcpy(&request->payload, urb->transfer_buffer, + urb->transfer_buffer_length); + + ret = gb_operation_request_send_sync(operation); + gb_operation_destroy(operation); + + return ret; +} + +static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_usb_urb_dequeue_request request; + int ret; + + urb->ep->hcpriv = NULL; + request.hcpriv_ep = cpu_to_le64(urb->hcpriv); + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_URB_DEQUEUE, + &request, sizeof(request), NULL, 0); + urb->hcpriv = NULL; + return ret; +} + +static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_usb_endpoint_disable_request request; + int ret; + + request.hcpriv = cpu_to_le64(ep->hcpriv); + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_ENDPOINT_DISABLE, + &request, sizeof(request), NULL, 0); + ep->hcpriv = NULL; +} + +static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ +} + +static int get_frame_number(struct usb_hcd *hcd) +{ + return atomic_read(&frame_number); +} + +static int hub_status_data(struct usb_hcd *hcd, char *buf) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&hub_status_lock, flags); + memcpy(buf, hub_status->buf, le16_to_cpu(hub_status->buf_size)); + retval = le32_to_cpu(hub_status->status); + spin_unlock_irqrestore(&hub_status_lock, flags); + + return retval; +} + +static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, + char *buf, u16 wLength) +{ + struct gb_usb_hub_control_request request; + struct gb_usb_device *dev = to_gb_usb_device(hcd); + int ret; + + request.typeReq = cpu_to_le16(typeReq); + request.wValue = cpu_to_le16(wValue); + request.wIndex = cpu_to_le16(wIndex); + request.wLength = cpu_to_le16(wLength); + + // FIXME - buf needs to come back in struct gb_usb_hub_control_response + // for some types of requests, depending on typeReq. Do we do this in a + // "generic" way, or only ask for a response for the ones we "know" need + // a response (a small subset of all valid typeReq, thankfully.) + ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HUB_CONTROL, + &request, sizeof(request), NULL, 0); + + return ret; +} + +static struct hc_driver usb_gb_hc_driver = { + .description = "greybus_usb", + .product_desc = "GB-Bridge USB Controller", /* TODO: Get this from GPB ?*/ + .flags = HCD_MEMORY | HCD_USB2, /* FIXME: Get this from GPB */ + .hcd_priv_size = sizeof(struct gb_usb_device), + + .start = hcd_start, + .stop = hcd_stop, + .urb_enqueue = urb_enqueue, + .urb_dequeue = urb_dequeue, + .endpoint_disable = endpoint_disable, + .endpoint_reset = endpoint_reset, + .get_frame_number = get_frame_number, + .hub_status_data = hub_status_data, + .hub_control = hub_control, +}; + +#if 0 +static inline void gb_usb_handle_get_frame_number(struct gbuf *gbuf) +{ + __le32 frame_num; + const size_t packet_size = sizeof(struct gb_usb_header) + + sizeof(frame_num); + struct gb_usb_header* hdr = gbuf->transfer_buffer; + + if (le16_to_cpu(hdr->size) != packet_size) { + pr_err("%s(): dropping packet too small\n", __func__); + return; + } + + frame_num = (__le32) ((char*) gbuf->transfer_buffer + + sizeof(struct gb_usb_header)); + atomic_set(&frame_number, le32_to_cpu(frame_num)); +} + +static inline void gb_usb_handle_hubs_status_data(struct gbuf *gbuf) +{ + struct gb_usb_hub_status *new_hubstatus, *hubstatus; + struct gb_usb_header* hdr = gbuf->transfer_buffer; + const size_t min_packet_size = sizeof(struct gb_usb_header) + + sizeof(struct gb_usb_hub_status); + unsigned long flags; + + if (le16_to_cpu(hdr->size) < min_packet_size) { + pr_err("%s(): dropping packet too small\n", __func__); + return; + } + + hubstatus = (struct gb_usb_hub_status*) ((char*) gbuf->transfer_buffer + + sizeof(struct gb_usb_header)); + + if (le16_to_cpu(hdr->size) != min_packet_size + hubstatus->buf_size) { + pr_err("%s(): invalid packet size, dropping packet\n", + __func__); + return; + } + + new_hubstatus = kmalloc(hubstatus->buf_size, GFP_KERNEL); + memcpy(&new_hubstatus, hubstatus, hubstatus->buf_size); + + spin_lock_irqsave(&hub_status_lock, flags); + hubstatus = hub_status; + hub_status = new_hubstatus; + spin_unlock_irqrestore(&hub_status_lock, flags); + + kfree(hubstatus); +} + +static void gb_usb_in_handler(struct gbuf *gbuf) +{ + struct gb_usb_header* hdr = gbuf->transfer_buffer; + + switch (hdr->type) { + case GB_USB_TYPE_GET_FRAME_NUMBER: + gb_usb_handle_get_frame_number(gbuf); + break; + + case GB_USB_TYPE_HUB_STATUS_DATA: + gb_usb_handle_hubs_status_data(gbuf); + break; + } +} +#endif + +static int gb_usb_connection_init(struct gb_connection *connection) +{ + struct device *dev = &connection->dev; + struct gb_usb_device *gb_usb_dev; + + int retval; + + gb_usb_dev = kzalloc(sizeof(*gb_usb_dev), GFP_KERNEL); + if (!gb_usb_dev) + return -ENOMEM; + + gb_usb_dev->connection = connection; + connection->private = gb_usb_dev; + + /* Check for compatible protocol version */ + retval = get_version(gb_usb_dev); + if (retval) + goto error_create_hcd; + + gb_usb_dev->hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); + if (!gb_usb_dev->hcd) { + retval = -ENODEV; + goto error_create_hcd; + } + + gb_usb_dev->hcd->has_tt = 1; + gb_usb_dev->hcd->hcd_priv[0] = (unsigned long) gb_usb_dev; + + retval = usb_add_hcd(gb_usb_dev->hcd, 0, 0); + if (retval) + goto error_add_hcd; + + return 0; +error_add_hcd: + usb_put_hcd(gb_usb_dev->hcd); +error_create_hcd: + kfree(gb_usb_dev); + return retval; +} + +static void gb_usb_connection_exit(struct gb_connection *connection) +{ + // FIXME - tear everything down! +} + +static struct gb_protocol usb_protocol = { + .name = "usb", + .id = GREYBUS_PROTOCOL_USB, + .major = 0, + .minor = 1, + .connection_init = gb_usb_connection_init, + .connection_exit = gb_usb_connection_exit, + .request_recv = NULL, /* FIXME we have requests!!! */ +}; + +int gb_usb_protocol_init(void) +{ + return gb_protocol_register(&usb_protocol); +} + +void gb_usb_protocol_exit(void) +{ + gb_protocol_deregister(&usb_protocol); +} -- cgit v1.2.3-59-g8ed1b From 98abb4146ed31f1ec97145ef808d864096d31c4b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Jan 2015 16:10:41 +0530 Subject: greybus: Remove "gb-" prefix from .c files Some files are still prefixed with "gb-" with the reasoning that the modules would be named so, i.e. gb-*.ko. But this can be done by playing a bit in Makefile instead and keep uniform naming of .c files. Lets try it. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 6 + drivers/staging/greybus/battery.c | 373 ++++++++++++++++++++ drivers/staging/greybus/es1.c | 618 ++++++++++++++++++++++++++++++++++ drivers/staging/greybus/es2.c | 618 ++++++++++++++++++++++++++++++++++ drivers/staging/greybus/gb-battery.c | 373 -------------------- drivers/staging/greybus/gb-es1.c | 618 ---------------------------------- drivers/staging/greybus/gb-es2.c | 618 ---------------------------------- drivers/staging/greybus/gb-vibrator.c | 227 ------------- drivers/staging/greybus/vibrator.c | 227 +++++++++++++ 9 files changed, 1842 insertions(+), 1836 deletions(-) create mode 100644 drivers/staging/greybus/battery.c create mode 100644 drivers/staging/greybus/es1.c create mode 100644 drivers/staging/greybus/es2.c delete mode 100644 drivers/staging/greybus/gb-battery.c delete mode 100644 drivers/staging/greybus/gb-es1.c delete mode 100644 drivers/staging/greybus/gb-es2.c delete mode 100644 drivers/staging/greybus/gb-vibrator.c create mode 100644 drivers/staging/greybus/vibrator.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 55b4a37c58fb..79af8128aac9 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -17,6 +17,12 @@ gb-phy-y := gpb.o \ i2c.o \ usb.o +# Prefix all modules with gb- +gb-vibrator-y := vibrator.o +gb-battery-y := battery.o +gb-es1-y := es1.o +gb-es2-y := es2.o + obj-m += greybus.o obj-m += gb-phy.o obj-m += gb-vibrator.o diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c new file mode 100644 index 000000000000..2130d73cbfde --- /dev/null +++ b/drivers/staging/greybus/battery.c @@ -0,0 +1,373 @@ +/* + * Battery driver for a Greybus module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_battery { + struct power_supply bat; + // FIXME + // we will want to keep the battery stats in here as we will be getting + // updates from the SVC "on the fly" so we don't have to always go ask + // the battery for some information. Hopefully... + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + +}; +#define to_gb_battery(x) container_of(x, struct gb_battery, bat) + +/* Version of the Greybus battery protocol we support */ +#define GB_BATTERY_VERSION_MAJOR 0x00 +#define GB_BATTERY_VERSION_MINOR 0x01 + +/* Greybus battery request types */ +#define GB_BATTERY_TYPE_INVALID 0x00 +#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01 +#define GB_BATTERY_TYPE_TECHNOLOGY 0x02 +#define GB_BATTERY_TYPE_STATUS 0x03 +#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 +#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05 +#define GB_BATTERY_TYPE_TEMPERATURE 0x06 +#define GB_BATTERY_TYPE_VOLTAGE 0x07 +#define GB_BATTERY_TYPE_CURRENT 0x08 +#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX +#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX + +struct gb_battery_proto_version_response { + __u8 major; + __u8 minor; +}; + +/* Should match up with battery types in linux/power_supply.h */ +#define GB_BATTERY_TECH_UNKNOWN 0x0000 +#define GB_BATTERY_TECH_NiMH 0x0001 +#define GB_BATTERY_TECH_LION 0x0002 +#define GB_BATTERY_TECH_LIPO 0x0003 +#define GB_BATTERY_TECH_LiFe 0x0004 +#define GB_BATTERY_TECH_NiCd 0x0005 +#define GB_BATTERY_TECH_LiMn 0x0006 + +struct gb_battery_technology_response { + __le32 technology; +}; + +/* Should match up with battery status in linux/power_supply.h */ +#define GB_BATTERY_STATUS_UNKNOWN 0x0000 +#define GB_BATTERY_STATUS_CHARGING 0x0001 +#define GB_BATTERY_STATUS_DISCHARGING 0x0002 +#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003 +#define GB_BATTERY_STATUS_FULL 0x0004 + +struct gb_battery_status_response { + __le16 battery_status; +}; + +struct gb_battery_max_voltage_response { + __le32 max_voltage; +}; + +struct gb_battery_capacity_response { + __le32 capacity; +}; + +struct gb_battery_temperature_response { + __le32 temperature; +}; + +struct gb_battery_voltage_response { + __le32 voltage; +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_battery *gb) +{ + struct gb_battery_proto_version_response version_response; + int retval; + + retval = gb_operation_sync(gb->connection, + GB_BATTERY_TYPE_PROTOCOL_VERSION, + NULL, 0, + &version_response, sizeof(version_response)); + if (retval) + return retval; + + if (version_response.major > GB_BATTERY_VERSION_MAJOR) { + pr_err("unsupported major version (%hhu > %hhu)\n", + version_response.major, GB_BATTERY_VERSION_MAJOR); + return -ENOTSUPP; + } + + gb->version_major = version_response.major; + gb->version_minor = version_response.minor; + return 0; +} + +static int get_tech(struct gb_battery *gb) +{ + struct gb_battery_technology_response tech_response; + u32 technology; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY, + NULL, 0, + &tech_response, sizeof(tech_response)); + if (retval) + return retval; + + /* + * Map greybus values to power_supply values. Hopefully these are + * "identical" which should allow gcc to optimize the code away to + * nothing. + */ + technology = le32_to_cpu(tech_response.technology); + switch (technology) { + case GB_BATTERY_TECH_NiMH: + technology = POWER_SUPPLY_TECHNOLOGY_NiMH; + break; + case GB_BATTERY_TECH_LION: + technology = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case GB_BATTERY_TECH_LIPO: + technology = POWER_SUPPLY_TECHNOLOGY_LIPO; + break; + case GB_BATTERY_TECH_LiFe: + technology = POWER_SUPPLY_TECHNOLOGY_LiFe; + break; + case GB_BATTERY_TECH_NiCd: + technology = POWER_SUPPLY_TECHNOLOGY_NiCd; + break; + case GB_BATTERY_TECH_LiMn: + technology = POWER_SUPPLY_TECHNOLOGY_LiMn; + break; + case GB_BATTERY_TECH_UNKNOWN: + default: + technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + break; + } + return technology; +} + +static int get_status(struct gb_battery *gb) +{ + struct gb_battery_status_response status_response; + u16 battery_status; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS, + NULL, 0, + &status_response, sizeof(status_response)); + if (retval) + return retval; + + /* + * Map greybus values to power_supply values. Hopefully these are + * "identical" which should allow gcc to optimize the code away to + * nothing. + */ + battery_status = le16_to_cpu(status_response.battery_status); + switch (battery_status) { + case GB_BATTERY_STATUS_CHARGING: + battery_status = POWER_SUPPLY_STATUS_CHARGING; + break; + case GB_BATTERY_STATUS_DISCHARGING: + battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case GB_BATTERY_STATUS_NOT_CHARGING: + battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case GB_BATTERY_STATUS_FULL: + battery_status = POWER_SUPPLY_STATUS_FULL; + break; + case GB_BATTERY_STATUS_UNKNOWN: + default: + battery_status = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + return battery_status; +} + +static int get_max_voltage(struct gb_battery *gb) +{ + struct gb_battery_max_voltage_response volt_response; + u32 max_voltage; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE, + NULL, 0, + &volt_response, sizeof(volt_response)); + if (retval) + return retval; + + max_voltage = le32_to_cpu(volt_response.max_voltage); + return max_voltage; +} + +static int get_percent_capacity(struct gb_battery *gb) +{ + struct gb_battery_capacity_response capacity_response; + u32 capacity; + int retval; + + retval = gb_operation_sync(gb->connection, + GB_BATTERY_TYPE_PERCENT_CAPACITY, + NULL, 0, &capacity_response, + sizeof(capacity_response)); + if (retval) + return retval; + + capacity = le32_to_cpu(capacity_response.capacity); + return capacity; +} + +static int get_temp(struct gb_battery *gb) +{ + struct gb_battery_temperature_response temp_response; + u32 temperature; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE, + NULL, 0, + &temp_response, sizeof(temp_response)); + if (retval) + return retval; + + temperature = le32_to_cpu(temp_response.temperature); + return temperature; +} + +static int get_voltage(struct gb_battery *gb) +{ + struct gb_battery_voltage_response voltage_response; + u32 voltage; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE, + NULL, 0, + &voltage_response, sizeof(voltage_response)); + if (retval) + return retval; + + voltage = le32_to_cpu(voltage_response.voltage); + return voltage; +} + +static int get_property(struct power_supply *b, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct gb_battery *gb = to_gb_battery(b); + + switch (psp) { + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = get_tech(gb); + break; + + case POWER_SUPPLY_PROP_STATUS: + val->intval = get_status(gb); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = get_max_voltage(gb); + break; + + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = get_percent_capacity(gb); + break; + + case POWER_SUPPLY_PROP_TEMP: + val->intval = get_temp(gb); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = get_voltage(gb); + break; + + default: + return -EINVAL; + } + + return 0; +} + +// FIXME - verify this list, odds are some can be removed and others added. +static enum power_supply_property battery_props[] = { + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static int gb_battery_connection_init(struct gb_connection *connection) +{ + struct gb_battery *gb; + struct power_supply *b; + int retval; + + gb = kzalloc(sizeof(*gb), GFP_KERNEL); + if (!gb) + return -ENOMEM; + + gb->connection = connection; + connection->private = gb; + + /* Check the version */ + retval = get_version(gb); + if (retval) { + kfree(gb); + return retval; + } + + b = &gb->bat; + // FIXME - get a better (i.e. unique) name + // FIXME - anything else needs to be set? + b->name = "gb_battery"; + b->type = POWER_SUPPLY_TYPE_BATTERY, + b->properties = battery_props, + b->num_properties = ARRAY_SIZE(battery_props), + b->get_property = get_property, + + retval = power_supply_register(&connection->bundle->intf->dev, b); + if (retval) { + kfree(gb); + return retval; + } + + return 0; +} + +static void gb_battery_connection_exit(struct gb_connection *connection) +{ + struct gb_battery *gb = connection->private; + + power_supply_unregister(&gb->bat); + kfree(gb); +} + +static struct gb_protocol battery_protocol = { + .name = "battery", + .id = GREYBUS_PROTOCOL_BATTERY, + .major = 0, + .minor = 1, + .connection_init = gb_battery_connection_init, + .connection_exit = gb_battery_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +gb_protocol_driver(&battery_protocol); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c new file mode 100644 index 000000000000..2ec5d7b403a3 --- /dev/null +++ b/drivers/staging/greybus/es1.c @@ -0,0 +1,618 @@ +/* + * Greybus "AP" USB driver + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include +#include +#include +#include +#include +#include + +#include "greybus.h" +#include "svc_msg.h" +#include "kernel_ver.h" + +/* + * Macros for making pointers explicitly opaque, such that the result + * isn't valid but also can't be mistaken for an ERR_PTR() value. + */ +#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) +#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) + +/* Memory sizes for the buffers sent to/from the ES1 controller */ +#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) +#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE + +static const struct usb_device_id id_table[] = { + /* Made up numbers for the SVC USB Bridge in ES1 */ + { USB_DEVICE(0xffff, 0x0001) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* + * Number of CPort IN urbs in flight at any point in time. + * Adjust if we are having stalls in the USB buffer due to not enough urbs in + * flight. + */ +#define NUM_CPORT_IN_URB 4 + +/* Number of CPort OUT urbs in flight at any point in time. + * Adjust if we get messages saying we are out of urbs in the system log. + */ +#define NUM_CPORT_OUT_URB 8 + +/** + * es1_ap_dev - ES1 USB Bridge to AP structure + * @usb_dev: pointer to the USB device we are. + * @usb_intf: pointer to the USB interface we are bound to. + * @hd: pointer to our greybus_host_device structure + * @control_endpoint: endpoint to send data to SVC + * @svc_endpoint: endpoint for SVC data in + * @cport_in_endpoint: bulk in endpoint for CPort data + * @cport-out_endpoint: bulk out endpoint for CPort data + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint + * @svc_urb: urb for SVC messages coming in on @svc_endpoint + * @cport_in_urb: array of urbs for the CPort in messages + * @cport_in_buffer: array of buffers for the @cport_in_urb urbs + * @cport_out_urb: array of urbs for the CPort out messages + * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or + * not. + * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" + */ +struct es1_ap_dev { + struct usb_device *usb_dev; + struct usb_interface *usb_intf; + struct greybus_host_device *hd; + + __u8 control_endpoint; + __u8 svc_endpoint; + __u8 cport_in_endpoint; + __u8 cport_out_endpoint; + + u8 *svc_buffer; + struct urb *svc_urb; + + struct urb *cport_in_urb[NUM_CPORT_IN_URB]; + u8 *cport_in_buffer[NUM_CPORT_IN_URB]; + struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; + bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; + spinlock_t cport_out_urb_lock; +}; + +static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) +{ + return (struct es1_ap_dev *)&hd->hd_priv; +} + +static void cport_out_callback(struct urb *urb); + +/* + * Buffer constraints for the host driver. + * + * A "buffer" is used to hold data to be transferred for Greybus by + * the host driver. A buffer is represented by a "buffer pointer", + * which defines a region of memory used by the host driver for + * transferring the data. When Greybus allocates a buffer, it must + * do so subject to the constraints associated with the host driver. + * These constraints are specified by two parameters: the + * headroom; and the maximum buffer size. + * + * +------------------+ + * | Host driver | \ + * | reserved area | }- headroom + * | . . . | / + * buffer pointer ---> +------------------+ + * | Buffer space for | \ + * | transferred data | }- buffer size + * | . . . | / (limited to size_max) + * +------------------+ + * + * headroom: Every buffer must have at least this much space + * *before* the buffer pointer, reserved for use by the + * host driver. I.e., ((char *)buffer - headroom) must + * point to valid memory, usable only by the host driver. + * size_max: The maximum size of a buffer (not including the + * headroom) must not exceed this. + */ +static void hd_buffer_constraints(struct greybus_host_device *hd) +{ + /* + * Only one byte is required, but this produces a result + * that's better aligned for the user. + */ + hd->buffer_headroom = sizeof(u32); /* For cport id */ + hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; + BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); +} + +#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + int retval; + + /* SVC messages go down our control pipe */ + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + 0x01, /* vendor request AP message */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + (char *)svc_msg, + sizeof(*svc_msg), + ES1_TIMEOUT); + if (retval != sizeof(*svc_msg)) + return retval; + + return 0; +} + +static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) +{ + struct urb *urb = NULL; + unsigned long flags; + int i; + + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + + /* Look in our pool of allocated urbs first, as that's the "fastest" */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (es1->cport_out_urb_busy[i] == false) { + es1->cport_out_urb_busy[i] = true; + urb = es1->cport_out_urb[i]; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + if (urb) + return urb; + + /* + * Crap, pool is empty, complain to the syslog and go allocate one + * dynamically as we have to succeed. + */ + dev_err(&es1->usb_dev->dev, + "No free CPort OUT urbs, having to dynamically allocate one!\n"); + return usb_alloc_urb(0, gfp_mask); +} + +static void free_urb(struct es1_ap_dev *es1, struct urb *urb) +{ + unsigned long flags; + int i; + /* + * See if this was an urb in our pool, if so mark it "free", otherwise + * we need to free it ourselves. + */ + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (urb == es1->cport_out_urb[i]) { + es1->cport_out_urb_busy[i] = false; + urb = NULL; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + + /* If urb is not NULL, then we need to free this urb */ + usb_free_urb(urb); +} + +/* + * Returns an opaque cookie value if successful, or a pointer coded + * error otherwise. If the caller wishes to cancel the in-flight + * buffer, it must supply the returned cookie to the cancel routine. + */ +static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, + void *buffer, size_t buffer_size, gfp_t gfp_mask) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + u8 *transfer_buffer = buffer; + int transfer_buffer_size; + int retval; + struct urb *urb; + + if (!buffer) { + pr_err("null buffer supplied to send\n"); + return ERR_PTR(-EINVAL); + } + if (buffer_size > (size_t)INT_MAX) { + pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); + return ERR_PTR(-EINVAL); + } + transfer_buffer--; + transfer_buffer_size = buffer_size + 1; + + /* + * The data actually transferred will include an indication + * of where the data should be sent. Do one last check of + * the target CPort id before filling it in. + */ + if (cport_id == CPORT_ID_BAD) { + pr_err("request to send inbound data buffer\n"); + return ERR_PTR(-EINVAL); + } + if (cport_id > (u16)U8_MAX) { + pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); + return ERR_PTR(-EINVAL); + } + /* OK, the destination is fine; record it in the transfer buffer */ + *transfer_buffer = cport_id; + + /* Find a free urb */ + urb = next_free_urb(es1, gfp_mask); + if (!urb) + return ERR_PTR(-ENOMEM); + + usb_fill_bulk_urb(urb, udev, + usb_sndbulkpipe(udev, es1->cport_out_endpoint), + transfer_buffer, transfer_buffer_size, + cport_out_callback, hd); + retval = usb_submit_urb(urb, gfp_mask); + if (retval) { + pr_err("error %d submitting URB\n", retval); + free_urb(es1, urb); + return ERR_PTR(retval); + } + + return conceal_urb(urb); +} + +/* + * The cookie value supplied is the value that buffer_send() + * returned to its caller. It identifies the buffer that should be + * canceled. This function must also handle (which is to say, + * ignore) a null cookie value. + */ +static void buffer_cancel(void *cookie) +{ + + /* + * We really should be defensive and track all outstanding + * (sent) buffers rather than trusting the cookie provided + * is valid. For the time being, this will do. + */ + if (cookie) + usb_kill_urb(reveal_urb(cookie)); +} + +static struct greybus_host_driver es1_driver = { + .hd_priv_size = sizeof(struct es1_ap_dev), + .buffer_send = buffer_send, + .buffer_cancel = buffer_cancel, + .submit_svc = submit_svc, +}; + +/* Common function to report consistent warnings based on URB status */ +static int check_urb_status(struct urb *urb) +{ + struct device *dev = &urb->dev->dev; + int status = urb->status; + + switch (status) { + case 0: + return 0; + + case -EOVERFLOW: + dev_err(dev, "%s: overflow actual length is %d\n", + __func__, urb->actual_length); + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EILSEQ: + case -EPROTO: + /* device is gone, stop sending */ + return status; + } + dev_err(dev, "%s: unknown status %d\n", __func__, status); + + return -EAGAIN; +} + +static void ap_disconnect(struct usb_interface *interface) +{ + struct es1_ap_dev *es1; + struct usb_device *udev; + int i; + + es1 = usb_get_intfdata(interface); + if (!es1) + return; + + /* Tear down everything! */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + struct urb *urb = es1->cport_out_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + es1->cport_out_urb[i] = NULL; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ + } + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb = es1->cport_in_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + kfree(es1->cport_in_buffer[i]); + es1->cport_in_buffer[i] = NULL; + } + + usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); + es1->svc_urb = NULL; + kfree(es1->svc_buffer); + es1->svc_buffer = NULL; + + usb_set_intfdata(interface, NULL); + udev = es1->usb_dev; + greybus_remove_hd(es1->hd); + + usb_put_dev(udev); +} + +/* Callback for when we get a SVC message */ +static void svc_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb svc in error %d (dropped)\n", status); + return; + } + + /* We have a message, create a new message structure, add it to the + * list, and wake up our thread that will process the messages. + */ + greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); + +exit: + /* resubmit the urb to get more messages */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "Can not submit urb for AP data: %d\n", retval); +} + +static void cport_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + u16 cport_id; + u8 *data; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb cport in error %d (dropped)\n", status); + return; + } + + /* The size has to be at least one, for the cport id */ + if (!urb->actual_length) { + dev_err(dev, "%s: no cport id in input buffer?\n", __func__); + goto exit; + } + + /* + * Our CPort number is the first byte of the data stream, + * the rest of the stream is "real" data + */ + data = urb->transfer_buffer; + cport_id = (u16)data[0]; + data = &data[1]; + + /* Pass this data to the greybus core */ + greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); + +exit: + /* put our urb back in the request pool */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "%s: error %d in submitting urb.\n", + __func__, retval); +} + +static void cport_out_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct es1_ap_dev *es1 = hd_to_es1(hd); + int status = check_urb_status(urb); + u8 *data = urb->transfer_buffer + 1; + + /* + * Tell the submitter that the buffer send (attempt) is + * complete, and report the status. The submitter's buffer + * starts after the one-byte CPort id we inserted. + */ + data = urb->transfer_buffer + 1; + greybus_data_sent(hd, data, status); + + free_urb(es1, urb); + /* + * Rest assured Greg, this craziness is getting fixed. + * + * Yes, you are right, we aren't telling anyone that the urb finished. + * "That's crazy! How does this all even work?" you might be saying. + * The "magic" is the idea that greybus works on the "operation" level, + * not the "send a buffer" level. All operations are "round-trip" with + * a response from the device that the operation finished, or it will + * time out. Because of that, we don't care that this urb finished, or + * failed, or did anything else, as higher levels of the protocol stack + * will handle completions and timeouts and the rest. + * + * This protocol is "needed" due to some hardware restrictions on the + * current generation of Unipro controllers. Think about it for a + * minute, this is a USB driver, talking to a Unipro bridge, impedance + * mismatch is huge, yet the Unipro controller are even more + * underpowered than this little USB controller. We rely on the round + * trip to keep stalls in the Unipro controllers from happening so that + * we can keep data flowing properly, no matter how slow it might be. + * + * Once again, a wonderful bus protocol cut down in its prime by a naive + * controller chip. We dream of the day we have a "real" HCD for + * Unipro. Until then, we suck it up and make the hardware work, as + * that's the job of the firmware and kernel. + * + */ +} + +/* + * The ES1 USB Bridge device contains 4 endpoints + * 1 Control - usual USB stuff + AP -> SVC messages + * 1 Interrupt IN - SVC -> AP messages + * 1 Bulk IN - CPort data in + * 1 Bulk OUT - CPort data out + */ +static int ap_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct es1_ap_dev *es1; + struct greybus_host_device *hd; + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + bool int_in_found = false; + bool bulk_in_found = false; + bool bulk_out_found = false; + int retval = -ENOMEM; + int i; + u8 svc_interval = 0; + + udev = usb_get_dev(interface_to_usbdev(interface)); + + hd = greybus_create_hd(&es1_driver, &udev->dev); + if (!hd) { + usb_put_dev(udev); + return -ENOMEM; + } + + /* Fill in the buffer allocation constraints */ + hd_buffer_constraints(hd); + + es1 = hd_to_es1(hd); + es1->hd = hd; + es1->usb_intf = interface; + es1->usb_dev = udev; + spin_lock_init(&es1->cport_out_urb_lock); + usb_set_intfdata(interface, es1); + + /* Control endpoint is the pipe to talk to this AP, so save it off */ + endpoint = &udev->ep0.desc; + es1->control_endpoint = endpoint->bEndpointAddress; + + /* find all 3 of our endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) { + es1->svc_endpoint = endpoint->bEndpointAddress; + svc_interval = endpoint->bInterval; + int_in_found = true; + } else if (usb_endpoint_is_bulk_in(endpoint)) { + es1->cport_in_endpoint = endpoint->bEndpointAddress; + bulk_in_found = true; + } else if (usb_endpoint_is_bulk_out(endpoint)) { + es1->cport_out_endpoint = endpoint->bEndpointAddress; + bulk_out_found = true; + } else { + dev_err(&udev->dev, + "Unknown endpoint type found, address %x\n", + endpoint->bEndpointAddress); + } + } + if ((int_in_found == false) || + (bulk_in_found == false) || + (bulk_out_found == false)) { + dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); + goto error; + } + + /* Create our buffer and URB to get SVC messages, and start it up */ + es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); + if (!es1->svc_buffer) + goto error; + + es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!es1->svc_urb) + goto error; + + usb_fill_int_urb(es1->svc_urb, udev, + usb_rcvintpipe(udev, es1->svc_endpoint), + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, + hd, svc_interval); + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + + /* Allocate buffers for our cport in messages and start them up */ + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb; + u8 *buffer; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error; + buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); + if (!buffer) + goto error; + + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, es1->cport_in_endpoint), + buffer, ES1_GBUF_MSG_SIZE_MAX, + cport_in_callback, hd); + es1->cport_in_urb[i] = urb; + es1->cport_in_buffer[i] = buffer; + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) + goto error; + } + + /* Allocate urbs for our CPort OUT messages */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + struct urb *urb; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error; + + es1->cport_out_urb[i] = urb; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ + } + + return 0; +error: + ap_disconnect(interface); + + return retval; +} + +static struct usb_driver es1_ap_driver = { + .name = "es1_ap_driver", + .probe = ap_probe, + .disconnect = ap_disconnect, + .id_table = id_table, +}; + +module_usb_driver(es1_ap_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c new file mode 100644 index 000000000000..4154cce52a16 --- /dev/null +++ b/drivers/staging/greybus/es2.c @@ -0,0 +1,618 @@ +/* + * Greybus "AP" USB driver for "ES2" controller chips + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include +#include +#include +#include +#include +#include + +#include "greybus.h" +#include "svc_msg.h" +#include "kernel_ver.h" + +/* + * Macros for making pointers explicitly opaque, such that the result + * isn't valid but also can't be mistaken for an ERR_PTR() value. + */ +#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) +#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) + +/* Memory sizes for the buffers sent to/from the ES1 controller */ +#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) +#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE + +static const struct usb_device_id id_table[] = { + /* Made up numbers for the SVC USB Bridge in ES1 */ + { USB_DEVICE(0xffff, 0x0001) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* + * Number of CPort IN urbs in flight at any point in time. + * Adjust if we are having stalls in the USB buffer due to not enough urbs in + * flight. + */ +#define NUM_CPORT_IN_URB 4 + +/* Number of CPort OUT urbs in flight at any point in time. + * Adjust if we get messages saying we are out of urbs in the system log. + */ +#define NUM_CPORT_OUT_URB 8 + +/** + * es1_ap_dev - ES1 USB Bridge to AP structure + * @usb_dev: pointer to the USB device we are. + * @usb_intf: pointer to the USB interface we are bound to. + * @hd: pointer to our greybus_host_device structure + * @control_endpoint: endpoint to send data to SVC + * @svc_endpoint: endpoint for SVC data in + * @cport_in_endpoint: bulk in endpoint for CPort data + * @cport-out_endpoint: bulk out endpoint for CPort data + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint + * @svc_urb: urb for SVC messages coming in on @svc_endpoint + * @cport_in_urb: array of urbs for the CPort in messages + * @cport_in_buffer: array of buffers for the @cport_in_urb urbs + * @cport_out_urb: array of urbs for the CPort out messages + * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or + * not. + * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" + */ +struct es1_ap_dev { + struct usb_device *usb_dev; + struct usb_interface *usb_intf; + struct greybus_host_device *hd; + + __u8 control_endpoint; + __u8 svc_endpoint; + __u8 cport_in_endpoint; + __u8 cport_out_endpoint; + + u8 *svc_buffer; + struct urb *svc_urb; + + struct urb *cport_in_urb[NUM_CPORT_IN_URB]; + u8 *cport_in_buffer[NUM_CPORT_IN_URB]; + struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; + bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; + spinlock_t cport_out_urb_lock; +}; + +static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) +{ + return (struct es1_ap_dev *)&hd->hd_priv; +} + +static void cport_out_callback(struct urb *urb); + +/* + * Buffer constraints for the host driver. + * + * A "buffer" is used to hold data to be transferred for Greybus by + * the host driver. A buffer is represented by a "buffer pointer", + * which defines a region of memory used by the host driver for + * transferring the data. When Greybus allocates a buffer, it must + * do so subject to the constraints associated with the host driver. + * These constraints are specified by two parameters: the + * headroom; and the maximum buffer size. + * + * +------------------+ + * | Host driver | \ + * | reserved area | }- headroom + * | . . . | / + * buffer pointer ---> +------------------+ + * | Buffer space for | \ + * | transferred data | }- buffer size + * | . . . | / (limited to size_max) + * +------------------+ + * + * headroom: Every buffer must have at least this much space + * *before* the buffer pointer, reserved for use by the + * host driver. I.e., ((char *)buffer - headroom) must + * point to valid memory, usable only by the host driver. + * size_max: The maximum size of a buffer (not including the + * headroom) must not exceed this. + */ +static void hd_buffer_constraints(struct greybus_host_device *hd) +{ + /* + * Only one byte is required, but this produces a result + * that's better aligned for the user. + */ + hd->buffer_headroom = sizeof(u32); /* For cport id */ + hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; + BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); +} + +#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + int retval; + + /* SVC messages go down our control pipe */ + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + 0x01, /* vendor request AP message */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + (char *)svc_msg, + sizeof(*svc_msg), + ES1_TIMEOUT); + if (retval != sizeof(*svc_msg)) + return retval; + + return 0; +} + +static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) +{ + struct urb *urb = NULL; + unsigned long flags; + int i; + + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + + /* Look in our pool of allocated urbs first, as that's the "fastest" */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (es1->cport_out_urb_busy[i] == false) { + es1->cport_out_urb_busy[i] = true; + urb = es1->cport_out_urb[i]; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + if (urb) + return urb; + + /* + * Crap, pool is empty, complain to the syslog and go allocate one + * dynamically as we have to succeed. + */ + dev_err(&es1->usb_dev->dev, + "No free CPort OUT urbs, having to dynamically allocate one!\n"); + return usb_alloc_urb(0, gfp_mask); +} + +static void free_urb(struct es1_ap_dev *es1, struct urb *urb) +{ + unsigned long flags; + int i; + /* + * See if this was an urb in our pool, if so mark it "free", otherwise + * we need to free it ourselves. + */ + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (urb == es1->cport_out_urb[i]) { + es1->cport_out_urb_busy[i] = false; + urb = NULL; + break; + } + } + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + + /* If urb is not NULL, then we need to free this urb */ + usb_free_urb(urb); +} + +/* + * Returns an opaque cookie value if successful, or a pointer coded + * error otherwise. If the caller wishes to cancel the in-flight + * buffer, it must supply the returned cookie to the cancel routine. + */ +static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, + void *buffer, size_t buffer_size, gfp_t gfp_mask) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + u8 *transfer_buffer = buffer; + int transfer_buffer_size; + int retval; + struct urb *urb; + + if (!buffer) { + pr_err("null buffer supplied to send\n"); + return ERR_PTR(-EINVAL); + } + if (buffer_size > (size_t)INT_MAX) { + pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); + return ERR_PTR(-EINVAL); + } + transfer_buffer--; + transfer_buffer_size = buffer_size + 1; + + /* + * The data actually transferred will include an indication + * of where the data should be sent. Do one last check of + * the target CPort id before filling it in. + */ + if (cport_id == CPORT_ID_BAD) { + pr_err("request to send inbound data buffer\n"); + return ERR_PTR(-EINVAL); + } + if (cport_id > (u16)U8_MAX) { + pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); + return ERR_PTR(-EINVAL); + } + /* OK, the destination is fine; record it in the transfer buffer */ + *transfer_buffer = cport_id; + + /* Find a free urb */ + urb = next_free_urb(es1, gfp_mask); + if (!urb) + return ERR_PTR(-ENOMEM); + + usb_fill_bulk_urb(urb, udev, + usb_sndbulkpipe(udev, es1->cport_out_endpoint), + transfer_buffer, transfer_buffer_size, + cport_out_callback, hd); + retval = usb_submit_urb(urb, gfp_mask); + if (retval) { + pr_err("error %d submitting URB\n", retval); + free_urb(es1, urb); + return ERR_PTR(retval); + } + + return conceal_urb(urb); +} + +/* + * The cookie value supplied is the value that buffer_send() + * returned to its caller. It identifies the buffer that should be + * canceled. This function must also handle (which is to say, + * ignore) a null cookie value. + */ +static void buffer_cancel(void *cookie) +{ + + /* + * We really should be defensive and track all outstanding + * (sent) buffers rather than trusting the cookie provided + * is valid. For the time being, this will do. + */ + if (cookie) + usb_kill_urb(reveal_urb(cookie)); +} + +static struct greybus_host_driver es1_driver = { + .hd_priv_size = sizeof(struct es1_ap_dev), + .buffer_send = buffer_send, + .buffer_cancel = buffer_cancel, + .submit_svc = submit_svc, +}; + +/* Common function to report consistent warnings based on URB status */ +static int check_urb_status(struct urb *urb) +{ + struct device *dev = &urb->dev->dev; + int status = urb->status; + + switch (status) { + case 0: + return 0; + + case -EOVERFLOW: + dev_err(dev, "%s: overflow actual length is %d\n", + __func__, urb->actual_length); + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EILSEQ: + case -EPROTO: + /* device is gone, stop sending */ + return status; + } + dev_err(dev, "%s: unknown status %d\n", __func__, status); + + return -EAGAIN; +} + +static void ap_disconnect(struct usb_interface *interface) +{ + struct es1_ap_dev *es1; + struct usb_device *udev; + int i; + + es1 = usb_get_intfdata(interface); + if (!es1) + return; + + /* Tear down everything! */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + struct urb *urb = es1->cport_out_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + es1->cport_out_urb[i] = NULL; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ + } + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb = es1->cport_in_urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + kfree(es1->cport_in_buffer[i]); + es1->cport_in_buffer[i] = NULL; + } + + usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); + es1->svc_urb = NULL; + kfree(es1->svc_buffer); + es1->svc_buffer = NULL; + + usb_set_intfdata(interface, NULL); + udev = es1->usb_dev; + greybus_remove_hd(es1->hd); + + usb_put_dev(udev); +} + +/* Callback for when we get a SVC message */ +static void svc_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb svc in error %d (dropped)\n", status); + return; + } + + /* We have a message, create a new message structure, add it to the + * list, and wake up our thread that will process the messages. + */ + greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); + +exit: + /* resubmit the urb to get more messages */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "Can not submit urb for AP data: %d\n", retval); +} + +static void cport_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + u16 cport_id; + u8 *data; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb cport in error %d (dropped)\n", status); + return; + } + + /* The size has to be at least one, for the cport id */ + if (!urb->actual_length) { + dev_err(dev, "%s: no cport id in input buffer?\n", __func__); + goto exit; + } + + /* + * Our CPort number is the first byte of the data stream, + * the rest of the stream is "real" data + */ + data = urb->transfer_buffer; + cport_id = (u16)data[0]; + data = &data[1]; + + /* Pass this data to the greybus core */ + greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); + +exit: + /* put our urb back in the request pool */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "%s: error %d in submitting urb.\n", + __func__, retval); +} + +static void cport_out_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct es1_ap_dev *es1 = hd_to_es1(hd); + int status = check_urb_status(urb); + u8 *data = urb->transfer_buffer + 1; + + /* + * Tell the submitter that the buffer send (attempt) is + * complete, and report the status. The submitter's buffer + * starts after the one-byte CPort id we inserted. + */ + data = urb->transfer_buffer + 1; + greybus_data_sent(hd, data, status); + + free_urb(es1, urb); + /* + * Rest assured Greg, this craziness is getting fixed. + * + * Yes, you are right, we aren't telling anyone that the urb finished. + * "That's crazy! How does this all even work?" you might be saying. + * The "magic" is the idea that greybus works on the "operation" level, + * not the "send a buffer" level. All operations are "round-trip" with + * a response from the device that the operation finished, or it will + * time out. Because of that, we don't care that this urb finished, or + * failed, or did anything else, as higher levels of the protocol stack + * will handle completions and timeouts and the rest. + * + * This protocol is "needed" due to some hardware restrictions on the + * current generation of Unipro controllers. Think about it for a + * minute, this is a USB driver, talking to a Unipro bridge, impedance + * mismatch is huge, yet the Unipro controller are even more + * underpowered than this little USB controller. We rely on the round + * trip to keep stalls in the Unipro controllers from happening so that + * we can keep data flowing properly, no matter how slow it might be. + * + * Once again, a wonderful bus protocol cut down in its prime by a naive + * controller chip. We dream of the day we have a "real" HCD for + * Unipro. Until then, we suck it up and make the hardware work, as + * that's the job of the firmware and kernel. + * + */ +} + +/* + * The ES1 USB Bridge device contains 4 endpoints + * 1 Control - usual USB stuff + AP -> SVC messages + * 1 Interrupt IN - SVC -> AP messages + * 1 Bulk IN - CPort data in + * 1 Bulk OUT - CPort data out + */ +static int ap_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct es1_ap_dev *es1; + struct greybus_host_device *hd; + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + bool int_in_found = false; + bool bulk_in_found = false; + bool bulk_out_found = false; + int retval = -ENOMEM; + int i; + u8 svc_interval = 0; + + udev = usb_get_dev(interface_to_usbdev(interface)); + + hd = greybus_create_hd(&es1_driver, &udev->dev); + if (!hd) { + usb_put_dev(udev); + return -ENOMEM; + } + + /* Fill in the buffer allocation constraints */ + hd_buffer_constraints(hd); + + es1 = hd_to_es1(hd); + es1->hd = hd; + es1->usb_intf = interface; + es1->usb_dev = udev; + spin_lock_init(&es1->cport_out_urb_lock); + usb_set_intfdata(interface, es1); + + /* Control endpoint is the pipe to talk to this AP, so save it off */ + endpoint = &udev->ep0.desc; + es1->control_endpoint = endpoint->bEndpointAddress; + + /* find all 3 of our endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) { + es1->svc_endpoint = endpoint->bEndpointAddress; + svc_interval = endpoint->bInterval; + int_in_found = true; + } else if (usb_endpoint_is_bulk_in(endpoint)) { + es1->cport_in_endpoint = endpoint->bEndpointAddress; + bulk_in_found = true; + } else if (usb_endpoint_is_bulk_out(endpoint)) { + es1->cport_out_endpoint = endpoint->bEndpointAddress; + bulk_out_found = true; + } else { + dev_err(&udev->dev, + "Unknown endpoint type found, address %x\n", + endpoint->bEndpointAddress); + } + } + if ((int_in_found == false) || + (bulk_in_found == false) || + (bulk_out_found == false)) { + dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); + goto error; + } + + /* Create our buffer and URB to get SVC messages, and start it up */ + es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); + if (!es1->svc_buffer) + goto error; + + es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!es1->svc_urb) + goto error; + + usb_fill_int_urb(es1->svc_urb, udev, + usb_rcvintpipe(udev, es1->svc_endpoint), + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, + hd, svc_interval); + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + + /* Allocate buffers for our cport in messages and start them up */ + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb; + u8 *buffer; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error; + buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); + if (!buffer) + goto error; + + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, es1->cport_in_endpoint), + buffer, ES1_GBUF_MSG_SIZE_MAX, + cport_in_callback, hd); + es1->cport_in_urb[i] = urb; + es1->cport_in_buffer[i] = buffer; + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) + goto error; + } + + /* Allocate urbs for our CPort OUT messages */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + struct urb *urb; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error; + + es1->cport_out_urb[i] = urb; + es1->cport_out_urb_busy[i] = false; /* just to be anal */ + } + + return 0; +error: + ap_disconnect(interface); + + return retval; +} + +static struct usb_driver es1_ap_driver = { + .name = "es1_ap_driver", + .probe = ap_probe, + .disconnect = ap_disconnect, + .id_table = id_table, +}; + +module_usb_driver(es1_ap_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/gb-battery.c b/drivers/staging/greybus/gb-battery.c deleted file mode 100644 index 2130d73cbfde..000000000000 --- a/drivers/staging/greybus/gb-battery.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Battery driver for a Greybus module. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include "greybus.h" - -struct gb_battery { - struct power_supply bat; - // FIXME - // we will want to keep the battery stats in here as we will be getting - // updates from the SVC "on the fly" so we don't have to always go ask - // the battery for some information. Hopefully... - struct gb_connection *connection; - u8 version_major; - u8 version_minor; - -}; -#define to_gb_battery(x) container_of(x, struct gb_battery, bat) - -/* Version of the Greybus battery protocol we support */ -#define GB_BATTERY_VERSION_MAJOR 0x00 -#define GB_BATTERY_VERSION_MINOR 0x01 - -/* Greybus battery request types */ -#define GB_BATTERY_TYPE_INVALID 0x00 -#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01 -#define GB_BATTERY_TYPE_TECHNOLOGY 0x02 -#define GB_BATTERY_TYPE_STATUS 0x03 -#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 -#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05 -#define GB_BATTERY_TYPE_TEMPERATURE 0x06 -#define GB_BATTERY_TYPE_VOLTAGE 0x07 -#define GB_BATTERY_TYPE_CURRENT 0x08 -#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX -#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX - -struct gb_battery_proto_version_response { - __u8 major; - __u8 minor; -}; - -/* Should match up with battery types in linux/power_supply.h */ -#define GB_BATTERY_TECH_UNKNOWN 0x0000 -#define GB_BATTERY_TECH_NiMH 0x0001 -#define GB_BATTERY_TECH_LION 0x0002 -#define GB_BATTERY_TECH_LIPO 0x0003 -#define GB_BATTERY_TECH_LiFe 0x0004 -#define GB_BATTERY_TECH_NiCd 0x0005 -#define GB_BATTERY_TECH_LiMn 0x0006 - -struct gb_battery_technology_response { - __le32 technology; -}; - -/* Should match up with battery status in linux/power_supply.h */ -#define GB_BATTERY_STATUS_UNKNOWN 0x0000 -#define GB_BATTERY_STATUS_CHARGING 0x0001 -#define GB_BATTERY_STATUS_DISCHARGING 0x0002 -#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003 -#define GB_BATTERY_STATUS_FULL 0x0004 - -struct gb_battery_status_response { - __le16 battery_status; -}; - -struct gb_battery_max_voltage_response { - __le32 max_voltage; -}; - -struct gb_battery_capacity_response { - __le32 capacity; -}; - -struct gb_battery_temperature_response { - __le32 temperature; -}; - -struct gb_battery_voltage_response { - __le32 voltage; -}; - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_battery *gb) -{ - struct gb_battery_proto_version_response version_response; - int retval; - - retval = gb_operation_sync(gb->connection, - GB_BATTERY_TYPE_PROTOCOL_VERSION, - NULL, 0, - &version_response, sizeof(version_response)); - if (retval) - return retval; - - if (version_response.major > GB_BATTERY_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - version_response.major, GB_BATTERY_VERSION_MAJOR); - return -ENOTSUPP; - } - - gb->version_major = version_response.major; - gb->version_minor = version_response.minor; - return 0; -} - -static int get_tech(struct gb_battery *gb) -{ - struct gb_battery_technology_response tech_response; - u32 technology; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY, - NULL, 0, - &tech_response, sizeof(tech_response)); - if (retval) - return retval; - - /* - * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optimize the code away to - * nothing. - */ - technology = le32_to_cpu(tech_response.technology); - switch (technology) { - case GB_BATTERY_TECH_NiMH: - technology = POWER_SUPPLY_TECHNOLOGY_NiMH; - break; - case GB_BATTERY_TECH_LION: - technology = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case GB_BATTERY_TECH_LIPO: - technology = POWER_SUPPLY_TECHNOLOGY_LIPO; - break; - case GB_BATTERY_TECH_LiFe: - technology = POWER_SUPPLY_TECHNOLOGY_LiFe; - break; - case GB_BATTERY_TECH_NiCd: - technology = POWER_SUPPLY_TECHNOLOGY_NiCd; - break; - case GB_BATTERY_TECH_LiMn: - technology = POWER_SUPPLY_TECHNOLOGY_LiMn; - break; - case GB_BATTERY_TECH_UNKNOWN: - default: - technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; - break; - } - return technology; -} - -static int get_status(struct gb_battery *gb) -{ - struct gb_battery_status_response status_response; - u16 battery_status; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS, - NULL, 0, - &status_response, sizeof(status_response)); - if (retval) - return retval; - - /* - * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optimize the code away to - * nothing. - */ - battery_status = le16_to_cpu(status_response.battery_status); - switch (battery_status) { - case GB_BATTERY_STATUS_CHARGING: - battery_status = POWER_SUPPLY_STATUS_CHARGING; - break; - case GB_BATTERY_STATUS_DISCHARGING: - battery_status = POWER_SUPPLY_STATUS_DISCHARGING; - break; - case GB_BATTERY_STATUS_NOT_CHARGING: - battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; - break; - case GB_BATTERY_STATUS_FULL: - battery_status = POWER_SUPPLY_STATUS_FULL; - break; - case GB_BATTERY_STATUS_UNKNOWN: - default: - battery_status = POWER_SUPPLY_STATUS_UNKNOWN; - break; - } - return battery_status; -} - -static int get_max_voltage(struct gb_battery *gb) -{ - struct gb_battery_max_voltage_response volt_response; - u32 max_voltage; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE, - NULL, 0, - &volt_response, sizeof(volt_response)); - if (retval) - return retval; - - max_voltage = le32_to_cpu(volt_response.max_voltage); - return max_voltage; -} - -static int get_percent_capacity(struct gb_battery *gb) -{ - struct gb_battery_capacity_response capacity_response; - u32 capacity; - int retval; - - retval = gb_operation_sync(gb->connection, - GB_BATTERY_TYPE_PERCENT_CAPACITY, - NULL, 0, &capacity_response, - sizeof(capacity_response)); - if (retval) - return retval; - - capacity = le32_to_cpu(capacity_response.capacity); - return capacity; -} - -static int get_temp(struct gb_battery *gb) -{ - struct gb_battery_temperature_response temp_response; - u32 temperature; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE, - NULL, 0, - &temp_response, sizeof(temp_response)); - if (retval) - return retval; - - temperature = le32_to_cpu(temp_response.temperature); - return temperature; -} - -static int get_voltage(struct gb_battery *gb) -{ - struct gb_battery_voltage_response voltage_response; - u32 voltage; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE, - NULL, 0, - &voltage_response, sizeof(voltage_response)); - if (retval) - return retval; - - voltage = le32_to_cpu(voltage_response.voltage); - return voltage; -} - -static int get_property(struct power_supply *b, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct gb_battery *gb = to_gb_battery(b); - - switch (psp) { - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = get_tech(gb); - break; - - case POWER_SUPPLY_PROP_STATUS: - val->intval = get_status(gb); - break; - - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = get_max_voltage(gb); - break; - - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = get_percent_capacity(gb); - break; - - case POWER_SUPPLY_PROP_TEMP: - val->intval = get_temp(gb); - break; - - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = get_voltage(gb); - break; - - default: - return -EINVAL; - } - - return 0; -} - -// FIXME - verify this list, odds are some can be removed and others added. -static enum power_supply_property battery_props[] = { - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_VOLTAGE_NOW, -}; - -static int gb_battery_connection_init(struct gb_connection *connection) -{ - struct gb_battery *gb; - struct power_supply *b; - int retval; - - gb = kzalloc(sizeof(*gb), GFP_KERNEL); - if (!gb) - return -ENOMEM; - - gb->connection = connection; - connection->private = gb; - - /* Check the version */ - retval = get_version(gb); - if (retval) { - kfree(gb); - return retval; - } - - b = &gb->bat; - // FIXME - get a better (i.e. unique) name - // FIXME - anything else needs to be set? - b->name = "gb_battery"; - b->type = POWER_SUPPLY_TYPE_BATTERY, - b->properties = battery_props, - b->num_properties = ARRAY_SIZE(battery_props), - b->get_property = get_property, - - retval = power_supply_register(&connection->bundle->intf->dev, b); - if (retval) { - kfree(gb); - return retval; - } - - return 0; -} - -static void gb_battery_connection_exit(struct gb_connection *connection) -{ - struct gb_battery *gb = connection->private; - - power_supply_unregister(&gb->bat); - kfree(gb); -} - -static struct gb_protocol battery_protocol = { - .name = "battery", - .id = GREYBUS_PROTOCOL_BATTERY, - .major = 0, - .minor = 1, - .connection_init = gb_battery_connection_init, - .connection_exit = gb_battery_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -gb_protocol_driver(&battery_protocol); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/gb-es1.c b/drivers/staging/greybus/gb-es1.c deleted file mode 100644 index 2ec5d7b403a3..000000000000 --- a/drivers/staging/greybus/gb-es1.c +++ /dev/null @@ -1,618 +0,0 @@ -/* - * Greybus "AP" USB driver - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ -#include -#include -#include -#include -#include -#include - -#include "greybus.h" -#include "svc_msg.h" -#include "kernel_ver.h" - -/* - * Macros for making pointers explicitly opaque, such that the result - * isn't valid but also can't be mistaken for an ERR_PTR() value. - */ -#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) -#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) - -/* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) -#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE - -static const struct usb_device_id id_table[] = { - /* Made up numbers for the SVC USB Bridge in ES1 */ - { USB_DEVICE(0xffff, 0x0001) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -/* - * Number of CPort IN urbs in flight at any point in time. - * Adjust if we are having stalls in the USB buffer due to not enough urbs in - * flight. - */ -#define NUM_CPORT_IN_URB 4 - -/* Number of CPort OUT urbs in flight at any point in time. - * Adjust if we get messages saying we are out of urbs in the system log. - */ -#define NUM_CPORT_OUT_URB 8 - -/** - * es1_ap_dev - ES1 USB Bridge to AP structure - * @usb_dev: pointer to the USB device we are. - * @usb_intf: pointer to the USB interface we are bound to. - * @hd: pointer to our greybus_host_device structure - * @control_endpoint: endpoint to send data to SVC - * @svc_endpoint: endpoint for SVC data in - * @cport_in_endpoint: bulk in endpoint for CPort data - * @cport-out_endpoint: bulk out endpoint for CPort data - * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint - * @svc_urb: urb for SVC messages coming in on @svc_endpoint - * @cport_in_urb: array of urbs for the CPort in messages - * @cport_in_buffer: array of buffers for the @cport_in_urb urbs - * @cport_out_urb: array of urbs for the CPort out messages - * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or - * not. - * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" - */ -struct es1_ap_dev { - struct usb_device *usb_dev; - struct usb_interface *usb_intf; - struct greybus_host_device *hd; - - __u8 control_endpoint; - __u8 svc_endpoint; - __u8 cport_in_endpoint; - __u8 cport_out_endpoint; - - u8 *svc_buffer; - struct urb *svc_urb; - - struct urb *cport_in_urb[NUM_CPORT_IN_URB]; - u8 *cport_in_buffer[NUM_CPORT_IN_URB]; - struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; - bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; - spinlock_t cport_out_urb_lock; -}; - -static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) -{ - return (struct es1_ap_dev *)&hd->hd_priv; -} - -static void cport_out_callback(struct urb *urb); - -/* - * Buffer constraints for the host driver. - * - * A "buffer" is used to hold data to be transferred for Greybus by - * the host driver. A buffer is represented by a "buffer pointer", - * which defines a region of memory used by the host driver for - * transferring the data. When Greybus allocates a buffer, it must - * do so subject to the constraints associated with the host driver. - * These constraints are specified by two parameters: the - * headroom; and the maximum buffer size. - * - * +------------------+ - * | Host driver | \ - * | reserved area | }- headroom - * | . . . | / - * buffer pointer ---> +------------------+ - * | Buffer space for | \ - * | transferred data | }- buffer size - * | . . . | / (limited to size_max) - * +------------------+ - * - * headroom: Every buffer must have at least this much space - * *before* the buffer pointer, reserved for use by the - * host driver. I.e., ((char *)buffer - headroom) must - * point to valid memory, usable only by the host driver. - * size_max: The maximum size of a buffer (not including the - * headroom) must not exceed this. - */ -static void hd_buffer_constraints(struct greybus_host_device *hd) -{ - /* - * Only one byte is required, but this produces a result - * that's better aligned for the user. - */ - hd->buffer_headroom = sizeof(u32); /* For cport id */ - hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; - BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); -} - -#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ -static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - int retval; - - /* SVC messages go down our control pipe */ - retval = usb_control_msg(es1->usb_dev, - usb_sndctrlpipe(es1->usb_dev, - es1->control_endpoint), - 0x01, /* vendor request AP message */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, 0x00, - (char *)svc_msg, - sizeof(*svc_msg), - ES1_TIMEOUT); - if (retval != sizeof(*svc_msg)) - return retval; - - return 0; -} - -static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) -{ - struct urb *urb = NULL; - unsigned long flags; - int i; - - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - - /* Look in our pool of allocated urbs first, as that's the "fastest" */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (es1->cport_out_urb_busy[i] == false) { - es1->cport_out_urb_busy[i] = true; - urb = es1->cport_out_urb[i]; - break; - } - } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - if (urb) - return urb; - - /* - * Crap, pool is empty, complain to the syslog and go allocate one - * dynamically as we have to succeed. - */ - dev_err(&es1->usb_dev->dev, - "No free CPort OUT urbs, having to dynamically allocate one!\n"); - return usb_alloc_urb(0, gfp_mask); -} - -static void free_urb(struct es1_ap_dev *es1, struct urb *urb) -{ - unsigned long flags; - int i; - /* - * See if this was an urb in our pool, if so mark it "free", otherwise - * we need to free it ourselves. - */ - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (urb == es1->cport_out_urb[i]) { - es1->cport_out_urb_busy[i] = false; - urb = NULL; - break; - } - } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - - /* If urb is not NULL, then we need to free this urb */ - usb_free_urb(urb); -} - -/* - * Returns an opaque cookie value if successful, or a pointer coded - * error otherwise. If the caller wishes to cancel the in-flight - * buffer, it must supply the returned cookie to the cancel routine. - */ -static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, - void *buffer, size_t buffer_size, gfp_t gfp_mask) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; - u8 *transfer_buffer = buffer; - int transfer_buffer_size; - int retval; - struct urb *urb; - - if (!buffer) { - pr_err("null buffer supplied to send\n"); - return ERR_PTR(-EINVAL); - } - if (buffer_size > (size_t)INT_MAX) { - pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); - return ERR_PTR(-EINVAL); - } - transfer_buffer--; - transfer_buffer_size = buffer_size + 1; - - /* - * The data actually transferred will include an indication - * of where the data should be sent. Do one last check of - * the target CPort id before filling it in. - */ - if (cport_id == CPORT_ID_BAD) { - pr_err("request to send inbound data buffer\n"); - return ERR_PTR(-EINVAL); - } - if (cport_id > (u16)U8_MAX) { - pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); - return ERR_PTR(-EINVAL); - } - /* OK, the destination is fine; record it in the transfer buffer */ - *transfer_buffer = cport_id; - - /* Find a free urb */ - urb = next_free_urb(es1, gfp_mask); - if (!urb) - return ERR_PTR(-ENOMEM); - - usb_fill_bulk_urb(urb, udev, - usb_sndbulkpipe(udev, es1->cport_out_endpoint), - transfer_buffer, transfer_buffer_size, - cport_out_callback, hd); - retval = usb_submit_urb(urb, gfp_mask); - if (retval) { - pr_err("error %d submitting URB\n", retval); - free_urb(es1, urb); - return ERR_PTR(retval); - } - - return conceal_urb(urb); -} - -/* - * The cookie value supplied is the value that buffer_send() - * returned to its caller. It identifies the buffer that should be - * canceled. This function must also handle (which is to say, - * ignore) a null cookie value. - */ -static void buffer_cancel(void *cookie) -{ - - /* - * We really should be defensive and track all outstanding - * (sent) buffers rather than trusting the cookie provided - * is valid. For the time being, this will do. - */ - if (cookie) - usb_kill_urb(reveal_urb(cookie)); -} - -static struct greybus_host_driver es1_driver = { - .hd_priv_size = sizeof(struct es1_ap_dev), - .buffer_send = buffer_send, - .buffer_cancel = buffer_cancel, - .submit_svc = submit_svc, -}; - -/* Common function to report consistent warnings based on URB status */ -static int check_urb_status(struct urb *urb) -{ - struct device *dev = &urb->dev->dev; - int status = urb->status; - - switch (status) { - case 0: - return 0; - - case -EOVERFLOW: - dev_err(dev, "%s: overflow actual length is %d\n", - __func__, urb->actual_length); - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - case -EILSEQ: - case -EPROTO: - /* device is gone, stop sending */ - return status; - } - dev_err(dev, "%s: unknown status %d\n", __func__, status); - - return -EAGAIN; -} - -static void ap_disconnect(struct usb_interface *interface) -{ - struct es1_ap_dev *es1; - struct usb_device *udev; - int i; - - es1 = usb_get_intfdata(interface); - if (!es1) - return; - - /* Tear down everything! */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb = es1->cport_out_urb[i]; - - if (!urb) - break; - usb_kill_urb(urb); - usb_free_urb(urb); - es1->cport_out_urb[i] = NULL; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ - } - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb = es1->cport_in_urb[i]; - - if (!urb) - break; - usb_kill_urb(urb); - usb_free_urb(urb); - kfree(es1->cport_in_buffer[i]); - es1->cport_in_buffer[i] = NULL; - } - - usb_kill_urb(es1->svc_urb); - usb_free_urb(es1->svc_urb); - es1->svc_urb = NULL; - kfree(es1->svc_buffer); - es1->svc_buffer = NULL; - - usb_set_intfdata(interface, NULL); - udev = es1->usb_dev; - greybus_remove_hd(es1->hd); - - usb_put_dev(udev); -} - -/* Callback for when we get a SVC message */ -static void svc_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb svc in error %d (dropped)\n", status); - return; - } - - /* We have a message, create a new message structure, add it to the - * list, and wake up our thread that will process the messages. - */ - greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); - -exit: - /* resubmit the urb to get more messages */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "Can not submit urb for AP data: %d\n", retval); -} - -static void cport_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - u16 cport_id; - u8 *data; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb cport in error %d (dropped)\n", status); - return; - } - - /* The size has to be at least one, for the cport id */ - if (!urb->actual_length) { - dev_err(dev, "%s: no cport id in input buffer?\n", __func__); - goto exit; - } - - /* - * Our CPort number is the first byte of the data stream, - * the rest of the stream is "real" data - */ - data = urb->transfer_buffer; - cport_id = (u16)data[0]; - data = &data[1]; - - /* Pass this data to the greybus core */ - greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); - -exit: - /* put our urb back in the request pool */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "%s: error %d in submitting urb.\n", - __func__, retval); -} - -static void cport_out_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct es1_ap_dev *es1 = hd_to_es1(hd); - int status = check_urb_status(urb); - u8 *data = urb->transfer_buffer + 1; - - /* - * Tell the submitter that the buffer send (attempt) is - * complete, and report the status. The submitter's buffer - * starts after the one-byte CPort id we inserted. - */ - data = urb->transfer_buffer + 1; - greybus_data_sent(hd, data, status); - - free_urb(es1, urb); - /* - * Rest assured Greg, this craziness is getting fixed. - * - * Yes, you are right, we aren't telling anyone that the urb finished. - * "That's crazy! How does this all even work?" you might be saying. - * The "magic" is the idea that greybus works on the "operation" level, - * not the "send a buffer" level. All operations are "round-trip" with - * a response from the device that the operation finished, or it will - * time out. Because of that, we don't care that this urb finished, or - * failed, or did anything else, as higher levels of the protocol stack - * will handle completions and timeouts and the rest. - * - * This protocol is "needed" due to some hardware restrictions on the - * current generation of Unipro controllers. Think about it for a - * minute, this is a USB driver, talking to a Unipro bridge, impedance - * mismatch is huge, yet the Unipro controller are even more - * underpowered than this little USB controller. We rely on the round - * trip to keep stalls in the Unipro controllers from happening so that - * we can keep data flowing properly, no matter how slow it might be. - * - * Once again, a wonderful bus protocol cut down in its prime by a naive - * controller chip. We dream of the day we have a "real" HCD for - * Unipro. Until then, we suck it up and make the hardware work, as - * that's the job of the firmware and kernel. - * - */ -} - -/* - * The ES1 USB Bridge device contains 4 endpoints - * 1 Control - usual USB stuff + AP -> SVC messages - * 1 Interrupt IN - SVC -> AP messages - * 1 Bulk IN - CPort data in - * 1 Bulk OUT - CPort data out - */ -static int ap_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct es1_ap_dev *es1; - struct greybus_host_device *hd; - struct usb_device *udev; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - bool int_in_found = false; - bool bulk_in_found = false; - bool bulk_out_found = false; - int retval = -ENOMEM; - int i; - u8 svc_interval = 0; - - udev = usb_get_dev(interface_to_usbdev(interface)); - - hd = greybus_create_hd(&es1_driver, &udev->dev); - if (!hd) { - usb_put_dev(udev); - return -ENOMEM; - } - - /* Fill in the buffer allocation constraints */ - hd_buffer_constraints(hd); - - es1 = hd_to_es1(hd); - es1->hd = hd; - es1->usb_intf = interface; - es1->usb_dev = udev; - spin_lock_init(&es1->cport_out_urb_lock); - usb_set_intfdata(interface, es1); - - /* Control endpoint is the pipe to talk to this AP, so save it off */ - endpoint = &udev->ep0.desc; - es1->control_endpoint = endpoint->bEndpointAddress; - - /* find all 3 of our endpoints */ - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (usb_endpoint_is_int_in(endpoint)) { - es1->svc_endpoint = endpoint->bEndpointAddress; - svc_interval = endpoint->bInterval; - int_in_found = true; - } else if (usb_endpoint_is_bulk_in(endpoint)) { - es1->cport_in_endpoint = endpoint->bEndpointAddress; - bulk_in_found = true; - } else if (usb_endpoint_is_bulk_out(endpoint)) { - es1->cport_out_endpoint = endpoint->bEndpointAddress; - bulk_out_found = true; - } else { - dev_err(&udev->dev, - "Unknown endpoint type found, address %x\n", - endpoint->bEndpointAddress); - } - } - if ((int_in_found == false) || - (bulk_in_found == false) || - (bulk_out_found == false)) { - dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); - goto error; - } - - /* Create our buffer and URB to get SVC messages, and start it up */ - es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); - if (!es1->svc_buffer) - goto error; - - es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!es1->svc_urb) - goto error; - - usb_fill_int_urb(es1->svc_urb, udev, - usb_rcvintpipe(udev, es1->svc_endpoint), - es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, - hd, svc_interval); - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; - - /* Allocate buffers for our cport in messages and start them up */ - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb; - u8 *buffer; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - goto error; - buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); - if (!buffer) - goto error; - - usb_fill_bulk_urb(urb, udev, - usb_rcvbulkpipe(udev, es1->cport_in_endpoint), - buffer, ES1_GBUF_MSG_SIZE_MAX, - cport_in_callback, hd); - es1->cport_in_urb[i] = urb; - es1->cport_in_buffer[i] = buffer; - retval = usb_submit_urb(urb, GFP_KERNEL); - if (retval) - goto error; - } - - /* Allocate urbs for our CPort OUT messages */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - goto error; - - es1->cport_out_urb[i] = urb; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ - } - - return 0; -error: - ap_disconnect(interface); - - return retval; -} - -static struct usb_driver es1_ap_driver = { - .name = "es1_ap_driver", - .probe = ap_probe, - .disconnect = ap_disconnect, - .id_table = id_table, -}; - -module_usb_driver(es1_ap_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/gb-es2.c b/drivers/staging/greybus/gb-es2.c deleted file mode 100644 index 4154cce52a16..000000000000 --- a/drivers/staging/greybus/gb-es2.c +++ /dev/null @@ -1,618 +0,0 @@ -/* - * Greybus "AP" USB driver for "ES2" controller chips - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ -#include -#include -#include -#include -#include -#include - -#include "greybus.h" -#include "svc_msg.h" -#include "kernel_ver.h" - -/* - * Macros for making pointers explicitly opaque, such that the result - * isn't valid but also can't be mistaken for an ERR_PTR() value. - */ -#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) -#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) - -/* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) -#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE - -static const struct usb_device_id id_table[] = { - /* Made up numbers for the SVC USB Bridge in ES1 */ - { USB_DEVICE(0xffff, 0x0001) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -/* - * Number of CPort IN urbs in flight at any point in time. - * Adjust if we are having stalls in the USB buffer due to not enough urbs in - * flight. - */ -#define NUM_CPORT_IN_URB 4 - -/* Number of CPort OUT urbs in flight at any point in time. - * Adjust if we get messages saying we are out of urbs in the system log. - */ -#define NUM_CPORT_OUT_URB 8 - -/** - * es1_ap_dev - ES1 USB Bridge to AP structure - * @usb_dev: pointer to the USB device we are. - * @usb_intf: pointer to the USB interface we are bound to. - * @hd: pointer to our greybus_host_device structure - * @control_endpoint: endpoint to send data to SVC - * @svc_endpoint: endpoint for SVC data in - * @cport_in_endpoint: bulk in endpoint for CPort data - * @cport-out_endpoint: bulk out endpoint for CPort data - * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint - * @svc_urb: urb for SVC messages coming in on @svc_endpoint - * @cport_in_urb: array of urbs for the CPort in messages - * @cport_in_buffer: array of buffers for the @cport_in_urb urbs - * @cport_out_urb: array of urbs for the CPort out messages - * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or - * not. - * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" - */ -struct es1_ap_dev { - struct usb_device *usb_dev; - struct usb_interface *usb_intf; - struct greybus_host_device *hd; - - __u8 control_endpoint; - __u8 svc_endpoint; - __u8 cport_in_endpoint; - __u8 cport_out_endpoint; - - u8 *svc_buffer; - struct urb *svc_urb; - - struct urb *cport_in_urb[NUM_CPORT_IN_URB]; - u8 *cport_in_buffer[NUM_CPORT_IN_URB]; - struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; - bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; - spinlock_t cport_out_urb_lock; -}; - -static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) -{ - return (struct es1_ap_dev *)&hd->hd_priv; -} - -static void cport_out_callback(struct urb *urb); - -/* - * Buffer constraints for the host driver. - * - * A "buffer" is used to hold data to be transferred for Greybus by - * the host driver. A buffer is represented by a "buffer pointer", - * which defines a region of memory used by the host driver for - * transferring the data. When Greybus allocates a buffer, it must - * do so subject to the constraints associated with the host driver. - * These constraints are specified by two parameters: the - * headroom; and the maximum buffer size. - * - * +------------------+ - * | Host driver | \ - * | reserved area | }- headroom - * | . . . | / - * buffer pointer ---> +------------------+ - * | Buffer space for | \ - * | transferred data | }- buffer size - * | . . . | / (limited to size_max) - * +------------------+ - * - * headroom: Every buffer must have at least this much space - * *before* the buffer pointer, reserved for use by the - * host driver. I.e., ((char *)buffer - headroom) must - * point to valid memory, usable only by the host driver. - * size_max: The maximum size of a buffer (not including the - * headroom) must not exceed this. - */ -static void hd_buffer_constraints(struct greybus_host_device *hd) -{ - /* - * Only one byte is required, but this produces a result - * that's better aligned for the user. - */ - hd->buffer_headroom = sizeof(u32); /* For cport id */ - hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; - BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); -} - -#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ -static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - int retval; - - /* SVC messages go down our control pipe */ - retval = usb_control_msg(es1->usb_dev, - usb_sndctrlpipe(es1->usb_dev, - es1->control_endpoint), - 0x01, /* vendor request AP message */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, 0x00, - (char *)svc_msg, - sizeof(*svc_msg), - ES1_TIMEOUT); - if (retval != sizeof(*svc_msg)) - return retval; - - return 0; -} - -static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) -{ - struct urb *urb = NULL; - unsigned long flags; - int i; - - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - - /* Look in our pool of allocated urbs first, as that's the "fastest" */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (es1->cport_out_urb_busy[i] == false) { - es1->cport_out_urb_busy[i] = true; - urb = es1->cport_out_urb[i]; - break; - } - } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - if (urb) - return urb; - - /* - * Crap, pool is empty, complain to the syslog and go allocate one - * dynamically as we have to succeed. - */ - dev_err(&es1->usb_dev->dev, - "No free CPort OUT urbs, having to dynamically allocate one!\n"); - return usb_alloc_urb(0, gfp_mask); -} - -static void free_urb(struct es1_ap_dev *es1, struct urb *urb) -{ - unsigned long flags; - int i; - /* - * See if this was an urb in our pool, if so mark it "free", otherwise - * we need to free it ourselves. - */ - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (urb == es1->cport_out_urb[i]) { - es1->cport_out_urb_busy[i] = false; - urb = NULL; - break; - } - } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - - /* If urb is not NULL, then we need to free this urb */ - usb_free_urb(urb); -} - -/* - * Returns an opaque cookie value if successful, or a pointer coded - * error otherwise. If the caller wishes to cancel the in-flight - * buffer, it must supply the returned cookie to the cancel routine. - */ -static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, - void *buffer, size_t buffer_size, gfp_t gfp_mask) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; - u8 *transfer_buffer = buffer; - int transfer_buffer_size; - int retval; - struct urb *urb; - - if (!buffer) { - pr_err("null buffer supplied to send\n"); - return ERR_PTR(-EINVAL); - } - if (buffer_size > (size_t)INT_MAX) { - pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); - return ERR_PTR(-EINVAL); - } - transfer_buffer--; - transfer_buffer_size = buffer_size + 1; - - /* - * The data actually transferred will include an indication - * of where the data should be sent. Do one last check of - * the target CPort id before filling it in. - */ - if (cport_id == CPORT_ID_BAD) { - pr_err("request to send inbound data buffer\n"); - return ERR_PTR(-EINVAL); - } - if (cport_id > (u16)U8_MAX) { - pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); - return ERR_PTR(-EINVAL); - } - /* OK, the destination is fine; record it in the transfer buffer */ - *transfer_buffer = cport_id; - - /* Find a free urb */ - urb = next_free_urb(es1, gfp_mask); - if (!urb) - return ERR_PTR(-ENOMEM); - - usb_fill_bulk_urb(urb, udev, - usb_sndbulkpipe(udev, es1->cport_out_endpoint), - transfer_buffer, transfer_buffer_size, - cport_out_callback, hd); - retval = usb_submit_urb(urb, gfp_mask); - if (retval) { - pr_err("error %d submitting URB\n", retval); - free_urb(es1, urb); - return ERR_PTR(retval); - } - - return conceal_urb(urb); -} - -/* - * The cookie value supplied is the value that buffer_send() - * returned to its caller. It identifies the buffer that should be - * canceled. This function must also handle (which is to say, - * ignore) a null cookie value. - */ -static void buffer_cancel(void *cookie) -{ - - /* - * We really should be defensive and track all outstanding - * (sent) buffers rather than trusting the cookie provided - * is valid. For the time being, this will do. - */ - if (cookie) - usb_kill_urb(reveal_urb(cookie)); -} - -static struct greybus_host_driver es1_driver = { - .hd_priv_size = sizeof(struct es1_ap_dev), - .buffer_send = buffer_send, - .buffer_cancel = buffer_cancel, - .submit_svc = submit_svc, -}; - -/* Common function to report consistent warnings based on URB status */ -static int check_urb_status(struct urb *urb) -{ - struct device *dev = &urb->dev->dev; - int status = urb->status; - - switch (status) { - case 0: - return 0; - - case -EOVERFLOW: - dev_err(dev, "%s: overflow actual length is %d\n", - __func__, urb->actual_length); - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - case -EILSEQ: - case -EPROTO: - /* device is gone, stop sending */ - return status; - } - dev_err(dev, "%s: unknown status %d\n", __func__, status); - - return -EAGAIN; -} - -static void ap_disconnect(struct usb_interface *interface) -{ - struct es1_ap_dev *es1; - struct usb_device *udev; - int i; - - es1 = usb_get_intfdata(interface); - if (!es1) - return; - - /* Tear down everything! */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb = es1->cport_out_urb[i]; - - if (!urb) - break; - usb_kill_urb(urb); - usb_free_urb(urb); - es1->cport_out_urb[i] = NULL; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ - } - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb = es1->cport_in_urb[i]; - - if (!urb) - break; - usb_kill_urb(urb); - usb_free_urb(urb); - kfree(es1->cport_in_buffer[i]); - es1->cport_in_buffer[i] = NULL; - } - - usb_kill_urb(es1->svc_urb); - usb_free_urb(es1->svc_urb); - es1->svc_urb = NULL; - kfree(es1->svc_buffer); - es1->svc_buffer = NULL; - - usb_set_intfdata(interface, NULL); - udev = es1->usb_dev; - greybus_remove_hd(es1->hd); - - usb_put_dev(udev); -} - -/* Callback for when we get a SVC message */ -static void svc_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb svc in error %d (dropped)\n", status); - return; - } - - /* We have a message, create a new message structure, add it to the - * list, and wake up our thread that will process the messages. - */ - greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); - -exit: - /* resubmit the urb to get more messages */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "Can not submit urb for AP data: %d\n", retval); -} - -static void cport_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - u16 cport_id; - u8 *data; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb cport in error %d (dropped)\n", status); - return; - } - - /* The size has to be at least one, for the cport id */ - if (!urb->actual_length) { - dev_err(dev, "%s: no cport id in input buffer?\n", __func__); - goto exit; - } - - /* - * Our CPort number is the first byte of the data stream, - * the rest of the stream is "real" data - */ - data = urb->transfer_buffer; - cport_id = (u16)data[0]; - data = &data[1]; - - /* Pass this data to the greybus core */ - greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); - -exit: - /* put our urb back in the request pool */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "%s: error %d in submitting urb.\n", - __func__, retval); -} - -static void cport_out_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct es1_ap_dev *es1 = hd_to_es1(hd); - int status = check_urb_status(urb); - u8 *data = urb->transfer_buffer + 1; - - /* - * Tell the submitter that the buffer send (attempt) is - * complete, and report the status. The submitter's buffer - * starts after the one-byte CPort id we inserted. - */ - data = urb->transfer_buffer + 1; - greybus_data_sent(hd, data, status); - - free_urb(es1, urb); - /* - * Rest assured Greg, this craziness is getting fixed. - * - * Yes, you are right, we aren't telling anyone that the urb finished. - * "That's crazy! How does this all even work?" you might be saying. - * The "magic" is the idea that greybus works on the "operation" level, - * not the "send a buffer" level. All operations are "round-trip" with - * a response from the device that the operation finished, or it will - * time out. Because of that, we don't care that this urb finished, or - * failed, or did anything else, as higher levels of the protocol stack - * will handle completions and timeouts and the rest. - * - * This protocol is "needed" due to some hardware restrictions on the - * current generation of Unipro controllers. Think about it for a - * minute, this is a USB driver, talking to a Unipro bridge, impedance - * mismatch is huge, yet the Unipro controller are even more - * underpowered than this little USB controller. We rely on the round - * trip to keep stalls in the Unipro controllers from happening so that - * we can keep data flowing properly, no matter how slow it might be. - * - * Once again, a wonderful bus protocol cut down in its prime by a naive - * controller chip. We dream of the day we have a "real" HCD for - * Unipro. Until then, we suck it up and make the hardware work, as - * that's the job of the firmware and kernel. - * - */ -} - -/* - * The ES1 USB Bridge device contains 4 endpoints - * 1 Control - usual USB stuff + AP -> SVC messages - * 1 Interrupt IN - SVC -> AP messages - * 1 Bulk IN - CPort data in - * 1 Bulk OUT - CPort data out - */ -static int ap_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct es1_ap_dev *es1; - struct greybus_host_device *hd; - struct usb_device *udev; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - bool int_in_found = false; - bool bulk_in_found = false; - bool bulk_out_found = false; - int retval = -ENOMEM; - int i; - u8 svc_interval = 0; - - udev = usb_get_dev(interface_to_usbdev(interface)); - - hd = greybus_create_hd(&es1_driver, &udev->dev); - if (!hd) { - usb_put_dev(udev); - return -ENOMEM; - } - - /* Fill in the buffer allocation constraints */ - hd_buffer_constraints(hd); - - es1 = hd_to_es1(hd); - es1->hd = hd; - es1->usb_intf = interface; - es1->usb_dev = udev; - spin_lock_init(&es1->cport_out_urb_lock); - usb_set_intfdata(interface, es1); - - /* Control endpoint is the pipe to talk to this AP, so save it off */ - endpoint = &udev->ep0.desc; - es1->control_endpoint = endpoint->bEndpointAddress; - - /* find all 3 of our endpoints */ - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (usb_endpoint_is_int_in(endpoint)) { - es1->svc_endpoint = endpoint->bEndpointAddress; - svc_interval = endpoint->bInterval; - int_in_found = true; - } else if (usb_endpoint_is_bulk_in(endpoint)) { - es1->cport_in_endpoint = endpoint->bEndpointAddress; - bulk_in_found = true; - } else if (usb_endpoint_is_bulk_out(endpoint)) { - es1->cport_out_endpoint = endpoint->bEndpointAddress; - bulk_out_found = true; - } else { - dev_err(&udev->dev, - "Unknown endpoint type found, address %x\n", - endpoint->bEndpointAddress); - } - } - if ((int_in_found == false) || - (bulk_in_found == false) || - (bulk_out_found == false)) { - dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); - goto error; - } - - /* Create our buffer and URB to get SVC messages, and start it up */ - es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); - if (!es1->svc_buffer) - goto error; - - es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!es1->svc_urb) - goto error; - - usb_fill_int_urb(es1->svc_urb, udev, - usb_rcvintpipe(udev, es1->svc_endpoint), - es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, - hd, svc_interval); - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; - - /* Allocate buffers for our cport in messages and start them up */ - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb; - u8 *buffer; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - goto error; - buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); - if (!buffer) - goto error; - - usb_fill_bulk_urb(urb, udev, - usb_rcvbulkpipe(udev, es1->cport_in_endpoint), - buffer, ES1_GBUF_MSG_SIZE_MAX, - cport_in_callback, hd); - es1->cport_in_urb[i] = urb; - es1->cport_in_buffer[i] = buffer; - retval = usb_submit_urb(urb, GFP_KERNEL); - if (retval) - goto error; - } - - /* Allocate urbs for our CPort OUT messages */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - goto error; - - es1->cport_out_urb[i] = urb; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ - } - - return 0; -error: - ap_disconnect(interface); - - return retval; -} - -static struct usb_driver es1_ap_driver = { - .name = "es1_ap_driver", - .probe = ap_probe, - .disconnect = ap_disconnect, - .id_table = id_table, -}; - -module_usb_driver(es1_ap_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/gb-vibrator.c b/drivers/staging/greybus/gb-vibrator.c deleted file mode 100644 index b5332df7039c..000000000000 --- a/drivers/staging/greybus/gb-vibrator.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Greybus Vibrator protocol driver. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include -#include -#include "greybus.h" - -struct gb_vibrator_device { - struct gb_connection *connection; - struct device *dev; - int minor; /* vibrator minor number */ - u8 version_major; - u8 version_minor; -}; - -/* Version of the Greybus vibrator protocol we support */ -#define GB_VIBRATOR_VERSION_MAJOR 0x00 -#define GB_VIBRATOR_VERSION_MINOR 0x01 - -/* Greybus Vibrator request types */ -#define GB_VIBRATOR_TYPE_INVALID 0x00 -#define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01 -#define GB_VIBRATOR_TYPE_ON 0x02 -#define GB_VIBRATOR_TYPE_OFF 0x03 -#define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -struct gb_vibrator_proto_version_response { - __u8 major; - __u8 minor; -}; - -struct gb_vibrator_on_request { - __le16 timeout_ms; -}; - -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_vibrator_device *vib) -{ - struct gb_connection *connection = vib->connection; - struct gb_vibrator_proto_version_response version_response; - int retval; - - retval = gb_operation_sync(connection, - GB_VIBRATOR_TYPE_PROTOCOL_VERSION, - NULL, 0, - &version_response, sizeof(version_response)); - if (retval) - return retval; - - if (version_response.major > GB_VIBRATOR_VERSION_MAJOR) { - dev_err(&connection->dev, - "unsupported major version (%hhu > %hhu)\n", - version_response.major, GB_VIBRATOR_VERSION_MAJOR); - return -ENOTSUPP; - } - - vib->version_major = version_response.major; - vib->version_minor = version_response.minor; - return 0; -} - -static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) -{ - struct gb_vibrator_on_request request; - - request.timeout_ms = cpu_to_le16(timeout_ms); - return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, - &request, sizeof(request), NULL, 0); -} - -static int turn_off(struct gb_vibrator_device *vib) -{ - return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, - NULL, 0, NULL, 0); -} - -static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct gb_vibrator_device *vib = dev_get_drvdata(dev); - unsigned long val; - int retval; - - retval = kstrtoul(buf, 10, &val); - if (retval < 0) { - dev_err(dev, "could not parse timeout value %d\n", retval); - return retval; - } - - if (val) - retval = turn_on(vib, (u16)val); - else - retval = turn_off(vib); - if (retval) - return retval; - - return count; -} -static DEVICE_ATTR_WO(timeout); - -static struct attribute *vibrator_attrs[] = { - &dev_attr_timeout.attr, - NULL, -}; -ATTRIBUTE_GROUPS(vibrator); - -static struct class vibrator_class = { - .name = "vibrator", - .owner = THIS_MODULE, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) - .dev_groups = vibrator_groups, -#endif -}; - -static DEFINE_IDR(minors); - -static int gb_vibrator_connection_init(struct gb_connection *connection) -{ - struct gb_vibrator_device *vib; - struct device *dev; - int retval; - - vib = kzalloc(sizeof(*vib), GFP_KERNEL); - if (!vib) - return -ENOMEM; - - vib->connection = connection; - connection->private = vib; - - retval = get_version(vib); - if (retval) - goto error; - - /* - * For now we create a device in sysfs for the vibrator, but odds are - * there is a "real" device somewhere in the kernel for this, but I - * can't find it at the moment... - */ - vib->minor = idr_alloc(&minors, vib, 0, 0, GFP_KERNEL); - if (vib->minor < 0) { - retval = vib->minor; - goto error; - } - dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib, - "vibrator%d", vib->minor); - if (IS_ERR(dev)) { - retval = -EINVAL; - goto error; - } - vib->dev = dev; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) - /* - * Newer kernels handle this in a race-free manner, by the dev_groups - * field in the struct class up above. But for older kernels, we need - * to "open code this :( - */ - retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); - if (retval) { - device_unregister(dev); - goto error; - } -#endif - - return 0; - -error: - kfree(vib); - return retval; -} - -static void gb_vibrator_connection_exit(struct gb_connection *connection) -{ - struct gb_vibrator_device *vib = connection->private; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) - sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); -#endif - idr_remove(&minors, vib->minor); - device_unregister(vib->dev); - kfree(vib); -} - -static struct gb_protocol vibrator_protocol = { - .name = "vibrator", - .id = GREYBUS_PROTOCOL_VIBRATOR, - .major = 0, - .minor = 1, - .connection_init = gb_vibrator_connection_init, - .connection_exit = gb_vibrator_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -static __init int protocol_init(void) -{ - int retval; - - retval = class_register(&vibrator_class); - if (retval) - return retval; - - return gb_protocol_register(&vibrator_protocol); -} - -static __exit void protocol_exit(void) -{ - gb_protocol_deregister(&vibrator_protocol); - class_unregister(&vibrator_class); -} - -module_init(protocol_init); -module_exit(protocol_exit); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c new file mode 100644 index 000000000000..b5332df7039c --- /dev/null +++ b/drivers/staging/greybus/vibrator.c @@ -0,0 +1,227 @@ +/* + * Greybus Vibrator protocol driver. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include +#include +#include "greybus.h" + +struct gb_vibrator_device { + struct gb_connection *connection; + struct device *dev; + int minor; /* vibrator minor number */ + u8 version_major; + u8 version_minor; +}; + +/* Version of the Greybus vibrator protocol we support */ +#define GB_VIBRATOR_VERSION_MAJOR 0x00 +#define GB_VIBRATOR_VERSION_MINOR 0x01 + +/* Greybus Vibrator request types */ +#define GB_VIBRATOR_TYPE_INVALID 0x00 +#define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01 +#define GB_VIBRATOR_TYPE_ON 0x02 +#define GB_VIBRATOR_TYPE_OFF 0x03 +#define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +struct gb_vibrator_proto_version_response { + __u8 major; + __u8 minor; +}; + +struct gb_vibrator_on_request { + __le16 timeout_ms; +}; + +/* + * This request only uses the connection field, and if successful, + * fills in the major and minor protocol version of the target. + */ +static int get_version(struct gb_vibrator_device *vib) +{ + struct gb_connection *connection = vib->connection; + struct gb_vibrator_proto_version_response version_response; + int retval; + + retval = gb_operation_sync(connection, + GB_VIBRATOR_TYPE_PROTOCOL_VERSION, + NULL, 0, + &version_response, sizeof(version_response)); + if (retval) + return retval; + + if (version_response.major > GB_VIBRATOR_VERSION_MAJOR) { + dev_err(&connection->dev, + "unsupported major version (%hhu > %hhu)\n", + version_response.major, GB_VIBRATOR_VERSION_MAJOR); + return -ENOTSUPP; + } + + vib->version_major = version_response.major; + vib->version_minor = version_response.minor; + return 0; +} + +static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) +{ + struct gb_vibrator_on_request request; + + request.timeout_ms = cpu_to_le16(timeout_ms); + return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, + &request, sizeof(request), NULL, 0); +} + +static int turn_off(struct gb_vibrator_device *vib) +{ + return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, + NULL, 0, NULL, 0); +} + +static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gb_vibrator_device *vib = dev_get_drvdata(dev); + unsigned long val; + int retval; + + retval = kstrtoul(buf, 10, &val); + if (retval < 0) { + dev_err(dev, "could not parse timeout value %d\n", retval); + return retval; + } + + if (val) + retval = turn_on(vib, (u16)val); + else + retval = turn_off(vib); + if (retval) + return retval; + + return count; +} +static DEVICE_ATTR_WO(timeout); + +static struct attribute *vibrator_attrs[] = { + &dev_attr_timeout.attr, + NULL, +}; +ATTRIBUTE_GROUPS(vibrator); + +static struct class vibrator_class = { + .name = "vibrator", + .owner = THIS_MODULE, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + .dev_groups = vibrator_groups, +#endif +}; + +static DEFINE_IDR(minors); + +static int gb_vibrator_connection_init(struct gb_connection *connection) +{ + struct gb_vibrator_device *vib; + struct device *dev; + int retval; + + vib = kzalloc(sizeof(*vib), GFP_KERNEL); + if (!vib) + return -ENOMEM; + + vib->connection = connection; + connection->private = vib; + + retval = get_version(vib); + if (retval) + goto error; + + /* + * For now we create a device in sysfs for the vibrator, but odds are + * there is a "real" device somewhere in the kernel for this, but I + * can't find it at the moment... + */ + vib->minor = idr_alloc(&minors, vib, 0, 0, GFP_KERNEL); + if (vib->minor < 0) { + retval = vib->minor; + goto error; + } + dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib, + "vibrator%d", vib->minor); + if (IS_ERR(dev)) { + retval = -EINVAL; + goto error; + } + vib->dev = dev; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) + /* + * Newer kernels handle this in a race-free manner, by the dev_groups + * field in the struct class up above. But for older kernels, we need + * to "open code this :( + */ + retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); + if (retval) { + device_unregister(dev); + goto error; + } +#endif + + return 0; + +error: + kfree(vib); + return retval; +} + +static void gb_vibrator_connection_exit(struct gb_connection *connection) +{ + struct gb_vibrator_device *vib = connection->private; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) + sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); +#endif + idr_remove(&minors, vib->minor); + device_unregister(vib->dev); + kfree(vib); +} + +static struct gb_protocol vibrator_protocol = { + .name = "vibrator", + .id = GREYBUS_PROTOCOL_VIBRATOR, + .major = 0, + .minor = 1, + .connection_init = gb_vibrator_connection_init, + .connection_exit = gb_vibrator_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +static __init int protocol_init(void) +{ + int retval; + + retval = class_register(&vibrator_class); + if (retval) + return retval; + + return gb_protocol_register(&vibrator_protocol); +} + +static __exit void protocol_exit(void) +{ + gb_protocol_deregister(&vibrator_protocol); + class_unregister(&vibrator_class); +} + +module_init(protocol_init); +module_exit(protocol_exit); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 530430b717f02843fe1f2e77e6f52a41e05d6c3a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Jan 2015 18:12:35 +0530 Subject: greybus: uart: s/REQ/TYPE Request type for all other protocols is defined like: GB__TYPE_, but for UART is like: GB__REQ_. Replace REQ with TYPE to make it consistent. It will also be useful in a later patch that creates get_version() routines with the help of a macro. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 032062019d20..543c88992b0d 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -38,14 +38,14 @@ #define GB_UART_VERSION_MINOR 0x01 /* Greybus UART request types */ -#define GB_UART_REQ_INVALID 0x00 -#define GB_UART_REQ_PROTOCOL_VERSION 0x01 -#define GB_UART_REQ_SEND_DATA 0x02 -#define GB_UART_REQ_RECEIVE_DATA 0x03 /* Unsolicited data */ -#define GB_UART_REQ_SET_LINE_CODING 0x04 -#define GB_UART_REQ_SET_CONTROL_LINE_STATE 0x05 -#define GB_UART_REQ_SET_BREAK 0x06 -#define GB_UART_REQ_SERIAL_STATE 0x07 /* Unsolicited data */ +#define GB_UART_TYPE_INVALID 0x00 +#define GB_UART_TYPE_PROTOCOL_VERSION 0x01 +#define GB_UART_TYPE_SEND_DATA 0x02 +#define GB_UART_TYPE_RECEIVE_DATA 0x03 /* Unsolicited data */ +#define GB_UART_TYPE_SET_LINE_CODING 0x04 +#define GB_UART_TYPE_SET_CONTROL_LINE_STATE 0x05 +#define GB_UART_TYPE_SET_BREAK 0x06 +#define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ #define GB_UART_TYPE_RESPONSE 0x80 /* OR'd with rest */ struct gb_uart_proto_version_response { @@ -141,7 +141,7 @@ static int get_version(struct gb_tty *tty) int ret; ret = gb_operation_sync(tty->connection, - GB_UART_REQ_PROTOCOL_VERSION, + GB_UART_TYPE_PROTOCOL_VERSION, NULL, 0, &response, sizeof(response)); if (ret) return ret; @@ -173,7 +173,7 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) request->size = cpu_to_le16(size); memcpy(&request->data[0], data, size); - retval = gb_operation_sync(tty->connection, GB_UART_REQ_SEND_DATA, + retval = gb_operation_sync(tty->connection, GB_UART_TYPE_SEND_DATA, request, sizeof(*request) + size, NULL, 0); kfree(request); @@ -186,7 +186,7 @@ static int send_line_coding(struct gb_tty *tty) memcpy(&request.line_coding, &tty->line_coding, sizeof(tty->line_coding)); - return gb_operation_sync(tty->connection, GB_UART_REQ_SET_LINE_CODING, + return gb_operation_sync(tty->connection, GB_UART_TYPE_SET_LINE_CODING, &request, sizeof(request), NULL, 0); } @@ -196,7 +196,7 @@ static int send_control(struct gb_tty *tty, u16 control) request.control = cpu_to_le16(control); return gb_operation_sync(tty->connection, - GB_UART_REQ_SET_CONTROL_LINE_STATE, + GB_UART_TYPE_SET_CONTROL_LINE_STATE, &request, sizeof(request), NULL, 0); } @@ -211,7 +211,7 @@ static int send_break(struct gb_tty *tty, u8 state) } request.state = state; - return gb_operation_sync(tty->connection, GB_UART_REQ_SET_BREAK, + return gb_operation_sync(tty->connection, GB_UART_TYPE_SET_BREAK, &request, sizeof(request), NULL, 0); } -- cgit v1.2.3-59-g8ed1b From 36e79dec96f652110ae2b06bfcf9e67e1b770787 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Jan 2015 18:12:36 +0530 Subject: greybus: create get_version() routines with the help of a macro This gets rid of lots of duplication of code. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery.c | 28 ++-------------------------- drivers/staging/greybus/gpio.c | 27 +++------------------------ drivers/staging/greybus/i2c.c | 28 +++------------------------- drivers/staging/greybus/protocol.c | 26 ++++++++++++++++++++++++++ drivers/staging/greybus/protocol.h | 34 ++++++++++++++++++++++++++++++++++ drivers/staging/greybus/pwm.c | 28 +++------------------------- drivers/staging/greybus/uart.c | 29 ++--------------------------- drivers/staging/greybus/usb.c | 22 ++-------------------- drivers/staging/greybus/vibrator.c | 30 ++---------------------------- 9 files changed, 77 insertions(+), 175 deletions(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index 2130d73cbfde..c14f44b4e86a 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -88,32 +88,8 @@ struct gb_battery_voltage_response { __le32 voltage; }; -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_battery *gb) -{ - struct gb_battery_proto_version_response version_response; - int retval; - - retval = gb_operation_sync(gb->connection, - GB_BATTERY_TYPE_PROTOCOL_VERSION, - NULL, 0, - &version_response, sizeof(version_response)); - if (retval) - return retval; - - if (version_response.major > GB_BATTERY_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - version_response.major, GB_BATTERY_VERSION_MAJOR); - return -ENOTSUPP; - } - - gb->version_major = version_response.major; - gb->version_minor = version_response.minor; - return 0; -} +/* Define get_version() routine */ +define_get_version(gb_battery, BATTERY); static int get_tech(struct gb_battery *gb) { diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 4997588e2617..a93583341811 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -113,29 +113,8 @@ struct gb_gpio_set_debounce_request { /* debounce response has no payload */ -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int gb_gpio_proto_version_operation(struct gb_gpio_controller *ggc) -{ - struct gb_gpio_proto_version_response response; - int ret; - - ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_GPIO_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_GPIO_VERSION_MAJOR); - return -ENOTSUPP; - } - ggc->version_major = response.major; - ggc->version_minor = response.minor; - return 0; -} +/* Define get_version() routine */ +define_get_version(gb_gpio_controller, GPIO); static int gb_gpio_line_count_operation(struct gb_gpio_controller *ggc) { @@ -446,7 +425,7 @@ static int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controlle int ret; /* First thing we need to do is check the version */ - ret = gb_gpio_proto_version_operation(gb_gpio_controller); + ret = get_version(gb_gpio_controller); if (ret) ; /* return ret; */ diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index c967ae3161c5..589691d05750 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -87,30 +87,8 @@ struct gb_i2c_transfer_response { __u8 data[0]; /* inbound data */ }; -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev) -{ - struct gb_i2c_proto_version_response response; - int ret; - - ret = gb_operation_sync(gb_i2c_dev->connection, - GB_I2C_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_I2C_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_I2C_VERSION_MAJOR); - return -ENOTSUPP; - } - gb_i2c_dev->version_major = response.major; - gb_i2c_dev->version_minor = response.minor; - return 0; -} +/* Define get_version() routine */ +define_get_version(gb_i2c_device, I2C); /* * Map Greybus i2c functionality bits into Linux ones @@ -361,7 +339,7 @@ static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) int ret; /* First thing we need to do is check the version */ - ret = gb_i2c_proto_version_operation(gb_i2c_dev); + ret = get_version(gb_i2c_dev); if (ret) return ret; diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 12bbc2f2191f..ae8ab21ee920 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -148,6 +148,32 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) return protocol; } +int gb_protocol_get_version(struct gb_connection *connection, int type, + void *request, int request_size, + struct gb_protocol_version_response *response, + __u8 major) +{ + int retval; + + retval = gb_operation_sync(connection, type, request, request_size, + response, sizeof(*response)); + if (retval) + return retval; + + if (response->major > major) { + dev_err(&connection->dev, + "unsupported major version (%hhu > %hhu)\n", + response->major, major); + return -ENOTSUPP; + } + + dev_dbg(&connection->dev, "version_major = %u version_minor = %u\n", + response->major, response->minor); + + return 0; +} +EXPORT_SYMBOL_GPL(gb_protocol_get_version); + void gb_protocol_put(struct gb_protocol *protocol) { u8 major; diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 8bda524a4d6d..e65cc18f4dfa 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -14,6 +14,12 @@ struct gb_operation; +/* version request has no payload */ +struct gb_protocol_version_response { + __u8 major; + __u8 minor; +}; + typedef int (*gb_connection_init_t)(struct gb_connection *); typedef void (*gb_connection_exit_t)(struct gb_connection *); typedef void (*gb_request_recv_t)(u8, struct gb_operation *); @@ -45,6 +51,11 @@ int gb_protocol_deregister(struct gb_protocol *protocol); __gb_protocol_register(protocol, THIS_MODULE) struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); +int gb_protocol_get_version(struct gb_connection *connection, int type, + void *request, int request_size, + struct gb_protocol_version_response *response, + __u8 major); + void gb_protocol_put(struct gb_protocol *protocol); /* @@ -82,4 +93,27 @@ static void __exit protocol_exit(void) \ } \ module_exit(protocol_exit); +/* + * Macro to create get_version() routine for protocols + * @__device: name of the device struct + * @__protocol: name of protocol in CAPITALS + */ +#define define_get_version(__device, __protocol) \ +static int get_version(struct __device *dev) \ +{ \ + struct gb_protocol_version_response response; \ + int retval; \ + \ + retval = gb_protocol_get_version(dev->connection, \ + GB_##__protocol##_TYPE_PROTOCOL_VERSION,\ + NULL, 0, &response, \ + GB_##__protocol##_VERSION_MAJOR); \ + if (retval) \ + return retval; \ + \ + dev->version_major = response.major; \ + dev->version_minor = response.minor; \ + return 0; \ +} + #endif /* __PROTOCOL_H */ diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 91f7b87a1cae..7b7252358575 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -79,30 +79,8 @@ struct gb_pwm_disable_request { __u8 which; }; -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int gb_pwm_proto_version_operation(struct gb_pwm_chip *pwmc) -{ - struct gb_pwm_proto_version_response response; - int ret; - - ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - - if (ret) - return ret; - - if (response.major > GB_PWM_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_PWM_VERSION_MAJOR); - return -ENOTSUPP; - } - pwmc->version_major = response.major; - pwmc->version_minor = response.minor; - return 0; -} +/* Define get_version() routine */ +define_get_version(gb_pwm_chip, PWM); static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) { @@ -269,7 +247,7 @@ static int gb_pwm_connection_init(struct gb_connection *connection) connection->private = pwmc; /* Check for compatible protocol version */ - ret = gb_pwm_proto_version_operation(pwmc); + ret = get_version(pwmc); if (ret) goto out_err; diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 543c88992b0d..d0669ae2d4c1 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -131,33 +131,8 @@ static DEFINE_IDR(tty_minors); static DEFINE_MUTEX(table_lock); static atomic_t reference_count = ATOMIC_INIT(0); -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_tty *tty) -{ - struct gb_uart_proto_version_response response; - int ret; - - ret = gb_operation_sync(tty->connection, - GB_UART_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_UART_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_UART_VERSION_MAJOR); - return -ENOTSUPP; - } - tty->version_major = response.major; - tty->version_minor = response.minor; - - pr_debug("%s: version_major = %u version_minor = %u\n", - __func__, tty->version_major, tty->version_minor); - return 0; -} +/* Define get_version() routine */ +define_get_version(gb_tty, UART); static int send_data(struct gb_tty *tty, u16 size, const u8 *data) { diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 010ef9ee831f..c66d7681d4b3 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -93,26 +93,8 @@ struct gb_usb_device { #define to_gb_usb_device(d) ((struct gb_usb_device*) d->hcd_priv) -static int get_version(struct gb_usb_device *dev) -{ - struct gb_usb_proto_version_response response; - int ret; - - ret = gb_operation_sync(dev->connection, - GB_USB_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - - if (response.major > GB_USB_VERSION_MAJOR) { - pr_err("unsupported major version (%hhu > %hhu)\n", - response.major, GB_USB_VERSION_MAJOR); - return -ENOTSUPP; - } - dev->version_major = response.major; - dev->version_minor = response.minor; - return 0; -} +/* Define get_version() routine */ +define_get_version(gb_usb_device, USB); static void hcd_stop(struct usb_hcd *hcd) { diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index b5332df7039c..141ccdbf7b64 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -43,34 +43,8 @@ struct gb_vibrator_on_request { __le16 timeout_ms; }; -/* - * This request only uses the connection field, and if successful, - * fills in the major and minor protocol version of the target. - */ -static int get_version(struct gb_vibrator_device *vib) -{ - struct gb_connection *connection = vib->connection; - struct gb_vibrator_proto_version_response version_response; - int retval; - - retval = gb_operation_sync(connection, - GB_VIBRATOR_TYPE_PROTOCOL_VERSION, - NULL, 0, - &version_response, sizeof(version_response)); - if (retval) - return retval; - - if (version_response.major > GB_VIBRATOR_VERSION_MAJOR) { - dev_err(&connection->dev, - "unsupported major version (%hhu > %hhu)\n", - version_response.major, GB_VIBRATOR_VERSION_MAJOR); - return -ENOTSUPP; - } - - vib->version_major = version_response.major; - vib->version_minor = version_response.minor; - return 0; -} +/* Define get_version() routine */ +define_get_version(gb_vibrator_device, VIBRATOR); static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) { -- cgit v1.2.3-59-g8ed1b From 89210f64bae6bd6bba90d9e08d1b88b4ba103f59 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Jan 2015 18:12:37 +0530 Subject: greybus: remove unused version-response structs These aren't used anymore and so can be removed. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery.c | 5 ----- drivers/staging/greybus/gpio.c | 6 ------ drivers/staging/greybus/i2c.c | 6 ------ drivers/staging/greybus/pwm.c | 6 ------ drivers/staging/greybus/uart.c | 5 ----- drivers/staging/greybus/usb.c | 5 ----- drivers/staging/greybus/vibrator.c | 5 ----- 7 files changed, 38 deletions(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index c14f44b4e86a..b968d149741e 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -43,11 +43,6 @@ struct gb_battery { #define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX #define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX -struct gb_battery_proto_version_response { - __u8 major; - __u8 minor; -}; - /* Should match up with battery types in linux/power_supply.h */ #define GB_BATTERY_TECH_UNKNOWN 0x0000 #define GB_BATTERY_TECH_NiMH 0x0001 diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index a93583341811..85d89b86d5f7 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -54,12 +54,6 @@ struct gb_gpio_controller { #define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ -/* version request has no payload */ -struct gb_gpio_proto_version_response { - __u8 major; - __u8 minor; -}; - /* line count request has no payload */ struct gb_gpio_line_count_response { __u8 count; diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 589691d05750..ca8891413c0e 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -42,12 +42,6 @@ struct gb_i2c_device { #define GB_I2C_RETRIES_DEFAULT 3 #define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ -/* version request has no payload */ -struct gb_i2c_proto_version_response { - __u8 major; - __u8 minor; -}; - /* functionality request has no payload */ struct gb_i2c_functionality_response { __le32 functionality; diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 7b7252358575..e2ab6f5f610e 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -41,12 +41,6 @@ struct gb_pwm_chip { #define GB_PWM_TYPE_DISABLE 0x08 #define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ -/* version request has no payload */ -struct gb_pwm_proto_version_response { - __u8 major; - __u8 minor; -}; - /* pwm count request has no payload */ struct gb_pwm_count_response { __u8 count; diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index d0669ae2d4c1..575ca568a4f1 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -48,11 +48,6 @@ #define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ #define GB_UART_TYPE_RESPONSE 0x80 /* OR'd with rest */ -struct gb_uart_proto_version_response { - __u8 major; - __u8 minor; -}; - struct gb_uart_send_data_request { __le16 size; __u8 data[0]; diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index c66d7681d4b3..ff4556fb7dd5 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -31,11 +31,6 @@ #define GB_USB_TYPE_GET_FRAME_NUMBER 0x08 #define GB_USB_TYPE_HUB_STATUS_DATA 0x09 -struct gb_usb_proto_version_response { - __u8 major; - __u8 minor; -}; - struct gb_usb_urb_enqueue_request { __le32 pipe; __le32 transfer_flags; diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 141ccdbf7b64..b6ec9f2373bb 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -34,11 +34,6 @@ struct gb_vibrator_device { #define GB_VIBRATOR_TYPE_OFF 0x03 #define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */ -struct gb_vibrator_proto_version_response { - __u8 major; - __u8 minor; -}; - struct gb_vibrator_on_request { __le16 timeout_ms; }; -- cgit v1.2.3-59-g8ed1b From c2f792382bc23bddd67a5da72faf9d46bf0f3ef9 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Thu, 22 Jan 2015 15:23:37 +0800 Subject: greybus: protocol.c: fix a kernel panic caused by __gb_protocol_register __gb_protocol_register check if the protocol is not already registred, and then register it. It register in existing->lists but at this point, existing is always NULL (we exist just before if not). Use gb_protocols instead. Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index ae8ab21ee920..400f73386474 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -70,7 +70,7 @@ int __gb_protocol_register(struct gb_protocol *protocol, struct module *module) * We need to insert the protocol here, before the existing one * (or before the head if we searched the whole list) */ - list_add_tail(&protocol->links, &existing->links); + list_add_tail(&protocol->links, &gb_protocols); spin_unlock_irq(&gb_protocols_lock); pr_info("Registered %s protocol.\n", protocol->name); -- cgit v1.2.3-59-g8ed1b From f281f2dec14231b5da8ca44100b4e2887724e5fd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 22 Jan 2015 12:42:39 +0530 Subject: greybus: i2c: fix name conflict between function and struct: gb_i2c_transfer_response 'gb_i2c_transfer_response' is the name given to a function and a struct. Though we don't get any compilation errors/warnings about it, but the names should be unique. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index ca8891413c0e..31528afe9533 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -230,7 +230,7 @@ gb_i2c_transfer_request(struct gb_connection *connection, return operation; } -static void gb_i2c_transfer_response(struct i2c_msg *msgs, u32 msg_count, +static void gb_i2c_decode_response(struct i2c_msg *msgs, u32 msg_count, struct gb_i2c_transfer_response *response) { struct i2c_msg *msg = msgs; @@ -273,7 +273,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, struct gb_i2c_transfer_response *response; response = operation->response->payload; - gb_i2c_transfer_response(msgs, msg_count, response); + gb_i2c_decode_response(msgs, msg_count, response); ret = msg_count; } else if (!gb_i2c_expected_transfer_error(ret)) { pr_err("transfer operation failed (%d)\n", ret); -- cgit v1.2.3-59-g8ed1b From 62aadeeafa1585d6acf3e9b378eb554f57f3bc84 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 22 Jan 2015 12:10:38 +0530 Subject: greybus: i2c: fix name conflict between function and struct: gb_i2c_transfer_request 'gb_i2c_transfer_request' is the name given to a function and a struct. Though we don't get any compilation errors/warnings about it, but the names should be unique. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 31528afe9533..0bcd7a9b0e12 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -163,8 +163,8 @@ gb_i2c_fill_transfer_op(struct gb_i2c_transfer_op *op, struct i2c_msg *msg) } static struct gb_operation * -gb_i2c_transfer_request(struct gb_connection *connection, - struct i2c_msg *msgs, u32 msg_count) +gb_i2c_operation_create(struct gb_connection *connection, + struct i2c_msg *msgs, u32 msg_count) { struct gb_i2c_transfer_request *request; struct gb_operation *operation; @@ -264,7 +264,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, struct gb_operation *operation; int ret; - operation = gb_i2c_transfer_request(connection, msgs, msg_count); + operation = gb_i2c_operation_create(connection, msgs, msg_count); if (!operation) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From c1a0a8fccf92d825a6c324c3e2363fefdb3f19da Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 23 Jan 2015 10:05:58 +0800 Subject: greybus: Revert "protocol.c: fix a kernel panic caused by __gb_protocol_register" This reverts commit 57131bf309d34568dd3b8f8e9da7a7ba25e9495e, it isn't going to be needed as the patch this fixes will be reverted. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 400f73386474..ae8ab21ee920 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -70,7 +70,7 @@ int __gb_protocol_register(struct gb_protocol *protocol, struct module *module) * We need to insert the protocol here, before the existing one * (or before the head if we searched the whole list) */ - list_add_tail(&protocol->links, &gb_protocols); + list_add_tail(&protocol->links, &existing->links); spin_unlock_irq(&gb_protocols_lock); pr_info("Registered %s protocol.\n", protocol->name); -- cgit v1.2.3-59-g8ed1b From 6869eb56ebe074bbd34de0fd5e63ad31cd578094 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 23 Jan 2015 10:06:24 +0800 Subject: greybus: Revert "protocol: dedup protocol find code" This reverts commit 241b5fefc54eae95239b0f7dc4e2b0db49457729 as it's wrong, we want to insert into the correct place in the list. Reported-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index ae8ab21ee920..51549e20f398 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -20,13 +20,6 @@ static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) { struct gb_protocol *protocol; - /* - * The protocols list is sorted first by protocol id (low to - * high), then by major version (high to low), and finally - * by minor version (high to low). Searching only by - * protocol id will produce the newest implemented version - * of the protocol. - */ list_for_each_entry(protocol, &gb_protocols, links) { if (protocol->id < id) continue; @@ -57,12 +50,34 @@ int __gb_protocol_register(struct gb_protocol *protocol, struct module *module) protocol->owner = module; + /* + * The protocols list is sorted first by protocol id (low to + * high), then by major version (high to low), and finally + * by minor version (high to low). Searching only by + * protocol id will produce the newest implemented version + * of the protocol. + */ spin_lock_irq(&gb_protocols_lock); - /* check if the protocol already wos registered */ - existing = _gb_protocol_find(id, major, minor); - if (existing) { + list_for_each_entry(existing, &gb_protocols, links) { + if (existing->id < id) + continue; + if (existing->id > id) + break; + + if (existing->major > major) + continue; + if (existing->major < major) + break; + + if (existing->minor > minor) + continue; + if (existing->minor < minor) + break; + + /* A matching protocol has already been registered */ spin_unlock_irq(&gb_protocols_lock); + return -EEXIST; } -- cgit v1.2.3-59-g8ed1b From 65760032f5887351537fb3d3e095392a8874aa88 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Thu, 22 Jan 2015 16:22:20 -0800 Subject: greybus: build: android: replace hard-coded build destination with variable Make sure destination for greybus modules is consistent and easier to maintain Signed-off-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Android.mk | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/Android.mk b/drivers/staging/greybus/Android.mk index 924099b42c06..5d96a24c6838 100644 --- a/drivers/staging/greybus/Android.mk +++ b/drivers/staging/greybus/Android.mk @@ -2,13 +2,15 @@ $(PRODUCT_OUT)/ramdisk.img: build-greybus +GREYBUS_MODULE_OUT_PATH := $(PRODUCT_OUT)/root/lib/modules + include $(CLEAR_VARS) GREYBUS_SRC_PATH := $(ANDROID_BUILD_TOP)/external/greybus/ LOCAL_PATH := $(GREYBUS_SRC_PATH) LOCAL_SRC_FILES := greybus.ko LOCAL_MODULE := $(LOCAL_SRC_FILES) LOCAL_MODULE_CLASS := EXECUTABLES -LOCAL_MODULE_PATH := $(PRODUCT_OUT)/root/lib/modules +LOCAL_MODULE_PATH := $(GREYBUS_MODULE_OUT_PATH) $(LOCAL_PATH)/$(LOCAL_SRC_FILES): build-greybus include $(BUILD_PREBUILT) @@ -26,5 +28,5 @@ build-greybus: android_kernel make clean -C $(GREYBUS_SRC_PATH) cd $(GREYBUS_SRC_PATH) &&\ $(MAKE) -j$(MAKE_JOBS) CROSS_COMPILE=$(KERNEL_TOOLS_PREFIX) $(ARGS) - mkdir -p $(PRODUCT_OUT)/root/lib/modules - cp $(GREYBUS_SRC_PATH)/greybus.ko $(PRODUCT_OUT)/root/lib/modules + mkdir -p $(GREYBUS_MODULE_OUT_PATH) + cp $(GREYBUS_SRC_PATH)/greybus.ko $(GREYBUS_MODULE_OUT_PATH) -- cgit v1.2.3-59-g8ed1b From 58b978c48bcde959c81f0501be1fdee4b2823687 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Thu, 22 Jan 2015 16:22:47 -0800 Subject: greybus: build: android: add all greybus modules to the build We now have several modules generated by the greybus build. Let's add any *.ko files we find to the buid. Signed-off-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Android.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Android.mk b/drivers/staging/greybus/Android.mk index 5d96a24c6838..6f13d35f4cf7 100644 --- a/drivers/staging/greybus/Android.mk +++ b/drivers/staging/greybus/Android.mk @@ -29,4 +29,6 @@ build-greybus: android_kernel cd $(GREYBUS_SRC_PATH) &&\ $(MAKE) -j$(MAKE_JOBS) CROSS_COMPILE=$(KERNEL_TOOLS_PREFIX) $(ARGS) mkdir -p $(GREYBUS_MODULE_OUT_PATH) - cp $(GREYBUS_SRC_PATH)/greybus.ko $(GREYBUS_MODULE_OUT_PATH) + ko=`find $$GREYBUS_SRC_PATH -type f -name *.ko`;\ + for i in $$ko; do $(KERNEL_TOOLCHAIN_PATH)strip --strip-unneeded $$i;\ + mv $$i $(GREYBUS_MODULE_OUT_PATH)/; done; -- cgit v1.2.3-59-g8ed1b From e0b179ee782d80b31ccfe8cbcb1505cb703dfdbc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 Jan 2015 20:33:41 +0800 Subject: greybus: ap.c: our workqueue should be ordered SVC messages come in in an "order", so don't mess them up by processing them out of order. Fix this by making our work queue ordered, which should keep everything in line. Reported-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index e69566b119cf..3e4d4fbfd7fc 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -356,7 +356,7 @@ EXPORT_SYMBOL_GPL(greybus_svc_in); int gb_ap_init(void) { - ap_workqueue = alloc_workqueue("greybus_ap", 0, 1); + ap_workqueue = alloc_ordered_workqueue("greybus_ap", 0); if (!ap_workqueue) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 45a706368d9b162dde2455c305158131af37131d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 Jan 2015 21:19:25 +0800 Subject: greybus: es1.c: wait until the last possible minute to start the svc messages When initializing the USB device, we were starting up the svc message queue before the cport urbs were allocated. This might not be an issue for "slower" machines, but not having any allocated urbs for a cport might be an issue if we were to handle svc messages. So wait until everything is properly initialized and allocated before starting the svc urb. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 2ec5d7b403a3..bbf1bd1048be 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -559,9 +559,6 @@ static int ap_probe(struct usb_interface *interface, usb_rcvintpipe(udev, es1->svc_endpoint), es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, hd, svc_interval); - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; /* Allocate buffers for our cport in messages and start them up */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { @@ -598,6 +595,11 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } + /* Start up our svc urb, which allows events to start flowing */ + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + return 0; error: ap_disconnect(interface); -- cgit v1.2.3-59-g8ed1b From 15d651b0dba16a81285686bf52f4d5b1656362d8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 23 Jan 2015 13:07:45 +0530 Subject: greybus: spi: add bridged-PHY spi protocol driver Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/gpb.c | 7 + drivers/staging/greybus/protocol.h | 3 + drivers/staging/greybus/spi.c | 443 +++++++++++++++++++++++++++++++++++++ 4 files changed, 454 insertions(+) create mode 100644 drivers/staging/greybus/spi.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 79af8128aac9..a22ad690d3a9 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -15,6 +15,7 @@ gb-phy-y := gpb.o \ pwm.o \ gpio.o \ i2c.o \ + spi.o \ usb.o # Prefix all modules with gb- diff --git a/drivers/staging/greybus/gpb.c b/drivers/staging/greybus/gpb.c index 0776df65e4b6..5f080d40ced4 100644 --- a/drivers/staging/greybus/gpb.c +++ b/drivers/staging/greybus/gpb.c @@ -45,8 +45,14 @@ static int __init gpbridge_init(void) pr_err("error initializing usb protocol\n"); goto error_i2c; } + if (gb_spi_protocol_init()) { + pr_err("error initializing usb protocol\n"); + goto error_spi; + } return 0; +error_spi: + gb_i2c_protocol_exit(); error_i2c: gb_usb_protocol_exit(); error_usb: @@ -63,6 +69,7 @@ error_gpio: static void __exit gpbridge_exit(void) { + gb_spi_protocol_exit(); gb_i2c_protocol_exit(); gb_usb_protocol_exit(); gb_sdio_protocol_exit(); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index e65cc18f4dfa..2d0100008138 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -81,6 +81,9 @@ extern void gb_usb_protocol_exit(void); extern int gb_i2c_protocol_init(void); extern void gb_i2c_protocol_exit(void); +extern int gb_spi_protocol_init(void); +extern void gb_spi_protocol_exit(void); + #define gb_protocol_driver(__protocol) \ static int __init protocol_init(void) \ { \ diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c new file mode 100644 index 000000000000..11859047ac31 --- /dev/null +++ b/drivers/staging/greybus/spi.c @@ -0,0 +1,443 @@ +/* + * SPI bridge driver for the Greybus "generic" SPI module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include + +#include "greybus.h" + +struct gb_spi { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + + /* Modes supported by spi controller */ + u16 mode; + /* constraints of the spi controller */ + u16 flags; + + /* + * copied from kernel: + * + * A mask indicating which values of bits_per_word are supported by the + * controller. Bit n indicates that a bits_per_word n+1 is suported. If + * set, the SPI core will reject any transfer with an unsupported + * bits_per_word. If not set, this value is simply ignored, and it's up + * to the individual driver to perform any validation. + */ + u32 bits_per_word_mask; + + /* + * chipselects will be integral to many controllers; some others might + * use board-specific GPIOs. + */ + u16 num_chipselect; +}; + +/* Version of the Greybus spi protocol we support */ +#define GB_SPI_VERSION_MAJOR 0x00 +#define GB_SPI_VERSION_MINOR 0x01 + +/* Should match up with modes in linux/spi/spi.h */ +#define GB_SPI_MODE_CPHA 0x01 /* clock phase */ +#define GB_SPI_MODE_CPOL 0x02 /* clock polarity */ +#define GB_SPI_MODE_MODE_0 (0|0) /* (original MicroWire) */ +#define GB_SPI_MODE_MODE_1 (0|GB_SPI_MODE_CPHA) +#define GB_SPI_MODE_MODE_2 (GB_SPI_MODE_CPOL|0) +#define GB_SPI_MODE_MODE_3 (GB_SPI_MODE_CPOL|GB_SPI_MODE_CPHA) +#define GB_SPI_MODE_CS_HIGH 0x04 /* chipselect active high? */ +#define GB_SPI_MODE_LSB_FIRST 0x08 /* per-word bits-on-wire */ +#define GB_SPI_MODE_3WIRE 0x10 /* SI/SO signals shared */ +#define GB_SPI_MODE_LOOP 0x20 /* loopback mode */ +#define GB_SPI_MODE_NO_CS 0x40 /* 1 dev/bus, no chipselect */ +#define GB_SPI_MODE_READY 0x80 /* slave pulls low to pause */ + +/* Should match up with flags in linux/spi/spi.h */ +#define GB_SPI_FLAG_HALF_DUPLEX BIT(0) /* can't do full duplex */ +#define GB_SPI_FLAG_NO_RX BIT(1) /* can't do buffer read */ +#define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */ + +/* Greybus spi request types */ +#define GB_SPI_TYPE_INVALID 0x00 +#define GB_SPI_TYPE_PROTOCOL_VERSION 0x01 +#define GB_SPI_TYPE_MODE 0x02 +#define GB_SPI_TYPE_FLAGS 0x03 +#define GB_SPI_TYPE_BITS_PER_WORD_MASK 0x04 +#define GB_SPI_TYPE_NUM_CHIPSELECT 0x05 +#define GB_SPI_TYPE_TRANSFER 0x06 +#define GB_SPI_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +/* mode request has no payload */ +struct gb_spi_mode_response { + __le16 mode; +}; + +/* flags request has no payload */ +struct gb_spi_flags_response { + __le16 flags; +}; + +/* bits-per-word request has no payload */ +struct gb_spi_bpw_response { + __le32 bits_per_word_mask; +}; + +/* num-chipselects request has no payload */ +struct gb_spi_chipselect_response { + __le16 num_chipselect; +}; + +/** + * struct gb_spi_transfer - a read/write buffer pair + * @speed_hz: Select a speed other than the device default for this transfer. If + * 0 the default (from @spi_device) is used. + * @len: size of rx and tx buffers (in bytes) + * @delay_usecs: microseconds to delay after this transfer before (optionally) + * changing the chipselect status, then starting the next transfer or + * completing this spi_message. + * @cs_change: affects chipselect after this transfer completes + * @bits_per_word: select a bits_per_word other than the device default for this + * transfer. If 0 the default (from @spi_device) is used. + */ +struct gb_spi_transfer { + __le32 speed_hz; + __le32 len; + __le16 delay_usecs; + __u8 cs_change; + __u8 bits_per_word; +}; + +struct gb_spi_transfer_request { + __u8 chip_select; /* of the spi device */ + __u8 mode; /* of the spi device */ + __le16 count; + struct gb_spi_transfer transfers[0]; /* trnasfer_count of these */ +}; + +struct gb_spi_transfer_response { + __u8 data[0]; /* inbound data */ +}; + +/* Routines to transfer data */ +static struct gb_operation * +gb_spi_operation_create(struct gb_connection *connection, + struct spi_message *msg, u32 *total_len) +{ + struct gb_spi_transfer_request *request; + struct spi_device *dev = msg->spi; + struct spi_transfer *xfer; + struct gb_spi_transfer *gb_xfer; + struct gb_operation *operation; + u32 tx_size = 0, rx_size = 0, count = 0, request_size; + void *tx_data; + + /* Find number of transfers queued and tx/rx length in the message */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (!xfer->tx_buf && !xfer->rx_buf) { + gb_connection_err(connection, + "Bufferless transfer, length %u\n", + xfer->len); + return NULL; + } + + if (xfer->tx_buf) + tx_size += xfer->len; + if (xfer->rx_buf) + rx_size += xfer->len; + + *total_len += xfer->len; + count++; + } + + /* Too many transfers ? */ + if (count > (u32)U16_MAX) { + gb_connection_err(connection, "transfer count (%u) too big", + count); + return NULL; + } + + /* + * In addition to space for all message descriptors we need + * to have enough to hold all tx data. + */ + request_size = sizeof(*request); + request_size += count * sizeof(*gb_xfer); + request_size += tx_size; + + /* Response consists only of incoming data */ + operation = gb_operation_create(connection, GB_SPI_TYPE_TRANSFER, + request_size, rx_size); + if (!operation) + return NULL; + + request = operation->request->payload; + request->count = count; + request->mode = dev->mode; + request->chip_select = dev->chip_select; + + gb_xfer = &request->transfers[0]; + tx_data = gb_xfer + count; /* place tx data after last gb_xfer */ + + /* Fill in the transfers array */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + gb_xfer->speed_hz = cpu_to_le16(xfer->speed_hz); + gb_xfer->len = cpu_to_le32(xfer->len); + gb_xfer->delay_usecs = cpu_to_le16(xfer->delay_usecs); + gb_xfer->cs_change = xfer->cs_change; + gb_xfer->bits_per_word = xfer->bits_per_word; + gb_xfer++; + + /* Copy tx data */ + if (xfer->tx_buf) { + memcpy(tx_data, xfer->tx_buf, xfer->len); + tx_data += xfer->len; + } + } + + return operation; +} + +static void gb_spi_decode_response(struct spi_message *msg, + struct gb_spi_transfer_response *response) +{ + struct spi_transfer *xfer; + void *rx_data = response->data; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + /* Copy rx data */ + if (xfer->rx_buf) { + memcpy(xfer->rx_buf, rx_data, xfer->len); + rx_data += xfer->len; + } + } +} + +static int gb_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct gb_spi *spi = spi_master_get_devdata(master); + struct gb_connection *connection = spi->connection; + struct gb_spi_transfer_response *response; + struct gb_operation *operation; + u32 len = 0; + int ret; + + operation = gb_spi_operation_create(connection, msg, &len); + if (!operation) + return -ENOMEM; + + ret = gb_operation_request_send_sync(operation); + if (!ret) { + response = operation->response->payload; + if (response) + gb_spi_decode_response(msg, response); + } else { + pr_err("transfer operation failed (%d)\n", ret); + } + gb_operation_destroy(operation); + + msg->actual_length = len; + msg->status = 0; + spi_finalize_current_message(master); + + return ret; +} + +static int gb_spi_setup(struct spi_device *spi) +{ + /* Nothing to do for now */ + return 0; +} + +static void gb_spi_cleanup(struct spi_device *spi) +{ + /* Nothing to do for now */ +} + + +/* Routines to get controller infomation */ + +/* Define get_version() routine */ +define_get_version(gb_spi, SPI); + +/* + * Map Greybus spi mode bits/flags/bpw into Linux ones. + * All bits are same for now and so these macro's return same values. + */ +#define gb_spi_mode_map(mode) mode +#define gb_spi_flags_map(flags) flags + +static int gb_spi_mode_operation(struct gb_spi *spi) +{ + struct gb_spi_mode_response response; + u16 mode; + int ret; + + ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_MODE, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + mode = le16_to_cpu(response.mode); + spi->mode = gb_spi_mode_map(mode); + + return 0; +} + +static int gb_spi_flags_operation(struct gb_spi *spi) +{ + struct gb_spi_flags_response response; + u16 flags; + int ret; + + ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_FLAGS, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + flags = le16_to_cpu(response.flags); + spi->flags = gb_spi_flags_map(flags); + + return 0; +} + +static int gb_spi_bpw_operation(struct gb_spi *spi) +{ + struct gb_spi_bpw_response response; + int ret; + + ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_BITS_PER_WORD_MASK, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + spi->bits_per_word_mask = le32_to_cpu(response.bits_per_word_mask); + + return 0; +} + +static int gb_spi_chipselect_operation(struct gb_spi *spi) +{ + struct gb_spi_chipselect_response response; + int ret; + + ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_NUM_CHIPSELECT, + NULL, 0, &response, sizeof(response)); + if (ret) + return ret; + + spi->num_chipselect = le32_to_cpu(response.num_chipselect); + + return 0; +} + +/* + * Initialize the spi device. This includes verifying we can support it (based + * on the protocol version it advertises). If that's OK, we get and cached its + * mode bits & flags. + */ +static int gb_spi_init(struct gb_spi *spi) +{ + int ret; + + /* First thing we need to do is check the version */ + ret = get_version(spi); + if (ret) + return ret; + + /* mode never changes, just get it once */ + ret = gb_spi_mode_operation(spi); + if (ret) + return ret; + + /* flags never changes, just get it once */ + ret = gb_spi_flags_operation(spi); + if (ret) + return ret; + + /* total number of chipselects never changes, just get it once */ + ret = gb_spi_chipselect_operation(spi); + if (ret) + return ret; + + /* bits-per-word-mask never changes, just get it once */ + return gb_spi_bpw_operation(spi); +} + +static int gb_spi_connection_init(struct gb_connection *connection) +{ + struct gb_spi *spi; + struct spi_master *master; + int ret; + + /* Allocate master with space for data */ + master = spi_alloc_master(&connection->dev, sizeof(*spi)); + if (!master) { + gb_connection_err(connection, "cannot alloc SPI master\n"); + return -ENOMEM; + } + + spi = spi_master_get_devdata(master); + spi->connection = connection; + connection->private = master; + + ret = gb_spi_init(spi); + if (ret) + goto out_err; + + master->bus_num = 0; /* How do we get controller id here? */ + master->num_chipselect = spi->num_chipselect; + master->mode_bits = spi->mode; + master->flags = spi->flags; + master->bits_per_word_mask = spi->bits_per_word_mask; + + /* Attach methods */ + master->cleanup = gb_spi_cleanup; + master->setup = gb_spi_setup; + master->transfer_one_message = gb_spi_transfer_one_message; + + ret = spi_register_master(master); + if (!ret) + return 0; + +out_err: + spi_master_put(master); + + return ret; +} + +static void gb_spi_connection_exit(struct gb_connection *connection) +{ + struct spi_master *master = connection->private; + + spi_unregister_master(master); +} + +static struct gb_protocol spi_protocol = { + .name = "spi", + .id = GREYBUS_PROTOCOL_SPI, + .major = 0, + .minor = 1, + .connection_init = gb_spi_connection_init, + .connection_exit = gb_spi_connection_exit, + .request_recv = NULL, +}; + +int gb_spi_protocol_init(void) +{ + return gb_protocol_register(&spi_protocol); +} + +void gb_spi_protocol_exit(void) +{ + gb_protocol_deregister(&spi_protocol); +} -- cgit v1.2.3-59-g8ed1b From b3b983c2eee8e7464651caa63bbbd6ca4bb65830 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Tue, 27 Jan 2015 12:14:09 -0800 Subject: greybus: build: android: Fix script which locates .ko files and moves them into ramdisk - Fixed incorrect use of $$GREYBUS_SRC_PATH variable - Added quotes around find pattern to stop shell expansion of "*" Signed-off-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Android.mk b/drivers/staging/greybus/Android.mk index 6f13d35f4cf7..112f4dedb0d8 100644 --- a/drivers/staging/greybus/Android.mk +++ b/drivers/staging/greybus/Android.mk @@ -29,6 +29,6 @@ build-greybus: android_kernel cd $(GREYBUS_SRC_PATH) &&\ $(MAKE) -j$(MAKE_JOBS) CROSS_COMPILE=$(KERNEL_TOOLS_PREFIX) $(ARGS) mkdir -p $(GREYBUS_MODULE_OUT_PATH) - ko=`find $$GREYBUS_SRC_PATH -type f -name *.ko`;\ + ko=`find $(GREYBUS_SRC_PATH) -type f -name "*.ko"`;\ for i in $$ko; do $(KERNEL_TOOLCHAIN_PATH)strip --strip-unneeded $$i;\ mv $$i $(GREYBUS_MODULE_OUT_PATH)/; done; -- cgit v1.2.3-59-g8ed1b From f4e6c817b377833a725ba0be80c29fbb59ffd0f7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 27 Jan 2015 09:08:04 +0530 Subject: greybus: spi:fix sparse warnings Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 11859047ac31..ad0c179e51f5 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -180,7 +180,7 @@ gb_spi_operation_create(struct gb_connection *connection, return NULL; request = operation->request->payload; - request->count = count; + request->count = cpu_to_le16(count); request->mode = dev->mode; request->chip_select = dev->chip_select; @@ -189,7 +189,7 @@ gb_spi_operation_create(struct gb_connection *connection, /* Fill in the transfers array */ list_for_each_entry(xfer, &msg->transfers, transfer_list) { - gb_xfer->speed_hz = cpu_to_le16(xfer->speed_hz); + gb_xfer->speed_hz = cpu_to_le32(xfer->speed_hz); gb_xfer->len = cpu_to_le32(xfer->len); gb_xfer->delay_usecs = cpu_to_le16(xfer->delay_usecs); gb_xfer->cs_change = xfer->cs_change; @@ -335,7 +335,7 @@ static int gb_spi_chipselect_operation(struct gb_spi *spi) if (ret) return ret; - spi->num_chipselect = le32_to_cpu(response.num_chipselect); + spi->num_chipselect = le16_to_cpu(response.num_chipselect); return 0; } -- cgit v1.2.3-59-g8ed1b From 5f345a5d394270596f532db6611b12a1377fb622 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 3 Feb 2015 13:17:39 -0500 Subject: greybus: operation: add missing gb_operation_response_send() export Export gb_operation_response_send() for other modules Signed-off-by: Matt Porter Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 82ff306d15e3..50498ec3fdfa 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -734,6 +734,7 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) return gb_message_send(operation->response); } +EXPORT_SYMBOL_GPL(gb_operation_response_send); /* * This function is called when a buffer send request has completed. -- cgit v1.2.3-59-g8ed1b From 067f3b6bfaa132269756e4e6d0c61360f8d6a4b5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Feb 2015 11:22:47 +0800 Subject: greybus: connection: fix non-atomic allocations under spin lock Use GFP_ATOMIC for IDA memory allocations under spin lock, which must not sleep. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c805022c50bc..5f60e83aa37c 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -63,7 +63,7 @@ static bool gb_connection_hd_cport_id_alloc(struct gb_connection *connection) int id; spin_lock_irq(&gb_connections_lock); - id = ida_simple_get(ida, 0, HOST_DEV_CPORT_ID_MAX, GFP_KERNEL); + id = ida_simple_get(ida, 0, HOST_DEV_CPORT_ID_MAX, GFP_ATOMIC); spin_unlock_irq(&gb_connections_lock); if (id < 0) return false; -- cgit v1.2.3-59-g8ed1b From 48d7077c007d27851952861d002d660b552b9888 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 13 Feb 2015 11:28:09 +0800 Subject: greybus: bundle: fix sleep-while-atomic in gb_bundle_destroy Make sure to release the spin lock protecting the interface bundle lists before tearing down the connections and removing the bundle device, which are operations that may sleep. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index d0206602eacd..93f80dc5a815 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -126,6 +126,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 interface_id) */ void gb_bundle_destroy(struct gb_interface *intf) { + LIST_HEAD(list); struct gb_bundle *bundle; struct gb_bundle *temp; @@ -133,12 +134,14 @@ void gb_bundle_destroy(struct gb_interface *intf) return; spin_lock_irq(&gb_bundles_lock); - list_for_each_entry_safe(bundle, temp, &intf->bundles, links) { + list_splice_init(&intf->bundles, &list); + spin_unlock_irq(&gb_bundles_lock); + + list_for_each_entry_safe(bundle, temp, &list, links) { list_del(&bundle->links); gb_bundle_connections_exit(bundle); device_del(&bundle->dev); } - spin_unlock_irq(&gb_bundles_lock); } int gb_bundle_init(struct gb_interface *intf, u8 bundle_id, u8 device_id) -- cgit v1.2.3-59-g8ed1b From 036aad9d0224f6f29c08abe102df709a27f68c17 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 17 Feb 2015 10:48:23 -0500 Subject: greybus: gpio: add interrupt handling support Adds gpio interrupt handling support using an irqchip/irqdomain instantiation inside the GB GPIO driver. This implementation works on older kernels such as 3.10 that do not have the gpiolib irqchip helpers. Any line on a Greybus gpiochip may be configured as an interrupt. Once configured, IRQ event messages received from a module fire off the registered interrupt handler. Signed-off-by: Matt Porter Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 326 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 314 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 85d89b86d5f7..a55327c17c79 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "greybus.h" struct gb_gpio_line { @@ -30,9 +32,16 @@ struct gb_gpio_controller { struct gb_gpio_line *lines; struct gpio_chip chip; + struct irq_chip irqc; + struct irq_chip *irqchip; + struct irq_domain *irqdomain; + unsigned int irq_base; + irq_flow_handler_t irq_handler; + unsigned int irq_default_type; }; #define gpio_chip_to_gb_gpio_controller(chip) \ container_of(chip, struct gb_gpio_controller, chip) +#define irq_data_to_gpio_chip(d) (d->domain->host_data) /* Version of the Greybus GPIO protocol we support */ #define GB_GPIO_VERSION_MAJOR 0x00 @@ -50,6 +59,11 @@ struct gb_gpio_controller { #define GB_GPIO_TYPE_GET_VALUE 0x08 #define GB_GPIO_TYPE_SET_VALUE 0x09 #define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a +#define GB_GPIO_TYPE_IRQ_TYPE 0x0b +#define GB_GPIO_TYPE_IRQ_ACK 0x0c +#define GB_GPIO_TYPE_IRQ_MASK 0x0d +#define GB_GPIO_TYPE_IRQ_UNMASK 0x0e +#define GB_GPIO_TYPE_IRQ_EVENT 0x0f #define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ #define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ @@ -106,6 +120,32 @@ struct gb_gpio_set_debounce_request { }; /* debounce response has no payload */ +struct gb_gpio_irq_type_request { + __u8 which; + __u8 type; +}; +/* irq type response has no payload */ + +struct gb_gpio_irq_mask_request { + __u8 which; +}; +/* irq mask response has no payload */ + +struct gb_gpio_irq_unmask_request { + __u8 which; +}; +/* irq unmask response has no payload */ + +struct gb_gpio_irq_ack_request { + __u8 which; +}; +/* irq ack response has no payload */ + +/* irq event requests originate on another module and are handled on the AP */ +struct gb_gpio_irq_event_request { + __u8 which; +}; +/* irq event response has no payload */ /* Define get_version() routine */ define_get_version(gb_gpio_controller, GPIO); @@ -280,6 +320,120 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, return ret; } +static void gb_gpio_ack_irq(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_to_gpio_chip(d); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_irq_ack_request request; + int ret; + + request.which = d->hwirq; + ret = gb_operation_sync(ggc->connection, + GB_GPIO_TYPE_IRQ_ACK, + &request, sizeof(request), NULL, 0); + if (ret) + pr_err("irq ack operation failed (%d)\n", ret); +} + +static void gb_gpio_mask_irq(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_to_gpio_chip(d); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_irq_mask_request request; + int ret; + + request.which = d->hwirq; + ret = gb_operation_sync(ggc->connection, + GB_GPIO_TYPE_IRQ_MASK, + &request, sizeof(request), NULL, 0); + if (ret) + pr_err("irq mask operation failed (%d)\n", ret); +} + +static void gb_gpio_unmask_irq(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_to_gpio_chip(d); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_irq_unmask_request request; + int ret; + + request.which = d->hwirq; + ret = gb_operation_sync(ggc->connection, + GB_GPIO_TYPE_IRQ_UNMASK, + &request, sizeof(request), NULL, 0); + if (ret) + pr_err("irq unmask operation failed (%d)\n", ret); +} + +static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *chip = irq_data_to_gpio_chip(d); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_irq_type_request request; + int ret = 0; + + request.which = d->hwirq; + request.type = type; + + switch (type) { + case IRQ_TYPE_NONE: + break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + case IRQ_TYPE_LEVEL_LOW: + case IRQ_TYPE_LEVEL_HIGH: + ret = gb_operation_sync(ggc->connection, + GB_GPIO_TYPE_IRQ_TYPE, + &request, sizeof(request), NULL, 0); + if (ret) + pr_err("irq type operation failed (%d)\n", ret); + break; + default: + pr_err("No such irq type %d", type); + ret = -EINVAL; + } + + return ret; +} + +static void gb_gpio_request_recv(u8 type, struct gb_operation *op) +{ + struct gb_gpio_controller *ggc; + struct gb_connection *connection; + struct gb_message *request; + struct gb_gpio_irq_event_request *event; + int irq; + struct irq_desc *desc; + int ret; + + if (type != GB_GPIO_TYPE_IRQ_EVENT) { + pr_err("unsupported unsolicited request\n"); + return; + } + + connection = op->connection; + ggc = connection->private; + + request = op->request; + event = request->payload; + if (event->which > ggc->line_max) { + pr_err("Unsupported hw irq %d\n", event->which); + return; + } + irq = gpio_to_irq(ggc->irq_base + event->which); + desc = irq_to_desc(irq); + + /* Dispatch interrupt */ + local_irq_disable(); + handle_simple_irq(irq, desc); + local_irq_enable(); + + ret = gb_operation_response_send(op, 0); + if (ret) + pr_err("error %d sending response status %d\n", ret, 0); +} + static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) { struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); @@ -399,14 +553,6 @@ static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, return 0; /* XXX */ } -static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - if (offset >= chip->ngpio) - return -EINVAL; - - return 0; /* XXX */ -} - static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { return; /* XXX */ @@ -436,10 +582,148 @@ static int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controlle return ret; } +/** + * gb_gpio_irq_map() - maps an IRQ into a GB gpio irqchip + * @d: the irqdomain used by this irqchip + * @irq: the global irq number used by this GB gpio irqchip irq + * @hwirq: the local IRQ/GPIO line offset on this GB gpio + * + * This function will set up the mapping for a certain IRQ line on a + * GB gpio by assigning the GB gpio as chip data, and using the irqchip + * stored inside the GB gpio. + */ +static int gb_gpio_irq_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct gpio_chip *chip = domain->host_data; + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + + irq_set_chip_data(irq, ggc); + irq_set_chip_and_handler(irq, ggc->irqchip, ggc->irq_handler); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + /* + * No set-up of the hardware will happen if IRQ_TYPE_NONE + * is passed as default type. + */ + if (ggc->irq_default_type != IRQ_TYPE_NONE) + irq_set_irq_type(irq, ggc->irq_default_type); + + return 0; +} + +static void gb_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) +{ +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops gb_gpio_domain_ops = { + .map = gb_gpio_irq_map, + .unmap = gb_gpio_irq_unmap, +}; + +/** + * gb_gpio_irqchip_remove() - removes an irqchip added to a gb_gpio_controller + * @ggc: the gb_gpio_controller to remove the irqchip from + * + * This is called only from gb_gpio_remove() + */ +static void gb_gpio_irqchip_remove(struct gb_gpio_controller *ggc) +{ + unsigned int offset; + + /* Remove all IRQ mappings and delete the domain */ + if (ggc->irqdomain) { + for (offset = 0; offset < (ggc->line_max + 1); offset++) + irq_dispose_mapping(irq_find_mapping(ggc->irqdomain, offset)); + irq_domain_remove(ggc->irqdomain); + } + + if (ggc->irqchip) { + ggc->irqchip = NULL; + } +} + + +/** + * gb_gpio_irqchip_add() - adds an irqchip to a gpio chip + * @chip: the gpio chip to add the irqchip to + * @irqchip: the irqchip to add to the adapter + * @first_irq: if not dynamically assigned, the base (first) IRQ to + * allocate gpio irqs from + * @handler: the irq handler to use (often a predefined irq core function) + * @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE + * to have the core avoid setting up any default type in the hardware. + * + * This function closely associates a certain irqchip with a certain + * gpio chip, providing an irq domain to translate the local IRQs to + * global irqs, and making sure that the gpio chip + * is passed as chip data to all related functions. Driver callbacks + * need to use container_of() to get their local state containers back + * from the gpio chip passed as chip data. An irqdomain will be stored + * in the gpio chip that shall be used by the driver to handle IRQ number + * translation. The gpio chip will need to be initialized and registered + * before calling this function. + */ +static int gb_gpio_irqchip_add(struct gpio_chip *chip, + struct irq_chip *irqchip, + unsigned int first_irq, + irq_flow_handler_t handler, + unsigned int type) +{ + struct gb_gpio_controller *ggc; + unsigned int offset; + unsigned irq_base; + + if (!chip || !irqchip) + return -EINVAL; + + ggc = gpio_chip_to_gb_gpio_controller(chip); + + ggc->irqchip = irqchip; + ggc->irq_handler = handler; + ggc->irq_default_type = type; + ggc->irqdomain = irq_domain_add_simple(NULL, + ggc->line_max + 1, first_irq, + &gb_gpio_domain_ops, chip); + if (!ggc->irqdomain) { + ggc->irqchip = NULL; + return -EINVAL; + } + + /* + * Prepare the mapping since the irqchip shall be orthogonal to + * any gpio calls. If the first_irq was zero, this is + * necessary to allocate descriptors for all IRQs. + */ + for (offset = 0; offset < (ggc->line_max + 1); offset++) { + irq_base = irq_create_mapping(ggc->irqdomain, offset); + if (offset == 0) + ggc->irq_base = irq_base; + } + + return 0; +} + +static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + + return irq_find_mapping(ggc->irqdomain, offset); +} + static int gb_gpio_connection_init(struct gb_connection *connection) { struct gb_gpio_controller *gb_gpio_controller; struct gpio_chip *gpio; + struct irq_chip *irqc; int ret; gb_gpio_controller = kzalloc(sizeof(*gb_gpio_controller), GFP_KERNEL); @@ -452,9 +736,17 @@ static int gb_gpio_connection_init(struct gb_connection *connection) if (ret) goto out_err; + irqc = &gb_gpio_controller->irqc; + irqc->irq_ack = gb_gpio_ack_irq; + irqc->irq_mask = gb_gpio_mask_irq; + irqc->irq_unmask = gb_gpio_unmask_irq; + irqc->irq_set_type = gb_gpio_irq_set_type; + irqc->name = "greybus_gpio"; + gpio = &gb_gpio_controller->chip; gpio->label = "greybus_gpio"; + gpio->dev = &connection->dev; gpio->owner = THIS_MODULE; /* XXX Module get? */ gpio->request = gb_gpio_request; @@ -465,9 +757,8 @@ static int gb_gpio_connection_init(struct gb_connection *connection) gpio->get = gb_gpio_get; gpio->set = gb_gpio_set; gpio->set_debounce = gb_gpio_set_debounce; - gpio->to_irq = gb_gpio_to_irq; gpio->dbg_show = gb_gpio_dbg_show; - + gpio->to_irq = gb_gpio_to_irq; gpio->base = -1; /* Allocate base dynamically */ gpio->ngpio = gb_gpio_controller->line_max + 1; gpio->can_sleep = true; /* XXX */ @@ -475,10 +766,20 @@ static int gb_gpio_connection_init(struct gb_connection *connection) ret = gpiochip_add(gpio); if (ret) { pr_err("Failed to register GPIO\n"); - return ret; + goto out_err; + } + + ret = gb_gpio_irqchip_add(gpio, irqc, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret) { + pr_err("Couldn't add irqchip to Greybus GPIO controller %d\n", ret); + goto irqchip_err; } return 0; + +irqchip_err: + gb_gpiochip_remove(gpio); out_err: kfree(gb_gpio_controller); return ret; @@ -491,6 +792,7 @@ static void gb_gpio_connection_exit(struct gb_connection *connection) if (!gb_gpio_controller) return; + gb_gpio_irqchip_remove(gb_gpio_controller); gb_gpiochip_remove(&gb_gpio_controller->chip); /* kref_put(gb_gpio_controller->connection) */ kfree(gb_gpio_controller); @@ -503,7 +805,7 @@ static struct gb_protocol gpio_protocol = { .minor = 1, .connection_init = gb_gpio_connection_init, .connection_exit = gb_gpio_connection_exit, - .request_recv = NULL, /* no incoming requests */ + .request_recv = gb_gpio_request_recv, }; int gb_gpio_protocol_init(void) -- cgit v1.2.3-59-g8ed1b From 35a64f2c492867e5e404b7620c5784603caae8f1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 13 Feb 2015 14:58:04 +0800 Subject: greybus: gpio: fix memory leaks at init and exit Fix three related memory leaks in the init an exit callbacks, where the gpio-lines array was never freed at all and the controller data wasn't freed in the init error path. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index a55327c17c79..81901bdd9477 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -734,7 +734,7 @@ static int gb_gpio_connection_init(struct gb_connection *connection) ret = gb_gpio_controller_setup(gb_gpio_controller); if (ret) - goto out_err; + goto err_free_controller; irqc = &gb_gpio_controller->irqc; irqc->irq_ack = gb_gpio_ack_irq; @@ -766,7 +766,7 @@ static int gb_gpio_connection_init(struct gb_connection *connection) ret = gpiochip_add(gpio); if (ret) { pr_err("Failed to register GPIO\n"); - goto out_err; + goto err_free_lines; } ret = gb_gpio_irqchip_add(gpio, irqc, 0, @@ -780,7 +780,9 @@ static int gb_gpio_connection_init(struct gb_connection *connection) irqchip_err: gb_gpiochip_remove(gpio); -out_err: +err_free_lines: + kfree(gb_gpio_controller->lines); +err_free_controller: kfree(gb_gpio_controller); return ret; } @@ -795,6 +797,7 @@ static void gb_gpio_connection_exit(struct gb_connection *connection) gb_gpio_irqchip_remove(gb_gpio_controller); gb_gpiochip_remove(&gb_gpio_controller->chip); /* kref_put(gb_gpio_controller->connection) */ + kfree(gb_gpio_controller->lines); kfree(gb_gpio_controller); } -- cgit v1.2.3-59-g8ed1b From 2bf4c87605879758f299953fa5cf4a4ecb6a4edb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 2 Mar 2015 08:52:07 -0800 Subject: greybus: es2: fix USB id to not be the same as ES1 We don't want to bind to the ES1 device, that would be bad. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 4154cce52a16..d8d45bec4f1d 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -29,8 +29,8 @@ #define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE static const struct usb_device_id id_table[] = { - /* Made up numbers for the SVC USB Bridge in ES1 */ - { USB_DEVICE(0xffff, 0x0001) }, + /* Made up numbers for the SVC USB Bridge in ES2 */ + { USB_DEVICE(0xffff, 0x0002) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); -- cgit v1.2.3-59-g8ed1b From 184ab534de842a1baf08a0d7f3621b26bcea44dc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 2 Mar 2015 12:34:40 +0100 Subject: greybus: operation: fix locking issues Fix unconditional re-enabling of interrupts in various operation functions that can all be called with local interrupts disabled from USB completion handlers. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 50498ec3fdfa..5e859c04b716 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -94,6 +94,7 @@ static DEFINE_SPINLOCK(gb_operations_lock); */ static bool gb_operation_result_set(struct gb_operation *operation, int result) { + unsigned long flags; int prev; if (result == -EINPROGRESS) { @@ -104,13 +105,13 @@ static bool gb_operation_result_set(struct gb_operation *operation, int result) * and record an implementation error if it's * set at any other time. */ - spin_lock_irq(&gb_operations_lock); + spin_lock_irqsave(&gb_operations_lock, flags); prev = operation->errno; if (prev == -EBADR) operation->errno = result; else operation->errno = -EILSEQ; - spin_unlock_irq(&gb_operations_lock); + spin_unlock_irqrestore(&gb_operations_lock, flags); WARN_ON(prev != -EBADR); return true; @@ -128,11 +129,11 @@ static bool gb_operation_result_set(struct gb_operation *operation, int result) if (WARN_ON(result == -EBADR)) result = -EILSEQ; /* Nobody should be setting -EBADR */ - spin_lock_irq(&gb_operations_lock); + spin_lock_irqsave(&gb_operations_lock, flags); prev = operation->errno; if (prev == -EINPROGRESS) operation->errno = result; /* First and final result */ - spin_unlock_irq(&gb_operations_lock); + spin_unlock_irqrestore(&gb_operations_lock, flags); return prev == -EINPROGRESS; } @@ -151,15 +152,16 @@ static struct gb_operation * gb_operation_find(struct gb_connection *connection, u16 operation_id) { struct gb_operation *operation; + unsigned long flags; bool found = false; - spin_lock_irq(&gb_operations_lock); + spin_lock_irqsave(&gb_operations_lock, flags); list_for_each_entry(operation, &connection->operations, links) if (operation->id == operation_id) { found = true; break; } - spin_unlock_irq(&gb_operations_lock); + spin_unlock_irqrestore(&gb_operations_lock, flags); return found ? operation : NULL; } @@ -490,6 +492,7 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, { struct greybus_host_device *hd = connection->hd; struct gb_operation *operation; + unsigned long flags; gfp_t gfp_flags; /* @@ -525,9 +528,9 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, init_completion(&operation->completion); kref_init(&operation->kref); - spin_lock_irq(&gb_operations_lock); + spin_lock_irqsave(&gb_operations_lock, flags); list_add_tail(&operation->links, &connection->operations); - spin_unlock_irq(&gb_operations_lock); + spin_unlock_irqrestore(&gb_operations_lock, flags); return operation; @@ -593,13 +596,14 @@ void gb_operation_get(struct gb_operation *operation) static void _gb_operation_destroy(struct kref *kref) { struct gb_operation *operation; + unsigned long flags; operation = container_of(kref, struct gb_operation, kref); /* XXX Make sure it's not in flight */ - spin_lock_irq(&gb_operations_lock); + spin_lock_irqsave(&gb_operations_lock, flags); list_del(&operation->links); - spin_unlock_irq(&gb_operations_lock); + spin_unlock_irqrestore(&gb_operations_lock, flags); gb_operation_message_free(operation->response); gb_operation_message_free(operation->request); -- cgit v1.2.3-59-g8ed1b From d1b20d72ae01a2846ed4cad7bfef0bea717ffb68 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 2 Mar 2015 17:32:43 +0100 Subject: greybus: gpio.c: fix a bad irq number When it receive an interrupt, the function gb_gpio_request_recv doesn't use the good gpio number to get the irq number. Then, the expected irq is never fired. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 81901bdd9477..4af5050edea0 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -421,7 +421,7 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) pr_err("Unsupported hw irq %d\n", event->which); return; } - irq = gpio_to_irq(ggc->irq_base + event->which); + irq = gpio_to_irq(ggc->chip.base + event->which); desc = irq_to_desc(irq); /* Dispatch interrupt */ -- cgit v1.2.3-59-g8ed1b From 8f5eadb7ea6a0c1cbedbfd33d9d0b94dac400d90 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 2 Mar 2015 09:55:26 +0100 Subject: greybus: connection: fix locking in gb_hd_connection_find Fix unconditional re-enabling of interrupts in gb_hd_connection_find, which can be called with local interrupts disabled from the USB completion handler. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 5f60e83aa37c..3ec984c8d7e5 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -18,14 +18,15 @@ struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, u16 cport_id) { struct gb_connection *connection = NULL; + unsigned long flags; - spin_lock_irq(&gb_connections_lock); + spin_lock_irqsave(&gb_connections_lock, flags); list_for_each_entry(connection, &hd->connections, hd_links) if (connection->hd_cport_id == cport_id) goto found; connection = NULL; found: - spin_unlock_irq(&gb_connections_lock); + spin_unlock_irqrestore(&gb_connections_lock, flags); return connection; } -- cgit v1.2.3-59-g8ed1b From b908dec468c95d51922cb72a076fde813622ee54 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 10 Mar 2015 14:41:12 +0530 Subject: greybus: gpb: Fix print mistakes Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpb.c b/drivers/staging/greybus/gpb.c index 5f080d40ced4..61771058a0c3 100644 --- a/drivers/staging/greybus/gpb.c +++ b/drivers/staging/greybus/gpb.c @@ -42,11 +42,11 @@ static int __init gpbridge_init(void) goto error_usb; } if (gb_i2c_protocol_init()) { - pr_err("error initializing usb protocol\n"); + pr_err("error initializing i2c protocol\n"); goto error_i2c; } if (gb_spi_protocol_init()) { - pr_err("error initializing usb protocol\n"); + pr_err("error initializing spi protocol\n"); goto error_spi; } return 0; -- cgit v1.2.3-59-g8ed1b From 96eab779e1985fd0b39426d288d3af38e3bce50c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 16 Mar 2015 16:49:37 +0530 Subject: greybus: hid: add HID class driver This adds HID transport layer driver for Greybus. Most of the stuff is implemented, but is untested. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/gpb.c | 7 + drivers/staging/greybus/hid.c | 565 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/protocol.h | 3 + 4 files changed, 576 insertions(+) create mode 100644 drivers/staging/greybus/hid.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index a22ad690d3a9..6cb08ae544cd 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -14,6 +14,7 @@ gb-phy-y := gpb.o \ uart.o \ pwm.o \ gpio.o \ + hid.o \ i2c.o \ spi.o \ usb.o diff --git a/drivers/staging/greybus/gpb.c b/drivers/staging/greybus/gpb.c index 61771058a0c3..931d739f9fb9 100644 --- a/drivers/staging/greybus/gpb.c +++ b/drivers/staging/greybus/gpb.c @@ -49,8 +49,14 @@ static int __init gpbridge_init(void) pr_err("error initializing spi protocol\n"); goto error_spi; } + if (gb_hid_protocol_init()) { + pr_err("error initializing hid protocol\n"); + goto error_hid; + } return 0; +error_hid: + gb_spi_protocol_exit(); error_spi: gb_i2c_protocol_exit(); error_i2c: @@ -69,6 +75,7 @@ error_gpio: static void __exit gpbridge_exit(void) { + gb_hid_protocol_exit(); gb_spi_protocol_exit(); gb_i2c_protocol_exit(); gb_usb_protocol_exit(); diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c new file mode 100644 index 000000000000..17ca476c2568 --- /dev/null +++ b/drivers/staging/greybus/hid.c @@ -0,0 +1,565 @@ +/* + * HID class driver for the Greybus. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include +#include + +#include "greybus.h" + +/* Version of the Greybus hid protocol we support */ +#define GB_HID_VERSION_MAJOR 0x00 +#define GB_HID_VERSION_MINOR 0x01 + +/* Greybus HID request types */ +#define GB_HID_TYPE_INVALID 0x00 +#define GB_HID_TYPE_PROTOCOL_VERSION 0x01 +#define GB_HID_TYPE_GET_DESC 0x02 +#define GB_HID_TYPE_GET_REPORT_DESC 0x03 +#define GB_HID_TYPE_PWR_ON 0x04 +#define GB_HID_TYPE_PWR_OFF 0x05 +#define GB_HID_TYPE_GET_REPORT 0x06 +#define GB_HID_TYPE_SET_REPORT 0x07 /* Feature or Output, via control pipe */ +#define GB_HID_TYPE_OUTPUT_REPORT 0x08 /* Output report via interrupt pipe */ +#define GB_HID_TYPE_IRQ_EVENT 0x09 +#define GB_HID_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +/* Report type */ +#define GB_HID_INPUT_REPORT 0 +#define GB_HID_OUTPUT_REPORT 1 +#define GB_HID_FEATURE_REPORT 2 + +/* Different request/response structures */ +/* HID get descriptor response */ +struct gb_hid_desc_response { + __u8 bLength; + __le16 wReportDescLength; + __le16 bcdHID; + __le16 wProductID; + __le16 wVendorID; + __u8 bCountryCode; +} __packed; + +/* HID get report request/response */ +struct gb_hid_get_report_request { + __u8 report_type; + __u8 report_id; +}; + +/* HID set report request */ +struct gb_hid_set_report_request { + __u8 report_type; + __u8 report_id; + __u8 report[0]; +}; + +/* HID input report request, via interrupt pipe */ +struct gb_hid_input_report_request { + __u8 report[0]; +}; + +/* Greybus HID device's structure */ +struct gb_hid { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + + struct hid_device *hid; + struct gb_hid_desc_response hdesc; + + unsigned long flags; +#define GB_HID_STARTED 0x01 +#define GB_HID_READ_PENDING 0x04 + + unsigned int bufsize; + char *inbuf; +}; + +static DEFINE_MUTEX(gb_hid_open_mutex); + +/* Routines to get controller's infomation over greybus */ + +/* Define get_version() routine */ +define_get_version(gb_hid, HID); + +/* Operations performed on greybus */ +static int gb_hid_get_desc(struct gb_hid *ghid) +{ + return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_DESC, NULL, + 0, &ghid->hdesc, sizeof(ghid->hdesc)); +} + +static int gb_hid_get_report_desc(struct gb_hid *ghid, char *rdesc) +{ + return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT_DESC, + NULL, 0, rdesc, + le16_to_cpu(ghid->hdesc.wReportDescLength)); +} + +static int gb_hid_set_power(struct gb_hid *ghid, int type) +{ + return gb_operation_sync(ghid->connection, type, NULL, 0, NULL, 0); +} + +static int gb_hid_get_report(struct gb_hid *ghid, u8 report_type, u8 report_id, + unsigned char *buf, int len) +{ + struct gb_hid_get_report_request request; + + request.report_type = report_type; + request.report_id = report_id; + + return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT, + &request, sizeof(request), buf, len); +} + +/* + * @raw: true: use SET_REPORT HID command, false: send plain OUTPUT report. + * + * Use SET_REPORT for feature reports or if the device does not support the + * output plain report. + */ +static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, + unsigned char *buf, int len, int raw) +{ + struct gb_hid_set_report_request *request; + struct gb_operation *operation; + int ret, size = sizeof(*request) + len - 1; + int type = raw ? GB_HID_TYPE_SET_REPORT : GB_HID_TYPE_OUTPUT_REPORT; + + operation = gb_operation_create(ghid->connection, type, size, 0); + if (!operation) + return -ENOMEM; + + request = operation->request->payload; + request->report_type = report_type; + request->report_id = report_id; + memcpy(request->report, buf, len); + + ret = gb_operation_request_send_sync(operation); + if (ret) + pr_err("%s: operation failed (%d)\n", __func__, ret); + else + ret = len; + + gb_operation_destroy(operation); + return ret; +} + +static void gb_hid_irq_handler(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_hid *ghid = connection->private; + struct gb_hid_input_report_request *request = op->request->payload; + int ret, size; + + if (type != GB_HID_TYPE_IRQ_EVENT) { + pr_err("unsupported unsolicited request\n"); + return; + } + + ret = gb_operation_response_send(op, 0); + if (ret) + pr_err("%s: error %d sending response status %d\n", __func__, + ret, 0); + + size = request->report[0] | request->report[1] << 8; + if (!size) { + pr_err("%s: size can't be zero.\n", __func__); + return; + } + + if (test_bit(GB_HID_STARTED, &ghid->flags)) + hid_input_report(ghid->hid, HID_INPUT_REPORT, + request->report + 2, size - 2, 1); +} + + +static int gb_hid_report_len(struct hid_report *report) +{ + return ((report->size - 1) >> 3) + 1 + + report->device->report_enum[report->type].numbered; +} + +static void gb_hid_find_max_report(struct hid_device *hid, unsigned int type, + unsigned int *max) +{ + struct hid_report *report; + unsigned int size; + + list_for_each_entry(report, &hid->report_enum[type].report_list, list) { + size = gb_hid_report_len(report); + if (*max < size) + *max = size; + } +} + +static void gb_hid_free_buffers(struct gb_hid *ghid) +{ + kfree(ghid->inbuf); + ghid->inbuf = NULL; + ghid->bufsize = 0; +} + +static int gb_hid_alloc_buffers(struct gb_hid *ghid, size_t bufsize) +{ + ghid->inbuf = kzalloc(bufsize, GFP_KERNEL); + if (!ghid->inbuf) + return -ENOMEM; + + ghid->bufsize = bufsize; + + return 0; +} + +/* Routines dealing with reports */ +static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report) +{ + unsigned int size; + + size = gb_hid_report_len(report); + if (gb_hid_get_report(ghid, report->type, report->id, ghid->inbuf, + size)) + return; + + /* + * hid->driver_lock is held as we are in probe function, + * we just need to setup the input fields, so using + * hid_report_raw_event is safe. + */ + hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1); +} + +static void gb_hid_init_reports(struct gb_hid *ghid) +{ + struct hid_device *hid = ghid->hid; + struct hid_report *report; + + list_for_each_entry(report, + &hid->report_enum[HID_INPUT_REPORT].report_list, list) + gb_hid_init_report(ghid, report); + + list_for_each_entry(report, + &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + gb_hid_init_report(ghid, report); +} + +static int __gb_hid_get_raw_report(struct hid_device *hid, + unsigned char report_number, __u8 *buf, size_t count, + unsigned char report_type) +{ + struct gb_hid *ghid = hid->driver_data; + int ret; + + if (report_type == HID_OUTPUT_REPORT) + return -EINVAL; + + ret = gb_hid_get_report(ghid, report_type, report_number, buf, count); + if (!ret) + ret = count; + + return ret; +} + +static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf, + size_t len, unsigned char report_type, + bool raw) +{ + struct gb_hid *ghid = hid->driver_data; + int report_id = buf[0]; + int ret; + + if (report_type == HID_INPUT_REPORT) + return -EINVAL; + + if (report_id) { + buf++; + len--; + } + + ret = gb_hid_set_report(ghid, report_type, report_id, buf, len, raw); + if (report_id && ret >= 0) + ret++; /* add report_id to the number of transfered bytes */ + + return 0; +} + +static int gb_hid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + return __gb_hid_get_raw_report(hid, reportnum, buf, len, rtype); + case HID_REQ_SET_REPORT: + if (buf[0] != reportnum) + return -EINVAL; + return __gb_hid_output_raw_report(hid, buf, len, rtype, true); + default: + return -EIO; + } +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) +static int gb_hid_output_report(struct hid_device *hid, __u8 *buf, size_t len) +{ + return __gb_hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT, + false); +} + +#else + +static int gb_hid_get_raw_report(struct hid_device *hid, + unsigned char reportnum, __u8 *buf, + size_t len, unsigned char rtype) +{ + return gb_hid_raw_request(hid, reportnum, buf, len, rtype, + HID_REQ_GET_REPORT); +} + +static int gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf, + size_t len, unsigned char rtype) +{ + return gb_hid_raw_request(hid, buf[0], buf, len, rtype, + HID_REQ_SET_REPORT); +} +#endif + +/* HID Callbacks */ +static int gb_hid_parse(struct hid_device *hid) +{ + struct gb_hid *ghid = hid->driver_data; + unsigned int rsize; + char *rdesc; + int ret; + + rsize = le16_to_cpu(ghid->hdesc.wReportDescLength); + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { + dbg_hid("weird size of report descriptor (%u)\n", rsize); + return -EINVAL; + } + + rdesc = kzalloc(rsize, GFP_KERNEL); + if (!rdesc) { + dbg_hid("couldn't allocate rdesc memory\n"); + return -ENOMEM; + } + + ret = gb_hid_get_report_desc(ghid, rdesc); + if (ret) { + hid_err(hid, "reading report descriptor failed\n"); + goto free_rdesc; + } + + ret = hid_parse_report(hid, rdesc, rsize); + if (ret) + dbg_hid("parsing report descriptor failed\n"); + +free_rdesc: + kfree(rdesc); + + return ret; +} + +static int gb_hid_start(struct hid_device *hid) +{ + struct gb_hid *ghid = hid->driver_data; + unsigned int bufsize = HID_MIN_BUFFER_SIZE; + int ret; + + gb_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize); + gb_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize); + gb_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize); + + if (bufsize > HID_MAX_BUFFER_SIZE) + bufsize = HID_MAX_BUFFER_SIZE; + + ret = gb_hid_alloc_buffers(ghid, bufsize); + if (ret) + return ret; + + if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS)) + gb_hid_init_reports(ghid); + + return 0; +} + +static void gb_hid_stop(struct hid_device *hid) +{ + struct gb_hid *ghid = hid->driver_data; + + gb_hid_free_buffers(ghid); +} + +static int gb_hid_open(struct hid_device *hid) +{ + struct gb_hid *ghid = hid->driver_data; + int ret = 0; + + mutex_lock(&gb_hid_open_mutex); + if (!hid->open++) { + ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON); + if (ret < 0) + hid->open--; + else + set_bit(GB_HID_STARTED, &ghid->flags); + } + mutex_unlock(&gb_hid_open_mutex); + + return ret; +} + +static void gb_hid_close(struct hid_device *hid) +{ + struct gb_hid *ghid = hid->driver_data; + + /* + * Protecting hid->open to make sure we don't restart data acquistion + * due to a resumption we no longer care about.. + */ + mutex_lock(&gb_hid_open_mutex); + if (!--hid->open) { + clear_bit(GB_HID_STARTED, &ghid->flags); + + /* Save some power */ + WARN_ON(gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF)); + } + mutex_unlock(&gb_hid_open_mutex); +} + +static int gb_hid_power(struct hid_device *hid, int lvl) +{ + struct gb_hid *ghid = hid->driver_data; + + switch (lvl) { + case PM_HINT_FULLON: + return gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON); + case PM_HINT_NORMAL: + return gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF); + } + + return 0; +} + +/* HID structure to pass callbacks */ +static struct hid_ll_driver gb_hid_ll_driver = { + .parse = gb_hid_parse, + .start = gb_hid_start, + .stop = gb_hid_stop, + .open = gb_hid_open, + .close = gb_hid_close, + .power = gb_hid_power, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) + .output_report = gb_hid_output_report, + .raw_request = gb_hid_raw_request, +#endif +}; + +static int gb_hid_init(struct gb_hid *ghid) +{ + struct hid_device *hid = ghid->hid; + int ret; + + ret = get_version(ghid); + if (ret) + return ret; + + ret = gb_hid_get_desc(ghid); + if (ret) + return ret; + + hid->version = le16_to_cpu(ghid->hdesc.bcdHID); + hid->vendor = le16_to_cpu(ghid->hdesc.wVendorID); + hid->product = le16_to_cpu(ghid->hdesc.wProductID); + hid->country = ghid->hdesc.bCountryCode; + + hid->driver_data = ghid; + hid->ll_driver = &gb_hid_ll_driver; + hid->dev.parent = &ghid->connection->dev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0) + hid->hid_get_raw_report = gb_hid_get_raw_report; + hid->hid_output_raw_report = gb_hid_output_raw_report; +#endif +// hid->bus = BUS_GREYBUS; /* Need a bustype for GREYBUS in */ + + /* Set HID device's name */ + snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", + dev_name(&ghid->connection->dev), hid->vendor, hid->product); + + return 0; +} + +static int gb_hid_connection_init(struct gb_connection *connection) +{ + struct hid_device *hid; + struct gb_hid *ghid; + int ret; + + ghid = kzalloc(sizeof(*ghid), GFP_KERNEL); + if (!ghid) + return -ENOMEM; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto free_ghid; + } + + connection->private = ghid; + ghid->connection = connection; + ghid->hid = hid; + + ret = gb_hid_init(ghid); + if (ret) + goto destroy_hid; + + ret = hid_add_device(hid); + if (!ret) + return 0; + + hid_err(hid, "can't add hid device: %d\n", ret); + +destroy_hid: + hid_destroy_device(hid); +free_ghid: + kfree(ghid); + + return ret; +} + +static void gb_hid_connection_exit(struct gb_connection *connection) +{ + struct gb_hid *ghid = connection->private; + + hid_destroy_device(ghid->hid); + kfree(ghid); +} + +static struct gb_protocol hid_protocol = { + .name = "hid", + .id = GREYBUS_PROTOCOL_HID, + .major = 0, + .minor = 1, + .connection_init = gb_hid_connection_init, + .connection_exit = gb_hid_connection_exit, + .request_recv = gb_hid_irq_handler, +}; + +int gb_hid_protocol_init(void) +{ + return gb_protocol_register(&hid_protocol); +} + +void gb_hid_protocol_exit(void) +{ + gb_protocol_deregister(&hid_protocol); +} diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 2d0100008138..a74afef92a01 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -84,6 +84,9 @@ extern void gb_i2c_protocol_exit(void); extern int gb_spi_protocol_init(void); extern void gb_spi_protocol_exit(void); +extern int gb_hid_protocol_init(void); +extern void gb_hid_protocol_exit(void); + #define gb_protocol_driver(__protocol) \ static int __init protocol_init(void) \ { \ -- cgit v1.2.3-59-g8ed1b From 6a80ed4d2c26b5934f3bfb9beafe73a9c7946d34 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 17 Mar 2015 10:55:50 +0100 Subject: greybus: pwm: fix memory leak in error path Fix memory leak in connection_init error path. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/pwm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index e2ab6f5f610e..3f508bf66ec0 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -261,7 +261,7 @@ static int gb_pwm_connection_init(struct gb_connection *connection) ret = pwmchip_add(pwm); if (ret) { pr_err("Failed to register PWM\n"); - return ret; + goto out_err; } return 0; -- cgit v1.2.3-59-g8ed1b From deeb57f5bd990f747815216ab772e92413848f6e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 17 Mar 2015 10:55:51 +0100 Subject: greybus: vibrator: fix memory leak in error path Fix memory leak in connection_init error path. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/vibrator.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index b6ec9f2373bb..c92c69ef3025 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -127,7 +127,7 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) "vibrator%d", vib->minor); if (IS_ERR(dev)) { retval = -EINVAL; - goto error; + goto err_idr_remove; } vib->dev = dev; @@ -140,12 +140,14 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); if (retval) { device_unregister(dev); - goto error; + goto err_idr_remove; } #endif return 0; +err_idr_remove: + idr_remove(&minors, vib->minor); error: kfree(vib); return retval; -- cgit v1.2.3-59-g8ed1b From 44538397e79987080adc619c6fd4edda92093d46 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 17 Mar 2015 10:55:52 +0100 Subject: greybus: connection: fix oops after failed init Make sure not to call connection_exit for connections that have never been initialised (e.g. due to failure to init). This fixes oopses due to null-dereferences and use-after-free in connection_exit callbacks (e.g. trying to remove a gpio-chip that has never been added) when the bundle and interface are ultimately destroyed. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3ec984c8d7e5..46e259f05a55 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -298,6 +298,10 @@ void gb_connection_exit(struct gb_connection *connection) dev_warn(&connection->dev, "exit without protocol.\n"); return; } + + if (connection->state != GB_CONNECTION_STATE_ENABLED) + return; + connection->state = GB_CONNECTION_STATE_DESTROYING; connection->protocol->connection_exit(connection); } -- cgit v1.2.3-59-g8ed1b From fcc4356de4601c83530928e05b041e4ac678fd6a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 17 Mar 2015 18:24:28 +0100 Subject: greybus: gpio: fix set-debounce request alignment Fix alignment of the usec-field in the set-debounce request, which should follow the which-field without any inserted padding. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 4af5050edea0..458565a7afb8 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -116,7 +116,7 @@ struct gb_gpio_set_value_request { struct gb_gpio_set_debounce_request { __u8 which; - __le16 usec; + __le16 usec __packed; }; /* debounce response has no payload */ -- cgit v1.2.3-59-g8ed1b From b41caa99a25f62326be451592c493de29347d142 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 17 Mar 2015 18:24:29 +0100 Subject: greybus: pwm: fix config-request alignment Fix alignment of the duty and period-fields in the config request, which should follow the which-field without any inserted padding. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/pwm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 3f508bf66ec0..4e38b8a4624e 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -56,8 +56,8 @@ struct gb_pwm_deactivate_request { struct gb_pwm_config_request { __u8 which; - __le32 duty; - __le32 period; + __le32 duty __packed; + __le32 period __packed; }; struct gb_pwm_polarity_request { -- cgit v1.2.3-59-g8ed1b From afcf8c715e7cb615dad484913b70c714594ce159 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 17 Mar 2015 18:24:30 +0100 Subject: greybus: uart: remove packed-attribute from line-coding struct Remove packed-attribute from line-coding struct, whose members are all naturally aligned. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 575ca568a4f1..51e4f7bad47f 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -68,7 +68,7 @@ struct gb_serial_line_coding { #define GB_SERIAL_SPACE_PARITY 4 __u8 data; -} __attribute__ ((packed)); +}; struct gb_uart_set_line_coding_request { struct gb_serial_line_coding line_coding; -- cgit v1.2.3-59-g8ed1b From e8f824b6589cd5477adf7a069e7c67918a6bb949 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Wed, 18 Mar 2015 15:42:51 +0100 Subject: greybus: Export greybus debugfs folder Add gb_debugfs_get method to access to gb_debug_root dentry, in order to use it from other greybus modules. Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/debugfs.c | 6 ++++++ drivers/staging/greybus/greybus.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c index 770b7c0857d3..b8865d707362 100644 --- a/drivers/staging/greybus/debugfs.c +++ b/drivers/staging/greybus/debugfs.c @@ -29,3 +29,9 @@ void gb_debugfs_cleanup(void) debugfs_remove_recursive(gb_debug_root); gb_debug_root = NULL; } + +struct dentry *gb_debugfs_get(void) +{ + return gb_debug_root; +} +EXPORT_SYMBOL_GPL(gb_debugfs_get); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 68382b383390..967d64faa8fd 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -160,6 +160,7 @@ int gb_ap_init(void); void gb_ap_exit(void); int gb_debugfs_init(void); void gb_debugfs_cleanup(void); +struct dentry *gb_debugfs_get(void); extern struct bus_type greybus_bus_type; -- cgit v1.2.3-59-g8ed1b From 183f872eb1895359ad41b2f3570c37b212fc29c1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Mar 2015 17:02:45 +0530 Subject: greybus: operation: s/status/result to match field name Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5e859c04b716..b01106f29ab3 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -42,11 +42,11 @@ static DEFINE_MUTEX(gb_message_mutex); * (0x80) of the operation type field is used to indicate whether * the message is a request (clear) or a response (set). * - * Response messages include an additional status byte, which + * Response messages include an additional result byte, which * communicates the result of the corresponding request. A zero - * status value means the operation completed successfully. Any + * result value means the operation completed successfully. Any * other value indicates an error; in this case, the payload of the - * response message (if any) is ignored. The status byte must be + * response message (if any) is ignored. The result byte must be * zero in the header for a request message. * * The wire format for all numeric fields in the header is little -- cgit v1.2.3-59-g8ed1b From 3b6cf2ee3b726747b61285c36a76cc810b102c35 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Mar 2015 17:02:46 +0530 Subject: greybus: operation: Fix comment mistake Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b01106f29ab3..d4b59fe79559 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -965,7 +965,7 @@ int gb_operation_init(void) U16_MAX - sizeof(struct gb_operation_msg_hdr)); /* - * A message structure with consists of: + * A message structure consists of: * - the message structure itself * - the headroom set aside for the host device * - the message header -- cgit v1.2.3-59-g8ed1b From 9ee2b61df14f0afe8d7df86d8eaa24e6d59a923b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Mar 2015 17:02:48 +0530 Subject: greybus: interface: remove double underscore from fn name Also bring * closer to gb_interface_get_drvdata :) Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index f5b0cef9311b..f444e311bfac 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -37,7 +37,7 @@ static inline void gb_interface_set_drvdata(struct gb_interface *intf, dev_set_drvdata(&intf->dev, data); } -static inline void * gb_interface__get_drvdata(struct gb_interface *intf) +static inline void *gb_interface_get_drvdata(struct gb_interface *intf) { return dev_get_drvdata(&intf->dev); } -- cgit v1.2.3-59-g8ed1b From d71aaf288f7e79e7c7ed9893a1b4e28221d1b8d0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Mar 2015 17:02:49 +0530 Subject: greybus: core: place module_{init|exit}() right below the routines To follow coding guidelines a bit :) Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index ee8bba59faab..15408ec61dc3 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -244,6 +244,7 @@ error_bus: return retval; } +module_init(gb_init); static void __exit gb_exit(void) { @@ -252,8 +253,6 @@ static void __exit gb_exit(void) bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); } - -module_init(gb_init); module_exit(gb_exit); MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 292cca99fb9c04888880de4ea9a1984ffcc4e7bd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 18 Mar 2015 11:41:39 +0530 Subject: greybus: hid: don't support OUTPUT report over interrupt channel There is no interrupt channel as such and so no need to support ->output_report(). Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 17ca476c2568..fe05a08aa7f2 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -28,9 +28,8 @@ #define GB_HID_TYPE_PWR_ON 0x04 #define GB_HID_TYPE_PWR_OFF 0x05 #define GB_HID_TYPE_GET_REPORT 0x06 -#define GB_HID_TYPE_SET_REPORT 0x07 /* Feature or Output, via control pipe */ -#define GB_HID_TYPE_OUTPUT_REPORT 0x08 /* Output report via interrupt pipe */ -#define GB_HID_TYPE_IRQ_EVENT 0x09 +#define GB_HID_TYPE_SET_REPORT 0x07 +#define GB_HID_TYPE_IRQ_EVENT 0x08 #define GB_HID_TYPE_RESPONSE 0x80 /* OR'd with rest */ /* Report type */ @@ -122,21 +121,15 @@ static int gb_hid_get_report(struct gb_hid *ghid, u8 report_type, u8 report_id, &request, sizeof(request), buf, len); } -/* - * @raw: true: use SET_REPORT HID command, false: send plain OUTPUT report. - * - * Use SET_REPORT for feature reports or if the device does not support the - * output plain report. - */ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, - unsigned char *buf, int len, int raw) + unsigned char *buf, int len) { struct gb_hid_set_report_request *request; struct gb_operation *operation; int ret, size = sizeof(*request) + len - 1; - int type = raw ? GB_HID_TYPE_SET_REPORT : GB_HID_TYPE_OUTPUT_REPORT; - operation = gb_operation_create(ghid->connection, type, size, 0); + operation = gb_operation_create(ghid->connection, + GB_HID_TYPE_SET_REPORT, size, 0); if (!operation) return -ENOMEM; @@ -271,8 +264,7 @@ static int __gb_hid_get_raw_report(struct hid_device *hid, } static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf, - size_t len, unsigned char report_type, - bool raw) + size_t len, unsigned char report_type) { struct gb_hid *ghid = hid->driver_data; int report_id = buf[0]; @@ -286,7 +278,7 @@ static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf, len--; } - ret = gb_hid_set_report(ghid, report_type, report_id, buf, len, raw); + ret = gb_hid_set_report(ghid, report_type, report_id, buf, len); if (report_id && ret >= 0) ret++; /* add report_id to the number of transfered bytes */ @@ -303,21 +295,13 @@ static int gb_hid_raw_request(struct hid_device *hid, unsigned char reportnum, case HID_REQ_SET_REPORT: if (buf[0] != reportnum) return -EINVAL; - return __gb_hid_output_raw_report(hid, buf, len, rtype, true); + return __gb_hid_output_raw_report(hid, buf, len, rtype); default: return -EIO; } } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) -static int gb_hid_output_report(struct hid_device *hid, __u8 *buf, size_t len) -{ - return __gb_hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT, - false); -} - -#else - +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0) static int gb_hid_get_raw_report(struct hid_device *hid, unsigned char reportnum, __u8 *buf, size_t len, unsigned char rtype) @@ -459,7 +443,6 @@ static struct hid_ll_driver gb_hid_ll_driver = { .close = gb_hid_close, .power = gb_hid_power, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) - .output_report = gb_hid_output_report, .raw_request = gb_hid_raw_request, #endif }; -- cgit v1.2.3-59-g8ed1b From c020d568f5630d38b72b61a97a4d04f1428b9771 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:46:13 +0100 Subject: greybus: usb: silence compiler warning This driver is being rewritten, but let's silence a pointer-to-int-cast compiler warning meanwhile. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/usb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index ff4556fb7dd5..ea9784171352 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -140,7 +140,7 @@ static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) request->transfer_flags = cpu_to_le32(urb->transfer_flags); request->transfer_buffer_length = cpu_to_le32(urb->transfer_buffer_length); request->interval = cpu_to_le32(urb->interval); - request->hcpriv_ep = cpu_to_le64(urb->ep->hcpriv); + request->hcpriv_ep = cpu_to_le64((unsigned long)urb->ep->hcpriv); request->number_of_packets = cpu_to_le32(urb->number_of_packets); memcpy(request->setup_packet, urb->setup_packet, 8); @@ -160,7 +160,7 @@ static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) int ret; urb->ep->hcpriv = NULL; - request.hcpriv_ep = cpu_to_le64(urb->hcpriv); + request.hcpriv_ep = cpu_to_le64((unsigned long)urb->hcpriv); ret = gb_operation_sync(dev->connection, GB_USB_TYPE_URB_DEQUEUE, &request, sizeof(request), NULL, 0); urb->hcpriv = NULL; @@ -173,7 +173,7 @@ static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) struct gb_usb_endpoint_disable_request request; int ret; - request.hcpriv = cpu_to_le64(ep->hcpriv); + request.hcpriv = cpu_to_le64((unsigned long)ep->hcpriv); ret = gb_operation_sync(dev->connection, GB_USB_TYPE_ENDPOINT_DISABLE, &request, sizeof(request), NULL, 0); ep->hcpriv = NULL; -- cgit v1.2.3-59-g8ed1b From 0b7534b86d672752babd18e061b0d869781e3615 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:46:14 +0100 Subject: greybus: ap: fix svc handshake protocol check Fix incorrect SVC handshake protocol check, which would only bail out if both major and minor protocol versions supported by the SVC differed. Since we currently only support one version of the protocol, upgrade the debug message to warning and bail unless the protocol versions match perfectly for now. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 3e4d4fbfd7fc..d5edef90df48 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -91,9 +91,10 @@ static void svc_handshake(struct svc_function_handshake *handshake, } /* A new SVC communication channel, let's verify a supported version */ - if ((handshake->version_major != GREYBUS_VERSION_MAJOR) && + if ((handshake->version_major != GREYBUS_VERSION_MAJOR) || (handshake->version_minor != GREYBUS_VERSION_MINOR)) { - dev_dbg(hd->parent, "received invalid greybus version %d:%d\n", + dev_warn(hd->parent, + "received invalid greybus version %u.%u\n", handshake->version_major, handshake->version_minor); return; } -- cgit v1.2.3-59-g8ed1b From 69bae8910e558356a09f080a02b64d72a0990409 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:46:15 +0100 Subject: greybus: ap: fix typo in comment Fix typo in svc_hotplug comment. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index d5edef90df48..10f3b4d2443d 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -169,7 +169,7 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, case SVC_HOTPLUG_EVENT: /* Add a new module to the system */ if (payload_length < 0x03) { - /* Hotplug message is at lest 3 bytes big */ + /* Hotplug message is at least 3 bytes big */ dev_err(hd->parent, "Illegal size of svc hotplug message %d\n", payload_length); -- cgit v1.2.3-59-g8ed1b From fe4c0e548aea5f4bc2df967db69d7d0b509327ef Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:46:16 +0100 Subject: greybus: ap: clean up svc link management error path Return immediately on bundle-init failure when processing SVC link up. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 10f3b4d2443d..ea197ac57b52 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -149,11 +149,13 @@ static void svc_management(struct svc_function_unipro_management *management, ret = gb_bundle_init(intf, management->link_up.interface_id, management->link_up.device_id); - if (ret) + if (ret) { dev_err(hd->parent, "error %d initializing interface %hhu bundle %hhu\n", ret, management->link_up.module_id, management->link_up.interface_id); + return; + } break; default: dev_err(hd->parent, "Unhandled UniPro management message\n"); -- cgit v1.2.3-59-g8ed1b From 25eb732954ee656edd92770f498d28f87086511b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:46:17 +0100 Subject: greybus: connection: replace custom error function with dev_err Remove custom connection error function and replace it with dev_err. The standard error function provides more information in the message prefix (e.g. includes the interface id), has a well-known semantics (e.g. does does not add newlines to messages), and is even somewhat shorter to type. Note that some uses of the custom function were already adding double newlines due to the non-standard semantics. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 19 ------------------- drivers/staging/greybus/connection.h | 2 -- drivers/staging/greybus/i2c.c | 4 ++-- drivers/staging/greybus/operation.c | 14 +++++++------- drivers/staging/greybus/spi.c | 11 +++++------ 5 files changed, 14 insertions(+), 36 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 46e259f05a55..102e1a4c6e74 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -255,25 +255,6 @@ void gb_connection_destroy(struct gb_connection *connection) device_del(&connection->dev); } -void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - pr_err("greybus: [%hhu:%hhu:%hu]: %pV\n", - connection->bundle->intf->module->module_id, - connection->bundle->id, - connection->bundle_cport_id, &vaf); - - va_end(args); -} -EXPORT_SYMBOL_GPL(gb_connection_err); - int gb_connection_init(struct gb_connection *connection) { int ret; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index b07df79f9d86..7febf4e03049 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -58,8 +58,6 @@ struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); -__printf(2, 3) -void gb_connection_err(struct gb_connection *connection, const char *fmt, ...); void gb_connection_bind_protocol(struct gb_connection *connection); diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 0bcd7a9b0e12..84d20e54ae9b 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -178,8 +178,8 @@ gb_i2c_operation_create(struct gb_connection *connection, u32 i; if (msg_count > (u32)U16_MAX) { - gb_connection_err(connection, "msg_count (%u) too big", - msg_count); + dev_err(&connection->dev, "msg_count (%u) too big\n", + msg_count); return NULL; } op_count = (u16)msg_count; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d4b59fe79559..83359dde1a8f 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -220,7 +220,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) return; } - gb_connection_err(operation->connection, + dev_err(&operation->connection->dev, "unexpected incoming request type 0x%02hhx\n", operation->type); if (gb_operation_result_set(operation, -EPROTONOSUPPORT)) queue_work(gb_operation_workqueue, &operation->work); @@ -793,7 +793,7 @@ static void gb_connection_recv_request(struct gb_connection *connection, operation = gb_operation_create_incoming(connection, operation_id, type, data, size); if (!operation) { - gb_connection_err(connection, "can't create operation"); + dev_err(&connection->dev, "can't create operation\n"); return; /* XXX Respond with pre-allocated ENOMEM */ } @@ -830,14 +830,14 @@ static void gb_connection_recv_response(struct gb_connection *connection, operation = gb_operation_find(connection, operation_id); if (!operation) { - gb_connection_err(connection, "operation not found"); + dev_err(&connection->dev, "operation not found\n"); return; } message = operation->response; message_size = sizeof(*message->header) + message->payload_size; if (!errno && size != message_size) { - gb_connection_err(connection, "bad message size (%zu != %zu)", + dev_err(&connection->dev, "bad message size (%zu != %zu)\n", size, message_size); errno = -EMSGSIZE; } @@ -865,20 +865,20 @@ void gb_connection_recv(struct gb_connection *connection, u16 operation_id; if (connection->state != GB_CONNECTION_STATE_ENABLED) { - gb_connection_err(connection, "dropping %zu received bytes", + dev_err(&connection->dev, "dropping %zu received bytes\n", size); return; } if (size < sizeof(*header)) { - gb_connection_err(connection, "message too small"); + dev_err(&connection->dev, "message too small\n"); return; } header = data; msg_size = (size_t)le16_to_cpu(header->size); if (msg_size > size) { - gb_connection_err(connection, "incomplete message"); + dev_err(&connection->dev, "incomplete message\n"); return; /* XXX Should still complete operation */ } diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index ad0c179e51f5..639c9cdac516 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -143,9 +143,8 @@ gb_spi_operation_create(struct gb_connection *connection, /* Find number of transfers queued and tx/rx length in the message */ list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!xfer->tx_buf && !xfer->rx_buf) { - gb_connection_err(connection, - "Bufferless transfer, length %u\n", - xfer->len); + dev_err(&connection->dev, + "bufferless transfer, length %u\n", xfer->len); return NULL; } @@ -160,8 +159,8 @@ gb_spi_operation_create(struct gb_connection *connection, /* Too many transfers ? */ if (count > (u32)U16_MAX) { - gb_connection_err(connection, "transfer count (%u) too big", - count); + dev_err(&connection->dev, "transfer count (%u) too big\n", + count); return NULL; } @@ -382,7 +381,7 @@ static int gb_spi_connection_init(struct gb_connection *connection) /* Allocate master with space for data */ master = spi_alloc_master(&connection->dev, sizeof(*spi)); if (!master) { - gb_connection_err(connection, "cannot alloc SPI master\n"); + dev_err(&connection->dev, "cannot alloc SPI master\n"); return -ENOMEM; } -- cgit v1.2.3-59-g8ed1b From ee8f81b0961fa240ddf4d7740ac501606e4ed230 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:46:18 +0100 Subject: greybus: operation: use dev_err in gb_operation_sync Use the more informative dev_err in gb_operation_sync, which includes the connection device name in the error message (which in turn encodes the module, interface, bundle and cport ids). Add missing braces to conditional-construct branches while at it. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 83359dde1a8f..c85fd401546c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -945,12 +945,15 @@ int gb_operation_sync(struct gb_connection *connection, int type, memcpy(operation->request->payload, request, request_size); ret = gb_operation_request_send_sync(operation); - if (ret) - pr_err("synchronous operation failed (%d)\n", ret); - else - if (response_size) + if (ret) { + dev_err(&connection->dev, "synchronous operation failed: %d\n", + ret); + } else { + if (response_size) { memcpy(response, operation->response->payload, response_size); + } + } gb_operation_destroy(operation); return ret; -- cgit v1.2.3-59-g8ed1b From c0d209a07beb3550203bfa237d27433a1fbc9cee Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:46:19 +0100 Subject: greybus: operation: remove unnecessary cast Remove unnecessary cast of the message size in gb_connection_recv. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c85fd401546c..d5fa2f04642d 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -876,7 +876,7 @@ void gb_connection_recv(struct gb_connection *connection, } header = data; - msg_size = (size_t)le16_to_cpu(header->size); + msg_size = le16_to_cpu(header->size); if (msg_size > size) { dev_err(&connection->dev, "incomplete message\n"); return; /* XXX Should still complete operation */ -- cgit v1.2.3-59-g8ed1b From c2a66106867343b5c5f017ccc724ba1c1aa9b540 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:51:09 +0100 Subject: greybus: gpio: fix truncated debounce times Fix set_debounce, which silently truncated the time argument to 255us even though we support 16-bit values. Also remove the unnecessary explicit cast when verifying the argument. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 458565a7afb8..f75dd40033af 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -543,9 +543,9 @@ static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, if (offset >= chip->ngpio) return -EINVAL; - if (debounce > (unsigned int)U16_MAX) + if (debounce > U16_MAX) return -EINVAL; - usec = (u8)debounce; + usec = (u16)debounce; ret = gb_gpio_set_debounce_operation(gb_gpio_controller, (u8)offset, usec); if (ret) ; /* return ret; */ -- cgit v1.2.3-59-g8ed1b From 56c2da1873d68b96458064a306e5bb4c50ccdb5d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:51:10 +0100 Subject: greybus: gpio: remove incorrect todo comments The module reference count is incremented by gpiolib when a gpio is requested, and the driver callbacks certainly do sleep. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index f75dd40033af..6a493d4758b4 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -747,7 +747,7 @@ static int gb_gpio_connection_init(struct gb_connection *connection) gpio->label = "greybus_gpio"; gpio->dev = &connection->dev; - gpio->owner = THIS_MODULE; /* XXX Module get? */ + gpio->owner = THIS_MODULE; gpio->request = gb_gpio_request; gpio->free = gb_gpio_free; @@ -761,7 +761,7 @@ static int gb_gpio_connection_init(struct gb_connection *connection) gpio->to_irq = gb_gpio_to_irq; gpio->base = -1; /* Allocate base dynamically */ gpio->ngpio = gb_gpio_controller->line_max + 1; - gpio->can_sleep = true; /* XXX */ + gpio->can_sleep = true; ret = gpiochip_add(gpio); if (ret) { -- cgit v1.2.3-59-g8ed1b From 65f5a5f1614d1a3dab375588f5fd03590c87215b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:51:11 +0100 Subject: greybus: gpio: remove redundant argument verification Remove redundant verification of gpio numbers (which have already been verified in the gpio-chip callbacks) from greybus-operation helpers. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 6a493d4758b4..a1bf1430fced 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -167,9 +167,6 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) struct gb_gpio_activate_request request; int ret; - if (which > ggc->line_max) - return -EINVAL; - request.which = which; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_ACTIVATE, &request, sizeof(request), NULL, 0); @@ -184,9 +181,6 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, struct gb_gpio_deactivate_request request; int ret; - if (which > ggc->line_max) - return -EINVAL; - request.which = which; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DEACTIVATE, &request, sizeof(request), NULL, 0); @@ -203,9 +197,6 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, int ret; u8 direction; - if (which > ggc->line_max) - return -EINVAL; - request.which = which; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_DIRECTION, &request, sizeof(request), @@ -227,9 +218,6 @@ static int gb_gpio_direction_in_operation(struct gb_gpio_controller *ggc, struct gb_gpio_direction_in_request request; int ret; - if (which > ggc->line_max) - return -EINVAL; - request.which = which; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_IN, &request, sizeof(request), NULL, 0); @@ -244,9 +232,6 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, struct gb_gpio_direction_out_request request; int ret; - if (which > ggc->line_max) - return -EINVAL; - request.which = which; request.value = value_high ? 1 : 0; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_OUT, @@ -264,9 +249,6 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, int ret; u8 value; - if (which > ggc->line_max) - return -EINVAL; - request.which = which; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_VALUE, &request, sizeof(request), @@ -288,9 +270,6 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, struct gb_gpio_set_value_request request; int ret; - if (which > ggc->line_max) - return -EINVAL; - request.which = which; request.value = value_high ? 1 : 0; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, @@ -308,9 +287,6 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, struct gb_gpio_set_debounce_request request; int ret; - if (which > ggc->line_max) - return -EINVAL; - request.which = which; request.usec = cpu_to_le16(debounce_usec); ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_DEBOUNCE, -- cgit v1.2.3-59-g8ed1b From 83d9cddb85a0cc7497012846567d16add52916d4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:51:12 +0100 Subject: greybus: gpio: remove overly defensive argument verification Remove overly defensive argument verification in gpio-chip callbacks. We should trust gpiolib to get this right (or we would not even get any callback) just like the other gpio drivers do. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index a1bf1430fced..8384ad17e9be 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -415,8 +415,6 @@ static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); int ret; - if (offset >= chip->ngpio) - return -EINVAL; ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); if (ret) ; /* return ret; */ @@ -428,11 +426,6 @@ static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); int ret; - if (offset >= chip->ngpio) { - pr_err("bad offset %u supplied (must be 0..%u)\n", - offset, chip->ngpio - 1); - return; - } ret = gb_gpio_deactivate_operation(gb_gpio_controller, (u8)offset); if (ret) ; /* return ret; */ @@ -444,8 +437,6 @@ static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) u8 which; int ret; - if (offset >= chip->ngpio) - return -EINVAL; which = (u8)offset; ret = gb_gpio_get_direction_operation(gb_gpio_controller, which); if (ret) @@ -458,8 +449,6 @@ static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); int ret; - if (offset >= chip->ngpio) - return -EINVAL; ret = gb_gpio_direction_in_operation(gb_gpio_controller, (u8)offset); if (ret) ; /* return ret; */ @@ -472,8 +461,6 @@ static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); int ret; - if (offset >= chip->ngpio) - return -EINVAL; ret = gb_gpio_direction_out_operation(gb_gpio_controller, (u8)offset, !!value); if (ret) ; /* return ret; */ @@ -486,8 +473,6 @@ static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) u8 which; int ret; - if (offset >= chip->ngpio) - return -EINVAL; which = (u8)offset; ret = gb_gpio_get_value_operation(gb_gpio_controller, which); if (ret) @@ -500,11 +485,6 @@ static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); int ret; - if (offset < 0 || offset >= chip->ngpio) { - pr_err("bad offset %u supplied (must be 0..%u)\n", - offset, chip->ngpio - 1); - return; - } ret = gb_gpio_set_value_operation(gb_gpio_controller, (u8)offset, !!value); if (ret) ; /* return ret; */ @@ -517,8 +497,6 @@ static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, u16 usec; int ret; - if (offset >= chip->ngpio) - return -EINVAL; if (debounce > U16_MAX) return -EINVAL; usec = (u16)debounce; -- cgit v1.2.3-59-g8ed1b From bda7e2d1126f20c57f175c37c26c42b41ee4b64f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:51:13 +0100 Subject: greybus: gpio: remove unnecessary explicit cast Remove unnecessary explicit cast of line value. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 8384ad17e9be..e0a871d37655 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -477,7 +477,7 @@ static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) ret = gb_gpio_get_value_operation(gb_gpio_controller, which); if (ret) return ret; - return (int)gb_gpio_controller->lines[which].value; + return gb_gpio_controller->lines[which].value; } static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -- cgit v1.2.3-59-g8ed1b From 8aff1aceb28dd692a35f628479357896551ab4e9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:51:14 +0100 Subject: greybus: gpio: make gb_gpio_controller pointer naming consistent Rename all struct gb_gpio_controller-pointer variables "ggc" for consistency and readability reason. This also fixes a bunch of lines that exceeded the 80 columns limit. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 86 +++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index e0a871d37655..d19427409696 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -412,10 +412,10 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) { - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); int ret; - ret = gb_gpio_activate_operation(gb_gpio_controller, (u8)offset); + ret = gb_gpio_activate_operation(ggc, (u8)offset); if (ret) ; /* return ret; */ return 0; @@ -423,33 +423,33 @@ static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) { - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); int ret; - ret = gb_gpio_deactivate_operation(gb_gpio_controller, (u8)offset); + ret = gb_gpio_deactivate_operation(ggc, (u8)offset); if (ret) ; /* return ret; */ } static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); u8 which; int ret; which = (u8)offset; - ret = gb_gpio_get_direction_operation(gb_gpio_controller, which); + ret = gb_gpio_get_direction_operation(ggc, which); if (ret) ; /* return ret; */ - return gb_gpio_controller->lines[which].direction ? 1 : 0; + return ggc->lines[which].direction ? 1 : 0; } static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); int ret; - ret = gb_gpio_direction_in_operation(gb_gpio_controller, (u8)offset); + ret = gb_gpio_direction_in_operation(ggc, (u8)offset); if (ret) ; /* return ret; */ return 0; @@ -458,10 +458,10 @@ static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); int ret; - ret = gb_gpio_direction_out_operation(gb_gpio_controller, (u8)offset, !!value); + ret = gb_gpio_direction_out_operation(ggc, (u8)offset, !!value); if (ret) ; /* return ret; */ return 0; @@ -469,23 +469,23 @@ static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) { - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); u8 which; int ret; which = (u8)offset; - ret = gb_gpio_get_value_operation(gb_gpio_controller, which); + ret = gb_gpio_get_value_operation(ggc, which); if (ret) return ret; - return gb_gpio_controller->lines[which].value; + return ggc->lines[which].value; } static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); int ret; - ret = gb_gpio_set_value_operation(gb_gpio_controller, (u8)offset, !!value); + ret = gb_gpio_set_value_operation(ggc, (u8)offset, !!value); if (ret) ; /* return ret; */ } @@ -493,14 +493,14 @@ static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, unsigned debounce) { - struct gb_gpio_controller *gb_gpio_controller = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); u16 usec; int ret; if (debounce > U16_MAX) return -EINVAL; usec = (u16)debounce; - ret = gb_gpio_set_debounce_operation(gb_gpio_controller, (u8)offset, usec); + ret = gb_gpio_set_debounce_operation(ggc, (u8)offset, usec); if (ret) ; /* return ret; */ @@ -512,25 +512,25 @@ static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) return; /* XXX */ } -static int gb_gpio_controller_setup(struct gb_gpio_controller *gb_gpio_controller) +static int gb_gpio_controller_setup(struct gb_gpio_controller *ggc) { u32 line_count; size_t size; int ret; /* First thing we need to do is check the version */ - ret = get_version(gb_gpio_controller); + ret = get_version(ggc); if (ret) ; /* return ret; */ /* Now find out how many lines there are */ - ret = gb_gpio_line_count_operation(gb_gpio_controller); + ret = gb_gpio_line_count_operation(ggc); if (ret) ; /* return ret; */ - line_count = (u32)gb_gpio_controller->line_max + 1; - size = line_count * sizeof(*gb_gpio_controller->lines); - gb_gpio_controller->lines = kzalloc(size, GFP_KERNEL); - if (!gb_gpio_controller->lines) + line_count = (u32)ggc->line_max + 1; + size = line_count * sizeof(*ggc->lines); + ggc->lines = kzalloc(size, GFP_KERNEL); + if (!ggc->lines) return -ENOMEM; return ret; @@ -675,29 +675,29 @@ static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) static int gb_gpio_connection_init(struct gb_connection *connection) { - struct gb_gpio_controller *gb_gpio_controller; + struct gb_gpio_controller *ggc; struct gpio_chip *gpio; struct irq_chip *irqc; int ret; - gb_gpio_controller = kzalloc(sizeof(*gb_gpio_controller), GFP_KERNEL); - if (!gb_gpio_controller) + ggc = kzalloc(sizeof(*ggc), GFP_KERNEL); + if (!ggc) return -ENOMEM; - gb_gpio_controller->connection = connection; - connection->private = gb_gpio_controller; + ggc->connection = connection; + connection->private = ggc; - ret = gb_gpio_controller_setup(gb_gpio_controller); + ret = gb_gpio_controller_setup(ggc); if (ret) goto err_free_controller; - irqc = &gb_gpio_controller->irqc; + irqc = &ggc->irqc; irqc->irq_ack = gb_gpio_ack_irq; irqc->irq_mask = gb_gpio_mask_irq; irqc->irq_unmask = gb_gpio_unmask_irq; irqc->irq_set_type = gb_gpio_irq_set_type; irqc->name = "greybus_gpio"; - gpio = &gb_gpio_controller->chip; + gpio = &ggc->chip; gpio->label = "greybus_gpio"; gpio->dev = &connection->dev; @@ -714,7 +714,7 @@ static int gb_gpio_connection_init(struct gb_connection *connection) gpio->dbg_show = gb_gpio_dbg_show; gpio->to_irq = gb_gpio_to_irq; gpio->base = -1; /* Allocate base dynamically */ - gpio->ngpio = gb_gpio_controller->line_max + 1; + gpio->ngpio = ggc->line_max + 1; gpio->can_sleep = true; ret = gpiochip_add(gpio); @@ -735,24 +735,24 @@ static int gb_gpio_connection_init(struct gb_connection *connection) irqchip_err: gb_gpiochip_remove(gpio); err_free_lines: - kfree(gb_gpio_controller->lines); + kfree(ggc->lines); err_free_controller: - kfree(gb_gpio_controller); + kfree(ggc); return ret; } static void gb_gpio_connection_exit(struct gb_connection *connection) { - struct gb_gpio_controller *gb_gpio_controller = connection->private; + struct gb_gpio_controller *ggc = connection->private; - if (!gb_gpio_controller) + if (!ggc) return; - gb_gpio_irqchip_remove(gb_gpio_controller); - gb_gpiochip_remove(&gb_gpio_controller->chip); - /* kref_put(gb_gpio_controller->connection) */ - kfree(gb_gpio_controller->lines); - kfree(gb_gpio_controller); + gb_gpio_irqchip_remove(ggc); + gb_gpiochip_remove(&ggc->chip); + /* kref_put(ggc->connection) */ + kfree(ggc->lines); + kfree(ggc); } static struct gb_protocol gpio_protocol = { -- cgit v1.2.3-59-g8ed1b From 86c6816158d79228162b6dc2e759e3769dd3ab09 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:51:15 +0100 Subject: greybus: gpio: fix error handling Make sure to propagate any errors detected up the call chain. This specifically means that we will detect failed connection init, something which is now handled more gracefully by greybus core. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 49 ++++++++++++------------------------------ 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index d19427409696..acc8f395fc6b 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -175,7 +175,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) return ret; } -static int gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, +static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, u8 which) { struct gb_gpio_deactivate_request request; @@ -186,7 +186,6 @@ static int gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, &request, sizeof(request), NULL, 0); if (!ret) ggc->lines[which].active = false; - return ret; } static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, @@ -264,7 +263,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, return 0; } -static int gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, +static void gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, u8 which, bool value_high) { struct gb_gpio_set_value_request request; @@ -278,7 +277,6 @@ static int gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, /* XXX should this set direction to out? */ ggc->lines[which].value = request.value; } - return ret; } static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, @@ -413,22 +411,15 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) { struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); - int ret; - ret = gb_gpio_activate_operation(ggc, (u8)offset); - if (ret) - ; /* return ret; */ - return 0; + return gb_gpio_activate_operation(ggc, (u8)offset); } static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) { struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); - int ret; - ret = gb_gpio_deactivate_operation(ggc, (u8)offset); - if (ret) - ; /* return ret; */ + gb_gpio_deactivate_operation(ggc, (u8)offset); } static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) @@ -440,31 +431,24 @@ static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) which = (u8)offset; ret = gb_gpio_get_direction_operation(ggc, which); if (ret) - ; /* return ret; */ + return ret; + return ggc->lines[which].direction ? 1 : 0; } static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); - int ret; - ret = gb_gpio_direction_in_operation(ggc, (u8)offset); - if (ret) - ; /* return ret; */ - return 0; + return gb_gpio_direction_in_operation(ggc, (u8)offset); } static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); - int ret; - ret = gb_gpio_direction_out_operation(ggc, (u8)offset, !!value); - if (ret) - ; /* return ret; */ - return 0; + return gb_gpio_direction_out_operation(ggc, (u8)offset, !!value); } static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -477,17 +461,15 @@ static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) ret = gb_gpio_get_value_operation(ggc, which); if (ret) return ret; + return ggc->lines[which].value; } static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); - int ret; - ret = gb_gpio_set_value_operation(ggc, (u8)offset, !!value); - if (ret) - ; /* return ret; */ + gb_gpio_set_value_operation(ggc, (u8)offset, !!value); } static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, @@ -495,16 +477,12 @@ static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, { struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); u16 usec; - int ret; if (debounce > U16_MAX) return -EINVAL; usec = (u16)debounce; - ret = gb_gpio_set_debounce_operation(ggc, (u8)offset, usec); - if (ret) - ; /* return ret; */ - return 0; /* XXX */ + return gb_gpio_set_debounce_operation(ggc, (u8)offset, usec); } static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -521,12 +499,13 @@ static int gb_gpio_controller_setup(struct gb_gpio_controller *ggc) /* First thing we need to do is check the version */ ret = get_version(ggc); if (ret) - ; /* return ret; */ + return ret; /* Now find out how many lines there are */ ret = gb_gpio_line_count_operation(ggc); if (ret) - ; /* return ret; */ + return ret; + line_count = (u32)ggc->line_max + 1; size = line_count * sizeof(*ggc->lines); ggc->lines = kzalloc(size, GFP_KERNEL); -- cgit v1.2.3-59-g8ed1b From e5032d8511cdb0b7b594863effa2c2a685beaaa5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:51:16 +0100 Subject: greybus: gpio: use dev_err and dev_warn Use the more informative dev_err and dev_warn for messages, and make the messages more uniform. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 49 +++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index acc8f395fc6b..a9153a85b011 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -204,9 +204,11 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, return ret; direction = response.direction; - if (direction && direction != 1) - pr_warn("gpio %u direction was %u (should be 0 or 1)\n", - which, direction); + if (direction && direction != 1) { + dev_warn(ggc->chip.dev, + "gpio %u direction was %u (should be 0 or 1)\n", + which, direction); + } ggc->lines[which].direction = direction ? 1 : 0; return 0; } @@ -256,9 +258,11 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, return ret; value = response.value; - if (value && value != 1) - pr_warn("gpio %u value was %u (should be 0 or 1)\n", - which, value); + if (value && value != 1) { + dev_warn(ggc->chip.dev, + "gpio %u value was %u (should be 0 or 1)\n", + which, value); + } ggc->lines[which].value = value ? 1 : 0; return 0; } @@ -306,7 +310,7 @@ static void gb_gpio_ack_irq(struct irq_data *d) GB_GPIO_TYPE_IRQ_ACK, &request, sizeof(request), NULL, 0); if (ret) - pr_err("irq ack operation failed (%d)\n", ret); + dev_err(chip->dev, "failed to ack irq: %d\n", ret); } static void gb_gpio_mask_irq(struct irq_data *d) @@ -321,7 +325,7 @@ static void gb_gpio_mask_irq(struct irq_data *d) GB_GPIO_TYPE_IRQ_MASK, &request, sizeof(request), NULL, 0); if (ret) - pr_err("irq mask operation failed (%d)\n", ret); + dev_err(chip->dev, "failed to mask irq: %d\n", ret); } static void gb_gpio_unmask_irq(struct irq_data *d) @@ -336,7 +340,7 @@ static void gb_gpio_unmask_irq(struct irq_data *d) GB_GPIO_TYPE_IRQ_UNMASK, &request, sizeof(request), NULL, 0); if (ret) - pr_err("irq unmask operation failed (%d)\n", ret); + dev_err(chip->dev, "failed to unmask irq: %d\n", ret); } static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) @@ -360,11 +364,13 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_IRQ_TYPE, &request, sizeof(request), NULL, 0); - if (ret) - pr_err("irq type operation failed (%d)\n", ret); + if (ret) { + dev_err(chip->dev, "failed to set irq type: %d\n", + ret); + } break; default: - pr_err("No such irq type %d", type); + dev_err(chip->dev, "unsupported irq type: %u\n", type); ret = -EINVAL; } @@ -374,7 +380,7 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) static void gb_gpio_request_recv(u8 type, struct gb_operation *op) { struct gb_gpio_controller *ggc; - struct gb_connection *connection; + struct gb_connection *connection = op->connection; struct gb_message *request; struct gb_gpio_irq_event_request *event; int irq; @@ -382,17 +388,17 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) int ret; if (type != GB_GPIO_TYPE_IRQ_EVENT) { - pr_err("unsupported unsolicited request\n"); + dev_err(&connection->dev, + "unsupported unsolicited request: %u\n", type); return; } - connection = op->connection; ggc = connection->private; request = op->request; event = request->payload; if (event->which > ggc->line_max) { - pr_err("Unsupported hw irq %d\n", event->which); + dev_err(ggc->chip.dev, "invalid hw irq: %d\n", event->which); return; } irq = gpio_to_irq(ggc->chip.base + event->which); @@ -404,8 +410,10 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) local_irq_enable(); ret = gb_operation_response_send(op, 0); - if (ret) - pr_err("error %d sending response status %d\n", ret, 0); + if (ret) { + dev_err(ggc->chip.dev, + "failed to send response status %d: %d\n", 0, ret); + } } static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) @@ -698,14 +706,15 @@ static int gb_gpio_connection_init(struct gb_connection *connection) ret = gpiochip_add(gpio); if (ret) { - pr_err("Failed to register GPIO\n"); + dev_err(&connection->dev, "failed to add gpio chip: %d\n", + ret); goto err_free_lines; } ret = gb_gpio_irqchip_add(gpio, irqc, 0, handle_simple_irq, IRQ_TYPE_NONE); if (ret) { - pr_err("Couldn't add irqchip to Greybus GPIO controller %d\n", ret); + dev_err(&connection->dev, "failed to add irq chip: %d\n", ret); goto irqchip_err; } -- cgit v1.2.3-59-g8ed1b From 64d2f4e5f94a95b23532efcbc3c955b5086bd890 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:51:17 +0100 Subject: greybus: gpio: clean up line-state allocation Clean up allocation of line-state array. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index a9153a85b011..b18fb7e12a5b 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -500,8 +500,6 @@ static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) static int gb_gpio_controller_setup(struct gb_gpio_controller *ggc) { - u32 line_count; - size_t size; int ret; /* First thing we need to do is check the version */ @@ -514,9 +512,8 @@ static int gb_gpio_controller_setup(struct gb_gpio_controller *ggc) if (ret) return ret; - line_count = (u32)ggc->line_max + 1; - size = line_count * sizeof(*ggc->lines); - ggc->lines = kzalloc(size, GFP_KERNEL); + ggc->lines = kcalloc(ggc->line_max + 1, sizeof(*ggc->lines), + GFP_KERNEL); if (!ggc->lines) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 7bfa0781406f9df6cb20ce5134faa19d34cb4e18 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:55:22 +0100 Subject: greybus: gpio: refuse to set value of input pins Add warning and refuse to set output value for pin configured as input, as the result of such an operation is undefined. Remove incorrect todo-comment suggesting that the driver could implicitly switch direction as part of the call. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index b18fb7e12a5b..7e51840cd262 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -273,14 +273,18 @@ static void gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, struct gb_gpio_set_value_request request; int ret; + if (ggc->lines[which].direction == 1) { + dev_warn(ggc->chip.dev, + "refusing to set value of input gpio %u\n", which); + return; + } + request.which = which; request.value = value_high ? 1 : 0; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, &request, sizeof(request), NULL, 0); - if (!ret) { - /* XXX should this set direction to out? */ + if (!ret) ggc->lines[which].value = request.value; - } } static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, -- cgit v1.2.3-59-g8ed1b From 792c17e64383340a75579cdfc6b9b6d2b1647b43 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Mar 2015 16:55:23 +0100 Subject: greybus: gpio: add error messages to callbacks not propagating errors Add error messages on failures to deactivate, set and get operation handlers as any errors would not be detected by the upper layers (either because the callbacks are declared void or expected to return a boolean value). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 7e51840cd262..2bac28ec7f85 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -184,8 +184,13 @@ static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, request.which = which; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DEACTIVATE, &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].active = false; + if (ret) { + dev_err(ggc->chip.dev, "failed to deactivate gpio %u\n", + which); + return; + } + + ggc->lines[which].active = false; } static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, @@ -254,8 +259,11 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_VALUE, &request, sizeof(request), &response, sizeof(response)); - if (ret) + if (ret) { + dev_err(ggc->chip.dev, "failed to get value of gpio %u\n", + which); return ret; + } value = response.value; if (value && value != 1) { @@ -283,8 +291,13 @@ static void gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, request.value = value_high ? 1 : 0; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].value = request.value; + if (ret) { + dev_err(ggc->chip.dev, "failed to set value of gpio %u\n", + which); + return; + } + + ggc->lines[which].value = request.value; } static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, -- cgit v1.2.3-59-g8ed1b From 337b068722a21cbbd4c5e925f0ac90209b44a8a8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 20 Mar 2015 20:29:13 +0530 Subject: greybus: core: Don't initialize greybus if it is disabled Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 15408ec61dc3..a25df369d2b9 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -207,6 +207,9 @@ static int __init gb_init(void) { int retval; + if (greybus_disabled()) + return -ENODEV; + BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD); retval = gb_debugfs_init(); -- cgit v1.2.3-59-g8ed1b From 59e3344461baeab1a605543aec0ff9ddf209b20f Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 23 Mar 2015 17:52:37 +0100 Subject: greybus: Dump log from APB1 On AP module (form factor), we don't have access to APBridge JTAG or UART. But sometime, we still need to get log from APBridge. Add a new request in control endpoint to get APBridge logs. Logs can be accessed using debugfs (greybus/apb1_log). Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 136 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index bbf1bd1048be..9ad8a76d33ac 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -8,10 +8,14 @@ */ #include #include +#include #include #include #include #include +#include +#include +#include #include "greybus.h" #include "svc_msg.h" @@ -35,6 +39,12 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); +#define APB1_LOG_SIZE SZ_16K +static struct dentry *apb1_log_dentry; +static struct dentry *apb1_log_enable_dentry; +static struct task_struct *apb1_log_task; +static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); + /* * Number of CPort IN urbs in flight at any point in time. * Adjust if we are having stalls in the USB buffer due to not enough urbs in @@ -91,6 +101,7 @@ static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) } static void cport_out_callback(struct urb *urb); +static void usb_log_enable(struct es1_ap_dev *es1, int enable); /* * Buffer constraints for the host driver. @@ -326,6 +337,8 @@ static void ap_disconnect(struct usb_interface *interface) if (!es1) return; + usb_log_enable(es1, 0); + /* Tear down everything! */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { struct urb *urb = es1->cport_out_urb[i]; @@ -474,6 +487,125 @@ static void cport_out_callback(struct urb *urb) */ } +static void apb1_log_get(struct es1_ap_dev *es1) +{ + char buf[65]; + int retval; + + /* SVC messages go down our control pipe */ + do { + memset(buf, 0, 65); + retval = usb_control_msg(es1->usb_dev, + usb_rcvctrlpipe(es1->usb_dev, + es1->control_endpoint), + 0x02, /* vendor request APB1 log */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + buf, + 64, + ES1_TIMEOUT); + if (retval > 0) + kfifo_in(&apb1_log_fifo, buf, retval); + } while (retval > 0); +} + +static int apb1_log_poll(void *data) +{ + while (!kthread_should_stop()) { + msleep(1000); + apb1_log_get((struct es1_ap_dev *)data); + } + return 0; +} + +static ssize_t apb1_log_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t ret; + size_t copied; + char *tmp_buf; + + if (count > APB1_LOG_SIZE) + count = APB1_LOG_SIZE; + + tmp_buf = kmalloc(count, GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + + copied = kfifo_out(&apb1_log_fifo, tmp_buf, count); + ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied); + + kfree(tmp_buf); + + return ret; +} + +static struct file_operations apb1_log_fops = { + .read = apb1_log_read, +}; + +static void usb_log_enable(struct es1_ap_dev *es1, int enable) +{ + if (enable && apb1_log_task != NULL) + return; + + if (enable) { + /* get log from APB1 */ + apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); + if (apb1_log_task == ERR_PTR(-ENOMEM)) + return; + apb1_log_dentry = debugfs_create_file("apb1_log", 444, + gb_debugfs_get(), NULL, + &apb1_log_fops); + } else { + if (apb1_log_dentry) { + debugfs_remove(apb1_log_dentry); + apb1_log_dentry = NULL; + } + + if (apb1_log_task) { + kthread_stop(apb1_log_task); + apb1_log_task = NULL; + } + } +} + +static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + char tmp_buf[3]; + int enable = apb1_log_task != NULL; + sprintf(tmp_buf, "%d\n", enable); + return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); +} + +static ssize_t apb1_log_enable_write(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + int enable; + char *tmp_buf; + ssize_t retval = -EINVAL; + struct es1_ap_dev *es1 = (struct es1_ap_dev *)f->f_inode->i_private; + + tmp_buf = kmalloc(count, GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + + copy_from_user(tmp_buf, buf, count); + if (sscanf(tmp_buf, "%d", &enable) == 1) { + usb_log_enable(es1, enable); + retval = count; + } + kfree(tmp_buf); + + return retval; +} + +static struct file_operations apb1_log_enable_fops = { + .read = apb1_log_enable_read, + .write = apb1_log_enable_write, +}; + /* * The ES1 USB Bridge device contains 4 endpoints * 1 Control - usual USB stuff + AP -> SVC messages @@ -600,6 +732,10 @@ static int ap_probe(struct usb_interface *interface, if (retval) goto error; + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", 666, + gb_debugfs_get(), es1, + &apb1_log_enable_fops); + return 0; error: ap_disconnect(interface); -- cgit v1.2.3-59-g8ed1b From 6c28f09658483dcfb1a7f2adc8160982c652bc83 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 19:55:14 +0100 Subject: greybus: es1: fix build warning for apb1_log_enable_write It's "const char __user *buf", not "char __user *buf". 'make check' is your friend. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 9ad8a76d33ac..8de005ba1e8d 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -579,7 +579,7 @@ static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); } -static ssize_t apb1_log_enable_write(struct file *f, char __user *buf, +static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, size_t count, loff_t *ppos) { int enable; -- cgit v1.2.3-59-g8ed1b From 2f4e236648d9c7f622518ec0098cd1cb6af21659 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 20:03:39 +0100 Subject: greybus: es1: fix tiny whitespace issues No trailing spaces or spaces before tabs are allowed. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 8de005ba1e8d..4524add9c868 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -129,7 +129,7 @@ static void usb_log_enable(struct es1_ap_dev *es1, int enable); * host driver. I.e., ((char *)buffer - headroom) must * point to valid memory, usable only by the host driver. * size_max: The maximum size of a buffer (not including the - * headroom) must not exceed this. + * headroom) must not exceed this. */ static void hd_buffer_constraints(struct greybus_host_device *hd) { @@ -597,7 +597,7 @@ static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, retval = count; } kfree(tmp_buf); - + return retval; } -- cgit v1.2.3-59-g8ed1b From cd674c8d4c3a1a9736ed1e9f8ec8f9ee2aaa26e0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 20:04:49 +0100 Subject: greybus: es1: use and not Asm is only for when you are doing arch-specific stuff, which we aren't doing here. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 4524add9c868..cce31558573b 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include "greybus.h" #include "svc_msg.h" -- cgit v1.2.3-59-g8ed1b From 26164edb8fb467a4249fab159709a6cfc50ddc8c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 20:06:41 +0100 Subject: greybus: es1: no need to check for NULL on debugfs_remove() The function can, and even expects NULL, so don't check. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index cce31558573b..723d8b7a0eab 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -558,10 +558,8 @@ static void usb_log_enable(struct es1_ap_dev *es1, int enable) gb_debugfs_get(), NULL, &apb1_log_fops); } else { - if (apb1_log_dentry) { - debugfs_remove(apb1_log_dentry); - apb1_log_dentry = NULL; - } + debugfs_remove(apb1_log_dentry); + apb1_log_dentry = NULL; if (apb1_log_task) { kthread_stop(apb1_log_task); -- cgit v1.2.3-59-g8ed1b From 4600d03f80aea65f29f324b89d1b3437982f3eba Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 20:08:12 +0100 Subject: greybus: es1: struct file_operations needs to be const We aren't changing these pointers, so mark them read-only as that is the preferred way. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 723d8b7a0eab..7e612cbd813e 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -540,7 +540,7 @@ static ssize_t apb1_log_read(struct file *f, char __user *buf, return ret; } -static struct file_operations apb1_log_fops = { +static const struct file_operations apb1_log_fops = { .read = apb1_log_read, }; @@ -599,7 +599,7 @@ static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, return retval; } -static struct file_operations apb1_log_enable_fops = { +static const struct file_operations apb1_log_enable_fops = { .read = apb1_log_enable_read, .write = apb1_log_enable_write, }; -- cgit v1.2.3-59-g8ed1b From d52f9973e2de01e6da7beb2e91ece009ffd880a2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 20:10:58 +0100 Subject: greybus: es1: decimal modes are not what are wanted for debugfs decimal is not octal, so use the proper mode settings for the debugfs files. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 7e612cbd813e..796c2ab5dafd 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -554,7 +554,7 @@ static void usb_log_enable(struct es1_ap_dev *es1, int enable) apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); if (apb1_log_task == ERR_PTR(-ENOMEM)) return; - apb1_log_dentry = debugfs_create_file("apb1_log", 444, + apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, gb_debugfs_get(), NULL, &apb1_log_fops); } else { @@ -730,7 +730,8 @@ static int ap_probe(struct usb_interface *interface, if (retval) goto error; - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", 666, + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", + (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, &apb1_log_enable_fops); -- cgit v1.2.3-59-g8ed1b From 64b8a16f4486912e4aa315497ea13c2d57402fc3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 20:32:40 +0100 Subject: greybus: es1: move debugfs function to use kstrotoint_from_user() No need to duplicate built-in functions that the kernel has, so have the core kernel parse the userspace string. Saves us an allocation and makes the logic simpler. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 796c2ab5dafd..a7fb4b52991b 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -581,20 +581,19 @@ static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, size_t count, loff_t *ppos) { int enable; - char *tmp_buf; - ssize_t retval = -EINVAL; + ssize_t retval; struct es1_ap_dev *es1 = (struct es1_ap_dev *)f->f_inode->i_private; - tmp_buf = kmalloc(count, GFP_KERNEL); - if (!tmp_buf) - return -ENOMEM; + retval = kstrtoint_from_user(buf, count, 10, &enable); + if (retval) + return retval; - copy_from_user(tmp_buf, buf, count); - if (sscanf(tmp_buf, "%d", &enable) == 1) { + if (enable) { usb_log_enable(es1, enable); retval = count; + } else { + retval = -EINVAL; } - kfree(tmp_buf); return retval; } -- cgit v1.2.3-59-g8ed1b From efdc43130cdb3dbcd25d1586e93181abbc714ea5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 20:34:02 +0100 Subject: greybus: es1: fix checkpatch warning about blank lines needed Add a blank line in apb1_log_enable_read() to make checkpatch happy. Oh, and it makes the code more readable too... Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index a7fb4b52991b..225ef3ff1181 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -573,6 +573,7 @@ static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, { char tmp_buf[3]; int enable = apb1_log_task != NULL; + sprintf(tmp_buf, "%d\n", enable); return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); } -- cgit v1.2.3-59-g8ed1b From 0c264c6fc2c3f47d94169a38506cdf151a9ba52e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 20:45:31 +0100 Subject: greybus: es1: separate usb_log enable/disable logic into different functions One function shouldn't do two different things depending on a parameter passed to it, so split usb_log_enable() into usb_log_disable() Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 45 +++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 225ef3ff1181..53d2d470e593 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -101,7 +101,8 @@ static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) } static void cport_out_callback(struct urb *urb); -static void usb_log_enable(struct es1_ap_dev *es1, int enable); +static void usb_log_enable(struct es1_ap_dev *es1); +static void usb_log_disable(struct es1_ap_dev *es1); /* * Buffer constraints for the host driver. @@ -337,7 +338,7 @@ static void ap_disconnect(struct usb_interface *interface) if (!es1) return; - usb_log_enable(es1, 0); + usb_log_disable(es1); /* Tear down everything! */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { @@ -544,28 +545,30 @@ static const struct file_operations apb1_log_fops = { .read = apb1_log_read, }; -static void usb_log_enable(struct es1_ap_dev *es1, int enable) +static void usb_log_enable(struct es1_ap_dev *es1) { - if (enable && apb1_log_task != NULL) + if (apb1_log_task != NULL) return; - if (enable) { - /* get log from APB1 */ - apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); - if (apb1_log_task == ERR_PTR(-ENOMEM)) - return; - apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, - gb_debugfs_get(), NULL, - &apb1_log_fops); - } else { - debugfs_remove(apb1_log_dentry); - apb1_log_dentry = NULL; + /* get log from APB1 */ + apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); + if (apb1_log_task == ERR_PTR(-ENOMEM)) + return; + apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, + gb_debugfs_get(), NULL, + &apb1_log_fops); +} - if (apb1_log_task) { - kthread_stop(apb1_log_task); - apb1_log_task = NULL; - } - } +static void usb_log_disable(struct es1_ap_dev *es1) +{ + if (apb1_log_task == NULL) + return; + + debugfs_remove(apb1_log_dentry); + apb1_log_dentry = NULL; + + kthread_stop(apb1_log_task); + apb1_log_task = NULL; } static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, @@ -590,7 +593,7 @@ static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, return retval; if (enable) { - usb_log_enable(es1, enable); + usb_log_enable(es1); retval = count; } else { retval = -EINVAL; -- cgit v1.2.3-59-g8ed1b From 25d1c37798cd298d726dfb114f0dcaa15824b138 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Mar 2015 20:47:24 +0100 Subject: greybus: es1: allow the debug log to be stopped If you write 0 to the debugfs file, the log will stop being updated. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 53d2d470e593..8aad4fbe25e6 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -592,14 +592,12 @@ static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, if (retval) return retval; - if (enable) { + if (enable) usb_log_enable(es1); - retval = count; - } else { - retval = -EINVAL; - } + else + usb_log_disable(es1); - return retval; + return count; } static const struct file_operations apb1_log_enable_fops = { -- cgit v1.2.3-59-g8ed1b From 19b3b2c2aeb7e1e787a65936ada0dabc229620ea Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 24 Mar 2015 17:08:13 +0530 Subject: greybus: manifest: descriptor size should be >= header size We are calculating descriptors expected size differently based on the type of descriptor, that's fine but at few places we aren't taking size of the header into account. And that looks wrong. Lets make sure it is atleast as big as descriptor's header. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 51 +++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 8b61b3486a3d..545a3bd4e751 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -13,6 +13,27 @@ #include "greybus.h" +static const char *get_descriptor_type_string(u8 type) +{ + switch(type) { + case GREYBUS_TYPE_INVALID: + return "invalid"; + case GREYBUS_TYPE_MODULE: + return "module"; + case GREYBUS_TYPE_STRING: + return "string"; + case GREYBUS_TYPE_INTERFACE: + return "interface"; + case GREYBUS_TYPE_CPORT: + return "cport"; + case GREYBUS_TYPE_CLASS: + return "class"; + default: + WARN_ON(1); + return "unknown"; + } +} + /* * We scan the manifest once to identify where all the descriptors * are. The result is a list of these manifest_desc structures. We @@ -72,32 +93,21 @@ static int identify_descriptor(struct gb_interface *intf, return -EINVAL; } + /* Descriptor needs to at least have a header */ + expected_size = sizeof(*desc_header); + switch (desc_header->type) { case GREYBUS_TYPE_MODULE: - if (desc_size < sizeof(struct greybus_descriptor_module)) { - pr_err("module descriptor too small (%u)\n", - desc_size); - return -EINVAL; - } + expected_size += sizeof(struct greybus_descriptor_module); break; case GREYBUS_TYPE_STRING: - expected_size = sizeof(*desc_header); expected_size += sizeof(struct greybus_descriptor_string); - expected_size += (size_t)desc->string.length; - if (desc_size < expected_size) { - pr_err("string descriptor too small (%u)\n", - desc_size); - return -EINVAL; - } + expected_size += desc->string.length; break; case GREYBUS_TYPE_INTERFACE: break; case GREYBUS_TYPE_CPORT: - if (desc_size < sizeof(struct greybus_descriptor_cport)) { - pr_err("cport descriptor too small (%u)\n", - desc_size); - return -EINVAL; - } + expected_size += sizeof(struct greybus_descriptor_cport); break; case GREYBUS_TYPE_CLASS: pr_warn("class descriptor found (ignoring)\n"); @@ -108,6 +118,13 @@ static int identify_descriptor(struct gb_interface *intf, return -EINVAL; } + if (desc_size < expected_size) { + pr_err("%s descriptor too small (%u < %zu)\n", + get_descriptor_type_string(desc_header->type), + desc_size, expected_size); + return -EINVAL; + } + descriptor = kzalloc(sizeof(*descriptor), GFP_KERNEL); if (!descriptor) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 13fe6a9af3d86019a10bdbd3195434e5a795b3b3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 24 Mar 2015 17:08:14 +0530 Subject: greybus: manifest: don't need to check for desc_size == 0 anymore identify_descriptor() doesn't return 0 anymore and so we don't need to check the returned value against 0. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 545a3bd4e751..1b9edbcd08c5 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -383,9 +383,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) int desc_size; desc_size = identify_descriptor(intf, desc, size); - if (desc_size <= 0) { - if (!desc_size) - pr_err("zero-sized manifest descriptor\n"); + if (desc_size < 0) { result = false; goto out; } -- cgit v1.2.3-59-g8ed1b From 0d34be75878093bef5055311c254ae3ff459fd8f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 24 Mar 2015 20:14:28 +0530 Subject: greybus: Greybus: Place module_init/exit() right after respective routines As mentioned in kernel coding guidelines. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpb.c | 3 +-- drivers/staging/greybus/vibrator.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/gpb.c b/drivers/staging/greybus/gpb.c index 931d739f9fb9..33bbe7917f09 100644 --- a/drivers/staging/greybus/gpb.c +++ b/drivers/staging/greybus/gpb.c @@ -72,6 +72,7 @@ error_pwm: error_gpio: return -EPROTO; } +module_init(gpbridge_init); static void __exit gpbridge_exit(void) { @@ -84,8 +85,6 @@ static void __exit gpbridge_exit(void) gb_pwm_protocol_exit(); gb_gpio_protocol_exit(); } - -module_init(gpbridge_init); module_exit(gpbridge_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index c92c69ef3025..2943a9b381c6 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -185,14 +185,13 @@ static __init int protocol_init(void) return gb_protocol_register(&vibrator_protocol); } +module_init(protocol_init); static __exit void protocol_exit(void) { gb_protocol_deregister(&vibrator_protocol); class_unregister(&vibrator_class); } - -module_init(protocol_init); module_exit(protocol_exit); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 71e4938012faff9b84916f5424dbf4dff8d1d09e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 24 Mar 2015 20:14:29 +0530 Subject: greybus: interface: put module->dev on failures In order to decrement the reference count of module on failures, we must call put_device(module->dev). This was missing for one of the error cases, fix it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index d2b2e3df33fa..b687908cfa16 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -132,7 +132,7 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, intf = kzalloc(sizeof(*intf), GFP_KERNEL); if (!intf) - return NULL; + goto put_module; intf->hd = hd; /* XXX refcount? */ intf->module = module; @@ -151,10 +151,7 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, if (retval) { pr_err("failed to add module device for id 0x%02hhx\n", module_id); - put_device(&intf->dev); - put_device(&module->dev); - kfree(intf); - return NULL; + goto free_intf; } spin_lock_irq(&gb_interfaces_lock); @@ -162,6 +159,13 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, spin_unlock_irq(&gb_interfaces_lock); return intf; + +free_intf: + put_device(&intf->dev); + kfree(intf); +put_module: + put_device(&module->dev); + return NULL; } /* -- cgit v1.2.3-59-g8ed1b From d3d2bea16149f36b0b654a0874cb9092ff86b134 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 26 Mar 2015 21:25:01 -0500 Subject: greybus: clean up some small messes This is an old patch that I neglected to send out. It's cleaning up a couple things that got committed before I had a chance to comment on them. In operation.c there is a "FIXME" comment that is easily proven wrong by inspection. In gb_protocol_put(), there is another wrong "FIXME" comment as well. We can also use our cached copies of the protocol major and minor version number in another spot. And balance that out by using a cached copy of the protocol id. Signed-off-by: Alex Elder Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 ++--- drivers/staging/greybus/protocol.c | 14 ++++++-------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d5fa2f04642d..5117f0b08331 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -1,8 +1,8 @@ /* * Greybus operations * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ @@ -732,7 +732,6 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) } } - /* FIXME operation->response could still be NULL here */ /* Fill in the response header and send it */ operation->response->header->result = gb_operation_errno_map(errno); diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 51549e20f398..ba4cc0f4ac8c 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -1,8 +1,8 @@ /* * Greybus protocol handling * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ @@ -191,6 +191,7 @@ EXPORT_SYMBOL_GPL(gb_protocol_get_version); void gb_protocol_put(struct gb_protocol *protocol) { + u8 id; u8 major; u8 minor; u8 protocol_count; @@ -198,12 +199,12 @@ void gb_protocol_put(struct gb_protocol *protocol) if (!protocol) return; + id = protocol->id; major = protocol->major; minor = protocol->minor; spin_lock_irq(&gb_protocols_lock); - protocol = _gb_protocol_find(protocol->id, protocol->major, - protocol->minor); + protocol = _gb_protocol_find(id, major, minor); if (protocol) { protocol_count = protocol->count; if (protocol_count) @@ -214,9 +215,6 @@ void gb_protocol_put(struct gb_protocol *protocol) if (protocol) WARN_ON(!protocol_count); else - /* FIXME a different message is needed since this one - * will result in a NULL dereference - */ pr_err("protocol id %hhu version %hhu.%hhu not found\n", - protocol->id, major, minor); + id, major, minor); } -- cgit v1.2.3-59-g8ed1b From 142f8ddf71e2e081955b4f54ba72c78dbb7b7ce8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 26 Mar 2015 21:25:06 -0500 Subject: greybus: get rid of {conceal,reveal}_urb() These clever macros were fine for early development, but they're more of a distraction now. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 11 ++--------- drivers/staging/greybus/es2.c | 15 ++++----------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 8aad4fbe25e6..239358dce9be 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -21,13 +21,6 @@ #include "svc_msg.h" #include "kernel_ver.h" -/* - * Macros for making pointers explicitly opaque, such that the result - * isn't valid but also can't be mistaken for an ERR_PTR() value. - */ -#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) -#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) - /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE @@ -274,7 +267,7 @@ static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, return ERR_PTR(retval); } - return conceal_urb(urb); + return urb; } /* @@ -292,7 +285,7 @@ static void buffer_cancel(void *cookie) * is valid. For the time being, this will do. */ if (cookie) - usb_kill_urb(reveal_urb(cookie)); + usb_kill_urb(cookie); } static struct greybus_host_driver es1_driver = { diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index d8d45bec4f1d..a73a268c8759 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1,8 +1,8 @@ /* * Greybus "AP" USB driver for "ES2" controller chips * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ @@ -17,13 +17,6 @@ #include "svc_msg.h" #include "kernel_ver.h" -/* - * Macros for making pointers explicitly opaque, such that the result - * isn't valid but also can't be mistaken for an ERR_PTR() value. - */ -#define conceal_urb(urb) ((void *)((uintptr_t)(urb) ^ 0xbad)) -#define reveal_urb(cookie) ((void *)((uintptr_t)(cookie) ^ 0xbad)) - /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE @@ -262,7 +255,7 @@ static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, return ERR_PTR(retval); } - return conceal_urb(urb); + return urb; } /* @@ -280,7 +273,7 @@ static void buffer_cancel(void *cookie) * is valid. For the time being, this will do. */ if (cookie) - usb_kill_urb(reveal_urb(cookie)); + usb_kill_urb(cookie); } static struct greybus_host_driver es1_driver = { -- cgit v1.2.3-59-g8ed1b From 48f70474f48f87cc2dec5ee0a2ba7139bbef3cef Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 27 Mar 2015 11:38:06 +0100 Subject: greybus: debugfs: we shouldn't care if debugfs is working or not This removes the error checking for debugfs initialization as we really don't care if it failed or not. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/core.c | 6 +----- drivers/staging/greybus/debugfs.c | 6 +----- drivers/staging/greybus/greybus.h | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index a25df369d2b9..96265a1d668f 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -212,11 +212,7 @@ static int __init gb_init(void) BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD); - retval = gb_debugfs_init(); - if (retval) { - pr_err("debugfs failed\n"); - return retval; - } + gb_debugfs_init(); retval = bus_register(&greybus_bus_type); if (retval) { diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c index b8865d707362..59c570964b01 100644 --- a/drivers/staging/greybus/debugfs.c +++ b/drivers/staging/greybus/debugfs.c @@ -15,13 +15,9 @@ static struct dentry *gb_debug_root; -int gb_debugfs_init(void) +void gb_debugfs_init(void) { gb_debug_root = debugfs_create_dir("greybus", NULL); - if (!gb_debug_root) - return -ENOENT; - - return 0; } void gb_debugfs_cleanup(void) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 967d64faa8fd..5a7f6227c1e5 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -158,7 +158,7 @@ int greybus_disabled(void); int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); int gb_ap_init(void); void gb_ap_exit(void); -int gb_debugfs_init(void); +void gb_debugfs_init(void); void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); -- cgit v1.2.3-59-g8ed1b From ca3ec299c2d090f99fedcc5995dc1f25ab609408 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 27 Mar 2015 11:38:07 +0100 Subject: greybus: es2: sync up with recent es1.c changes This brings the debug log functionality of es1.c to es2.c, along with some other minor cleanups. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/es2.c | 146 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 142 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index a73a268c8759..e4637500d05c 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -8,10 +8,14 @@ */ #include #include +#include #include #include #include #include +#include +#include +#include #include "greybus.h" #include "svc_msg.h" @@ -28,6 +32,12 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); +#define APB1_LOG_SIZE SZ_16K +static struct dentry *apb1_log_dentry; +static struct dentry *apb1_log_enable_dentry; +static struct task_struct *apb1_log_task; +static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); + /* * Number of CPort IN urbs in flight at any point in time. * Adjust if we are having stalls in the USB buffer due to not enough urbs in @@ -84,6 +94,8 @@ static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) } static void cport_out_callback(struct urb *urb); +static void usb_log_enable(struct es1_ap_dev *es1); +static void usb_log_disable(struct es1_ap_dev *es1); /* * Buffer constraints for the host driver. @@ -111,7 +123,7 @@ static void cport_out_callback(struct urb *urb); * host driver. I.e., ((char *)buffer - headroom) must * point to valid memory, usable only by the host driver. * size_max: The maximum size of a buffer (not including the - * headroom) must not exceed this. + * headroom) must not exceed this. */ static void hd_buffer_constraints(struct greybus_host_device *hd) { @@ -319,6 +331,8 @@ static void ap_disconnect(struct usb_interface *interface) if (!es1) return; + usb_log_disable(es1); + /* Tear down everything! */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { struct urb *urb = es1->cport_out_urb[i]; @@ -467,6 +481,123 @@ static void cport_out_callback(struct urb *urb) */ } +static void apb1_log_get(struct es1_ap_dev *es1) +{ + char buf[65]; + int retval; + + /* SVC messages go down our control pipe */ + do { + memset(buf, 0, 65); + retval = usb_control_msg(es1->usb_dev, + usb_rcvctrlpipe(es1->usb_dev, + es1->control_endpoint), + 0x02, /* vendor request APB1 log */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + buf, + 64, + ES1_TIMEOUT); + if (retval > 0) + kfifo_in(&apb1_log_fifo, buf, retval); + } while (retval > 0); +} + +static int apb1_log_poll(void *data) +{ + while (!kthread_should_stop()) { + msleep(1000); + apb1_log_get((struct es1_ap_dev *)data); + } + return 0; +} + +static ssize_t apb1_log_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t ret; + size_t copied; + char *tmp_buf; + + if (count > APB1_LOG_SIZE) + count = APB1_LOG_SIZE; + + tmp_buf = kmalloc(count, GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + + copied = kfifo_out(&apb1_log_fifo, tmp_buf, count); + ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied); + + kfree(tmp_buf); + + return ret; +} + +static const struct file_operations apb1_log_fops = { + .read = apb1_log_read, +}; + +static void usb_log_enable(struct es1_ap_dev *es1) +{ + if (apb1_log_task != NULL) + return; + + /* get log from APB1 */ + apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); + if (apb1_log_task == ERR_PTR(-ENOMEM)) + return; + apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, + gb_debugfs_get(), NULL, + &apb1_log_fops); +} + +static void usb_log_disable(struct es1_ap_dev *es1) +{ + if (apb1_log_task == NULL) + return; + + debugfs_remove(apb1_log_dentry); + apb1_log_dentry = NULL; + + kthread_stop(apb1_log_task); + apb1_log_task = NULL; +} + +static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + char tmp_buf[3]; + int enable = apb1_log_task != NULL; + + sprintf(tmp_buf, "%d\n", enable); + return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); +} + +static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + int enable; + ssize_t retval; + struct es1_ap_dev *es1 = (struct es1_ap_dev *)f->f_inode->i_private; + + retval = kstrtoint_from_user(buf, count, 10, &enable); + if (retval) + return retval; + + if (enable) + usb_log_enable(es1); + else + usb_log_disable(es1); + + return count; +} + +static const struct file_operations apb1_log_enable_fops = { + .read = apb1_log_enable_read, + .write = apb1_log_enable_write, +}; + /* * The ES1 USB Bridge device contains 4 endpoints * 1 Control - usual USB stuff + AP -> SVC messages @@ -552,9 +683,6 @@ static int ap_probe(struct usb_interface *interface, usb_rcvintpipe(udev, es1->svc_endpoint), es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, hd, svc_interval); - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; /* Allocate buffers for our cport in messages and start them up */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { @@ -591,6 +719,16 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } + /* Start up our svc urb, which allows events to start flowing */ + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", + (S_IWUSR | S_IRUGO), + gb_debugfs_get(), es1, + &apb1_log_enable_fops); + return 0; error: ap_disconnect(interface); -- cgit v1.2.3-59-g8ed1b From e2cb6cacad80355b1b9ba864ab9e68b9e4014c21 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 27 Mar 2015 16:32:56 +0530 Subject: greybus: kernel_ver.h: include to fix warning And this is the warning I was getting on kernel version > 3.14 CC [M] greybus/connection.o In file included from include/asm-generic/gpio.h:4:0, from arch/arm/include/asm/gpio.h:9, from include/linux/gpio.h:48, from greybus/kernel_ver.h:59, from greybus/connection.c:12: include/linux/kernel.h:35:0: warning: "U16_MAX" redefined kernel_ver.h is taking care of defining U16_MAX only if is not defined earlier, but it is often included as the first .h file. might be included later, which always defines it, unconditionally. And so this warning. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index f0010a865c22..12b454e4dbf2 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -14,6 +14,8 @@ #ifndef __GREYBUS_KERNEL_VER_H #define __GREYBUS_KERNEL_VER_H +#include + #ifndef __ATTR_WO #define __ATTR_WO(_name) { \ .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ -- cgit v1.2.3-59-g8ed1b From 1dad6b3515b2cac308a039cf2d081f6b72db1e4a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:10 +0100 Subject: greybus: operation: fix missing symbol exports Add missing EXPORT_SYMBOL_GPL for gb_operation_response_alloc, gb_operation_result, gb_operation_get, gb_operation_request_send and gb_operation_cancel, which are all supposed to be accessible from protocol handlers. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5117f0b08331..5447351880d3 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -147,6 +147,7 @@ int gb_operation_result(struct gb_operation *operation) return result; } +EXPORT_SYMBOL_GPL(gb_operation_result); static struct gb_operation * gb_operation_find(struct gb_connection *connection, u16 operation_id) @@ -463,6 +464,7 @@ bool gb_operation_response_alloc(struct gb_operation *operation, return true; } +EXPORT_SYMBOL_GPL(gb_operation_response_alloc); /* * Create a Greybus operation to be sent over the given connection. @@ -589,6 +591,7 @@ void gb_operation_get(struct gb_operation *operation) { kref_get(&operation->kref); } +EXPORT_SYMBOL_GPL(gb_operation_get); /* * Destroy a previously created operation. @@ -677,6 +680,7 @@ int gb_operation_request_send(struct gb_operation *operation, return gb_message_send(operation->request); } +EXPORT_SYMBOL_GPL(gb_operation_request_send); /* * Send a synchronous operation. This function is expected to @@ -901,6 +905,7 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) } gb_operation_put(operation); } +EXPORT_SYMBOL_GPL(gb_operation_cancel); /** * gb_operation_sync: implement a "simple" synchronous gb operation. -- cgit v1.2.3-59-g8ed1b From d506283491eb423cb8c6957d91070d3ac2d81b6a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:11 +0100 Subject: greybus: operation: fix typo in comment Fix typo in comment. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5447351880d3..41a3deb760fe 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -75,7 +75,7 @@ static DEFINE_SPINLOCK(gb_operations_lock); * valid value operation->errno can be set to is -EINPROGRESS, * indicating the request has been (or rather is about to be) sent. * At that point nobody should be looking at the result until the - * reponse arrives. + * response arrives. * * The first time the result gets set after the request has been * sent, that result "sticks." That is, if two concurrent threads -- cgit v1.2.3-59-g8ed1b From 37754030b0b9f7b81217b891cdc5f21dd7d29e4d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:12 +0100 Subject: greybus: operation: fix callback handling and documentation Fix up obsolete comments referring to null callback pointers for synchronous operations, and make sure a callback is always provided when sending a request. Also document that the callback is responsible for dropping the initial (and not necessarily final) reference to the operation. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 41a3deb760fe..c64b2bf47a43 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -244,10 +244,9 @@ static void gb_operation_work(struct work_struct *work) struct gb_operation *operation; operation = container_of(work, struct gb_operation, work); - if (operation->callback) { - operation->callback(operation); - operation->callback = NULL; - } + + operation->callback(operation); + gb_operation_put(operation); } @@ -526,7 +525,6 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, operation->errno = -EBADR; /* Initial value--means "never set" */ INIT_WORK(&operation->work, gb_operation_work); - operation->callback = NULL; /* set at submit time */ init_completion(&operation->completion); kref_init(&operation->kref); @@ -632,16 +630,12 @@ static void gb_operation_sync_callback(struct gb_operation *operation) } /* - * Send an operation request message. The caller has filled in - * any payload so the request message is ready to go. If non-null, - * the callback function supplied will be called when the response - * message has arrived indicating the operation is complete. In - * that case, the callback function is responsible for fetching the - * result of the operation using gb_operation_result() if desired, - * and dropping the final reference to (i.e., destroying) the - * operation. A null callback function is used for a synchronous - * request; in that case return from this function won't occur until - * the operation is complete. + * Send an operation request message. The caller has filled in any payload so + * the request message is ready to go. The callback function supplied will be + * called when the response message has arrived indicating the operation is + * complete. In that case, the callback function is responsible for fetching + * the result of the operation using gb_operation_result() if desired, and + * dropping the initial reference to the operation. */ int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback) @@ -650,6 +644,9 @@ int gb_operation_request_send(struct gb_operation *operation, struct gb_operation_msg_hdr *header; unsigned int cycle; + if (!callback) + return -EINVAL; + if (connection->state != GB_CONNECTION_STATE_ENABLED) return -ENOTCONN; @@ -805,10 +802,8 @@ static void gb_connection_recv_request(struct gb_connection *connection, * request handler to be the operation's callback function. * * The last thing the handler does is send a response - * message. The callback function is then cleared (in - * gb_operation_work()). The original reference to the - * operation will be dropped when the response has been - * sent. + * message. The original reference to the operation will be + * dropped when the response has been sent. */ operation->callback = gb_operation_request_handle; if (gb_operation_result_set(operation, -EINPROGRESS)) -- cgit v1.2.3-59-g8ed1b From 0fb5acc4018c0da61f9084932d0cd816fab77eec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:13 +0100 Subject: greybus: operation: fix use-after-free when sending responses Fix use-after-free when sending responses due to reference imbalance. Make sure to take a reference to the operation when sending responses. This reference is dropped in greybus_data_sent when the message has been sent, while the initial reference is dropped in gb_operation_work after processing the corresponding request. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c64b2bf47a43..ad45dee19a5a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -719,6 +719,8 @@ EXPORT_SYMBOL_GPL(gb_operation_request_send_sync); */ int gb_operation_response_send(struct gb_operation *operation, int errno) { + int ret; + /* Record the result */ if (!gb_operation_result_set(operation, errno)) { pr_err("request result already set\n"); @@ -733,10 +735,17 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) } } + /* Reference will be dropped when message has been sent. */ + gb_operation_get(operation); + /* Fill in the response header and send it */ operation->response->header->result = gb_operation_errno_map(errno); - return gb_message_send(operation->response); + ret = gb_message_send(operation->response); + if (ret) + gb_operation_put(operation); + + return ret; } EXPORT_SYMBOL_GPL(gb_operation_response_send); @@ -802,8 +811,8 @@ static void gb_connection_recv_request(struct gb_connection *connection, * request handler to be the operation's callback function. * * The last thing the handler does is send a response - * message. The original reference to the operation will be - * dropped when the response has been sent. + * message. The initial reference to the operation will be + * dropped when the handler returns. */ operation->callback = gb_operation_request_handle; if (gb_operation_result_set(operation, -EINPROGRESS)) -- cgit v1.2.3-59-g8ed1b From ea2c2ee80571388ef4641597c00ca10da3728127 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:14 +0100 Subject: greybus: operation: fix memory leak in request_send error path Make sure to drop the operation reference when sending the request fails to avoid leaking the operation structures. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index ad45dee19a5a..dcf987fb103d 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -643,6 +643,7 @@ int gb_operation_request_send(struct gb_operation *operation, struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; unsigned int cycle; + int ret; if (!callback) return -EINVAL; @@ -675,7 +676,11 @@ int gb_operation_request_send(struct gb_operation *operation, /* All set, send the request */ gb_operation_result_set(operation, -EINPROGRESS); - return gb_message_send(operation->request); + ret = gb_message_send(operation->request); + if (ret) + gb_operation_put(operation); + + return ret; } EXPORT_SYMBOL_GPL(gb_operation_request_send); -- cgit v1.2.3-59-g8ed1b From ff65be7a58201ca7843813f92192d997a2a4b4ee Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:15 +0100 Subject: greybus: operation: fix use-after-free and infinite loop on unhandled requests Make sure to return a proper response in case we get a request we do not recognise. This fixes an infinite loop and use-after-free bug, where the freed operations structure would get re-added to the work queue indefinitely. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index dcf987fb103d..f194b1eeb539 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -208,14 +208,11 @@ static void gb_message_cancel(struct gb_message *message) static void gb_operation_request_handle(struct gb_operation *operation) { struct gb_protocol *protocol = operation->connection->protocol; + int ret; if (!protocol) return; - /* - * If the protocol has no incoming request handler, report - * an error and mark the request bad. - */ if (protocol->request_recv) { protocol->request_recv(operation->type, operation); return; @@ -223,10 +220,14 @@ static void gb_operation_request_handle(struct gb_operation *operation) dev_err(&operation->connection->dev, "unexpected incoming request type 0x%02hhx\n", operation->type); - if (gb_operation_result_set(operation, -EPROTONOSUPPORT)) - queue_work(gb_operation_workqueue, &operation->work); - else - WARN(true, "failed to mark request bad\n"); + + ret = gb_operation_response_send(operation, -EPROTONOSUPPORT); + if (ret) { + dev_err(&operation->connection->dev, + "failed to send response %d: %d\n", + -EPROTONOSUPPORT, ret); + return; + } } /* -- cgit v1.2.3-59-g8ed1b From 772f3e90605641592435ec7c0a960e858925a0fe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:16 +0100 Subject: greybus: operation: fix null-deref on operation cancel Incoming operations are created without a response message. If an operation were to be cancelled before it has been fully processed (e.g. on connection destroy), we would get a null-pointer dereference in gb_operation_cancel. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f194b1eeb539..17f4eab5c076 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -911,7 +911,8 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) { if (gb_operation_result_set(operation, errno)) { gb_message_cancel(operation->request); - gb_message_cancel(operation->response); + if (operation->response) + gb_message_cancel(operation->response); } gb_operation_put(operation); } -- cgit v1.2.3-59-g8ed1b From 9489667684fbed2114dcdd10cdee2e4d20d9f308 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:17 +0100 Subject: greybus: operation: fix null-deref on operation destroy Incoming operations are created without a response message. If a protocol driver fails to send a response, or if the operation were to be cancelled before it has been fully processed, we get a null-pointer dereference when the operation is released. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 17f4eab5c076..cb0c87aa4f98 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -607,7 +607,8 @@ static void _gb_operation_destroy(struct kref *kref) list_del(&operation->links); spin_unlock_irqrestore(&gb_operations_lock, flags); - gb_operation_message_free(operation->response); + if (operation->response) + gb_operation_message_free(operation->response); gb_operation_message_free(operation->request); kmem_cache_free(gb_operation_cache, operation); -- cgit v1.2.3-59-g8ed1b From cfa79699cdef2e006f8414587c0e4d62209e4897 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:18 +0100 Subject: greybus: operation: fix incoming request payload size Fix the payload size of incoming requests, which should not include the operation message-header size. When creating requests we pass the sizes of request and response payloads and greybus core allocates buffers and adds the required headers. Specifically, the payload sizes do not include the message-header size. This is currently not the case for incoming requests however, something which prevents protocol drivers from implementing appropriate input verification and could lead to random data being treated as a valid message in case of a short request. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index cb0c87aa4f98..8e37d144c89f 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -567,9 +567,13 @@ EXPORT_SYMBOL_GPL(gb_operation_create); static struct gb_operation * gb_operation_create_incoming(struct gb_connection *connection, u16 id, - u8 type, void *data, size_t request_size) + u8 type, void *data, size_t size) { struct gb_operation *operation; + size_t request_size; + + /* Caller has made sure we at least have a message header. */ + request_size = size - sizeof(struct gb_operation_msg_hdr); operation = gb_operation_create_common(connection, GB_OPERATION_TYPE_INVALID, @@ -577,7 +581,7 @@ gb_operation_create_incoming(struct gb_connection *connection, u16 id, if (operation) { operation->id = id; operation->type = type; - memcpy(operation->request->header, data, request_size); + memcpy(operation->request->header, data, size); } return operation; -- cgit v1.2.3-59-g8ed1b From e1baa3f0a956764816d0614fb3636ecc7230c171 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:19 +0100 Subject: greybus: operation: replace pr_err with dev_err Use dev_err whenever we have a connection for more informative error messages. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 8e37d144c89f..878bfdb6781e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -730,17 +730,19 @@ EXPORT_SYMBOL_GPL(gb_operation_request_send_sync); */ int gb_operation_response_send(struct gb_operation *operation, int errno) { + struct gb_connection *connection = operation->connection; int ret; /* Record the result */ if (!gb_operation_result_set(operation, errno)) { - pr_err("request result already set\n"); + dev_err(&connection->dev, "request result already set\n"); return -EIO; /* Shouldn't happen */ } if (!operation->response) { if (!gb_operation_response_alloc(operation, 0)) { - pr_err("error allocating response\n"); + dev_err(&connection->dev, + "error allocating response\n"); /* XXX Respond with pre-allocated -ENOMEM? */ return -ENOMEM; } @@ -787,8 +789,10 @@ greybus_data_sent(struct greybus_host_device *hd, void *header, int status) */ operation = message->operation; if (message == operation->response) { - if (status) - pr_err("error %d sending response\n", status); + if (status) { + dev_err(&operation->connection->dev, + "error sending response: %d\n", status); + } gb_operation_put(operation); } else if (status) { if (gb_operation_result_set(operation, status)) -- cgit v1.2.3-59-g8ed1b From 0150bd7f23618eb4feb293f4db007833555a887f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:41:20 +0100 Subject: greybus: operation: make incomplete-message errors more informative Include the operation id as well as the received and expected size (from header) when reporting incomplete messages. This information is useful when debugging communication errors. Also invert the size test to match the error message and increase readability. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 878bfdb6781e..2b2b0d6332c8 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -899,8 +899,10 @@ void gb_connection_recv(struct gb_connection *connection, header = data; msg_size = le16_to_cpu(header->size); - if (msg_size > size) { - dev_err(&connection->dev, "incomplete message\n"); + if (size < msg_size) { + dev_err(&connection->dev, + "incomplete message received: 0x%04x (%zu < %zu)\n", + le16_to_cpu(header->operation_id), size, msg_size); return; /* XXX Should still complete operation */ } -- cgit v1.2.3-59-g8ed1b From 1842dd8b7bc7d713d3c252a501411b5009b3987d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:45:41 +0100 Subject: greybus: gpio: fix null-deref on short irq requests Make sure to verify the length of incoming requests before trying to parse the request buffer, which can even be NULL on empty requests. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 2bac28ec7f85..7dc675d7bd5b 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -413,6 +413,12 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) ggc = connection->private; request = op->request; + + if (request->payload_size < sizeof(*event)) { + dev_err(ggc->chip.dev, "short event received\n"); + return; + } + event = request->payload; if (event->which > ggc->line_max) { dev_err(ggc->chip.dev, "invalid hw irq: %d\n", event->which); -- cgit v1.2.3-59-g8ed1b From 244b5a2344bce5d9675e02da65b24e026926348c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:45:42 +0100 Subject: greybus: gpio: fix null-deref on unexpected irq requests Fix null-pointer dereference on failure to look up irq due to missing error handling. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 7dc675d7bd5b..0500a6f58526 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -425,7 +425,15 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) return; } irq = gpio_to_irq(ggc->chip.base + event->which); + if (irq < 0) { + dev_err(ggc->chip.dev, "failed to map irq\n"); + return; + } desc = irq_to_desc(irq); + if (!desc) { + dev_err(ggc->chip.dev, "failed to look up irq\n"); + return; + } /* Dispatch interrupt */ local_irq_disable(); -- cgit v1.2.3-59-g8ed1b From fef96a226eca4eae4aea1657ebcf5b013d6e48b6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:45:43 +0100 Subject: greybus: gpio: fix missing response on request errors Send response also to incoming requests that cannot be fulfilled. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 0500a6f58526..9a34fc5a52b8 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -396,43 +396,47 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) static void gb_gpio_request_recv(u8 type, struct gb_operation *op) { - struct gb_gpio_controller *ggc; struct gb_connection *connection = op->connection; + struct gb_gpio_controller *ggc = connection->private; struct gb_message *request; struct gb_gpio_irq_event_request *event; int irq; struct irq_desc *desc; int ret; + int status; if (type != GB_GPIO_TYPE_IRQ_EVENT) { dev_err(&connection->dev, "unsupported unsolicited request: %u\n", type); - return; + status = -EINVAL; + goto send_response; } - ggc = connection->private; - request = op->request; if (request->payload_size < sizeof(*event)) { dev_err(ggc->chip.dev, "short event received\n"); - return; + status = -EINVAL; + goto send_response; } event = request->payload; if (event->which > ggc->line_max) { dev_err(ggc->chip.dev, "invalid hw irq: %d\n", event->which); - return; + status = -EINVAL; + goto send_response; } irq = gpio_to_irq(ggc->chip.base + event->which); if (irq < 0) { dev_err(ggc->chip.dev, "failed to map irq\n"); - return; + status = -EINVAL; + goto send_response; } desc = irq_to_desc(irq); if (!desc) { dev_err(ggc->chip.dev, "failed to look up irq\n"); - return; + status = -EINVAL; + goto send_response; } /* Dispatch interrupt */ @@ -440,10 +444,13 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) handle_simple_irq(irq, desc); local_irq_enable(); - ret = gb_operation_response_send(op, 0); + status = 0; +send_response: + ret = gb_operation_response_send(op, status); if (ret) { dev_err(ggc->chip.dev, - "failed to send response status %d: %d\n", 0, ret); + "failed to send response status %d: %d\n", + status, ret); } } -- cgit v1.2.3-59-g8ed1b From b67f2d13db21de2fc948d6f7090fe2b9bae5d5bd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:45:44 +0100 Subject: greybus: hid: replace pr_err with dev_err Replace pr_err with dev_err and clean up error messages somewhat. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index fe05a08aa7f2..9734c7b35cc2 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -139,10 +139,12 @@ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, memcpy(request->report, buf, len); ret = gb_operation_request_send_sync(operation); - if (ret) - pr_err("%s: operation failed (%d)\n", __func__, ret); - else + if (ret) { + dev_err(&operation->connection->dev, + "failed to set report: %d\n", ret); + } else { ret = len; + } gb_operation_destroy(operation); return ret; @@ -156,18 +158,21 @@ static void gb_hid_irq_handler(u8 type, struct gb_operation *op) int ret, size; if (type != GB_HID_TYPE_IRQ_EVENT) { - pr_err("unsupported unsolicited request\n"); + dev_err(&connection->dev, + "unsupported unsolicited request\n"); return; } ret = gb_operation_response_send(op, 0); - if (ret) - pr_err("%s: error %d sending response status %d\n", __func__, - ret, 0); + if (ret) { + dev_err(&connection->dev, + "failed to send response status %d: %d\n", + 0, ret); + } size = request->report[0] | request->report[1] << 8; if (!size) { - pr_err("%s: size can't be zero.\n", __func__); + dev_err(&connection->dev, "bad report size: %d\n", size); return; } -- cgit v1.2.3-59-g8ed1b From ecf47ab9c5ca1427baea7ab6d3226502d967f19d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:45:45 +0100 Subject: greybus: hid: fix success response being sent on errors Make sure to only send a success response if we did not detect any errors. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 9734c7b35cc2..f0da387476b8 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -163,13 +163,6 @@ static void gb_hid_irq_handler(u8 type, struct gb_operation *op) return; } - ret = gb_operation_response_send(op, 0); - if (ret) { - dev_err(&connection->dev, - "failed to send response status %d: %d\n", - 0, ret); - } - size = request->report[0] | request->report[1] << 8; if (!size) { dev_err(&connection->dev, "bad report size: %d\n", size); @@ -179,6 +172,13 @@ static void gb_hid_irq_handler(u8 type, struct gb_operation *op) if (test_bit(GB_HID_STARTED, &ghid->flags)) hid_input_report(ghid->hid, HID_INPUT_REPORT, request->report + 2, size - 2, 1); + + ret = gb_operation_response_send(op, 0); + if (ret) { + dev_err(&connection->dev, + "failed to send response status %d: %d\n", + 0, ret); + } } -- cgit v1.2.3-59-g8ed1b From 36257f6b4e7671cb12f98d91a6ffdeabdc254d0c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:45:46 +0100 Subject: greybus: hid: fix null-deref on short report requests Make sure to verify the length of incoming requests before trying to parse the request buffer, which can even be NULL on empty requests. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index f0da387476b8..a225813bc477 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -163,6 +163,11 @@ static void gb_hid_irq_handler(u8 type, struct gb_operation *op) return; } + if (op->request->payload_size < 2) { + dev_err(&connection->dev, "short report received\n"); + return; + } + size = request->report[0] | request->report[1] << 8; if (!size) { dev_err(&connection->dev, "bad report size: %d\n", size); -- cgit v1.2.3-59-g8ed1b From 382145beb4a4bb057f17d5b411546b6b56fbacd1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:45:47 +0100 Subject: greybus: hid: fix missing input verification of report events Add minimal verification of incoming report size, before using it to determine what buffer and size to pass on to HID core. Add comment about protocol needing to be revisited. If we are going to be parsing the report data received, then those fields have to be defined in the Greybus specification at least. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index a225813bc477..8e32dfcd1131 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -168,8 +168,12 @@ static void gb_hid_irq_handler(u8 type, struct gb_operation *op) return; } + /* + * FIXME: add report size to Greybus HID protocol if we need to parse + * it here. + */ size = request->report[0] | request->report[1] << 8; - if (!size) { + if (size < 2 || size > op->request->payload_size - 2) { dev_err(&connection->dev, "bad report size: %d\n", size); return; } -- cgit v1.2.3-59-g8ed1b From d0eb755aeef092f27b3dd2a4c90616f613541f56 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:45:48 +0100 Subject: greybus: hid: fix missing response on request errors Send response also to incoming requests that cannot be fulfilled. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 8e32dfcd1131..cc5708ddf068 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -155,17 +155,20 @@ static void gb_hid_irq_handler(u8 type, struct gb_operation *op) struct gb_connection *connection = op->connection; struct gb_hid *ghid = connection->private; struct gb_hid_input_report_request *request = op->request->payload; + int status; int ret, size; if (type != GB_HID_TYPE_IRQ_EVENT) { dev_err(&connection->dev, "unsupported unsolicited request\n"); - return; + status = -EINVAL; + goto send_response; } if (op->request->payload_size < 2) { dev_err(&connection->dev, "short report received\n"); - return; + status = -EINVAL; + goto send_response; } /* @@ -175,18 +178,21 @@ static void gb_hid_irq_handler(u8 type, struct gb_operation *op) size = request->report[0] | request->report[1] << 8; if (size < 2 || size > op->request->payload_size - 2) { dev_err(&connection->dev, "bad report size: %d\n", size); - return; + status = -EINVAL; + goto send_response; } if (test_bit(GB_HID_STARTED, &ghid->flags)) hid_input_report(ghid->hid, HID_INPUT_REPORT, request->report + 2, size - 2, 1); - ret = gb_operation_response_send(op, 0); + status = 0; +send_response: + ret = gb_operation_response_send(op, status); if (ret) { dev_err(&connection->dev, "failed to send response status %d: %d\n", - 0, ret); + status, ret); } } -- cgit v1.2.3-59-g8ed1b From 973ccfd62686a2331f43b0053de052d958f50d31 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2015 12:45:49 +0100 Subject: greybus: operation: refactor response handling Send response to incoming requests from the operation request handler rather than in every protocol request_recv callback. This simplifies request_recv error handling and allows for further code reuse. Note that if we ever get protocols that need to hold off sending responses we could implement this by letting them return a special value (after acquiring the necessary operation references) to suppress the response from being sent by greybus core. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 29 ++++++++--------------------- drivers/staging/greybus/hid.c | 23 ++++++----------------- drivers/staging/greybus/operation.c | 17 ++++++++++------- drivers/staging/greybus/protocol.h | 2 +- 4 files changed, 25 insertions(+), 46 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 9a34fc5a52b8..20917bf6479f 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -394,7 +394,7 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) return ret; } -static void gb_gpio_request_recv(u8 type, struct gb_operation *op) +static int gb_gpio_request_recv(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_gpio_controller *ggc = connection->private; @@ -402,41 +402,35 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) struct gb_gpio_irq_event_request *event; int irq; struct irq_desc *desc; - int ret; - int status; if (type != GB_GPIO_TYPE_IRQ_EVENT) { dev_err(&connection->dev, "unsupported unsolicited request: %u\n", type); - status = -EINVAL; - goto send_response; + return -EINVAL; } request = op->request; if (request->payload_size < sizeof(*event)) { dev_err(ggc->chip.dev, "short event received\n"); - status = -EINVAL; - goto send_response; + return -EINVAL; } event = request->payload; if (event->which > ggc->line_max) { dev_err(ggc->chip.dev, "invalid hw irq: %d\n", event->which); - status = -EINVAL; - goto send_response; + return -EINVAL; } + irq = gpio_to_irq(ggc->chip.base + event->which); if (irq < 0) { dev_err(ggc->chip.dev, "failed to map irq\n"); - status = -EINVAL; - goto send_response; + return -EINVAL; } desc = irq_to_desc(irq); if (!desc) { dev_err(ggc->chip.dev, "failed to look up irq\n"); - status = -EINVAL; - goto send_response; + return -EINVAL; } /* Dispatch interrupt */ @@ -444,14 +438,7 @@ static void gb_gpio_request_recv(u8 type, struct gb_operation *op) handle_simple_irq(irq, desc); local_irq_enable(); - status = 0; -send_response: - ret = gb_operation_response_send(op, status); - if (ret) { - dev_err(ggc->chip.dev, - "failed to send response status %d: %d\n", - status, ret); - } + return 0; } static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index cc5708ddf068..5935aa6c6334 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -150,25 +150,22 @@ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, return ret; } -static void gb_hid_irq_handler(u8 type, struct gb_operation *op) +static int gb_hid_irq_handler(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_hid *ghid = connection->private; struct gb_hid_input_report_request *request = op->request->payload; - int status; - int ret, size; + int size; if (type != GB_HID_TYPE_IRQ_EVENT) { dev_err(&connection->dev, "unsupported unsolicited request\n"); - status = -EINVAL; - goto send_response; + return -EINVAL; } if (op->request->payload_size < 2) { dev_err(&connection->dev, "short report received\n"); - status = -EINVAL; - goto send_response; + return -EINVAL; } /* @@ -178,22 +175,14 @@ static void gb_hid_irq_handler(u8 type, struct gb_operation *op) size = request->report[0] | request->report[1] << 8; if (size < 2 || size > op->request->payload_size - 2) { dev_err(&connection->dev, "bad report size: %d\n", size); - status = -EINVAL; - goto send_response; + return -EINVAL; } if (test_bit(GB_HID_STARTED, &ghid->flags)) hid_input_report(ghid->hid, HID_INPUT_REPORT, request->report + 2, size - 2, 1); - status = 0; -send_response: - ret = gb_operation_response_send(op, status); - if (ret) { - dev_err(&connection->dev, - "failed to send response status %d: %d\n", - status, ret); - } + return 0; } diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 2b2b0d6332c8..260774e414f3 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -208,24 +208,27 @@ static void gb_message_cancel(struct gb_message *message) static void gb_operation_request_handle(struct gb_operation *operation) { struct gb_protocol *protocol = operation->connection->protocol; + int status; int ret; if (!protocol) return; if (protocol->request_recv) { - protocol->request_recv(operation->type, operation); - return; - } + status = protocol->request_recv(operation->type, operation); + } else { + dev_err(&operation->connection->dev, + "unexpected incoming request type 0x%02hhx\n", + operation->type); - dev_err(&operation->connection->dev, - "unexpected incoming request type 0x%02hhx\n", operation->type); + status = -EPROTONOSUPPORT; + } - ret = gb_operation_response_send(operation, -EPROTONOSUPPORT); + ret = gb_operation_response_send(operation, status); if (ret) { dev_err(&operation->connection->dev, "failed to send response %d: %d\n", - -EPROTONOSUPPORT, ret); + status, ret); return; } } diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index a74afef92a01..82d9e81386e3 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -22,7 +22,7 @@ struct gb_protocol_version_response { typedef int (*gb_connection_init_t)(struct gb_connection *); typedef void (*gb_connection_exit_t)(struct gb_connection *); -typedef void (*gb_request_recv_t)(u8, struct gb_operation *); +typedef int (*gb_request_recv_t)(u8, struct gb_operation *); /* * Protocols having the same id but different major and/or minor -- cgit v1.2.3-59-g8ed1b From d8187aa2241136e9fb3801e82fe8c2dfdae06802 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 27 Mar 2015 15:06:24 -0500 Subject: greybus: manifest: use size_t for a size variable In identify_descriptor(), the variable desc_size represents the size of a memory object. So change its type from int to size_t. The return value for this function can be desc_size cast to int. One can verify by inspection this will never exceed INT_MAX. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 1b9edbcd08c5..c29a0c822f23 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -1,8 +1,8 @@ /* * Greybus module manifest parsing * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ @@ -79,7 +79,7 @@ static int identify_descriptor(struct gb_interface *intf, { struct greybus_descriptor_header *desc_header = &desc->header; struct manifest_desc *descriptor; - int desc_size; + size_t desc_size; size_t expected_size; if (size < sizeof(*desc_header)) { @@ -87,8 +87,8 @@ static int identify_descriptor(struct gb_interface *intf, return -EINVAL; /* Must at least have header */ } - desc_size = (int)le16_to_cpu(desc_header->size); - if ((size_t)desc_size > size) { + desc_size = le16_to_cpu(desc_header->size); + if (desc_size > size) { pr_err("descriptor too big\n"); return -EINVAL; } @@ -119,7 +119,7 @@ static int identify_descriptor(struct gb_interface *intf, } if (desc_size < expected_size) { - pr_err("%s descriptor too small (%u < %zu)\n", + pr_err("%s descriptor too small (%zu < %zu)\n", get_descriptor_type_string(desc_header->type), desc_size, expected_size); return -EINVAL; @@ -134,6 +134,8 @@ static int identify_descriptor(struct gb_interface *intf, descriptor->type = desc_header->type; list_add_tail(&descriptor->links, &intf->manifest_descs); + /* desc_size is is positive and is known to fit in a signed int */ + return desc_size; } -- cgit v1.2.3-59-g8ed1b From dada3b02a38c2e1d06a43d0d909d48f4db798dfc Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 27 Mar 2015 15:20:49 -0500 Subject: greybus: es1: test apb1_log_task safely When usb_log_enable() is called, the global apb1_log_task is used to hold the result of kthread_run(). It is possible for kthread_run() to return an error pointer, so tests of apb_log_task against NULL are insufficient to determine its validity. Note that kthread_run() never returns NULL so we don't have to check for that. But apb1_log_task is initially NULL, so that global must be both non-null and not an error in order to be considered valid. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 239358dce9be..af8e7b33270b 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -540,12 +540,12 @@ static const struct file_operations apb1_log_fops = { static void usb_log_enable(struct es1_ap_dev *es1) { - if (apb1_log_task != NULL) + if (!IS_ERR_OR_NULL(apb1_log_task)) return; /* get log from APB1 */ apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); - if (apb1_log_task == ERR_PTR(-ENOMEM)) + if (IS_ERR(apb1_log_task)) return; apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, gb_debugfs_get(), NULL, @@ -554,7 +554,7 @@ static void usb_log_enable(struct es1_ap_dev *es1) static void usb_log_disable(struct es1_ap_dev *es1) { - if (apb1_log_task == NULL) + if (IS_ERR_OR_NULL(apb1_log_task)) return; debugfs_remove(apb1_log_dentry); @@ -568,7 +568,7 @@ static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, size_t count, loff_t *ppos) { char tmp_buf[3]; - int enable = apb1_log_task != NULL; + int enable = !IS_ERR_OR_NULL(apb1_log_task); sprintf(tmp_buf, "%d\n", enable); return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); -- cgit v1.2.3-59-g8ed1b From e0feaf14b102f767d00ee28443f8443e4d76ba8b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 27 Mar 2015 15:20:49 -0500 Subject: greybus: es2: test apb1_log_task safely When usb_log_enable() is called, the global apb1_log_task is used to hold the result of kthread_run(). It is possible for kthread_run() to return an error pointer, so tests of apb_log_task against NULL are insufficient to determine its validity. Note that kthread_run() never returns NULL so we don't have to check for that. But apb1_log_task is initially NULL, so that global must be both non-null and not an error in order to be considered valid. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index e4637500d05c..23e27782fe9f 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -540,12 +540,12 @@ static const struct file_operations apb1_log_fops = { static void usb_log_enable(struct es1_ap_dev *es1) { - if (apb1_log_task != NULL) + if (!IS_ERR_OR_NULL(apb1_log_task)) return; /* get log from APB1 */ apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); - if (apb1_log_task == ERR_PTR(-ENOMEM)) + if (IS_ERR(apb1_log_task)) return; apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, gb_debugfs_get(), NULL, @@ -554,7 +554,7 @@ static void usb_log_enable(struct es1_ap_dev *es1) static void usb_log_disable(struct es1_ap_dev *es1) { - if (apb1_log_task == NULL) + if (IS_ERR_OR_NULL(apb1_log_task)) return; debugfs_remove(apb1_log_dentry); @@ -568,7 +568,7 @@ static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, size_t count, loff_t *ppos) { char tmp_buf[3]; - int enable = apb1_log_task != NULL; + int enable = !IS_ERR_OR_NULL(apb1_log_task); sprintf(tmp_buf, "%d\n", enable); return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); -- cgit v1.2.3-59-g8ed1b From 355a7058153e04b53bed3fcb792110294693d386 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Tue, 31 Mar 2015 09:51:59 +0200 Subject: greybus: Add loopback protocol Add a simple Greybus protocol in order to stress USB and Greybus. This protocol currently support 2 requests: ping and transfer. ping request is useful to measure latency. Kernel send a ping request and firmware should respond with a ping. The transfer request request is useful to stress Greybus and USB. Kernel can send data from 0 to 4k and the firmware must send back the data to kernel. This behaviour of gb-loopback module is controlled via sysfs. Curently, connection sysfs folder is updated with new entries: - type: Type of loopback message to send * 0 => Don't send message * 1 => Send ping message continuously (message without payload) * 2 => Send transer message continuously (message with payload) - size: Size of transfer message payload: 0-4096 bytes - ms_wait: Time to wait between two messages: 0-1024 ms Module also export some statistics about connection: - latency: Time to send and receive one message - frequency: Number of packet sent per second on this cport - throughput: Quantity of data sent and received on this cport - error All this statistics are cleared everytime type, size or ms_wait entries are updated. Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/loopback.c | 390 +++++++++++++++++++++++++++++ 3 files changed, 393 insertions(+) create mode 100644 drivers/staging/greybus/loopback.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 6cb08ae544cd..f6ad19a1329b 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -22,6 +22,7 @@ gb-phy-y := gpb.o \ # Prefix all modules with gb- gb-vibrator-y := vibrator.o gb-battery-y := battery.o +gb-loopback-y := loopback.o gb-es1-y := es1.o gb-es2-y := es2.o @@ -29,6 +30,7 @@ obj-m += greybus.o obj-m += gb-phy.o obj-m += gb-vibrator.o obj-m += gb-battery.o +obj-m += gb-loopback.o obj-m += gb-es1.o obj-m += gb-es2.o diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 398630ce8ac4..4b2cf9220843 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -42,6 +42,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_SENSOR = 0x0e, GREYBUS_PROTOCOL_LED = 0x0f, GREYBUS_PROTOCOL_VIBRATOR = 0x10, + GREYBUS_PROTOCOL_LOOPBACK = 0x11, /* ... */ GREYBUS_PROTOCOL_VENDOR = 0xff, }; diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c new file mode 100644 index 000000000000..ae8cc9da8347 --- /dev/null +++ b/drivers/staging/greybus/loopback.c @@ -0,0 +1,390 @@ +/* + * Loopback bridge driver for the Greybus loopback module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include +#include +#include +#include +#include +#include +#include "greybus.h" + +struct gb_loopback_stats { + u32 min; + u32 max; + u32 avg; + u32 sum; + u32 count; +}; + +struct gb_loopback { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + + struct task_struct *task; + + int type; + u32 size; + int ms_wait; + + struct gb_loopback_stats latency; + struct gb_loopback_stats throughput; + struct gb_loopback_stats frequency; + struct timeval ts; + struct timeval te; + u64 elapsed_nsecs; + u32 error; +}; + +/* Version of the Greybus loopback protocol we support */ +#define GB_LOOPBACK_VERSION_MAJOR 0x00 +#define GB_LOOPBACK_VERSION_MINOR 0x01 + +/* Greybus loopback request types */ +#define GB_LOOPBACK_TYPE_INVALID 0x00 +#define GB_LOOPBACK_TYPE_PROTOCOL_VERSION 0x01 +#define GB_LOOPBACK_TYPE_PING 0x02 +#define GB_LOOPBACK_TYPE_TRANSFER 0x03 + +#define GB_LOOPBACK_SIZE_MAX SZ_4K + +/* Define get_version() routine */ +define_get_version(gb_loopback, LOOPBACK); + +/* interface sysfs attributes */ +#define gb_loopback_ro_attr(field, type) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct gb_connection *connection = to_gb_connection(dev); \ + struct gb_loopback *gb = \ + (struct gb_loopback *)connection->private; \ + return sprintf(buf, "%"#type"\n", gb->field); \ +} \ +static DEVICE_ATTR_RO(field) + +#define gb_loopback_ro_stats_attr(name, field, type) \ +static ssize_t name##_##field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct gb_connection *connection = to_gb_connection(dev); \ + struct gb_loopback *gb = \ + (struct gb_loopback *)connection->private; \ + return sprintf(buf, "%"#type"\n", gb->name.field); \ +} \ +static DEVICE_ATTR_RO(name##_##field) + +#define gb_loopback_stats_attrs(field) \ + gb_loopback_ro_stats_attr(field, min, d); \ + gb_loopback_ro_stats_attr(field, max, d); \ + gb_loopback_ro_stats_attr(field, avg, d); + +#define gb_loopback_attr(field, type) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct gb_connection *connection = to_gb_connection(dev); \ + struct gb_loopback *gb = \ + (struct gb_loopback *)connection->private; \ + return sprintf(buf, "%"#type"\n", gb->field); \ +} \ +static ssize_t field##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, \ + size_t len) \ +{ \ + int ret; \ + struct gb_connection *connection = to_gb_connection(dev); \ + struct gb_loopback *gb = \ + (struct gb_loopback *)connection->private; \ + ret = sscanf(buf, "%"#type, &gb->field); \ + pr_err("%s = %"#type"\n", #field, gb->field); \ + if (ret != 1) \ + return -EINVAL; \ + gb_loopback_check_attr(gb); \ + return len; \ +} \ +static DEVICE_ATTR_RW(field) + +static void gb_loopback_reset_stats(struct gb_loopback *gb); +static void gb_loopback_check_attr(struct gb_loopback *gb) +{ + if (gb->ms_wait > 1000) + gb->ms_wait = 1000; + if (gb->type > 3) + gb->type = 0; + if (gb->size > GB_LOOPBACK_SIZE_MAX) + gb->size = GB_LOOPBACK_SIZE_MAX; + gb->error = 0; + gb_loopback_reset_stats(gb); +} + +/* Time to send and receive one message */ +gb_loopback_stats_attrs(latency); +/* Number of packet sent per second on this cport */ +gb_loopback_stats_attrs(frequency); +/* Quantity of data sent and received on this cport */ +gb_loopback_stats_attrs(throughput); +gb_loopback_ro_attr(error, d); + +/* + * Type of loopback message to send + * 0 => Don't send message + * 1 => Send ping message continuously (message without payload) + * 2 => Send transer message continuously (message with payload) + */ +gb_loopback_attr(type, d); +/* Size of transfer message payload: 0-4096 bytes */ +gb_loopback_attr(size, u); +/* Time to wait between two messages: 0-1024 ms */ +gb_loopback_attr(ms_wait, d); + +#define dev_stats_attrs(name) \ + &dev_attr_##name##_min.attr, \ + &dev_attr_##name##_max.attr, \ + &dev_attr_##name##_avg.attr + +static struct attribute *loopback_attrs[] = { + dev_stats_attrs(latency), + dev_stats_attrs(frequency), + dev_stats_attrs(throughput), + &dev_attr_type.attr, + &dev_attr_size.attr, + &dev_attr_ms_wait.attr, + &dev_attr_error.attr, + NULL, +}; +ATTRIBUTE_GROUPS(loopback); + +struct gb_loopback_transfer_request { + __le32 len; + __u8 data[0]; +}; + +struct gb_loopback_transfer_response { + __u8 data[0]; +}; + + +static int gb_loopback_transfer(struct gb_loopback *gb, + struct timeval *tping, u32 len) +{ + struct timeval ts, te; + u64 elapsed_nsecs; + struct gb_loopback_transfer_request *request; + struct gb_loopback_transfer_response *response; + int retval; + + request = kmalloc(len + sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + response = kmalloc(len + sizeof(*response), GFP_KERNEL); + if (!response) { + kfree(request); + return -ENOMEM; + } + + request->len = cpu_to_le32(len); + + do_gettimeofday(&ts); + retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_TRANSFER, + request, len + sizeof(*request), + response, len + sizeof(*response)); + do_gettimeofday(&te); + elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); + *tping = ns_to_timeval(elapsed_nsecs); + + if (retval) + goto gb_error; + + if (memcmp(request->data, response->data, len)) + retval = -EREMOTEIO; + +gb_error: + kfree(request); + kfree(response); + + return retval; +} + +static int gb_loopback_ping(struct gb_loopback *gb, struct timeval *tping) +{ + struct timeval ts, te; + u64 elapsed_nsecs; + int retval; + + do_gettimeofday(&ts); + retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_PING, + NULL, 0, NULL, 0); + do_gettimeofday(&te); + elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); + *tping = ns_to_timeval(elapsed_nsecs); + + return retval; +} + +static void gb_loopback_reset_stats(struct gb_loopback *gb) +{ + struct gb_loopback_stats reset = { + .min = 0xffffffff, + }; + memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb->frequency, &reset, sizeof(struct gb_loopback_stats)); + memset(&gb->ts, 0, sizeof(struct timeval)); +} + +static void gb_loopback_update_stats(struct gb_loopback_stats *stats, + u64 elapsed_nsecs) +{ + u32 avg; + + if (elapsed_nsecs >= NSEC_PER_SEC) { + if (!stats->count) + avg = stats->sum * (elapsed_nsecs / NSEC_PER_SEC); + else + avg = stats->sum / stats->count; + if (stats->min > avg) + stats->min = avg; + if (stats->max < avg) + stats->max = avg; + stats->avg = avg; + stats->count = 0; + stats->sum = 0; + } +} + +static void gb_loopback_freq_update(struct gb_loopback *gb) +{ + gb->frequency.sum++; + gb_loopback_update_stats(&gb->frequency, gb->elapsed_nsecs); +} + +static void gb_loopback_bw_update(struct gb_loopback *gb, int error) +{ + if (!error) + gb->throughput.sum += gb->size * 2; + gb_loopback_update_stats(&gb->throughput, gb->elapsed_nsecs); +} + +static void gb_loopback_latency_update(struct gb_loopback *gb, + struct timeval *tlat) +{ + u32 lat; + u64 nsecs; + + nsecs = timeval_to_ns(tlat); + lat = nsecs / NSEC_PER_MSEC; + + if (gb->latency.min > lat) + gb->latency.min = lat; + if (gb->latency.max < lat) + gb->latency.max = lat; + gb->latency.sum += lat; + gb->latency.count++; + gb_loopback_update_stats(&gb->latency, gb->elapsed_nsecs); +} + +static int gb_loopback_fn(void *data) +{ + int error = 0; + struct timeval tlat = {0, 0}; + struct gb_loopback *gb = (struct gb_loopback *)data; + + while (!kthread_should_stop()) { + if (gb->type == 0) { + msleep(1000); + continue; + } + if (gb->type == 1) + error = gb_loopback_ping(gb, &tlat); + if (gb->type == 2) + error = gb_loopback_transfer(gb, &tlat, gb->size); + if (error) + gb->error++; + if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) { + do_gettimeofday(&gb->ts); + continue; + } + do_gettimeofday(&gb->te); + gb->elapsed_nsecs = timeval_to_ns(&gb->te) - + timeval_to_ns(&gb->ts); + gb_loopback_freq_update(gb); + if (gb->type == 2) + gb_loopback_bw_update(gb, error); + gb_loopback_latency_update(gb, &tlat); + if (gb->elapsed_nsecs >= NSEC_PER_SEC) + gb->ts = gb->te; + if (gb->ms_wait) + msleep(gb->ms_wait); + + } + return 0; +} + +static int gb_loopback_connection_init(struct gb_connection *connection) +{ + struct gb_loopback *gb; + int retval; + + gb = kzalloc(sizeof(*gb), GFP_KERNEL); + if (!gb) + return -ENOMEM; + + gb->connection = connection; + connection->private = gb; + retval = sysfs_update_group(&connection->dev.kobj, &loopback_group); + if (retval) + goto error; + + /* Check the version */ + retval = get_version(gb); + if (retval) + goto error; + + gb_loopback_reset_stats(gb); + gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); + if (IS_ERR(gb->task)) { + retval = IS_ERR(gb->task); + goto error; + } + + return 0; + +error: + kfree(gb); + return retval; +} + +static void gb_loopback_connection_exit(struct gb_connection *connection) +{ + struct gb_loopback *gb = connection->private; + + if (!IS_ERR_OR_NULL(gb->task)) + kthread_stop(gb->task); + sysfs_remove_group(&connection->dev.kobj, &loopback_group); + kfree(gb); +} + +static struct gb_protocol loopback_protocol = { + .name = "loopback", + .id = GREYBUS_PROTOCOL_LOOPBACK, + .major = GB_LOOPBACK_VERSION_MAJOR, + .minor = GB_LOOPBACK_VERSION_MINOR, + .connection_init = gb_loopback_connection_init, + .connection_exit = gb_loopback_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +gb_protocol_driver(&loopback_protocol); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 5679f783b1ef953f5c5706f226580abf45c82c46 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 31 Mar 2015 23:01:45 +0200 Subject: greybus: loopback: fix build breakage about SZ_4K x86 doesn't include SZ_4K somehow so explicitly include to fix the build breakage. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ae8cc9da8347..7003bce148ff 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "greybus.h" struct gb_loopback_stats { -- cgit v1.2.3-59-g8ed1b From 7a51b9362b547b2f02ef88c10636950637c71fa5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 31 Mar 2015 23:02:34 +0200 Subject: greybus: loopback: use the attribute groups, not group We should use the attribute groups, not group, for the device, so add and remove it. No one should ever be updating a sysfs group for a device, as that can be pretty dangerous if you don't duplicate _all_ existing attribute for that device, and I don't think we were doing that here. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 7003bce148ff..9914b52c71ce 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -343,7 +343,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb->connection = connection; connection->private = gb; - retval = sysfs_update_group(&connection->dev.kobj, &loopback_group); + retval = sysfs_create_groups(&connection->dev.kobj, loopback_groups); if (retval) goto error; @@ -372,7 +372,7 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); - sysfs_remove_group(&connection->dev.kobj, &loopback_group); + sysfs_remove_groups(&connection->dev.kobj, loopback_groups); kfree(gb); } -- cgit v1.2.3-59-g8ed1b From 66c98986c909bfe789e8b15f805fd28ef4c36b76 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 1 Apr 2015 01:36:23 +0200 Subject: greybus: kernel_ver.h: add sysfs_create_groups() and sysfs_remove_groups() These functions showed up in 3.12 or so, and we are stuck on 3.10 for various reasons, so provide backports in kernel_ver.h so that we can rely on these functions. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 12b454e4dbf2..92eb024c3bed 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -78,6 +78,8 @@ static inline void gb_gpiochip_remove(struct gpio_chip *chip) * it here. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) +#include + #define ATTRIBUTE_GROUPS(name) \ static const struct attribute_group name##_group = { \ .attrs = name##_attrs, \ @@ -86,6 +88,37 @@ static const struct attribute_group *name##_groups[] = { \ &name##_group, \ NULL, \ } + +static inline int sysfs_create_groups(struct kobject *kobj, + const struct attribute_group **groups) +{ + int error = 0; + int i; + + if (!groups) + return 0; + + for (i = 0; groups[i]; i++) { + error = sysfs_create_group(kobj, groups[i]); + if (error) { + while (--i >= 0) + sysfs_remove_group(kobj, groups[i]); + break; + } + } + return error; +} + +static inline void sysfs_remove_groups(struct kobject *kobj, + const struct attribute_group **groups) +{ + int i; + + if (!groups) + return; + for (i = 0; groups[i]; i++) + sysfs_remove_group(kobj, groups[i]); +} #endif #endif /* __GREYBUS_KERNEL_VER_H */ -- cgit v1.2.3-59-g8ed1b From e4c4b4dce628d529bc073952ddcf272d307d06bd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 26 Mar 2015 21:25:08 -0500 Subject: greybus: reduce the ranting Cut out some comments that are no longer operative. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 27 --------------------------- drivers/staging/greybus/es2.c | 27 --------------------------- 2 files changed, 54 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index af8e7b33270b..f559c1d53e89 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -451,34 +451,7 @@ static void cport_out_callback(struct urb *urb) */ data = urb->transfer_buffer + 1; greybus_data_sent(hd, data, status); - free_urb(es1, urb); - /* - * Rest assured Greg, this craziness is getting fixed. - * - * Yes, you are right, we aren't telling anyone that the urb finished. - * "That's crazy! How does this all even work?" you might be saying. - * The "magic" is the idea that greybus works on the "operation" level, - * not the "send a buffer" level. All operations are "round-trip" with - * a response from the device that the operation finished, or it will - * time out. Because of that, we don't care that this urb finished, or - * failed, or did anything else, as higher levels of the protocol stack - * will handle completions and timeouts and the rest. - * - * This protocol is "needed" due to some hardware restrictions on the - * current generation of Unipro controllers. Think about it for a - * minute, this is a USB driver, talking to a Unipro bridge, impedance - * mismatch is huge, yet the Unipro controller are even more - * underpowered than this little USB controller. We rely on the round - * trip to keep stalls in the Unipro controllers from happening so that - * we can keep data flowing properly, no matter how slow it might be. - * - * Once again, a wonderful bus protocol cut down in its prime by a naive - * controller chip. We dream of the day we have a "real" HCD for - * Unipro. Until then, we suck it up and make the hardware work, as - * that's the job of the firmware and kernel. - * - */ } static void apb1_log_get(struct es1_ap_dev *es1) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 23e27782fe9f..a6c47a3dd62f 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -451,34 +451,7 @@ static void cport_out_callback(struct urb *urb) */ data = urb->transfer_buffer + 1; greybus_data_sent(hd, data, status); - free_urb(es1, urb); - /* - * Rest assured Greg, this craziness is getting fixed. - * - * Yes, you are right, we aren't telling anyone that the urb finished. - * "That's crazy! How does this all even work?" you might be saying. - * The "magic" is the idea that greybus works on the "operation" level, - * not the "send a buffer" level. All operations are "round-trip" with - * a response from the device that the operation finished, or it will - * time out. Because of that, we don't care that this urb finished, or - * failed, or did anything else, as higher levels of the protocol stack - * will handle completions and timeouts and the rest. - * - * This protocol is "needed" due to some hardware restrictions on the - * current generation of Unipro controllers. Think about it for a - * minute, this is a USB driver, talking to a Unipro bridge, impedance - * mismatch is huge, yet the Unipro controller are even more - * underpowered than this little USB controller. We rely on the round - * trip to keep stalls in the Unipro controllers from happening so that - * we can keep data flowing properly, no matter how slow it might be. - * - * Once again, a wonderful bus protocol cut down in its prime by a naive - * controller chip. We dream of the day we have a "real" HCD for - * Unipro. Until then, we suck it up and make the hardware work, as - * that's the job of the firmware and kernel. - * - */ } static void apb1_log_get(struct es1_ap_dev *es1) -- cgit v1.2.3-59-g8ed1b From 6023629d3676a07e88a4661fca503e3e48d95299 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 4 Apr 2015 10:54:25 +0200 Subject: greybus: Documentation/sysfs: add a proposed sysfs tree for greybus This adds a proposed sysfs layout for greybus to Documentation to make it easier for people to discuss / test things. It includes a module, an interface, a bundle, and a gpbridge binding to that bundle. This was discussed on the projectara software mailing list. Signed-off-by: Greg Kroah-Hartman --- .../Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore | 1 + .../Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore | 1 + .../Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore | 1 + drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/firmware | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/manifest | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_id | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_name | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/state | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/uid | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_id | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_name | 0 .../staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_major | 0 .../staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_minor | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/epm | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/power | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/present | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/02/.gitignore | 1 + drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/firmware | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/version | 0 20 files changed, 4 insertions(+) create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/firmware create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/manifest create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_name create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/uid create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_name create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_major create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_minor create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/epm create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/power create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/present create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/02/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/firmware create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/serial_number create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/version diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/firmware b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/firmware new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/manifest b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/manifest new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_name b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_name new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/state b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/uid b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/uid new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_name b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_name new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_major b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_major new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_minor b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_minor new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/epm b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/epm new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/power b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/power new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/present b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/present new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/02/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/02/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/02/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/firmware b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/firmware new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/serial_number b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/version b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/version new file mode 100644 index 000000000000..e69de29bb2d1 -- cgit v1.2.3-59-g8ed1b From 045235f118917210ab9574c768b2625231e2069b Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Tue, 31 Mar 2015 16:49:56 -0700 Subject: greybus: Initial I2S definitions These are definitions from Mark that I've consolidated into one header file. I'd like to get these merged at some point soon, so the audio driver and gbsim work can avoid having out-of-tree dependencies. Signed-off-by: Mark A. Greer Reviewed-by: Alex Elder Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 4 +- drivers/staging/greybus/i2s.h | 145 +++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/greybus/i2s.h diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 4b2cf9220843..9ab1c82bbf18 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -35,7 +35,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_SDIO = 0x07, GREYBUS_PROTOCOL_BATTERY = 0x08, GREYBUS_PROTOCOL_PWM = 0x09, - GREYBUS_PROTOCOL_I2S = 0x0a, + GREYBUS_PROTOCOL_I2S_MGMT = 0x0a, GREYBUS_PROTOCOL_SPI = 0x0b, GREYBUS_PROTOCOL_DISPLAY = 0x0c, GREYBUS_PROTOCOL_CAMERA = 0x0d, @@ -43,6 +43,8 @@ enum greybus_protocol { GREYBUS_PROTOCOL_LED = 0x0f, GREYBUS_PROTOCOL_VIBRATOR = 0x10, GREYBUS_PROTOCOL_LOOPBACK = 0x11, + GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12, + GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13, /* ... */ GREYBUS_PROTOCOL_VENDOR = 0xff, }; diff --git a/drivers/staging/greybus/i2s.h b/drivers/staging/greybus/i2s.h new file mode 100644 index 000000000000..5c4275682c3a --- /dev/null +++ b/drivers/staging/greybus/i2s.h @@ -0,0 +1,145 @@ +#ifndef __GB_I2S_H__ +#define __GB_I2S_H__ + +#ifndef BIT +#define BIT(n) (1UL << (n)) +#endif + +#define GB_I2S_MGMT_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS 0x02 +#define GB_I2S_MGMT_TYPE_SET_CONFIGURATION 0x03 +#define GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE 0x04 +#define GB_I2S_MGMT_TYPE_GET_PROCESSING_DELAY 0x05 +#define GB_I2S_MGMT_TYPE_SET_START_DELAY 0x06 +#define GB_I2S_MGMT_TYPE_ACTIVATE_CPORT 0x07 +#define GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT 0x08 +#define GB_I2S_MGMT_TYPE_REPORT_EVENT 0x09 + +#define GB_I2S_MGMT_BYTE_ORDER_NA BIT(0) +#define GB_I2S_MGMT_BYTE_ORDER_BE BIT(1) +#define GB_I2S_MGMT_BYTE_ORDER_LE BIT(2) + +#define GB_I2S_MGMT_SPATIAL_LOCATION_FL BIT(0) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FR BIT(1) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FC BIT(2) +#define GB_I2S_MGMT_SPATIAL_LOCATION_LFE BIT(3) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BL BIT(4) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BR BIT(5) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FLC BIT(6) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FRC BIT(7) +#define GB_I2S_MGMT_SPATIAL_LOCATION_C BIT(8) /* BC in USB */ +#define GB_I2S_MGMT_SPATIAL_LOCATION_SL BIT(9) +#define GB_I2S_MGMT_SPATIAL_LOCATION_SR BIT(10) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TC BIT(11) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFL BIT(12) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFC BIT(13) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFR BIT(14) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TBL BIT(15) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TBC BIT(16) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TBR BIT(17) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFLC BIT(18) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFRC BIT(19) +#define GB_I2S_MGMT_SPATIAL_LOCATION_LLFE BIT(20) +#define GB_I2S_MGMT_SPATIAL_LOCATION_RLFE BIT(21) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TSL BIT(22) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TSR BIT(23) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BC BIT(24) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BLC BIT(25) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BRC BIT(26) +#define GB_I2S_MGMT_SPATIAL_LOCATION_RD BIT(31) + +#define GB_I2S_MGMT_PROTOCOL_PCM BIT(0) +#define GB_I2S_MGMT_PROTOCOL_I2S BIT(1) +#define GB_I2S_MGMT_PROTOCOL_LR_STEREO BIT(2) + +#define GB_I2S_MGMT_ROLE_MASTER BIT(0) +#define GB_I2S_MGMT_ROLE_SLAVE BIT(1) + +#define GB_I2S_MGMT_POLARITY_NORMAL BIT(0) +#define GB_I2S_MGMT_POLARITY_REVERSED BIT(1) + +#define GB_I2S_MGMT_EDGE_RISING BIT(0) +#define GB_I2S_MGMT_EDGE_FALLING BIT(1) + +#define GB_I2S_MGMT_EVENT_UNSPECIFIED 0x1 +#define GB_I2S_MGMT_EVENT_HALT 0x2 +#define GB_I2S_MGMT_EVENT_INTERNAL_ERROR 0x3 +#define GB_I2S_MGMT_EVENT_PROTOCOL_ERROR 0x4 +#define GB_I2S_MGMT_EVENT_FAILURE 0x5 +#define GB_I2S_MGMT_EVENT_OUT_OF_SEQUENCE 0x6 +#define GB_I2S_MGMT_EVENT_UNDERRUN 0x7 +#define GB_I2S_MGMT_EVENT_OVERRUN 0x8 +#define GB_I2S_MGMT_EVENT_CLOCKING 0x9 +#define GB_I2S_MGMT_EVENT_DATA_LEN 0xa + +struct gb_i2s_mgmt_configuration { + __le32 sample_frequency; + __u8 num_channels; + __u8 bytes_per_channel; + __u8 byte_order; + __u8 pad; + __le32 spatial_locations; + __le32 ll_protocol; + __u8 ll_bclk_role; + __u8 ll_wclk_role; + __u8 ll_wclk_polarity; + __u8 ll_wclk_change_edge; + __u8 ll_wclk_tx_edge; + __u8 ll_wclk_rx_edge; + __u8 ll_data_offset; + __u8 ll_pad; +}; + +/* get supported configurations request has no payload */ +struct gb_i2s_mgmt_get_supported_configurations_response { + __u8 config_count; + __u8 pad[3]; + struct gb_i2s_mgmt_configuration config[0]; +}; + +struct gb_i2s_mgmt_set_configuration_request { + struct gb_i2s_mgmt_configuration config; +}; +/* set configuration response has no payload */ + +struct gb_i2s_mgmt_set_samples_per_message_request { + __le16 samples_per_message; +}; +/* set samples per message response has no payload */ + +/* get processing request delay has no payload */ +struct gb_i2s_mgmt_get_processing_delay_response { + __le32 microseconds; +}; + +struct gb_i2s_mgmt_set_start_delay_request { + __le32 microseconds; +}; +/* set start delay response has no payload */ + +struct gb_i2s_mgmt_activate_cport_request { + __le16 cport; +}; +/* activate cport response has no payload */ + +struct gb_i2s_mgmt_deactivate_cport_request { + __le16 cport; +}; +/* deactivate cport response has no payload */ + +struct gb_i2s_mgmt_report_event_request { + __u8 event; +}; +/* report event response has no payload */ + +#define GB_I2S_DATA_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2S_DATA_TYPE_SEND_DATA 0x02 + +struct gb_i2s_send_data_request { + __le32 sample_number; + __le32 size; + __u8 data[0]; +}; +/* send data has no response at all */ + +#endif /* __GB_I2S_H__ */ -- cgit v1.2.3-59-g8ed1b From 2352a73212a503dfe77970c48a891c008b7edd5b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 2 Apr 2015 17:53:47 +0530 Subject: greybus: Unregister devices to get them freed Devices registered with the device-core needs to be freed by calling device_unregister(). For module we are calling just put_device() and for bundle, connection and interface we are calling device_del(). All of these are incomplete and so none of them get freed, i.e. the .release() routine is never called for their devices. Module being a special case that it needs to maintain a refcount or a list of interfaces to trace its usage count. I have chosen refcount. And so once the refcount is zero, we can Unregister the device and module will get free as well. Because of this bug in freeing devices, their sysfs directories were not getting removed properly and after a manifest is parsed with the help of gbsim, removing modules was creating problems. The sysfs directory 'greybus' wasn't getting removed. And inserting the modules again resulted in warnings and insmod failure. WARNING: CPU: 3 PID: 4277 at /build/buildd/linux-3.13.0/fs/sysfs/dir.c:486 sysfs_warn_dup+0x86/0xa0() Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 2 +- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/interface.c | 7 +++++-- drivers/staging/greybus/module.c | 14 +++++++++++++- drivers/staging/greybus/module.h | 2 ++ 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 93f80dc5a815..5ced992e17d4 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -140,7 +140,7 @@ void gb_bundle_destroy(struct gb_interface *intf) list_for_each_entry_safe(bundle, temp, &list, links) { list_del(&bundle->links); gb_bundle_connections_exit(bundle); - device_del(&bundle->dev); + device_unregister(&bundle->dev); } } diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 102e1a4c6e74..5ec161b9b2f2 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -252,7 +252,7 @@ void gb_connection_destroy(struct gb_connection *connection) gb_connection_hd_cport_id_free(connection); gb_protocol_put(connection->protocol); - device_del(&connection->dev); + device_unregister(&connection->dev); } int gb_connection_init(struct gb_connection *connection) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index b687908cfa16..122281f2cd2b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -173,6 +173,8 @@ put_module: */ static void gb_interface_destroy(struct gb_interface *intf) { + struct gb_module *module; + if (WARN_ON(!intf)) return; @@ -184,10 +186,11 @@ static void gb_interface_destroy(struct gb_interface *intf) kfree(intf->product_string); kfree(intf->vendor_string); - put_device(&intf->module->dev); /* kref_put(module->hd); */ - device_del(&intf->dev); + module = intf->module; + device_unregister(&intf->dev); + gb_module_remove(module); } /** diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 56a55fea107b..538182b60dd9 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -101,6 +101,7 @@ static struct gb_module *gb_module_create(struct greybus_host_device *hd, return NULL; module->module_id = module_id; + module->refcount = 1; module->dev.parent = hd->parent; module->dev.bus = &greybus_bus_type; module->dev.type = &greybus_module_type; @@ -127,9 +128,20 @@ struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd, struct gb_module *module; module = gb_module_find(module_id); - if (module) + if (module) { + module->refcount++; return module; + } return gb_module_create(hd, module_id); } +void gb_module_remove(struct gb_module *module) +{ + if (!module) + return; + + if (!--module->refcount) + device_unregister(&module->dev); +} + diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 75a8818de1c5..4f02e46301e1 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -13,6 +13,7 @@ struct gb_module { struct device dev; u8 module_id; /* Physical location within the Endo */ + u16 refcount; }; #define to_gb_module(d) container_of(d, struct gb_module, dev) @@ -21,6 +22,7 @@ struct greybus_host_device; /* Greybus "private" definitions */ struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd, u8 module_id); +void gb_module_remove(struct gb_module *module); #endif /* __MODULE_H */ -- cgit v1.2.3-59-g8ed1b From c9d9d0d443afab6c4c1ce295c283f0caab56db16 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Apr 2015 20:31:58 +0530 Subject: greybus: interface: Fetch interface id instead of module id during setup There can be more than one interface on a module and we need to know the interface for which the event has occurred. But at the same time we may not need the module id at all. During initial phase when AP is probed, the AP will receive the unique Endo id which shall be enough to draw relationships between interface and module ids. Code for that isn't available today and so lets create another routine to get module id (which needs to be fixed separately), which will simply return interface id passed to it. Now that we have interface id, update rest of the code to use it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 31 +++++++++++++++--------------- drivers/staging/greybus/interface.c | 38 +++++++++++++++++++------------------ drivers/staging/greybus/interface.h | 8 ++++---- drivers/staging/greybus/module.c | 11 +++++++++++ drivers/staging/greybus/module.h | 1 + drivers/staging/greybus/svc_msg.h | 13 ++++--------- 6 files changed, 55 insertions(+), 47 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index ea197ac57b52..132ecb4a7974 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -140,10 +140,10 @@ static void svc_management(struct svc_function_unipro_management *management, hd->device_id = management->ap_id.device_id; break; case SVC_MANAGEMENT_LINK_UP: - intf = gb_interface_find(hd, management->link_up.module_id); + intf = gb_interface_find(hd, management->link_up.interface_id); if (!intf) { - dev_err(hd->parent, "Module ID %d not found\n", - management->link_up.module_id); + dev_err(hd->parent, "Interface ID %d not found\n", + management->link_up.interface_id); return; } ret = gb_bundle_init(intf, @@ -151,9 +151,8 @@ static void svc_management(struct svc_function_unipro_management *management, management->link_up.device_id); if (ret) { dev_err(hd->parent, - "error %d initializing interface %hhu bundle %hhu\n", - ret, management->link_up.module_id, - management->link_up.interface_id); + "error %d initializing bundles for interface %hhu\n", + ret, management->link_up.interface_id); return; } break; @@ -165,11 +164,11 @@ static void svc_management(struct svc_function_unipro_management *management, static void svc_hotplug(struct svc_function_hotplug *hotplug, int payload_length, struct greybus_host_device *hd) { - u8 module_id = hotplug->module_id; + u8 interface_id = hotplug->interface_id; switch (hotplug->hotplug_event) { case SVC_HOTPLUG_EVENT: - /* Add a new module to the system */ + /* Add a new interface to the system */ if (payload_length < 0x03) { /* Hotplug message is at least 3 bytes big */ dev_err(hd->parent, @@ -177,13 +176,13 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, payload_length); return; } - dev_dbg(hd->parent, "module id %d added\n", module_id); - gb_add_interface(hd, module_id, hotplug->data, + dev_dbg(hd->parent, "interface id %d added\n", interface_id); + gb_add_interface(hd, interface_id, hotplug->data, payload_length - 0x02); break; case SVC_HOTUNPLUG_EVENT: - /* Remove a module from the system */ + /* Remove a interface from the system */ if (payload_length != 0x02) { /* Hotunplug message is only 2 bytes big */ dev_err(hd->parent, @@ -191,8 +190,8 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, payload_length); return; } - dev_dbg(hd->parent, "module id %d removed\n", module_id); - gb_remove_interface(hd, module_id); + dev_dbg(hd->parent, "interface id %d removed\n", interface_id); + gb_remove_interface(hd, interface_id); break; default: @@ -206,7 +205,7 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, static void svc_power(struct svc_function_power *power, int payload_length, struct greybus_host_device *hd) { - u8 module_id = power->module_id; + u8 interface_id = power->interface_id; /* * The AP is only allowed to get a Battery Status message, not a Battery @@ -230,8 +229,8 @@ static void svc_power(struct svc_function_power *power, return; } - dev_dbg(hd->parent, "power status for module id %d is %d\n", - module_id, power->status.status); + dev_dbg(hd->parent, "power status for interface id %d is %d\n", + interface_id, power->status.status); // FIXME - do something with the power information, like update our // battery information... diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 122281f2cd2b..5c64bc773487 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -77,12 +77,12 @@ gb_interface_match_id(struct gb_interface *intf, // FIXME, odds are you don't want to call this function, rework the caller to // not need it please. struct gb_interface *gb_interface_find(struct greybus_host_device *hd, - u8 module_id) + u8 interface_id) { struct gb_interface *intf; list_for_each_entry(intf, &hd->interfaces, links) - if (intf->module->module_id == module_id) + if (intf->interface_id == interface_id) return intf; return NULL; @@ -105,28 +105,28 @@ struct device_type greybus_interface_type = { * phone. An interface is the physical connection on that module. A * module may have more than one interface. * - * Create a gb_interface structure to represent a discovered module. - * The position within the Endo is encoded in the "module_id" argument. + * Create a gb_interface structure to represent a discovered interface. + * The position of interface within the Endo is encoded in "interface_id" + * argument. + * * Returns a pointer to the new interfce or a null pointer if a * failure occurs due to memory exhaustion. */ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, - u8 module_id) + u8 interface_id) { struct gb_module *module; struct gb_interface *intf; int retval; - u8 interface_id = module_id; - // FIXME we need an interface id here to check for this properly! intf = gb_interface_find(hd, interface_id); if (intf) { - dev_err(hd->parent, "Duplicate module id %d will not be created\n", - module_id); + dev_err(hd->parent, "Duplicate interface with interface-id: %d will not be created\n", + interface_id); return NULL; } - module = gb_module_find_or_create(hd, module_id); + module = gb_module_find_or_create(hd, get_module_id(interface_id)); if (!module) return NULL; @@ -136,6 +136,7 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, intf->hd = hd; /* XXX refcount? */ intf->module = module; + intf->interface_id = interface_id; INIT_LIST_HEAD(&intf->bundles); INIT_LIST_HEAD(&intf->manifest_descs); @@ -149,8 +150,8 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, retval = device_add(&intf->dev); if (retval) { - pr_err("failed to add module device for id 0x%02hhx\n", - module_id); + pr_err("failed to add interface device for id 0x%02hhx\n", + interface_id); goto free_intf; } @@ -199,12 +200,12 @@ static void gb_interface_destroy(struct gb_interface *intf) * Pass in a buffer that _should_ contain a Greybus module manifest * and register a greybus device structure with the kernel core. */ -void gb_add_interface(struct greybus_host_device *hd, u8 module_id, - u8 *data, int size) +void gb_add_interface(struct greybus_host_device *hd, u8 interface_id, u8 *data, + int size) { struct gb_interface *intf; - intf = gb_interface_create(hd, module_id); + intf = gb_interface_create(hd, interface_id); if (!intf) { dev_err(hd->parent, "failed to create interface\n"); return; @@ -234,14 +235,15 @@ err_parse: gb_interface_destroy(intf); } -void gb_remove_interface(struct greybus_host_device *hd, u8 module_id) +void gb_remove_interface(struct greybus_host_device *hd, u8 interface_id) { - struct gb_interface *intf = gb_interface_find(hd, module_id); + struct gb_interface *intf = gb_interface_find(hd, interface_id); if (intf) gb_interface_destroy(intf); else - dev_err(hd->parent, "interface id %d not found\n", module_id); + dev_err(hd->parent, "interface id %d not found\n", + interface_id); } void gb_remove_interfaces(struct greybus_host_device *hd) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index f444e311bfac..45ce99609656 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -49,11 +49,11 @@ const struct greybus_interface_id * const struct greybus_interface_id *id); struct gb_interface *gb_interface_find(struct greybus_host_device *hd, - u8 module_id); + u8 interface_id); -void gb_add_interface(struct greybus_host_device *hd, u8 module_id, - u8 *data, int size); -void gb_remove_interface(struct greybus_host_device *hd, u8 module_id); +void gb_add_interface(struct greybus_host_device *hd, u8 interface_id, u8 *data, + int size); +void gb_remove_interface(struct greybus_host_device *hd, u8 interface_id); void gb_remove_interfaces(struct greybus_host_device *hd); diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 538182b60dd9..e8c1c07cff60 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -58,6 +58,17 @@ struct device_type greybus_module_type = { .release = greybus_module_release, }; +u8 get_module_id(u8 interface_id) +{ + /* + * FIXME: + * + * We should be able to find it from Endo ID passed during greybus + * control operation, while setting up AP. + */ + return interface_id; +} + static int module_find(struct device *dev, void *data) { struct gb_module *module; diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 4f02e46301e1..f3e3bdd6a671 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -24,5 +24,6 @@ struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd, u8 module_id); void gb_module_remove(struct gb_module *module); +u8 get_module_id(u8 interface_id); #endif /* __MODULE_H */ diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index 471baa59078e..cb7bb19975de 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -52,13 +52,12 @@ struct svc_function_unipro_set_route { }; struct svc_function_unipro_link_up { - __u8 module_id; - __u8 interface_id; + __u8 interface_id; /* Interface id within the Endo */ __u8 device_id; }; struct svc_function_ap_id { - __u8 module_id; + __u8 interface_id; __u8 device_id; }; @@ -82,13 +81,9 @@ enum svc_function_hotplug_event { SVC_HOTUNPLUG_EVENT = 0x01, }; -/* XXX - * Does a hotplug come from module insertion, or from detection of each - * interface (UniPro device) in a module? Assume the former for now. - */ struct svc_function_hotplug { __u8 hotplug_event; /* enum svc_function_hotplug_event */ - __u8 module_id; + __u8 interface_id; /* Interface id within the Endo */ __u8 data[0]; }; @@ -121,7 +116,7 @@ struct svc_function_power_battery_status_request { */ struct svc_function_power { __u8 power_type; /* enum svc_function_power_type */ - __u8 module_id; + __u8 interface_id; union { struct svc_function_power_battery_status status; struct svc_function_power_battery_status_request request; -- cgit v1.2.3-59-g8ed1b From 83a0cb593b5517a13f88c8f9ae9ce7e43af4b54b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Apr 2015 20:31:59 +0530 Subject: greybus: Add bundle descriptor type A bundle corresponds to a device and a greybus driver binds to it. This patch adds a type and descriptor for bundle. This also shuffles the values of 'enum greybus_descriptor_type' to align them with Greybus Specifications. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 30 ++++++++++++++++++++++++++++-- drivers/staging/greybus/manifest.c | 2 ++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 9ab1c82bbf18..96702f4ce14a 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -17,11 +17,12 @@ enum greybus_descriptor_type { GREYBUS_TYPE_INVALID = 0x00, - GREYBUS_TYPE_MODULE = 0x01, + GREYBUS_TYPE_INTERFACE = 0x01, GREYBUS_TYPE_STRING = 0x02, - GREYBUS_TYPE_INTERFACE = 0x03, + GREYBUS_TYPE_BUNDLE = 0x03, GREYBUS_TYPE_CPORT = 0x04, GREYBUS_TYPE_CLASS = 0x05, + GREYBUS_TYPE_MODULE = 0x06, }; enum greybus_protocol { @@ -108,6 +109,30 @@ struct greybus_descriptor_interface { __u8 id; /* module-relative id (0..) */ }; +/* + * An bundle descriptor defines an identification number and a class type for + * each bundle. + * + * @id: Uniquely identifies a bundle within a interface, its sole purpose is to + * allow CPort descriptors to specify which bundle they are associated with. + * The first bundle will have id 0, second will have 1 and so on. + * + * The largest CPort id associated with an bundle (defined by a + * CPort descriptor in the manifest) is used to determine how to + * encode the device id and module number in UniPro packets + * that use the bundle. + * + * @class_type: It is used by kernel to know the functionality provided by the + * bundle and will be matched against drivers functinality while probing greybus + * driver. It should contain one of the values defined in + * 'enum greybus_class_type'. + * + */ +struct greybus_descriptor_bundle { + __u8 id; /* interface-relative id (0..) */ + __u8 class_type; +}; + /* * A CPort descriptor indicates the id of the bundle within the * module it's associated with, along with the CPort id used to @@ -139,6 +164,7 @@ struct greybus_descriptor { struct greybus_descriptor_module module; struct greybus_descriptor_string string; struct greybus_descriptor_interface interface; + struct greybus_descriptor_bundle bundle; struct greybus_descriptor_cport cport; struct greybus_descriptor_class class; }; diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index c29a0c822f23..c00e378453df 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -26,6 +26,8 @@ static const char *get_descriptor_type_string(u8 type) return "interface"; case GREYBUS_TYPE_CPORT: return "cport"; + case GREYBUS_TYPE_BUNDLE: + return "bundle"; case GREYBUS_TYPE_CLASS: return "class"; default: -- cgit v1.2.3-59-g8ed1b From 581baacd33b087e4f7322f42ca8f336f7f0acc4d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 3 Apr 2015 12:00:48 +0530 Subject: greybus: hid: Use payload-size to get report size Report size isn't passed as first two bytes of the report according to USB-HID spec. Get it from payload-size. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 5935aa6c6334..556cf9b0fe93 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -155,7 +155,6 @@ static int gb_hid_irq_handler(u8 type, struct gb_operation *op) struct gb_connection *connection = op->connection; struct gb_hid *ghid = connection->private; struct gb_hid_input_report_request *request = op->request->payload; - int size; if (type != GB_HID_TYPE_IRQ_EVENT) { dev_err(&connection->dev, @@ -163,24 +162,9 @@ static int gb_hid_irq_handler(u8 type, struct gb_operation *op) return -EINVAL; } - if (op->request->payload_size < 2) { - dev_err(&connection->dev, "short report received\n"); - return -EINVAL; - } - - /* - * FIXME: add report size to Greybus HID protocol if we need to parse - * it here. - */ - size = request->report[0] | request->report[1] << 8; - if (size < 2 || size > op->request->payload_size - 2) { - dev_err(&connection->dev, "bad report size: %d\n", size); - return -EINVAL; - } - if (test_bit(GB_HID_STARTED, &ghid->flags)) hid_input_report(ghid->hid, HID_INPUT_REPORT, - request->report + 2, size - 2, 1); + request->report, op->request->payload_size, 1); return 0; } -- cgit v1.2.3-59-g8ed1b From 7c183f70ed5b1fa368559031b1ef2245827f2918 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Apr 2015 20:32:00 +0530 Subject: greybus: bundle: Create bundles using bundle descriptors Currently we are creating bundles based on interface descriptors. An interface can have one or more bundles associated with it and so a bundle must be created based on a bundle descriptor. Also get class_type from bundle descriptor. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 10 ++++++---- drivers/staging/greybus/bundle.h | 4 +++- drivers/staging/greybus/manifest.c | 14 +++++++++----- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 5ced992e17d4..47a3413b6f21 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -80,7 +80,8 @@ void gb_bundle_bind_protocols(void) * bundle. Returns a pointer to the new bundle or a null * pointer if a failure occurs due to memory exhaustion. */ -struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 interface_id) +struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, + u8 class_type) { struct gb_bundle *bundle; int retval; @@ -90,7 +91,8 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 interface_id) return NULL; bundle->intf = intf; - bundle->id = interface_id; + bundle->id = bundle_id; + bundle->class_type = class_type; INIT_LIST_HEAD(&bundle->connections); /* Invalid device id to start with */ @@ -103,12 +105,12 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 interface_id) bundle->dev.type = &greybus_bundle_type; bundle->dev.groups = bundle_groups; device_initialize(&bundle->dev); - dev_set_name(&bundle->dev, "%s:%d", dev_name(&intf->dev), interface_id); + dev_set_name(&bundle->dev, "%s:%d", dev_name(&intf->dev), bundle_id); retval = device_add(&bundle->dev); if (retval) { pr_err("failed to add bundle device for id 0x%02hhx\n", - interface_id); + bundle_id); put_device(&bundle->dev); kfree(bundle); return NULL; diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 62969cf0fa1f..385c90a5e9f8 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -17,6 +17,7 @@ struct gb_bundle { struct device dev; struct gb_interface *intf; u8 id; + u8 class_type; u8 device_id; struct list_head connections; @@ -27,7 +28,8 @@ struct gb_bundle { #define GB_DEVICE_ID_BAD 0xff /* Greybus "private" definitions" */ -struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 module_id); +struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, + u8 class_type); void gb_bundle_destroy(struct gb_interface *intf); int gb_bundle_init(struct gb_interface *intf, u8 module_id, u8 device_id); diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index c00e378453df..8541a2a9f0d2 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -108,6 +108,9 @@ static int identify_descriptor(struct gb_interface *intf, break; case GREYBUS_TYPE_INTERFACE: break; + case GREYBUS_TYPE_BUNDLE: + expected_size += sizeof(struct greybus_descriptor_bundle); + break; case GREYBUS_TYPE_CPORT: expected_size += sizeof(struct greybus_descriptor_cport); break; @@ -237,7 +240,7 @@ static u32 gb_manifest_parse_cports(struct gb_interface *intf, /* * Find bundle descriptors in the manifest and set up their data * structures. Returns the number of bundles set up for the - * given module. + * given interface. */ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) { @@ -245,13 +248,13 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) while (true) { struct manifest_desc *descriptor; - struct greybus_descriptor_interface *desc_interface; + struct greybus_descriptor_bundle *desc_bundle; struct gb_bundle *bundle; bool found = false; /* Find an bundle descriptor */ list_for_each_entry(descriptor, &intf->manifest_descs, links) { - if (descriptor->type == GREYBUS_TYPE_INTERFACE) { + if (descriptor->type == GREYBUS_TYPE_BUNDLE) { found = true; break; } @@ -260,8 +263,9 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) break; /* Found one. Set up its bundle structure*/ - desc_interface = descriptor->data; - bundle = gb_bundle_create(intf, desc_interface->id); + desc_bundle = descriptor->data; + bundle = gb_bundle_create(intf, desc_bundle->id, + desc_bundle->class_type); if (!bundle) return 0; /* Error */ -- cgit v1.2.3-59-g8ed1b From bb97ea813b1c31a0fc78af3d06a8dbd793ea372b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Apr 2015 20:32:01 +0530 Subject: greybus: bundle: Initialize all bundles on link-up An interface can have 1 or more bundles. On link-up event, we must initialize all the bundles associated with the interface. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 4 +--- drivers/staging/greybus/bundle.c | 27 ++++++++++++++++++++------- drivers/staging/greybus/bundle.h | 3 ++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 132ecb4a7974..1dc13396a567 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -146,9 +146,7 @@ static void svc_management(struct svc_function_unipro_management *management, management->link_up.interface_id); return; } - ret = gb_bundle_init(intf, - management->link_up.interface_id, - management->link_up.device_id); + ret = gb_bundles_init(intf, management->link_up.device_id); if (ret) { dev_err(hd->parent, "error %d initializing bundles for interface %hhu\n", diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 47a3413b6f21..969197872484 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -146,16 +146,11 @@ void gb_bundle_destroy(struct gb_interface *intf) } } -int gb_bundle_init(struct gb_interface *intf, u8 bundle_id, u8 device_id) +int gb_bundle_init(struct gb_bundle *bundle, u8 device_id) { - struct gb_bundle *bundle; + struct gb_interface *intf = bundle->intf; int ret; - bundle = gb_bundle_find(intf, bundle_id); - if (!bundle) { - dev_err(intf->hd->parent, "bundle %hhu not found\n", bundle_id); - return -ENOENT; - } bundle->device_id = device_id; ret = svc_set_route_send(bundle, intf->hd); @@ -175,6 +170,24 @@ int gb_bundle_init(struct gb_interface *intf, u8 bundle_id, u8 device_id) return 0; } +int gb_bundles_init(struct gb_interface *intf, u8 device_id) +{ + struct gb_bundle *bundle; + int ret = 0; + + list_for_each_entry(bundle, &intf->bundles, links) { + ret = gb_bundle_init(bundle, device_id); + if (ret) { + dev_err(intf->hd->parent, + "Failed to initialize bundle %hhu\n", + bundle->id); + break; + } + } + + return ret; +} + struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id) { struct gb_bundle *bundle; diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 385c90a5e9f8..2948098070c4 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -31,7 +31,8 @@ struct gb_bundle { struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, u8 class_type); void gb_bundle_destroy(struct gb_interface *intf); -int gb_bundle_init(struct gb_interface *intf, u8 module_id, u8 device_id); +int gb_bundle_init(struct gb_bundle *bundle, u8 device_id); +int gb_bundles_init(struct gb_interface *intf, u8 device_id); struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id); void gb_bundle_bind_protocols(void); -- cgit v1.2.3-59-g8ed1b From a93db2d1f6939bf260dbdf0d32a20eda3ad2e620 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Apr 2015 20:32:02 +0530 Subject: greybus: manifest: Use interface descriptor instead of module descriptor to get information A module can have more than one interfaces and we get hotplug events or manifests for interfaces, not modules. Details like version, vendor, product id, etc. can be different for different interfaces within the same module and so shall be fetched from interface descriptor instead of module descriptor. So what we have been doing for module descriptors until now must be done for interface descriptors. There can only be one interface descriptor in the manifest. Module descriptor isn't used anymore and probably most of its fields can be removed now. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 21 ++++++-------- drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/interface.h | 2 +- drivers/staging/greybus/manifest.c | 45 +++++++++++++++--------------- drivers/staging/greybus/manifest.h | 2 +- 5 files changed, 35 insertions(+), 37 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 96702f4ce14a..143e28451e7b 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -1,5 +1,5 @@ /* - * Greybus module manifest definition + * Greybus manifest definition * * See "Greybus Application Protocol" document (version 0.1) for * details on these values and structures. @@ -94,19 +94,16 @@ struct greybus_descriptor_string { }; /* - * An interface descriptor simply defines a module-unique id for - * each interface present on a module. Its sole purpose is to allow - * CPort descriptors to specify which interface they are associated - * with. Normally there's only one interface, with id 0. The - * second one must have id 1, and so on consecutively. - * - * The largest CPort id associated with an interface (defined by a - * CPort descriptor in the manifest) is used to determine how to - * encode the device id and module number in UniPro packets - * that use the interface. + * An interface descriptor describes information about an interface as a whole, + * *not* the functions within it. */ struct greybus_descriptor_interface { - __u8 id; /* module-relative id (0..) */ + __le16 vendor; + __le16 product; + __le16 version; // TODO - remove after Dec demo. + __u8 vendor_stringid; + __u8 product_stringid; + __le64 unique_id; }; /* diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 5c64bc773487..4d5f1907ef50 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -197,7 +197,7 @@ static void gb_interface_destroy(struct gb_interface *intf) /** * gb_add_interface * - * Pass in a buffer that _should_ contain a Greybus module manifest + * Pass in a buffer that _should_ contain a Greybus manifest * and register a greybus device structure with the kernel core. */ void gb_add_interface(struct greybus_host_device *hd, u8 interface_id, u8 *data, diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 45ce99609656..147c31119bec 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -19,7 +19,7 @@ struct gb_interface { struct list_head manifest_descs; u8 interface_id; /* Physical location within the Endo */ - /* Information taken from the manifest module descriptor */ + /* Information taken from the manifest descriptor */ u16 vendor; u16 product; char *vendor_string; diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 8541a2a9f0d2..f3d3a2f32b47 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -1,5 +1,5 @@ /* - * Greybus module manifest parsing + * Greybus manifest parsing * * Copyright 2014-2015 Google Inc. * Copyright 2014-2015 Linaro Ltd. @@ -40,7 +40,7 @@ static const char *get_descriptor_type_string(u8 type) * We scan the manifest once to identify where all the descriptors * are. The result is a list of these manifest_desc structures. We * then pick through them for what we're looking for (starting with - * the module descriptor). As each is processed we remove it from + * the interface descriptor). As each is processed we remove it from * the list. When we're done the list should (probably) be empty. */ struct manifest_desc { @@ -107,6 +107,7 @@ static int identify_descriptor(struct gb_interface *intf, expected_size += desc->string.length; break; case GREYBUS_TYPE_INTERFACE: + expected_size += sizeof(struct greybus_descriptor_interface); break; case GREYBUS_TYPE_BUNDLE: expected_size += sizeof(struct greybus_descriptor_bundle); @@ -282,28 +283,28 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) return count; } -static bool gb_manifest_parse_module(struct gb_interface *intf, - struct manifest_desc *module_desc) +static bool gb_manifest_parse_interface(struct gb_interface *intf, + struct manifest_desc *interface_desc) { - struct greybus_descriptor_module *desc_module = module_desc->data; + struct greybus_descriptor_interface *desc_intf = interface_desc->data; /* Handle the strings first--they can fail */ - intf->vendor_string = gb_string_get(intf, desc_module->vendor_stringid); + intf->vendor_string = gb_string_get(intf, desc_intf->vendor_stringid); if (IS_ERR(intf->vendor_string)) return false; intf->product_string = gb_string_get(intf, - desc_module->product_stringid); + desc_intf->product_stringid); if (IS_ERR(intf->product_string)) { goto out_free_vendor_string; } - intf->vendor = le16_to_cpu(desc_module->vendor); - intf->product = le16_to_cpu(desc_module->product); - intf->unique_id = le64_to_cpu(desc_module->unique_id); + intf->vendor = le16_to_cpu(desc_intf->vendor); + intf->product = le16_to_cpu(desc_intf->product); + intf->unique_id = le64_to_cpu(desc_intf->unique_id); - /* Release the module descriptor, now that we're done with it */ - release_manifest_descriptor(module_desc); + /* Release the interface descriptor, now that we're done with it */ + release_manifest_descriptor(interface_desc); /* An interface must have at least one bundle descriptor */ if (!gb_manifest_parse_bundles(intf)) { @@ -323,7 +324,7 @@ out_free_vendor_string: } /* - * Parse a buffer containing a module manifest. + * Parse a buffer containing a Interface manifest. * * If we find anything wrong with the content/format of the buffer * we reject it. @@ -335,7 +336,7 @@ out_free_vendor_string: * the descriptors it contains, keeping track for each its type * and the location size of its data in the buffer. * - * Next we scan the descriptors, looking for a module descriptor; + * Next we scan the descriptors, looking for a interface descriptor; * there must be exactly one of those. When found, we record the * information it contains, and then remove that descriptor (and any * string descriptors it refers to) from further consideration. @@ -351,7 +352,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) struct greybus_manifest_header *header; struct greybus_descriptor *desc; struct manifest_desc *descriptor; - struct manifest_desc *module_desc = NULL; + struct manifest_desc *interface_desc = NULL; u16 manifest_size; u32 found = 0; bool result; @@ -399,28 +400,28 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) size -= desc_size; } - /* There must be a single module descriptor */ + /* There must be a single interface descriptor */ list_for_each_entry(descriptor, &intf->manifest_descs, links) { - if (descriptor->type == GREYBUS_TYPE_MODULE) + if (descriptor->type == GREYBUS_TYPE_INTERFACE) if (!found++) - module_desc = descriptor; + interface_desc = descriptor; } if (found != 1) { - pr_err("manifest must have 1 module descriptor (%u found)\n", + pr_err("manifest must have 1 interface descriptor (%u found)\n", found); result = false; goto out; } - /* Parse the module manifest, starting with the module descriptor */ - result = gb_manifest_parse_module(intf, module_desc); + /* Parse the manifest, starting with the interface descriptor */ + result = gb_manifest_parse_interface(intf, interface_desc); /* * We really should have no remaining descriptors, but we * don't know what newer format manifests might leave. */ if (result && !list_empty(&intf->manifest_descs)) - pr_info("excess descriptors in module manifest\n"); + pr_info("excess descriptors in interface manifest\n"); out: release_manifest_descriptors(intf); diff --git a/drivers/staging/greybus/manifest.h b/drivers/staging/greybus/manifest.h index 90fb62df8063..d96428407cd7 100644 --- a/drivers/staging/greybus/manifest.h +++ b/drivers/staging/greybus/manifest.h @@ -1,5 +1,5 @@ /* - * Greybus module manifest parsing + * Greybus manifest parsing * * Copyright 2014 Google Inc. * Copyright 2014 Linaro Ltd. -- cgit v1.2.3-59-g8ed1b From 8e2e22d7830616ac37a9045e9e749e35cbc10ffe Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Apr 2015 20:32:03 +0530 Subject: greybus: drop module descriptors Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 15 --------------- drivers/staging/greybus/manifest.c | 5 ----- 2 files changed, 20 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 143e28451e7b..a3386836d6d7 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -22,7 +22,6 @@ enum greybus_descriptor_type { GREYBUS_TYPE_BUNDLE = 0x03, GREYBUS_TYPE_CPORT = 0x04, GREYBUS_TYPE_CLASS = 0x05, - GREYBUS_TYPE_MODULE = 0x06, }; enum greybus_protocol { @@ -69,19 +68,6 @@ enum greybus_class_type { GREYBUS_CLASS_VENDOR = 0xff, }; -/* - * A module descriptor describes information about a module as a - * whole, *not* the functions within it. - */ -struct greybus_descriptor_module { - __le16 vendor; - __le16 product; - __le16 version; // TODO - remove after Dec demo. - __u8 vendor_stringid; - __u8 product_stringid; - __le64 unique_id; -}; - /* * The string in a string descriptor is not NUL-terminated. The * size of the descriptor will be rounded up to a multiple of 4 @@ -158,7 +144,6 @@ struct greybus_descriptor_header { struct greybus_descriptor { struct greybus_descriptor_header header; union { - struct greybus_descriptor_module module; struct greybus_descriptor_string string; struct greybus_descriptor_interface interface; struct greybus_descriptor_bundle bundle; diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index f3d3a2f32b47..12eee3adf91a 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -18,8 +18,6 @@ static const char *get_descriptor_type_string(u8 type) switch(type) { case GREYBUS_TYPE_INVALID: return "invalid"; - case GREYBUS_TYPE_MODULE: - return "module"; case GREYBUS_TYPE_STRING: return "string"; case GREYBUS_TYPE_INTERFACE: @@ -99,9 +97,6 @@ static int identify_descriptor(struct gb_interface *intf, expected_size = sizeof(*desc_header); switch (desc_header->type) { - case GREYBUS_TYPE_MODULE: - expected_size += sizeof(struct greybus_descriptor_module); - break; case GREYBUS_TYPE_STRING: expected_size += sizeof(struct greybus_descriptor_string); expected_size += desc->string.length; -- cgit v1.2.3-59-g8ed1b From 9f5f30e712430912f4cff65e97dd36f9b3bdbe4e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Apr 2015 20:32:04 +0530 Subject: greybus: driver corresponds to a bundle, not interface A Greybus driver will bind to a bundle, not an interface. Lets follow this rule in code. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 48 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/bundle.h | 4 +++ drivers/staging/greybus/core.c | 18 +++++++------- drivers/staging/greybus/greybus.h | 12 ++++----- drivers/staging/greybus/greybus_id.h | 6 +++-- drivers/staging/greybus/interface.c | 34 ------------------------- drivers/staging/greybus/interface.h | 4 --- 7 files changed, 71 insertions(+), 55 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 969197872484..ce7db9734dad 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -22,8 +22,18 @@ static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(device_id); +static ssize_t class_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + + return sprintf(buf, "%d\n", bundle->class_type); +} +static DEVICE_ATTR_RO(class_type); + static struct attribute *bundle_attrs[] = { &dev_attr_device_id.attr, + &dev_attr_class_type.attr, NULL, }; @@ -41,6 +51,44 @@ struct device_type greybus_bundle_type = { .release = gb_bundle_release, }; +static int gb_bundle_match_one_id(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && + (id->vendor != bundle->intf->vendor)) + return 0; + + if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && + (id->product != bundle->intf->product)) + return 0; + + if ((id->match_flags & GREYBUS_ID_MATCH_SERIAL) && + (id->unique_id != bundle->intf->unique_id)) + return 0; + + if ((id->match_flags & GREYBUS_ID_MATCH_CLASS_TYPE) && + (id->class_type != bundle->class_type)) + return 0; + + return 1; +} + +const struct greybus_bundle_id * +gb_bundle_match_id(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + if (id == NULL) + return NULL; + + for (; id->vendor || id->product || id->unique_id || id->class_type || + id->driver_info; id++) { + if (gb_bundle_match_one_id(bundle, id)) + return id; + } + + return NULL; +} + /* XXX This could be per-host device or per-module */ static DEFINE_SPINLOCK(gb_bundles_lock); diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 2948098070c4..1fcf5b8f2e53 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -37,4 +37,8 @@ int gb_bundles_init(struct gb_interface *intf, u8 device_id); struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id); void gb_bundle_bind_protocols(void); +const struct greybus_bundle_id * + gb_bundle_match_id(struct gb_bundle *bundle, + const struct greybus_bundle_id *id); + #endif /* __BUNDLE_H */ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 96265a1d668f..0ac2fb808734 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -34,10 +34,10 @@ EXPORT_SYMBOL_GPL(greybus_disabled); static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(drv); - struct gb_interface *intf = to_gb_interface(dev); - const struct greybus_interface_id *id; + struct gb_bundle *bundle = to_gb_bundle(dev); + const struct greybus_bundle_id *id; - id = gb_interface_match_id(intf, driver->id_table); + id = gb_bundle_match_id(bundle, driver->id_table); if (id) return 1; /* FIXME - Dynamic ids? */ @@ -97,16 +97,16 @@ struct bus_type greybus_bus_type = { static int greybus_probe(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct gb_interface *intf = to_gb_interface(dev); - const struct greybus_interface_id *id; + struct gb_bundle *bundle = to_gb_bundle(dev); + const struct greybus_bundle_id *id; int retval; /* match id */ - id = gb_interface_match_id(intf, driver->id_table); + id = gb_bundle_match_id(bundle, driver->id_table); if (!id) return -ENODEV; - retval = driver->probe(intf, id); + retval = driver->probe(bundle, id); if (retval) return retval; @@ -116,9 +116,9 @@ static int greybus_probe(struct device *dev) static int greybus_remove(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); - struct gb_interface *intf = to_gb_interface(dev); + struct gb_bundle *bundle = to_gb_bundle(dev); - driver->disconnect(intf); + driver->disconnect(bundle); return 0; } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 5a7f6227c1e5..a466fc5cdae4 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -120,14 +120,14 @@ void greybus_remove_hd(struct greybus_host_device *hd); struct greybus_driver { const char *name; - int (*probe)(struct gb_interface *intf, - const struct greybus_interface_id *id); - void (*disconnect)(struct gb_interface *intf); + int (*probe)(struct gb_bundle *bundle, + const struct greybus_bundle_id *id); + void (*disconnect)(struct gb_bundle *bundle); - int (*suspend)(struct gb_interface *intf, pm_message_t message); - int (*resume)(struct gb_interface *intf); + int (*suspend)(struct gb_bundle *bundle, pm_message_t message); + int (*resume)(struct gb_bundle *bundle); - const struct greybus_interface_id *id_table; + const struct greybus_bundle_id *id_table; struct device_driver driver; }; diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index 3c462ef7ba6c..4204a8c7ca74 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -9,18 +9,20 @@ #include -struct greybus_interface_id { +struct greybus_bundle_id { __u16 match_flags; __u16 vendor; __u16 product; + __u8 class_type; __u64 unique_id; kernel_ulong_t driver_info __aligned(sizeof(kernel_ulong_t)); }; -/* Used to match the greybus_interface_id */ +/* Used to match the greybus_bundle_id */ #define GREYBUS_ID_MATCH_VENDOR BIT(0) #define GREYBUS_ID_MATCH_PRODUCT BIT(1) #define GREYBUS_ID_MATCH_SERIAL BIT(2) +#define GREYBUS_ID_MATCH_CLASS_TYPE BIT(3) #endif /* __LINUX_GREYBUS_ID_H */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 4d5f1907ef50..5b1d5dde3c90 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -40,40 +40,6 @@ ATTRIBUTE_GROUPS(interface); /* XXX This could be per-host device */ static DEFINE_SPINLOCK(gb_interfaces_lock); -static int gb_interface_match_one_id(struct gb_interface *intf, - const struct greybus_interface_id *id) -{ - if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && - (id->vendor != intf->vendor)) - return 0; - - if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && - (id->product != intf->product)) - return 0; - - if ((id->match_flags & GREYBUS_ID_MATCH_SERIAL) && - (id->unique_id != intf->unique_id)) - return 0; - - return 1; -} - -const struct greybus_interface_id * -gb_interface_match_id(struct gb_interface *intf, - const struct greybus_interface_id *id) -{ - if (id == NULL) - return NULL; - - for (; id->vendor || id->product || id->unique_id || - id->driver_info; id++) { - if (gb_interface_match_one_id(intf, id)) - return id; - } - - return NULL; -} - // FIXME, odds are you don't want to call this function, rework the caller to // not need it please. struct gb_interface *gb_interface_find(struct greybus_host_device *hd, diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 147c31119bec..9ee5b551029e 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -44,10 +44,6 @@ static inline void *gb_interface_get_drvdata(struct gb_interface *intf) /* Greybus "private" definitions */ -const struct greybus_interface_id * - gb_interface_match_id(struct gb_interface *intf, - const struct greybus_interface_id *id); - struct gb_interface *gb_interface_find(struct greybus_host_device *hd, u8 interface_id); -- cgit v1.2.3-59-g8ed1b From 88e6d37c448062b86211e89d23348d952fd166c0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 6 Apr 2015 15:49:36 +0530 Subject: greybus: bundle: s/class_type/class Alex suggested to name it class instead of class type. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 20 ++++++++++---------- drivers/staging/greybus/bundle.h | 4 ++-- drivers/staging/greybus/greybus_id.h | 4 ++-- drivers/staging/greybus/greybus_manifest.h | 6 +++--- drivers/staging/greybus/manifest.c | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index ce7db9734dad..3f1aa6490e48 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -22,18 +22,18 @@ static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(device_id); -static ssize_t class_type_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t class_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct gb_bundle *bundle = to_gb_bundle(dev); - return sprintf(buf, "%d\n", bundle->class_type); + return sprintf(buf, "%d\n", bundle->class); } -static DEVICE_ATTR_RO(class_type); +static DEVICE_ATTR_RO(class); static struct attribute *bundle_attrs[] = { &dev_attr_device_id.attr, - &dev_attr_class_type.attr, + &dev_attr_class.attr, NULL, }; @@ -66,8 +66,8 @@ static int gb_bundle_match_one_id(struct gb_bundle *bundle, (id->unique_id != bundle->intf->unique_id)) return 0; - if ((id->match_flags & GREYBUS_ID_MATCH_CLASS_TYPE) && - (id->class_type != bundle->class_type)) + if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) && + (id->class != bundle->class)) return 0; return 1; @@ -80,7 +80,7 @@ gb_bundle_match_id(struct gb_bundle *bundle, if (id == NULL) return NULL; - for (; id->vendor || id->product || id->unique_id || id->class_type || + for (; id->vendor || id->product || id->unique_id || id->class || id->driver_info; id++) { if (gb_bundle_match_one_id(bundle, id)) return id; @@ -129,7 +129,7 @@ void gb_bundle_bind_protocols(void) * pointer if a failure occurs due to memory exhaustion. */ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, - u8 class_type) + u8 class) { struct gb_bundle *bundle; int retval; @@ -140,7 +140,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, bundle->intf = intf; bundle->id = bundle_id; - bundle->class_type = class_type; + bundle->class = class; INIT_LIST_HEAD(&bundle->connections); /* Invalid device id to start with */ diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 1fcf5b8f2e53..3265a008e5df 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -17,7 +17,7 @@ struct gb_bundle { struct device dev; struct gb_interface *intf; u8 id; - u8 class_type; + u8 class; u8 device_id; struct list_head connections; @@ -29,7 +29,7 @@ struct gb_bundle { /* Greybus "private" definitions" */ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, - u8 class_type); + u8 class); void gb_bundle_destroy(struct gb_interface *intf); int gb_bundle_init(struct gb_bundle *bundle, u8 device_id); int gb_bundles_init(struct gb_interface *intf, u8 device_id); diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index 4204a8c7ca74..8e76d4218c14 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -13,7 +13,7 @@ struct greybus_bundle_id { __u16 match_flags; __u16 vendor; __u16 product; - __u8 class_type; + __u8 class; __u64 unique_id; kernel_ulong_t driver_info __aligned(sizeof(kernel_ulong_t)); @@ -23,6 +23,6 @@ struct greybus_bundle_id { #define GREYBUS_ID_MATCH_VENDOR BIT(0) #define GREYBUS_ID_MATCH_PRODUCT BIT(1) #define GREYBUS_ID_MATCH_SERIAL BIT(2) -#define GREYBUS_ID_MATCH_CLASS_TYPE BIT(3) +#define GREYBUS_ID_MATCH_CLASS BIT(3) #endif /* __LINUX_GREYBUS_ID_H */ diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index a3386836d6d7..e855adab3c22 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -93,7 +93,7 @@ struct greybus_descriptor_interface { }; /* - * An bundle descriptor defines an identification number and a class type for + * An bundle descriptor defines an identification number and a class for * each bundle. * * @id: Uniquely identifies a bundle within a interface, its sole purpose is to @@ -105,7 +105,7 @@ struct greybus_descriptor_interface { * encode the device id and module number in UniPro packets * that use the bundle. * - * @class_type: It is used by kernel to know the functionality provided by the + * @class: It is used by kernel to know the functionality provided by the * bundle and will be matched against drivers functinality while probing greybus * driver. It should contain one of the values defined in * 'enum greybus_class_type'. @@ -113,7 +113,7 @@ struct greybus_descriptor_interface { */ struct greybus_descriptor_bundle { __u8 id; /* interface-relative id (0..) */ - __u8 class_type; + __u8 class; }; /* diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 12eee3adf91a..d6cafebefd26 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -261,7 +261,7 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) /* Found one. Set up its bundle structure*/ desc_bundle = descriptor->data; bundle = gb_bundle_create(intf, desc_bundle->id, - desc_bundle->class_type); + desc_bundle->class); if (!bundle) return 0; /* Error */ -- cgit v1.2.3-59-g8ed1b From b7744b7f97d1c86d2a480fdc53a22a042ac4dda9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:09 +0200 Subject: greybus: es1: drop unnecessary casts Drop unnecessary explicit casts. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index f559c1d53e89..7f7e2fdc094f 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -244,7 +244,7 @@ static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, pr_err("request to send inbound data buffer\n"); return ERR_PTR(-EINVAL); } - if (cport_id > (u16)U8_MAX) { + if (cport_id > U8_MAX) { pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); return ERR_PTR(-EINVAL); } @@ -423,7 +423,7 @@ static void cport_in_callback(struct urb *urb) * the rest of the stream is "real" data */ data = urb->transfer_buffer; - cport_id = (u16)data[0]; + cport_id = data[0]; data = &data[1]; /* Pass this data to the greybus core */ -- cgit v1.2.3-59-g8ed1b From 79940cf8759558d55b597e27353f6521516aa7ac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:10 +0200 Subject: greybus: es1: fix DMA-buffer on stack A stack-allocated buffer is not generally DMA-able and must not be used for USB control transfers. Note that the memset and extra buffer byte were redundant as no more than the bytes actually transferred was ever added to the fifo. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 7f7e2fdc094f..f2c1cde96221 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -454,14 +454,13 @@ static void cport_out_callback(struct urb *urb) free_urb(es1, urb); } -static void apb1_log_get(struct es1_ap_dev *es1) +#define APB1_LOG_MSG_SIZE 64 +static void apb1_log_get(struct es1_ap_dev *es1, char *buf) { - char buf[65]; int retval; /* SVC messages go down our control pipe */ do { - memset(buf, 0, 65); retval = usb_control_msg(es1->usb_dev, usb_rcvctrlpipe(es1->usb_dev, es1->control_endpoint), @@ -469,7 +468,7 @@ static void apb1_log_get(struct es1_ap_dev *es1) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, buf, - 64, + APB1_LOG_MSG_SIZE, ES1_TIMEOUT); if (retval > 0) kfifo_in(&apb1_log_fifo, buf, retval); @@ -478,10 +477,20 @@ static void apb1_log_get(struct es1_ap_dev *es1) static int apb1_log_poll(void *data) { + struct es1_ap_dev *es1 = data; + char *buf; + + buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + while (!kthread_should_stop()) { msleep(1000); - apb1_log_get((struct es1_ap_dev *)data); + apb1_log_get(es1, buf); } + + kfree(buf); + return 0; } -- cgit v1.2.3-59-g8ed1b From bfd9a94d1aa2a8592749342d8024543ae3ff9e57 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:11 +0200 Subject: greybus: es1: fix buffer-size limit The maximum buffer size does not include the headroom, so subtract the headroom size from the actual buffer size. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index f2c1cde96221..b9fd5111d3f9 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -132,7 +132,7 @@ static void hd_buffer_constraints(struct greybus_host_device *hd) * that's better aligned for the user. */ hd->buffer_headroom = sizeof(u32); /* For cport id */ - hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; + hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX - hd->buffer_headroom; BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); } -- cgit v1.2.3-59-g8ed1b From c15ccabe8105e2ef5e34e610c068e6f50ff08598 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:12 +0200 Subject: greybus: es2: sync up with recent es1 changes Fix two bugs in es2 and do some minor clean up. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index a6c47a3dd62f..526e23c8b416 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -132,7 +132,7 @@ static void hd_buffer_constraints(struct greybus_host_device *hd) * that's better aligned for the user. */ hd->buffer_headroom = sizeof(u32); /* For cport id */ - hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; + hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX - hd->buffer_headroom; BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); } @@ -244,7 +244,7 @@ static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, pr_err("request to send inbound data buffer\n"); return ERR_PTR(-EINVAL); } - if (cport_id > (u16)U8_MAX) { + if (cport_id > U8_MAX) { pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); return ERR_PTR(-EINVAL); } @@ -423,7 +423,7 @@ static void cport_in_callback(struct urb *urb) * the rest of the stream is "real" data */ data = urb->transfer_buffer; - cport_id = (u16)data[0]; + cport_id = data[0]; data = &data[1]; /* Pass this data to the greybus core */ @@ -454,14 +454,13 @@ static void cport_out_callback(struct urb *urb) free_urb(es1, urb); } -static void apb1_log_get(struct es1_ap_dev *es1) +#define APB1_LOG_MSG_SIZE 64 +static void apb1_log_get(struct es1_ap_dev *es1, char *buf) { - char buf[65]; int retval; /* SVC messages go down our control pipe */ do { - memset(buf, 0, 65); retval = usb_control_msg(es1->usb_dev, usb_rcvctrlpipe(es1->usb_dev, es1->control_endpoint), @@ -469,7 +468,7 @@ static void apb1_log_get(struct es1_ap_dev *es1) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, buf, - 64, + APB1_LOG_MSG_SIZE, ES1_TIMEOUT); if (retval > 0) kfifo_in(&apb1_log_fifo, buf, retval); @@ -478,10 +477,20 @@ static void apb1_log_get(struct es1_ap_dev *es1) static int apb1_log_poll(void *data) { + struct es1_ap_dev *es1 = data; + char *buf; + + buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + while (!kthread_should_stop()) { msleep(1000); - apb1_log_get((struct es1_ap_dev *)data); + apb1_log_get(es1, buf); } + + kfree(buf); + return 0; } -- cgit v1.2.3-59-g8ed1b From 564c72b1c6f753f562e42c2a30a5dc50c194be0f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:13 +0200 Subject: greybus: operation: fix unaligned memory accesses in receive path The buffer received from our current host driver is 1-byte aligned and will therefore cause unaligned memory accesses if simply cast to an operation-message header. Fix this by making a properly aligned copy of the header in gb_connection_recv_response before accessing its fields. Note that this does not affect protocol drivers as the whole buffer is copied when creating the corresponding request or response before being forwarded. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 260774e414f3..0fd77c9ef5a6 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -885,7 +885,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, void gb_connection_recv(struct gb_connection *connection, void *data, size_t size) { - struct gb_operation_msg_hdr *header; + struct gb_operation_msg_hdr header; size_t msg_size; u16 operation_id; @@ -895,27 +895,28 @@ void gb_connection_recv(struct gb_connection *connection, return; } - if (size < sizeof(*header)) { + if (size < sizeof(header)) { dev_err(&connection->dev, "message too small\n"); return; } - header = data; - msg_size = le16_to_cpu(header->size); + /* Use memcpy as data may be unaligned */ + memcpy(&header, data, sizeof(header)); + msg_size = le16_to_cpu(header.size); if (size < msg_size) { dev_err(&connection->dev, "incomplete message received: 0x%04x (%zu < %zu)\n", - le16_to_cpu(header->operation_id), size, msg_size); + le16_to_cpu(header.operation_id), size, msg_size); return; /* XXX Should still complete operation */ } - operation_id = le16_to_cpu(header->operation_id); - if (header->type & GB_OPERATION_TYPE_RESPONSE) + operation_id = le16_to_cpu(header.operation_id); + if (header.type & GB_OPERATION_TYPE_RESPONSE) gb_connection_recv_response(connection, operation_id, - header->result, data, msg_size); + header.result, data, msg_size); else gb_connection_recv_request(connection, operation_id, - header->type, data, msg_size); + header.type, data, msg_size); } /* -- cgit v1.2.3-59-g8ed1b From cbba76f5cdbc14d2ae415c6f949c7d725b06a27b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:14 +0200 Subject: greybus: remove obsolete buffer-alignment requirement Remove unused and unnecessary buffer-alignment define that host driver were supposed to use. We can handle unaligned incoming buffers just fine by accessing the operation-message header via a copy in the receive path, rather than requiring host drivers to make sure the alignment is correct. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index a466fc5cdae4..fb90f96e0375 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -79,9 +79,6 @@ struct svc_msg; */ #define GB_BUFFER_HEADROOM_MAX sizeof(u64) -/* Buffers allocated from the host driver will be aligned to this multiple */ -#define GB_BUFFER_ALIGN sizeof(u32) - /* Greybus "Host driver" structure, needed by a host controller driver to be * able to handle both SVC control as well as "real" greybus messages */ -- cgit v1.2.3-59-g8ed1b From ac67acd3040affb7a7baa0cc626a3757758ed8a7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:15 +0200 Subject: greybus: operation: move message-header definition to header file Move operation message-header to operation.h so that it can be used by host drivers. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 29 ----------------------------- drivers/staging/greybus/operation.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 0fd77c9ef5a6..2dbb1e98b509 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -32,35 +32,6 @@ static struct workqueue_struct *gb_operation_workqueue; /* Protects the cookie representing whether a message is in flight */ static DEFINE_MUTEX(gb_message_mutex); -/* - * All operation messages (both requests and responses) begin with - * a header that encodes the size of the message (header included). - * This header also contains a unique identifier, that associates a - * response message with its operation. The header contains an - * operation type field, whose interpretation is dependent on what - * type of protocol is used over the connection. The high bit - * (0x80) of the operation type field is used to indicate whether - * the message is a request (clear) or a response (set). - * - * Response messages include an additional result byte, which - * communicates the result of the corresponding request. A zero - * result value means the operation completed successfully. Any - * other value indicates an error; in this case, the payload of the - * response message (if any) is ignored. The result byte must be - * zero in the header for a request message. - * - * The wire format for all numeric fields in the header is little - * endian. Any operation-specific data begins immediately after the - * header, and is 64-bit aligned. - */ -struct gb_operation_msg_hdr { - __le16 size; /* Size in bytes of header + payload */ - __le16 operation_id; /* Operation unique id */ - __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ - __u8 result; /* Result of request (in responses only) */ - /* 2 bytes pad, must be zero (ignore when read) */ -} __aligned(sizeof(u64)); - /* * Protects access to connection operations lists, as well as * updates to operation->errno. diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 6784f9832c8e..5ed1f6e3e97e 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -40,6 +40,35 @@ enum gb_operation_result { GB_OP_MALFUNCTION = 0xff, }; +/* + * All operation messages (both requests and responses) begin with + * a header that encodes the size of the message (header included). + * This header also contains a unique identifier, that associates a + * response message with its operation. The header contains an + * operation type field, whose interpretation is dependent on what + * type of protocol is used over the connection. The high bit + * (0x80) of the operation type field is used to indicate whether + * the message is a request (clear) or a response (set). + * + * Response messages include an additional result byte, which + * communicates the result of the corresponding request. A zero + * result value means the operation completed successfully. Any + * other value indicates an error; in this case, the payload of the + * response message (if any) is ignored. The result byte must be + * zero in the header for a request message. + * + * The wire format for all numeric fields in the header is little + * endian. Any operation-specific data begins immediately after the + * header, and is 64-bit aligned. + */ +struct gb_operation_msg_hdr { + __le16 size; /* Size in bytes of header + payload */ + __le16 operation_id; /* Operation unique id */ + __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ + __u8 result; /* Result of request (in responses only) */ + /* 2 bytes pad, must be zero (ignore when read) */ +} __aligned(sizeof(u64)); + /* * Protocol code should only examine the payload and payload_size * fields. All other fields are intended to be private to the -- cgit v1.2.3-59-g8ed1b From 7cf7bca9ec5659efa9520ffc5a3ff4ef59383560 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:16 +0200 Subject: greybus: pass messages to host drivers Pass structured greybus messages rather than buffers to the host drivers. This will allow us to separate the transfer buffers from the message structures. Rename the related functions to reflect the new interface. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/es1.c | 48 ++++++++++++++++--------------------- drivers/staging/greybus/es2.c | 48 ++++++++++++++++--------------------- drivers/staging/greybus/greybus.h | 6 ++--- drivers/staging/greybus/operation.c | 42 +++++++------------------------- drivers/staging/greybus/operation.h | 4 ++-- 6 files changed, 56 insertions(+), 94 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 0ac2fb808734..da62c5496e50 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -173,7 +173,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * Validate that the driver implements all of the callbacks * so that we don't have to every time we make them. */ - if ((!driver->buffer_send) || (!driver->buffer_cancel) || + if ((!driver->message_send) || (!driver->message_cancel) || (!driver->submit_svc)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); return NULL; diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index b9fd5111d3f9..55f8c7558dd4 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -214,26 +214,21 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb) * error otherwise. If the caller wishes to cancel the in-flight * buffer, it must supply the returned cookie to the cancel routine. */ -static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, - void *buffer, size_t buffer_size, gfp_t gfp_mask) +static void *message_send(struct greybus_host_device *hd, u16 cport_id, + struct gb_message *message, gfp_t gfp_mask) { struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; - u8 *transfer_buffer = buffer; + u8 *transfer_buffer; + size_t buffer_size; int transfer_buffer_size; int retval; struct urb *urb; - if (!buffer) { - pr_err("null buffer supplied to send\n"); - return ERR_PTR(-EINVAL); - } - if (buffer_size > (size_t)INT_MAX) { - pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); - return ERR_PTR(-EINVAL); - } - transfer_buffer--; - transfer_buffer_size = buffer_size + 1; + buffer_size = hd->buffer_headroom + sizeof(*message->header) + + message->payload_size; + transfer_buffer = message->buffer + hd->buffer_headroom - 1; + transfer_buffer_size = buffer_size - (hd->buffer_headroom - 1); /* * The data actually transferred will include an indication @@ -259,7 +254,7 @@ static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), transfer_buffer, transfer_buffer_size, - cport_out_callback, hd); + cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); @@ -271,17 +266,17 @@ static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, } /* - * The cookie value supplied is the value that buffer_send() - * returned to its caller. It identifies the buffer that should be + * The cookie value supplied is the value that message_send() + * returned to its caller. It identifies the message that should be * canceled. This function must also handle (which is to say, * ignore) a null cookie value. */ -static void buffer_cancel(void *cookie) +static void message_cancel(void *cookie) { /* * We really should be defensive and track all outstanding - * (sent) buffers rather than trusting the cookie provided + * (sent) messages rather than trusting the cookie provided * is valid. For the time being, this will do. */ if (cookie) @@ -290,8 +285,8 @@ static void buffer_cancel(void *cookie) static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), - .buffer_send = buffer_send, - .buffer_cancel = buffer_cancel, + .message_send = message_send, + .message_cancel = message_cancel, .submit_svc = submit_svc, }; @@ -439,18 +434,17 @@ exit: static void cport_out_callback(struct urb *urb) { - struct greybus_host_device *hd = urb->context; + struct gb_message *message = urb->context; + struct greybus_host_device *hd = message->operation->connection->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); - u8 *data = urb->transfer_buffer + 1; /* - * Tell the submitter that the buffer send (attempt) is - * complete, and report the status. The submitter's buffer - * starts after the one-byte CPort id we inserted. + * Tell the submitter that the message send (attempt) is + * complete, and report the status. */ - data = urb->transfer_buffer + 1; - greybus_data_sent(hd, data, status); + greybus_message_sent(hd, message, status); + free_urb(es1, urb); } diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 526e23c8b416..10b21df253a0 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -214,26 +214,21 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb) * error otherwise. If the caller wishes to cancel the in-flight * buffer, it must supply the returned cookie to the cancel routine. */ -static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, - void *buffer, size_t buffer_size, gfp_t gfp_mask) +static void *message_send(struct greybus_host_device *hd, u16 cport_id, + struct gb_message *message, gfp_t gfp_mask) { struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; - u8 *transfer_buffer = buffer; + u8 *transfer_buffer; + size_t buffer_size; int transfer_buffer_size; int retval; struct urb *urb; - if (!buffer) { - pr_err("null buffer supplied to send\n"); - return ERR_PTR(-EINVAL); - } - if (buffer_size > (size_t)INT_MAX) { - pr_err("bad buffer size (%zu) supplied to send\n", buffer_size); - return ERR_PTR(-EINVAL); - } - transfer_buffer--; - transfer_buffer_size = buffer_size + 1; + buffer_size = hd->buffer_headroom + sizeof(*message->header) + + message->payload_size; + transfer_buffer = message->buffer + hd->buffer_headroom - 1; + transfer_buffer_size = buffer_size - (hd->buffer_headroom - 1); /* * The data actually transferred will include an indication @@ -259,7 +254,7 @@ static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), transfer_buffer, transfer_buffer_size, - cport_out_callback, hd); + cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); @@ -271,17 +266,17 @@ static void *buffer_send(struct greybus_host_device *hd, u16 cport_id, } /* - * The cookie value supplied is the value that buffer_send() - * returned to its caller. It identifies the buffer that should be + * The cookie value supplied is the value that message_send() + * returned to its caller. It identifies the message that should be * canceled. This function must also handle (which is to say, * ignore) a null cookie value. */ -static void buffer_cancel(void *cookie) +static void message_cancel(void *cookie) { /* * We really should be defensive and track all outstanding - * (sent) buffers rather than trusting the cookie provided + * (sent) messages rather than trusting the cookie provided * is valid. For the time being, this will do. */ if (cookie) @@ -290,8 +285,8 @@ static void buffer_cancel(void *cookie) static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), - .buffer_send = buffer_send, - .buffer_cancel = buffer_cancel, + .message_send = message_send, + .message_cancel = message_cancel, .submit_svc = submit_svc, }; @@ -439,18 +434,17 @@ exit: static void cport_out_callback(struct urb *urb) { - struct greybus_host_device *hd = urb->context; + struct gb_message *message = urb->context; + struct greybus_host_device *hd = message->operation->connection->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); - u8 *data = urb->transfer_buffer + 1; /* - * Tell the submitter that the buffer send (attempt) is - * complete, and report the status. The submitter's buffer - * starts after the one-byte CPort id we inserted. + * Tell the submitter that the message send (attempt) is + * complete, and report the status. */ - data = urb->transfer_buffer + 1; - greybus_data_sent(hd, data, status); + greybus_message_sent(hd, message, status); + free_urb(es1, urb); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index fb90f96e0375..6f156e5b08c9 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -85,9 +85,9 @@ struct svc_msg; struct greybus_host_driver { size_t hd_priv_size; - void *(*buffer_send)(struct greybus_host_device *hd, u16 dest_cport_id, - void *buffer, size_t buffer_size, gfp_t gfp_mask); - void (*buffer_cancel)(void *cookie); + void *(*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, + struct gb_message *message, gfp_t gfp_mask); + void (*message_cancel)(void *cookie); int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 2dbb1e98b509..cdfb8938c236 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -140,16 +140,14 @@ gb_operation_find(struct gb_connection *connection, u16 operation_id) static int gb_message_send(struct gb_message *message) { - size_t message_size = sizeof(*message->header) + message->payload_size; struct gb_connection *connection = message->operation->connection; int ret = 0; void *cookie; mutex_lock(&gb_message_mutex); - cookie = connection->hd->driver->buffer_send(connection->hd, + cookie = connection->hd->driver->message_send(connection->hd, connection->hd_cport_id, - message->header, - message_size, + message, GFP_KERNEL); if (IS_ERR(cookie)) ret = PTR_ERR(cookie); @@ -161,8 +159,7 @@ static int gb_message_send(struct gb_message *message) } /* - * Cancel a message whose buffer we have passed to the host device - * layer to be sent. + * Cancel a message we have passed to the host device layer to be sent. */ static void gb_message_cancel(struct gb_message *message) { @@ -171,7 +168,7 @@ static void gb_message_cancel(struct gb_message *message) struct greybus_host_device *hd; hd = message->operation->connection->hd; - hd->driver->buffer_cancel(message->cookie); + hd->driver->message_cancel(message->cookie); } mutex_unlock(&gb_message_mutex); } @@ -225,25 +222,6 @@ static void gb_operation_work(struct work_struct *work) gb_operation_put(operation); } - -/* - * Given a pointer to the header in a message sent on a given host - * device, return the associated message structure. (This "header" - * is just the buffer pointer we supply to the host device for - * sending.) - */ -static struct gb_message * -gb_hd_message_find(struct greybus_host_device *hd, void *header) -{ - struct gb_message *message; - u8 *result; - - result = (u8 *)header - hd->buffer_headroom - sizeof(*message); - message = (struct gb_message *)result; - - return message; -} - static void gb_operation_message_init(struct greybus_host_device *hd, struct gb_message *message, u16 operation_id, size_t payload_size, u8 type) @@ -737,18 +715,14 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) EXPORT_SYMBOL_GPL(gb_operation_response_send); /* - * This function is called when a buffer send request has completed. - * The "header" is the message header--the beginning of what we - * asked to have sent. + * This function is called when a message send request has completed. */ -void -greybus_data_sent(struct greybus_host_device *hd, void *header, int status) +void greybus_message_sent(struct greybus_host_device *hd, + struct gb_message *message, int status) { - struct gb_message *message; struct gb_operation *operation; /* Get the message and record that it is no longer in flight */ - message = gb_hd_message_find(hd, header); message->cookie = NULL; /* @@ -773,7 +747,7 @@ greybus_data_sent(struct greybus_host_device *hd, void *header, int status) queue_work(gb_operation_workqueue, &operation->work); } } -EXPORT_SYMBOL_GPL(greybus_data_sent); +EXPORT_SYMBOL_GPL(greybus_message_sent); /* * We've received data on a connection, and it doesn't look like a diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 5ed1f6e3e97e..cbd347c6b427 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -147,8 +147,8 @@ int gb_operation_response_send(struct gb_operation *operation, int errno); void gb_operation_cancel(struct gb_operation *operation, int errno); -void greybus_data_sent(struct greybus_host_device *hd, - void *header, int status); +void greybus_message_sent(struct greybus_host_device *hd, + struct gb_message *message, int status); int gb_operation_sync(struct gb_connection *connection, int type, void *request, int request_size, -- cgit v1.2.3-59-g8ed1b From 1e5613b4a673f0670b64fe24f1c987604403e8c1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:17 +0200 Subject: greybus: operation: fix potential message corruption Make sure to allocate the message transfer-buffer separately from the containing message structure to avoid data corruption on systems without DMA-coherent caches. The message structure contains state that is updated while the buffer may be used for DMA, something which could lead to data corruption due to cache-line sharing on some architectures. Use the (renamed) message cache for the message structure itself and allocate the buffer separately. If the additional allocation is a concern, the message structures could eventually be allocated as part of the operation structure. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 72 ++++++++++++++++--------------------- drivers/staging/greybus/operation.h | 2 +- 2 files changed, 31 insertions(+), 43 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index cdfb8938c236..4d9e321b9e1d 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -24,7 +24,7 @@ #define GB_OPERATION_MESSAGE_SIZE_MAX 4096 static struct kmem_cache *gb_operation_cache; -static struct kmem_cache *gb_simple_message_cache; +static struct kmem_cache *gb_message_cache; /* Workqueue to handle Greybus operation completions. */ static struct workqueue_struct *gb_operation_workqueue; @@ -229,7 +229,7 @@ static void gb_operation_message_init(struct greybus_host_device *hd, struct gb_operation_msg_hdr *header; u8 *buffer; - buffer = &message->buffer[0]; + buffer = message->buffer; header = (struct gb_operation_msg_hdr *)(buffer + hd->buffer_headroom); message->header = header; @@ -270,8 +270,7 @@ static void gb_operation_message_init(struct greybus_host_device *hd, * The headers for inbound messages don't need to be initialized; * they'll be filled in by arriving data. * - * Our message structure consists of: - * message structure + * Our message buffers have the following layout: * headroom * message header \_ these combined are * message payload / the message size @@ -291,34 +290,37 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, hd->buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX; } - /* Allocate the message. Use the slab cache for simple messages */ - if (payload_size) { - if (message_size > hd->buffer_size_max) { - pr_warn("requested message size too big (%zu > %zu)\n", + if (message_size > hd->buffer_size_max) { + pr_warn("requested message size too big (%zu > %zu)\n", message_size, hd->buffer_size_max); - return NULL; - } - - size = sizeof(*message) + hd->buffer_headroom + message_size; - message = kzalloc(size, gfp_flags); - } else { - message = kmem_cache_zalloc(gb_simple_message_cache, gfp_flags); + return NULL; } + + /* Allocate the message structure and buffer. */ + message = kmem_cache_zalloc(gb_message_cache, gfp_flags); if (!message) return NULL; + size = hd->buffer_headroom + message_size; + message->buffer = kzalloc(size, gfp_flags); + if (!message->buffer) + goto err_free_message; + /* Initialize the message. Operation id is filled in later. */ gb_operation_message_init(hd, message, 0, payload_size, type); return message; + +err_free_message: + kmem_cache_free(gb_message_cache, message); + + return NULL; } static void gb_operation_message_free(struct gb_message *message) { - if (message->payload_size) - kfree(message); - else - kmem_cache_free(gb_simple_message_cache, message); + kfree(message->buffer); + kmem_cache_free(gb_message_cache, message); } /* @@ -937,32 +939,18 @@ EXPORT_SYMBOL_GPL(gb_operation_sync); int gb_operation_init(void) { - size_t size; - BUILD_BUG_ON(GB_OPERATION_MESSAGE_SIZE_MAX > U16_MAX - sizeof(struct gb_operation_msg_hdr)); - /* - * A message structure consists of: - * - the message structure itself - * - the headroom set aside for the host device - * - the message header - * - space for the message payload - * Messages with no payload are a fairly common case and - * have a known fixed maximum size, so we use a slab cache - * for them. - */ - size = sizeof(struct gb_message) + GB_BUFFER_HEADROOM_MAX + - sizeof(struct gb_operation_msg_hdr); - gb_simple_message_cache = kmem_cache_create("gb_simple_message_cache", - size, 0, 0, NULL); - if (!gb_simple_message_cache) + gb_message_cache = kmem_cache_create("gb_message_cache", + sizeof(struct gb_message), 0, 0, NULL); + if (!gb_message_cache) return -ENOMEM; gb_operation_cache = kmem_cache_create("gb_operation_cache", sizeof(struct gb_operation), 0, 0, NULL); if (!gb_operation_cache) - goto err_simple; + goto err_destroy_message_cache; gb_operation_workqueue = alloc_workqueue("greybus_operation", 0, 1); if (!gb_operation_workqueue) @@ -972,9 +960,9 @@ int gb_operation_init(void) err_operation: kmem_cache_destroy(gb_operation_cache); gb_operation_cache = NULL; -err_simple: - kmem_cache_destroy(gb_simple_message_cache); - gb_simple_message_cache = NULL; +err_destroy_message_cache: + kmem_cache_destroy(gb_message_cache); + gb_message_cache = NULL; return -ENOMEM; } @@ -985,6 +973,6 @@ void gb_operation_exit(void) gb_operation_workqueue = NULL; kmem_cache_destroy(gb_operation_cache); gb_operation_cache = NULL; - kmem_cache_destroy(gb_simple_message_cache); - gb_simple_message_cache = NULL; + kmem_cache_destroy(gb_message_cache); + gb_message_cache = NULL; } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index cbd347c6b427..647e0bfc54ee 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -82,7 +82,7 @@ struct gb_message { void *payload; size_t payload_size; - u8 buffer[]; + void *buffer; }; /* -- cgit v1.2.3-59-g8ed1b From da9dd11943d1409c9992fe058c67e1017426a1d9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:18 +0200 Subject: greybus: operation: add explicit padding to message header Add explicit pad bytes to the message header. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 647e0bfc54ee..38684f2cdbaa 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -66,7 +66,7 @@ struct gb_operation_msg_hdr { __le16 operation_id; /* Operation unique id */ __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ __u8 result; /* Result of request (in responses only) */ - /* 2 bytes pad, must be zero (ignore when read) */ + __u8 pad[2]; /* must be zero (ignore when read) */ } __aligned(sizeof(u64)); /* -- cgit v1.2.3-59-g8ed1b From a9cf7da195d99ceac46d46bf5ac31586afb66af7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:19 +0200 Subject: greybus: es1: fix transfer-buffer alignment Fix transfer-buffer alignment of outgoing transfers which are currently byte aligned. Some USB host drivers cannot handle byte-aligned buffers and will allocate temporary buffers, which the data is copied to or from on every transfer. This affects for example musb (e.g. Beaglebone Black) and ehci-tegra (e.g. Jetson). Instead of transferring pad bytes on the wire, let's (ab)use the pad bytes of the operation message header to transfer the cport id. This gives us properly aligned buffers and more efficient transfers in both directions. By using both pad bytes, we can also remove the arbitrary limitation of 256 cports. Note that the protocol between the host driver and the UniPro bridge is not necessarily Greybus. As long as the firmware clears the pad bytes before forwarding the data, and the host driver does the same before passing received data up the stack, this should be considered "legal" use. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 59 ++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 55f8c7558dd4..bf448353b55e 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "greybus.h" #include "svc_msg.h" @@ -127,13 +128,8 @@ static void usb_log_disable(struct es1_ap_dev *es1); */ static void hd_buffer_constraints(struct greybus_host_device *hd) { - /* - * Only one byte is required, but this produces a result - * that's better aligned for the user. - */ - hd->buffer_headroom = sizeof(u32); /* For cport id */ - hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX - hd->buffer_headroom; - BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); + hd->buffer_headroom = 0; + hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ @@ -219,16 +215,13 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, { struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; - u8 *transfer_buffer; + void *buffer; size_t buffer_size; - int transfer_buffer_size; int retval; struct urb *urb; - buffer_size = hd->buffer_headroom + sizeof(*message->header) + - message->payload_size; - transfer_buffer = message->buffer + hd->buffer_headroom - 1; - transfer_buffer_size = buffer_size - (hd->buffer_headroom - 1); + buffer = message->buffer; + buffer_size = sizeof(*message->header) + message->payload_size; /* * The data actually transferred will include an indication @@ -239,26 +232,27 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, pr_err("request to send inbound data buffer\n"); return ERR_PTR(-EINVAL); } - if (cport_id > U8_MAX) { - pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); - return ERR_PTR(-EINVAL); - } - /* OK, the destination is fine; record it in the transfer buffer */ - *transfer_buffer = cport_id; /* Find a free urb */ urb = next_free_urb(es1, gfp_mask); if (!urb) return ERR_PTR(-ENOMEM); + /* + * We (ab)use the operation-message header pad bytes to transfer the + * cport id in order to minimise overhead. + */ + put_unaligned_le16(cport_id, message->header->pad); + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), - transfer_buffer, transfer_buffer_size, + buffer, buffer_size, cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); free_urb(es1, urb); + put_unaligned_le16(0, message->header->pad); return ERR_PTR(retval); } @@ -395,10 +389,10 @@ static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; struct device *dev = &urb->dev->dev; + struct gb_operation_msg_hdr *header; int status = check_urb_status(urb); int retval; u16 cport_id; - u8 *data; if (status) { if ((status == -EAGAIN) || (status == -EPROTO)) @@ -407,23 +401,17 @@ static void cport_in_callback(struct urb *urb) return; } - /* The size has to be at least one, for the cport id */ - if (!urb->actual_length) { - dev_err(dev, "%s: no cport id in input buffer?\n", __func__); + if (urb->actual_length < sizeof(*header)) { + dev_err(dev, "%s: short message received\n", __func__); goto exit; } - /* - * Our CPort number is the first byte of the data stream, - * the rest of the stream is "real" data - */ - data = urb->transfer_buffer; - cport_id = data[0]; - data = &data[1]; - - /* Pass this data to the greybus core */ - greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); + header = urb->transfer_buffer; + cport_id = get_unaligned_le16(header->pad); + put_unaligned_le16(0, header->pad); + greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, + urb->actual_length); exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -439,6 +427,9 @@ static void cport_out_callback(struct urb *urb) struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); + /* Clear the pad bytes used for the cport id */ + put_unaligned_le16(0, message->header->pad); + /* * Tell the submitter that the message send (attempt) is * complete, and report the status. -- cgit v1.2.3-59-g8ed1b From 491e60d63fde0a9ff09490a070d7bc22ff82c9ce Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:20 +0200 Subject: greybus: es2: sync up with recent es1 changes Fix transfer-buffer alignment of es2 as well. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 59 ++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 10b21df253a0..45b6815dc06b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "greybus.h" #include "svc_msg.h" @@ -127,13 +128,8 @@ static void usb_log_disable(struct es1_ap_dev *es1); */ static void hd_buffer_constraints(struct greybus_host_device *hd) { - /* - * Only one byte is required, but this produces a result - * that's better aligned for the user. - */ - hd->buffer_headroom = sizeof(u32); /* For cport id */ - hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX - hd->buffer_headroom; - BUILD_BUG_ON(hd->buffer_headroom > GB_BUFFER_HEADROOM_MAX); + hd->buffer_headroom = 0; + hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ @@ -219,16 +215,13 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, { struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; - u8 *transfer_buffer; + void *buffer; size_t buffer_size; - int transfer_buffer_size; int retval; struct urb *urb; - buffer_size = hd->buffer_headroom + sizeof(*message->header) + - message->payload_size; - transfer_buffer = message->buffer + hd->buffer_headroom - 1; - transfer_buffer_size = buffer_size - (hd->buffer_headroom - 1); + buffer = message->buffer; + buffer_size = sizeof(*message->header) + message->payload_size; /* * The data actually transferred will include an indication @@ -239,26 +232,27 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, pr_err("request to send inbound data buffer\n"); return ERR_PTR(-EINVAL); } - if (cport_id > U8_MAX) { - pr_err("cport_id (%hd) is out of range for ES1\n", cport_id); - return ERR_PTR(-EINVAL); - } - /* OK, the destination is fine; record it in the transfer buffer */ - *transfer_buffer = cport_id; /* Find a free urb */ urb = next_free_urb(es1, gfp_mask); if (!urb) return ERR_PTR(-ENOMEM); + /* + * We (ab)use the operation-message header pad bytes to transfer the + * cport id in order to minimise overhead. + */ + put_unaligned_le16(cport_id, message->header->pad); + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), - transfer_buffer, transfer_buffer_size, + buffer, buffer_size, cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); free_urb(es1, urb); + put_unaligned_le16(0, message->header->pad); return ERR_PTR(retval); } @@ -395,10 +389,10 @@ static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; struct device *dev = &urb->dev->dev; + struct gb_operation_msg_hdr *header; int status = check_urb_status(urb); int retval; u16 cport_id; - u8 *data; if (status) { if ((status == -EAGAIN) || (status == -EPROTO)) @@ -407,23 +401,17 @@ static void cport_in_callback(struct urb *urb) return; } - /* The size has to be at least one, for the cport id */ - if (!urb->actual_length) { - dev_err(dev, "%s: no cport id in input buffer?\n", __func__); + if (urb->actual_length < sizeof(*header)) { + dev_err(dev, "%s: short message received\n", __func__); goto exit; } - /* - * Our CPort number is the first byte of the data stream, - * the rest of the stream is "real" data - */ - data = urb->transfer_buffer; - cport_id = data[0]; - data = &data[1]; - - /* Pass this data to the greybus core */ - greybus_data_rcvd(hd, cport_id, data, urb->actual_length - 1); + header = urb->transfer_buffer; + cport_id = get_unaligned_le16(header->pad); + put_unaligned_le16(0, header->pad); + greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, + urb->actual_length); exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -439,6 +427,9 @@ static void cport_out_callback(struct urb *urb) struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); + /* Clear the pad bytes used for the cport id */ + put_unaligned_le16(0, message->header->pad); + /* * Tell the submitter that the message send (attempt) is * complete, and report the status. -- cgit v1.2.3-59-g8ed1b From 24ef4853986e740b90c4647b8ede70ed690185e2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2015 11:27:21 +0200 Subject: greybus: drop host-driver buffer headroom Drop the host-driver buffer headroom that was used to transfer the cport id on ES1 and ES2. Rather than transferring additional bytes on the wire and having to deal with buffer-alignment issues (e.g. requiring the headroom to be a multiple of 8 bytes) simply drop the headroom functionality. Host drivers are expected set up their transfer descriptors separately from the data buffers and any intermediate drivers (e.g. for Greybus over USB) can (ab)use the operation message pad bytes for now. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 20 +------------------- drivers/staging/greybus/es2.c | 20 +------------------- drivers/staging/greybus/greybus.h | 9 --------- drivers/staging/greybus/operation.c | 7 ++----- 4 files changed, 4 insertions(+), 52 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index bf448353b55e..0df7bddbff69 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -106,29 +106,11 @@ static void usb_log_disable(struct es1_ap_dev *es1); * which defines a region of memory used by the host driver for * transferring the data. When Greybus allocates a buffer, it must * do so subject to the constraints associated with the host driver. - * These constraints are specified by two parameters: the - * headroom; and the maximum buffer size. * - * +------------------+ - * | Host driver | \ - * | reserved area | }- headroom - * | . . . | / - * buffer pointer ---> +------------------+ - * | Buffer space for | \ - * | transferred data | }- buffer size - * | . . . | / (limited to size_max) - * +------------------+ - * - * headroom: Every buffer must have at least this much space - * *before* the buffer pointer, reserved for use by the - * host driver. I.e., ((char *)buffer - headroom) must - * point to valid memory, usable only by the host driver. - * size_max: The maximum size of a buffer (not including the - * headroom) must not exceed this. + * size_max: The maximum size of a buffer */ static void hd_buffer_constraints(struct greybus_host_device *hd) { - hd->buffer_headroom = 0; hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; } diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 45b6815dc06b..dfadcb4ad93c 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -106,29 +106,11 @@ static void usb_log_disable(struct es1_ap_dev *es1); * which defines a region of memory used by the host driver for * transferring the data. When Greybus allocates a buffer, it must * do so subject to the constraints associated with the host driver. - * These constraints are specified by two parameters: the - * headroom; and the maximum buffer size. * - * +------------------+ - * | Host driver | \ - * | reserved area | }- headroom - * | . . . | / - * buffer pointer ---> +------------------+ - * | Buffer space for | \ - * | transferred data | }- buffer size - * | . . . | / (limited to size_max) - * +------------------+ - * - * headroom: Every buffer must have at least this much space - * *before* the buffer pointer, reserved for use by the - * host driver. I.e., ((char *)buffer - headroom) must - * point to valid memory, usable only by the host driver. - * size_max: The maximum size of a buffer (not including the - * headroom) must not exceed this. + * size_max: The maximum size of a buffer */ static void hd_buffer_constraints(struct greybus_host_device *hd) { - hd->buffer_headroom = 0; hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 6f156e5b08c9..8d4bde3815d6 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -71,14 +71,6 @@ struct greybus_host_device; struct svc_msg; -/* - * When the Greybus code allocates a buffer it sets aside bytes - * prior to the beginning of the payload area for the host device's - * exclusive use. The size is specified by hd->buffer_headroom, and - * which can't be greater than GB_BUFFER_HEADROOM_MAX. - */ -#define GB_BUFFER_HEADROOM_MAX sizeof(u64) - /* Greybus "Host driver" structure, needed by a host controller driver to be * able to handle both SVC control as well as "real" greybus messages */ @@ -103,7 +95,6 @@ struct greybus_host_device { u8 device_id; /* Host device buffer constraints */ - size_t buffer_headroom; size_t buffer_size_max; /* Private data for the host driver */ diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 4d9e321b9e1d..3639e27e288a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -230,7 +230,7 @@ static void gb_operation_message_init(struct greybus_host_device *hd, u8 *buffer; buffer = message->buffer; - header = (struct gb_operation_msg_hdr *)(buffer + hd->buffer_headroom); + header = message->buffer; message->header = header; message->payload = payload_size ? header + 1 : NULL; @@ -271,7 +271,6 @@ static void gb_operation_message_init(struct greybus_host_device *hd, * they'll be filled in by arriving data. * * Our message buffers have the following layout: - * headroom * message header \_ these combined are * message payload / the message size */ @@ -282,7 +281,6 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, struct gb_message *message; struct gb_operation_msg_hdr *header; size_t message_size = payload_size + sizeof(*header); - size_t size; if (hd->buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) { pr_warn("limiting buffer size to %u\n", @@ -301,8 +299,7 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, if (!message) return NULL; - size = hd->buffer_headroom + message_size; - message->buffer = kzalloc(size, gfp_flags); + message->buffer = kzalloc(message_size, gfp_flags); if (!message->buffer) goto err_free_message; -- cgit v1.2.3-59-g8ed1b From 764e824e2fa5cca0d7c671ae58e65cd06aed0fce Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Apr 2015 20:26:30 +0200 Subject: greybus: module.c: add attributes This adds the attributes power_control and present to a module. It also removes the unneeded module_id attribute, as that comes from the name of the module itself. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/module.c | 57 +++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index e8c1c07cff60..780d163b3e00 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -11,37 +11,62 @@ /* module sysfs attributes */ -#define gb_module_attr(field, type) \ -static ssize_t field##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - struct gb_module *module = to_gb_module(dev); \ - return sprintf(buf, "%"#type"\n", module->field); \ -} \ -static DEVICE_ATTR_RO(field) - -// FIXME, do we really need this attribute? -gb_module_attr(module_id, x); - static ssize_t epm_show(struct device *dev, struct device_attribute *attr, char *buf) { - // FIXME, implement something here + // FIXME + // Implement something here when we have a working control protocol return sprintf(buf, "1\n"); } static ssize_t epm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - // FIXME, implement something here. + // FIXME + // Implement something here when we have a working control protocol return 0; } static DEVICE_ATTR_RW(epm); +static ssize_t power_control_show(struct device *dev, + struct device_attribute *addr, char *buf) +{ + // FIXME + // Implement something here when we have a working control protocol + return sprintf(buf, "1\n"); +} + +static ssize_t power_control_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + // FIXME + // Implement something here when we have a working control protocol + return 0; +} +static DEVICE_ATTR_RW(power_control); + +static ssize_t present_show(struct device *dev, + struct device_attribute *addr, char *buf) +{ + // FIXME + // Implement something here when we have a working control protocol + return sprintf(buf, "1\n"); +} + +static ssize_t present_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + // FIXME + // Implement something here when we have a working control protocol + return 0; +} +static DEVICE_ATTR_RW(present); + static struct attribute *module_attrs[] = { - &dev_attr_module_id.attr, &dev_attr_epm.attr, + &dev_attr_power_control.attr, + &dev_attr_present.attr, NULL, }; ATTRIBUTE_GROUPS(module); -- cgit v1.2.3-59-g8ed1b From 0f035acdedcb4f016c2b7cc23758d9191713d951 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Apr 2015 20:26:53 +0200 Subject: greybus: endo: add endo structures and logic This adds endo.c and endo.h and provides functions to create an endo and the initial 0x0555 set of modules. But, it doesn't hook this logic up into the running code yet, that comes next. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/core.c | 8 ++ drivers/staging/greybus/endo.c | 164 ++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/endo.h | 32 ++++++++ drivers/staging/greybus/greybus.h | 7 ++ 5 files changed, 212 insertions(+) create mode 100644 drivers/staging/greybus/endo.c create mode 100644 drivers/staging/greybus/endo.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index f6ad19a1329b..9945cb804d10 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -2,6 +2,7 @@ greybus-y := core.o \ debugfs.o \ ap.o \ manifest.o \ + endo.o \ module.o \ interface.o \ bundle.o \ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index da62c5496e50..7c701f398a4d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -51,6 +51,14 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) struct gb_bundle *bundle = NULL; struct gb_connection *connection = NULL; + if (is_gb_endo(dev)) { + /* + * Not much to do for an endo, just fall through, as the + * "default" attributes are good enough for us. + */ + return 0; + } + if (is_gb_module(dev)) { module = to_gb_module(dev); } else if (is_gb_interface(dev)) { diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c new file mode 100644 index 000000000000..e3bb25f7ec2b --- /dev/null +++ b/drivers/staging/greybus/endo.c @@ -0,0 +1,164 @@ +/* + * Greybus endo code + * + * Copyright 2015 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +/* endo sysfs attributes */ +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_endo *endo = to_gb_endo(dev); + + return sprintf(buf, "%s", &endo->svc.serial_number[0]); +} +static DEVICE_ATTR_RO(serial_number); + +static ssize_t version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_endo *endo = to_gb_endo(dev); + + return sprintf(buf, "%s", &endo->svc.version[0]); +} +static DEVICE_ATTR_RO(version); + +static struct attribute *endo_attrs[] = { + &dev_attr_serial_number.attr, + &dev_attr_version.attr, + NULL, +}; +static const struct attribute_group endo_group = { + .attrs = endo_attrs, + .name = "SVC", +}; +static const struct attribute_group *endo_groups[] = { + &endo_group, + NULL, +}; + +static void greybus_endo_release(struct device *dev) +{ + struct gb_endo *endo = to_gb_endo(dev); + + kfree(endo); +} + +struct device_type greybus_endo_type = { + .name = "greybus_endo", + .release = greybus_endo_release, +}; + + +/* + * Endo "types" have different module locations, these are tables based on those + * types that list the module ids for the different locations. + * + * List must end with 0x00 in order to properly terminate the list. + */ +static u8 endo_0555[] = { + 0x01, + 0x03, + 0x05, + 0x07, + 0x08, + 0x0a, + 0x0c, + 0x00, +}; + + +static int create_modules(struct gb_endo *endo) +{ + struct gb_module *module; + u8 *endo_modules; + int i; + + /* Depending on the endo type, create a bunch of different modules */ + switch (endo->type) { + case 0x0555: + endo_modules = &endo_0555[0]; + break; + default: + dev_err(&endo->dev, "Unknown endo type 0x%04x, aborting!", + endo->type); + return -EINVAL; + } + + for (i = 0; endo_modules[i] != 0x00; ++i) { +// module = gb_module_create(&endo->dev, endo_modules[i]); + if (!module) + return -EINVAL; + } + + return 0; +} + +static void remove_modules(struct gb_endo *endo) +{ + /* + * We really don't care how many modules have been created, or what the + * configuration of them are, let's just enumerate over everything in + * the system and delete all found modules. + */ + +} + +struct gb_endo *gb_endo_create(struct greybus_host_device *hd) +{ + struct gb_endo *endo; + int retval; + + endo = kzalloc(sizeof(*endo), GFP_KERNEL); + if (!endo) + return NULL; + + endo->dev.parent = hd->parent; + endo->dev.bus = &greybus_bus_type; + endo->dev.type = &greybus_endo_type; + endo->dev.groups = endo_groups; + endo->dev.dma_mask = hd->parent->dma_mask; + device_initialize(&endo->dev); + + // FIXME - determine endo "type" from the SVC + // Also get the version and serial number from the SVC, right now we are + // using "fake" numbers. + strcpy(&endo->svc.serial_number[0], "042"); + strcpy(&endo->svc.version[0], "0.0"); + endo->type = 0x0555; + + dev_set_name(&endo->dev, "endo-0x%04x", endo->type); + retval = device_add(&endo->dev); + if (retval) { + dev_err(hd->parent, "failed to add endo device of type 0x%04x\n", + endo->type); + put_device(&endo->dev); + kfree(endo); + return NULL; + } + + retval = create_modules(endo); + if (retval) { + gb_endo_remove(endo); + return NULL; + } + + return endo; +} + +void gb_endo_remove(struct gb_endo *endo) +{ + if (!endo) + return; + + /* remove all modules first */ + remove_modules(endo); + + device_unregister(&endo->dev); +} + diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h new file mode 100644 index 000000000000..649093e4025d --- /dev/null +++ b/drivers/staging/greybus/endo.h @@ -0,0 +1,32 @@ +/* + * Greybus endo code + * + * Copyright 2015 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __ENDO_H +#define __ENDO_H + +/* Greybus "public" definitions" */ +struct gb_svc { + u8 serial_number[10]; + u8 version[10]; +}; + +struct gb_endo { + struct device dev; + struct gb_svc svc; + u16 type; +}; +#define to_gb_endo(d) container_of(d, struct gb_endo, dev) + + +/* Greybus "private" definitions */ +struct greybus_host_device; + +struct gb_endo *gb_endo_create(struct greybus_host_device *hd); +void gb_endo_remove(struct gb_endo *endo); + +#endif /* __ENDO_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 8d4bde3815d6..e0aae42fc4ce 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -24,6 +24,7 @@ #include "greybus_id.h" #include "greybus_manifest.h" #include "manifest.h" +#include "endo.h" #include "module.h" #include "interface.h" #include "bundle.h" @@ -158,11 +159,17 @@ void gb_uart_device_exit(struct gb_connection *connection); int svc_set_route_send(struct gb_bundle *bundle, struct greybus_host_device *hd); +extern struct device_type greybus_endo_type; extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_bundle_type; extern struct device_type greybus_connection_type; +static inline int is_gb_endo(const struct device *dev) +{ + return dev->type == &greybus_endo_type; +} + static inline int is_gb_module(const struct device *dev) { return dev->type == &greybus_module_type; -- cgit v1.2.3-59-g8ed1b From a4d9150cbaafd0d58eff54ea928d155ae582aa9c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Apr 2015 20:27:15 +0200 Subject: greybus: endo: hook up endos into the device tree This hooks up the endo, and modules, into the device tree. All modules for a specific endo are created when the host device is initialized. When an interface is registered, the correct module for it is found and that module is used for the sysfs tree. When the interface is removed, the reference on the module is dropped. When the host device goes away, the whole endo and modules are removed at once. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/core.c | 14 ++++++++-- drivers/staging/greybus/endo.c | 16 +++--------- drivers/staging/greybus/greybus.h | 2 ++ drivers/staging/greybus/interface.c | 4 +-- drivers/staging/greybus/module.c | 52 ++++++++++++++++++++----------------- drivers/staging/greybus/module.h | 7 +++-- 6 files changed, 50 insertions(+), 45 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 7c701f398a4d..458020997294 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -198,15 +198,25 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver INIT_LIST_HEAD(&hd->connections); ida_init(&hd->cport_id_map); + hd->endo = gb_endo_create(hd); + if (!hd->endo) { + greybus_remove_hd(hd); + return NULL; + } + return hd; } EXPORT_SYMBOL_GPL(greybus_create_hd); void greybus_remove_hd(struct greybus_host_device *hd) { - /* Tear down all modules that happen to be associated with this host - * controller */ + /* + * Tear down all interfaces, modules, and the endo that is associated + * with this host controller before freeing the memory associated with + * the host controller. + */ gb_remove_interfaces(hd); + gb_endo_remove(hd->endo); kref_put_mutex(&hd->kref, free_hd, &hd_mutex); } EXPORT_SYMBOL_GPL(greybus_remove_hd); diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index e3bb25f7ec2b..28e1f2871bf8 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -91,7 +91,7 @@ static int create_modules(struct gb_endo *endo) } for (i = 0; endo_modules[i] != 0x00; ++i) { -// module = gb_module_create(&endo->dev, endo_modules[i]); + module = gb_module_create(&endo->dev, endo_modules[i]); if (!module) return -EINVAL; } @@ -99,16 +99,6 @@ static int create_modules(struct gb_endo *endo) return 0; } -static void remove_modules(struct gb_endo *endo) -{ - /* - * We really don't care how many modules have been created, or what the - * configuration of them are, let's just enumerate over everything in - * the system and delete all found modules. - */ - -} - struct gb_endo *gb_endo_create(struct greybus_host_device *hd) { struct gb_endo *endo; @@ -156,8 +146,8 @@ void gb_endo_remove(struct gb_endo *endo) if (!endo) return; - /* remove all modules first */ - remove_modules(endo); + /* remove all modules for this endo */ + gb_module_remove_all(endo); device_unregister(&endo->dev); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index e0aae42fc4ce..109727f9f7c2 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -98,6 +98,8 @@ struct greybus_host_device { /* Host device buffer constraints */ size_t buffer_size_max; + struct gb_endo *endo; + /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); }; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 5b1d5dde3c90..63665a2d8015 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -92,7 +92,7 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, return NULL; } - module = gb_module_find_or_create(hd, get_module_id(interface_id)); + module = gb_module_find(hd, get_module_id(interface_id)); if (!module) return NULL; @@ -157,7 +157,7 @@ static void gb_interface_destroy(struct gb_interface *intf) module = intf->module; device_unregister(&intf->dev); - gb_module_remove(module); + put_device(&module->dev); } /** diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 780d163b3e00..202f141c7fe3 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -94,16 +94,22 @@ u8 get_module_id(u8 interface_id) return interface_id; } +struct module_find { + struct gb_endo *endo; + u8 module_id; +}; + static int module_find(struct device *dev, void *data) { struct gb_module *module; - u8 *module_id = data; + struct module_find *find = data; if (!is_gb_module(dev)) return 0; module = to_gb_module(dev); - if (module->module_id == *module_id) + if ((module->module_id == find->module_id) && + (module->dev.parent == &find->endo->dev)) return 1; return 0; @@ -113,21 +119,24 @@ static int module_find(struct device *dev, void *data) * Search the list of modules in the system. If one is found, return it, with * the reference count incremented. */ -static struct gb_module *gb_module_find(u8 module_id) +struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) { struct device *dev; struct gb_module *module = NULL; + struct module_find find; + + find.module_id = module_id; + find.endo = hd->endo; dev = bus_find_device(&greybus_bus_type, NULL, - &module_id, module_find); + &find, module_find); if (dev) module = to_gb_module(dev); return module; } -static struct gb_module *gb_module_create(struct greybus_host_device *hd, - u8 module_id) +struct gb_module *gb_module_create(struct device *parent, u8 module_id) { struct gb_module *module; int retval; @@ -137,12 +146,11 @@ static struct gb_module *gb_module_create(struct greybus_host_device *hd, return NULL; module->module_id = module_id; - module->refcount = 1; - module->dev.parent = hd->parent; + module->dev.parent = parent; module->dev.bus = &greybus_bus_type; module->dev.type = &greybus_module_type; module->dev.groups = module_groups; - module->dev.dma_mask = hd->parent->dma_mask; + module->dev.dma_mask = parent->dma_mask; device_initialize(&module->dev); dev_set_name(&module->dev, "%d", module_id); @@ -158,26 +166,22 @@ static struct gb_module *gb_module_create(struct greybus_host_device *hd, return module; } -struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd, - u8 module_id) +static int module_remove(struct device *dev, void *data) { struct gb_module *module; + struct gb_endo *endo = data; - module = gb_module_find(module_id); - if (module) { - module->refcount++; - return module; - } + if (!is_gb_module(dev)) + return 0; + + module = to_gb_module(dev); + if (module->dev.parent == &endo->dev) + device_unregister(&module->dev); - return gb_module_create(hd, module_id); + return 0; } -void gb_module_remove(struct gb_module *module) +void gb_module_remove_all(struct gb_endo *endo) { - if (!module) - return; - - if (!--module->refcount) - device_unregister(&module->dev); + bus_for_each_dev(&greybus_bus_type, NULL, endo, module_remove); } - diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index f3e3bdd6a671..c23ac98fc1ba 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -13,16 +13,15 @@ struct gb_module { struct device dev; u8 module_id; /* Physical location within the Endo */ - u16 refcount; }; #define to_gb_module(d) container_of(d, struct gb_module, dev) struct greybus_host_device; /* Greybus "private" definitions */ -struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd, - u8 module_id); -void gb_module_remove(struct gb_module *module); +struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id); +struct gb_module *gb_module_create(struct device *parent, u8 module_id); +void gb_module_remove_all(struct gb_endo *endo); u8 get_module_id(u8 interface_id); -- cgit v1.2.3-59-g8ed1b From f0b678709a7d2c4eefa834db70580a226d212f8a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Apr 2015 20:27:36 +0200 Subject: greybus: Documentation/sysfs-bus-greybus: update kernel version and date The kernel is now on the 4.XX numbering scheme, and it's going to be a while before we merge this code, so pick a date sometime in the future to be safe. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- .../greybus/Documentation/sysfs-bus-greybus | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 748ea3eed6fd..9a945dbdff66 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -1,41 +1,41 @@ What: /sys/bus/greybus/device/.../product -Date: December 2014 -KernelVersion: 3.XX +Date: October 2015 +KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Product ID of a Greybus interface block. What: /sys/bus/greybus/device/.../product_string -Date: December 2014 -KernelVersion: 3.XX +Date: October 2015 +KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Product ID string of a Greybus interface block. What: /sys/bus/greybus/device/.../unique_id -Date: December 2014 -KernelVersion: 3.XX +Date: October 2015 +KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Unique ID of a Greybus interface block. What: /sys/bus/greybus/device/.../vendor -Date: December 2014 -KernelVersion: 3.XX +Date: October 2015 +KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Vendor ID of a Greybus interface block. What: /sys/bus/greybus/device/.../vendor_string -Date: December 2014 -KernelVersion: 3.XX +Date: October 2015 +KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Vendor ID string of a Greybus interface block. What: /sys/bus/greybus/device/.../state -Date: December 2014 -KernelVersion: 3.XX +Date: October 2015 +KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The current state of a Greybus connection. @@ -48,15 +48,15 @@ Description: 4 - destroying What: /sys/bus/greybus/device/.../protocol_id -Date: December 2014 -KernelVersion: 3.XX +Date: October 2015 +KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The protocol id of a Greybus connection. What: /sys/bus/greybus/device/.../device_id -Date: December 2014 -KernelVersion: 3.XX +Date: October 2015 +KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The device id of a Greybus bundle. -- cgit v1.2.3-59-g8ed1b From 7baa184dbd407b4a1b619c6f5f78074f0bb19ae9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Apr 2015 20:27:53 +0200 Subject: greybus: Documentation/sysfs-bus-greybus: document the endo and SVC This documents the endo device, and the SVC specific files that are present in the sysfs device tree. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- .../greybus/Documentation/sysfs-bus-greybus | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 9a945dbdff66..c0e0c0aad94b 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -1,3 +1,26 @@ +What: /sys/bus/greybus/device/endo-XXXX +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The "root" endo for the Greybus device tree. XXX is + replaced with the numeric value of the endo layout + scheme as documented in the ARA Module Developer Kit. + +What: /sys/bus/greybus/device/endo-XXXX/SVC/serial_number +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The serial number of the SVC device + +What: /sys/bus/greybus/device/endo-XXXX/SVC/version +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The version number of the firmware in the SVC device. + What: /sys/bus/greybus/device/.../product Date: October 2015 KernelVersion: 4.XX -- cgit v1.2.3-59-g8ed1b From b0235b2263406728a0b6ee8b13be22f07507812d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Apr 2015 20:28:13 +0200 Subject: greybus: Documentation/sysfs-bus-greybus: document module sysfs files This documents the module slot sysfs files "epm", "power_control", and "present". Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- .../greybus/Documentation/sysfs-bus-greybus | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index c0e0c0aad94b..4b511dbff0da 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -21,6 +21,36 @@ Contact: Greg Kroah-Hartman Description: The version number of the firmware in the SVC device. +What: /sys/bus/greybus/device/endo-XXXX/../epm +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The EPM (Electropermanent Magnet) control file for + the specific module slot the file is present in. + Writing 1 to it turns it on, writing 0 to it turns it + off. Reading the value returns if it is on or off. + +What: /sys/bus/greybus/device/endo-XXXX/../power_control +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The power control file for the specific module slot that + the file is present in. Writing 1 to it turns power on + to the module, writing 0 to it turns power off to the + module. Reading the value returns if it is on or off. + +What: /sys/bus/greybus/device/endo-XXXX/../present +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The "is a module present in the slot" file for the + specific module slot that the file is present in. + This is read-only, 1 means a module is present, 0 means + no module is present. + What: /sys/bus/greybus/device/.../product Date: October 2015 KernelVersion: 4.XX -- cgit v1.2.3-59-g8ed1b From 453bbea807345db3faab8f4a432b4b1b2b245a27 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 9 Apr 2015 16:01:31 -0700 Subject: greybus: Move briged phy structure definitions into gpbridge.h In order to facilitate re-use of the gpio, i2c, pwm and i2s structures, split them out of independent files and add them into a shared gpbridge.h This will be a prereq to sharing these headers w/ gbsim. Cc: Alex Elder Cc: Greg Kroah-Hartman CC: Johan Hovold Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpbridge.h | 412 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/gpio.c | 105 +--------- drivers/staging/greybus/i2c.c | 57 +---- drivers/staging/greybus/i2s.h | 145 ------------- drivers/staging/greybus/pwm.c | 48 +---- 5 files changed, 416 insertions(+), 351 deletions(-) create mode 100644 drivers/staging/greybus/gpbridge.h delete mode 100644 drivers/staging/greybus/i2s.h diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h new file mode 100644 index 000000000000..8888238df4c3 --- /dev/null +++ b/drivers/staging/greybus/gpbridge.h @@ -0,0 +1,412 @@ +#ifndef __GB_GPBRIDGE_H__ +#define __GB_GPBRIDGE_H__ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 - 2015 Google Inc. All rights reserved. + * Copyright(c) 2014 - 2015 Linaro Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program 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 version 2 for more details. + * + * BSD LICENSE + * + * Copyright(c) 2014 - 2015 Google Inc. All rights reserved. + * Copyright(c) 2014 - 2015 Linaro Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. or Linaro Ltd. 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 COPYRIGHT HOLDERS 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 GOOGLE INC. OR + * LINARO LTD. 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. + */ + + +/* I2C */ + +/* Version of the Greybus i2c protocol we support */ +#define GB_I2C_VERSION_MAJOR 0x00 +#define GB_I2C_VERSION_MINOR 0x01 + +/* Greybus i2c request types */ +#define GB_I2C_TYPE_INVALID 0x00 +#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2C_TYPE_FUNCTIONALITY 0x02 +#define GB_I2C_TYPE_TIMEOUT 0x03 +#define GB_I2C_TYPE_RETRIES 0x04 +#define GB_I2C_TYPE_TRANSFER 0x05 +#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +#define GB_I2C_RETRIES_DEFAULT 3 +#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ + +/* functionality request has no payload */ +struct gb_i2c_functionality_response { + __le32 functionality; +}; + +struct gb_i2c_timeout_request { + __le16 msec; +}; +/* timeout response has no payload */ + +struct gb_i2c_retries_request { + __u8 retries; +}; +/* retries response has no payload */ + +/* + * Outgoing data immediately follows the op count and ops array. + * The data for each write (master -> slave) op in the array is sent + * in order, with no (e.g. pad) bytes separating them. + * + * Short reads cause the entire transfer request to fail So response + * payload consists only of bytes read, and the number of bytes is + * exactly what was specified in the corresponding op. Like + * outgoing data, the incoming data is in order and contiguous. + */ +struct gb_i2c_transfer_op { + __le16 addr; + __le16 flags; + __le16 size; +}; + +struct gb_i2c_transfer_request { + __le16 op_count; + struct gb_i2c_transfer_op ops[0]; /* op_count of these */ +}; +struct gb_i2c_transfer_response { + __u8 data[0]; /* inbound data */ +}; + + +/* GPIO */ + +/* Version of the Greybus GPIO protocol we support */ +#define GB_GPIO_VERSION_MAJOR 0x00 +#define GB_GPIO_VERSION_MINOR 0x01 + +/* Greybus GPIO request types */ +#define GB_GPIO_TYPE_INVALID 0x00 +#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 +#define GB_GPIO_TYPE_LINE_COUNT 0x02 +#define GB_GPIO_TYPE_ACTIVATE 0x03 +#define GB_GPIO_TYPE_DEACTIVATE 0x04 +#define GB_GPIO_TYPE_GET_DIRECTION 0x05 +#define GB_GPIO_TYPE_DIRECTION_IN 0x06 +#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 +#define GB_GPIO_TYPE_GET_VALUE 0x08 +#define GB_GPIO_TYPE_SET_VALUE 0x09 +#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a +#define GB_GPIO_TYPE_IRQ_TYPE 0x0b +#define GB_GPIO_TYPE_IRQ_ACK 0x0c +#define GB_GPIO_TYPE_IRQ_MASK 0x0d +#define GB_GPIO_TYPE_IRQ_UNMASK 0x0e +#define GB_GPIO_TYPE_IRQ_EVENT 0x0f +#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ + +/* line count request has no payload */ +struct gb_gpio_line_count_response { + __u8 count; +}; + +struct gb_gpio_activate_request { + __u8 which; +}; +/* activate response has no payload */ + +struct gb_gpio_deactivate_request { + __u8 which; +}; +/* deactivate response has no payload */ + +struct gb_gpio_get_direction_request { + __u8 which; +}; +struct gb_gpio_get_direction_response { + __u8 direction; +}; + +struct gb_gpio_direction_in_request { + __u8 which; +}; +/* direction in response has no payload */ + +struct gb_gpio_direction_out_request { + __u8 which; + __u8 value; +}; +/* direction out response has no payload */ + +struct gb_gpio_get_value_request { + __u8 which; +}; +struct gb_gpio_get_value_response { + __u8 value; +}; + +struct gb_gpio_set_value_request { + __u8 which; + __u8 value; +}; +/* set value response has no payload */ + +struct gb_gpio_set_debounce_request { + __u8 which; + __le16 usec __packed; +}; +/* debounce response has no payload */ + +struct gb_gpio_irq_type_request { + __u8 which; + __u8 type; +}; +/* irq type response has no payload */ + +struct gb_gpio_irq_mask_request { + __u8 which; +}; +/* irq mask response has no payload */ + +struct gb_gpio_irq_unmask_request { + __u8 which; +}; +/* irq unmask response has no payload */ + +struct gb_gpio_irq_ack_request { + __u8 which; +}; +/* irq ack response has no payload */ + +/* irq event requests originate on another module and are handled on the AP */ +struct gb_gpio_irq_event_request { + __u8 which; +}; +/* irq event response has no payload */ + + +/* PWM */ + +/* Version of the Greybus PWM protocol we support */ +#define GB_PWM_VERSION_MAJOR 0x00 +#define GB_PWM_VERSION_MINOR 0x01 + +/* Greybus PWM request types */ +#define GB_PWM_TYPE_INVALID 0x00 +#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 +#define GB_PWM_TYPE_PWM_COUNT 0x02 +#define GB_PWM_TYPE_ACTIVATE 0x03 +#define GB_PWM_TYPE_DEACTIVATE 0x04 +#define GB_PWM_TYPE_CONFIG 0x05 +#define GB_PWM_TYPE_POLARITY 0x06 +#define GB_PWM_TYPE_ENABLE 0x07 +#define GB_PWM_TYPE_DISABLE 0x08 +#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ + +/* pwm count request has no payload */ +struct gb_pwm_count_response { + __u8 count; +}; + +struct gb_pwm_activate_request { + __u8 which; +}; + +struct gb_pwm_deactivate_request { + __u8 which; +}; + +struct gb_pwm_config_request { + __u8 which; + __le32 duty __packed; + __le32 period __packed; +}; + +struct gb_pwm_polarity_request { + __u8 which; + __u8 polarity; +}; + +struct gb_pwm_enable_request { + __u8 which; +}; + +struct gb_pwm_disable_request { + __u8 which; +}; + +/* I2S */ +#ifndef BIT +#define BIT(n) (1UL << (n)) +#endif + +#define GB_I2S_MGMT_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS 0x02 +#define GB_I2S_MGMT_TYPE_SET_CONFIGURATION 0x03 +#define GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE 0x04 +#define GB_I2S_MGMT_TYPE_GET_PROCESSING_DELAY 0x05 +#define GB_I2S_MGMT_TYPE_SET_START_DELAY 0x06 +#define GB_I2S_MGMT_TYPE_ACTIVATE_CPORT 0x07 +#define GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT 0x08 +#define GB_I2S_MGMT_TYPE_REPORT_EVENT 0x09 + +#define GB_I2S_MGMT_BYTE_ORDER_NA BIT(0) +#define GB_I2S_MGMT_BYTE_ORDER_BE BIT(1) +#define GB_I2S_MGMT_BYTE_ORDER_LE BIT(2) + +#define GB_I2S_MGMT_SPATIAL_LOCATION_FL BIT(0) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FR BIT(1) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FC BIT(2) +#define GB_I2S_MGMT_SPATIAL_LOCATION_LFE BIT(3) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BL BIT(4) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BR BIT(5) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FLC BIT(6) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FRC BIT(7) +#define GB_I2S_MGMT_SPATIAL_LOCATION_C BIT(8) /* BC in USB */ +#define GB_I2S_MGMT_SPATIAL_LOCATION_SL BIT(9) +#define GB_I2S_MGMT_SPATIAL_LOCATION_SR BIT(10) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TC BIT(11) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFL BIT(12) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFC BIT(13) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFR BIT(14) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TBL BIT(15) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TBC BIT(16) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TBR BIT(17) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFLC BIT(18) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFRC BIT(19) +#define GB_I2S_MGMT_SPATIAL_LOCATION_LLFE BIT(20) +#define GB_I2S_MGMT_SPATIAL_LOCATION_RLFE BIT(21) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TSL BIT(22) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TSR BIT(23) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BC BIT(24) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BLC BIT(25) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BRC BIT(26) +#define GB_I2S_MGMT_SPATIAL_LOCATION_RD BIT(31) + +#define GB_I2S_MGMT_PROTOCOL_PCM BIT(0) +#define GB_I2S_MGMT_PROTOCOL_I2S BIT(1) +#define GB_I2S_MGMT_PROTOCOL_LR_STEREO BIT(2) + +#define GB_I2S_MGMT_ROLE_MASTER BIT(0) +#define GB_I2S_MGMT_ROLE_SLAVE BIT(1) + +#define GB_I2S_MGMT_POLARITY_NORMAL BIT(0) +#define GB_I2S_MGMT_POLARITY_REVERSED BIT(1) + +#define GB_I2S_MGMT_EDGE_RISING BIT(0) +#define GB_I2S_MGMT_EDGE_FALLING BIT(1) + +#define GB_I2S_MGMT_EVENT_UNSPECIFIED 0x1 +#define GB_I2S_MGMT_EVENT_HALT 0x2 +#define GB_I2S_MGMT_EVENT_INTERNAL_ERROR 0x3 +#define GB_I2S_MGMT_EVENT_PROTOCOL_ERROR 0x4 +#define GB_I2S_MGMT_EVENT_FAILURE 0x5 +#define GB_I2S_MGMT_EVENT_OUT_OF_SEQUENCE 0x6 +#define GB_I2S_MGMT_EVENT_UNDERRUN 0x7 +#define GB_I2S_MGMT_EVENT_OVERRUN 0x8 +#define GB_I2S_MGMT_EVENT_CLOCKING 0x9 +#define GB_I2S_MGMT_EVENT_DATA_LEN 0xa + +struct gb_i2s_mgmt_configuration { + __le32 sample_frequency; + __u8 num_channels; + __u8 bytes_per_channel; + __u8 byte_order; + __u8 pad; + __le32 spatial_locations; + __le32 ll_protocol; + __u8 ll_bclk_role; + __u8 ll_wclk_role; + __u8 ll_wclk_polarity; + __u8 ll_wclk_change_edge; + __u8 ll_wclk_tx_edge; + __u8 ll_wclk_rx_edge; + __u8 ll_data_offset; + __u8 ll_pad; +}; + +/* get supported configurations request has no payload */ +struct gb_i2s_mgmt_get_supported_configurations_response { + __u8 config_count; + __u8 pad[3]; + struct gb_i2s_mgmt_configuration config[0]; +}; + +struct gb_i2s_mgmt_set_configuration_request { + struct gb_i2s_mgmt_configuration config; +}; +/* set configuration response has no payload */ + +struct gb_i2s_mgmt_set_samples_per_message_request { + __le16 samples_per_message; +}; +/* set samples per message response has no payload */ + +/* get processing request delay has no payload */ +struct gb_i2s_mgmt_get_processing_delay_response { + __le32 microseconds; +}; + +struct gb_i2s_mgmt_set_start_delay_request { + __le32 microseconds; +}; +/* set start delay response has no payload */ + +struct gb_i2s_mgmt_activate_cport_request { + __le16 cport; +}; +/* activate cport response has no payload */ + +struct gb_i2s_mgmt_deactivate_cport_request { + __le16 cport; +}; +/* deactivate cport response has no payload */ + +struct gb_i2s_mgmt_report_event_request { + __u8 event; +}; +/* report event response has no payload */ + +#define GB_I2S_DATA_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2S_DATA_TYPE_SEND_DATA 0x02 + +struct gb_i2s_send_data_request { + __le32 sample_number; + __le32 size; + __u8 data[0]; +}; +/* send data has no response at all */ + +#endif /* __GB_GPBRIDGE_H__ */ diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 20917bf6479f..5ed1895e8701 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -14,6 +14,7 @@ #include #include #include "greybus.h" +#include "gpbridge.h" struct gb_gpio_line { /* The following has to be an array of line_max entries */ @@ -43,110 +44,6 @@ struct gb_gpio_controller { container_of(chip, struct gb_gpio_controller, chip) #define irq_data_to_gpio_chip(d) (d->domain->host_data) -/* Version of the Greybus GPIO protocol we support */ -#define GB_GPIO_VERSION_MAJOR 0x00 -#define GB_GPIO_VERSION_MINOR 0x01 - -/* Greybus GPIO request types */ -#define GB_GPIO_TYPE_INVALID 0x00 -#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 -#define GB_GPIO_TYPE_LINE_COUNT 0x02 -#define GB_GPIO_TYPE_ACTIVATE 0x03 -#define GB_GPIO_TYPE_DEACTIVATE 0x04 -#define GB_GPIO_TYPE_GET_DIRECTION 0x05 -#define GB_GPIO_TYPE_DIRECTION_IN 0x06 -#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 -#define GB_GPIO_TYPE_GET_VALUE 0x08 -#define GB_GPIO_TYPE_SET_VALUE 0x09 -#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a -#define GB_GPIO_TYPE_IRQ_TYPE 0x0b -#define GB_GPIO_TYPE_IRQ_ACK 0x0c -#define GB_GPIO_TYPE_IRQ_MASK 0x0d -#define GB_GPIO_TYPE_IRQ_UNMASK 0x0e -#define GB_GPIO_TYPE_IRQ_EVENT 0x0f -#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ - -/* line count request has no payload */ -struct gb_gpio_line_count_response { - __u8 count; -}; - -struct gb_gpio_activate_request { - __u8 which; -}; -/* activate response has no payload */ - -struct gb_gpio_deactivate_request { - __u8 which; -}; -/* deactivate response has no payload */ - -struct gb_gpio_get_direction_request { - __u8 which; -}; -struct gb_gpio_get_direction_response { - __u8 direction; -}; - -struct gb_gpio_direction_in_request { - __u8 which; -}; -/* direction in response has no payload */ - -struct gb_gpio_direction_out_request { - __u8 which; - __u8 value; -}; -/* direction out response has no payload */ - -struct gb_gpio_get_value_request { - __u8 which; -}; -struct gb_gpio_get_value_response { - __u8 value; -}; - -struct gb_gpio_set_value_request { - __u8 which; - __u8 value; -}; -/* set value response has no payload */ - -struct gb_gpio_set_debounce_request { - __u8 which; - __le16 usec __packed; -}; -/* debounce response has no payload */ - -struct gb_gpio_irq_type_request { - __u8 which; - __u8 type; -}; -/* irq type response has no payload */ - -struct gb_gpio_irq_mask_request { - __u8 which; -}; -/* irq mask response has no payload */ - -struct gb_gpio_irq_unmask_request { - __u8 which; -}; -/* irq unmask response has no payload */ - -struct gb_gpio_irq_ack_request { - __u8 which; -}; -/* irq ack response has no payload */ - -/* irq event requests originate on another module and are handled on the AP */ -struct gb_gpio_irq_event_request { - __u8 which; -}; -/* irq event response has no payload */ - /* Define get_version() routine */ define_get_version(gb_gpio_controller, GPIO); diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 84d20e54ae9b..6fdbf1167ecb 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -13,6 +13,8 @@ #include #include "greybus.h" +#include "gpbridge.h" + struct gb_i2c_device { struct gb_connection *connection; @@ -26,61 +28,6 @@ struct gb_i2c_device { struct i2c_adapter adapter; }; -/* Version of the Greybus i2c protocol we support */ -#define GB_I2C_VERSION_MAJOR 0x00 -#define GB_I2C_VERSION_MINOR 0x01 - -/* Greybus i2c request types */ -#define GB_I2C_TYPE_INVALID 0x00 -#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 -#define GB_I2C_TYPE_FUNCTIONALITY 0x02 -#define GB_I2C_TYPE_TIMEOUT 0x03 -#define GB_I2C_TYPE_RETRIES 0x04 -#define GB_I2C_TYPE_TRANSFER 0x05 -#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -#define GB_I2C_RETRIES_DEFAULT 3 -#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ - -/* functionality request has no payload */ -struct gb_i2c_functionality_response { - __le32 functionality; -}; - -struct gb_i2c_timeout_request { - __le16 msec; -}; -/* timeout response has no payload */ - -struct gb_i2c_retries_request { - __u8 retries; -}; -/* retries response has no payload */ - -/* - * Outgoing data immediately follows the op count and ops array. - * The data for each write (master -> slave) op in the array is sent - * in order, with no (e.g. pad) bytes separating them. - * - * Short reads cause the entire transfer request to fail So response - * payload consists only of bytes read, and the number of bytes is - * exactly what was specified in the corresponding op. Like - * outgoing data, the incoming data is in order and contiguous. - */ -struct gb_i2c_transfer_op { - __le16 addr; - __le16 flags; - __le16 size; -}; - -struct gb_i2c_transfer_request { - __le16 op_count; - struct gb_i2c_transfer_op ops[0]; /* op_count of these */ -}; -struct gb_i2c_transfer_response { - __u8 data[0]; /* inbound data */ -}; - /* Define get_version() routine */ define_get_version(gb_i2c_device, I2C); diff --git a/drivers/staging/greybus/i2s.h b/drivers/staging/greybus/i2s.h deleted file mode 100644 index 5c4275682c3a..000000000000 --- a/drivers/staging/greybus/i2s.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef __GB_I2S_H__ -#define __GB_I2S_H__ - -#ifndef BIT -#define BIT(n) (1UL << (n)) -#endif - -#define GB_I2S_MGMT_TYPE_PROTOCOL_VERSION 0x01 -#define GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS 0x02 -#define GB_I2S_MGMT_TYPE_SET_CONFIGURATION 0x03 -#define GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE 0x04 -#define GB_I2S_MGMT_TYPE_GET_PROCESSING_DELAY 0x05 -#define GB_I2S_MGMT_TYPE_SET_START_DELAY 0x06 -#define GB_I2S_MGMT_TYPE_ACTIVATE_CPORT 0x07 -#define GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT 0x08 -#define GB_I2S_MGMT_TYPE_REPORT_EVENT 0x09 - -#define GB_I2S_MGMT_BYTE_ORDER_NA BIT(0) -#define GB_I2S_MGMT_BYTE_ORDER_BE BIT(1) -#define GB_I2S_MGMT_BYTE_ORDER_LE BIT(2) - -#define GB_I2S_MGMT_SPATIAL_LOCATION_FL BIT(0) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FR BIT(1) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FC BIT(2) -#define GB_I2S_MGMT_SPATIAL_LOCATION_LFE BIT(3) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BL BIT(4) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BR BIT(5) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FLC BIT(6) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FRC BIT(7) -#define GB_I2S_MGMT_SPATIAL_LOCATION_C BIT(8) /* BC in USB */ -#define GB_I2S_MGMT_SPATIAL_LOCATION_SL BIT(9) -#define GB_I2S_MGMT_SPATIAL_LOCATION_SR BIT(10) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TC BIT(11) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFL BIT(12) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFC BIT(13) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFR BIT(14) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TBL BIT(15) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TBC BIT(16) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TBR BIT(17) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFLC BIT(18) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFRC BIT(19) -#define GB_I2S_MGMT_SPATIAL_LOCATION_LLFE BIT(20) -#define GB_I2S_MGMT_SPATIAL_LOCATION_RLFE BIT(21) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TSL BIT(22) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TSR BIT(23) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BC BIT(24) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BLC BIT(25) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BRC BIT(26) -#define GB_I2S_MGMT_SPATIAL_LOCATION_RD BIT(31) - -#define GB_I2S_MGMT_PROTOCOL_PCM BIT(0) -#define GB_I2S_MGMT_PROTOCOL_I2S BIT(1) -#define GB_I2S_MGMT_PROTOCOL_LR_STEREO BIT(2) - -#define GB_I2S_MGMT_ROLE_MASTER BIT(0) -#define GB_I2S_MGMT_ROLE_SLAVE BIT(1) - -#define GB_I2S_MGMT_POLARITY_NORMAL BIT(0) -#define GB_I2S_MGMT_POLARITY_REVERSED BIT(1) - -#define GB_I2S_MGMT_EDGE_RISING BIT(0) -#define GB_I2S_MGMT_EDGE_FALLING BIT(1) - -#define GB_I2S_MGMT_EVENT_UNSPECIFIED 0x1 -#define GB_I2S_MGMT_EVENT_HALT 0x2 -#define GB_I2S_MGMT_EVENT_INTERNAL_ERROR 0x3 -#define GB_I2S_MGMT_EVENT_PROTOCOL_ERROR 0x4 -#define GB_I2S_MGMT_EVENT_FAILURE 0x5 -#define GB_I2S_MGMT_EVENT_OUT_OF_SEQUENCE 0x6 -#define GB_I2S_MGMT_EVENT_UNDERRUN 0x7 -#define GB_I2S_MGMT_EVENT_OVERRUN 0x8 -#define GB_I2S_MGMT_EVENT_CLOCKING 0x9 -#define GB_I2S_MGMT_EVENT_DATA_LEN 0xa - -struct gb_i2s_mgmt_configuration { - __le32 sample_frequency; - __u8 num_channels; - __u8 bytes_per_channel; - __u8 byte_order; - __u8 pad; - __le32 spatial_locations; - __le32 ll_protocol; - __u8 ll_bclk_role; - __u8 ll_wclk_role; - __u8 ll_wclk_polarity; - __u8 ll_wclk_change_edge; - __u8 ll_wclk_tx_edge; - __u8 ll_wclk_rx_edge; - __u8 ll_data_offset; - __u8 ll_pad; -}; - -/* get supported configurations request has no payload */ -struct gb_i2s_mgmt_get_supported_configurations_response { - __u8 config_count; - __u8 pad[3]; - struct gb_i2s_mgmt_configuration config[0]; -}; - -struct gb_i2s_mgmt_set_configuration_request { - struct gb_i2s_mgmt_configuration config; -}; -/* set configuration response has no payload */ - -struct gb_i2s_mgmt_set_samples_per_message_request { - __le16 samples_per_message; -}; -/* set samples per message response has no payload */ - -/* get processing request delay has no payload */ -struct gb_i2s_mgmt_get_processing_delay_response { - __le32 microseconds; -}; - -struct gb_i2s_mgmt_set_start_delay_request { - __le32 microseconds; -}; -/* set start delay response has no payload */ - -struct gb_i2s_mgmt_activate_cport_request { - __le16 cport; -}; -/* activate cport response has no payload */ - -struct gb_i2s_mgmt_deactivate_cport_request { - __le16 cport; -}; -/* deactivate cport response has no payload */ - -struct gb_i2s_mgmt_report_event_request { - __u8 event; -}; -/* report event response has no payload */ - -#define GB_I2S_DATA_TYPE_PROTOCOL_VERSION 0x01 -#define GB_I2S_DATA_TYPE_SEND_DATA 0x02 - -struct gb_i2s_send_data_request { - __le32 sample_number; - __le32 size; - __u8 data[0]; -}; -/* send data has no response at all */ - -#endif /* __GB_I2S_H__ */ diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 4e38b8a4624e..7495716070c7 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -12,6 +12,7 @@ #include #include #include "greybus.h" +#include "gpbridge.h" struct gb_pwm_chip { struct gb_connection *connection; @@ -25,53 +26,6 @@ struct gb_pwm_chip { #define pwm_chip_to_gb_pwm_chip(chip) \ container_of(chip, struct gb_pwm_chip, chip) -/* Version of the Greybus PWM protocol we support */ -#define GB_PWM_VERSION_MAJOR 0x00 -#define GB_PWM_VERSION_MINOR 0x01 - -/* Greybus PWM request types */ -#define GB_PWM_TYPE_INVALID 0x00 -#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 -#define GB_PWM_TYPE_PWM_COUNT 0x02 -#define GB_PWM_TYPE_ACTIVATE 0x03 -#define GB_PWM_TYPE_DEACTIVATE 0x04 -#define GB_PWM_TYPE_CONFIG 0x05 -#define GB_PWM_TYPE_POLARITY 0x06 -#define GB_PWM_TYPE_ENABLE 0x07 -#define GB_PWM_TYPE_DISABLE 0x08 -#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ - -/* pwm count request has no payload */ -struct gb_pwm_count_response { - __u8 count; -}; - -struct gb_pwm_activate_request { - __u8 which; -}; - -struct gb_pwm_deactivate_request { - __u8 which; -}; - -struct gb_pwm_config_request { - __u8 which; - __le32 duty __packed; - __le32 period __packed; -}; - -struct gb_pwm_polarity_request { - __u8 which; - __u8 polarity; -}; - -struct gb_pwm_enable_request { - __u8 which; -}; - -struct gb_pwm_disable_request { - __u8 which; -}; /* Define get_version() routine */ define_get_version(gb_pwm_chip, PWM); -- cgit v1.2.3-59-g8ed1b From 67c920b892fc16141734bdf03f9d16974d8a076f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 10 Apr 2015 11:18:49 +0200 Subject: greybus: gpbridge.h: whitespace cleanups Replace #define with #define. Also move the #ifdef block to below the initial comment block, like other .h files are. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpbridge.h | 80 +++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 8888238df4c3..44a7091b0951 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -1,5 +1,3 @@ -#ifndef __GB_GPBRIDGE_H__ -#define __GB_GPBRIDGE_H__ /* * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. @@ -51,24 +49,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef __GB_GPBRIDGE_H__ +#define __GB_GPBRIDGE_H__ /* I2C */ /* Version of the Greybus i2c protocol we support */ -#define GB_I2C_VERSION_MAJOR 0x00 -#define GB_I2C_VERSION_MINOR 0x01 +#define GB_I2C_VERSION_MAJOR 0x00 +#define GB_I2C_VERSION_MINOR 0x01 /* Greybus i2c request types */ -#define GB_I2C_TYPE_INVALID 0x00 -#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 -#define GB_I2C_TYPE_FUNCTIONALITY 0x02 -#define GB_I2C_TYPE_TIMEOUT 0x03 -#define GB_I2C_TYPE_RETRIES 0x04 -#define GB_I2C_TYPE_TRANSFER 0x05 -#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ +#define GB_I2C_TYPE_INVALID 0x00 +#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2C_TYPE_FUNCTIONALITY 0x02 +#define GB_I2C_TYPE_TIMEOUT 0x03 +#define GB_I2C_TYPE_RETRIES 0x04 +#define GB_I2C_TYPE_TRANSFER 0x05 +#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ -#define GB_I2C_RETRIES_DEFAULT 3 -#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ +#define GB_I2C_RETRIES_DEFAULT 3 +#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ /* functionality request has no payload */ struct gb_i2c_functionality_response { @@ -113,29 +113,29 @@ struct gb_i2c_transfer_response { /* GPIO */ /* Version of the Greybus GPIO protocol we support */ -#define GB_GPIO_VERSION_MAJOR 0x00 -#define GB_GPIO_VERSION_MINOR 0x01 +#define GB_GPIO_VERSION_MAJOR 0x00 +#define GB_GPIO_VERSION_MINOR 0x01 /* Greybus GPIO request types */ -#define GB_GPIO_TYPE_INVALID 0x00 -#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 -#define GB_GPIO_TYPE_LINE_COUNT 0x02 -#define GB_GPIO_TYPE_ACTIVATE 0x03 -#define GB_GPIO_TYPE_DEACTIVATE 0x04 -#define GB_GPIO_TYPE_GET_DIRECTION 0x05 -#define GB_GPIO_TYPE_DIRECTION_IN 0x06 -#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 -#define GB_GPIO_TYPE_GET_VALUE 0x08 -#define GB_GPIO_TYPE_SET_VALUE 0x09 -#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a +#define GB_GPIO_TYPE_INVALID 0x00 +#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 +#define GB_GPIO_TYPE_LINE_COUNT 0x02 +#define GB_GPIO_TYPE_ACTIVATE 0x03 +#define GB_GPIO_TYPE_DEACTIVATE 0x04 +#define GB_GPIO_TYPE_GET_DIRECTION 0x05 +#define GB_GPIO_TYPE_DIRECTION_IN 0x06 +#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 +#define GB_GPIO_TYPE_GET_VALUE 0x08 +#define GB_GPIO_TYPE_SET_VALUE 0x09 +#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a #define GB_GPIO_TYPE_IRQ_TYPE 0x0b #define GB_GPIO_TYPE_IRQ_ACK 0x0c #define GB_GPIO_TYPE_IRQ_MASK 0x0d #define GB_GPIO_TYPE_IRQ_UNMASK 0x0e #define GB_GPIO_TYPE_IRQ_EVENT 0x0f -#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ +#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ -#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ +#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ /* line count request has no payload */ struct gb_gpio_line_count_response { @@ -220,20 +220,20 @@ struct gb_gpio_irq_event_request { /* PWM */ /* Version of the Greybus PWM protocol we support */ -#define GB_PWM_VERSION_MAJOR 0x00 -#define GB_PWM_VERSION_MINOR 0x01 +#define GB_PWM_VERSION_MAJOR 0x00 +#define GB_PWM_VERSION_MINOR 0x01 /* Greybus PWM request types */ -#define GB_PWM_TYPE_INVALID 0x00 -#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 -#define GB_PWM_TYPE_PWM_COUNT 0x02 -#define GB_PWM_TYPE_ACTIVATE 0x03 -#define GB_PWM_TYPE_DEACTIVATE 0x04 -#define GB_PWM_TYPE_CONFIG 0x05 -#define GB_PWM_TYPE_POLARITY 0x06 -#define GB_PWM_TYPE_ENABLE 0x07 -#define GB_PWM_TYPE_DISABLE 0x08 -#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ +#define GB_PWM_TYPE_INVALID 0x00 +#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 +#define GB_PWM_TYPE_PWM_COUNT 0x02 +#define GB_PWM_TYPE_ACTIVATE 0x03 +#define GB_PWM_TYPE_DEACTIVATE 0x04 +#define GB_PWM_TYPE_CONFIG 0x05 +#define GB_PWM_TYPE_POLARITY 0x06 +#define GB_PWM_TYPE_ENABLE 0x07 +#define GB_PWM_TYPE_DISABLE 0x08 +#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ /* pwm count request has no payload */ struct gb_pwm_count_response { -- cgit v1.2.3-59-g8ed1b From dcd1dadd7ee32b3fd135904fac85c556bac5cbc5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 10 Apr 2015 11:20:35 +0200 Subject: greybus: gpbridge.h: remove BIT() define It's up to other files to define this if it's not present, not this file. Reported-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpbridge.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 44a7091b0951..e2301888a322 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -268,9 +268,6 @@ struct gb_pwm_disable_request { }; /* I2S */ -#ifndef BIT -#define BIT(n) (1UL << (n)) -#endif #define GB_I2S_MGMT_TYPE_PROTOCOL_VERSION 0x01 #define GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS 0x02 -- cgit v1.2.3-59-g8ed1b From 1ffc12be5549085faac2d6116f666cb9cbcb2930 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 16 Apr 2015 09:53:59 +0200 Subject: greybus: loopback: fix 64-bit divisions The code uses 64-bit divisions, which should be avoided, and also prevents the module from loading on 32-bit systems: gb_loopback: Unknown symbol __aeabi_uldivmod (err 0) Fix by using the kernel's 64-bit by 32-bit division implementation do_div. Compile tested only. I did not look very closely at the code itself. Perhaps this could be worked around in some other way, but this silences the linker warning and allows the module to be loaded. Reviewed-by: Alex Elder Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 9914b52c71ce..9860d64e50ba 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -13,6 +13,8 @@ #include #include #include +#include + #include "greybus.h" struct gb_loopback_stats { @@ -248,12 +250,16 @@ static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u64 elapsed_nsecs) { u32 avg; + u64 tmp; if (elapsed_nsecs >= NSEC_PER_SEC) { - if (!stats->count) - avg = stats->sum * (elapsed_nsecs / NSEC_PER_SEC); - else + if (!stats->count) { + tmp = elapsed_nsecs; + do_div(tmp, NSEC_PER_SEC); + avg = stats->sum * tmp; + } else { avg = stats->sum / stats->count; + } if (stats->min > avg) stats->min = avg; if (stats->max < avg) @@ -281,10 +287,11 @@ static void gb_loopback_latency_update(struct gb_loopback *gb, struct timeval *tlat) { u32 lat; - u64 nsecs; + u64 tmp; - nsecs = timeval_to_ns(tlat); - lat = nsecs / NSEC_PER_MSEC; + tmp = timeval_to_ns(tlat); + do_div(tmp, NSEC_PER_MSEC); + lat = tmp; if (gb->latency.min > lat) gb->latency.min = lat; -- cgit v1.2.3-59-g8ed1b From 75052a5504705aded68e544967fdd1d296470bcc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Apr 2015 19:48:37 +0200 Subject: greybus: bundle: add state sysfs file A bundle has a state file, that is managed by the endo userspace process. This file can be written to and any process that is polling on the file will be woken up and can read the new value. It's a "cheap" IPC for programs that are not allowed to do anything other than read/write to kernel sysfs files. Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs-bus-greybus | 13 ++++++++ drivers/staging/greybus/bundle.c | 37 ++++++++++++++++++++-- drivers/staging/greybus/bundle.h | 1 + 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 4b511dbff0da..ab728a46e657 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -113,3 +113,16 @@ KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The device id of a Greybus bundle. + +What: /sys/bus/greybus/device/.../state +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + A bundle has a state that is managed by the userspace + Endo process. This file allows that Endo to signal + other Android HALs that the state of the bundle has + changed to a specific value. When written to, any + process watching the file will be woken up, and the new + value can be read. It's a "poor-man's IPC", yes, but + simplifies the Android userspace code immensely. diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 3f1aa6490e48..e7b2199f39b4 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -1,8 +1,8 @@ /* * Greybus bundles * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ @@ -31,9 +31,41 @@ static ssize_t class_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(class); +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + + if (bundle->state == NULL) + return sprintf(buf, "\n"); + + return sprintf(buf, "%s\n", bundle->state); +} + +static ssize_t state_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + + kfree(bundle->state); + bundle->state = kzalloc(size + 1, GFP_KERNEL); + if (!bundle->state) + return -ENOMEM; + + memcpy(bundle->state, buf, size); + + /* Tell userspace that the file contents changed */ + sysfs_notify(&bundle->dev.kobj, NULL, "state"); + + return size; +} +static DEVICE_ATTR_RW(state); + + static struct attribute *bundle_attrs[] = { &dev_attr_device_id.attr, &dev_attr_class.attr, + &dev_attr_state.attr, NULL, }; @@ -43,6 +75,7 @@ static void gb_bundle_release(struct device *dev) { struct gb_bundle *bundle = to_gb_bundle(dev); + kfree(bundle->state); kfree(bundle); } diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 3265a008e5df..5c12c72ddcec 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -20,6 +20,7 @@ struct gb_bundle { u8 class; u8 device_id; struct list_head connections; + u8 *state; struct list_head links; /* interface->bundles */ }; -- cgit v1.2.3-59-g8ed1b From 6cf42a447dece6d4b75b59dc531ddddf770ee324 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Apr 2015 19:51:33 +0200 Subject: greybus: MODULE_LICENSE cleanup These are all GPLv2-only kernel modules, so properly set the correct MODULE_LICENSE string to make static checkers happy. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/es1.c | 2 +- drivers/staging/greybus/es2.c | 2 +- drivers/staging/greybus/gpb.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 458020997294..db1da8a37787 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -272,5 +272,5 @@ static void __exit gb_exit(void) } module_exit(gb_exit); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 0df7bddbff69..4bba5d558e84 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -694,5 +694,5 @@ static struct usb_driver es1_ap_driver = { module_usb_driver(es1_ap_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index dfadcb4ad93c..efdd48d9df8f 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -694,5 +694,5 @@ static struct usb_driver es1_ap_driver = { module_usb_driver(es1_ap_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/gpb.c b/drivers/staging/greybus/gpb.c index 33bbe7917f09..d5747b291171 100644 --- a/drivers/staging/greybus/gpb.c +++ b/drivers/staging/greybus/gpb.c @@ -87,4 +87,4 @@ static void __exit gpbridge_exit(void) } module_exit(gpbridge_exit); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 58d674650b96d7ae81cda68b0cbf40fb3f9a9726 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Apr 2015 19:40:26 +0530 Subject: greybus: endo: Use a real endo id 0x0555 isn't a valid endo id, use a real one. 0x4755 should be the Endo id for the (medium) Spiral 2 prototype. Lets use that. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 28e1f2871bf8..b8f6163566be 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -61,14 +61,17 @@ struct device_type greybus_endo_type = { * * List must end with 0x00 in order to properly terminate the list. */ -static u8 endo_0555[] = { +static u8 endo_4755[] = { 0x01, 0x03, 0x05, + 0x06, 0x07, 0x08, 0x0a, 0x0c, + 0x0d, + 0x0e, 0x00, }; @@ -81,8 +84,8 @@ static int create_modules(struct gb_endo *endo) /* Depending on the endo type, create a bunch of different modules */ switch (endo->type) { - case 0x0555: - endo_modules = &endo_0555[0]; + case 0x4755: + endo_modules = &endo_4755[0]; break; default: dev_err(&endo->dev, "Unknown endo type 0x%04x, aborting!", @@ -120,7 +123,7 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd) // using "fake" numbers. strcpy(&endo->svc.serial_number[0], "042"); strcpy(&endo->svc.version[0], "0.0"); - endo->type = 0x0555; + endo->type = 0x4755; dev_set_name(&endo->dev, "endo-0x%04x", endo->type); retval = device_add(&endo->dev); -- cgit v1.2.3-59-g8ed1b From 8f8b2297c0a17236ca4b5413e1f737536a8154a1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Apr 2015 19:40:27 +0530 Subject: greybus: endo: Rename endo's 'type' as 'id' There can be three Endo types: mini, medium and large. And that's what Endo 'type' should refer to. But we have named the 16 bit number that uniquely represents a valid endo, as its type. 'id' seems to be a more suitable name to that instead of 'type'. Lets rename it. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 18 +++++++++--------- drivers/staging/greybus/endo.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index b8f6163566be..b9c7ee62456a 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -82,14 +82,14 @@ static int create_modules(struct gb_endo *endo) u8 *endo_modules; int i; - /* Depending on the endo type, create a bunch of different modules */ - switch (endo->type) { + /* Depending on the endo id, create a bunch of different modules */ + switch (endo->id) { case 0x4755: endo_modules = &endo_4755[0]; break; default: - dev_err(&endo->dev, "Unknown endo type 0x%04x, aborting!", - endo->type); + dev_err(&endo->dev, "Unknown endo id 0x%04x, aborting!", + endo->id); return -EINVAL; } @@ -118,18 +118,18 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd) endo->dev.dma_mask = hd->parent->dma_mask; device_initialize(&endo->dev); - // FIXME - determine endo "type" from the SVC + // FIXME - determine endo "id" from the SVC // Also get the version and serial number from the SVC, right now we are // using "fake" numbers. strcpy(&endo->svc.serial_number[0], "042"); strcpy(&endo->svc.version[0], "0.0"); - endo->type = 0x4755; + endo->id = 0x4755; - dev_set_name(&endo->dev, "endo-0x%04x", endo->type); + dev_set_name(&endo->dev, "endo-0x%04x", endo->id); retval = device_add(&endo->dev); if (retval) { - dev_err(hd->parent, "failed to add endo device of type 0x%04x\n", - endo->type); + dev_err(hd->parent, "failed to add endo device of id 0x%04x\n", + endo->id); put_device(&endo->dev); kfree(endo); return NULL; diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index 649093e4025d..a4342aaa582b 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -18,7 +18,7 @@ struct gb_svc { struct gb_endo { struct device dev; struct gb_svc svc; - u16 type; + u16 id; }; #define to_gb_endo(d) container_of(d, struct gb_endo, dev) -- cgit v1.2.3-59-g8ed1b From af6e8b42334c92b2dcbb93e79b0e7ba4f1d51bb4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Apr 2015 19:51:34 +0530 Subject: greybus: Explicitly mark structures as packed These structures are already marked as __packed, as these are enclosed within: #pragma pack(push, 1) #pragma pack(pop) Lets mark them __packed explicitly. Reviewed-by: Alex Elder Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 22 +++++++++------------- drivers/staging/greybus/svc_msg.h | 30 +++++++++++++----------------- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index e855adab3c22..90965766dceb 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -13,8 +13,6 @@ #ifndef __GREYBUS_MANIFEST_H #define __GREYBUS_MANIFEST_H -#pragma pack(push, 1) - enum greybus_descriptor_type { GREYBUS_TYPE_INVALID = 0x00, GREYBUS_TYPE_INTERFACE = 0x01, @@ -77,7 +75,7 @@ struct greybus_descriptor_string { __u8 length; __u8 id; __u8 string[0]; -}; +} __packed; /* * An interface descriptor describes information about an interface as a whole, @@ -90,7 +88,7 @@ struct greybus_descriptor_interface { __u8 vendor_stringid; __u8 product_stringid; __le64 unique_id; -}; +} __packed; /* * An bundle descriptor defines an identification number and a class for @@ -114,7 +112,7 @@ struct greybus_descriptor_interface { struct greybus_descriptor_bundle { __u8 id; /* interface-relative id (0..) */ __u8 class; -}; +} __packed; /* * A CPort descriptor indicates the id of the bundle within the @@ -126,7 +124,7 @@ struct greybus_descriptor_cport { __u8 bundle; __le16 id; __u8 protocol_id; /* enum greybus_protocol */ -}; +} __packed; /* * A class descriptor defines functionality supplied by a module. @@ -134,12 +132,12 @@ struct greybus_descriptor_cport { */ struct greybus_descriptor_class { __u8 class; /* enum greybus_class_type */ -}; +} __packed; struct greybus_descriptor_header { __le16 size; __u8 type; /* enum greybus_descriptor_type */ -}; +} __packed; struct greybus_descriptor { struct greybus_descriptor_header header; @@ -150,19 +148,17 @@ struct greybus_descriptor { struct greybus_descriptor_cport cport; struct greybus_descriptor_class class; }; -}; +} __packed; struct greybus_manifest_header { __le16 size; __u8 version_major; __u8 version_minor; -}; +} __packed; struct greybus_manifest { struct greybus_manifest_header header; struct greybus_descriptor descriptors[0]; -}; - -#pragma pack(pop) +} __packed; #endif /* __GREYBUS_MANIFEST_H */ diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index cb7bb19975de..fadc1d70952e 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -13,8 +13,6 @@ #ifndef __SVC_MSG_H #define __SVC_MSG_H -#pragma pack(push, 1) - enum svc_function_id { SVC_FUNCTION_HANDSHAKE = 0x00, SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, @@ -33,7 +31,7 @@ struct svc_msg_header { __u8 function_id; /* enum svc_function_id */ __u8 message_type; __le16 payload_length; -}; +} __packed; enum svc_function_handshake_type { SVC_HANDSHAKE_SVC_HELLO = 0x00, @@ -45,21 +43,21 @@ struct svc_function_handshake { __u8 version_major; __u8 version_minor; __u8 handshake_type; /* enum svc_function_handshake_type */ -}; +} __packed; struct svc_function_unipro_set_route { __u8 device_id; -}; +} __packed; struct svc_function_unipro_link_up { __u8 interface_id; /* Interface id within the Endo */ __u8 device_id; -}; +} __packed; struct svc_function_ap_id { __u8 interface_id; __u8 device_id; -}; +} __packed; enum svc_function_management_event { SVC_MANAGEMENT_AP_ID = 0x00, @@ -74,7 +72,7 @@ struct svc_function_unipro_management { struct svc_function_unipro_link_up link_up; struct svc_function_unipro_set_route set_route; }; -}; +} __packed; enum svc_function_hotplug_event { SVC_HOTPLUG_EVENT = 0x00, @@ -85,7 +83,7 @@ struct svc_function_hotplug { __u8 hotplug_event; /* enum svc_function_hotplug_event */ __u8 interface_id; /* Interface id within the Endo */ __u8 data[0]; -}; +} __packed; enum svc_function_power_type { SVC_POWER_BATTERY_STATUS = 0x00, @@ -104,10 +102,10 @@ struct svc_function_power_battery_status { __le16 charge_full; __le16 charge_now; __u8 status; /* enum svc_function_battery_status */ -}; +} __packed; struct svc_function_power_battery_status_request { -}; +} __packed; /* XXX * Each interface carries power, so it's possible these things @@ -121,7 +119,7 @@ struct svc_function_power { struct svc_function_power_battery_status status; struct svc_function_power_battery_status_request request; }; -}; +} __packed; enum svc_function_epm_command_type { SVC_EPM_ENABLE = 0x00, @@ -132,7 +130,7 @@ enum svc_function_epm_command_type { struct svc_function_epm { __u8 epm_command_type; /* enum svc_function_epm_command_type */ __u8 module_id; -}; +} __packed; enum svc_function_suspend_command_type { SVC_SUSPEND_FIXME_1 = 0x00, // FIXME @@ -143,7 +141,7 @@ enum svc_function_suspend_command_type { struct svc_function_suspend { __u8 suspend_command_type; /* enum function_suspend_command_type */ __u8 device_id; -}; +} __packed; struct svc_msg { struct svc_msg_header header; @@ -155,8 +153,6 @@ struct svc_msg { struct svc_function_epm epm; struct svc_function_suspend suspend; }; -}; - -#pragma pack(pop) +} __packed; #endif /* __SVC_MSG_H */ -- cgit v1.2.3-59-g8ed1b From fa2fbf16f6aaaabee9394edc57dc803b59de6699 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Apr 2015 19:51:35 +0530 Subject: greybus: manifest: Account for padding in string descriptor String descriptors are padded towards the end to align them to 4 byte boundaries. Take that into account while calculating expected size. Reviewed-by: Alex Elder Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index d6cafebefd26..12536637536c 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -100,6 +100,9 @@ static int identify_descriptor(struct gb_interface *intf, case GREYBUS_TYPE_STRING: expected_size += sizeof(struct greybus_descriptor_string); expected_size += desc->string.length; + + /* String descriptors are padded to 4 byte boundaries */ + expected_size = ALIGN(expected_size, 4); break; case GREYBUS_TYPE_INTERFACE: expected_size += sizeof(struct greybus_descriptor_interface); -- cgit v1.2.3-59-g8ed1b From 55b930cd17a7cd66a1fb2a587660a344ea5a9ee2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Apr 2015 11:02:08 +0530 Subject: greybus: manifest: Warn if descriptor size > expected size A descriptor passed to AP can be bigger than what AP expects, if manifest's minor version is higher with same major number as the AP. As it can have some extra data in descriptor. But, if AP and manifest versions are identical, or if the AP's minor version is greater than the manifest version, we should at least warn (if not fail). Doing this would require some changes to record the manifest version somewhere reachable by identify_descriptor(). For now, just warn if descriptor is bigger than expected. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 12536637536c..2346ead2a977 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -129,6 +129,13 @@ static int identify_descriptor(struct gb_interface *intf, return -EINVAL; } + /* Descriptor bigger than what we expect */ + if (desc_size > expected_size) { + pr_warn("%s descriptor size mismatch (want %zu got %zu)\n", + get_descriptor_type_string(desc_header->type), + expected_size, desc_size); + } + descriptor = kzalloc(sizeof(*descriptor), GFP_KERNEL); if (!descriptor) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 22fd2a8ade59bb97f0a64282ba5b3903ba3e9e89 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 17 Apr 2015 14:41:47 -0500 Subject: greybus: bundle: use kstrdup() for state file The kernfs code guarantees we'll get a NUL-terminated buffer. Use kstrdup() rather than kzalloc() + memcpy() in state_store() making it slightly clearer what we're doing. This has the added benefit of guaranteeing that the stored string has no NUL character inside it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index e7b2199f39b4..2047e173ebd9 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -48,12 +48,10 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr, struct gb_bundle *bundle = to_gb_bundle(dev); kfree(bundle->state); - bundle->state = kzalloc(size + 1, GFP_KERNEL); + bundle->state = kstrdup(buf, GFP_KERNEL); if (!bundle->state) return -ENOMEM; - memcpy(bundle->state, buf, size); - /* Tell userspace that the file contents changed */ sysfs_notify(&bundle->dev.kobj, NULL, "state"); -- cgit v1.2.3-59-g8ed1b From 8a5286ed2a0a7d394817a63ae37ae54608a4d3b4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Apr 2015 19:51:37 +0530 Subject: greybus: manifest: Remove vendor, product and unique-id from interface descriptor These should come from control protocol instead. For now, initialize this statically with a FIXME to not forget it later. Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 3 --- drivers/staging/greybus/manifest.c | 8 +++++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 90965766dceb..1fb01319b9a3 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -82,12 +82,9 @@ struct greybus_descriptor_string { * *not* the functions within it. */ struct greybus_descriptor_interface { - __le16 vendor; - __le16 product; __le16 version; // TODO - remove after Dec demo. __u8 vendor_stringid; __u8 product_stringid; - __le64 unique_id; } __packed; /* diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 2346ead2a977..de234d2386f6 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -304,9 +304,11 @@ static bool gb_manifest_parse_interface(struct gb_interface *intf, goto out_free_vendor_string; } - intf->vendor = le16_to_cpu(desc_intf->vendor); - intf->product = le16_to_cpu(desc_intf->product); - intf->unique_id = le64_to_cpu(desc_intf->unique_id); + // FIXME + // Vendor, Product and Unique id must come via control protocol + intf->vendor = 0xffff; + intf->product = 0x0001; + intf->unique_id = 0; /* Release the interface descriptor, now that we're done with it */ release_manifest_descriptor(interface_desc); -- cgit v1.2.3-59-g8ed1b From 20f087ec5c8c746020c91d296339eebe4d8ed4dd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Apr 2015 19:51:38 +0530 Subject: greybus: manifest: drop interface-version field It is not required anymore. Drop it. Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 1fb01319b9a3..bea4de27ff9f 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -82,7 +82,6 @@ struct greybus_descriptor_string { * *not* the functions within it. */ struct greybus_descriptor_interface { - __le16 version; // TODO - remove after Dec demo. __u8 vendor_stringid; __u8 product_stringid; } __packed; -- cgit v1.2.3-59-g8ed1b From b022515ee64b38baed81b83a47783cbb52b9c5f9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Apr 2015 19:51:39 +0530 Subject: greybus: Remove class descriptor We carry this information as part of bundle descriptor now and this can be removed. Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 10 ---------- drivers/staging/greybus/manifest.c | 5 ----- 2 files changed, 15 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index bea4de27ff9f..60f183a9940a 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -19,7 +19,6 @@ enum greybus_descriptor_type { GREYBUS_TYPE_STRING = 0x02, GREYBUS_TYPE_BUNDLE = 0x03, GREYBUS_TYPE_CPORT = 0x04, - GREYBUS_TYPE_CLASS = 0x05, }; enum greybus_protocol { @@ -122,14 +121,6 @@ struct greybus_descriptor_cport { __u8 protocol_id; /* enum greybus_protocol */ } __packed; -/* - * A class descriptor defines functionality supplied by a module. - * Beyond that, not much else is defined yet... - */ -struct greybus_descriptor_class { - __u8 class; /* enum greybus_class_type */ -} __packed; - struct greybus_descriptor_header { __le16 size; __u8 type; /* enum greybus_descriptor_type */ @@ -142,7 +133,6 @@ struct greybus_descriptor { struct greybus_descriptor_interface interface; struct greybus_descriptor_bundle bundle; struct greybus_descriptor_cport cport; - struct greybus_descriptor_class class; }; } __packed; diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index de234d2386f6..597ba7077668 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -26,8 +26,6 @@ static const char *get_descriptor_type_string(u8 type) return "cport"; case GREYBUS_TYPE_BUNDLE: return "bundle"; - case GREYBUS_TYPE_CLASS: - return "class"; default: WARN_ON(1); return "unknown"; @@ -113,9 +111,6 @@ static int identify_descriptor(struct gb_interface *intf, case GREYBUS_TYPE_CPORT: expected_size += sizeof(struct greybus_descriptor_cport); break; - case GREYBUS_TYPE_CLASS: - pr_warn("class descriptor found (ignoring)\n"); - break; case GREYBUS_TYPE_INVALID: default: pr_err("invalid descriptor type (%hhu)\n", desc_header->type); -- cgit v1.2.3-59-g8ed1b From 499ee955a132c772d14b839bcec3adef2eebcb6b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Apr 2015 19:51:40 +0530 Subject: greybus: Explicitly add pad-bytes to manifest descriptors Explicitly add pad-bytes to manifest descriptors to match their layout in greybus specification. Reviewed-by: Alex Elder Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 60f183a9940a..9d32a4c66bc9 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -83,6 +83,7 @@ struct greybus_descriptor_string { struct greybus_descriptor_interface { __u8 vendor_stringid; __u8 product_stringid; + __u8 pad[2]; } __packed; /* @@ -107,6 +108,7 @@ struct greybus_descriptor_interface { struct greybus_descriptor_bundle { __u8 id; /* interface-relative id (0..) */ __u8 class; + __u8 pad[2]; } __packed; /* @@ -124,6 +126,7 @@ struct greybus_descriptor_cport { struct greybus_descriptor_header { __le16 size; __u8 type; /* enum greybus_descriptor_type */ + __u8 pad; } __packed; struct greybus_descriptor { -- cgit v1.2.3-59-g8ed1b From f03eec87720d3427443b64f2d1b237b85d5c68b4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Apr 2015 19:51:41 +0530 Subject: greybus: Move id-field before bundle-field in CPort Descriptor. Note that this also makes sure the id-field is naturally aligned in case we ever were to remove the __packed attribute. Reviewed-by: Alex Elder Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 9d32a4c66bc9..05af0292b690 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -118,8 +118,8 @@ struct greybus_descriptor_bundle { * exchanged using the CPort. */ struct greybus_descriptor_cport { - __u8 bundle; __le16 id; + __u8 bundle; __u8 protocol_id; /* enum greybus_protocol */ } __packed; -- cgit v1.2.3-59-g8ed1b From a549be518623e0948213bdacd338183bba033229 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 1 May 2015 20:41:00 +0200 Subject: greybus: battery: update for 4.1 power supply api changes The 4.1-rc1 kernel changed the power supply apis such that the structures are now owned by the power supply core, and not the individual drivers. This broke the greybus battery driver, so update it to support both the old and the new apis. The API changes were such that I can't "hide" them in kernel_ver.h, but rather the driver itself needs to have ugly #ifdefs in it. I tried to keep it to a minimum, making a sub-function for initializing the power supply device that is implemented differently for different kernel versions. When this is submitted upstream, or if we ever move our AP development to 4.1 or greater, the support for older kernels can be removed. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/battery.c | 83 ++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index b968d149741e..eb688c9e821a 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -7,6 +7,7 @@ * Released under the GPLv2 only. */ +#include #include #include #include @@ -14,7 +15,19 @@ #include "greybus.h" struct gb_battery { + /* + * The power supply api changed in 4.1, so handle both the old + * and new apis in the same driver for now, until this is merged + * upstream, when all of these version checks can be removed. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) struct power_supply bat; +#define to_gb_battery(x) container_of(x, struct gb_battery, bat) +#else + struct power_supply *bat; + struct power_supply_desc desc; +#define to_gb_battery(x) power_supply_get_drvdata(x) +#endif // FIXME // we will want to keep the battery stats in here as we will be getting // updates from the SVC "on the fly" so we don't have to always go ask @@ -24,7 +37,6 @@ struct gb_battery { u8 version_minor; }; -#define to_gb_battery(x) container_of(x, struct gb_battery, bat) /* Version of the Greybus battery protocol we support */ #define GB_BATTERY_VERSION_MAJOR 0x00 @@ -283,10 +295,56 @@ static enum power_supply_property battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +static int init_and_register(struct gb_connection *connection, + struct gb_battery *gb) +{ + int retval; + + // FIXME - get a better (i.e. unique) name + // FIXME - anything else needs to be set? + gb->bat.name = "gb_battery"; + gb->bat.type = POWER_SUPPLY_TYPE_BATTERY; + gb->bat.properties = battery_props; + gb->bat.num_properties = ARRAY_SIZE(battery_props); + gb->bat.get_property = get_property; + + retval = power_supply_register(&connection->bundle->intf->dev, + &gb->bat); + if (retval) + kfree(gb); + return retval; +} +#else +static int init_and_register(struct gb_connection *connection, + struct gb_battery *gb) +{ + struct power_supply_config cfg = {}; + int retval = 0; + + cfg.drv_data = gb; + + // FIXME - get a better (i.e. unique) name + // FIXME - anything else needs to be set? + gb->desc.name = "gb_battery"; + gb->desc.type = POWER_SUPPLY_TYPE_BATTERY; + gb->desc.properties = battery_props; + gb->desc.num_properties = ARRAY_SIZE(battery_props); + gb->desc.get_property = get_property; + + gb->bat = power_supply_register(&connection->bundle->intf->dev, + &gb->desc, &cfg); + if (IS_ERR(gb->bat)) { + retval = PTR_ERR(gb->bat); + kfree(gb); + } + return retval; +} +#endif + static int gb_battery_connection_init(struct gb_connection *connection) { struct gb_battery *gb; - struct power_supply *b; int retval; gb = kzalloc(sizeof(*gb), GFP_KERNEL); @@ -303,29 +361,18 @@ static int gb_battery_connection_init(struct gb_connection *connection) return retval; } - b = &gb->bat; - // FIXME - get a better (i.e. unique) name - // FIXME - anything else needs to be set? - b->name = "gb_battery"; - b->type = POWER_SUPPLY_TYPE_BATTERY, - b->properties = battery_props, - b->num_properties = ARRAY_SIZE(battery_props), - b->get_property = get_property, - - retval = power_supply_register(&connection->bundle->intf->dev, b); - if (retval) { - kfree(gb); - return retval; - } - - return 0; + return init_and_register(connection, gb); } static void gb_battery_connection_exit(struct gb_connection *connection) { struct gb_battery *gb = connection->private; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) power_supply_unregister(&gb->bat); +#else + power_supply_unregister(gb->bat); +#endif kfree(gb); } -- cgit v1.2.3-59-g8ed1b From 99a4bd5902224065debff524624800bd9806419e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 1 May 2015 21:04:47 +0200 Subject: greybus: kernel_ver.h: fix leading space coding style issues When cutting and pasting some of the ATTR macros into kernel_ver.h, I dropped the tabs. Fix this up and make checkpatch.pl happy. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/kernel_ver.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 92eb024c3bed..891090f67e28 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -18,14 +18,14 @@ #ifndef __ATTR_WO #define __ATTR_WO(_name) { \ - .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ - .store = _name##_store, \ + .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ + .store = _name##_store, \ } #endif #ifndef __ATTR_RW #define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \ - _name##_show, _name##_store) + _name##_show, _name##_store) #endif #ifndef DEVICE_ATTR_RO -- cgit v1.2.3-59-g8ed1b From 3906a59c96ac770d54728d2cbd6564cda3b2cdd9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 1 May 2015 21:05:03 +0200 Subject: greybus: kernel_ver.h: add a ' ' character to KERNEL_VERSION() You should always put a space after a ',', so do it for the KERNEL_VERSION() macro as well. This makes checkpatch.pl happy also. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/kernel_ver.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 891090f67e28..83d08dad6c14 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -60,7 +60,7 @@ #include #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) static inline void gb_gpiochip_remove(struct gpio_chip *chip) { gpiochip_remove(chip); @@ -77,7 +77,7 @@ static inline void gb_gpiochip_remove(struct gpio_chip *chip) * ATTRIBUTE_GROUPS showed up in 3.11-rc2, but we need to build on 3.10, so add * it here. */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) #include #define ATTRIBUTE_GROUPS(name) \ -- cgit v1.2.3-59-g8ed1b From 63ca78b9abe4d216aec08724c9677b86481fa271 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 1 May 2015 21:05:29 +0200 Subject: greybus: kernel_ver.h: add a blank line after a variable definition checkpatch reminds us that a blank line should go after a variable definition, so fix it up here. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/kernel_ver.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 83d08dad6c14..e63d6cb58ce7 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -69,6 +69,7 @@ static inline void gb_gpiochip_remove(struct gpio_chip *chip) static inline void gb_gpiochip_remove(struct gpio_chip *chip) { int ret; + ret = gpiochip_remove(chip); } #endif -- cgit v1.2.3-59-g8ed1b From f487c802cba56b5f38c5cb5d2eb32b9562f50dde Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 1 May 2015 21:05:50 +0200 Subject: greybus: protocol.h: macros should not have a trailing ';' Remove the trailing ';' character from the gb_protocol_driver() macro as it's not needed and is bad coding style. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/protocol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 82d9e81386e3..d16a582d571a 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -97,7 +97,7 @@ static void __exit protocol_exit(void) \ { \ gb_protocol_deregister(__protocol); \ } \ -module_exit(protocol_exit); +module_exit(protocol_exit) /* * Macro to create get_version() routine for protocols -- cgit v1.2.3-59-g8ed1b From e80d09af1e8a23c1d9c9da99540855cde21e00a1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 1 May 2015 21:06:12 +0200 Subject: greybus: protocol.h: fix up long lines in define_get_version() No need to go past 80 characters for the define_get_version macro, so fix up the indentation to not do so. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/protocol.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index d16a582d571a..495d13aed365 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -110,10 +110,10 @@ static int get_version(struct __device *dev) \ struct gb_protocol_version_response response; \ int retval; \ \ - retval = gb_protocol_get_version(dev->connection, \ - GB_##__protocol##_TYPE_PROTOCOL_VERSION,\ - NULL, 0, &response, \ - GB_##__protocol##_VERSION_MAJOR); \ + retval = gb_protocol_get_version(dev->connection, \ + GB_##__protocol##_TYPE_PROTOCOL_VERSION,\ + NULL, 0, &response, \ + GB_##__protocol##_VERSION_MAJOR); \ if (retval) \ return retval; \ \ -- cgit v1.2.3-59-g8ed1b From c13c8bf0cccd6a9daf8ea02919aecaa45c842fc7 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 5 May 2015 11:04:22 -0500 Subject: greybus: es2: fix driver name collision with es1 Both ES1 and ES2 drivers cannot be loaded due to a driver name conflict. Give ES2 driver the correct name. Signed-off-by: Rob Herring Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index efdd48d9df8f..cc73fbd9b77b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -686,7 +686,7 @@ error: } static struct usb_driver es1_ap_driver = { - .name = "es1_ap_driver", + .name = "es2_ap_driver", .probe = ap_probe, .disconnect = ap_disconnect, .id_table = id_table, -- cgit v1.2.3-59-g8ed1b From 9ade6d312287fd1729a02e349f5f23483ffc4e1c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 7 May 2015 13:00:20 -0500 Subject: greybus: battery: free struct on error in caller When a battery connection is initialized, a gb_battery structure for it is allocated in gb_battery_connection_init(). Currently that function ends by calling init_and_register(); in the event an error occurs, init_and_register() is responsible for freeing the allocated gb_battery structure. Make the code a bit better balanced by having the function that allocates the structure be responsible for freeing it in case of error. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index eb688c9e821a..c815338ba307 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -299,8 +299,6 @@ static enum power_supply_property battery_props[] = { static int init_and_register(struct gb_connection *connection, struct gb_battery *gb) { - int retval; - // FIXME - get a better (i.e. unique) name // FIXME - anything else needs to be set? gb->bat.name = "gb_battery"; @@ -309,18 +307,13 @@ static int init_and_register(struct gb_connection *connection, gb->bat.num_properties = ARRAY_SIZE(battery_props); gb->bat.get_property = get_property; - retval = power_supply_register(&connection->bundle->intf->dev, - &gb->bat); - if (retval) - kfree(gb); - return retval; + return power_supply_register(&connection->bundle->intf->dev, &gb->bat); } #else static int init_and_register(struct gb_connection *connection, struct gb_battery *gb) { struct power_supply_config cfg = {}; - int retval = 0; cfg.drv_data = gb; @@ -334,11 +327,10 @@ static int init_and_register(struct gb_connection *connection, gb->bat = power_supply_register(&connection->bundle->intf->dev, &gb->desc, &cfg); - if (IS_ERR(gb->bat)) { - retval = PTR_ERR(gb->bat); - kfree(gb); - } - return retval; + if (IS_ERR(gb->bat)) + return PTR_ERR(gb->bat); + + return 0; } #endif @@ -356,12 +348,14 @@ static int gb_battery_connection_init(struct gb_connection *connection) /* Check the version */ retval = get_version(gb); - if (retval) { + if (retval) + goto out; + retval = init_and_register(connection, gb); +out: + if (retval) kfree(gb); - return retval; - } - return init_and_register(connection, gb); + return retval; } static void gb_battery_connection_exit(struct gb_connection *connection) -- cgit v1.2.3-59-g8ed1b From 5c58640d8d66504c43c062e6e7d4ed0ade6bde9e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 7 May 2015 13:00:21 -0500 Subject: greybus: battery: use feature tag rather than kernel version Conditionally define a new symbol DRIVER_OWNS_PSY_STRUCT, which is set in "kernel_ver.h" based on on the kernel version. Use it to distinguish code used for kernels that differ in whether a power supply structure is owned by the driver, or by the power supply core. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery.c | 7 +++---- drivers/staging/greybus/kernel_ver.h | 7 ++++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index c815338ba307..aad174975258 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -7,7 +7,6 @@ * Released under the GPLv2 only. */ -#include #include #include #include @@ -20,7 +19,7 @@ struct gb_battery { * and new apis in the same driver for now, until this is merged * upstream, when all of these version checks can be removed. */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +#ifdef DRIVER_OWNS_PSY_STRUCT struct power_supply bat; #define to_gb_battery(x) container_of(x, struct gb_battery, bat) #else @@ -295,7 +294,7 @@ static enum power_supply_property battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +#ifdef DRIVER_OWNS_PSY_STRUCT static int init_and_register(struct gb_connection *connection, struct gb_battery *gb) { @@ -362,7 +361,7 @@ static void gb_battery_connection_exit(struct gb_connection *connection) { struct gb_battery *gb = connection->private; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +#ifdef DRIVER_OWNS_PSY_STRUCT power_supply_unregister(&gb->bat); #else power_supply_unregister(gb->bat); diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index e63d6cb58ce7..8b8e712df48e 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -15,6 +15,12 @@ #define __GREYBUS_KERNEL_VER_H #include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +/* Commit: 297d716 power_supply: Change ownership from driver to core */ +#define DRIVER_OWNS_PSY_STRUCT +#endif #ifndef __ATTR_WO #define __ATTR_WO(_name) { \ @@ -57,7 +63,6 @@ * 3.17, which they explicitly changed in the 3.17 kernel. Consistency is * overrated. */ -#include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) -- cgit v1.2.3-59-g8ed1b From 6d653370c0a67ff9b9b0d8249182d537859b528c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 7 May 2015 13:03:52 -0500 Subject: greybus: eliminate extra response flag definitions All protocols use the same value to distinguish between request and response message types. This is a requirement. Use GB_MESSAGE_TYPE_RESPONSE rather than GB_OPERATION_TYPE_RESPONSE for the name of the flag used to distiguish between request and response messages. Get rid of the redundant response flag definitions that are associated with specific protocols. Describe the symbolic values as "operation types" rather than "message types" where they are defined. The message type for a request is the same as the operation type; the message type for a response is the operation type OR'd with GB_MESSAGE_TYPE_RESPONSE. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpbridge.h | 5 +---- drivers/staging/greybus/hid.c | 3 +-- drivers/staging/greybus/operation.c | 8 ++++---- drivers/staging/greybus/operation.h | 2 +- drivers/staging/greybus/spi.c | 3 +-- drivers/staging/greybus/uart.c | 3 +-- drivers/staging/greybus/vibrator.c | 3 +-- 7 files changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index e2301888a322..85d801960564 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -65,7 +65,6 @@ #define GB_I2C_TYPE_TIMEOUT 0x03 #define GB_I2C_TYPE_RETRIES 0x04 #define GB_I2C_TYPE_TRANSFER 0x05 -#define GB_I2C_TYPE_RESPONSE 0x80 /* OR'd with rest */ #define GB_I2C_RETRIES_DEFAULT 3 #define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ @@ -133,7 +132,6 @@ struct gb_i2c_transfer_response { #define GB_GPIO_TYPE_IRQ_MASK 0x0d #define GB_GPIO_TYPE_IRQ_UNMASK 0x0e #define GB_GPIO_TYPE_IRQ_EVENT 0x0f -#define GB_GPIO_TYPE_RESPONSE 0x80 /* OR'd with rest */ #define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ @@ -223,7 +221,7 @@ struct gb_gpio_irq_event_request { #define GB_PWM_VERSION_MAJOR 0x00 #define GB_PWM_VERSION_MINOR 0x01 -/* Greybus PWM request types */ +/* Greybus PWM operation types */ #define GB_PWM_TYPE_INVALID 0x00 #define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 #define GB_PWM_TYPE_PWM_COUNT 0x02 @@ -233,7 +231,6 @@ struct gb_gpio_irq_event_request { #define GB_PWM_TYPE_POLARITY 0x06 #define GB_PWM_TYPE_ENABLE 0x07 #define GB_PWM_TYPE_DISABLE 0x08 -#define GB_PWM_TYPE_RESPONSE 0x80 /* OR'd with rest */ /* pwm count request has no payload */ struct gb_pwm_count_response { diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 556cf9b0fe93..00dc7e57f4a4 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -20,7 +20,7 @@ #define GB_HID_VERSION_MAJOR 0x00 #define GB_HID_VERSION_MINOR 0x01 -/* Greybus HID request types */ +/* Greybus HID operation types */ #define GB_HID_TYPE_INVALID 0x00 #define GB_HID_TYPE_PROTOCOL_VERSION 0x01 #define GB_HID_TYPE_GET_DESC 0x02 @@ -30,7 +30,6 @@ #define GB_HID_TYPE_GET_REPORT 0x06 #define GB_HID_TYPE_SET_REPORT 0x07 #define GB_HID_TYPE_IRQ_EVENT 0x08 -#define GB_HID_TYPE_RESPONSE 0x80 /* OR'd with rest */ /* Report type */ #define GB_HID_INPUT_REPORT 0 diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 3639e27e288a..1ec930c5cba9 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -396,7 +396,7 @@ bool gb_operation_response_alloc(struct gb_operation *operation, struct gb_message *response; u8 type; - type = operation->type | GB_OPERATION_TYPE_RESPONSE; + type = operation->type | GB_MESSAGE_TYPE_RESPONSE; response = gb_operation_message_alloc(hd, type, response_size, GFP_KERNEL); if (!response) @@ -508,8 +508,8 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, { if (WARN_ON_ONCE(type == GB_OPERATION_TYPE_INVALID)) return NULL; - if (WARN_ON_ONCE(type & GB_OPERATION_TYPE_RESPONSE)) - type &= ~GB_OPERATION_TYPE_RESPONSE; + if (WARN_ON_ONCE(type & GB_MESSAGE_TYPE_RESPONSE)) + type &= ~GB_MESSAGE_TYPE_RESPONSE; return gb_operation_create_common(connection, type, request_size, response_size); @@ -855,7 +855,7 @@ void gb_connection_recv(struct gb_connection *connection, } operation_id = le16_to_cpu(header.operation_id); - if (header.type & GB_OPERATION_TYPE_RESPONSE) + if (header.type & GB_MESSAGE_TYPE_RESPONSE) gb_connection_recv_response(connection, operation_id, header.result, data, msg_size); else diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 38684f2cdbaa..82b8fe57f8a0 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -24,7 +24,7 @@ struct gb_operation; * The top bit of the type in an operation message header indicates * whether the message is a request (bit clear) or response (bit set) */ -#define GB_OPERATION_TYPE_RESPONSE ((u8)0x80) +#define GB_MESSAGE_TYPE_RESPONSE ((u8)0x80) enum gb_operation_result { GB_OP_SUCCESS = 0x00, diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 639c9cdac516..731639a810b2 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -66,7 +66,7 @@ struct gb_spi { #define GB_SPI_FLAG_NO_RX BIT(1) /* can't do buffer read */ #define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */ -/* Greybus spi request types */ +/* Greybus spi operation types */ #define GB_SPI_TYPE_INVALID 0x00 #define GB_SPI_TYPE_PROTOCOL_VERSION 0x01 #define GB_SPI_TYPE_MODE 0x02 @@ -74,7 +74,6 @@ struct gb_spi { #define GB_SPI_TYPE_BITS_PER_WORD_MASK 0x04 #define GB_SPI_TYPE_NUM_CHIPSELECT 0x05 #define GB_SPI_TYPE_TRANSFER 0x06 -#define GB_SPI_TYPE_RESPONSE 0x80 /* OR'd with rest */ /* mode request has no payload */ struct gb_spi_mode_response { diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 51e4f7bad47f..35ab3cad5714 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -37,7 +37,7 @@ #define GB_UART_VERSION_MAJOR 0x00 #define GB_UART_VERSION_MINOR 0x01 -/* Greybus UART request types */ +/* Greybus UART operation types */ #define GB_UART_TYPE_INVALID 0x00 #define GB_UART_TYPE_PROTOCOL_VERSION 0x01 #define GB_UART_TYPE_SEND_DATA 0x02 @@ -46,7 +46,6 @@ #define GB_UART_TYPE_SET_CONTROL_LINE_STATE 0x05 #define GB_UART_TYPE_SET_BREAK 0x06 #define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ -#define GB_UART_TYPE_RESPONSE 0x80 /* OR'd with rest */ struct gb_uart_send_data_request { __le16 size; diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 2943a9b381c6..aefd2cd3f18e 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -27,12 +27,11 @@ struct gb_vibrator_device { #define GB_VIBRATOR_VERSION_MAJOR 0x00 #define GB_VIBRATOR_VERSION_MINOR 0x01 -/* Greybus Vibrator request types */ +/* Greybus Vibrator operation types */ #define GB_VIBRATOR_TYPE_INVALID 0x00 #define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01 #define GB_VIBRATOR_TYPE_ON 0x02 #define GB_VIBRATOR_TYPE_OFF 0x03 -#define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */ struct gb_vibrator_on_request { __le16 timeout_ms; -- cgit v1.2.3-59-g8ed1b From a4749bbeee31204a48fc3bf10c80bb88cf52bab5 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 8 May 2015 12:57:36 -0700 Subject: greybus: gb-audio: Add integrated greybus audio driver So here's the current greybus audio driver, which I wanted to send out for more serious review and consideration for merging. I've tried to integrate much of the feedback from the last round and most of the hotplug issues that I've found have been resolved. I've tested this via gbsim, and the Android ARA HAL layer seems to work with it. Mark has also successfully played audio with this driver, adding a few hacks to get the codec's i2c connection to probe. Current issues: * Hotplug problem - When gbsim is killed, or the module removed, the greybus driver gets stuck since the android mediaserver process is holding the audio device open. Killing the mediaserver allows things to clean up and allows greybus to accept new gbsim connections. I have a workaround patch to the soc-core.c logic which converts the snd_card_free() call to snd_card_free_when_closed() which allows the greybus connection cleanup to finish. Remaining todos: * Probably need to break apart the mgmt_setup function to integrate better with the constraint logic. I took a really basic stab at this, but more is probably needed. * Figure out how to properly find and tie in the codec's I2C bus-id to the driver. This code requires that the kernel support the following config options, which I've enabled in a separate kernel patch: CONFIG_SND_SIMPLE_CARD CONFIG_SND_SOC_SPDIF CONFIG_SND_SOC_RT5645 I really can't calim to be the sole author of this, since many many fixes and tweaks that have been folded in have come from Mark Greer. His analsysis and debugging is really what has made this dummy-framework driver evolve into an actual audio driver. So much credit and thanks to Mark! Signed-off-by: John Stultz Signed-off-by: Mark A. Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 8 +- drivers/staging/greybus/audio-dai.c | 103 ++++++++ drivers/staging/greybus/audio-gb-cmds.c | 207 +++++++++++++++ drivers/staging/greybus/audio-pcm.c | 262 +++++++++++++++++++ drivers/staging/greybus/audio.c | 432 ++++++++++++++++++++++++++++++++ drivers/staging/greybus/audio.h | 100 ++++++++ drivers/staging/greybus/gpb.c | 8 + drivers/staging/greybus/gpbridge.h | 2 +- drivers/staging/greybus/protocol.h | 3 + 9 files changed, 1122 insertions(+), 3 deletions(-) create mode 100644 drivers/staging/greybus/audio-dai.c create mode 100644 drivers/staging/greybus/audio-gb-cmds.c create mode 100644 drivers/staging/greybus/audio-pcm.c create mode 100644 drivers/staging/greybus/audio.c create mode 100644 drivers/staging/greybus/audio.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 9945cb804d10..5158d87298d2 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -10,7 +10,7 @@ greybus-y := core.o \ protocol.o \ operation.o -gb-phy-y := gpb.o \ +gb-phy-y := gpb.o \ sdio.o \ uart.o \ pwm.o \ @@ -18,7 +18,11 @@ gb-phy-y := gpb.o \ hid.o \ i2c.o \ spi.o \ - usb.o + usb.o \ + audio.o \ + audio-pcm.o \ + audio-dai.o \ + audio-gb-cmds.o # Prefix all modules with gb- gb-vibrator-y := vibrator.o diff --git a/drivers/staging/greybus/audio-dai.c b/drivers/staging/greybus/audio-dai.c new file mode 100644 index 000000000000..f2c8ca0b4df6 --- /dev/null +++ b/drivers/staging/greybus/audio-dai.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "greybus.h" +#include "gpbridge.h" +#include "audio.h" + +/* + * This is the greybus cpu dai logic. It really doesn't do much + * other then provide the TRIGGER_START/STOP hooks that start + * and stop the timer sending audio data in the pcm logic. + */ + + +static int gb_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct gb_snd *snd_dev; + + + snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + gb_pcm_hrtimer_start(snd_dev); + break; + case SNDRV_PCM_TRIGGER_STOP: + gb_pcm_hrtimer_stop(snd_dev); + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * XXX This is annoying, if we don't have a set_fmt function + * the subsystem returns -ENOTSUPP, which causes applications + * to fail, so add a dummy function here. + */ +static int gb_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + return 0; +} + +static const struct snd_soc_dai_ops gb_dai_ops = { + .trigger = gb_dai_trigger, + .set_fmt = gb_dai_set_fmt, +}; + +struct snd_soc_dai_driver gb_cpu_dai = { + .name = "gb-cpu-dai", + .playback = { + .rates = GB_RATES, + .formats = GB_FMTS, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &gb_dai_ops, +}; + +static const struct snd_soc_component_driver gb_soc_component = { + .name = "gb-component", +}; + +static int gb_plat_probe(struct platform_device *pdev) +{ + struct gb_snd *snd_dev; + int ret; + + snd_dev = (struct gb_snd *)pdev->dev.platform_data; + dev_set_drvdata(&pdev->dev, snd_dev); + + ret = snd_soc_register_component(&pdev->dev, &gb_soc_component, + &gb_cpu_dai, 1); + return ret; +} + +static int gb_plat_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +struct platform_driver gb_audio_plat_driver = { + .driver = { + .name = "gb-dai-audio", + }, + .probe = gb_plat_probe, + .remove = gb_plat_remove, +}; diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c new file mode 100644 index 000000000000..ef3191670485 --- /dev/null +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -0,0 +1,207 @@ +#include +#include "greybus.h" +#include "gpbridge.h" +#include "audio.h" + +#define GB_I2S_MGMT_VERSION_MAJOR 0x00 +#define GB_I2S_MGMT_VERSION_MINOR 0x01 + +#define GB_I2S_DATA_VERSION_MAJOR 0x00 +#define GB_I2S_MGMT_VERSION_MINOR 0x01 + +/*********************************** + * GB I2S helper functions + ***********************************/ +int gb_i2s_mgmt_get_version(struct gb_connection *connection) +{ + struct gb_protocol_version_response response; + + memset(&response, 0, sizeof(response)); + return gb_protocol_get_version(connection, + GB_I2S_MGMT_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, + GB_I2S_MGMT_VERSION_MAJOR); +} + +int gb_i2s_data_get_version(struct gb_connection *connection) +{ + struct gb_protocol_version_response response; + + memset(&response, 0, sizeof(response)); + return gb_protocol_get_version(connection, + GB_I2S_DATA_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, + GB_I2S_DATA_VERSION_MAJOR); +} + +int gb_i2s_mgmt_activate_cport(struct gb_connection *connection, + uint16_t cport) +{ + struct gb_i2s_mgmt_activate_cport_request request; + + memset(&request, 0, sizeof(request)); + request.cport = cport; + + return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_ACTIVATE_CPORT, + &request, sizeof(request), NULL, 0); +} + +int gb_i2s_mgmt_deactivate_cport(struct gb_connection *connection, + uint16_t cport) +{ + struct gb_i2s_mgmt_deactivate_cport_request request; + + memset(&request, 0, sizeof(request)); + request.cport = cport; + + return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT, + &request, sizeof(request), NULL, 0); +} + +int gb_i2s_mgmt_get_supported_configurations( + struct gb_connection *connection, + struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg, + size_t size) +{ + return gb_operation_sync(connection, + GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS, + NULL, 0, get_cfg, size); +} + +int gb_i2s_mgmt_set_configuration(struct gb_connection *connection, + struct gb_i2s_mgmt_set_configuration_request *set_cfg) +{ + return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_SET_CONFIGURATION, + set_cfg, sizeof(*set_cfg), NULL, 0); +} + +int gb_i2s_mgmt_set_samples_per_message( + struct gb_connection *connection, + uint16_t samples_per_message) +{ + struct gb_i2s_mgmt_set_samples_per_message_request request; + + memset(&request, 0, sizeof(request)); + request.samples_per_message = samples_per_message; + + return gb_operation_sync(connection, + GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE, + &request, sizeof(request), NULL, 0); +} + +/* + * XXX This is sort of a generic "setup" function which probably needs + * to be broken up, and tied into the constraints. + * + * I'm on the fence if we should just dictate that we only support + * 48k, 16bit, 2 channel, and avoid doign the whole probe for configurations + * and then picking one. + */ +int gb_i2s_mgmt_setup(struct gb_connection *connection) +{ + struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg; + struct gb_i2s_mgmt_set_configuration_request set_cfg; + struct gb_i2s_mgmt_configuration *cfg; + size_t size; + int i, ret; + + size = sizeof(*get_cfg) + + (CONFIG_COUNT_MAX * sizeof(get_cfg->config[0])); + + get_cfg = kzalloc(size, GFP_KERNEL); + if (!get_cfg) + return -ENOMEM; + + ret = gb_i2s_mgmt_get_supported_configurations(connection, get_cfg, + size); + if (ret) { + pr_err("get_supported_config failed: %d\n", ret); + goto free_get_cfg; + } + + /* Pick 48KHz 16-bits/channel */ + for (i = 0, cfg = get_cfg->config; i < CONFIG_COUNT_MAX; i++, cfg++) { + if ((cfg->sample_frequency == GB_SAMPLE_RATE) && + (cfg->num_channels == 2) && + (cfg->bytes_per_channel == 2) && + (cfg->byte_order & GB_I2S_MGMT_BYTE_ORDER_LE) && + (cfg->spatial_locations == + (GB_I2S_MGMT_SPATIAL_LOCATION_FL | + GB_I2S_MGMT_SPATIAL_LOCATION_FR)) && + (cfg->ll_protocol & GB_I2S_MGMT_PROTOCOL_I2S) && + (cfg->ll_mclk_role & GB_I2S_MGMT_ROLE_MASTER) && + (cfg->ll_bclk_role & GB_I2S_MGMT_ROLE_MASTER) && + (cfg->ll_wclk_role & GB_I2S_MGMT_ROLE_MASTER) && + (cfg->ll_wclk_polarity & GB_I2S_MGMT_POLARITY_NORMAL) && + (cfg->ll_wclk_change_edge & GB_I2S_MGMT_EDGE_FALLING) && + (cfg->ll_wclk_tx_edge & GB_I2S_MGMT_EDGE_FALLING) && + (cfg->ll_wclk_rx_edge & GB_I2S_MGMT_EDGE_RISING) && + (cfg->ll_data_offset == 1)) + break; + } + + if (i >= CONFIG_COUNT_MAX) { + pr_err("No valid configuration\n"); + ret = -EINVAL; + goto free_get_cfg; + } + + memcpy(&set_cfg, cfg, sizeof(set_cfg)); + set_cfg.config.byte_order = GB_I2S_MGMT_BYTE_ORDER_LE; + set_cfg.config.ll_protocol = GB_I2S_MGMT_PROTOCOL_I2S; + set_cfg.config.ll_mclk_role = GB_I2S_MGMT_ROLE_MASTER; + set_cfg.config.ll_bclk_role = GB_I2S_MGMT_ROLE_MASTER; + set_cfg.config.ll_wclk_role = GB_I2S_MGMT_ROLE_MASTER; + set_cfg.config.ll_wclk_polarity = GB_I2S_MGMT_POLARITY_NORMAL; + set_cfg.config.ll_wclk_change_edge = GB_I2S_MGMT_EDGE_RISING; + set_cfg.config.ll_wclk_tx_edge = GB_I2S_MGMT_EDGE_FALLING; + set_cfg.config.ll_wclk_rx_edge = GB_I2S_MGMT_EDGE_RISING; + + ret = gb_i2s_mgmt_set_configuration(connection, &set_cfg); + if (ret) { + pr_err("set_configuration failed: %d\n", ret); + goto free_get_cfg; + } + + ret = gb_i2s_mgmt_set_samples_per_message(connection, + CONFIG_SAMPLES_PER_MSG); + if (ret) { + pr_err("set_samples_per_msg failed: %d\n", ret); + goto free_get_cfg; + } + + /* XXX Add start delay here (probably 1ms) */ + ret = gb_i2s_mgmt_activate_cport(connection, + CONFIG_I2S_REMOTE_DATA_CPORT); + if (ret) { + pr_err("activate_cport failed: %d\n", ret); + goto free_get_cfg; + } + +free_get_cfg: + kfree(get_cfg); + return ret; +} + +int gb_i2s_send_data(struct gb_connection *connection, + void *req_buf, void *source_addr, + size_t len, int sample_num) +{ + struct gb_i2s_send_data_request *gb_req; + int ret; + + gb_req = req_buf; + gb_req->sample_number = sample_num; + + memcpy((void *)&gb_req->data[0], source_addr, len); + + if (len < MAX_SEND_DATA_LEN) + for (; len < MAX_SEND_DATA_LEN; len++) + gb_req->data[len] = gb_req->data[len - SAMPLE_SIZE]; + + gb_req->size = len; + + ret = gb_operation_sync(connection, GB_I2S_DATA_TYPE_SEND_DATA, + (void *) gb_req, SEND_DATA_BUF_LEN, NULL, 0); + return ret; +} diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c new file mode 100644 index 000000000000..28b5e1106855 --- /dev/null +++ b/drivers/staging/greybus/audio-pcm.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "greybus.h" +#include "gpbridge.h" +#include "audio.h" + +/* + * timer/workqueue logic for pushing pcm data. + * + * Since when we are playing audio, we don't get any + * status or feedback from the codec, we have to use a + * hrtimer to trigger sending data to the remote codec. + * However since the hrtimer runs in irq context, so we + * have to schedule a workqueue to actually send the + * greybus data. + */ + +static void gb_pcm_work(struct work_struct *work) +{ + struct gb_snd *snd_dev = container_of(work, struct gb_snd, work); + struct snd_pcm_substream *substream = snd_dev->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int stride, frames, oldptr; + int period_elapsed; + char *address; + long len; + + if (!snd_dev) + return; + + if (!atomic_read(&snd_dev->running)) + return; + + address = runtime->dma_area + snd_dev->hwptr_done; + + len = frames_to_bytes(runtime, + runtime->buffer_size) - snd_dev->hwptr_done; + len = min(len, MAX_SEND_DATA_LEN); + gb_i2s_send_data(snd_dev->i2s_tx_connection, snd_dev->send_data_req_buf, + address, len, snd_dev->send_data_sample_count); + + snd_dev->send_data_sample_count += CONFIG_SAMPLES_PER_MSG; + + stride = runtime->frame_bits >> 3; + frames = len/stride; + + snd_pcm_stream_lock(substream); + oldptr = snd_dev->hwptr_done; + snd_dev->hwptr_done += len; + if (snd_dev->hwptr_done >= runtime->buffer_size * stride) + snd_dev->hwptr_done -= runtime->buffer_size * stride; + + frames = (len + (oldptr % stride)) / stride; + + snd_dev->transfer_done += frames; + if (snd_dev->transfer_done >= runtime->period_size) { + snd_dev->transfer_done -= runtime->period_size; + period_elapsed = 1; + } + + snd_pcm_stream_unlock(substream); + if (period_elapsed) + snd_pcm_period_elapsed(snd_dev->substream); +} + +static enum hrtimer_restart gb_pcm_timer_function(struct hrtimer *hrtimer) +{ + struct gb_snd *snd_dev = container_of(hrtimer, struct gb_snd, timer); + + if (!atomic_read(&snd_dev->running)) + return HRTIMER_NORESTART; + queue_work(snd_dev->workqueue, &snd_dev->work); + hrtimer_forward_now(hrtimer, ns_to_ktime(CONFIG_PERIOD_NS)); + return HRTIMER_RESTART; +} + +void gb_pcm_hrtimer_start(struct gb_snd *snd_dev) +{ + atomic_set(&snd_dev->running, 1); + hrtimer_start(&snd_dev->timer, ns_to_ktime(CONFIG_PERIOD_NS), + HRTIMER_MODE_REL); +} + +void gb_pcm_hrtimer_stop(struct gb_snd *snd_dev) +{ + atomic_set(&snd_dev->running, 0); + hrtimer_cancel(&snd_dev->timer); +} + +static int gb_pcm_hrtimer_init(struct gb_snd *snd_dev) +{ + hrtimer_init(&snd_dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + snd_dev->timer.function = gb_pcm_timer_function; + atomic_set(&snd_dev->running, 0); + snd_dev->workqueue = alloc_workqueue("gb-audio", WQ_HIGHPRI, 0); + if (!snd_dev->workqueue) + return -ENOMEM; + INIT_WORK(&snd_dev->work, gb_pcm_work); + return 0; +} + + +/* + * Core gb pcm structure + */ +static struct snd_pcm_hardware gb_plat_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = GB_FMTS, + .rates = GB_RATES, + .rate_min = 8000, + .rate_max = GB_SAMPLE_RATE, + .channels_min = 1, + .channels_max = 2, + /* XXX - All the values below are junk */ + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 2, + .periods_max = 32, +}; + +static snd_pcm_uframes_t gb_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct gb_snd *snd_dev; + + snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); + + return snd_dev->hwptr_done / (substream->runtime->frame_bits >> 3); +} + +static int gb_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct gb_snd *snd_dev; + + snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); + snd_dev->hwptr_done = 0; + snd_dev->transfer_done = 0; + return 0; +} + +static unsigned int rates[] = {GB_SAMPLE_RATE}; +static struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static int gb_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct gb_snd *snd_dev; + unsigned long flags; + int ret; + + snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); + + spin_lock_irqsave(&snd_dev->lock, flags); + runtime->private_data = snd_dev; + snd_dev->substream = substream; + ret = gb_pcm_hrtimer_init(snd_dev); + spin_unlock_irqrestore(&snd_dev->lock, flags); + + if (ret) + return ret; + + snd_soc_set_runtime_hwparams(substream, &gb_plat_pcm_hardware); + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + if (ret < 0) + return ret; + + return snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +} + +static int gb_pcm_close(struct snd_pcm_substream *substream) +{ + substream->runtime->private_data = NULL; + return 0; +} + +static int gb_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int gb_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static struct snd_pcm_ops gb_pcm_ops = { + .open = gb_pcm_open, + .close = gb_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = gb_pcm_hw_params, + .hw_free = gb_pcm_hw_free, + .prepare = gb_pcm_prepare, + .pointer = gb_pcm_pointer, +}; + +static void gb_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int gb_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + + return snd_pcm_lib_preallocate_pages_for_all( + pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); +} + +struct snd_soc_platform_driver gb_soc_platform = { + .ops = &gb_pcm_ops, + .pcm_new = gb_pcm_new, + .pcm_free = gb_pcm_free, +}; + +static int gb_soc_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_platform(&pdev->dev, &gb_soc_platform); +} + +static int gb_soc_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +struct platform_driver gb_audio_pcm_driver = { + .driver = { + .name = "gb-pcm-audio", + .owner = THIS_MODULE, + }, + .probe = gb_soc_platform_probe, + .remove = gb_soc_platform_remove, +}; diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c new file mode 100644 index 000000000000..717e00255ca6 --- /dev/null +++ b/drivers/staging/greybus/audio.c @@ -0,0 +1,432 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "greybus.h" +#include "gpbridge.h" +#include "audio.h" + + +#define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data" +#define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt" + +/* + * gb_snd management functions + */ +static DEFINE_SPINLOCK(gb_snd_list_lock); +static LIST_HEAD(gb_snd_list); +static int device_count; + +static struct gb_snd *gb_find_snd(int bundle_id) +{ + struct gb_snd *tmp, *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&gb_snd_list_lock, flags); + list_for_each_entry(tmp, &gb_snd_list, list) + if (tmp->gb_bundle_id == bundle_id) { + ret = tmp; + break; + } + spin_unlock_irqrestore(&gb_snd_list_lock, flags); + return ret; +} + +static struct gb_snd *gb_get_snd(int bundle_id) +{ + struct gb_snd *snd_dev; + unsigned long flags; + + snd_dev = gb_find_snd(bundle_id); + if (snd_dev) + return snd_dev; + + snd_dev = kzalloc(sizeof(*snd_dev), GFP_KERNEL); + if (!snd_dev) + return NULL; + + spin_lock_init(&snd_dev->lock); + snd_dev->device_count = device_count++; + snd_dev->gb_bundle_id = bundle_id; + spin_lock_irqsave(&gb_snd_list_lock, flags); + list_add(&snd_dev->list, &gb_snd_list); + spin_unlock_irqrestore(&gb_snd_list_lock, flags); + return snd_dev; +} + +static void gb_free_snd(struct gb_snd *snd) +{ + unsigned long flags; + + spin_lock_irqsave(&gb_snd_list_lock, flags); + if (!snd->i2s_tx_connection && + !snd->mgmt_connection) { + list_del(&snd->list); + spin_unlock_irqrestore(&gb_snd_list_lock, flags); + kfree(snd); + } else { + spin_unlock_irqrestore(&gb_snd_list_lock, flags); + } +} + + + + +/* + * This is the ASoC simple card binds the platform codec, + * cpu-dai and codec-dai togheter + */ +struct gb_card_info_object { + struct asoc_simple_card_info card_info; + char codec_name[255]; + char platform_name[255]; + char dai_name[255]; +}; + + +struct asoc_simple_card_info *setup_card_info(int device_count) +{ + struct gb_card_info_object *obj; + + obj = kzalloc(sizeof(struct gb_card_info_object), GFP_KERNEL); + if (!obj) + return NULL; + + obj->card_info.name = "Greybus Audio Module"; + obj->card_info.card = "gb-card"; + obj->card_info.codec = obj->codec_name; + obj->card_info.platform = obj->platform_name; + obj->card_info.cpu_dai.name = obj->dai_name; + obj->card_info.cpu_dai.fmt = GB_FMTS; +#if USE_RT5645 + obj->card_info.daifmt = GB_FMTS; + sprintf(obj->codec_name, "rt5645.%s", "6-001b"); /* XXX do i2c bus addr dynamically */ + obj->card_info.codec_dai.name = "rt5645-aif1"; + obj->card_info.codec_dai.fmt = SND_SOC_DAIFMT_CBM_CFM; + obj->card_info.codec_dai.sysclk = 12288000; +#else + sprintf(obj->codec_name, "spdif-dit"); + obj->card_info.codec_dai.name = "dit-hifi"; +#endif + sprintf(obj->platform_name, "gb-pcm-audio.%i", device_count); + sprintf(obj->dai_name, "gb-dai-audio.%i", device_count); + + return &obj->card_info; +} + +void free_card_info(struct asoc_simple_card_info *ci) +{ + struct gb_card_info_object *obj; + + obj = container_of(ci, struct gb_card_info_object, card_info); + kfree(obj); +} + + +/* + * XXX this is sort of cruddy but I get warnings if + * we don't have dev.release handler set. + */ +static void default_release(struct device *dev) +{ +} + +/* + * GB connection hooks + */ +static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) +{ + struct gb_snd *snd_dev; + struct platform_device *codec, *dai; + struct asoc_simple_card_info *simple_card; + unsigned long flags; + int ret; + + snd_dev = gb_get_snd(connection->bundle->id); + if (!snd_dev) + return -ENOMEM; + + codec = platform_device_register_simple("spdif-dit", -1, NULL, 0); + if (!codec) { + ret = -ENOMEM; + goto out; + } + + dai = platform_device_register_simple("gb-pcm-audio", snd_dev->device_count, NULL, 0); + if (!dai) { + ret = -ENOMEM; + goto out; + } + + simple_card = setup_card_info(snd_dev->device_count); + if (!simple_card) { + ret = -ENOMEM; + goto out; + } + + spin_lock_irqsave(&snd_dev->lock, flags); + snd_dev->card.name = "asoc-simple-card"; + snd_dev->card.id = snd_dev->device_count; + snd_dev->card.dev.release = default_release; /* XXX - suspicious */ + + snd_dev->cpu_dai.name = "gb-dai-audio"; + snd_dev->cpu_dai.id = snd_dev->device_count; + snd_dev->cpu_dai.dev.release = default_release; /* XXX - suspicious */ + + + snd_dev->simple_card_info = simple_card; + snd_dev->card.dev.platform_data = simple_card; + + snd_dev->codec = codec; + snd_dev->i2s_tx_connection = connection; + snd_dev->cpu_dai.dev.platform_data = snd_dev; + snd_dev->i2s_tx_connection->private = snd_dev; + spin_unlock_irqrestore(&snd_dev->lock, flags); + + ret = platform_device_register(&snd_dev->cpu_dai); + if (ret) { + pr_err("cpu_dai platform_device register failed\n"); + goto out_dai; + } + + ret = platform_device_register(&snd_dev->card); + if (ret) { + pr_err("card platform_device register failed\n"); + goto out_card; + } + + ret = gb_i2s_data_get_version(connection); + if (ret) { + pr_err("i2s data get_version() failed: %d\n", ret); + goto out_get_ver; + } + + return 0; + +out_get_ver: + platform_device_unregister(&snd_dev->card); +out_card: + platform_device_unregister(&snd_dev->cpu_dai); +out_dai: + platform_device_unregister(codec); +out: + gb_free_snd(snd_dev); + return ret; +} + +static void gb_i2s_transmitter_connection_exit(struct gb_connection *connection) +{ + struct gb_snd *snd_dev; + + snd_dev = (struct gb_snd *)connection->private; + + platform_device_unregister(&snd_dev->card); + platform_device_unregister(&snd_dev->cpu_dai); + platform_device_unregister(snd_dev->codec); + + free_card_info(snd_dev->simple_card_info); + snd_dev->i2s_tx_connection = NULL; + gb_free_snd(snd_dev); +} + +static int gb_i2s_mgmt_connection_init(struct gb_connection *connection) +{ + struct gb_snd *snd_dev; + unsigned long flags; + int ret; + + snd_dev = gb_get_snd(connection->bundle->id); + if (!snd_dev) + return -ENOMEM; + + spin_lock_irqsave(&snd_dev->lock, flags); + snd_dev->mgmt_connection = connection; + connection->private = snd_dev; + spin_unlock_irqrestore(&snd_dev->lock, flags); + + ret = gb_i2s_mgmt_get_version(connection); + if (ret) { + pr_err("i2s mgmt get_version() failed: %d\n", ret); + goto err_free_snd_dev; + } + + gb_i2s_mgmt_setup(connection); + + snd_dev->send_data_req_buf = kzalloc(SEND_DATA_BUF_LEN, GFP_KERNEL); + + if (!snd_dev->send_data_req_buf) { + ret = -ENOMEM; + goto err_deactivate_cport; + } + + return 0; + +err_deactivate_cport: + gb_i2s_mgmt_deactivate_cport(connection, CONFIG_I2S_REMOTE_DATA_CPORT); +err_free_snd_dev: + gb_free_snd(snd_dev); + return ret; +} + +static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection) +{ + struct gb_snd *snd_dev = (struct gb_snd *)connection->private; + int ret; + + ret = gb_i2s_mgmt_deactivate_cport(connection, + CONFIG_I2S_REMOTE_DATA_CPORT); + if (ret) + pr_err("deactivate_cport failed: %d\n", ret); + + kfree(snd_dev->send_data_req_buf); + snd_dev->send_data_req_buf = NULL; + + snd_dev->mgmt_connection = NULL; + gb_free_snd(snd_dev); +} + +static int gb_i2s_mgmt_report_event_recv(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_i2s_mgmt_report_event_request *req = op->request->payload; + char *event_name; + + if (type != GB_I2S_MGMT_TYPE_REPORT_EVENT) { + dev_err(&connection->dev, "Invalid request type: %d\n", + type); + return -EINVAL; + } + + if (op->request->payload_size < sizeof(*req)) { + dev_err(&connection->dev, "Short request received: %d, %d\n", + op->request->payload_size, sizeof(*req)); + return -EINVAL; + } + + switch (req->event) { + case GB_I2S_MGMT_EVENT_UNSPECIFIED: + event_name = "UNSPECIFIED"; + break; + case GB_I2S_MGMT_EVENT_HALT: + /* XXX Should stop streaming now */ + event_name = "HALT"; + break; + case GB_I2S_MGMT_EVENT_INTERNAL_ERROR: + event_name = "INTERNAL_ERROR"; + break; + case GB_I2S_MGMT_EVENT_PROTOCOL_ERROR: + event_name = "PROTOCOL_ERROR"; + break; + case GB_I2S_MGMT_EVENT_FAILURE: + event_name = "FAILURE"; + break; + case GB_I2S_MGMT_EVENT_OUT_OF_SEQUENCE: + event_name = "OUT_OF_SEQUENCE"; + break; + case GB_I2S_MGMT_EVENT_UNDERRUN: + event_name = "UNDERRUN"; + break; + case GB_I2S_MGMT_EVENT_OVERRUN: + event_name = "OVERRUN"; + break; + case GB_I2S_MGMT_EVENT_CLOCKING: + event_name = "CLOCKING"; + break; + case GB_I2S_MGMT_EVENT_DATA_LEN: + event_name = "DATA_LEN"; + break; + default: + dev_warn(&connection->dev, "Unknown I2S Event received: %d\n", + req->event); + return -EINVAL; + } + + dev_warn(&connection->dev, "I2S Event received: %d - '%s'\n", + req->event, event_name); + + return 0; +} + +static struct gb_protocol gb_i2s_receiver_protocol = { + .name = GB_AUDIO_DATA_DRIVER_NAME, + .id = GREYBUS_PROTOCOL_I2S_RECEIVER, + .major = 0, + .minor = 1, + .connection_init = gb_i2s_transmitter_connection_init, + .connection_exit = gb_i2s_transmitter_connection_exit, + .request_recv = NULL, +}; + +static struct gb_protocol gb_i2s_mgmt_protocol = { + .name = GB_AUDIO_MGMT_DRIVER_NAME, + .id = GREYBUS_PROTOCOL_I2S_MGMT, + .major = 0, + .minor = 1, + .connection_init = gb_i2s_mgmt_connection_init, + .connection_exit = gb_i2s_mgmt_connection_exit, + .request_recv = gb_i2s_mgmt_report_event_recv, +}; + + +/* + * This is the basic hook get things initialized and registered w/ gb + */ + +int gb_audio_protocol_init(void) +{ + int err; + + err = gb_protocol_register(&gb_i2s_mgmt_protocol); + if (err) { + pr_err("Can't register i2s mgmt protocol driver: %d\n", -err); + return err; + } + + err = gb_protocol_register(&gb_i2s_receiver_protocol); + if (err) { + pr_err("Can't register Audio protocol driver: %d\n", -err); + goto err_unregister_i2s_mgmt; + } + + err = platform_driver_register(&gb_audio_plat_driver); + if (err) { + pr_err("Can't register platform driver: %d\n", -err); + goto err_unregister_plat; + } + + err = platform_driver_register(&gb_audio_pcm_driver); + if (err) { + pr_err("Can't register pcm driver: %d\n", -err); + goto err_unregister_pcm; + } + + return 0; + +err_unregister_pcm: + platform_driver_unregister(&gb_audio_plat_driver); +err_unregister_plat: + gb_protocol_deregister(&gb_i2s_receiver_protocol); +err_unregister_i2s_mgmt: + gb_protocol_deregister(&gb_i2s_mgmt_protocol); + return err; +} + +void gb_audio_protocol_exit(void) +{ + platform_driver_unregister(&gb_audio_pcm_driver); + platform_driver_unregister(&gb_audio_plat_driver); + gb_protocol_deregister(&gb_i2s_receiver_protocol); + gb_protocol_deregister(&gb_i2s_mgmt_protocol); +} + + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h new file mode 100644 index 000000000000..6a337d112976 --- /dev/null +++ b/drivers/staging/greybus/audio.h @@ -0,0 +1,100 @@ +#ifndef __GB_AUDIO_H +#define __GB_AUDIO_H +#include +#include +#include +#include +#include +#include +#include "greybus.h" +#include "gpbridge.h" + + +#define GB_SAMPLE_RATE 48000 +#define GB_RATES SNDRV_PCM_RATE_48000 +#define GB_FMTS SNDRV_PCM_FMTBIT_S16_LE +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (32 * 1024) + +/* assuming 1 ms samples @ 48KHz */ +#define CONFIG_SAMPLES_PER_MSG 48L +#define CONFIG_PERIOD_NS 1000000 /* send msg every 1ms */ + +#define CONFIG_COUNT_MAX 32 +#define CONFIG_I2S_REMOTE_DATA_CPORT 7 /* XXX shouldn't be hardcoded...*/ +#define RT5647_SLAVE_ADDR 0x1b /* from toshiba/quanta code */ + +/* Switch between dummy spdif and jetson rt5645 codec */ +#define USE_RT5645 0 + +#define SAMPLE_SIZE 4 +#define MAX_SEND_DATA_LEN (CONFIG_SAMPLES_PER_MSG * SAMPLE_SIZE) +#define SEND_DATA_BUF_LEN (sizeof(struct gb_i2s_send_data_request) + \ + MAX_SEND_DATA_LEN) + + +/* + * This is the gb_snd structure which ties everything together + * and fakes DMA interrupts via a timer. + */ +struct gb_snd { + struct platform_device card; + struct platform_device cpu_dai; + struct platform_device *codec; + struct asoc_simple_card_info *simple_card_info; + struct gb_connection *mgmt_connection; + struct gb_connection *i2s_tx_connection; + struct gb_connection *i2s_rx_connection; + char *send_data_req_buf; + long send_data_sample_count; + int gb_bundle_id; + int device_count; + struct snd_pcm_substream *substream; + struct hrtimer timer; + atomic_t running; + struct workqueue_struct *workqueue; + struct work_struct work; + int hwptr_done; + int transfer_done; + struct list_head list; + spinlock_t lock; +}; + + +/* + * GB I2S cmd functions + */ +int gb_i2s_mgmt_get_version(struct gb_connection *connection); +int gb_i2s_data_get_version(struct gb_connection *connection); +int gb_i2s_mgmt_activate_cport(struct gb_connection *connection, + uint16_t cport); +int gb_i2s_mgmt_deactivate_cport(struct gb_connection *connection, + uint16_t cport); +int gb_i2s_mgmt_get_supported_configurations( + struct gb_connection *connection, + struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg, + size_t size); +int gb_i2s_mgmt_set_configuration(struct gb_connection *connection, + struct gb_i2s_mgmt_set_configuration_request *set_cfg); +int gb_i2s_mgmt_set_samples_per_message(struct gb_connection *connection, + uint16_t samples_per_message); +int gb_i2s_mgmt_setup(struct gb_connection *connection); +int gb_i2s_send_data(struct gb_connection *connection, void *req_buf, + void *source_addr, size_t len, int sample_num); + + +/* + * GB PCM hooks + */ +void gb_pcm_hrtimer_start(struct gb_snd *snd_dev); +void gb_pcm_hrtimer_stop(struct gb_snd *snd_dev); + + +/* + * Platform drivers + */ +extern struct platform_driver gb_audio_pcm_driver; +extern struct platform_driver gb_audio_plat_driver; + + +#endif /* __GB_AUDIO_H */ diff --git a/drivers/staging/greybus/gpb.c b/drivers/staging/greybus/gpb.c index d5747b291171..2324270e5c55 100644 --- a/drivers/staging/greybus/gpb.c +++ b/drivers/staging/greybus/gpb.c @@ -53,8 +53,15 @@ static int __init gpbridge_init(void) pr_err("error initializing hid protocol\n"); goto error_hid; } + if (gb_audio_protocol_init()) { + pr_err("error initializing audio protocols\n"); + goto error_audio; + } + return 0; +error_audio: + gb_hid_protocol_exit(); error_hid: gb_spi_protocol_exit(); error_spi: @@ -76,6 +83,7 @@ module_init(gpbridge_init); static void __exit gpbridge_exit(void) { + gb_audio_protocol_exit(); gb_hid_protocol_exit(); gb_spi_protocol_exit(); gb_i2c_protocol_exit(); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 85d801960564..85cc38522967 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -341,6 +341,7 @@ struct gb_i2s_mgmt_configuration { __u8 pad; __le32 spatial_locations; __le32 ll_protocol; + __u8 ll_mclk_role; __u8 ll_bclk_role; __u8 ll_wclk_role; __u8 ll_wclk_polarity; @@ -348,7 +349,6 @@ struct gb_i2s_mgmt_configuration { __u8 ll_wclk_tx_edge; __u8 ll_wclk_rx_edge; __u8 ll_data_offset; - __u8 ll_pad; }; /* get supported configurations request has no payload */ diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 495d13aed365..7958802f2629 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -87,6 +87,9 @@ extern void gb_spi_protocol_exit(void); extern int gb_hid_protocol_init(void); extern void gb_hid_protocol_exit(void); +extern int gb_audio_protocol_init(void); +extern void gb_audio_protocol_exit(void); + #define gb_protocol_driver(__protocol) \ static int __init protocol_init(void) \ { \ -- cgit v1.2.3-59-g8ed1b From 68fff9604b8bce5cd023585ff41710948e61076e Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 7 May 2015 17:00:50 -0700 Subject: greybus: Support building greybus on host PC Introduce INSTALL_MOD_PATH to allow for building and installing of the greybus modules from a different location. This lets you build the greybus modules on a PC and then install the modules to an SD card in the appropriate location relative to the SD such as /media/sdcard/lib/modules/version and subsequent running of depmod in the same location. If INSTALL_MOD_PATH isn't specified the default behaviour of installing and depmoding to /lib/modules/version is maintained. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 5158d87298d2..f366da6fba12 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -41,6 +41,7 @@ obj-m += gb-es2.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build +INSTALL_MOD_PATH ?= /.. PWD := $(shell pwd) # add -Wall to try to catch everything we can. @@ -63,6 +64,6 @@ coccicheck: $(MAKE) -C $(KERNELDIR) M=$(PWD) coccicheck install: module - mkdir -p /lib/modules/$(KERNELVER)/kernel/drivers/greybus/ - cp -f *.ko /lib/modules/$(KERNELVER)/kernel/drivers/greybus/ - depmod -a $(KERNELVER) + mkdir -p $(INSTALL_MOD_PATH)/lib/modules/$(KERNELVER)/kernel/drivers/greybus/ + cp -f *.ko $(INSTALL_MOD_PATH)/lib/modules/$(KERNELVER)/kernel/drivers/greybus/ + depmod -b $(INSTALL_MOD_PATH) -a $(KERNELVER) -- cgit v1.2.3-59-g8ed1b From 7f3f356e455dd4438fdad2d45330f0d27bd087b6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 8 May 2015 22:33:36 +0200 Subject: greybus: gb-audio: fix build warning sizeof wants %zu when on a 64bit build, so change the dev_err() call to remove a build warning in the audio.c file. Signed-off-by: Greg Kroah-Hartman Acked-by: John Stultz --- drivers/staging/greybus/audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 717e00255ca6..29f5d4bc2fd2 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -307,7 +307,7 @@ static int gb_i2s_mgmt_report_event_recv(u8 type, struct gb_operation *op) } if (op->request->payload_size < sizeof(*req)) { - dev_err(&connection->dev, "Short request received: %d, %d\n", + dev_err(&connection->dev, "Short request received: %zu, %zu\n", op->request->payload_size, sizeof(*req)); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 61c0926eec4153e51f047fbf91b897abeca4cccb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 8 May 2015 22:40:47 +0200 Subject: greybus: gb-audio: fix build breakage on 4.1-rc1 In commit 1efb53a220b7 ("ASoC: simple-card: Remove support for setting differing DAI formats"), the .fmt field was removed from struct asoc_simple_dai. Fix this build breakage by not trying to set it. Signed-off-by: Greg Kroah-Hartman Acked-by: John Stultz --- drivers/staging/greybus/audio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 29f5d4bc2fd2..1ef09035706f 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -106,7 +106,9 @@ struct asoc_simple_card_info *setup_card_info(int device_count) obj->card_info.codec = obj->codec_name; obj->card_info.platform = obj->platform_name; obj->card_info.cpu_dai.name = obj->dai_name; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) obj->card_info.cpu_dai.fmt = GB_FMTS; +#endif #if USE_RT5645 obj->card_info.daifmt = GB_FMTS; sprintf(obj->codec_name, "rt5645.%s", "6-001b"); /* XXX do i2c bus addr dynamically */ -- cgit v1.2.3-59-g8ed1b From 4f4cc1bf070e05d4ee54e569e7ec87168fa8e284 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 8 May 2015 12:58:50 +0530 Subject: greybus: endo: Add code to validate Endo ID Endo is described by a 16 bit value. Which represents the properties of modules, interface and ribs on front and back of endo. This 16 bit value can be used to find all possible pairs of modules and interfaces and creating modules based on that. This patch provides helpers to parse 16 bit Endo ID. (Based on original code written by Alex Elder.) Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 294 +++++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/endo.h | 23 ++++ 2 files changed, 317 insertions(+) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index b9c7ee62456a..e47ec6963fa1 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -9,6 +9,26 @@ #include "greybus.h" +/* Endo ID (16 bits long) Masks */ +#define ENDO_ID_MASK 0xFFFF +#define ENDO_LARGE_MASK 0x1000 +#define ENDO_MEDIUM_MASK 0x0400 +#define ENDO_MINI_MASK 0x0100 + +#define ENDO_FRONT_MASK(id) ((id) >> 13) +#define ENDO_BACK_SIDE_RIBS_MASK(ribs) ((1 << (ribs)) - 1) + +/* + * endo_is_medium() should be used only if endo isn't large. And endo_is_mini() + * should be used only if endo isn't large or medium. + */ +#define endo_is_large(id) ((id) & ENDO_LARGE_MASK) +#define endo_is_medium(id) ((id) & ENDO_MEDIUM_MASK) +#define endo_is_mini(id) ((id) & ENDO_MINI_MASK) + +#define endo_back_left_ribs(id, ribs) (((id) >> (ribs)) & ENDO_BACK_SIDE_RIBS_MASK(ribs)) +#define endo_back_right_ribs(id, ribs) ((id) & ENDO_BACK_SIDE_RIBS_MASK(ribs)) + /* endo sysfs attributes */ static ssize_t serial_number_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -55,6 +75,280 @@ struct device_type greybus_endo_type = { }; +/* Validate Endo ID */ + +/* + * The maximum module height is 2 units. This means any adjacent pair of bits + * in the left or right mask must have at least one bit set. + */ +static inline bool modules_oversized(unsigned int count, unsigned int mask) +{ + int i; + + for (i = 0; i < count - 1; i++) + if (!(mask & (0x3 << i))) + return true; + + return false; +} + +/* Reverse a number of least significant bits in a value */ +static u8 reverse_bits(unsigned int value, unsigned int bits) +{ + u8 result = 0; + u8 result_mask = 1 << (bits - 1); + u8 value_mask = 1; + + while (value && result_mask) { + if (value & value_mask) { + result |= result_mask; + value ^= value_mask; + } + value_mask <<= 1; + result_mask >>= 1; + } + + return result; +} + +/* + * An Endo can have at most one instance of a single rib spanning its whole + * width. That is, the left and right bit masks representing the rib positions + * must have at most one bit set in both masks. + */ +static bool single_cross_rib(u8 left_ribs, u8 right_ribs) +{ + u8 span_ribs = left_ribs & right_ribs; + + /* Power of 2 ? */ + if (span_ribs & (span_ribs - 1)) + return false; + return true; +} + +/* + * Each Endo size has its own set of front module configurations. For most, the + * resulting rib mask is the same regardless of the Endo size. The mini Endo + * has a few differences though. + * + * Endo front has 4 interface blocks and 3 rib positions. A maximum of 2 ribs + * are allowed to be present for any endo type. + * + * This routine validates front mask and sets 'front_ribs', its 3 least + * significant bits represent front ribs mask, other are 0. The front values + * should be within range (1..6). + * + * front_ribs bitmask: + * - Bit 0: 1st rib location from top, i.e. between interface 1 and 2. + * - Bit 1: 2nd rib location from top, i.e. between interface 2 and 3. + * - Bit 2: 3rd rib location from top, i.e. between interface 3 and 4. + */ +static bool validate_front_ribs(struct greybus_host_device *hd, + struct endo_layout *layout, bool mini, + u16 endo_id) +{ + u8 front_mask = ENDO_FRONT_MASK(endo_id); + + /* Verify front endo mask is in valid range, i.e. 1-6 */ + + switch (front_mask) { + case 1: + layout->front_ribs = 0x0; + break; + case 2: + layout->front_ribs = 0x1; + break; + case 3: + layout->front_ribs = 0x4; + break; + case 4: + layout->front_ribs = mini ? 0x2 : 0x3; + break; + case 5: + layout->front_ribs = mini ? 0x2 : 0x6; + break; + case 6: + layout->front_ribs = 0x5; + break; + default: + dev_err(hd->parent, + "%s: Invalid endo front mask 0x%02x, id 0x%04x\n", + __func__, front_mask, endo_id); + return false; + } + + return true; +} + +/* + * The rear of an endo has a single vertical "spine", and the modules placed on + * the left and right of that spine are separated by ribs. Only one "cross" + * (i.e. rib that spans the entire width) is allowed of the back of the endo; + * all other ribs reach from the spine to the left or right edge. + * + * The width of the module positions on the left and right of the spine are + * determined by the width of the endo (either 1 or 2 "units"). The height of + * the modules is determined by the placement of the ribs (a module can be + * either 1 or 2 units high). + * + * The lower 13 bits of the 16-bit endo id are used to encode back ribs + * information. The large form factor endo uses all of these bits; the medium + * and mini form factors leave some bits unused (such bits shall be ignored, and + * are 0 for the purposes of this endo id definition). + * + * Each defined bit represents a rib position on one or the other side + * of the spine on the back of an endo. If that bit is set (1), it + * means a rib is present in the corresponding location; otherwise + * there is no rib there. + * + * Rotating an endo 180 degrees does not produce a new rib configuration. A + * single endo id represents a specific configuration of ribs without regard to + * its rotational orientation. We define one canonical id to represent a + * particular endo configuration. + */ +static bool validate_back_ribs(struct greybus_host_device *hd, + struct endo_layout *layout, u16 endo_id) +{ + u8 max_ribs = layout->max_ribs; + u8 left_ribs; + u8 right_ribs; + + /* Extract the left and right rib masks */ + left_ribs = endo_back_left_ribs(endo_id, max_ribs); + right_ribs = endo_back_right_ribs(endo_id, max_ribs); + + if (!single_cross_rib(left_ribs, right_ribs)) { + dev_err(hd->parent, + "%s: More than one spanning rib (left 0x%02x right 0x%02x), id 0x%04x\n", + __func__, left_ribs, right_ribs, endo_id); + return false; + } + + if (modules_oversized(max_ribs, left_ribs)) { + dev_err(hd->parent, + "%s: Oversized module (left) 0x%02x, id 0x%04x\n", + __func__, left_ribs, endo_id); + return false; + } + + if (modules_oversized(max_ribs, right_ribs)) { + dev_err(hd->parent, + "%s: Oversized module (Right) 0x%02x, id 0x%04x\n", + __func__, right_ribs, endo_id); + return false; + } + + /* + * The Endo numbering scheme represents the left and right rib + * configuration in a way that's convenient for looking for multiple + * spanning ribs. But it doesn't match the normal Endo interface + * numbering scheme (increasing counter-clockwise around the back). + * Reverse the right bit positions so they do match. + */ + right_ribs = reverse_bits(right_ribs, max_ribs); + + /* + * A mini or large Endo rotated 180 degrees is still the same Endo. In + * most cases that allows two distinct values to represent the same + * Endo; we choose one of them to be the canonical one (and the other is + * invalid). The canonical one is identified by higher value of left + * ribs mask. + * + * This doesn't apply to medium Endos, because the left and right sides + * are of different widths. + */ + if (max_ribs != ENDO_BACK_RIBS_MEDIUM && left_ribs < right_ribs) { + dev_err(hd->parent, "%s: Non-canonical endo id 0x%04x\n", __func__, + endo_id); + return false; + } + + layout->left_ribs = left_ribs; + layout->right_ribs = right_ribs; + return true; +} + +/* + * Validate the endo-id passed from SVC. Error out if its not a valid Endo, + * else return structure representing ribs positions on front and back of Endo. + */ +static int gb_validate_endo_id(struct greybus_host_device *hd, + struct endo_layout *layout, u16 endo_id) +{ + /* Validate Endo Size */ + if (endo_is_large(endo_id)) { + /* Large Endo type */ + layout->max_ribs = ENDO_BACK_RIBS_LARGE; + } else if (endo_is_medium(endo_id)) { + /* Medium Endo type */ + layout->max_ribs = ENDO_BACK_RIBS_MEDIUM; + } else if (endo_is_mini(endo_id)) { + /* Mini Endo type */ + layout->max_ribs = ENDO_BACK_RIBS_MINI; + } else { + dev_err(hd->parent, "%s: Invalid endo type, id 0x%04x\n", + __func__, endo_id); + return -EINVAL; + } + + if (!validate_back_ribs(hd, layout, endo_id)) + return -EINVAL; + + if (!validate_front_ribs(hd, layout, + layout->max_ribs == ENDO_BACK_RIBS_MINI, + endo_id)) + return -EINVAL; + + return 0; +} + +/* + * Look up which module contains the given interface. + * + * A module's ID is the same as its lowest-numbered interface ID. So the module + * ID for a 1x1 module is always the same as its interface ID. + * + * For Endo Back: + * The module ID for an interface on a 1x2 or 2x2 module (which use two + * interface blocks) can be either the interface ID, or one less than the + * interface ID if there is no rib "above" the interface. + * + * For Endo Front: + * There are three rib locations in front and all of them might be unused, i.e. + * a single module is used for all 4 interfaces. We need to check all ribs in + * that case to find module ID. + */ +u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id) +{ + struct endo_layout *layout = &endo->layout; + unsigned int height = layout->max_ribs + 1; + unsigned int iid = interface_id - 1; + unsigned int mask, rib_mask; + + if (!interface_id) + return 0; + + if (iid < height) { /* back left */ + mask = layout->left_ribs; + } else if (iid < 2 * height) { /* back right */ + mask = layout->right_ribs; + iid -= height; + } else { /* front */ + mask = layout->front_ribs; + iid -= 2 * height; + } + + /* + * Find the next rib *above* this interface to determine the lowest + * interface ID in the module. + */ + rib_mask = 1 << iid; + while ((rib_mask >>= 1) != 0 && !(mask & rib_mask)) + --interface_id; + + return interface_id; +} + /* * Endo "types" have different module locations, these are tables based on those * types that list the module ids for the different locations. diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index a4342aaa582b..dd0526949737 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -15,7 +15,28 @@ struct gb_svc { u8 version[10]; }; +/* Max ribs per Endo size */ +#define ENDO_BACK_RIBS_MINI 0x4 +#define ENDO_BACK_RIBS_MEDIUM 0x5 +#define ENDO_BACK_RIBS_LARGE 0x6 + +/** + * struct endo_layout - represents front/back ribs of the endo. + * + * @front_ribs: Mask of present ribs in front. + * @left_ribs: Mask of present ribs in back (left). + * @right_ribs: Mask of present ribs in back (right). + * @max_ribs: Max ribs on endo back, possible values defined above. + */ +struct endo_layout { + u8 front_ribs; + u8 left_ribs; + u8 right_ribs; + u8 max_ribs; +}; + struct gb_endo { + struct endo_layout layout; struct device dev; struct gb_svc svc; u16 id; @@ -29,4 +50,6 @@ struct greybus_host_device; struct gb_endo *gb_endo_create(struct greybus_host_device *hd); void gb_endo_remove(struct gb_endo *endo); +u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id); + #endif /* __ENDO_H */ -- cgit v1.2.3-59-g8ed1b From 51e93aea65cdab93ae013b87a7e6b3a9eccef5ad Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 8 May 2015 12:58:51 +0530 Subject: greybus: endo: Create modules after validating Endo ID We already have code to parse Endo ID, lets use it to create modules at run time instead of creating them from a static array. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 105 +++++++++++++++++++++--------------- drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/module.c | 14 ++--- drivers/staging/greybus/module.h | 2 - 4 files changed, 66 insertions(+), 57 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index e47ec6963fa1..80d9397c565f 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -350,45 +350,43 @@ u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id) } /* - * Endo "types" have different module locations, these are tables based on those - * types that list the module ids for the different locations. + * Creates all possible modules for the Endo. * - * List must end with 0x00 in order to properly terminate the list. + * We try to create modules for all possible interface IDs. If a module is + * already created, we skip creating it again with the help of prev_module_id. */ -static u8 endo_4755[] = { - 0x01, - 0x03, - 0x05, - 0x06, - 0x07, - 0x08, - 0x0a, - 0x0c, - 0x0d, - 0x0e, - 0x00, -}; - - static int create_modules(struct gb_endo *endo) { struct gb_module *module; - u8 *endo_modules; - int i; + int prev_module_id = 0; + int interface_id; + int module_id; + int interfaces; - /* Depending on the endo id, create a bunch of different modules */ - switch (endo->id) { - case 0x4755: - endo_modules = &endo_4755[0]; - break; - default: - dev_err(&endo->dev, "Unknown endo id 0x%04x, aborting!", - endo->id); - return -EINVAL; - } + /* + * Total number of interfaces: + * - Front: 4 + * - Back: + * - Left: max_ribs + 1 + * - Right: max_ribs + 1 + */ + interfaces = 4 + (endo->layout.max_ribs + 1) * 2; + + /* Find module corresponding to each interface */ + for (interface_id = 1; interface_id <= interfaces; interface_id++) { + module_id = endo_get_module_id(endo, interface_id); + + if (WARN_ON(!module_id)) + continue; + + /* Skip already created modules */ + if (module_id == prev_module_id) + continue; + + prev_module_id = module_id; - for (i = 0; endo_modules[i] != 0x00; ++i) { - module = gb_module_create(&endo->dev, endo_modules[i]); + /* New module, create it */ + module = gb_module_create(&endo->dev, module_id); if (!module) return -EINVAL; } @@ -396,15 +394,11 @@ static int create_modules(struct gb_endo *endo) return 0; } -struct gb_endo *gb_endo_create(struct greybus_host_device *hd) +static int gb_endo_register(struct greybus_host_device *hd, + struct gb_endo *endo) { - struct gb_endo *endo; int retval; - endo = kzalloc(sizeof(*endo), GFP_KERNEL); - if (!endo) - return NULL; - endo->dev.parent = hd->parent; endo->dev.bus = &greybus_bus_type; endo->dev.type = &greybus_endo_type; @@ -412,12 +406,11 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd) endo->dev.dma_mask = hd->parent->dma_mask; device_initialize(&endo->dev); - // FIXME - determine endo "id" from the SVC - // Also get the version and serial number from the SVC, right now we are + // FIXME + // Get the version and serial number from the SVC, right now we are // using "fake" numbers. strcpy(&endo->svc.serial_number[0], "042"); strcpy(&endo->svc.version[0], "0.0"); - endo->id = 0x4755; dev_set_name(&endo->dev, "endo-0x%04x", endo->id); retval = device_add(&endo->dev); @@ -425,10 +418,32 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd) dev_err(hd->parent, "failed to add endo device of id 0x%04x\n", endo->id); put_device(&endo->dev); - kfree(endo); - return NULL; } + return retval; +} + +struct gb_endo *gb_endo_create(struct greybus_host_device *hd) +{ + struct gb_endo *endo; + int retval; + u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC + + endo = kzalloc(sizeof(*endo), GFP_KERNEL); + if (!endo) + return NULL; + + /* First check if the value supplied is a valid endo id */ + if (gb_validate_endo_id(hd, &endo->layout, endo_id)) + goto free_endo; + + endo->id = endo_id; + + /* Register Endo device */ + if (gb_endo_register(hd, endo)) + goto free_endo; + + /* Create modules/interfaces */ retval = create_modules(endo); if (retval) { gb_endo_remove(endo); @@ -436,6 +451,10 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd) } return endo; + +free_endo: + kfree(endo); + return NULL; } void gb_endo_remove(struct gb_endo *endo) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 63665a2d8015..28b3c4fccf24 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -92,7 +92,7 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, return NULL; } - module = gb_module_find(hd, get_module_id(interface_id)); + module = gb_module_find(hd, endo_get_module_id(hd->endo, interface_id)); if (!module) return NULL; diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 202f141c7fe3..8ed96a8ba26d 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -83,17 +83,6 @@ struct device_type greybus_module_type = { .release = greybus_module_release, }; -u8 get_module_id(u8 interface_id) -{ - /* - * FIXME: - * - * We should be able to find it from Endo ID passed during greybus - * control operation, while setting up AP. - */ - return interface_id; -} - struct module_find { struct gb_endo *endo; u8 module_id; @@ -125,6 +114,9 @@ struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) struct gb_module *module = NULL; struct module_find find; + if (!module_id) + return NULL; + find.module_id = module_id; find.endo = hd->endo; diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index c23ac98fc1ba..3b3f421b244f 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -23,6 +23,4 @@ struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id); struct gb_module *gb_module_create(struct device *parent, u8 module_id); void gb_module_remove_all(struct gb_endo *endo); -u8 get_module_id(u8 interface_id); - #endif /* __MODULE_H */ -- cgit v1.2.3-59-g8ed1b From 563e6b97b87c18ec0d0d1fc541d59d1ee0842a39 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Mon, 11 May 2015 19:22:03 +0100 Subject: greybus: manifest: be coherent with protocol name remane protocol define from GREYBUS_PROTOCOL_LED to GREYBUS_PROTOCOL_LIGHTS to be coherent with the specification. Signed-off-by: Rui Miguel Silva Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 05af0292b690..ce15e1a9807a 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -37,7 +37,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_DISPLAY = 0x0c, GREYBUS_PROTOCOL_CAMERA = 0x0d, GREYBUS_PROTOCOL_SENSOR = 0x0e, - GREYBUS_PROTOCOL_LED = 0x0f, + GREYBUS_PROTOCOL_LIGHTS = 0x0f, GREYBUS_PROTOCOL_VIBRATOR = 0x10, GREYBUS_PROTOCOL_LOOPBACK = 0x11, GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12, -- cgit v1.2.3-59-g8ed1b From 13956900b069c98a7c97e32d2f18afa11dcf725c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 May 2015 14:09:09 -0700 Subject: greybus: audio: fix some sparse static warnings This makes some functions and structures static, as warned by sparse, as they don't need to be global. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/audio-dai.c | 2 +- drivers/staging/greybus/audio-pcm.c | 2 +- drivers/staging/greybus/audio.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/audio-dai.c b/drivers/staging/greybus/audio-dai.c index f2c8ca0b4df6..39b72f0d2138 100644 --- a/drivers/staging/greybus/audio-dai.c +++ b/drivers/staging/greybus/audio-dai.c @@ -59,7 +59,7 @@ static const struct snd_soc_dai_ops gb_dai_ops = { .set_fmt = gb_dai_set_fmt, }; -struct snd_soc_dai_driver gb_cpu_dai = { +static struct snd_soc_dai_driver gb_cpu_dai = { .name = "gb-cpu-dai", .playback = { .rates = GB_RATES, diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c index 28b5e1106855..92436300b3eb 100644 --- a/drivers/staging/greybus/audio-pcm.c +++ b/drivers/staging/greybus/audio-pcm.c @@ -235,7 +235,7 @@ static int gb_pcm_new(struct snd_soc_pcm_runtime *rtd) PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); } -struct snd_soc_platform_driver gb_soc_platform = { +static struct snd_soc_platform_driver gb_soc_platform = { .ops = &gb_pcm_ops, .pcm_new = gb_pcm_new, .pcm_free = gb_pcm_free, diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 1ef09035706f..232ba94b5b88 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -93,7 +93,7 @@ struct gb_card_info_object { }; -struct asoc_simple_card_info *setup_card_info(int device_count) +static struct asoc_simple_card_info *setup_card_info(int device_count) { struct gb_card_info_object *obj; @@ -125,7 +125,7 @@ struct asoc_simple_card_info *setup_card_info(int device_count) return &obj->card_info; } -void free_card_info(struct asoc_simple_card_info *ci) +static void free_card_info(struct asoc_simple_card_info *ci) { struct gb_card_info_object *obj; -- cgit v1.2.3-59-g8ed1b From 64a54801ae806d482d72fce9583cce7409ec146c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 May 2015 14:09:32 -0700 Subject: greybus: audio: fix a bunch of endian issues sparse is rightfully complaining about a lack of converting when accessing or assigning to little endian fields. Fix them all up to work properly. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/audio-gb-cmds.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index ef3191670485..daa3181c9422 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -40,7 +40,7 @@ int gb_i2s_mgmt_activate_cport(struct gb_connection *connection, struct gb_i2s_mgmt_activate_cport_request request; memset(&request, 0, sizeof(request)); - request.cport = cport; + request.cport = cpu_to_le16(cport); return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_ACTIVATE_CPORT, &request, sizeof(request), NULL, 0); @@ -52,7 +52,7 @@ int gb_i2s_mgmt_deactivate_cport(struct gb_connection *connection, struct gb_i2s_mgmt_deactivate_cport_request request; memset(&request, 0, sizeof(request)); - request.cport = cport; + request.cport = cpu_to_le16(cport); return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT, &request, sizeof(request), NULL, 0); @@ -82,7 +82,7 @@ int gb_i2s_mgmt_set_samples_per_message( struct gb_i2s_mgmt_set_samples_per_message_request request; memset(&request, 0, sizeof(request)); - request.samples_per_message = samples_per_message; + request.samples_per_message = cpu_to_le16(samples_per_message); return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE, @@ -121,14 +121,14 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) /* Pick 48KHz 16-bits/channel */ for (i = 0, cfg = get_cfg->config; i < CONFIG_COUNT_MAX; i++, cfg++) { - if ((cfg->sample_frequency == GB_SAMPLE_RATE) && + if ((le32_to_cpu(cfg->sample_frequency) == GB_SAMPLE_RATE) && (cfg->num_channels == 2) && (cfg->bytes_per_channel == 2) && (cfg->byte_order & GB_I2S_MGMT_BYTE_ORDER_LE) && - (cfg->spatial_locations == + (le32_to_cpu(cfg->spatial_locations) == (GB_I2S_MGMT_SPATIAL_LOCATION_FL | GB_I2S_MGMT_SPATIAL_LOCATION_FR)) && - (cfg->ll_protocol & GB_I2S_MGMT_PROTOCOL_I2S) && + (le32_to_cpu(cfg->ll_protocol) & GB_I2S_MGMT_PROTOCOL_I2S) && (cfg->ll_mclk_role & GB_I2S_MGMT_ROLE_MASTER) && (cfg->ll_bclk_role & GB_I2S_MGMT_ROLE_MASTER) && (cfg->ll_wclk_role & GB_I2S_MGMT_ROLE_MASTER) && @@ -148,7 +148,7 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) memcpy(&set_cfg, cfg, sizeof(set_cfg)); set_cfg.config.byte_order = GB_I2S_MGMT_BYTE_ORDER_LE; - set_cfg.config.ll_protocol = GB_I2S_MGMT_PROTOCOL_I2S; + set_cfg.config.ll_protocol = cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S); set_cfg.config.ll_mclk_role = GB_I2S_MGMT_ROLE_MASTER; set_cfg.config.ll_bclk_role = GB_I2S_MGMT_ROLE_MASTER; set_cfg.config.ll_wclk_role = GB_I2S_MGMT_ROLE_MASTER; @@ -191,7 +191,7 @@ int gb_i2s_send_data(struct gb_connection *connection, int ret; gb_req = req_buf; - gb_req->sample_number = sample_num; + gb_req->sample_number = cpu_to_le32(sample_num); memcpy((void *)&gb_req->data[0], source_addr, len); @@ -199,7 +199,7 @@ int gb_i2s_send_data(struct gb_connection *connection, for (; len < MAX_SEND_DATA_LEN; len++) gb_req->data[len] = gb_req->data[len - SAMPLE_SIZE]; - gb_req->size = len; + gb_req->size = cpu_to_le32(len); ret = gb_operation_sync(connection, GB_I2S_DATA_TYPE_SEND_DATA, (void *) gb_req, SEND_DATA_BUF_LEN, NULL, 0); -- cgit v1.2.3-59-g8ed1b From a04c640e5cf342a491afc06b0d931b7a046aa76b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 11 May 2015 21:16:34 -0500 Subject: greybus: loopback: fix the type attribute check In gb_loopback_check_attr(), the value of gb->type is checked for validity. The only valid values are 0, 1, and 2. But the check allows the value 3. Fix that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 9860d64e50ba..20aabebf0140 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -123,7 +123,7 @@ static void gb_loopback_check_attr(struct gb_loopback *gb) { if (gb->ms_wait > 1000) gb->ms_wait = 1000; - if (gb->type > 3) + if (gb->type > 2) gb->type = 0; if (gb->size > GB_LOOPBACK_SIZE_MAX) gb->size = GB_LOOPBACK_SIZE_MAX; -- cgit v1.2.3-59-g8ed1b From 69f6034792faf0f930f3797a0a3c3c9cf9ac73cc Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 11 May 2015 21:16:35 -0500 Subject: greybus: loopback: return the right error value If an error occurs starting up the loopback thread, the error code is not extracted properly. Fix that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 20aabebf0140..85e2fe7b5d76 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -362,7 +362,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb_loopback_reset_stats(gb); gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { - retval = IS_ERR(gb->task); + retval = PTR_ERR(gb->task); goto error; } -- cgit v1.2.3-59-g8ed1b From 48f19ee8244776a8e7fb47bac1a2b09e9920d035 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 11 May 2015 21:16:36 -0500 Subject: greybus: loopback: fix an incorrect comment Fix a comment that incorrectly says the delay between messages is limited to 1024 msec; it actually must be <= 1000 msec. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 85e2fe7b5d76..e1e40ea44ccf 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -148,7 +148,7 @@ gb_loopback_ro_attr(error, d); gb_loopback_attr(type, d); /* Size of transfer message payload: 0-4096 bytes */ gb_loopback_attr(size, u); -/* Time to wait between two messages: 0-1024 ms */ +/* Time to wait between two messages: 0-1000 ms */ gb_loopback_attr(ms_wait, d); #define dev_stats_attrs(name) \ -- cgit v1.2.3-59-g8ed1b From 91262c3ab70a02949db294b55b4cd49b8ae6505e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 11 May 2015 21:16:37 -0500 Subject: greybus: loopback: symbolically define max wait time Use a symbolic constant to define the maximum time (number of milliseconds) to delay between initiated operations. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index e1e40ea44ccf..f7538aa5fcd4 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -55,6 +55,7 @@ struct gb_loopback { #define GB_LOOPBACK_TYPE_PING 0x02 #define GB_LOOPBACK_TYPE_TRANSFER 0x03 +#define GB_LOOPBACK_MS_WAIT_MAX 1000 #define GB_LOOPBACK_SIZE_MAX SZ_4K /* Define get_version() routine */ @@ -121,8 +122,8 @@ static DEVICE_ATTR_RW(field) static void gb_loopback_reset_stats(struct gb_loopback *gb); static void gb_loopback_check_attr(struct gb_loopback *gb) { - if (gb->ms_wait > 1000) - gb->ms_wait = 1000; + if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) + gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; if (gb->type > 2) gb->type = 0; if (gb->size > GB_LOOPBACK_SIZE_MAX) -- cgit v1.2.3-59-g8ed1b From 48cc8b8bbaf0a6767380e4e2e55df3258e47bef6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 11 May 2015 21:16:38 -0500 Subject: greybus: loopback: define loopback functions symbolically Add some symbols to indicate what kind of "function" the loopback thread is supposed to be performing--the type of traffic it generates over its connection. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index f7538aa5fcd4..decaf71fb6e4 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -55,6 +55,12 @@ struct gb_loopback { #define GB_LOOPBACK_TYPE_PING 0x02 #define GB_LOOPBACK_TYPE_TRANSFER 0x03 +/* Current function (type of traffic generated by the loopback thread) */ +#define GB_LOOPBACK_FN_NONE 0x00 +#define GB_LOOPBACK_FN_PING 0x01 +#define GB_LOOPBACK_FN_XFER 0x02 +#define GB_LOOPBACK_FN_COUNT 0x03 + #define GB_LOOPBACK_MS_WAIT_MAX 1000 #define GB_LOOPBACK_SIZE_MAX SZ_4K @@ -124,8 +130,8 @@ static void gb_loopback_check_attr(struct gb_loopback *gb) { if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; - if (gb->type > 2) - gb->type = 0; + if (gb->type >= GB_LOOPBACK_FN_COUNT) + gb->type = GB_LOOPBACK_FN_NONE; if (gb->size > GB_LOOPBACK_SIZE_MAX) gb->size = GB_LOOPBACK_SIZE_MAX; gb->error = 0; @@ -310,13 +316,13 @@ static int gb_loopback_fn(void *data) struct gb_loopback *gb = (struct gb_loopback *)data; while (!kthread_should_stop()) { - if (gb->type == 0) { + if (gb->type == GB_LOOPBACK_FN_NONE) { msleep(1000); continue; } - if (gb->type == 1) + if (gb->type == GB_LOOPBACK_FN_PING) error = gb_loopback_ping(gb, &tlat); - if (gb->type == 2) + else if (gb->type == GB_LOOPBACK_FN_XFER) error = gb_loopback_transfer(gb, &tlat, gb->size); if (error) gb->error++; -- cgit v1.2.3-59-g8ed1b From ac1c2840bd41e89d55fccabe86cf9a3e2d56a652 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 11 May 2015 21:16:39 -0500 Subject: greybus: loopback: support module-initiated requests There's no reason we can't support loopback pings or transfers initiated by the module. Allow it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 44 +++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index decaf71fb6e4..285e44b9431f 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -242,6 +242,48 @@ static int gb_loopback_ping(struct gb_loopback *gb, struct timeval *tping) return retval; } +static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) +{ + struct gb_connection *connection = operation->connection; + struct gb_loopback_transfer_request *request; + struct gb_loopback_transfer_response *response; + u32 len; + + /* By convention, the AP initiates the version operation */ + switch (type) { + case GB_LOOPBACK_TYPE_PROTOCOL_VERSION: + dev_err(&connection->dev, + "module-initiated version operation\n"); + return -EINVAL; + case GB_LOOPBACK_TYPE_PING: + return 0; + case GB_LOOPBACK_TYPE_TRANSFER: + if (operation->request->payload_size < sizeof(*request)) { + dev_err(&connection->dev, + "transfer request too small (%zu < %zu)\n", + operation->request->payload_size, + sizeof(*request)); + return -EINVAL; /* -EMSGSIZE */ + } + request = operation->request->payload; + len = le32_to_cpu(request->len); + if (len) { + if (!gb_operation_response_alloc(operation, len)) { + dev_err(&connection->dev, + "error allocating response\n"); + return -ENOMEM; + } + response = operation->response->payload; + memcpy(response->data, request->data, len); + } + return 0; + default: + dev_err(&connection->dev, + "unsupported request: %hhu\n", type); + return -EINVAL; + } +} + static void gb_loopback_reset_stats(struct gb_loopback *gb) { struct gb_loopback_stats reset = { @@ -397,7 +439,7 @@ static struct gb_protocol loopback_protocol = { .minor = GB_LOOPBACK_VERSION_MINOR, .connection_init = gb_loopback_connection_init, .connection_exit = gb_loopback_connection_exit, - .request_recv = NULL, /* no incoming requests */ + .request_recv = gb_loopback_request_recv, }; gb_protocol_driver(&loopback_protocol); -- cgit v1.2.3-59-g8ed1b From 65cac604b21ae8c99bd501b369c254707f51c252 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 13 May 2015 18:59:00 +0100 Subject: greybus: make: check kernel configuration options Add checks for options that shall be enabled in the kernel config and for options that shall be disable. To add options to list append them to CONFIG_OPTIONS_ENABLE or CONFIG_OPTIONS_DISABLE respectively. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index f366da6fba12..130758f27298 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -44,6 +44,22 @@ KERNELDIR ?= /lib/modules/$(KERNELVER)/build INSTALL_MOD_PATH ?= /.. PWD := $(shell pwd) +# kernel config option that shall be enable +CONFIG_OPTIONS_ENABLE := SYSFS SPI USB SND_SOC + +# kernel config option that shall be disable +CONFIG_OPTIONS_DISABLE := + +# this only run in kbuild part of the makefile +ifneq ($(KERNELRELEASE),) +$(foreach opt,$(CONFIG_OPTIONS_ENABLE),$(if $(CONFIG_$(opt)),, \ + $(error CONFIG_$(opt) is disabled in the kernel configuration and must be enable \ + to continue compilation))) +$(foreach opt,$(CONFIG_OPTIONS_DISABLE),$(if $(filter m y, $(CONFIG_$(opt))), \ + $(error CONFIG_$(opt) is enabled in the kernel configuration and must be disable \ + to continue compilation),)) +endif + # add -Wall to try to catch everything we can. ccFlags-y := -Wall -- cgit v1.2.3-59-g8ed1b From 6f8528e0bea76850a153a98e47ad60aecf112801 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Thu, 14 May 2015 23:03:04 +0700 Subject: greybus: loopback: add more clean up when init connection fails It should remove the object from sysfs when loopback connection init error. Signed-off-by: Phong Tran Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 285e44b9431f..eeec3323ad6d 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -401,23 +401,25 @@ static int gb_loopback_connection_init(struct gb_connection *connection) connection->private = gb; retval = sysfs_create_groups(&connection->dev.kobj, loopback_groups); if (retval) - goto error; + goto out_free; /* Check the version */ retval = get_version(gb); if (retval) - goto error; + goto out_get_ver; gb_loopback_reset_stats(gb); gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { retval = PTR_ERR(gb->task); - goto error; + goto out_get_ver; } return 0; -error: +out_get_ver: + sysfs_remove_groups(&connection->dev.kobj, loopback_groups); +out_free: kfree(gb); return retval; } -- cgit v1.2.3-59-g8ed1b From 6b17492eee00cbaac20b8431e8fa63f137a9ca5c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 14 May 2015 10:39:35 -0700 Subject: greybus: vibrator: convert idr to be an ida All we need is a simple ida, so use that interface instead of the more "complex" idr one. Bonus is we don't need to fix the locking issue I forgot about when using an idr, as ida has one built-in. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/vibrator.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index aefd2cd3f18e..20f09bba5fac 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -93,7 +93,7 @@ static struct class vibrator_class = { #endif }; -static DEFINE_IDR(minors); +static DEFINE_IDA(minors); static int gb_vibrator_connection_init(struct gb_connection *connection) { @@ -117,7 +117,7 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) * there is a "real" device somewhere in the kernel for this, but I * can't find it at the moment... */ - vib->minor = idr_alloc(&minors, vib, 0, 0, GFP_KERNEL); + vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); if (vib->minor < 0) { retval = vib->minor; goto error; @@ -126,7 +126,7 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) "vibrator%d", vib->minor); if (IS_ERR(dev)) { retval = -EINVAL; - goto err_idr_remove; + goto err_ida_remove; } vib->dev = dev; @@ -139,14 +139,14 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); if (retval) { device_unregister(dev); - goto err_idr_remove; + goto err_ida_remove; } #endif return 0; -err_idr_remove: - idr_remove(&minors, vib->minor); +err_ida_remove: + ida_simple_remove(&minors, vib->minor); error: kfree(vib); return retval; @@ -159,7 +159,7 @@ static void gb_vibrator_connection_exit(struct gb_connection *connection) #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); #endif - idr_remove(&minors, vib->minor); + ida_simple_remove(&minors, vib->minor); device_unregister(vib->dev); kfree(vib); } -- cgit v1.2.3-59-g8ed1b From 5bd5f00c30fa8be047e7905739dcf6ff689718c4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 14 May 2015 10:40:02 -0700 Subject: greybus: connection: remove lock around ida_simple_* functions ida_simple_* has a built-in spinlock, no need to grab another lock when accessing it. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/connection.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 5ec161b9b2f2..51d505e7017d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -63,9 +63,7 @@ static bool gb_connection_hd_cport_id_alloc(struct gb_connection *connection) struct ida *ida = &connection->hd->cport_id_map; int id; - spin_lock_irq(&gb_connections_lock); id = ida_simple_get(ida, 0, HOST_DEV_CPORT_ID_MAX, GFP_ATOMIC); - spin_unlock_irq(&gb_connections_lock); if (id < 0) return false; @@ -81,9 +79,7 @@ static void gb_connection_hd_cport_id_free(struct gb_connection *connection) { struct ida *ida = &connection->hd->cport_id_map; - spin_lock_irq(&gb_connections_lock); ida_simple_remove(ida, connection->hd_cport_id); - spin_unlock_irq(&gb_connections_lock); connection->hd_cport_id = CPORT_ID_BAD; } -- cgit v1.2.3-59-g8ed1b From e806c7fb8e9bae87fc23958c3789f2c2f96f54a4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 8 May 2015 15:50:17 +0200 Subject: greybus: raw: add raw greybus kernel driver This adds a driver that implements the greybus Raw protocol as specified. It preserves the message boundries by only allowing a read to receive a "full" message, and any write() call also is passed in a single greybus request. Totally untested, given that we have no raw firmware or gbsim code yet. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Rui Miguel Silva --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/raw.c | 370 +++++++++++++++++++++++++++++ 3 files changed, 373 insertions(+) create mode 100644 drivers/staging/greybus/raw.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 130758f27298..e636fe01f033 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -28,6 +28,7 @@ gb-phy-y := gpb.o \ gb-vibrator-y := vibrator.o gb-battery-y := battery.o gb-loopback-y := loopback.o +gb-raw-y := raw.o gb-es1-y := es1.o gb-es2-y := es2.o @@ -36,6 +37,7 @@ obj-m += gb-phy.o obj-m += gb-vibrator.o obj-m += gb-battery.o obj-m += gb-loopback.o +obj-m += gb-raw.o obj-m += gb-es1.o obj-m += gb-es2.o diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index ce15e1a9807a..076d7114cb4e 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -43,6 +43,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12, GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13, /* ... */ + GREYBUS_PROTOCOL_RAW = 0xfe, GREYBUS_PROTOCOL_VENDOR = 0xff, }; diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c new file mode 100644 index 000000000000..eb43acc91e66 --- /dev/null +++ b/drivers/staging/greybus/raw.c @@ -0,0 +1,370 @@ +/* + * Greybus driver for the Raw protocol + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "greybus.h" + +struct gb_raw { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + + struct list_head list; + int list_data; + struct mutex list_lock; + dev_t dev; + struct cdev cdev; + struct device *device; +}; + +/* Version of the Greybus raw protocol we support */ +#define GB_RAW_VERSION_MAJOR 0x00 +#define GB_RAW_VERSION_MINOR 0x01 + +/* Greybus raw request types */ +#define GB_RAW_TYPE_INVALID 0x00 +#define GB_RAW_TYPE_PROTOCOL_VERSION 0x01 +#define GB_RAW_TYPE_SEND 0x02 + +/* Define get_version() routine */ +define_get_version(gb_raw, RAW); + +struct gb_raw_send_request { + __le32 len; + __u8 data[0]; +}; + +struct raw_data { + struct list_head entry; + u32 len; + u8 data[0]; +}; + +static struct class *raw_class; +static int raw_major; +static const struct file_operations raw_fops; +static DEFINE_IDA(minors); + +/* Number of minor devices this driver supports */ +#define NUM_MINORS 256 + +/* Maximum size of any one send data buffer we support */ +#define MAX_PACKET_SIZE (PAGE_SIZE * 2) + +/* + * Maximum size of the data in the receive buffer we allow before we start to + * drop messages on the floor + */ +#define MAX_DATA_SIZE (MAX_PACKET_SIZE * 8) + +/* + * Add the raw data message to the list of received messages. + */ +static int receive_data(struct gb_raw *raw, u32 len, u8 *data) +{ + struct raw_data *raw_data; + int retval = 0; + + if (len > MAX_PACKET_SIZE) { + dev_err(raw->device, "Too big of a data packet, rejected\n"); + return -EINVAL; + } + + mutex_lock(&raw->list_lock); + if ((raw->list_data + len) > MAX_DATA_SIZE) { + dev_err(raw->device, + "Too much data in receive buffer, now dropping packets\n"); + retval = -EINVAL; + goto exit; + } + + raw_data = kmalloc(sizeof(*raw_data) + len, GFP_KERNEL); + if (!raw_data) { + retval = -ENOMEM; + goto exit; + } + + raw->list_data += len; + raw_data->len = len; + memcpy(&raw_data->data[0], data, len); + + list_add_tail(&raw_data->entry, &raw->list); +exit: + mutex_unlock(&raw->list_lock); + return retval; +} + +static int gb_raw_receive(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_raw *raw = connection->private; + struct gb_raw_send_request *receive; + u32 len; + + if (type != GB_RAW_TYPE_SEND) { + dev_err(raw->device, "unknown request type %d\n", type); + return -EINVAL; + } + + /* Verify size of payload */ + if (op->request->payload_size < sizeof(*receive)) { + dev_err(raw->device, "raw receive request too small\n"); + return -EINVAL; + } + receive = op->request->payload; + len = le32_to_cpu(receive->len); + if (len != (int)(op->request->payload_size - sizeof(__le32))) { + dev_err(raw->device, + "raw receive request wrong size %d vs %d\n", + len, + (int)(op->request->payload_size - sizeof(__le32))); + return -EINVAL; + } + if (len == 0) { + dev_err(raw->device, "raw receive request of 0 bytes?\n"); + return -EINVAL; + } + + return receive_data(raw, len, receive->data); +} + +static int gb_raw_send(struct gb_raw *raw, u32 len, const char __user *data) +{ + struct gb_connection *connection = raw->connection; + struct gb_raw_send_request *request; + int retval; + + request = kmalloc(len + sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + if (copy_from_user(&request->data[0], data, len)) { + kfree(request); + return -EFAULT; + } + + request->len = cpu_to_le32(len); + + retval = gb_operation_sync(connection, GB_RAW_TYPE_SEND, + request, len + sizeof(*request), + NULL, 0); + + kfree(request); + return retval; +} + +static int gb_raw_connection_init(struct gb_connection *connection) +{ + struct gb_raw *raw; + int retval; + int minor; + + raw = kzalloc(sizeof(*raw), GFP_KERNEL); + if (!raw) + return -ENOMEM; + + raw->connection = connection; + connection->private = raw; + + /* Check the protocol version */ + retval = get_version(raw); + if (retval) + goto error_free; + + INIT_LIST_HEAD(&raw->list); + mutex_init(&raw->list_lock); + + minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); + if (minor < 0) { + retval = minor; + goto error_free; + } + + raw->dev = MKDEV(raw_major, minor); + cdev_init(&raw->cdev, &raw_fops); + retval = cdev_add(&raw->cdev, raw->dev, 1); + if (retval) + goto error_cdev; + + raw->device = device_create(raw_class, &connection->dev, raw->dev, raw, + "gb!raw%d", minor); + if (IS_ERR(raw->device)) { + retval = PTR_ERR(raw->device); + goto error_device; + } + + return 0; + +error_device: + cdev_del(&raw->cdev); + +error_cdev: + ida_simple_remove(&minors, minor); + +error_free: + kfree(raw); + return retval; +} + +static void gb_raw_connection_exit(struct gb_connection *connection) +{ + struct gb_raw *raw = connection->private; + struct raw_data *raw_data; + struct raw_data *temp; + + // FIXME - handle removing a connection when the char device node is open. + cdev_del(&raw->cdev); + ida_simple_remove(&minors, MINOR(raw->dev)); + device_del(raw->device); + mutex_lock(&raw->list_lock); + list_for_each_entry_safe(raw_data, temp, &raw->list, entry) { + list_del(&raw_data->entry); + kfree(raw_data); + } + mutex_unlock(&raw->list_lock); + + kfree(raw); +} + +static struct gb_protocol raw_protocol = { + .name = "raw", + .id = GREYBUS_PROTOCOL_RAW, + .major = GB_RAW_VERSION_MAJOR, + .minor = GB_RAW_VERSION_MINOR, + .connection_init = gb_raw_connection_init, + .connection_exit = gb_raw_connection_exit, + .request_recv = gb_raw_receive, +}; + +/* + * Character device node interfaces. + * + * Note, we are using read/write to only allow a single read/write per message. + * This means for read(), you have to provide a big enough buffer for the full + * message to be copied into. If the buffer isn't big enough, the read() will + * fail with -ENOSPC. + */ + +static int raw_open(struct inode *inode, struct file *file) +{ + struct cdev *cdev = inode->i_cdev; + struct gb_raw *raw = container_of(cdev, struct gb_raw, cdev); + + file->private_data = raw; + return 0; +} + +static ssize_t raw_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct gb_raw *raw = file->private_data; + int retval; + + if (!count) + return 0; + + if (count > MAX_PACKET_SIZE) + return -E2BIG; + + retval = gb_raw_send(raw, count, buf); + if (retval) + return retval; + + return count; +} + +static ssize_t raw_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct gb_raw *raw = file->private_data; + int retval = 0; + struct raw_data *raw_data; + + mutex_lock(&raw->list_lock); + if (list_empty(&raw->list)) + goto exit; + + raw_data = list_first_entry(&raw->list, struct raw_data, entry); + if (raw_data->len > count) { + retval = -ENOSPC; + goto exit; + } + + if (copy_to_user(buf, &raw_data->data[0], raw_data->len)) { + retval = -EFAULT; + goto exit; + } + + list_del(&raw_data->entry); + raw->list_data -= raw_data->len; + retval = raw_data->len; + kfree(raw_data); + +exit: + mutex_unlock(&raw->list_lock); + return retval; +} + +static const struct file_operations raw_fops = { + .owner = THIS_MODULE, + .write = raw_write, + .read = raw_read, + .open = raw_open, + .llseek = noop_llseek, +}; + +static int raw_init(void) +{ + dev_t dev; + int retval; + + raw_class = class_create(THIS_MODULE, "gb_raw"); + if (IS_ERR(raw_class)) { + retval = PTR_ERR(raw_class); + goto error_class; + } + + retval = alloc_chrdev_region(&dev, 0, NUM_MINORS, "gb_raw"); + if (retval < 0) + goto error_chrdev; + + + raw_major = MAJOR(dev); + + retval = gb_protocol_register(&raw_protocol); + if (retval) + goto error_gb; + + return 0; + +error_gb: + unregister_chrdev_region(dev, NUM_MINORS); +error_chrdev: + class_destroy(raw_class); +error_class: + return retval; +} + +static void __exit raw_exit(void) +{ + gb_protocol_deregister(&raw_protocol); + unregister_chrdev_region(MKDEV(raw_major, 0), NUM_MINORS); + class_destroy(raw_class); +} + +module_init(raw_init); +module_exit(raw_exit); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 4f8ab1105da2f13ae52dc2f2c31fb7bea1301793 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 16:31:19 +0530 Subject: greybus: raw: include uaccess.h to fix warning This is what I get over mainline: greybus/raw.c: In function 'gb_raw_send': greybus/raw.c:153:2: error: implicit declaration of function 'copy_from_user' [-Werror=implicit-function-declaration] if (copy_from_user(&request->data[0], data, len)) { ^ greybus/raw.c: In function 'raw_read': greybus/raw.c:305:2: error: implicit declaration of function 'copy_to_user' [-Werror=implicit-function-declaration] if (copy_to_user(buf, &raw_data->data[0], raw_data->len)) { ^ Fix this by including uaccess.h. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/raw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index eb43acc91e66..515c1877bc7f 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "greybus.h" -- cgit v1.2.3-59-g8ed1b From 8de925b973263694edcb13aa30f2fe0273163c0f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 16:32:28 +0530 Subject: greybus: gpio: fix tab/space mistake Spaces were present in place of tab. Fix that. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 5ed1895e8701..6a4a0f2a835d 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -282,7 +282,7 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) dev_err(chip->dev, "failed to set irq type: %d\n", ret); } - break; + break; default: dev_err(chip->dev, "unsupported irq type: %u\n", type); ret = -EINVAL; -- cgit v1.2.3-59-g8ed1b From 4890f31966a6991e7652f8a365ff59c1ba313c9d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 16:33:57 +0530 Subject: greybus: spi: Move structure definitions into gpbridge.h In order to facilitate re-use of spi structures, split them out of independent files and add them into a shared gpbridge.h This will be a prereq to sharing these headers w/ gbsim. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpbridge.h | 86 ++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/spi.c | 84 +------------------------------------ 2 files changed, 87 insertions(+), 83 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 85cc38522967..c8208054d942 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -403,4 +403,90 @@ struct gb_i2s_send_data_request { }; /* send data has no response at all */ + +/* SPI */ + +/* Version of the Greybus spi protocol we support */ +#define GB_SPI_VERSION_MAJOR 0x00 +#define GB_SPI_VERSION_MINOR 0x01 + +/* Should match up with modes in linux/spi/spi.h */ +#define GB_SPI_MODE_CPHA 0x01 /* clock phase */ +#define GB_SPI_MODE_CPOL 0x02 /* clock polarity */ +#define GB_SPI_MODE_MODE_0 (0|0) /* (original MicroWire) */ +#define GB_SPI_MODE_MODE_1 (0|GB_SPI_MODE_CPHA) +#define GB_SPI_MODE_MODE_2 (GB_SPI_MODE_CPOL|0) +#define GB_SPI_MODE_MODE_3 (GB_SPI_MODE_CPOL|GB_SPI_MODE_CPHA) +#define GB_SPI_MODE_CS_HIGH 0x04 /* chipselect active high? */ +#define GB_SPI_MODE_LSB_FIRST 0x08 /* per-word bits-on-wire */ +#define GB_SPI_MODE_3WIRE 0x10 /* SI/SO signals shared */ +#define GB_SPI_MODE_LOOP 0x20 /* loopback mode */ +#define GB_SPI_MODE_NO_CS 0x40 /* 1 dev/bus, no chipselect */ +#define GB_SPI_MODE_READY 0x80 /* slave pulls low to pause */ + +/* Should match up with flags in linux/spi/spi.h */ +#define GB_SPI_FLAG_HALF_DUPLEX BIT(0) /* can't do full duplex */ +#define GB_SPI_FLAG_NO_RX BIT(1) /* can't do buffer read */ +#define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */ + +/* Greybus spi operation types */ +#define GB_SPI_TYPE_INVALID 0x00 +#define GB_SPI_TYPE_PROTOCOL_VERSION 0x01 +#define GB_SPI_TYPE_MODE 0x02 +#define GB_SPI_TYPE_FLAGS 0x03 +#define GB_SPI_TYPE_BITS_PER_WORD_MASK 0x04 +#define GB_SPI_TYPE_NUM_CHIPSELECT 0x05 +#define GB_SPI_TYPE_TRANSFER 0x06 + +/* mode request has no payload */ +struct gb_spi_mode_response { + __le16 mode; +}; + +/* flags request has no payload */ +struct gb_spi_flags_response { + __le16 flags; +}; + +/* bits-per-word request has no payload */ +struct gb_spi_bpw_response { + __le32 bits_per_word_mask; +}; + +/* num-chipselects request has no payload */ +struct gb_spi_chipselect_response { + __le16 num_chipselect; +}; + +/** + * struct gb_spi_transfer - a read/write buffer pair + * @speed_hz: Select a speed other than the device default for this transfer. If + * 0 the default (from @spi_device) is used. + * @len: size of rx and tx buffers (in bytes) + * @delay_usecs: microseconds to delay after this transfer before (optionally) + * changing the chipselect status, then starting the next transfer or + * completing this spi_message. + * @cs_change: affects chipselect after this transfer completes + * @bits_per_word: select a bits_per_word other than the device default for this + * transfer. If 0 the default (from @spi_device) is used. + */ +struct gb_spi_transfer { + __le32 speed_hz; + __le32 len; + __le16 delay_usecs; + __u8 cs_change; + __u8 bits_per_word; +}; + +struct gb_spi_transfer_request { + __u8 chip_select; /* of the spi device */ + __u8 mode; /* of the spi device */ + __le16 count; + struct gb_spi_transfer transfers[0]; /* trnasfer_count of these */ +}; + +struct gb_spi_transfer_response { + __u8 data[0]; /* inbound data */ +}; + #endif /* __GB_GPBRIDGE_H__ */ diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 731639a810b2..a8fb04ad15b5 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -14,6 +14,7 @@ #include #include "greybus.h" +#include "gpbridge.h" struct gb_spi { struct gb_connection *connection; @@ -43,89 +44,6 @@ struct gb_spi { u16 num_chipselect; }; -/* Version of the Greybus spi protocol we support */ -#define GB_SPI_VERSION_MAJOR 0x00 -#define GB_SPI_VERSION_MINOR 0x01 - -/* Should match up with modes in linux/spi/spi.h */ -#define GB_SPI_MODE_CPHA 0x01 /* clock phase */ -#define GB_SPI_MODE_CPOL 0x02 /* clock polarity */ -#define GB_SPI_MODE_MODE_0 (0|0) /* (original MicroWire) */ -#define GB_SPI_MODE_MODE_1 (0|GB_SPI_MODE_CPHA) -#define GB_SPI_MODE_MODE_2 (GB_SPI_MODE_CPOL|0) -#define GB_SPI_MODE_MODE_3 (GB_SPI_MODE_CPOL|GB_SPI_MODE_CPHA) -#define GB_SPI_MODE_CS_HIGH 0x04 /* chipselect active high? */ -#define GB_SPI_MODE_LSB_FIRST 0x08 /* per-word bits-on-wire */ -#define GB_SPI_MODE_3WIRE 0x10 /* SI/SO signals shared */ -#define GB_SPI_MODE_LOOP 0x20 /* loopback mode */ -#define GB_SPI_MODE_NO_CS 0x40 /* 1 dev/bus, no chipselect */ -#define GB_SPI_MODE_READY 0x80 /* slave pulls low to pause */ - -/* Should match up with flags in linux/spi/spi.h */ -#define GB_SPI_FLAG_HALF_DUPLEX BIT(0) /* can't do full duplex */ -#define GB_SPI_FLAG_NO_RX BIT(1) /* can't do buffer read */ -#define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */ - -/* Greybus spi operation types */ -#define GB_SPI_TYPE_INVALID 0x00 -#define GB_SPI_TYPE_PROTOCOL_VERSION 0x01 -#define GB_SPI_TYPE_MODE 0x02 -#define GB_SPI_TYPE_FLAGS 0x03 -#define GB_SPI_TYPE_BITS_PER_WORD_MASK 0x04 -#define GB_SPI_TYPE_NUM_CHIPSELECT 0x05 -#define GB_SPI_TYPE_TRANSFER 0x06 - -/* mode request has no payload */ -struct gb_spi_mode_response { - __le16 mode; -}; - -/* flags request has no payload */ -struct gb_spi_flags_response { - __le16 flags; -}; - -/* bits-per-word request has no payload */ -struct gb_spi_bpw_response { - __le32 bits_per_word_mask; -}; - -/* num-chipselects request has no payload */ -struct gb_spi_chipselect_response { - __le16 num_chipselect; -}; - -/** - * struct gb_spi_transfer - a read/write buffer pair - * @speed_hz: Select a speed other than the device default for this transfer. If - * 0 the default (from @spi_device) is used. - * @len: size of rx and tx buffers (in bytes) - * @delay_usecs: microseconds to delay after this transfer before (optionally) - * changing the chipselect status, then starting the next transfer or - * completing this spi_message. - * @cs_change: affects chipselect after this transfer completes - * @bits_per_word: select a bits_per_word other than the device default for this - * transfer. If 0 the default (from @spi_device) is used. - */ -struct gb_spi_transfer { - __le32 speed_hz; - __le32 len; - __le16 delay_usecs; - __u8 cs_change; - __u8 bits_per_word; -}; - -struct gb_spi_transfer_request { - __u8 chip_select; /* of the spi device */ - __u8 mode; /* of the spi device */ - __le16 count; - struct gb_spi_transfer transfers[0]; /* trnasfer_count of these */ -}; - -struct gb_spi_transfer_response { - __u8 data[0]; /* inbound data */ -}; - /* Routines to transfer data */ static struct gb_operation * gb_spi_operation_create(struct gb_connection *connection, -- cgit v1.2.3-59-g8ed1b From 8fd46587dbc478168d799fb12828801250086ced Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 16:47:59 +0530 Subject: greybus: protocol: remove leading underscore from _gb_protocol_find() Remove the unnecessary underscore from _gb_protocol_find(). Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index ba4cc0f4ac8c..eed77c3f3a7f 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -16,7 +16,7 @@ static DEFINE_SPINLOCK(gb_protocols_lock); static LIST_HEAD(gb_protocols); /* Caller must hold gb_protocols_lock */ -static struct gb_protocol *_gb_protocol_find(u8 id, u8 major, u8 minor) +static struct gb_protocol *gb_protocol_find(u8 id, u8 major, u8 minor) { struct gb_protocol *protocol; @@ -119,8 +119,8 @@ int gb_protocol_deregister(struct gb_protocol *protocol) return 0; spin_lock_irq(&gb_protocols_lock); - protocol = _gb_protocol_find(protocol->id, protocol->major, - protocol->minor); + protocol = gb_protocol_find(protocol->id, protocol->major, + protocol->minor); if (protocol) { protocol_count = protocol->count; if (!protocol_count) @@ -142,7 +142,7 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) u8 protocol_count; spin_lock_irq(&gb_protocols_lock); - protocol = _gb_protocol_find(id, major, minor); + protocol = gb_protocol_find(id, major, minor); if (protocol) { if (!try_module_get(protocol->owner)) { protocol = NULL; @@ -204,7 +204,7 @@ void gb_protocol_put(struct gb_protocol *protocol) minor = protocol->minor; spin_lock_irq(&gb_protocols_lock); - protocol = _gb_protocol_find(id, major, minor); + protocol = gb_protocol_find(id, major, minor); if (protocol) { protocol_count = protocol->count; if (protocol_count) -- cgit v1.2.3-59-g8ed1b From 12eba9f8ef4e71eed17b08b89835c758c2a0c473 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 16:48:00 +0530 Subject: greybus: connection: name routines consistently Routines should be named this way: gb__. Fix all routines that don't match this. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ++-- drivers/staging/greybus/connection.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 51d505e7017d..e87521ec526d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -14,7 +14,7 @@ static DEFINE_SPINLOCK(gb_connections_lock); -struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, +struct gb_connection *gb_connection_hd_find(struct greybus_host_device *hd, u16 cport_id) { struct gb_connection *connection = NULL; @@ -40,7 +40,7 @@ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, { struct gb_connection *connection; - connection = gb_hd_connection_find(hd, cport_id); + connection = gb_connection_hd_find(hd, cport_id); if (!connection) { dev_err(hd->parent, "nonexistent connection (%zu bytes dropped)\n", length); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 7febf4e03049..93886b4177c9 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -53,7 +53,7 @@ void gb_connection_destroy(struct gb_connection *connection); int gb_connection_init(struct gb_connection *connection); void gb_connection_exit(struct gb_connection *connection); -struct gb_connection *gb_hd_connection_find(struct greybus_host_device *hd, +struct gb_connection *gb_connection_hd_find(struct greybus_host_device *hd, u16 cport_id); void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, -- cgit v1.2.3-59-g8ed1b From 5ddf738e944b652e8d2ddb17f97c0c72ee05667c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 16:48:01 +0530 Subject: greybus: endo: name routines consistently Routines should be named this way: gb__. Fix all routines that don't match this. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 80d9397c565f..42f26f07b52e 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -62,7 +62,7 @@ static const struct attribute_group *endo_groups[] = { NULL, }; -static void greybus_endo_release(struct device *dev) +static void gb_endo_release(struct device *dev) { struct gb_endo *endo = to_gb_endo(dev); @@ -71,7 +71,7 @@ static void greybus_endo_release(struct device *dev) struct device_type greybus_endo_type = { .name = "greybus_endo", - .release = greybus_endo_release, + .release = gb_endo_release, }; @@ -272,7 +272,7 @@ static bool validate_back_ribs(struct greybus_host_device *hd, * Validate the endo-id passed from SVC. Error out if its not a valid Endo, * else return structure representing ribs positions on front and back of Endo. */ -static int gb_validate_endo_id(struct greybus_host_device *hd, +static int gb_endo_validate_id(struct greybus_host_device *hd, struct endo_layout *layout, u16 endo_id) { /* Validate Endo Size */ @@ -434,7 +434,7 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd) return NULL; /* First check if the value supplied is a valid endo id */ - if (gb_validate_endo_id(hd, &endo->layout, endo_id)) + if (gb_endo_validate_id(hd, &endo->layout, endo_id)) goto free_endo; endo->id = endo_id; -- cgit v1.2.3-59-g8ed1b From 51b5d8d783fe8ee6b272f09ef645747e53166c7f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 17:33:51 +0530 Subject: greybus: interface: name routines consistently Routines should be named this way: gb__. Fix all routines that don't match this. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 4 ++-- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/interface.c | 12 ++++++------ drivers/staging/greybus/interface.h | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 1dc13396a567..843ddc24f41c 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -175,7 +175,7 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, return; } dev_dbg(hd->parent, "interface id %d added\n", interface_id); - gb_add_interface(hd, interface_id, hotplug->data, + gb_interface_add(hd, interface_id, hotplug->data, payload_length - 0x02); break; @@ -189,7 +189,7 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, return; } dev_dbg(hd->parent, "interface id %d removed\n", interface_id); - gb_remove_interface(hd, interface_id); + gb_interface_remove(hd, interface_id); break; default: diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index db1da8a37787..45fa4c3dfd2b 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -215,7 +215,7 @@ void greybus_remove_hd(struct greybus_host_device *hd) * with this host controller before freeing the memory associated with * the host controller. */ - gb_remove_interfaces(hd); + gb_interfaces_remove(hd); gb_endo_remove(hd->endo); kref_put_mutex(&hd->kref, free_hd, &hd_mutex); } diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 28b3c4fccf24..7a4c7dc2e941 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -54,7 +54,7 @@ struct gb_interface *gb_interface_find(struct greybus_host_device *hd, return NULL; } -static void greybus_interface_release(struct device *dev) +static void gb_interface_release(struct device *dev) { struct gb_interface *intf = to_gb_interface(dev); @@ -63,7 +63,7 @@ static void greybus_interface_release(struct device *dev) struct device_type greybus_interface_type = { .name = "greybus_interface", - .release = greybus_interface_release, + .release = gb_interface_release, }; /* @@ -161,12 +161,12 @@ static void gb_interface_destroy(struct gb_interface *intf) } /** - * gb_add_interface + * gb_interface_add * * Pass in a buffer that _should_ contain a Greybus manifest * and register a greybus device structure with the kernel core. */ -void gb_add_interface(struct greybus_host_device *hd, u8 interface_id, u8 *data, +void gb_interface_add(struct greybus_host_device *hd, u8 interface_id, u8 *data, int size) { struct gb_interface *intf; @@ -201,7 +201,7 @@ err_parse: gb_interface_destroy(intf); } -void gb_remove_interface(struct greybus_host_device *hd, u8 interface_id) +void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id) { struct gb_interface *intf = gb_interface_find(hd, interface_id); @@ -212,7 +212,7 @@ void gb_remove_interface(struct greybus_host_device *hd, u8 interface_id) interface_id); } -void gb_remove_interfaces(struct greybus_host_device *hd) +void gb_interfaces_remove(struct greybus_host_device *hd) { struct gb_interface *intf, *temp; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 9ee5b551029e..88a7a8056806 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -47,10 +47,10 @@ static inline void *gb_interface_get_drvdata(struct gb_interface *intf) struct gb_interface *gb_interface_find(struct greybus_host_device *hd, u8 interface_id); -void gb_add_interface(struct greybus_host_device *hd, u8 interface_id, u8 *data, +void gb_interface_add(struct greybus_host_device *hd, u8 interface_id, u8 *data, int size); -void gb_remove_interface(struct greybus_host_device *hd, u8 interface_id); -void gb_remove_interfaces(struct greybus_host_device *hd); +void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id); +void gb_interfaces_remove(struct greybus_host_device *hd); #endif /* __INTERFACE_H */ -- cgit v1.2.3-59-g8ed1b From c16b63e851e1100e670ab47a094e8c414996e6f9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 16:48:04 +0530 Subject: greybus: module: name routines consistently Routines should be named this way: gb__. Fix all routines that don't match this. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 8ed96a8ba26d..07b7e444d766 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -71,7 +71,7 @@ static struct attribute *module_attrs[] = { }; ATTRIBUTE_GROUPS(module); -static void greybus_module_release(struct device *dev) +static void gb_module_release(struct device *dev) { struct gb_module *module = to_gb_module(dev); @@ -80,7 +80,7 @@ static void greybus_module_release(struct device *dev) struct device_type greybus_module_type = { .name = "greybus_module", - .release = greybus_module_release, + .release = gb_module_release, }; struct module_find { -- cgit v1.2.3-59-g8ed1b From 8ba2522fa8397c894c2d42ed3461e23862ea6a45 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 16:54:22 +0530 Subject: greybus: raw: move module_{init|exit} to the end of functions This is what coding guidelines say. Lets do it. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/raw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index 515c1877bc7f..d93d052a8a7e 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -357,6 +357,7 @@ error_chrdev: error_class: return retval; } +module_init(raw_init); static void __exit raw_exit(void) { @@ -364,8 +365,6 @@ static void __exit raw_exit(void) unregister_chrdev_region(MKDEV(raw_major, 0), NUM_MINORS); class_destroy(raw_class); } - -module_init(raw_init); module_exit(raw_exit); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From f5db53b4c764ca4d1b1f1ca2bea5e276fc3befe1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 16:56:46 +0530 Subject: greybus: gpbridge: rename gpb.c to gpbridge.c That's what followed for .h, etc.. Rename for better consistency. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/gpb.c | 98 -------------------------------------- drivers/staging/greybus/gpbridge.c | 98 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 99 deletions(-) delete mode 100644 drivers/staging/greybus/gpb.c create mode 100644 drivers/staging/greybus/gpbridge.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index e636fe01f033..e5cae29d5a32 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -10,7 +10,7 @@ greybus-y := core.o \ protocol.o \ operation.o -gb-phy-y := gpb.o \ +gb-phy-y := gpbridge.o \ sdio.o \ uart.o \ pwm.o \ diff --git a/drivers/staging/greybus/gpb.c b/drivers/staging/greybus/gpb.c deleted file mode 100644 index 2324270e5c55..000000000000 --- a/drivers/staging/greybus/gpb.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Greybus GP Bridge driver - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include "greybus.h" - - -static int __init gpbridge_init(void) -{ - if (gb_gpio_protocol_init()) { - pr_err("error initializing gpio protocol\n"); - goto error_gpio; - } - if (gb_pwm_protocol_init()) { - pr_err("error initializing pwm protocol\n"); - goto error_pwm; - } - if (gb_uart_protocol_init()) { - pr_err("error initializing uart protocol\n"); - goto error_uart; - } - if (gb_sdio_protocol_init()) { - pr_err("error initializing sdio protocol\n"); - goto error_sdio; - } - if (gb_usb_protocol_init()) { - pr_err("error initializing usb protocol\n"); - goto error_usb; - } - if (gb_i2c_protocol_init()) { - pr_err("error initializing i2c protocol\n"); - goto error_i2c; - } - if (gb_spi_protocol_init()) { - pr_err("error initializing spi protocol\n"); - goto error_spi; - } - if (gb_hid_protocol_init()) { - pr_err("error initializing hid protocol\n"); - goto error_hid; - } - if (gb_audio_protocol_init()) { - pr_err("error initializing audio protocols\n"); - goto error_audio; - } - - return 0; - -error_audio: - gb_hid_protocol_exit(); -error_hid: - gb_spi_protocol_exit(); -error_spi: - gb_i2c_protocol_exit(); -error_i2c: - gb_usb_protocol_exit(); -error_usb: - gb_sdio_protocol_exit(); -error_sdio: - gb_uart_protocol_exit(); -error_uart: - gb_pwm_protocol_exit(); -error_pwm: - gb_gpio_protocol_exit(); -error_gpio: - return -EPROTO; -} -module_init(gpbridge_init); - -static void __exit gpbridge_exit(void) -{ - gb_audio_protocol_exit(); - gb_hid_protocol_exit(); - gb_spi_protocol_exit(); - gb_i2c_protocol_exit(); - gb_usb_protocol_exit(); - gb_sdio_protocol_exit(); - gb_uart_protocol_exit(); - gb_pwm_protocol_exit(); - gb_gpio_protocol_exit(); -} -module_exit(gpbridge_exit); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c new file mode 100644 index 000000000000..2324270e5c55 --- /dev/null +++ b/drivers/staging/greybus/gpbridge.c @@ -0,0 +1,98 @@ +/* + * Greybus GP Bridge driver + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "greybus.h" + + +static int __init gpbridge_init(void) +{ + if (gb_gpio_protocol_init()) { + pr_err("error initializing gpio protocol\n"); + goto error_gpio; + } + if (gb_pwm_protocol_init()) { + pr_err("error initializing pwm protocol\n"); + goto error_pwm; + } + if (gb_uart_protocol_init()) { + pr_err("error initializing uart protocol\n"); + goto error_uart; + } + if (gb_sdio_protocol_init()) { + pr_err("error initializing sdio protocol\n"); + goto error_sdio; + } + if (gb_usb_protocol_init()) { + pr_err("error initializing usb protocol\n"); + goto error_usb; + } + if (gb_i2c_protocol_init()) { + pr_err("error initializing i2c protocol\n"); + goto error_i2c; + } + if (gb_spi_protocol_init()) { + pr_err("error initializing spi protocol\n"); + goto error_spi; + } + if (gb_hid_protocol_init()) { + pr_err("error initializing hid protocol\n"); + goto error_hid; + } + if (gb_audio_protocol_init()) { + pr_err("error initializing audio protocols\n"); + goto error_audio; + } + + return 0; + +error_audio: + gb_hid_protocol_exit(); +error_hid: + gb_spi_protocol_exit(); +error_spi: + gb_i2c_protocol_exit(); +error_i2c: + gb_usb_protocol_exit(); +error_usb: + gb_sdio_protocol_exit(); +error_sdio: + gb_uart_protocol_exit(); +error_uart: + gb_pwm_protocol_exit(); +error_pwm: + gb_gpio_protocol_exit(); +error_gpio: + return -EPROTO; +} +module_init(gpbridge_init); + +static void __exit gpbridge_exit(void) +{ + gb_audio_protocol_exit(); + gb_hid_protocol_exit(); + gb_spi_protocol_exit(); + gb_i2c_protocol_exit(); + gb_usb_protocol_exit(); + gb_sdio_protocol_exit(); + gb_uart_protocol_exit(); + gb_pwm_protocol_exit(); + gb_gpio_protocol_exit(); +} +module_exit(gpbridge_exit); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 27551b1d5f9bc99881915adc558c2c6113846a40 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 17:20:09 +0530 Subject: greybus: protocol: Add gb_gpbridge_protocol_driver() There are many gpbridge protocol drivers that need gb_protocol_driver() without the module_init/exit() lines. Lets create one for them. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 7958802f2629..95e5b68915ee 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -90,6 +90,7 @@ extern void gb_hid_protocol_exit(void); extern int gb_audio_protocol_init(void); extern void gb_audio_protocol_exit(void); +/* __protocol: Pointer to struct gb_protocol */ #define gb_protocol_driver(__protocol) \ static int __init protocol_init(void) \ { \ @@ -102,6 +103,17 @@ static void __exit protocol_exit(void) \ } \ module_exit(protocol_exit) +/* __protocol: string matching name of struct gb_protocol */ +#define gb_gpbridge_protocol_driver(__protocol) \ +int __init gb_##__protocol##_init(void) \ +{ \ + return gb_protocol_register(&__protocol); \ +} \ +void __exit gb_##__protocol##_exit(void) \ +{ \ + gb_protocol_deregister(&__protocol); \ +} \ + /* * Macro to create get_version() routine for protocols * @__device: name of the device struct -- cgit v1.2.3-59-g8ed1b From bdac599c70409bbf48e77971e2b9ecc2a66ef151 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 May 2015 17:20:10 +0530 Subject: greybus: Use gb_gpbridge_protocol_init() Start using gb_gpbridge_protocol_init() in gpbridge drivers. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 10 +--------- drivers/staging/greybus/hid.c | 10 +--------- drivers/staging/greybus/i2c.c | 10 +--------- drivers/staging/greybus/pwm.c | 10 +--------- drivers/staging/greybus/sdio.c | 10 +--------- drivers/staging/greybus/spi.c | 10 +--------- drivers/staging/greybus/uart.c | 10 +--------- drivers/staging/greybus/usb.c | 10 +--------- 8 files changed, 8 insertions(+), 72 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 6a4a0f2a835d..871f2d0558b0 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -672,12 +672,4 @@ static struct gb_protocol gpio_protocol = { .request_recv = gb_gpio_request_recv, }; -int gb_gpio_protocol_init(void) -{ - return gb_protocol_register(&gpio_protocol); -} - -void gb_gpio_protocol_exit(void) -{ - gb_protocol_deregister(&gpio_protocol); -} +gb_gpbridge_protocol_driver(gpio_protocol); diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 00dc7e57f4a4..2f6e68c1be88 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -529,12 +529,4 @@ static struct gb_protocol hid_protocol = { .request_recv = gb_hid_irq_handler, }; -int gb_hid_protocol_init(void) -{ - return gb_protocol_register(&hid_protocol); -} - -void gb_hid_protocol_exit(void) -{ - gb_protocol_deregister(&hid_protocol); -} +gb_gpbridge_protocol_driver(hid_protocol); diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 6fdbf1167ecb..14fdea184c39 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -358,12 +358,4 @@ static struct gb_protocol i2c_protocol = { .request_recv = NULL, /* no incoming requests */ }; -int gb_i2c_protocol_init(void) -{ - return gb_protocol_register(&i2c_protocol); -} - -void gb_i2c_protocol_exit(void) -{ - gb_protocol_deregister(&i2c_protocol); -} +gb_gpbridge_protocol_driver(i2c_protocol); diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 7495716070c7..7f675bfed1d7 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -246,12 +246,4 @@ static struct gb_protocol pwm_protocol = { .request_recv = NULL, /* no incoming requests */ }; -int gb_pwm_protocol_init(void) -{ - return gb_protocol_register(&pwm_protocol); -} - -void gb_pwm_protocol_exit(void) -{ - gb_protocol_deregister(&pwm_protocol); -} +gb_gpbridge_protocol_driver(pwm_protocol); diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index d324846d09ab..9a0348c62478 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -88,12 +88,4 @@ static struct gb_protocol sdio_protocol = { .request_recv = NULL, /* no incoming requests */ }; -int gb_sdio_protocol_init(void) -{ - return gb_protocol_register(&sdio_protocol); -} - -void gb_sdio_protocol_exit(void) -{ - gb_protocol_deregister(&sdio_protocol); -} +gb_gpbridge_protocol_driver(sdio_protocol); diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index a8fb04ad15b5..430c3ad70a5a 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -348,12 +348,4 @@ static struct gb_protocol spi_protocol = { .request_recv = NULL, }; -int gb_spi_protocol_init(void) -{ - return gb_protocol_register(&spi_protocol); -} - -void gb_spi_protocol_exit(void) -{ - gb_protocol_deregister(&spi_protocol); -} +gb_gpbridge_protocol_driver(spi_protocol); diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 35ab3cad5714..47de9698f50c 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -745,12 +745,4 @@ static struct gb_protocol uart_protocol = { .request_recv = NULL, /* FIXME we have 2 types of requests!!! */ }; -int gb_uart_protocol_init(void) -{ - return gb_protocol_register(&uart_protocol); -} - -void gb_uart_protocol_exit(void) -{ - gb_protocol_deregister(&uart_protocol); -} +gb_gpbridge_protocol_driver(uart_protocol); diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index ea9784171352..609b7cc66768 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -362,12 +362,4 @@ static struct gb_protocol usb_protocol = { .request_recv = NULL, /* FIXME we have requests!!! */ }; -int gb_usb_protocol_init(void) -{ - return gb_protocol_register(&usb_protocol); -} - -void gb_usb_protocol_exit(void) -{ - gb_protocol_deregister(&usb_protocol); -} +gb_gpbridge_protocol_driver(usb_protocol); -- cgit v1.2.3-59-g8ed1b From fed78bf6a2452cfb9ef997779c73175a1933206c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 May 2015 11:22:41 +0200 Subject: greybus: remove unnecessary greybus.h includes Remove unnecessary greybus.h include from header files. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.h | 2 -- drivers/staging/greybus/protocol.h | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 93886b4177c9..8992b75394a5 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -12,8 +12,6 @@ #include -#include "greybus.h" - enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, GB_CONNECTION_STATE_DISABLED = 1, diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 95e5b68915ee..8bc3f769c398 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -10,8 +10,7 @@ #ifndef __PROTOCOL_H #define __PROTOCOL_H -#include "greybus.h" - +struct gb_connection; struct gb_operation; /* version request has no payload */ -- cgit v1.2.3-59-g8ed1b From d966820f2c28c621d4912a8277fa43a69242e8d5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 May 2015 11:22:42 +0200 Subject: greybus: remove unused prototypes from header Remove a couple of unused function prototypes from the greybus header file. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 109727f9f7c2..0a25d0c45e37 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -155,9 +155,6 @@ struct dentry *gb_debugfs_get(void); extern struct bus_type greybus_bus_type; -int gb_uart_device_init(struct gb_connection *connection); -void gb_uart_device_exit(struct gb_connection *connection); - int svc_set_route_send(struct gb_bundle *bundle, struct greybus_host_device *hd); -- cgit v1.2.3-59-g8ed1b From d933667a1e44be0d3fa137ec31686303c42c16fc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 May 2015 11:22:43 +0200 Subject: greybus: fix host-device buffer constraints Host devices impose buffer-size constraints on Greybus core which are taken into account when allocating messages. Make sure to verify these constraints when the host device is allocated, rather than when the first message is allocated. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 14 +++++++++++++- drivers/staging/greybus/es1.c | 21 +-------------------- drivers/staging/greybus/es2.c | 21 +-------------------- drivers/staging/greybus/greybus.h | 3 ++- drivers/staging/greybus/operation.c | 15 --------------- drivers/staging/greybus/operation.h | 2 ++ 6 files changed, 19 insertions(+), 57 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 45fa4c3dfd2b..e32d6c41e87e 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -173,7 +173,8 @@ static void free_hd(struct kref *kref) } struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver, - struct device *parent) + struct device *parent, + size_t buffer_size_max) { struct greybus_host_device *hd; @@ -187,6 +188,16 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver return NULL; } + /* + * Make sure to never allocate messages larger than what the Greybus + * protocol supports. + */ + if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) { + dev_warn(parent, "limiting buffer size to %u\n", + GB_OPERATION_MESSAGE_SIZE_MAX); + buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX; + } + hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL); if (!hd) return NULL; @@ -197,6 +208,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver INIT_LIST_HEAD(&hd->interfaces); INIT_LIST_HEAD(&hd->connections); ida_init(&hd->cport_id_map); + hd->buffer_size_max = buffer_size_max; hd->endo = gb_endo_create(hd); if (!hd->endo) { diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 4bba5d558e84..e0fae26d8ba3 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -98,22 +98,6 @@ static void cport_out_callback(struct urb *urb); static void usb_log_enable(struct es1_ap_dev *es1); static void usb_log_disable(struct es1_ap_dev *es1); -/* - * Buffer constraints for the host driver. - * - * A "buffer" is used to hold data to be transferred for Greybus by - * the host driver. A buffer is represented by a "buffer pointer", - * which defines a region of memory used by the host driver for - * transferring the data. When Greybus allocates a buffer, it must - * do so subject to the constraints associated with the host driver. - * - * size_max: The maximum size of a buffer - */ -static void hd_buffer_constraints(struct greybus_host_device *hd) -{ - hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; -} - #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) { @@ -571,15 +555,12 @@ static int ap_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); - hd = greybus_create_hd(&es1_driver, &udev->dev); + hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); if (!hd) { usb_put_dev(udev); return -ENOMEM; } - /* Fill in the buffer allocation constraints */ - hd_buffer_constraints(hd); - es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index cc73fbd9b77b..05aac3d7686b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -98,22 +98,6 @@ static void cport_out_callback(struct urb *urb); static void usb_log_enable(struct es1_ap_dev *es1); static void usb_log_disable(struct es1_ap_dev *es1); -/* - * Buffer constraints for the host driver. - * - * A "buffer" is used to hold data to be transferred for Greybus by - * the host driver. A buffer is represented by a "buffer pointer", - * which defines a region of memory used by the host driver for - * transferring the data. When Greybus allocates a buffer, it must - * do so subject to the constraints associated with the host driver. - * - * size_max: The maximum size of a buffer - */ -static void hd_buffer_constraints(struct greybus_host_device *hd) -{ - hd->buffer_size_max = ES1_GBUF_MSG_SIZE_MAX; -} - #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) { @@ -571,15 +555,12 @@ static int ap_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); - hd = greybus_create_hd(&es1_driver, &udev->dev); + hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); if (!hd) { usb_put_dev(udev); return -ENOMEM; } - /* Fill in the buffer allocation constraints */ - hd_buffer_constraints(hd); - es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 0a25d0c45e37..dbb4e78cf4c9 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -105,7 +105,8 @@ struct greybus_host_device { }; struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, - struct device *parent); + struct device *parent, + size_t buffer_size_max); void greybus_remove_hd(struct greybus_host_device *hd); struct greybus_driver { diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 1ec930c5cba9..c78ccc09d667 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -17,12 +17,6 @@ /* The default amount of time a request is given to complete */ #define OPERATION_TIMEOUT_DEFAULT 1000 /* milliseconds */ -/* - * XXX This needs to be coordinated with host driver parameters - * XXX May need to reduce to allow for message header within a page - */ -#define GB_OPERATION_MESSAGE_SIZE_MAX 4096 - static struct kmem_cache *gb_operation_cache; static struct kmem_cache *gb_message_cache; @@ -282,12 +276,6 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, struct gb_operation_msg_hdr *header; size_t message_size = payload_size + sizeof(*header); - if (hd->buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) { - pr_warn("limiting buffer size to %u\n", - GB_OPERATION_MESSAGE_SIZE_MAX); - hd->buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX; - } - if (message_size > hd->buffer_size_max) { pr_warn("requested message size too big (%zu > %zu)\n", message_size, hd->buffer_size_max); @@ -936,9 +924,6 @@ EXPORT_SYMBOL_GPL(gb_operation_sync); int gb_operation_init(void) { - BUILD_BUG_ON(GB_OPERATION_MESSAGE_SIZE_MAX > - U16_MAX - sizeof(struct gb_operation_msg_hdr)); - gb_message_cache = kmem_cache_create("gb_message_cache", sizeof(struct gb_message), 0, 0, NULL); if (!gb_message_cache) diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 82b8fe57f8a0..3b02db5cd08b 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -69,6 +69,8 @@ struct gb_operation_msg_hdr { __u8 pad[2]; /* must be zero (ignore when read) */ } __aligned(sizeof(u64)); +#define GB_OPERATION_MESSAGE_SIZE_MAX 4096 + /* * Protocol code should only examine the payload and payload_size * fields. All other fields are intended to be private to the -- cgit v1.2.3-59-g8ed1b From c38cf10bbe35ba6fa10437809585fd4d0b3b604d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 May 2015 11:22:44 +0200 Subject: greybus: operation: allow maximum-sized messages Increase the maximum allowed buffer size to the full 16-bit range supported by the protocol. Note that host devices will generally use smaller buffers than the maximum. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 3b02db5cd08b..1edd512d9a76 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -69,7 +69,7 @@ struct gb_operation_msg_hdr { __u8 pad[2]; /* must be zero (ignore when read) */ } __aligned(sizeof(u64)); -#define GB_OPERATION_MESSAGE_SIZE_MAX 4096 +#define GB_OPERATION_MESSAGE_SIZE_MAX U16_MAX /* * Protocol code should only examine the payload and payload_size -- cgit v1.2.3-59-g8ed1b From 8e929a8230794c4b034d0f4b658aa03702f3800d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 May 2015 11:22:45 +0200 Subject: greybus: core: add lower-limit for host-device buffers Make sure we never end up with a host device with maximum buffer size smaller than the shortest Greybus message. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 5 +++++ drivers/staging/greybus/operation.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index e32d6c41e87e..91dcc5bb158c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -188,6 +188,11 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver return NULL; } + if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) { + dev_err(parent, "greybus host-device buffers too small\n"); + return NULL; + } + /* * Make sure to never allocate messages larger than what the Greybus * protocol supports. diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 1edd512d9a76..740aacc3a3db 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -69,6 +69,7 @@ struct gb_operation_msg_hdr { __u8 pad[2]; /* must be zero (ignore when read) */ } __aligned(sizeof(u64)); +#define GB_OPERATION_MESSAGE_SIZE_MIN sizeof(struct gb_operation_msg_hdr) #define GB_OPERATION_MESSAGE_SIZE_MAX U16_MAX /* -- cgit v1.2.3-59-g8ed1b From d52b35f6b62c8b9e1b7e10010daa0370f8252f1d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 May 2015 11:22:46 +0200 Subject: greybus: operation: add helper to retrieve max payload size Add helper to retrieve the maximum payload size for operations on a specific connection. Note that the helper is not inlined due to how the header files are currently organised, but it is not expected to be called after a connection has been initialised either. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 8 ++++++++ drivers/staging/greybus/operation.h | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c78ccc09d667..f595b97fa900 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -504,6 +504,14 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, } EXPORT_SYMBOL_GPL(gb_operation_create); +size_t gb_operation_get_payload_size_max(struct gb_connection *connection) +{ + struct greybus_host_device *hd = connection->hd; + + return hd->buffer_size_max - sizeof(struct gb_operation_msg_hdr); +} +EXPORT_SYMBOL_GPL(gb_operation_get_payload_size_max); + static struct gb_operation * gb_operation_create_incoming(struct gb_connection *connection, u16 id, u8 type, void *data, size_t size) diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 740aacc3a3db..0199976c9b5f 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -130,6 +130,7 @@ void gb_connection_recv(struct gb_connection *connection, int gb_operation_result(struct gb_operation *operation); +size_t gb_operation_get_payload_size_max(struct gb_connection *connection); struct gb_operation *gb_operation_create(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size); -- cgit v1.2.3-59-g8ed1b From 8d346df0620eaeddfa1333d413596898fa7e576a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 May 2015 11:47:24 +0200 Subject: greybus: gpbridge: fix section mismatches Fix section mismatches introduced by b27227ce93c0 ("greybus: Use gb_gpbridge_protocol_init()"), which added __exit annotation to gpbridge-protocol exit functions that are called in the error path of gpbridge_init, which lives in the init section. This triggered the following modpost warning: WARNING: modpost: Found 8 section mismatch(es). Fixes: 16b33d100bff ("protocol: Add gb_gpbridge_protocol_driver()") Fixes: b27227ce93c0 ("greybus: Use gb_gpbridge_protocol_init()") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 8bc3f769c398..f6739f3332cd 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -108,7 +108,7 @@ int __init gb_##__protocol##_init(void) \ { \ return gb_protocol_register(&__protocol); \ } \ -void __exit gb_##__protocol##_exit(void) \ +void gb_##__protocol##_exit(void) \ { \ gb_protocol_deregister(&__protocol); \ } \ -- cgit v1.2.3-59-g8ed1b From 0e995aab21dc4a74a4e2daca5df027937cb0b975 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:54:27 -0700 Subject: greybus: gb-audio: Set clock edges to match rt5647 codec requirements The rt5647 codec on speaker and mediabar modules require that the following clock edge settings: ll_wclk_change_edge GB_I2S_MGMT_EDGE_FALLING ll_wclk_tx_edge GB_I2S_MGMT_EDGE_RISING ll_wclk_rx_edge GB_I2S_MGMT_EDGE_FALLING (Those are the setting that work, at least). So make the Greybus audio driver configure the GPBridge with those settings. Signed-off-by: Mark A. Greer Tested-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-gb-cmds.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index daa3181c9422..ffecf3abee5e 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -134,8 +134,8 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) (cfg->ll_wclk_role & GB_I2S_MGMT_ROLE_MASTER) && (cfg->ll_wclk_polarity & GB_I2S_MGMT_POLARITY_NORMAL) && (cfg->ll_wclk_change_edge & GB_I2S_MGMT_EDGE_FALLING) && - (cfg->ll_wclk_tx_edge & GB_I2S_MGMT_EDGE_FALLING) && - (cfg->ll_wclk_rx_edge & GB_I2S_MGMT_EDGE_RISING) && + (cfg->ll_wclk_tx_edge & GB_I2S_MGMT_EDGE_RISING) && + (cfg->ll_wclk_rx_edge & GB_I2S_MGMT_EDGE_FALLING) && (cfg->ll_data_offset == 1)) break; } @@ -153,9 +153,9 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) set_cfg.config.ll_bclk_role = GB_I2S_MGMT_ROLE_MASTER; set_cfg.config.ll_wclk_role = GB_I2S_MGMT_ROLE_MASTER; set_cfg.config.ll_wclk_polarity = GB_I2S_MGMT_POLARITY_NORMAL; - set_cfg.config.ll_wclk_change_edge = GB_I2S_MGMT_EDGE_RISING; - set_cfg.config.ll_wclk_tx_edge = GB_I2S_MGMT_EDGE_FALLING; - set_cfg.config.ll_wclk_rx_edge = GB_I2S_MGMT_EDGE_RISING; + set_cfg.config.ll_wclk_change_edge = GB_I2S_MGMT_EDGE_FALLING; + set_cfg.config.ll_wclk_tx_edge = GB_I2S_MGMT_EDGE_RISING; + set_cfg.config.ll_wclk_rx_edge = GB_I2S_MGMT_EDGE_FALLING; ret = gb_i2s_mgmt_set_configuration(connection, &set_cfg); if (ret) { -- cgit v1.2.3-59-g8ed1b From 555a0645b17114c559a2079ed64a8edb079b2339 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:54:48 -0700 Subject: greybus: gb-audio: Allocate space for 20 configurations The greybus code requires that an operation's response buffer be the exact size of the response; however, the size of the response to the GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS operation is unknown. To fix this, an extension to the I2S specification is required. In the meantime, set the number of configurations returned to 20 because that is how many configurations will be returned (using "insider knowledge" of the firmware). Signed-off-by: Mark A. Greer Tested-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index 6a337d112976..fb88a48d1c0b 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -20,7 +20,7 @@ #define CONFIG_SAMPLES_PER_MSG 48L #define CONFIG_PERIOD_NS 1000000 /* send msg every 1ms */ -#define CONFIG_COUNT_MAX 32 +#define CONFIG_COUNT_MAX 20 #define CONFIG_I2S_REMOTE_DATA_CPORT 7 /* XXX shouldn't be hardcoded...*/ #define RT5647_SLAVE_ADDR 0x1b /* from toshiba/quanta code */ -- cgit v1.2.3-59-g8ed1b From 34aa7e1cadef05f9494ff1f0c4977e762d9f32fa Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:56:56 -0700 Subject: greybus: gb-audio: Clean up codec name generation Instead of using the fixed suffix, '6-001b', in the codec name, generate it from the I2S adapter number and I2C address of the codec. Signed-off-by: Mark A. Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio.c | 6 +++++- drivers/staging/greybus/audio.h | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 232ba94b5b88..3e8c24275cc3 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -19,6 +19,9 @@ #define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data" #define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt" +#define RT5647_I2C_ADAPTER_NR 6 +#define RT5647_I2C_ADDR 0x1b + /* * gb_snd management functions */ @@ -111,7 +114,8 @@ static struct asoc_simple_card_info *setup_card_info(int device_count) #endif #if USE_RT5645 obj->card_info.daifmt = GB_FMTS; - sprintf(obj->codec_name, "rt5645.%s", "6-001b"); /* XXX do i2c bus addr dynamically */ + sprintf(obj->codec_name, "rt5645.%d-%04x", RT5647_I2C_ADAPTER_NR, + RT5647_I2C_ADDR); obj->card_info.codec_dai.name = "rt5645-aif1"; obj->card_info.codec_dai.fmt = SND_SOC_DAIFMT_CBM_CFM; obj->card_info.codec_dai.sysclk = 12288000; diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index fb88a48d1c0b..5095df9096f6 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -22,7 +22,6 @@ #define CONFIG_COUNT_MAX 20 #define CONFIG_I2S_REMOTE_DATA_CPORT 7 /* XXX shouldn't be hardcoded...*/ -#define RT5647_SLAVE_ADDR 0x1b /* from toshiba/quanta code */ /* Switch between dummy spdif and jetson rt5645 codec */ #define USE_RT5645 0 -- cgit v1.2.3-59-g8ed1b From 4c739e3adad46aded0428144681fdaa6437b9e84 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:56:57 -0700 Subject: greybus: gb-audio: Fix DAI formats and master/slave settings Set the various DAI formats so the bridge on the module is the master of all clocks and the codec is the slave. The only DAI protocol currently supported is I2S. Signed-off-by: Mark A. Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 3e8c24275cc3..03196a044694 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -110,14 +110,15 @@ static struct asoc_simple_card_info *setup_card_info(int device_count) obj->card_info.platform = obj->platform_name; obj->card_info.cpu_dai.name = obj->dai_name; #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) - obj->card_info.cpu_dai.fmt = GB_FMTS; + obj->card_info.cpu_dai.fmt = SND_SOC_DAIFMT_CBM_CFM; #endif #if USE_RT5645 - obj->card_info.daifmt = GB_FMTS; + obj->card_info.daifmt = SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S; sprintf(obj->codec_name, "rt5645.%d-%04x", RT5647_I2C_ADAPTER_NR, RT5647_I2C_ADDR); obj->card_info.codec_dai.name = "rt5645-aif1"; - obj->card_info.codec_dai.fmt = SND_SOC_DAIFMT_CBM_CFM; + obj->card_info.codec_dai.fmt = SND_SOC_DAIFMT_CBS_CFS; obj->card_info.codec_dai.sysclk = 12288000; #else sprintf(obj->codec_name, "spdif-dit"); -- cgit v1.2.3-59-g8ed1b From 827e27e8fe3cf1ce29343ed87e042075194ac6ca Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:56:58 -0700 Subject: greybus: gb-audio: cpu_dai.fmt does not exist in v4.1 The asoc_simple_dai structure does not contain the 'fmt' member in Linux kernel version v4.1 and later so only build code that uses it when the kernel version is earlier than v4.1. Signed-off-by: Mark A. Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 03196a044694..d4d1eff2d099 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -118,7 +118,9 @@ static struct asoc_simple_card_info *setup_card_info(int device_count) sprintf(obj->codec_name, "rt5645.%d-%04x", RT5647_I2C_ADAPTER_NR, RT5647_I2C_ADDR); obj->card_info.codec_dai.name = "rt5645-aif1"; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) obj->card_info.codec_dai.fmt = SND_SOC_DAIFMT_CBS_CFS; +#endif obj->card_info.codec_dai.sysclk = 12288000; #else sprintf(obj->codec_name, "spdif-dit"); -- cgit v1.2.3-59-g8ed1b From 415b83111caac7cd1b90e0df104f81bae8e6e4dd Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:56:59 -0700 Subject: greybus: gb-audio: Add I2C device for rt5647 codec Add the I2C device node for the rt5647 codec. Eventually, this will be done automatically somewhere else but for now its done in the audio driver. Signed-off-by: Mark A. Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio.c | 20 +++++++++++++++++++- drivers/staging/greybus/audio.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index d4d1eff2d099..1cc7c04d562b 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -157,6 +156,9 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) struct gb_snd *snd_dev; struct platform_device *codec, *dai; struct asoc_simple_card_info *simple_card; +#if USE_RT5645 + struct i2c_board_info rt5647_info; +#endif unsigned long flags; int ret; @@ -219,6 +221,18 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) goto out_get_ver; } +#if USE_RT5645 + rt5647_info.addr = RT5647_I2C_ADDR; + strlcpy(rt5647_info.type, "rt5647", I2C_NAME_SIZE); + + snd_dev->rt5647 = i2c_new_device(i2c_get_adapter(RT5647_I2C_ADAPTER_NR), + &rt5647_info); + if (!snd_dev->rt5647) { + pr_err("can't create rt5647 i2c device\n"); + goto out_get_ver; + } +#endif + return 0; out_get_ver: @@ -238,6 +252,10 @@ static void gb_i2s_transmitter_connection_exit(struct gb_connection *connection) snd_dev = (struct gb_snd *)connection->private; +#if USE_RT5645 + i2c_unregister_device(snd_dev->rt5647); +#endif + platform_device_unregister(&snd_dev->card); platform_device_unregister(&snd_dev->cpu_dai); platform_device_unregister(snd_dev->codec); diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index 5095df9096f6..020a8fc1d267 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "greybus.h" @@ -41,6 +42,7 @@ struct gb_snd { struct platform_device cpu_dai; struct platform_device *codec; struct asoc_simple_card_info *simple_card_info; + struct i2c_client *rt5647; struct gb_connection *mgmt_connection; struct gb_connection *i2s_tx_connection; struct gb_connection *i2s_rx_connection; -- cgit v1.2.3-59-g8ed1b From e803cf712c48e2923b89e134adde56a004a99722 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:57:00 -0700 Subject: greybus: gb-audio: Remove useless comment Remove comment about adding start delay since it will be done when support for A/V synchronization is added. Signed-off-by: Mark A. Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-gb-cmds.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index ffecf3abee5e..8a101286d7be 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -170,7 +170,6 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) goto free_get_cfg; } - /* XXX Add start delay here (probably 1ms) */ ret = gb_i2s_mgmt_activate_cport(connection, CONFIG_I2S_REMOTE_DATA_CPORT); if (ret) { -- cgit v1.2.3-59-g8ed1b From f9a4fee7fad79d46ef6a3b0142bed35638c8d620 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:57:01 -0700 Subject: greybus: gb-audio: Activate TX CPort in PCM workqueue Currently, the I2S TX CPort is configured and activated during the Greybus audio initialization. Unfortunately, this prevents the audio driver from ever changing the I2S configuration. To allow the I2S configuration to change according to ASOC requests, move the CPort activation & deactivation to the audio-pcm workqueue. Now, when audio is running but the CPort is not active, it will be activated. When audio is not running and the CPort is active, it will be deactivated. This has the side-effect of sending the first piece of audio data immediately after activating the CPort which is really how it should work anyway. Signed-off-by: Mark A. Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-gb-cmds.c | 11 +---------- drivers/staging/greybus/audio-pcm.c | 24 ++++++++++++++++++++++-- drivers/staging/greybus/audio.c | 10 +--------- drivers/staging/greybus/audio.h | 1 + 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index 8a101286d7be..d625f782b513 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -165,17 +165,8 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) ret = gb_i2s_mgmt_set_samples_per_message(connection, CONFIG_SAMPLES_PER_MSG); - if (ret) { + if (ret) pr_err("set_samples_per_msg failed: %d\n", ret); - goto free_get_cfg; - } - - ret = gb_i2s_mgmt_activate_cport(connection, - CONFIG_I2S_REMOTE_DATA_CPORT); - if (ret) { - pr_err("activate_cport failed: %d\n", ret); - goto free_get_cfg; - } free_get_cfg: kfree(get_cfg); diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c index 92436300b3eb..8eb803a7a40a 100644 --- a/drivers/staging/greybus/audio-pcm.c +++ b/drivers/staging/greybus/audio-pcm.c @@ -32,15 +32,33 @@ static void gb_pcm_work(struct work_struct *work) struct snd_pcm_substream *substream = snd_dev->substream; struct snd_pcm_runtime *runtime = substream->runtime; unsigned int stride, frames, oldptr; - int period_elapsed; + int period_elapsed, ret; char *address; long len; if (!snd_dev) return; - if (!atomic_read(&snd_dev->running)) + if (!atomic_read(&snd_dev->running)) { + if (snd_dev->cport_active) { + ret = gb_i2s_mgmt_deactivate_cport( + snd_dev->mgmt_connection, + CONFIG_I2S_REMOTE_DATA_CPORT); + if (ret) /* XXX Do what else with failure? */ + pr_err("deactivate_cport failed: %d\n", ret); + + snd_dev->cport_active = false; + } + return; + } else if (!snd_dev->cport_active) { + ret = gb_i2s_mgmt_activate_cport(snd_dev->mgmt_connection, + CONFIG_I2S_REMOTE_DATA_CPORT); + if (ret) + pr_err("activate_cport failed: %d\n", ret); + + snd_dev->cport_active = true; + } address = runtime->dma_area + snd_dev->hwptr_done; @@ -88,6 +106,7 @@ static enum hrtimer_restart gb_pcm_timer_function(struct hrtimer *hrtimer) void gb_pcm_hrtimer_start(struct gb_snd *snd_dev) { atomic_set(&snd_dev->running, 1); + queue_work(snd_dev->workqueue, &snd_dev->work); /* Activates CPort */ hrtimer_start(&snd_dev->timer, ns_to_ktime(CONFIG_PERIOD_NS), HRTIMER_MODE_REL); } @@ -96,6 +115,7 @@ void gb_pcm_hrtimer_stop(struct gb_snd *snd_dev) { atomic_set(&snd_dev->running, 0); hrtimer_cancel(&snd_dev->timer); + queue_work(snd_dev->workqueue, &snd_dev->work); /* Deactivates CPort */ } static int gb_pcm_hrtimer_init(struct gb_snd *snd_dev) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 1cc7c04d562b..1057e468d5d4 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -292,13 +292,11 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection) if (!snd_dev->send_data_req_buf) { ret = -ENOMEM; - goto err_deactivate_cport; + goto err_free_snd_dev; } return 0; -err_deactivate_cport: - gb_i2s_mgmt_deactivate_cport(connection, CONFIG_I2S_REMOTE_DATA_CPORT); err_free_snd_dev: gb_free_snd(snd_dev); return ret; @@ -307,12 +305,6 @@ err_free_snd_dev: static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection) { struct gb_snd *snd_dev = (struct gb_snd *)connection->private; - int ret; - - ret = gb_i2s_mgmt_deactivate_cport(connection, - CONFIG_I2S_REMOTE_DATA_CPORT); - if (ret) - pr_err("deactivate_cport failed: %d\n", ret); kfree(snd_dev->send_data_req_buf); snd_dev->send_data_req_buf = NULL; diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index 020a8fc1d267..50a9ebb6612e 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -53,6 +53,7 @@ struct gb_snd { struct snd_pcm_substream *substream; struct hrtimer timer; atomic_t running; + bool cport_active; struct workqueue_struct *workqueue; struct work_struct work; int hwptr_done; -- cgit v1.2.3-59-g8ed1b From 48229e592f95b4d0071f7711bd4c0e54104724e4 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:57:02 -0700 Subject: greybus: gb-audio: Activate TX connection Bundle CPort ID Currently, the audio driver activates & deactivates a predefined CPort ID but that can vary depending on the manifest data of the module. Instead, use the TX connection's Bundle CPort ID which contains the correct CPort ID. Signed-off-by: Mark A. Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-pcm.c | 6 +++--- drivers/staging/greybus/audio.h | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c index 8eb803a7a40a..30030f8d3c22 100644 --- a/drivers/staging/greybus/audio-pcm.c +++ b/drivers/staging/greybus/audio-pcm.c @@ -42,8 +42,8 @@ static void gb_pcm_work(struct work_struct *work) if (!atomic_read(&snd_dev->running)) { if (snd_dev->cport_active) { ret = gb_i2s_mgmt_deactivate_cport( - snd_dev->mgmt_connection, - CONFIG_I2S_REMOTE_DATA_CPORT); + snd_dev->mgmt_connection, + snd_dev->i2s_tx_connection->bundle_cport_id); if (ret) /* XXX Do what else with failure? */ pr_err("deactivate_cport failed: %d\n", ret); @@ -53,7 +53,7 @@ static void gb_pcm_work(struct work_struct *work) return; } else if (!snd_dev->cport_active) { ret = gb_i2s_mgmt_activate_cport(snd_dev->mgmt_connection, - CONFIG_I2S_REMOTE_DATA_CPORT); + snd_dev->i2s_tx_connection->bundle_cport_id); if (ret) pr_err("activate_cport failed: %d\n", ret); diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index 50a9ebb6612e..012c69a3ed83 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -22,7 +22,6 @@ #define CONFIG_PERIOD_NS 1000000 /* send msg every 1ms */ #define CONFIG_COUNT_MAX 20 -#define CONFIG_I2S_REMOTE_DATA_CPORT 7 /* XXX shouldn't be hardcoded...*/ /* Switch between dummy spdif and jetson rt5645 codec */ #define USE_RT5645 0 -- cgit v1.2.3-59-g8ed1b From 6b34099ec326e6c94fc5cf3ce3e1e4a9877c43ea Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:57:03 -0700 Subject: greybus: gb-audio: Set I2S Configuration according to ASOC requests Currently, the audio driver unconditionally sets the I2S configuration to have a sample rate of 48KHz, two channels, 16 bits per channel, in little endian order. Make this more flexible by setting the I2S configuration according to the arguments passed to the PCM 'hw_params' callback. To accomplish this, query for the supported I2S configurations at Greybus protocol init time and save them in the 'snd_dev' structure. When the 'hw_params' callback is called, compare its arguments to the table of supported configurations. If there is a match, set the I2S connection accordingly. Signed-off-by: Mark A. Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-gb-cmds.c | 79 ++++++++++++++++++++------------- drivers/staging/greybus/audio-pcm.c | 15 +++++++ drivers/staging/greybus/audio.c | 12 ++++- drivers/staging/greybus/audio.h | 8 +++- 4 files changed, 80 insertions(+), 34 deletions(-) diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index d625f782b513..73f47d84f1aa 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -89,21 +89,12 @@ int gb_i2s_mgmt_set_samples_per_message( &request, sizeof(request), NULL, 0); } -/* - * XXX This is sort of a generic "setup" function which probably needs - * to be broken up, and tied into the constraints. - * - * I'm on the fence if we should just dictate that we only support - * 48k, 16bit, 2 channel, and avoid doign the whole probe for configurations - * and then picking one. - */ -int gb_i2s_mgmt_setup(struct gb_connection *connection) +int gb_i2s_mgmt_get_cfgs(struct gb_snd *snd_dev, + struct gb_connection *connection) { struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg; - struct gb_i2s_mgmt_set_configuration_request set_cfg; - struct gb_i2s_mgmt_configuration *cfg; size_t size; - int i, ret; + int ret; size = sizeof(*get_cfg) + (CONFIG_COUNT_MAX * sizeof(get_cfg->config[0])); @@ -116,19 +107,48 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) size); if (ret) { pr_err("get_supported_config failed: %d\n", ret); - goto free_get_cfg; + goto err_free_get_cfg; } - /* Pick 48KHz 16-bits/channel */ - for (i = 0, cfg = get_cfg->config; i < CONFIG_COUNT_MAX; i++, cfg++) { - if ((le32_to_cpu(cfg->sample_frequency) == GB_SAMPLE_RATE) && - (cfg->num_channels == 2) && - (cfg->bytes_per_channel == 2) && - (cfg->byte_order & GB_I2S_MGMT_BYTE_ORDER_LE) && - (le32_to_cpu(cfg->spatial_locations) == - (GB_I2S_MGMT_SPATIAL_LOCATION_FL | - GB_I2S_MGMT_SPATIAL_LOCATION_FR)) && - (le32_to_cpu(cfg->ll_protocol) & GB_I2S_MGMT_PROTOCOL_I2S) && + snd_dev->i2s_configs = get_cfg; + + return 0; + +err_free_get_cfg: + kfree(get_cfg); + return ret; +} + +void gb_i2s_mgmt_free_cfgs(struct gb_snd *snd_dev) +{ + kfree(snd_dev->i2s_configs); + snd_dev->i2s_configs = NULL; +} + +int gb_i2s_mgmt_set_cfg(struct gb_snd *snd_dev, int rate, int chans, + int bytes_per_chan, int is_le) +{ + struct gb_i2s_mgmt_set_configuration_request set_cfg; + struct gb_i2s_mgmt_configuration *cfg; + int i, ret; + u8 byte_order = GB_I2S_MGMT_BYTE_ORDER_NA; + + if (bytes_per_chan > 1) { + if (is_le) + byte_order = GB_I2S_MGMT_BYTE_ORDER_LE; + else + byte_order = GB_I2S_MGMT_BYTE_ORDER_BE; + } + + for (i = 0, cfg = snd_dev->i2s_configs->config; + i < CONFIG_COUNT_MAX; + i++, cfg++) { + if ((cfg->sample_frequency == cpu_to_le32(rate)) && + (cfg->num_channels == chans) && + (cfg->bytes_per_channel == bytes_per_chan) && + (cfg->byte_order & byte_order) && + (cfg->ll_protocol & + cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S)) && (cfg->ll_mclk_role & GB_I2S_MGMT_ROLE_MASTER) && (cfg->ll_bclk_role & GB_I2S_MGMT_ROLE_MASTER) && (cfg->ll_wclk_role & GB_I2S_MGMT_ROLE_MASTER) && @@ -142,12 +162,11 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) if (i >= CONFIG_COUNT_MAX) { pr_err("No valid configuration\n"); - ret = -EINVAL; - goto free_get_cfg; + return -EINVAL; } memcpy(&set_cfg, cfg, sizeof(set_cfg)); - set_cfg.config.byte_order = GB_I2S_MGMT_BYTE_ORDER_LE; + set_cfg.config.byte_order = byte_order; set_cfg.config.ll_protocol = cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S); set_cfg.config.ll_mclk_role = GB_I2S_MGMT_ROLE_MASTER; set_cfg.config.ll_bclk_role = GB_I2S_MGMT_ROLE_MASTER; @@ -157,19 +176,17 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) set_cfg.config.ll_wclk_tx_edge = GB_I2S_MGMT_EDGE_RISING; set_cfg.config.ll_wclk_rx_edge = GB_I2S_MGMT_EDGE_FALLING; - ret = gb_i2s_mgmt_set_configuration(connection, &set_cfg); + ret = gb_i2s_mgmt_set_configuration(snd_dev->mgmt_connection, &set_cfg); if (ret) { pr_err("set_configuration failed: %d\n", ret); - goto free_get_cfg; + return ret; } - ret = gb_i2s_mgmt_set_samples_per_message(connection, + ret = gb_i2s_mgmt_set_samples_per_message(snd_dev->mgmt_connection, CONFIG_SAMPLES_PER_MSG); if (ret) pr_err("set_samples_per_msg failed: %d\n", ret); -free_get_cfg: - kfree(get_cfg); return ret; } diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c index 30030f8d3c22..b32700841444 100644 --- a/drivers/staging/greybus/audio-pcm.c +++ b/drivers/staging/greybus/audio-pcm.c @@ -220,6 +220,21 @@ static int gb_pcm_close(struct snd_pcm_substream *substream) static int gb_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct gb_snd *snd_dev; + int rate, chans, bytes_per_chan, is_le, ret; + + snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); + + rate = params_rate(hw_params); + chans = params_channels(hw_params); + bytes_per_chan = snd_pcm_format_width(params_format(hw_params)) / 8; + is_le = snd_pcm_format_little_endian(params_format(hw_params)); + + ret = gb_i2s_mgmt_set_cfg(snd_dev, rate, chans, bytes_per_chan, is_le); + if (ret) + return ret; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 1057e468d5d4..a1acbb039777 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -286,17 +286,23 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection) goto err_free_snd_dev; } - gb_i2s_mgmt_setup(connection); + ret = gb_i2s_mgmt_get_cfgs(snd_dev, connection); + if (ret) { + pr_err("can't get i2s configurations: %d\n", ret); + goto err_free_snd_dev; + } snd_dev->send_data_req_buf = kzalloc(SEND_DATA_BUF_LEN, GFP_KERNEL); if (!snd_dev->send_data_req_buf) { ret = -ENOMEM; - goto err_free_snd_dev; + goto err_free_i2s_configs; } return 0; +err_free_i2s_configs: + gb_i2s_mgmt_free_cfgs(snd_dev); err_free_snd_dev: gb_free_snd(snd_dev); return ret; @@ -306,6 +312,8 @@ static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection) { struct gb_snd *snd_dev = (struct gb_snd *)connection->private; + gb_i2s_mgmt_free_cfgs(snd_dev); + kfree(snd_dev->send_data_req_buf); snd_dev->send_data_req_buf = NULL; diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index 012c69a3ed83..fa1bb54fd4da 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -45,6 +45,8 @@ struct gb_snd { struct gb_connection *mgmt_connection; struct gb_connection *i2s_tx_connection; struct gb_connection *i2s_rx_connection; + struct gb_i2s_mgmt_get_supported_configurations_response + *i2s_configs; char *send_data_req_buf; long send_data_sample_count; int gb_bundle_id; @@ -79,7 +81,11 @@ int gb_i2s_mgmt_set_configuration(struct gb_connection *connection, struct gb_i2s_mgmt_set_configuration_request *set_cfg); int gb_i2s_mgmt_set_samples_per_message(struct gb_connection *connection, uint16_t samples_per_message); -int gb_i2s_mgmt_setup(struct gb_connection *connection); +int gb_i2s_mgmt_get_cfgs(struct gb_snd *snd_dev, + struct gb_connection *connection); +void gb_i2s_mgmt_free_cfgs(struct gb_snd *snd_dev); +int gb_i2s_mgmt_set_cfg(struct gb_snd *snd_dev, int rate, int chans, + int bytes_per_chan, int is_le); int gb_i2s_send_data(struct gb_connection *connection, void *req_buf, void *source_addr, size_t len, int sample_num); -- cgit v1.2.3-59-g8ed1b From 0d17e0c9f164d9c56471438b51ad75f4e894effa Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 21 May 2015 15:57:04 -0700 Subject: greybus: gb-audio: Set samples per message during init A recent commit moved the I2S samples per message operation to the PCM's 'hw_params' callback. However, the 'hw_params' callback is called numerous times while the samples per message need only be done once (or seldom). Eliminate the unnecessary samples per message operations by doing it only once at Greybus protocol init time. Signed-off-by: Mark A. Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-gb-cmds.c | 9 +-------- drivers/staging/greybus/audio.c | 7 +++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index 73f47d84f1aa..9dbde0d07a49 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -177,15 +177,8 @@ int gb_i2s_mgmt_set_cfg(struct gb_snd *snd_dev, int rate, int chans, set_cfg.config.ll_wclk_rx_edge = GB_I2S_MGMT_EDGE_FALLING; ret = gb_i2s_mgmt_set_configuration(snd_dev->mgmt_connection, &set_cfg); - if (ret) { - pr_err("set_configuration failed: %d\n", ret); - return ret; - } - - ret = gb_i2s_mgmt_set_samples_per_message(snd_dev->mgmt_connection, - CONFIG_SAMPLES_PER_MSG); if (ret) - pr_err("set_samples_per_msg failed: %d\n", ret); + pr_err("set_configuration failed: %d\n", ret); return ret; } diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index a1acbb039777..76b6bdc59847 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -292,6 +292,13 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection) goto err_free_snd_dev; } + ret = gb_i2s_mgmt_set_samples_per_message(snd_dev->mgmt_connection, + CONFIG_SAMPLES_PER_MSG); + if (ret) { + pr_err("set_samples_per_msg failed: %d\n", ret); + goto err_free_i2s_configs; + } + snd_dev->send_data_req_buf = kzalloc(SEND_DATA_BUF_LEN, GFP_KERNEL); if (!snd_dev->send_data_req_buf) { -- cgit v1.2.3-59-g8ed1b From a702a09f20be5f52ea1789b92a228592f4777084 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 09:52:42 -0500 Subject: greybus: endo: rename gb_svc Change the name of "struct gb_svc" to be "struct svc_info". The structure now contains only the SVC's serial number and version (and are place holders anyway). We will be defining a structure that represents the SVC for the SVC protocol connection, and I want to take back that name. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 8 ++++---- drivers/staging/greybus/endo.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 42f26f07b52e..859c9c57989d 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -35,7 +35,7 @@ static ssize_t serial_number_show(struct device *dev, { struct gb_endo *endo = to_gb_endo(dev); - return sprintf(buf, "%s", &endo->svc.serial_number[0]); + return sprintf(buf, "%s", &endo->svc_info.serial_number[0]); } static DEVICE_ATTR_RO(serial_number); @@ -44,7 +44,7 @@ static ssize_t version_show(struct device *dev, struct device_attribute *attr, { struct gb_endo *endo = to_gb_endo(dev); - return sprintf(buf, "%s", &endo->svc.version[0]); + return sprintf(buf, "%s", &endo->svc_info.version[0]); } static DEVICE_ATTR_RO(version); @@ -409,8 +409,8 @@ static int gb_endo_register(struct greybus_host_device *hd, // FIXME // Get the version and serial number from the SVC, right now we are // using "fake" numbers. - strcpy(&endo->svc.serial_number[0], "042"); - strcpy(&endo->svc.version[0], "0.0"); + strcpy(&endo->svc_info.serial_number[0], "042"); + strcpy(&endo->svc_info.version[0], "0.0"); dev_set_name(&endo->dev, "endo-0x%04x", endo->id); retval = device_add(&endo->dev); diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index dd0526949737..2920b130ce37 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -10,7 +10,7 @@ #define __ENDO_H /* Greybus "public" definitions" */ -struct gb_svc { +struct gb_svc_info { u8 serial_number[10]; u8 version[10]; }; @@ -36,9 +36,9 @@ struct endo_layout { }; struct gb_endo { - struct endo_layout layout; struct device dev; - struct gb_svc svc; + struct endo_layout layout; + struct gb_svc_info svc_info; u16 id; }; #define to_gb_endo(d) container_of(d, struct gb_endo, dev) -- cgit v1.2.3-59-g8ed1b From ee3ecf80284d973e491e653f74b2fd936c50bd2c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 09:52:43 -0500 Subject: greybus: endo: pass endo_id to gb_endo_create() We are going to want to defer creating the endo until we receive a probe operation from the SVC, which will supply the endo id. Change gb_endo_create() so it passes the endo_id value as an argument. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 ++- drivers/staging/greybus/endo.c | 3 +-- drivers/staging/greybus/endo.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index e32d6c41e87e..27062e7a5824 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -177,6 +177,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver size_t buffer_size_max) { struct greybus_host_device *hd; + u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC /* * Validate that the driver implements all of the callbacks @@ -210,7 +211,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; - hd->endo = gb_endo_create(hd); + hd->endo = gb_endo_create(hd, endo_id); if (!hd->endo) { greybus_remove_hd(hd); return NULL; diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 859c9c57989d..fb2f7c9c6dd7 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -423,11 +423,10 @@ static int gb_endo_register(struct greybus_host_device *hd, return retval; } -struct gb_endo *gb_endo_create(struct greybus_host_device *hd) +struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id) { struct gb_endo *endo; int retval; - u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC endo = kzalloc(sizeof(*endo), GFP_KERNEL); if (!endo) diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index 2920b130ce37..c1ccbcd576f3 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -47,7 +47,7 @@ struct gb_endo { /* Greybus "private" definitions */ struct greybus_host_device; -struct gb_endo *gb_endo_create(struct greybus_host_device *hd); +struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id); void gb_endo_remove(struct gb_endo *endo); u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id); -- cgit v1.2.3-59-g8ed1b From 6b7d5a1f47914b2e3917fb764b4b3fe3affe7f94 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 09:52:44 -0500 Subject: greybus: core: return error code when creating endo Return a pointer-coded error from gb_endo_create() rather than just a null pointer in the event an error occurs. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 6 ++++-- drivers/staging/greybus/endo.c | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 27062e7a5824..95d8c70cf20d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -177,6 +177,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver size_t buffer_size_max) { struct greybus_host_device *hd; + struct gb_endo *endo; u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC /* @@ -211,11 +212,12 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; - hd->endo = gb_endo_create(hd, endo_id); - if (!hd->endo) { + endo = gb_endo_create(hd, endo_id); + if (IS_ERR(endo)) { greybus_remove_hd(hd); return NULL; } + hd->endo = endo; return hd; } diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index fb2f7c9c6dd7..5b5a3c65de8f 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -430,16 +430,19 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id) endo = kzalloc(sizeof(*endo), GFP_KERNEL); if (!endo) - return NULL; + return ERR_PTR(-ENOMEM); /* First check if the value supplied is a valid endo id */ - if (gb_endo_validate_id(hd, &endo->layout, endo_id)) + if (gb_endo_validate_id(hd, &endo->layout, endo_id)) { + retval = -EINVAL; goto free_endo; + } endo->id = endo_id; /* Register Endo device */ - if (gb_endo_register(hd, endo)) + retval = gb_endo_register(hd, endo); + if (retval) goto free_endo; /* Create modules/interfaces */ @@ -453,7 +456,8 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id) free_endo: kfree(endo); - return NULL; + + return ERR_PTR(retval); } void gb_endo_remove(struct gb_endo *endo) -- cgit v1.2.3-59-g8ed1b From 8ea70fe0497c5dd11451e7cf1084844cbdb2a349 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 09:52:45 -0500 Subject: greybus: core: return error code when creating host device Return a pointer-coded error from greybus_create_hd() rather than NULL in the event an error occurs. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 6 +++--- drivers/staging/greybus/es1.c | 4 ++-- drivers/staging/greybus/es2.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 95d8c70cf20d..7bfdbadb6250 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -187,7 +187,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver if ((!driver->message_send) || (!driver->message_cancel) || (!driver->submit_svc)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); - return NULL; + return ERR_PTR(-EINVAL); } /* @@ -202,7 +202,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL); if (!hd) - return NULL; + return ERR_PTR(-ENOMEM); kref_init(&hd->kref); hd->parent = parent; @@ -215,7 +215,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver endo = gb_endo_create(hd, endo_id); if (IS_ERR(endo)) { greybus_remove_hd(hd); - return NULL; + return ERR_CAST(endo); } hd->endo = endo; diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index e0fae26d8ba3..1ed10f4b8e49 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -556,9 +556,9 @@ static int ap_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); - if (!hd) { + if (IS_ERR(hd)) { usb_put_dev(udev); - return -ENOMEM; + return PTR_ERR(hd); } es1 = hd_to_es1(hd); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 05aac3d7686b..4733adcb8a22 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -556,9 +556,9 @@ static int ap_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); - if (!hd) { + if (IS_ERR(hd)) { usb_put_dev(udev); - return -ENOMEM; + return PTR_ERR(hd); } es1 = hd_to_es1(hd); -- cgit v1.2.3-59-g8ed1b From e9385e5d5ab64a0d05758d84f2980943d3f5f12d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 12:35:31 -0500 Subject: greybus: include "gpbridge.h" from "greybus.h" Avoid the need for all the source files to include "gpbridge.h" by just having "greybus.h" include it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-dai.c | 2 +- drivers/staging/greybus/audio-gb-cmds.c | 2 +- drivers/staging/greybus/audio-pcm.c | 2 +- drivers/staging/greybus/audio.c | 2 +- drivers/staging/greybus/audio.h | 3 +-- drivers/staging/greybus/gpio.c | 1 - drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/i2c.c | 2 -- drivers/staging/greybus/pwm.c | 2 +- drivers/staging/greybus/spi.c | 1 - 10 files changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/audio-dai.c b/drivers/staging/greybus/audio-dai.c index 39b72f0d2138..0dd7364785ae 100644 --- a/drivers/staging/greybus/audio-dai.c +++ b/drivers/staging/greybus/audio-dai.c @@ -11,8 +11,8 @@ #include #include #include + #include "greybus.h" -#include "gpbridge.h" #include "audio.h" /* diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index 9dbde0d07a49..f6a29abca70f 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -1,6 +1,6 @@ #include + #include "greybus.h" -#include "gpbridge.h" #include "audio.h" #define GB_I2S_MGMT_VERSION_MAJOR 0x00 diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c index b32700841444..a1faf7895b58 100644 --- a/drivers/staging/greybus/audio-pcm.c +++ b/drivers/staging/greybus/audio-pcm.c @@ -11,8 +11,8 @@ #include #include #include + #include "greybus.h" -#include "gpbridge.h" #include "audio.h" /* diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 76b6bdc59847..c873f339da1d 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -10,8 +10,8 @@ #include #include #include + #include "greybus.h" -#include "gpbridge.h" #include "audio.h" diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index fa1bb54fd4da..c9087c9fc15c 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -7,9 +7,8 @@ #include #include #include -#include "greybus.h" -#include "gpbridge.h" +#include "greybus.h" #define GB_SAMPLE_RATE 48000 #define GB_RATES SNDRV_PCM_RATE_48000 diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 871f2d0558b0..6e5fe5b3db39 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -14,7 +14,6 @@ #include #include #include "greybus.h" -#include "gpbridge.h" struct gb_gpio_line { /* The following has to be an array of line_max entries */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index dbb4e78cf4c9..c58f12312d75 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -23,6 +23,7 @@ #include "kernel_ver.h" #include "greybus_id.h" #include "greybus_manifest.h" +#include "gpbridge.h" #include "manifest.h" #include "endo.h" #include "module.h" diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 14fdea184c39..01afca8408a0 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -13,8 +13,6 @@ #include #include "greybus.h" -#include "gpbridge.h" - struct gb_i2c_device { struct gb_connection *connection; diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 7f675bfed1d7..5dfeb0e761c1 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -11,8 +11,8 @@ #include #include #include + #include "greybus.h" -#include "gpbridge.h" struct gb_pwm_chip { struct gb_connection *connection; diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 430c3ad70a5a..78a7f85a4bf6 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -14,7 +14,6 @@ #include #include "greybus.h" -#include "gpbridge.h" struct gb_spi { struct gb_connection *connection; -- cgit v1.2.3-59-g8ed1b From 22e17edaa7c24d254e572738420873813f47efb5 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 12:35:32 -0500 Subject: greybus: rename "gpbridge.h" The file "gpbridge.h" is now used as a single place to define all protocol message structures. These protocols are not necessarily related to the GP bridge, so the name of the file is misleading. Rename it "greybus_protocols.h". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpbridge.h | 492 ---------------------------- drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/greybus_protocols.h | 492 ++++++++++++++++++++++++++++ 3 files changed, 493 insertions(+), 493 deletions(-) delete mode 100644 drivers/staging/greybus/gpbridge.h create mode 100644 drivers/staging/greybus/greybus_protocols.h diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h deleted file mode 100644 index c8208054d942..000000000000 --- a/drivers/staging/greybus/gpbridge.h +++ /dev/null @@ -1,492 +0,0 @@ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 - 2015 Google Inc. All rights reserved. - * Copyright(c) 2014 - 2015 Linaro Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program 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 version 2 for more details. - * - * BSD LICENSE - * - * Copyright(c) 2014 - 2015 Google Inc. All rights reserved. - * Copyright(c) 2014 - 2015 Linaro Ltd. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of Google Inc. or Linaro Ltd. 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 COPYRIGHT HOLDERS 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 GOOGLE INC. OR - * LINARO LTD. 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 __GB_GPBRIDGE_H__ -#define __GB_GPBRIDGE_H__ - -/* I2C */ - -/* Version of the Greybus i2c protocol we support */ -#define GB_I2C_VERSION_MAJOR 0x00 -#define GB_I2C_VERSION_MINOR 0x01 - -/* Greybus i2c request types */ -#define GB_I2C_TYPE_INVALID 0x00 -#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 -#define GB_I2C_TYPE_FUNCTIONALITY 0x02 -#define GB_I2C_TYPE_TIMEOUT 0x03 -#define GB_I2C_TYPE_RETRIES 0x04 -#define GB_I2C_TYPE_TRANSFER 0x05 - -#define GB_I2C_RETRIES_DEFAULT 3 -#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ - -/* functionality request has no payload */ -struct gb_i2c_functionality_response { - __le32 functionality; -}; - -struct gb_i2c_timeout_request { - __le16 msec; -}; -/* timeout response has no payload */ - -struct gb_i2c_retries_request { - __u8 retries; -}; -/* retries response has no payload */ - -/* - * Outgoing data immediately follows the op count and ops array. - * The data for each write (master -> slave) op in the array is sent - * in order, with no (e.g. pad) bytes separating them. - * - * Short reads cause the entire transfer request to fail So response - * payload consists only of bytes read, and the number of bytes is - * exactly what was specified in the corresponding op. Like - * outgoing data, the incoming data is in order and contiguous. - */ -struct gb_i2c_transfer_op { - __le16 addr; - __le16 flags; - __le16 size; -}; - -struct gb_i2c_transfer_request { - __le16 op_count; - struct gb_i2c_transfer_op ops[0]; /* op_count of these */ -}; -struct gb_i2c_transfer_response { - __u8 data[0]; /* inbound data */ -}; - - -/* GPIO */ - -/* Version of the Greybus GPIO protocol we support */ -#define GB_GPIO_VERSION_MAJOR 0x00 -#define GB_GPIO_VERSION_MINOR 0x01 - -/* Greybus GPIO request types */ -#define GB_GPIO_TYPE_INVALID 0x00 -#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 -#define GB_GPIO_TYPE_LINE_COUNT 0x02 -#define GB_GPIO_TYPE_ACTIVATE 0x03 -#define GB_GPIO_TYPE_DEACTIVATE 0x04 -#define GB_GPIO_TYPE_GET_DIRECTION 0x05 -#define GB_GPIO_TYPE_DIRECTION_IN 0x06 -#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 -#define GB_GPIO_TYPE_GET_VALUE 0x08 -#define GB_GPIO_TYPE_SET_VALUE 0x09 -#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a -#define GB_GPIO_TYPE_IRQ_TYPE 0x0b -#define GB_GPIO_TYPE_IRQ_ACK 0x0c -#define GB_GPIO_TYPE_IRQ_MASK 0x0d -#define GB_GPIO_TYPE_IRQ_UNMASK 0x0e -#define GB_GPIO_TYPE_IRQ_EVENT 0x0f - -#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ - -/* line count request has no payload */ -struct gb_gpio_line_count_response { - __u8 count; -}; - -struct gb_gpio_activate_request { - __u8 which; -}; -/* activate response has no payload */ - -struct gb_gpio_deactivate_request { - __u8 which; -}; -/* deactivate response has no payload */ - -struct gb_gpio_get_direction_request { - __u8 which; -}; -struct gb_gpio_get_direction_response { - __u8 direction; -}; - -struct gb_gpio_direction_in_request { - __u8 which; -}; -/* direction in response has no payload */ - -struct gb_gpio_direction_out_request { - __u8 which; - __u8 value; -}; -/* direction out response has no payload */ - -struct gb_gpio_get_value_request { - __u8 which; -}; -struct gb_gpio_get_value_response { - __u8 value; -}; - -struct gb_gpio_set_value_request { - __u8 which; - __u8 value; -}; -/* set value response has no payload */ - -struct gb_gpio_set_debounce_request { - __u8 which; - __le16 usec __packed; -}; -/* debounce response has no payload */ - -struct gb_gpio_irq_type_request { - __u8 which; - __u8 type; -}; -/* irq type response has no payload */ - -struct gb_gpio_irq_mask_request { - __u8 which; -}; -/* irq mask response has no payload */ - -struct gb_gpio_irq_unmask_request { - __u8 which; -}; -/* irq unmask response has no payload */ - -struct gb_gpio_irq_ack_request { - __u8 which; -}; -/* irq ack response has no payload */ - -/* irq event requests originate on another module and are handled on the AP */ -struct gb_gpio_irq_event_request { - __u8 which; -}; -/* irq event response has no payload */ - - -/* PWM */ - -/* Version of the Greybus PWM protocol we support */ -#define GB_PWM_VERSION_MAJOR 0x00 -#define GB_PWM_VERSION_MINOR 0x01 - -/* Greybus PWM operation types */ -#define GB_PWM_TYPE_INVALID 0x00 -#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 -#define GB_PWM_TYPE_PWM_COUNT 0x02 -#define GB_PWM_TYPE_ACTIVATE 0x03 -#define GB_PWM_TYPE_DEACTIVATE 0x04 -#define GB_PWM_TYPE_CONFIG 0x05 -#define GB_PWM_TYPE_POLARITY 0x06 -#define GB_PWM_TYPE_ENABLE 0x07 -#define GB_PWM_TYPE_DISABLE 0x08 - -/* pwm count request has no payload */ -struct gb_pwm_count_response { - __u8 count; -}; - -struct gb_pwm_activate_request { - __u8 which; -}; - -struct gb_pwm_deactivate_request { - __u8 which; -}; - -struct gb_pwm_config_request { - __u8 which; - __le32 duty __packed; - __le32 period __packed; -}; - -struct gb_pwm_polarity_request { - __u8 which; - __u8 polarity; -}; - -struct gb_pwm_enable_request { - __u8 which; -}; - -struct gb_pwm_disable_request { - __u8 which; -}; - -/* I2S */ - -#define GB_I2S_MGMT_TYPE_PROTOCOL_VERSION 0x01 -#define GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS 0x02 -#define GB_I2S_MGMT_TYPE_SET_CONFIGURATION 0x03 -#define GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE 0x04 -#define GB_I2S_MGMT_TYPE_GET_PROCESSING_DELAY 0x05 -#define GB_I2S_MGMT_TYPE_SET_START_DELAY 0x06 -#define GB_I2S_MGMT_TYPE_ACTIVATE_CPORT 0x07 -#define GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT 0x08 -#define GB_I2S_MGMT_TYPE_REPORT_EVENT 0x09 - -#define GB_I2S_MGMT_BYTE_ORDER_NA BIT(0) -#define GB_I2S_MGMT_BYTE_ORDER_BE BIT(1) -#define GB_I2S_MGMT_BYTE_ORDER_LE BIT(2) - -#define GB_I2S_MGMT_SPATIAL_LOCATION_FL BIT(0) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FR BIT(1) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FC BIT(2) -#define GB_I2S_MGMT_SPATIAL_LOCATION_LFE BIT(3) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BL BIT(4) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BR BIT(5) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FLC BIT(6) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FRC BIT(7) -#define GB_I2S_MGMT_SPATIAL_LOCATION_C BIT(8) /* BC in USB */ -#define GB_I2S_MGMT_SPATIAL_LOCATION_SL BIT(9) -#define GB_I2S_MGMT_SPATIAL_LOCATION_SR BIT(10) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TC BIT(11) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFL BIT(12) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFC BIT(13) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFR BIT(14) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TBL BIT(15) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TBC BIT(16) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TBR BIT(17) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFLC BIT(18) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFRC BIT(19) -#define GB_I2S_MGMT_SPATIAL_LOCATION_LLFE BIT(20) -#define GB_I2S_MGMT_SPATIAL_LOCATION_RLFE BIT(21) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TSL BIT(22) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TSR BIT(23) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BC BIT(24) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BLC BIT(25) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BRC BIT(26) -#define GB_I2S_MGMT_SPATIAL_LOCATION_RD BIT(31) - -#define GB_I2S_MGMT_PROTOCOL_PCM BIT(0) -#define GB_I2S_MGMT_PROTOCOL_I2S BIT(1) -#define GB_I2S_MGMT_PROTOCOL_LR_STEREO BIT(2) - -#define GB_I2S_MGMT_ROLE_MASTER BIT(0) -#define GB_I2S_MGMT_ROLE_SLAVE BIT(1) - -#define GB_I2S_MGMT_POLARITY_NORMAL BIT(0) -#define GB_I2S_MGMT_POLARITY_REVERSED BIT(1) - -#define GB_I2S_MGMT_EDGE_RISING BIT(0) -#define GB_I2S_MGMT_EDGE_FALLING BIT(1) - -#define GB_I2S_MGMT_EVENT_UNSPECIFIED 0x1 -#define GB_I2S_MGMT_EVENT_HALT 0x2 -#define GB_I2S_MGMT_EVENT_INTERNAL_ERROR 0x3 -#define GB_I2S_MGMT_EVENT_PROTOCOL_ERROR 0x4 -#define GB_I2S_MGMT_EVENT_FAILURE 0x5 -#define GB_I2S_MGMT_EVENT_OUT_OF_SEQUENCE 0x6 -#define GB_I2S_MGMT_EVENT_UNDERRUN 0x7 -#define GB_I2S_MGMT_EVENT_OVERRUN 0x8 -#define GB_I2S_MGMT_EVENT_CLOCKING 0x9 -#define GB_I2S_MGMT_EVENT_DATA_LEN 0xa - -struct gb_i2s_mgmt_configuration { - __le32 sample_frequency; - __u8 num_channels; - __u8 bytes_per_channel; - __u8 byte_order; - __u8 pad; - __le32 spatial_locations; - __le32 ll_protocol; - __u8 ll_mclk_role; - __u8 ll_bclk_role; - __u8 ll_wclk_role; - __u8 ll_wclk_polarity; - __u8 ll_wclk_change_edge; - __u8 ll_wclk_tx_edge; - __u8 ll_wclk_rx_edge; - __u8 ll_data_offset; -}; - -/* get supported configurations request has no payload */ -struct gb_i2s_mgmt_get_supported_configurations_response { - __u8 config_count; - __u8 pad[3]; - struct gb_i2s_mgmt_configuration config[0]; -}; - -struct gb_i2s_mgmt_set_configuration_request { - struct gb_i2s_mgmt_configuration config; -}; -/* set configuration response has no payload */ - -struct gb_i2s_mgmt_set_samples_per_message_request { - __le16 samples_per_message; -}; -/* set samples per message response has no payload */ - -/* get processing request delay has no payload */ -struct gb_i2s_mgmt_get_processing_delay_response { - __le32 microseconds; -}; - -struct gb_i2s_mgmt_set_start_delay_request { - __le32 microseconds; -}; -/* set start delay response has no payload */ - -struct gb_i2s_mgmt_activate_cport_request { - __le16 cport; -}; -/* activate cport response has no payload */ - -struct gb_i2s_mgmt_deactivate_cport_request { - __le16 cport; -}; -/* deactivate cport response has no payload */ - -struct gb_i2s_mgmt_report_event_request { - __u8 event; -}; -/* report event response has no payload */ - -#define GB_I2S_DATA_TYPE_PROTOCOL_VERSION 0x01 -#define GB_I2S_DATA_TYPE_SEND_DATA 0x02 - -struct gb_i2s_send_data_request { - __le32 sample_number; - __le32 size; - __u8 data[0]; -}; -/* send data has no response at all */ - - -/* SPI */ - -/* Version of the Greybus spi protocol we support */ -#define GB_SPI_VERSION_MAJOR 0x00 -#define GB_SPI_VERSION_MINOR 0x01 - -/* Should match up with modes in linux/spi/spi.h */ -#define GB_SPI_MODE_CPHA 0x01 /* clock phase */ -#define GB_SPI_MODE_CPOL 0x02 /* clock polarity */ -#define GB_SPI_MODE_MODE_0 (0|0) /* (original MicroWire) */ -#define GB_SPI_MODE_MODE_1 (0|GB_SPI_MODE_CPHA) -#define GB_SPI_MODE_MODE_2 (GB_SPI_MODE_CPOL|0) -#define GB_SPI_MODE_MODE_3 (GB_SPI_MODE_CPOL|GB_SPI_MODE_CPHA) -#define GB_SPI_MODE_CS_HIGH 0x04 /* chipselect active high? */ -#define GB_SPI_MODE_LSB_FIRST 0x08 /* per-word bits-on-wire */ -#define GB_SPI_MODE_3WIRE 0x10 /* SI/SO signals shared */ -#define GB_SPI_MODE_LOOP 0x20 /* loopback mode */ -#define GB_SPI_MODE_NO_CS 0x40 /* 1 dev/bus, no chipselect */ -#define GB_SPI_MODE_READY 0x80 /* slave pulls low to pause */ - -/* Should match up with flags in linux/spi/spi.h */ -#define GB_SPI_FLAG_HALF_DUPLEX BIT(0) /* can't do full duplex */ -#define GB_SPI_FLAG_NO_RX BIT(1) /* can't do buffer read */ -#define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */ - -/* Greybus spi operation types */ -#define GB_SPI_TYPE_INVALID 0x00 -#define GB_SPI_TYPE_PROTOCOL_VERSION 0x01 -#define GB_SPI_TYPE_MODE 0x02 -#define GB_SPI_TYPE_FLAGS 0x03 -#define GB_SPI_TYPE_BITS_PER_WORD_MASK 0x04 -#define GB_SPI_TYPE_NUM_CHIPSELECT 0x05 -#define GB_SPI_TYPE_TRANSFER 0x06 - -/* mode request has no payload */ -struct gb_spi_mode_response { - __le16 mode; -}; - -/* flags request has no payload */ -struct gb_spi_flags_response { - __le16 flags; -}; - -/* bits-per-word request has no payload */ -struct gb_spi_bpw_response { - __le32 bits_per_word_mask; -}; - -/* num-chipselects request has no payload */ -struct gb_spi_chipselect_response { - __le16 num_chipselect; -}; - -/** - * struct gb_spi_transfer - a read/write buffer pair - * @speed_hz: Select a speed other than the device default for this transfer. If - * 0 the default (from @spi_device) is used. - * @len: size of rx and tx buffers (in bytes) - * @delay_usecs: microseconds to delay after this transfer before (optionally) - * changing the chipselect status, then starting the next transfer or - * completing this spi_message. - * @cs_change: affects chipselect after this transfer completes - * @bits_per_word: select a bits_per_word other than the device default for this - * transfer. If 0 the default (from @spi_device) is used. - */ -struct gb_spi_transfer { - __le32 speed_hz; - __le32 len; - __le16 delay_usecs; - __u8 cs_change; - __u8 bits_per_word; -}; - -struct gb_spi_transfer_request { - __u8 chip_select; /* of the spi device */ - __u8 mode; /* of the spi device */ - __le16 count; - struct gb_spi_transfer transfers[0]; /* trnasfer_count of these */ -}; - -struct gb_spi_transfer_response { - __u8 data[0]; /* inbound data */ -}; - -#endif /* __GB_GPBRIDGE_H__ */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index c58f12312d75..4920458d65d0 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -23,7 +23,7 @@ #include "kernel_ver.h" #include "greybus_id.h" #include "greybus_manifest.h" -#include "gpbridge.h" +#include "greybus_protocols.h" #include "manifest.h" #include "endo.h" #include "module.h" diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h new file mode 100644 index 000000000000..c8208054d942 --- /dev/null +++ b/drivers/staging/greybus/greybus_protocols.h @@ -0,0 +1,492 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 - 2015 Google Inc. All rights reserved. + * Copyright(c) 2014 - 2015 Linaro Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program 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 version 2 for more details. + * + * BSD LICENSE + * + * Copyright(c) 2014 - 2015 Google Inc. All rights reserved. + * Copyright(c) 2014 - 2015 Linaro Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. or Linaro Ltd. 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 COPYRIGHT HOLDERS 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 GOOGLE INC. OR + * LINARO LTD. 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 __GB_GPBRIDGE_H__ +#define __GB_GPBRIDGE_H__ + +/* I2C */ + +/* Version of the Greybus i2c protocol we support */ +#define GB_I2C_VERSION_MAJOR 0x00 +#define GB_I2C_VERSION_MINOR 0x01 + +/* Greybus i2c request types */ +#define GB_I2C_TYPE_INVALID 0x00 +#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2C_TYPE_FUNCTIONALITY 0x02 +#define GB_I2C_TYPE_TIMEOUT 0x03 +#define GB_I2C_TYPE_RETRIES 0x04 +#define GB_I2C_TYPE_TRANSFER 0x05 + +#define GB_I2C_RETRIES_DEFAULT 3 +#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ + +/* functionality request has no payload */ +struct gb_i2c_functionality_response { + __le32 functionality; +}; + +struct gb_i2c_timeout_request { + __le16 msec; +}; +/* timeout response has no payload */ + +struct gb_i2c_retries_request { + __u8 retries; +}; +/* retries response has no payload */ + +/* + * Outgoing data immediately follows the op count and ops array. + * The data for each write (master -> slave) op in the array is sent + * in order, with no (e.g. pad) bytes separating them. + * + * Short reads cause the entire transfer request to fail So response + * payload consists only of bytes read, and the number of bytes is + * exactly what was specified in the corresponding op. Like + * outgoing data, the incoming data is in order and contiguous. + */ +struct gb_i2c_transfer_op { + __le16 addr; + __le16 flags; + __le16 size; +}; + +struct gb_i2c_transfer_request { + __le16 op_count; + struct gb_i2c_transfer_op ops[0]; /* op_count of these */ +}; +struct gb_i2c_transfer_response { + __u8 data[0]; /* inbound data */ +}; + + +/* GPIO */ + +/* Version of the Greybus GPIO protocol we support */ +#define GB_GPIO_VERSION_MAJOR 0x00 +#define GB_GPIO_VERSION_MINOR 0x01 + +/* Greybus GPIO request types */ +#define GB_GPIO_TYPE_INVALID 0x00 +#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 +#define GB_GPIO_TYPE_LINE_COUNT 0x02 +#define GB_GPIO_TYPE_ACTIVATE 0x03 +#define GB_GPIO_TYPE_DEACTIVATE 0x04 +#define GB_GPIO_TYPE_GET_DIRECTION 0x05 +#define GB_GPIO_TYPE_DIRECTION_IN 0x06 +#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 +#define GB_GPIO_TYPE_GET_VALUE 0x08 +#define GB_GPIO_TYPE_SET_VALUE 0x09 +#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a +#define GB_GPIO_TYPE_IRQ_TYPE 0x0b +#define GB_GPIO_TYPE_IRQ_ACK 0x0c +#define GB_GPIO_TYPE_IRQ_MASK 0x0d +#define GB_GPIO_TYPE_IRQ_UNMASK 0x0e +#define GB_GPIO_TYPE_IRQ_EVENT 0x0f + +#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ + +/* line count request has no payload */ +struct gb_gpio_line_count_response { + __u8 count; +}; + +struct gb_gpio_activate_request { + __u8 which; +}; +/* activate response has no payload */ + +struct gb_gpio_deactivate_request { + __u8 which; +}; +/* deactivate response has no payload */ + +struct gb_gpio_get_direction_request { + __u8 which; +}; +struct gb_gpio_get_direction_response { + __u8 direction; +}; + +struct gb_gpio_direction_in_request { + __u8 which; +}; +/* direction in response has no payload */ + +struct gb_gpio_direction_out_request { + __u8 which; + __u8 value; +}; +/* direction out response has no payload */ + +struct gb_gpio_get_value_request { + __u8 which; +}; +struct gb_gpio_get_value_response { + __u8 value; +}; + +struct gb_gpio_set_value_request { + __u8 which; + __u8 value; +}; +/* set value response has no payload */ + +struct gb_gpio_set_debounce_request { + __u8 which; + __le16 usec __packed; +}; +/* debounce response has no payload */ + +struct gb_gpio_irq_type_request { + __u8 which; + __u8 type; +}; +/* irq type response has no payload */ + +struct gb_gpio_irq_mask_request { + __u8 which; +}; +/* irq mask response has no payload */ + +struct gb_gpio_irq_unmask_request { + __u8 which; +}; +/* irq unmask response has no payload */ + +struct gb_gpio_irq_ack_request { + __u8 which; +}; +/* irq ack response has no payload */ + +/* irq event requests originate on another module and are handled on the AP */ +struct gb_gpio_irq_event_request { + __u8 which; +}; +/* irq event response has no payload */ + + +/* PWM */ + +/* Version of the Greybus PWM protocol we support */ +#define GB_PWM_VERSION_MAJOR 0x00 +#define GB_PWM_VERSION_MINOR 0x01 + +/* Greybus PWM operation types */ +#define GB_PWM_TYPE_INVALID 0x00 +#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 +#define GB_PWM_TYPE_PWM_COUNT 0x02 +#define GB_PWM_TYPE_ACTIVATE 0x03 +#define GB_PWM_TYPE_DEACTIVATE 0x04 +#define GB_PWM_TYPE_CONFIG 0x05 +#define GB_PWM_TYPE_POLARITY 0x06 +#define GB_PWM_TYPE_ENABLE 0x07 +#define GB_PWM_TYPE_DISABLE 0x08 + +/* pwm count request has no payload */ +struct gb_pwm_count_response { + __u8 count; +}; + +struct gb_pwm_activate_request { + __u8 which; +}; + +struct gb_pwm_deactivate_request { + __u8 which; +}; + +struct gb_pwm_config_request { + __u8 which; + __le32 duty __packed; + __le32 period __packed; +}; + +struct gb_pwm_polarity_request { + __u8 which; + __u8 polarity; +}; + +struct gb_pwm_enable_request { + __u8 which; +}; + +struct gb_pwm_disable_request { + __u8 which; +}; + +/* I2S */ + +#define GB_I2S_MGMT_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS 0x02 +#define GB_I2S_MGMT_TYPE_SET_CONFIGURATION 0x03 +#define GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE 0x04 +#define GB_I2S_MGMT_TYPE_GET_PROCESSING_DELAY 0x05 +#define GB_I2S_MGMT_TYPE_SET_START_DELAY 0x06 +#define GB_I2S_MGMT_TYPE_ACTIVATE_CPORT 0x07 +#define GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT 0x08 +#define GB_I2S_MGMT_TYPE_REPORT_EVENT 0x09 + +#define GB_I2S_MGMT_BYTE_ORDER_NA BIT(0) +#define GB_I2S_MGMT_BYTE_ORDER_BE BIT(1) +#define GB_I2S_MGMT_BYTE_ORDER_LE BIT(2) + +#define GB_I2S_MGMT_SPATIAL_LOCATION_FL BIT(0) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FR BIT(1) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FC BIT(2) +#define GB_I2S_MGMT_SPATIAL_LOCATION_LFE BIT(3) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BL BIT(4) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BR BIT(5) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FLC BIT(6) +#define GB_I2S_MGMT_SPATIAL_LOCATION_FRC BIT(7) +#define GB_I2S_MGMT_SPATIAL_LOCATION_C BIT(8) /* BC in USB */ +#define GB_I2S_MGMT_SPATIAL_LOCATION_SL BIT(9) +#define GB_I2S_MGMT_SPATIAL_LOCATION_SR BIT(10) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TC BIT(11) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFL BIT(12) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFC BIT(13) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFR BIT(14) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TBL BIT(15) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TBC BIT(16) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TBR BIT(17) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFLC BIT(18) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TFRC BIT(19) +#define GB_I2S_MGMT_SPATIAL_LOCATION_LLFE BIT(20) +#define GB_I2S_MGMT_SPATIAL_LOCATION_RLFE BIT(21) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TSL BIT(22) +#define GB_I2S_MGMT_SPATIAL_LOCATION_TSR BIT(23) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BC BIT(24) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BLC BIT(25) +#define GB_I2S_MGMT_SPATIAL_LOCATION_BRC BIT(26) +#define GB_I2S_MGMT_SPATIAL_LOCATION_RD BIT(31) + +#define GB_I2S_MGMT_PROTOCOL_PCM BIT(0) +#define GB_I2S_MGMT_PROTOCOL_I2S BIT(1) +#define GB_I2S_MGMT_PROTOCOL_LR_STEREO BIT(2) + +#define GB_I2S_MGMT_ROLE_MASTER BIT(0) +#define GB_I2S_MGMT_ROLE_SLAVE BIT(1) + +#define GB_I2S_MGMT_POLARITY_NORMAL BIT(0) +#define GB_I2S_MGMT_POLARITY_REVERSED BIT(1) + +#define GB_I2S_MGMT_EDGE_RISING BIT(0) +#define GB_I2S_MGMT_EDGE_FALLING BIT(1) + +#define GB_I2S_MGMT_EVENT_UNSPECIFIED 0x1 +#define GB_I2S_MGMT_EVENT_HALT 0x2 +#define GB_I2S_MGMT_EVENT_INTERNAL_ERROR 0x3 +#define GB_I2S_MGMT_EVENT_PROTOCOL_ERROR 0x4 +#define GB_I2S_MGMT_EVENT_FAILURE 0x5 +#define GB_I2S_MGMT_EVENT_OUT_OF_SEQUENCE 0x6 +#define GB_I2S_MGMT_EVENT_UNDERRUN 0x7 +#define GB_I2S_MGMT_EVENT_OVERRUN 0x8 +#define GB_I2S_MGMT_EVENT_CLOCKING 0x9 +#define GB_I2S_MGMT_EVENT_DATA_LEN 0xa + +struct gb_i2s_mgmt_configuration { + __le32 sample_frequency; + __u8 num_channels; + __u8 bytes_per_channel; + __u8 byte_order; + __u8 pad; + __le32 spatial_locations; + __le32 ll_protocol; + __u8 ll_mclk_role; + __u8 ll_bclk_role; + __u8 ll_wclk_role; + __u8 ll_wclk_polarity; + __u8 ll_wclk_change_edge; + __u8 ll_wclk_tx_edge; + __u8 ll_wclk_rx_edge; + __u8 ll_data_offset; +}; + +/* get supported configurations request has no payload */ +struct gb_i2s_mgmt_get_supported_configurations_response { + __u8 config_count; + __u8 pad[3]; + struct gb_i2s_mgmt_configuration config[0]; +}; + +struct gb_i2s_mgmt_set_configuration_request { + struct gb_i2s_mgmt_configuration config; +}; +/* set configuration response has no payload */ + +struct gb_i2s_mgmt_set_samples_per_message_request { + __le16 samples_per_message; +}; +/* set samples per message response has no payload */ + +/* get processing request delay has no payload */ +struct gb_i2s_mgmt_get_processing_delay_response { + __le32 microseconds; +}; + +struct gb_i2s_mgmt_set_start_delay_request { + __le32 microseconds; +}; +/* set start delay response has no payload */ + +struct gb_i2s_mgmt_activate_cport_request { + __le16 cport; +}; +/* activate cport response has no payload */ + +struct gb_i2s_mgmt_deactivate_cport_request { + __le16 cport; +}; +/* deactivate cport response has no payload */ + +struct gb_i2s_mgmt_report_event_request { + __u8 event; +}; +/* report event response has no payload */ + +#define GB_I2S_DATA_TYPE_PROTOCOL_VERSION 0x01 +#define GB_I2S_DATA_TYPE_SEND_DATA 0x02 + +struct gb_i2s_send_data_request { + __le32 sample_number; + __le32 size; + __u8 data[0]; +}; +/* send data has no response at all */ + + +/* SPI */ + +/* Version of the Greybus spi protocol we support */ +#define GB_SPI_VERSION_MAJOR 0x00 +#define GB_SPI_VERSION_MINOR 0x01 + +/* Should match up with modes in linux/spi/spi.h */ +#define GB_SPI_MODE_CPHA 0x01 /* clock phase */ +#define GB_SPI_MODE_CPOL 0x02 /* clock polarity */ +#define GB_SPI_MODE_MODE_0 (0|0) /* (original MicroWire) */ +#define GB_SPI_MODE_MODE_1 (0|GB_SPI_MODE_CPHA) +#define GB_SPI_MODE_MODE_2 (GB_SPI_MODE_CPOL|0) +#define GB_SPI_MODE_MODE_3 (GB_SPI_MODE_CPOL|GB_SPI_MODE_CPHA) +#define GB_SPI_MODE_CS_HIGH 0x04 /* chipselect active high? */ +#define GB_SPI_MODE_LSB_FIRST 0x08 /* per-word bits-on-wire */ +#define GB_SPI_MODE_3WIRE 0x10 /* SI/SO signals shared */ +#define GB_SPI_MODE_LOOP 0x20 /* loopback mode */ +#define GB_SPI_MODE_NO_CS 0x40 /* 1 dev/bus, no chipselect */ +#define GB_SPI_MODE_READY 0x80 /* slave pulls low to pause */ + +/* Should match up with flags in linux/spi/spi.h */ +#define GB_SPI_FLAG_HALF_DUPLEX BIT(0) /* can't do full duplex */ +#define GB_SPI_FLAG_NO_RX BIT(1) /* can't do buffer read */ +#define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */ + +/* Greybus spi operation types */ +#define GB_SPI_TYPE_INVALID 0x00 +#define GB_SPI_TYPE_PROTOCOL_VERSION 0x01 +#define GB_SPI_TYPE_MODE 0x02 +#define GB_SPI_TYPE_FLAGS 0x03 +#define GB_SPI_TYPE_BITS_PER_WORD_MASK 0x04 +#define GB_SPI_TYPE_NUM_CHIPSELECT 0x05 +#define GB_SPI_TYPE_TRANSFER 0x06 + +/* mode request has no payload */ +struct gb_spi_mode_response { + __le16 mode; +}; + +/* flags request has no payload */ +struct gb_spi_flags_response { + __le16 flags; +}; + +/* bits-per-word request has no payload */ +struct gb_spi_bpw_response { + __le32 bits_per_word_mask; +}; + +/* num-chipselects request has no payload */ +struct gb_spi_chipselect_response { + __le16 num_chipselect; +}; + +/** + * struct gb_spi_transfer - a read/write buffer pair + * @speed_hz: Select a speed other than the device default for this transfer. If + * 0 the default (from @spi_device) is used. + * @len: size of rx and tx buffers (in bytes) + * @delay_usecs: microseconds to delay after this transfer before (optionally) + * changing the chipselect status, then starting the next transfer or + * completing this spi_message. + * @cs_change: affects chipselect after this transfer completes + * @bits_per_word: select a bits_per_word other than the device default for this + * transfer. If 0 the default (from @spi_device) is used. + */ +struct gb_spi_transfer { + __le32 speed_hz; + __le32 len; + __le16 delay_usecs; + __u8 cs_change; + __u8 bits_per_word; +}; + +struct gb_spi_transfer_request { + __u8 chip_select; /* of the spi device */ + __u8 mode; /* of the spi device */ + __le16 count; + struct gb_spi_transfer transfers[0]; /* trnasfer_count of these */ +}; + +struct gb_spi_transfer_response { + __u8 data[0]; /* inbound data */ +}; + +#endif /* __GB_GPBRIDGE_H__ */ -- cgit v1.2.3-59-g8ed1b From 8a760437229ac327796902098f402ef75955b520 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 12:56:46 -0500 Subject: greybus: endo: encapsulate computing the max interface id The maximum interface id on an Endo is the result of a non-trivial calculation. It'll be needed for an upcoming patch, so create a macro to compute it. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 5b5a3c65de8f..e4faa5cedb70 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -29,6 +29,20 @@ #define endo_back_left_ribs(id, ribs) (((id) >> (ribs)) & ENDO_BACK_SIDE_RIBS_MASK(ribs)) #define endo_back_right_ribs(id, ribs) ((id) & ENDO_BACK_SIDE_RIBS_MASK(ribs)) +/* + * An Endo has interface block positions on the front and back. + * Each has numeric ID, starting with 1 (interface 0 represents + * the SVC within the Endo itself). The maximum interface ID is the + * also the number of non-SVC interfaces possible on the endo. + * + * Total number of interfaces: + * - Front: 4 + * - Back left: max_ribs + 1 + * - Back right: max_ribs + 1 + */ +#define max_endo_interface_id(endo_layout) \ + (4 + ((endo_layout)->max_ribs + 1) * 2) + /* endo sysfs attributes */ static ssize_t serial_number_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -361,19 +375,12 @@ static int create_modules(struct gb_endo *endo) int prev_module_id = 0; int interface_id; int module_id; - int interfaces; + int max_id; - /* - * Total number of interfaces: - * - Front: 4 - * - Back: - * - Left: max_ribs + 1 - * - Right: max_ribs + 1 - */ - interfaces = 4 + (endo->layout.max_ribs + 1) * 2; + max_id = max_endo_interface_id(&endo->layout); /* Find module corresponding to each interface */ - for (interface_id = 1; interface_id <= interfaces; interface_id++) { + for (interface_id = 1; interface_id <= max_id; interface_id++) { module_id = endo_get_module_id(endo, interface_id); if (WARN_ON(!module_id)) -- cgit v1.2.3-59-g8ed1b From e45524f849434074ff91d75bb9360a08aa535ba4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 12:56:47 -0500 Subject: greybus: endo: rework some attributes The SVC is not the same as the Endo. There are some attributes (such as the Endo ID) that are independent of attributes of the SVC (like its version). The current "Endo attributes" are really SVC attributes. Rename a few functions and variables to reflect that. Add a new attribute group for Endo-specific attributes, and populate it with the Endo ID. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index e4faa5cedb70..72037fff2c31 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -44,35 +44,57 @@ (4 + ((endo_layout)->max_ribs + 1) * 2) /* endo sysfs attributes */ -static ssize_t serial_number_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t svc_serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct gb_endo *endo = to_gb_endo(dev); return sprintf(buf, "%s", &endo->svc_info.serial_number[0]); } -static DEVICE_ATTR_RO(serial_number); +static DEVICE_ATTR_RO(svc_serial_number); -static ssize_t version_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t svc_version_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct gb_endo *endo = to_gb_endo(dev); return sprintf(buf, "%s", &endo->svc_info.version[0]); } -static DEVICE_ATTR_RO(version); +static DEVICE_ATTR_RO(svc_version); + +static struct attribute *svc_attrs[] = { + &dev_attr_svc_serial_number.attr, + &dev_attr_svc_version.attr, + NULL, +}; + +static const struct attribute_group svc_group = { + .attrs = svc_attrs, + .name = "SVC", +}; + +static ssize_t endo_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_endo *endo = to_gb_endo(dev); + + return sprintf(buf, "0x%04x", endo->id); +} +static DEVICE_ATTR_RO(endo_id); static struct attribute *endo_attrs[] = { - &dev_attr_serial_number.attr, - &dev_attr_version.attr, + &dev_attr_endo_id.attr, NULL, }; + static const struct attribute_group endo_group = { .attrs = endo_attrs, - .name = "SVC", + .name = "Endo", }; + static const struct attribute_group *endo_groups[] = { &endo_group, + &svc_group, NULL, }; -- cgit v1.2.3-59-g8ed1b From 344943d2cdd85347637d0f3691c8c8bd34432247 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 12:56:48 -0500 Subject: greybus: endo: record AP interface id The AP resides in a particular position on an Endo, which is identified by an interface ID. (For now we'll assume the AP uses just one interface.) Record the this AP interface ID when creating an Endo. Add a sysfs attribute to display it as well. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 5 +++-- drivers/staging/greybus/endo.c | 19 +++++++++++++++++-- drivers/staging/greybus/endo.h | 4 +++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 7bfdbadb6250..223c396c9992 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -178,7 +178,8 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver { struct greybus_host_device *hd; struct gb_endo *endo; - u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC + u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC + u8 ap_intf_id = 0x01; // FIXME - get AP interface ID from the SVC /* * Validate that the driver implements all of the callbacks @@ -212,7 +213,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; - endo = gb_endo_create(hd, endo_id); + endo = gb_endo_create(hd, endo_id, ap_intf_id); if (IS_ERR(endo)) { greybus_remove_hd(hd); return ERR_CAST(endo); diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 72037fff2c31..7128a0478cd3 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -82,8 +82,18 @@ static ssize_t endo_id_show(struct device *dev, } static DEVICE_ATTR_RO(endo_id); +static ssize_t ap_intf_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_endo *endo = to_gb_endo(dev); + + return sprintf(buf, "0x%02x", endo->ap_intf_id); +} +static DEVICE_ATTR_RO(ap_intf_id); + static struct attribute *endo_attrs[] = { &dev_attr_endo_id.attr, + &dev_attr_ap_intf_id.attr, NULL, }; @@ -452,7 +462,8 @@ static int gb_endo_register(struct greybus_host_device *hd, return retval; } -struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id) +struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id, + u8 ap_intf_id) { struct gb_endo *endo; int retval; @@ -466,8 +477,12 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id) retval = -EINVAL; goto free_endo; } - + if (ap_intf_id > max_endo_interface_id(&endo->layout)) { + retval = -EINVAL; + goto free_endo; + } endo->id = endo_id; + endo->ap_intf_id = ap_intf_id; /* Register Endo device */ retval = gb_endo_register(hd, endo); diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index c1ccbcd576f3..01ef5c86bf5a 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -40,6 +40,7 @@ struct gb_endo { struct endo_layout layout; struct gb_svc_info svc_info; u16 id; + u8 ap_intf_id; }; #define to_gb_endo(d) container_of(d, struct gb_endo, dev) @@ -47,7 +48,8 @@ struct gb_endo { /* Greybus "private" definitions */ struct greybus_host_device; -struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id); +struct gb_endo *gb_endo_create(struct greybus_host_device *hd, + u16 endo_id, u8 ap_intf_id); void gb_endo_remove(struct gb_endo *endo); u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id); -- cgit v1.2.3-59-g8ed1b From eb765e4e91c8614c11ef2c4af7ce1222a30d6b79 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 12:56:49 -0500 Subject: greybus: core: don't set up endo until host device is initialized Currently, the data structure representing an Endo is set up at the time a host device gets created. This is too early. Once the control infrastructure is in place, there's no sense in setting up the Endo utnil after we have heard from the SVC via a probe operation on our control CPort. And even then, there's no real point until we've successfully authenticated with the SVC, which will be indicated by the arrival of the Control protocol "connected" operation request notifying us that our SVC CPort is operational. In addition to this logical argument, we also can't actually receive any messages on the Control CPort until the host device is set up and ready to receive messages. At the point we're currently setting up the Endo data structure, that has not yet been done. Define a new exported function greybus_endo_setup(), which will be used (for now) as the entry point for setting up the Endo data structure. Arrange to call it in the host USB driver probe method, *after* we are set up for handling messages. Note: Once the control protocol has been implemented, this function may no longer need to be exported. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 22 +++++++++++++--------- drivers/staging/greybus/es1.c | 13 +++++++++++++ drivers/staging/greybus/es2.c | 13 +++++++++++++ drivers/staging/greybus/greybus.h | 2 ++ 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 223c396c9992..290bee58511f 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -177,9 +177,6 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver size_t buffer_size_max) { struct greybus_host_device *hd; - struct gb_endo *endo; - u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC - u8 ap_intf_id = 0x01; // FIXME - get AP interface ID from the SVC /* * Validate that the driver implements all of the callbacks @@ -213,16 +210,23 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; + return hd; +} +EXPORT_SYMBOL_GPL(greybus_create_hd); + +int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id, + u8 ap_intf_id) +{ + struct gb_endo *endo; + endo = gb_endo_create(hd, endo_id, ap_intf_id); - if (IS_ERR(endo)) { - greybus_remove_hd(hd); - return ERR_CAST(endo); - } + if (IS_ERR(endo)) + return PTR_ERR(endo); hd->endo = endo; - return hd; + return 0; } -EXPORT_SYMBOL_GPL(greybus_create_hd); +EXPORT_SYMBOL_GPL(greybus_endo_setup); void greybus_remove_hd(struct greybus_host_device *hd) { diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 1ed10f4b8e49..f6539549d27f 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -551,6 +551,8 @@ static int ap_probe(struct usb_interface *interface, bool bulk_out_found = false; int retval = -ENOMEM; int i; + u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC + u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -659,6 +661,17 @@ static int ap_probe(struct usb_interface *interface, gb_debugfs_get(), es1, &apb1_log_enable_fops); + /* + * XXX Soon this will be initiated later, with a combination + * XXX of a Control protocol probe operation and a + * XXX subsequent Control protocol connected operation for + * XXX the SVC connection. At that point we know we're + * XXX properly connected to an Endo. + */ + retval = greybus_endo_setup(hd, endo_id, ap_intf_id); + if (retval) + goto error; + return 0; error: ap_disconnect(interface); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 4733adcb8a22..4f676cf3c583 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -551,6 +551,8 @@ static int ap_probe(struct usb_interface *interface, bool bulk_out_found = false; int retval = -ENOMEM; int i; + u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC + u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -659,6 +661,17 @@ static int ap_probe(struct usb_interface *interface, gb_debugfs_get(), es1, &apb1_log_enable_fops); + /* + * XXX Soon this will be initiated later, with a combination + * XXX of a Control protocol probe operation and a + * XXX subsequent Control protocol connected operation for + * XXX the SVC connection. At that point we know we're + * XXX properly connected to an Endo. + */ + retval = greybus_endo_setup(hd, endo_id, ap_intf_id); + if (retval) + goto error; + return 0; error: ap_disconnect(interface); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4920458d65d0..30a93eafe064 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -108,6 +108,8 @@ struct greybus_host_device { struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, struct device *parent, size_t buffer_size_max); +int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id, + u8 ap_intf_id); void greybus_remove_hd(struct greybus_host_device *hd); struct greybus_driver { -- cgit v1.2.3-59-g8ed1b From 2de1a8b1a95ce36288b66a9b7c9f08ea82061b5f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 12:59:15 -0500 Subject: greybus: add copyright statements The Greybus audio source files included no copyright statements. Add them. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-dai.c | 9 +++++++++ drivers/staging/greybus/audio-gb-cmds.c | 9 +++++++++ drivers/staging/greybus/audio-pcm.c | 9 +++++++++ drivers/staging/greybus/audio.c | 9 +++++++++ drivers/staging/greybus/audio.h | 9 +++++++++ 5 files changed, 45 insertions(+) diff --git a/drivers/staging/greybus/audio-dai.c b/drivers/staging/greybus/audio-dai.c index 0dd7364785ae..9b162bfc2d17 100644 --- a/drivers/staging/greybus/audio-dai.c +++ b/drivers/staging/greybus/audio-dai.c @@ -1,3 +1,12 @@ +/* + * Greybus audio Digital Audio Interface (DAI) driver + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + #include #include #include diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index f6a29abca70f..9ca3164b1332 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -1,3 +1,12 @@ +/* + * Greybus audio commands + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + #include #include "greybus.h" diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c index a1faf7895b58..c1b6aa551ce1 100644 --- a/drivers/staging/greybus/audio-pcm.c +++ b/drivers/staging/greybus/audio-pcm.c @@ -1,3 +1,12 @@ +/* + * Greybus audio Pulse Code Modulation (PCM) driver + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + #include #include #include diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index c873f339da1d..57c738b1293f 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -1,3 +1,12 @@ +/* + * Greybus audio driver + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + #include #include #include diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index c9087c9fc15c..da95c1b3cb1e 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -1,3 +1,12 @@ +/* + * Greybus audio + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + #ifndef __GB_AUDIO_H #define __GB_AUDIO_H #include -- cgit v1.2.3-59-g8ed1b From 4441f4759cfdf12840999676d2428a56f6248d8e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 12:59:16 -0500 Subject: greybus: update copyrights Update the copyright statements for recently-modified source files. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 ++-- drivers/staging/greybus/endo.c | 4 ++-- drivers/staging/greybus/endo.h | 1 + drivers/staging/greybus/es1.c | 6 +++--- drivers/staging/greybus/greybus.h | 4 ++-- drivers/staging/greybus/greybus_manifest.h | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 290bee58511f..6106175b57c5 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -1,8 +1,8 @@ /* * Greybus "Core" * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 7128a0478cd3..1e8485b07640 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -1,8 +1,8 @@ /* * Greybus endo code * - * Copyright 2015 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index 01ef5c86bf5a..e211fb7d53bc 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -2,6 +2,7 @@ * Greybus endo code * * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index f6539549d27f..2acfe74fc7b7 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -1,8 +1,8 @@ /* - * Greybus "AP" USB driver + * Greybus "AP" USB driver for "ES1" controller chips * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 30a93eafe064..b9653f629a0a 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -1,8 +1,8 @@ /* * Greybus driver and device API * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 076d7114cb4e..de1b1dbefc55 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -4,8 +4,8 @@ * See "Greybus Application Protocol" document (version 0.1) for * details on these values and structures. * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 and BSD licenses. */ -- cgit v1.2.3-59-g8ed1b From 30c6d9d753724bc616ee1e2ca5cadc03747b8b07 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 22 May 2015 13:02:08 -0500 Subject: greybus: introduce SVC protocol This patch adds support for the Greybus SVC protocol. We may want to rearrange protocol numbers at some point, since this is a pretty fundamental protocol. Note: It has only been compile tested; no SVC CPorts have yet been defined, so this code is not yet exercised. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/greybus_protocols.h | 57 +++++++ drivers/staging/greybus/svc.c | 256 ++++++++++++++++++++++++++++ drivers/staging/greybus/svc.h | 22 +++ 6 files changed, 339 insertions(+) create mode 100644 drivers/staging/greybus/svc.c create mode 100644 drivers/staging/greybus/svc.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index e5cae29d5a32..42804547ba24 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -25,6 +25,7 @@ gb-phy-y := gpbridge.o \ audio-gb-cmds.o # Prefix all modules with gb- +gb-svc-y := svc.o gb-vibrator-y := vibrator.o gb-battery-y := battery.o gb-loopback-y := loopback.o @@ -33,6 +34,7 @@ gb-es1-y := es1.o gb-es2-y := es2.o obj-m += greybus.o +obj-m += gb-svc.o obj-m += gb-phy.o obj-m += gb-vibrator.o obj-m += gb-battery.o diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index b9653f629a0a..86b2f9c67d0b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -26,6 +26,7 @@ #include "greybus_protocols.h" #include "manifest.h" #include "endo.h" +#include "svc.h" #include "module.h" #include "interface.h" #include "bundle.h" diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index de1b1dbefc55..d6175cec6c17 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -42,6 +42,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_LOOPBACK = 0x11, GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12, GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13, + GREYBUS_PROTOCOL_SVC = 0x14, /* ... */ GREYBUS_PROTOCOL_RAW = 0xfe, GREYBUS_PROTOCOL_VENDOR = 0xff, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c8208054d942..28c40a09a05b 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -489,4 +489,61 @@ struct gb_spi_transfer_response { __u8 data[0]; /* inbound data */ }; +/* Version of the Greybus SVC protocol we support */ +#define GB_SVC_VERSION_MAJOR 0x00 +#define GB_SVC_VERSION_MINOR 0x01 + +/* Greybus SVC request types */ +#define GB_SVC_TYPE_INVALID 0x00 +#define GB_SVC_TYPE_PROTOCOL_VERSION 0x01 +#define GB_SVC_TYPE_INTF_DEVICE_ID 0x02 +#define GB_SVC_TYPE_INTF_HOTPLUG 0x03 +#define GB_SVC_TYPE_INTF_HOT_UNPLUG 0x04 +#define GB_SVC_TYPE_INTF_RESET 0x05 +#define GB_SVC_TYPE_CONN_CREATE 0x06 +#define GB_SVC_TYPE_CONN_DESTROY 0x07 + +struct gb_svc_intf_device_id_request { + __u8 intf_id; + __u8 device_id; +}; +/* device id response has no payload */ + +struct gb_svc_intf_hotplug_request { + __u8 intf_id; + struct { + __le32 unipro_mfg_id; + __le32 unipro_prod_id; + __le32 ara_vend_id; + __le32 ara_prod_id; + } data; +}; +/* hotplug response has no payload */ + +struct gb_svc_intf_hot_unplug_request { + __u8 intf_id; +}; +/* hot unplug response has no payload */ + +struct gb_svc_intf_reset_request { + __u8 intf_id; +}; +/* interface reset response has no payload */ + +struct gb_svc_conn_create_request { + __u8 intf1_id; + __u16 cport1_id; + __u8 intf2_id; + __u16 cport2_id; +}; +/* connection create response has no payload */ + +struct gb_svc_conn_destroy_request { + __u8 intf1_id; + __u16 cport1_id; + __u8 intf2_id; + __u16 cport2_id; +}; +/* connection destroy response has no payload */ + #endif /* __GB_GPBRIDGE_H__ */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c new file mode 100644 index 000000000000..e39eddbbcb9e --- /dev/null +++ b/drivers/staging/greybus/svc.c @@ -0,0 +1,256 @@ +/* + * SVC Greybus driver. + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include + +#include "greybus.h" +#include "greybus_protocols.h" + +struct gb_svc { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; +}; + +/* Define get_version() routine */ +define_get_version(gb_svc, SVC); + +static int intf_device_id_operation(struct gb_svc *svc, + u8 intf_id, u8 device_id) +{ + struct gb_svc_intf_device_id_request request; + + request.intf_id = intf_id; + request.device_id = device_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID, + &request, sizeof(request), NULL, 0); +} + +static int intf_reset_operation(struct gb_svc *svc, u8 intf_id) +{ + struct gb_svc_intf_reset_request request; + + request.intf_id = intf_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET, + &request, sizeof(request), NULL, 0); +} + +static int connection_create_operation(struct gb_svc *svc, + u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) +{ + struct gb_svc_conn_create_request request; + + request.intf1_id = intf1_id; + request.cport1_id = cport1_id; + request.intf2_id = intf2_id; + request.cport2_id = cport2_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, + &request, sizeof(request), NULL, 0); +} + +static int connection_destroy_operation(struct gb_svc *svc, + u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) +{ + struct gb_svc_conn_destroy_request request; + + request.intf1_id = intf1_id; + request.cport1_id = cport1_id; + request.intf2_id = intf2_id; + request.cport2_id = cport2_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_DESTROY, + &request, sizeof(request), NULL, 0); +} + +int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) +{ + return intf_device_id_operation(svc, intf_id, device_id); +} +EXPORT_SYMBOL_GPL(gb_svc_intf_device_id); + +int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id) +{ + return intf_reset_operation(svc, intf_id); +} +EXPORT_SYMBOL_GPL(gb_svc_intf_reset); + +int gb_svc_connection_create(struct gb_svc *svc, + u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) +{ + return connection_create_operation(svc, intf1_id, cport1_id, + intf2_id, cport2_id); +} +EXPORT_SYMBOL_GPL(gb_svc_connection_create); + +int gb_svc_connection_destroy(struct gb_svc *svc, + u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) +{ + return connection_destroy_operation(svc, intf1_id, cport1_id, + intf2_id, cport2_id); +} +EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); + +static int gb_svc_intf_hotplug_recv(struct gb_operation *op) +{ + struct gb_message *request = op->request; + struct gb_svc_intf_hotplug_request *hotplug; + u8 intf_id; + u32 unipro_mfg_id; + u32 unipro_prod_id; + u32 ara_vend_id; + u32 ara_prod_id; + + if (request->payload_size < sizeof(*hotplug)) { + dev_err(&op->connection->dev, + "short hotplug request received\n"); + return -EINVAL; + } + hotplug = request->payload; + + /* + * Grab the information we need. + * + * XXX I'd really like to acknowledge receipt, and then + * XXX continue processing the request. There's no need + * XXX for the SVC to wait. In fact, it might be best to + * XXX have the SVC get acknowledgement before we proceed. + * */ + intf_id = hotplug->intf_id; + unipro_mfg_id = hotplug->data.unipro_mfg_id; + unipro_prod_id = hotplug->data.unipro_prod_id; + ara_vend_id = hotplug->data.ara_vend_id; + ara_prod_id = hotplug->data.ara_prod_id; + + /* FIXME Set up the interface here; may required firmware download */ + + return 0; +} + +static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) +{ + struct gb_message *request = op->request; + struct gb_svc_intf_hot_unplug_request *hot_unplug; + u8 intf_id; + + if (request->payload_size < sizeof(*hot_unplug)) { + dev_err(&op->connection->dev, + "short hot unplug request received\n"); + return -EINVAL; + } + hot_unplug = request->payload; + + intf_id = hot_unplug->intf_id; + + /* FIXME Tear down the interface here */ + + return 0; + +} + +static int gb_svc_intf_reset_recv(struct gb_operation *op) +{ + struct gb_message *request = op->request; + struct gb_svc_intf_reset_request *reset; + u8 intf_id; + + if (request->payload_size < sizeof(*reset)) { + dev_err(&op->connection->dev, + "short reset request received\n"); + return -EINVAL; + } + reset = request->payload; + + intf_id = reset->intf_id; + + /* FIXME Reset the interface here */ + + return 0; +} + +static int gb_svc_request_recv(u8 type, struct gb_operation *op) +{ + switch (type) { + case GB_SVC_TYPE_INTF_HOTPLUG: + return gb_svc_intf_hotplug_recv(op); + case GB_SVC_TYPE_INTF_HOT_UNPLUG: + return gb_svc_intf_hot_unplug_recv(op); + case GB_SVC_TYPE_INTF_RESET: + return gb_svc_intf_reset_recv(op); + default: + dev_err(&op->connection->dev, + "unsupported request: %hhu\n", type); + return -EINVAL; + } +} + +/* + * Do initial setup of the SVC. + */ +static int gb_svc_device_setup(struct gb_svc *gb_svc) +{ + /* First thing we need to do is check the version */ + return get_version(gb_svc); +} + +static int gb_svc_connection_init(struct gb_connection *connection) +{ + struct gb_svc *svc; + int ret; + + svc = kzalloc(sizeof(*svc), GFP_KERNEL); + if (!svc) + return -ENOMEM; + + svc->connection = connection; + connection->private = svc; + ret = gb_svc_device_setup(svc); + if (ret) + kfree(svc); + + return ret; +} + +static void gb_svc_connection_exit(struct gb_connection *connection) +{ + struct gb_svc *svc = connection->private; + + if (!svc) + return; + + kfree(svc); +} + +static struct gb_protocol svc_protocol = { + .name = "svc", + .id = GREYBUS_PROTOCOL_SVC, + .major = 0, + .minor = 1, + .connection_init = gb_svc_connection_init, + .connection_exit = gb_svc_connection_exit, + .request_recv = gb_svc_request_recv, +}; + +int gb_svc_protocol_init(void) +{ + return gb_protocol_register(&svc_protocol); +} + +void gb_svc_protocol_exit(void) +{ + gb_protocol_deregister(&svc_protocol); +} diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h new file mode 100644 index 000000000000..e26e63707e3d --- /dev/null +++ b/drivers/staging/greybus/svc.h @@ -0,0 +1,22 @@ +/* + * Greybus SVC code + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __SVC_H +#define __SVC_H + +struct gb_svc; + +int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); +int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); +int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id); +int gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id); + +#endif /* __SVC_H */ -- cgit v1.2.3-59-g8ed1b From 1d771fe41e629fad9077a0a1a9cf2771c9a5287d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 26 May 2015 15:29:18 +0200 Subject: greybus: operation: add support for incoming unidirectional operations Add support for incoming, unidirectional operations where the sender of a request does not care about a response. Unidirectional operations have an operation id of 0. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f595b97fa900..4f7a555e4b96 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -686,6 +686,10 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) return -EIO; /* Shouldn't happen */ } + /* Sender of request does not care about response. */ + if (!operation->id) + return 0; + if (!operation->response) { if (!gb_operation_response_alloc(operation, 0)) { dev_err(&connection->dev, -- cgit v1.2.3-59-g8ed1b From c8d1ad8013681eeb2dc8dac405db3b95284adc1d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 26 May 2015 15:29:19 +0200 Subject: greybus: gpio: fix debugfs output Fix debugfs output by removing the unimplemented, custom dbg_show callback. The default implementation is perfectly sufficient. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 6e5fe5b3db39..0191bb809968 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -414,11 +414,6 @@ static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, return gb_gpio_set_debounce_operation(ggc, (u8)offset, usec); } -static void gb_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) -{ - return; /* XXX */ -} - static int gb_gpio_controller_setup(struct gb_gpio_controller *ggc) { int ret; @@ -616,7 +611,6 @@ static int gb_gpio_connection_init(struct gb_connection *connection) gpio->get = gb_gpio_get; gpio->set = gb_gpio_set; gpio->set_debounce = gb_gpio_set_debounce; - gpio->dbg_show = gb_gpio_dbg_show; gpio->to_irq = gb_gpio_to_irq; gpio->base = -1; /* Allocate base dynamically */ gpio->ngpio = ggc->line_max + 1; -- cgit v1.2.3-59-g8ed1b From b8e3ffebac09b29ad4cc0bdbcafbbd77b3278685 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 26 May 2015 15:29:20 +0200 Subject: greybus: gpio: remove unused irq-ack operation Remove unused irq-ack operation, which has never been called and does not make sense for message-signalled interrupts over slow buses. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 16 ---------------- drivers/staging/greybus/greybus_protocols.h | 6 ------ 2 files changed, 22 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 0191bb809968..c570f62046f5 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -211,21 +211,6 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, return ret; } -static void gb_gpio_ack_irq(struct irq_data *d) -{ - struct gpio_chip *chip = irq_data_to_gpio_chip(d); - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); - struct gb_gpio_irq_ack_request request; - int ret; - - request.which = d->hwirq; - ret = gb_operation_sync(ggc->connection, - GB_GPIO_TYPE_IRQ_ACK, - &request, sizeof(request), NULL, 0); - if (ret) - dev_err(chip->dev, "failed to ack irq: %d\n", ret); -} - static void gb_gpio_mask_irq(struct irq_data *d) { struct gpio_chip *chip = irq_data_to_gpio_chip(d); @@ -591,7 +576,6 @@ static int gb_gpio_connection_init(struct gb_connection *connection) goto err_free_controller; irqc = &ggc->irqc; - irqc->irq_ack = gb_gpio_ack_irq; irqc->irq_mask = gb_gpio_mask_irq; irqc->irq_unmask = gb_gpio_unmask_irq; irqc->irq_set_type = gb_gpio_irq_set_type; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 28c40a09a05b..0fd42bc44161 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -128,7 +128,6 @@ struct gb_i2c_transfer_response { #define GB_GPIO_TYPE_SET_VALUE 0x09 #define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a #define GB_GPIO_TYPE_IRQ_TYPE 0x0b -#define GB_GPIO_TYPE_IRQ_ACK 0x0c #define GB_GPIO_TYPE_IRQ_MASK 0x0d #define GB_GPIO_TYPE_IRQ_UNMASK 0x0e #define GB_GPIO_TYPE_IRQ_EVENT 0x0f @@ -203,11 +202,6 @@ struct gb_gpio_irq_unmask_request { }; /* irq unmask response has no payload */ -struct gb_gpio_irq_ack_request { - __u8 which; -}; -/* irq ack response has no payload */ - /* irq event requests originate on another module and are handled on the AP */ struct gb_gpio_irq_event_request { __u8 which; -- cgit v1.2.3-59-g8ed1b From 2611ebef8322fc12dc3c6b0ec869f1902aa25626 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 26 May 2015 15:29:21 +0200 Subject: greybus: gpio: don't call irq-flow handler directly Use generic_handle_irq_desc rather than call a hardcoded irq-flow handler directly. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index c570f62046f5..526dd7e73a2f 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -314,9 +314,8 @@ static int gb_gpio_request_recv(u8 type, struct gb_operation *op) return -EINVAL; } - /* Dispatch interrupt */ local_irq_disable(); - handle_simple_irq(irq, desc); + generic_handle_irq_desc(irq, desc); local_irq_enable(); return 0; -- cgit v1.2.3-59-g8ed1b From ec762115a5006db8549b3582f7f19849f7cf4ab4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 26 May 2015 15:29:22 +0200 Subject: greybus: gpio: use irq-domain lookups Use irq_find_mapping directly rather than go through the legacy gpio interface. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 526dd7e73a2f..15cc0ea4e3a5 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -303,9 +303,9 @@ static int gb_gpio_request_recv(u8 type, struct gb_operation *op) return -EINVAL; } - irq = gpio_to_irq(ggc->chip.base + event->which); - if (irq < 0) { - dev_err(ggc->chip.dev, "failed to map irq\n"); + irq = irq_find_mapping(ggc->irqdomain, event->which); + if (!irq) { + dev_err(ggc->chip.dev, "failed to find IRQ\n"); return -EINVAL; } desc = irq_to_desc(irq); -- cgit v1.2.3-59-g8ed1b From 0cb918d72d8f76b1189a63cefedbc5cb4dad9d54 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 26 May 2015 15:29:23 +0200 Subject: greybus: gpio: rename irq mask and unmask callbacks Rename irq mask and unmask functions to match the callback names. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 15cc0ea4e3a5..0220a9f1ff9c 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -211,7 +211,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, return ret; } -static void gb_gpio_mask_irq(struct irq_data *d) +static void gb_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *chip = irq_data_to_gpio_chip(d); struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); @@ -226,7 +226,7 @@ static void gb_gpio_mask_irq(struct irq_data *d) dev_err(chip->dev, "failed to mask irq: %d\n", ret); } -static void gb_gpio_unmask_irq(struct irq_data *d) +static void gb_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *chip = irq_data_to_gpio_chip(d); struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); @@ -575,8 +575,8 @@ static int gb_gpio_connection_init(struct gb_connection *connection) goto err_free_controller; irqc = &ggc->irqc; - irqc->irq_mask = gb_gpio_mask_irq; - irqc->irq_unmask = gb_gpio_unmask_irq; + irqc->irq_mask = gb_gpio_irq_mask; + irqc->irq_unmask = gb_gpio_irq_unmask; irqc->irq_set_type = gb_gpio_irq_set_type; irqc->name = "greybus_gpio"; -- cgit v1.2.3-59-g8ed1b From 25f11ed965bb57b6e25d5464a8cd8b3657319056 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 26 May 2015 15:29:24 +0200 Subject: greybus: gpio: fix atomic sleep when using interrupts The irq-chip callbacks are made in atomic context where we must not do any synchronous greybus operations. Fix the current gpio-interrupt implementation by using the bus-lock functionality provided by the irq subsystem. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 115 ++++++++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 0220a9f1ff9c..6cf3bb151f61 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -13,6 +13,8 @@ #include #include #include +#include + #include "greybus.h" struct gb_gpio_line { @@ -22,6 +24,11 @@ struct gb_gpio_line { direction: 1, /* 0 = output, 1 = input */ value: 1; /* 0 = low, 1 = high */ u16 debounce_usec; + + u8 irq_type; + bool irq_type_pending; + bool masked; + bool masked_pending; }; struct gb_gpio_controller { @@ -38,6 +45,7 @@ struct gb_gpio_controller { unsigned int irq_base; irq_flow_handler_t irq_handler; unsigned int irq_default_type; + struct mutex irq_lock; }; #define gpio_chip_to_gb_gpio_controller(chip) \ container_of(chip, struct gb_gpio_controller, chip) @@ -211,68 +219,121 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, return ret; } -static void gb_gpio_irq_mask(struct irq_data *d) +static void _gb_gpio_irq_mask(struct gb_gpio_controller *ggc, u8 hwirq) { - struct gpio_chip *chip = irq_data_to_gpio_chip(d); - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); struct gb_gpio_irq_mask_request request; int ret; - request.which = d->hwirq; + request.which = hwirq; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_IRQ_MASK, &request, sizeof(request), NULL, 0); if (ret) - dev_err(chip->dev, "failed to mask irq: %d\n", ret); + dev_err(ggc->chip.dev, "failed to mask irq: %d\n", ret); } -static void gb_gpio_irq_unmask(struct irq_data *d) +static void _gb_gpio_irq_unmask(struct gb_gpio_controller *ggc, u8 hwirq) { - struct gpio_chip *chip = irq_data_to_gpio_chip(d); - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); struct gb_gpio_irq_unmask_request request; int ret; - request.which = d->hwirq; + request.which = hwirq; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_IRQ_UNMASK, &request, sizeof(request), NULL, 0); if (ret) - dev_err(chip->dev, "failed to unmask irq: %d\n", ret); + dev_err(ggc->chip.dev, "failed to unmask irq: %d\n", ret); } -static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) +static void _gb_gpio_irq_set_type(struct gb_gpio_controller *ggc, + u8 hwirq, u8 type) { - struct gpio_chip *chip = irq_data_to_gpio_chip(d); - struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); struct gb_gpio_irq_type_request request; - int ret = 0; + int ret; - request.which = d->hwirq; + request.which = hwirq; request.type = type; + ret = gb_operation_sync(ggc->connection, + GB_GPIO_TYPE_IRQ_TYPE, + &request, sizeof(request), NULL, 0); + if (ret) + dev_err(ggc->chip.dev, "failed to set irq type: %d\n", ret); +} + +static void gb_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_to_gpio_chip(d); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_line *line = &ggc->lines[d->hwirq]; + + line->masked = true; + line->masked_pending = true; +} + +static void gb_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_to_gpio_chip(d); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_line *line = &ggc->lines[d->hwirq]; + + line->masked = false; + line->masked_pending = true; +} + +static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *chip = irq_data_to_gpio_chip(d); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_line *line = &ggc->lines[d->hwirq]; + switch (type) { case IRQ_TYPE_NONE: - break; case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_EDGE_BOTH: case IRQ_TYPE_LEVEL_LOW: case IRQ_TYPE_LEVEL_HIGH: - ret = gb_operation_sync(ggc->connection, - GB_GPIO_TYPE_IRQ_TYPE, - &request, sizeof(request), NULL, 0); - if (ret) { - dev_err(chip->dev, "failed to set irq type: %d\n", - ret); - } break; default: dev_err(chip->dev, "unsupported irq type: %u\n", type); - ret = -EINVAL; + return -EINVAL; } - return ret; + line->irq_type = type; + line->irq_type_pending = true; + + return 0; +} + +static void gb_gpio_irq_bus_lock(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_to_gpio_chip(d); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + + mutex_lock(&ggc->irq_lock); +} + +static void gb_gpio_irq_bus_sync_unlock(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_to_gpio_chip(d); + struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); + struct gb_gpio_line *line = &ggc->lines[d->hwirq]; + + if (line->irq_type_pending) { + _gb_gpio_irq_set_type(ggc, d->hwirq, line->irq_type); + line->irq_type_pending = false; + } + + if (line->masked_pending) { + if (line->masked) + _gb_gpio_irq_mask(ggc, d->hwirq); + else + _gb_gpio_irq_unmask(ggc, d->hwirq); + line->masked_pending = false; + } + + mutex_unlock(&ggc->irq_lock); } static int gb_gpio_request_recv(u8 type, struct gb_operation *op) @@ -578,8 +639,12 @@ static int gb_gpio_connection_init(struct gb_connection *connection) irqc->irq_mask = gb_gpio_irq_mask; irqc->irq_unmask = gb_gpio_irq_unmask; irqc->irq_set_type = gb_gpio_irq_set_type; + irqc->irq_bus_lock = gb_gpio_irq_bus_lock; + irqc->irq_bus_sync_unlock = gb_gpio_irq_bus_sync_unlock; irqc->name = "greybus_gpio"; + mutex_init(&ggc->irq_lock); + gpio = &ggc->chip; gpio->label = "greybus_gpio"; -- cgit v1.2.3-59-g8ed1b From 1409c4d6a841b00f3b4bdf010808a81eda6b7727 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 26 May 2015 15:29:25 +0200 Subject: greybus: gpio: fix interrupt protocol The current interrupt implementation uses the simple irq-flow handler, which means that the interrupt subsystem makes no irq-chip callbacks when handling an interrupt. Specifically, no end-of-interrupt message is sent when the threaded handler has run. This means that we may currently re-enable an interrupt before it has been serviced (i.e. the irq-event operation may complete before the threaded handler has run). The simple flow handler also silently drops a second interrupt arriving while a handler is running. This means that we may lose a second edge interrupt with the current firmware. Switch to a new one-shot interrupt protocol, where the primary handler (firmware) always masks and acks an interrupt before sending an event to the AP. The AP is responsible for unmasking the interrupt when it has been handled. By having the firmware ack an edge interrupt before sending the event, a second edge interrupt will no longer get lost. This one-shot protocol can be implemented in the kernel by using the level irq-flow handler, one-shot interrupts with threaded handlers and bus-lock synchronisation for slow buses. Note that the same flow handler is used for both edge and level interrupts. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 2 +- drivers/staging/greybus/greybus_protocols.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 6cf3bb151f61..8dad9e579881 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -672,7 +672,7 @@ static int gb_gpio_connection_init(struct gb_connection *connection) } ret = gb_gpio_irqchip_add(gpio, irqc, 0, - handle_simple_irq, IRQ_TYPE_NONE); + handle_level_irq, IRQ_TYPE_NONE); if (ret) { dev_err(&connection->dev, "failed to add irq chip: %d\n", ret); goto irqchip_err; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 0fd42bc44161..81ec3b246cc6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -206,7 +206,7 @@ struct gb_gpio_irq_unmask_request { struct gb_gpio_irq_event_request { __u8 which; }; -/* irq event response has no payload */ +/* irq event has no response */ /* PWM */ -- cgit v1.2.3-59-g8ed1b From ea15a40b32dcc068a2af406ba894e2179c315469 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Wed, 27 May 2015 21:31:02 +0700 Subject: greybus: svc: Fix endian of hotplug request data data of hotplug request should exchange to native CPU format before assignment. Signed-off-by: Phong Tran Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index e39eddbbcb9e..ffbeac57769d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -131,10 +131,10 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) * XXX have the SVC get acknowledgement before we proceed. * */ intf_id = hotplug->intf_id; - unipro_mfg_id = hotplug->data.unipro_mfg_id; - unipro_prod_id = hotplug->data.unipro_prod_id; - ara_vend_id = hotplug->data.ara_vend_id; - ara_prod_id = hotplug->data.ara_prod_id; + unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); + unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); + ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id); + ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id); /* FIXME Set up the interface here; may required firmware download */ -- cgit v1.2.3-59-g8ed1b From b61fa7bce4acdae51e5b76c358237d1ad5dfdbc5 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Wed, 27 May 2015 21:31:03 +0700 Subject: greybus: svc: use macro for init and exit protocol Change to gb_gpbridge_protocol_driver for making the consitent with other drivers. Signed-off-by: Phong Tran Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ffbeac57769d..4dc10e3778c4 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -245,12 +245,4 @@ static struct gb_protocol svc_protocol = { .request_recv = gb_svc_request_recv, }; -int gb_svc_protocol_init(void) -{ - return gb_protocol_register(&svc_protocol); -} - -void gb_svc_protocol_exit(void) -{ - gb_protocol_deregister(&svc_protocol); -} +gb_gpbridge_protocol_driver(svc_protocol); -- cgit v1.2.3-59-g8ed1b From 47bf0b44275eaf4659f99b1ddde5da85018aaf46 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 27 May 2015 12:45:07 +0200 Subject: greybus: gpio: update operation types Update the remaining operation types now that the ack operation is gone to avoid leaving a hole in the type definitions. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 81ec3b246cc6..305c33d08646 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -128,9 +128,9 @@ struct gb_i2c_transfer_response { #define GB_GPIO_TYPE_SET_VALUE 0x09 #define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a #define GB_GPIO_TYPE_IRQ_TYPE 0x0b -#define GB_GPIO_TYPE_IRQ_MASK 0x0d -#define GB_GPIO_TYPE_IRQ_UNMASK 0x0e -#define GB_GPIO_TYPE_IRQ_EVENT 0x0f +#define GB_GPIO_TYPE_IRQ_MASK 0x0c +#define GB_GPIO_TYPE_IRQ_UNMASK 0x0d +#define GB_GPIO_TYPE_IRQ_EVENT 0x0e #define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ -- cgit v1.2.3-59-g8ed1b From 012d7d4fbeb2b261c679f58e39b31657ffb0191d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 27 May 2015 14:45:58 -0500 Subject: greybus: greybus_protocols: fix guard tag I neglected to update the "#ifndef/#define" when I renamed "greybus_protocols.h". Fix that. Signed-off-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 305c33d08646..32d84640e295 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -49,8 +49,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __GB_GPBRIDGE_H__ -#define __GB_GPBRIDGE_H__ +#ifndef __GREYBUS_PROTOCOLS_H +#define __GREYBUS_PROTOCOLS_H /* I2C */ @@ -540,4 +540,4 @@ struct gb_svc_conn_destroy_request { }; /* connection destroy response has no payload */ -#endif /* __GB_GPBRIDGE_H__ */ +#endif /* __GREYBUS_PROTOCOLS_H */ -- cgit v1.2.3-59-g8ed1b From bcd8215b49b08194ee6e2968106ac5a14825b689 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Thu, 28 May 2015 23:21:00 +0700 Subject: greybus: audio: Fix typo for macro I2S data version minor This macro should be DATA not MGMT. Signed-off-by: Phong Tran Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-gb-cmds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index 9ca3164b1332..10de34d6fa93 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -16,7 +16,7 @@ #define GB_I2S_MGMT_VERSION_MINOR 0x01 #define GB_I2S_DATA_VERSION_MAJOR 0x00 -#define GB_I2S_MGMT_VERSION_MINOR 0x01 +#define GB_I2S_DATA_VERSION_MINOR 0x01 /*********************************** * GB I2S helper functions -- cgit v1.2.3-59-g8ed1b From df76422113b0a80198bccf1a3d2515afe00db736 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Thu, 28 May 2015 23:21:01 +0700 Subject: greybus: audio: Remove the MODULE_LICENSE() This is not a kernel module. It should not use the module license macro. Signed-off-by: Phong Tran Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 57c738b1293f..a077c2bbaad6 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -470,6 +470,3 @@ void gb_audio_protocol_exit(void) gb_protocol_deregister(&gb_i2s_receiver_protocol); gb_protocol_deregister(&gb_i2s_mgmt_protocol); } - - -MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From b4dbf1e13268e5a7d88e8bdc4d4d0c481f8f53e7 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 27 May 2015 11:17:50 -0500 Subject: greybus: add documentation for Endo sysfs files Update the files documenting Greybus-related sysfs files under Documentation/ to reflect the addition of the two recently-added Endo attributes. Signed-off-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- .../staging/greybus/Documentation/sysfs-bus-greybus | 19 +++++++++++++++++++ .../Documentation/sysfs/endo-TYPE/Endo/ap_intf_id | 0 .../Documentation/sysfs/endo-TYPE/Endo/endo_id | 0 3 files changed, 19 insertions(+) create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/ap_intf_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/endo_id diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index ab728a46e657..6815b91a67d6 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -7,6 +7,25 @@ Description: replaced with the numeric value of the endo layout scheme as documented in the ARA Module Developer Kit. +What: /sys/bus/greybus/device/endo-XXXX/Endo/id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The Endo ID, which is a 2-byte hexadecimal value + defined by the the Endo layout scheme, documented in + the ARA Module Developer Kit. + +What: /sys/bus/greybus/device/endo-XXXX/Endo/ap_intf_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The AP interface ID, a small non-zero integer which + defines the position of the AP module on the Endo. + The interface positions are defined in the ARA + Module Developer Kit. + What: /sys/bus/greybus/device/endo-XXXX/SVC/serial_number Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/ap_intf_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/endo_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/endo_id new file mode 100644 index 000000000000..e69de29bb2d1 -- cgit v1.2.3-59-g8ed1b From 28e3cedcceecb62ecbc77a35eda3875bbe981f1b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 27 May 2015 11:17:51 -0500 Subject: greybus: update sysfs documentation files Impose a few editorial conventions on the Greybus-related sysfs files under "Documentation". - Capitalize "Endo" (except in path names) - Capitalize "ID" (except in path names) - Use "..." to indicate unspecified path components (because ".." means something else). - Add the "0x" prior to the "XXXX" representing the Endo ID. Signed-off-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- .../staging/greybus/Documentation/sysfs-bus-greybus | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 6815b91a67d6..738dcffe35e9 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -1,4 +1,4 @@ -What: /sys/bus/greybus/device/endo-XXXX +What: /sys/bus/greybus/device/endo-0xXXXX Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -7,7 +7,7 @@ Description: replaced with the numeric value of the endo layout scheme as documented in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo-XXXX/Endo/id +What: /sys/bus/greybus/device/endo-0xXXXX/Endo/id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -16,7 +16,7 @@ Description: defined by the the Endo layout scheme, documented in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo-XXXX/Endo/ap_intf_id +What: /sys/bus/greybus/device/endo-0xXXXX/Endo/ap_intf_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -26,21 +26,21 @@ Description: The interface positions are defined in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo-XXXX/SVC/serial_number +What: /sys/bus/greybus/device/endo-0xXXXX/SVC/serial_number Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The serial number of the SVC device -What: /sys/bus/greybus/device/endo-XXXX/SVC/version +What: /sys/bus/greybus/device/endo-0xXXXX/SVC/version Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The version number of the firmware in the SVC device. -What: /sys/bus/greybus/device/endo-XXXX/../epm +What: /sys/bus/greybus/device/endo-0xXXXX/.../epm Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -50,7 +50,7 @@ Description: Writing 1 to it turns it on, writing 0 to it turns it off. Reading the value returns if it is on or off. -What: /sys/bus/greybus/device/endo-XXXX/../power_control +What: /sys/bus/greybus/device/endo-0xXXXX/.../power_control Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -60,7 +60,7 @@ Description: to the module, writing 0 to it turns power off to the module. Reading the value returns if it is on or off. -What: /sys/bus/greybus/device/endo-XXXX/../present +What: /sys/bus/greybus/device/endo-0xXXXX/.../present Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -124,14 +124,14 @@ Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The protocol id of a Greybus connection. + The protocol ID of a Greybus connection. What: /sys/bus/greybus/device/.../device_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The device id of a Greybus bundle. + The device ID of a Greybus bundle. What: /sys/bus/greybus/device/.../state Date: October 2015 -- cgit v1.2.3-59-g8ed1b From 312bb84942cb6707cf9b3933a6c74cbe5e9ed037 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 27 May 2015 11:17:52 -0500 Subject: greybus: endo: drop redundant prefixes from sysfs basenames This commit: 7e761e2 endo: rework some attributes added a new "endo_id" attribute, located under a new "Endo" directory in sysfs. The resulting path looks like: Documentation/sysfs/endo-TYPE/Endo/endo_id There's no need to have a separate "Endo" subdirectory to contain Endo-specific attributes. That commit also added "svc_" to some other paths related to the SVC, like: Documentation/sysfs/endo-TYPE/SVC/svc_version The additional "svc_" is redundant. This patch retouches those paths a bit, mainly to remove some redundancy. It also makes the pathname components all lower case. As a result, the above two paths now look like: Documentation/sysfs/endo-TYPE/id Documentation/sysfs/endo-TYPE/svc/version All other Endo sysfs files are updated similarly. Signed-off-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- .../staging/greybus/Documentation/sysfs-bus-greybus | 8 ++++---- .../Documentation/sysfs/endo-TYPE/Endo/ap_intf_id | 0 .../Documentation/sysfs/endo-TYPE/Endo/endo_id | 0 .../Documentation/sysfs/endo-TYPE/SVC/firmware | 0 .../Documentation/sysfs/endo-TYPE/SVC/serial_number | 0 .../Documentation/sysfs/endo-TYPE/SVC/version | 0 .../Documentation/sysfs/endo-TYPE/ap_intf_id | 0 .../greybus/Documentation/sysfs/endo-TYPE/id | 0 .../Documentation/sysfs/endo-TYPE/svc/firmware | 0 .../Documentation/sysfs/endo-TYPE/svc/serial_number | 0 .../Documentation/sysfs/endo-TYPE/svc/version | 0 drivers/staging/greybus/endo.c | 21 ++++++++++----------- 12 files changed, 14 insertions(+), 15 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/ap_intf_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/endo_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/firmware delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/version create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/ap_intf_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/firmware create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/serial_number create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/version diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 738dcffe35e9..9cd3e02e5701 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -7,7 +7,7 @@ Description: replaced with the numeric value of the endo layout scheme as documented in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo-0xXXXX/Endo/id +What: /sys/bus/greybus/device/endo-0xXXXX/id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -16,7 +16,7 @@ Description: defined by the the Endo layout scheme, documented in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo-0xXXXX/Endo/ap_intf_id +What: /sys/bus/greybus/device/endo-0xXXXX/ap_intf_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -26,14 +26,14 @@ Description: The interface positions are defined in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo-0xXXXX/SVC/serial_number +What: /sys/bus/greybus/device/endo-0xXXXX/svc/serial_number Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The serial number of the SVC device -What: /sys/bus/greybus/device/endo-0xXXXX/SVC/version +What: /sys/bus/greybus/device/endo-0xXXXX/svc/version Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/ap_intf_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/endo_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/Endo/endo_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/firmware b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/firmware deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/serial_number b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/version b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/SVC/version deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/ap_intf_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/firmware b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/firmware new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/serial_number b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/version b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/version new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 1e8485b07640..aa89868726d9 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -44,43 +44,43 @@ (4 + ((endo_layout)->max_ribs + 1) * 2) /* endo sysfs attributes */ -static ssize_t svc_serial_number_show(struct device *dev, +static ssize_t serial_number_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_endo *endo = to_gb_endo(dev); return sprintf(buf, "%s", &endo->svc_info.serial_number[0]); } -static DEVICE_ATTR_RO(svc_serial_number); +static DEVICE_ATTR_RO(serial_number); -static ssize_t svc_version_show(struct device *dev, +static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_endo *endo = to_gb_endo(dev); return sprintf(buf, "%s", &endo->svc_info.version[0]); } -static DEVICE_ATTR_RO(svc_version); +static DEVICE_ATTR_RO(version); static struct attribute *svc_attrs[] = { - &dev_attr_svc_serial_number.attr, - &dev_attr_svc_version.attr, + &dev_attr_serial_number.attr, + &dev_attr_version.attr, NULL, }; static const struct attribute_group svc_group = { .attrs = svc_attrs, - .name = "SVC", + .name = "svc", }; -static ssize_t endo_id_show(struct device *dev, +static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_endo *endo = to_gb_endo(dev); return sprintf(buf, "0x%04x", endo->id); } -static DEVICE_ATTR_RO(endo_id); +static DEVICE_ATTR_RO(id); static ssize_t ap_intf_id_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -92,14 +92,13 @@ static ssize_t ap_intf_id_show(struct device *dev, static DEVICE_ATTR_RO(ap_intf_id); static struct attribute *endo_attrs[] = { - &dev_attr_endo_id.attr, + &dev_attr_id.attr, &dev_attr_ap_intf_id.attr, NULL, }; static const struct attribute_group endo_group = { .attrs = endo_attrs, - .name = "Endo", }; static const struct attribute_group *endo_groups[] = { -- cgit v1.2.3-59-g8ed1b From e6cebf8770472f0cd8ac8dd887316164822228a6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 27 May 2015 11:17:53 -0500 Subject: greybus: endo: delete "0xXXXX" portion in sysfs "endo" directory With the Endo "id" attribute in place, there's no need to encode the ID of an Endo in its sysfs path. So get rid of it. Signed-off-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 16 ++++++++-------- .../sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore | 1 - .../sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore | 1 - .../sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore | 1 - .../greybus/Documentation/sysfs/endo-TYPE/01/01/firmware | 0 .../greybus/Documentation/sysfs/endo-TYPE/01/01/manifest | 0 .../Documentation/sysfs/endo-TYPE/01/01/product_id | 0 .../Documentation/sysfs/endo-TYPE/01/01/product_name | 0 .../greybus/Documentation/sysfs/endo-TYPE/01/01/state | 0 .../greybus/Documentation/sysfs/endo-TYPE/01/01/uid | 0 .../Documentation/sysfs/endo-TYPE/01/01/version_major | 0 .../Documentation/sysfs/endo-TYPE/01/01/version_minor | 0 .../staging/greybus/Documentation/sysfs/endo-TYPE/01/epm | 0 .../greybus/Documentation/sysfs/endo-TYPE/01/power | 0 .../greybus/Documentation/sysfs/endo-TYPE/01/present | 0 .../greybus/Documentation/sysfs/endo-TYPE/02/.gitignore | 1 - .../greybus/Documentation/sysfs/endo-TYPE/ap_intf_id | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/id | 0 .../greybus/Documentation/sysfs/endo-TYPE/svc/firmware | 0 .../Documentation/sysfs/endo-TYPE/svc/serial_number | 0 .../greybus/Documentation/sysfs/endo-TYPE/svc/version | 0 .../sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore | 1 + .../sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore | 1 + .../sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore | 1 + .../greybus/Documentation/sysfs/endo/01/01/firmware | 0 .../greybus/Documentation/sysfs/endo/01/01/manifest | 0 .../greybus/Documentation/sysfs/endo/01/01/product_id | 0 .../greybus/Documentation/sysfs/endo/01/01/product_name | 0 .../staging/greybus/Documentation/sysfs/endo/01/01/state | 0 .../staging/greybus/Documentation/sysfs/endo/01/01/uid | 0 .../greybus/Documentation/sysfs/endo/01/01/version_major | 0 .../greybus/Documentation/sysfs/endo/01/01/version_minor | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/epm | 0 .../staging/greybus/Documentation/sysfs/endo/01/power | 0 .../staging/greybus/Documentation/sysfs/endo/01/present | 0 .../greybus/Documentation/sysfs/endo/02/02/.gitignore | 1 + .../staging/greybus/Documentation/sysfs/endo/ap_intf_id | 0 drivers/staging/greybus/Documentation/sysfs/endo/id | 0 .../greybus/Documentation/sysfs/endo/svc/svc/firmware | 0 .../Documentation/sysfs/endo/svc/svc/serial_number | 0 .../greybus/Documentation/sysfs/endo/svc/svc/version | 0 drivers/staging/greybus/endo.c | 2 +- 42 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/firmware delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/manifest delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_name delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/state delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/uid delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_major delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_minor delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/epm delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/power delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/present delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/02/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/ap_intf_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/firmware delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/version create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/firmware create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/manifest create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_name create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/uid create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_major create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_minor create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/epm create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/power create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/present create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/02/02/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/ap_intf_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/firmware create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/serial_number create mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/version diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 9cd3e02e5701..0268dd24d2a4 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -1,4 +1,4 @@ -What: /sys/bus/greybus/device/endo-0xXXXX +What: /sys/bus/greybus/device/endo Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -7,7 +7,7 @@ Description: replaced with the numeric value of the endo layout scheme as documented in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo-0xXXXX/id +What: /sys/bus/greybus/device/endo/id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -16,7 +16,7 @@ Description: defined by the the Endo layout scheme, documented in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo-0xXXXX/ap_intf_id +What: /sys/bus/greybus/device/endo/ap_intf_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -26,21 +26,21 @@ Description: The interface positions are defined in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo-0xXXXX/svc/serial_number +What: /sys/bus/greybus/device/endo/svc/serial_number Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The serial number of the SVC device -What: /sys/bus/greybus/device/endo-0xXXXX/svc/version +What: /sys/bus/greybus/device/endo/svc/version Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The version number of the firmware in the SVC device. -What: /sys/bus/greybus/device/endo-0xXXXX/.../epm +What: /sys/bus/greybus/device/endo/../epm Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -50,7 +50,7 @@ Description: Writing 1 to it turns it on, writing 0 to it turns it off. Reading the value returns if it is on or off. -What: /sys/bus/greybus/device/endo-0xXXXX/.../power_control +What: /sys/bus/greybus/device/endo/.../power_control Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -60,7 +60,7 @@ Description: to the module, writing 0 to it turns power off to the module. Reading the value returns if it is on or off. -What: /sys/bus/greybus/device/endo-0xXXXX/.../present +What: /sys/bus/greybus/device/endo/.../present Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/gpio/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/i2c/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/01/gpbridge00/usb/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/firmware b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/firmware deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/manifest b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/manifest deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_name b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/product_name deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/state b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/uid b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/uid deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_major b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_major deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_minor b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/version_minor deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/epm b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/epm deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/power b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/power deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/present b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/present deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/02/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/02/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/02/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/ap_intf_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/firmware b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/firmware deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/serial_number b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/version b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/svc/version deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/firmware b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/firmware new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/manifest b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/manifest new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_id b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_name b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_name new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/state b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/uid b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/uid new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_major b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_major new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_minor b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_minor new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/epm b/drivers/staging/greybus/Documentation/sysfs/endo/01/epm new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/power b/drivers/staging/greybus/Documentation/sysfs/endo/01/power new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/present b/drivers/staging/greybus/Documentation/sysfs/endo/01/present new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/02/02/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo/02/02/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/endo/02/02/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/endo/ap_intf_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/id b/drivers/staging/greybus/Documentation/sysfs/endo/id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/firmware b/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/firmware new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/serial_number b/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/version b/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/version new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index aa89868726d9..d71f328dd40e 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -438,6 +438,7 @@ static int gb_endo_register(struct greybus_host_device *hd, int retval; endo->dev.parent = hd->parent; + endo->dev.init_name = "endo"; endo->dev.bus = &greybus_bus_type; endo->dev.type = &greybus_endo_type; endo->dev.groups = endo_groups; @@ -450,7 +451,6 @@ static int gb_endo_register(struct greybus_host_device *hd, strcpy(&endo->svc_info.serial_number[0], "042"); strcpy(&endo->svc_info.version[0], "0.0"); - dev_set_name(&endo->dev, "endo-0x%04x", endo->id); retval = device_add(&endo->dev); if (retval) { dev_err(hd->parent, "failed to add endo device of id 0x%04x\n", -- cgit v1.2.3-59-g8ed1b From 4bf3780982bde534d931ae77a0a6d46335c488ab Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 May 2015 19:03:33 +0200 Subject: greybus: gpio: remove unused debounce define Remove unused debounce define from protocol header. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 32d84640e295..25c9a380ad99 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -132,8 +132,6 @@ struct gb_i2c_transfer_response { #define GB_GPIO_TYPE_IRQ_UNMASK 0x0d #define GB_GPIO_TYPE_IRQ_EVENT 0x0e -#define GB_GPIO_DEBOUNCE_USEC_DEFAULT 0 /* microseconds */ - /* line count request has no payload */ struct gb_gpio_line_count_response { __u8 count; -- cgit v1.2.3-59-g8ed1b From 7ba864a19f97f06b0a685b083449b7278b635823 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 May 2015 19:03:34 +0200 Subject: greybus: gpio: add irq-type defines Add Greybus GPIO IRQ-type defines rather than rely on the current Linux implementation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 14 +++++++++++++- drivers/staging/greybus/greybus_protocols.h | 7 +++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 8dad9e579881..95909149eba2 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -286,21 +286,33 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_chip *chip = irq_data_to_gpio_chip(d); struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); struct gb_gpio_line *line = &ggc->lines[d->hwirq]; + u8 irq_type; switch (type) { case IRQ_TYPE_NONE: + irq_type = GB_GPIO_IRQ_TYPE_NONE; + break; case IRQ_TYPE_EDGE_RISING: + irq_type = GB_GPIO_IRQ_TYPE_EDGE_RISING; + break; case IRQ_TYPE_EDGE_FALLING: + irq_type = GB_GPIO_IRQ_TYPE_EDGE_FALLING; + break; case IRQ_TYPE_EDGE_BOTH: + irq_type = GB_GPIO_IRQ_TYPE_EDGE_BOTH; + break; case IRQ_TYPE_LEVEL_LOW: + irq_type = GB_GPIO_IRQ_TYPE_LEVEL_LOW; + break; case IRQ_TYPE_LEVEL_HIGH: + irq_type = GB_GPIO_IRQ_TYPE_LEVEL_HIGH; break; default: dev_err(chip->dev, "unsupported irq type: %u\n", type); return -EINVAL; } - line->irq_type = type; + line->irq_type = irq_type; line->irq_type_pending = true; return 0; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 25c9a380ad99..81f01d9ab293 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -132,6 +132,13 @@ struct gb_i2c_transfer_response { #define GB_GPIO_TYPE_IRQ_UNMASK 0x0d #define GB_GPIO_TYPE_IRQ_EVENT 0x0e +#define GB_GPIO_IRQ_TYPE_NONE 0x00 +#define GB_GPIO_IRQ_TYPE_EDGE_RISING 0x01 +#define GB_GPIO_IRQ_TYPE_EDGE_FALLING 0x02 +#define GB_GPIO_IRQ_TYPE_EDGE_BOTH 0x03 +#define GB_GPIO_IRQ_TYPE_LEVEL_HIGH 0x04 +#define GB_GPIO_IRQ_TYPE_LEVEL_LOW 0x08 + /* line count request has no payload */ struct gb_gpio_line_count_response { __u8 count; -- cgit v1.2.3-59-g8ed1b From 3cb494cd6c5d3ca1c381553f5379ca9577f8e871 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 1 Jun 2015 17:43:36 +0530 Subject: greybus: endo: Add missing '\n' sprintf() for sysfs files Because of the missing '\n', this is how the output of reading endo sysfs files looks: root# cat /sys/bus/greybus/devices/endo/id 0x4755root# Fix it by including \n to the end of the printed string. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index d71f328dd40e..4f9ae4b2a8e3 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -49,7 +49,7 @@ static ssize_t serial_number_show(struct device *dev, { struct gb_endo *endo = to_gb_endo(dev); - return sprintf(buf, "%s", &endo->svc_info.serial_number[0]); + return sprintf(buf, "%s\n", &endo->svc_info.serial_number[0]); } static DEVICE_ATTR_RO(serial_number); @@ -58,7 +58,7 @@ static ssize_t version_show(struct device *dev, { struct gb_endo *endo = to_gb_endo(dev); - return sprintf(buf, "%s", &endo->svc_info.version[0]); + return sprintf(buf, "%s\n", &endo->svc_info.version[0]); } static DEVICE_ATTR_RO(version); @@ -78,7 +78,7 @@ static ssize_t id_show(struct device *dev, { struct gb_endo *endo = to_gb_endo(dev); - return sprintf(buf, "0x%04x", endo->id); + return sprintf(buf, "0x%04x\n", endo->id); } static DEVICE_ATTR_RO(id); @@ -87,7 +87,7 @@ static ssize_t ap_intf_id_show(struct device *dev, { struct gb_endo *endo = to_gb_endo(dev); - return sprintf(buf, "0x%02x", endo->ap_intf_id); + return sprintf(buf, "0x%02x\n", endo->ap_intf_id); } static DEVICE_ATTR_RO(ap_intf_id); -- cgit v1.2.3-59-g8ed1b From 55f2291142efa42acab362bf71b52e03bd275a42 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Mon, 1 Jun 2015 22:19:45 +0700 Subject: greybus: uart: Fix the memory leak in connection init If alloc minor is error, gb_tty should free. Signed-off-by: Phong Tran Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 47de9698f50c..590bc9f5d0be 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -618,9 +618,11 @@ static int gb_uart_connection_init(struct gb_connection *connection) if (minor == -ENOSPC) { dev_err(&connection->dev, "no more free minor numbers\n"); - return -ENODEV; + retval = -ENODEV; + goto error_version; } - return minor; + retval = minor; + goto error_version; } gb_tty->minor = minor; -- cgit v1.2.3-59-g8ed1b From 4ef53485c3d2d7a36c163c0409c8e3eb324a70d4 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2015 13:40:44 +0100 Subject: greybus: uart: Move UART protocol structs/defines to greybus_protocols.h gbsim depends on the structures and defines in greybus_protocols.h generally in order to simulate firmware. Move UART defines into this header to facilitate. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 69 +++++++++++++++++++++++++++++ drivers/staging/greybus/uart.c | 66 --------------------------- 2 files changed, 69 insertions(+), 66 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 81f01d9ab293..04f3b222a111 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -545,4 +545,73 @@ struct gb_svc_conn_destroy_request { }; /* connection destroy response has no payload */ +/* UART */ + +/* Version of the Greybus UART protocol we support */ +#define GB_UART_VERSION_MAJOR 0x00 +#define GB_UART_VERSION_MINOR 0x01 + +/* Greybus UART operation types */ +#define GB_UART_TYPE_INVALID 0x00 +#define GB_UART_TYPE_PROTOCOL_VERSION 0x01 +#define GB_UART_TYPE_SEND_DATA 0x02 +#define GB_UART_TYPE_RECEIVE_DATA 0x03 /* Unsolicited data */ +#define GB_UART_TYPE_SET_LINE_CODING 0x04 +#define GB_UART_TYPE_SET_CONTROL_LINE_STATE 0x05 +#define GB_UART_TYPE_SET_BREAK 0x06 +#define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ + +struct gb_uart_send_data_request { + __le16 size; + __u8 data[0]; +}; + +struct gb_serial_line_coding { + __le32 rate; + __u8 format; +#define GB_SERIAL_1_STOP_BITS 0 +#define GB_SERIAL_1_5_STOP_BITS 1 +#define GB_SERIAL_2_STOP_BITS 2 + + __u8 parity; +#define GB_SERIAL_NO_PARITY 0 +#define GB_SERIAL_ODD_PARITY 1 +#define GB_SERIAL_EVEN_PARITY 2 +#define GB_SERIAL_MARK_PARITY 3 +#define GB_SERIAL_SPACE_PARITY 4 + + __u8 data; +}; + +struct gb_uart_set_line_coding_request { + struct gb_serial_line_coding line_coding; +}; + +/* output control lines */ +#define GB_UART_CTRL_DTR 0x01 +#define GB_UART_CTRL_RTS 0x02 + +struct gb_uart_set_control_line_state_request { + __le16 control; +}; + +struct gb_uart_set_break_request { + __u8 state; +}; + +/* input control lines and line errors */ +#define GB_UART_CTRL_DCD 0x01 +#define GB_UART_CTRL_DSR 0x02 +#define GB_UART_CTRL_BRK 0x04 +#define GB_UART_CTRL_RI 0x08 + +#define GB_UART_CTRL_FRAMING 0x10 +#define GB_UART_CTRL_PARITY 0x20 +#define GB_UART_CTRL_OVERRUN 0x40 + +struct gb_uart_serial_state_request { + __u16 control; +}; + #endif /* __GREYBUS_PROTOCOLS_H */ + diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 590bc9f5d0be..a4c0127687b7 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -33,72 +33,6 @@ #define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ #define GB_NAME "ttyGB" -/* Version of the Greybus PWM protocol we support */ -#define GB_UART_VERSION_MAJOR 0x00 -#define GB_UART_VERSION_MINOR 0x01 - -/* Greybus UART operation types */ -#define GB_UART_TYPE_INVALID 0x00 -#define GB_UART_TYPE_PROTOCOL_VERSION 0x01 -#define GB_UART_TYPE_SEND_DATA 0x02 -#define GB_UART_TYPE_RECEIVE_DATA 0x03 /* Unsolicited data */ -#define GB_UART_TYPE_SET_LINE_CODING 0x04 -#define GB_UART_TYPE_SET_CONTROL_LINE_STATE 0x05 -#define GB_UART_TYPE_SET_BREAK 0x06 -#define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ - -struct gb_uart_send_data_request { - __le16 size; - __u8 data[0]; -}; - -struct gb_serial_line_coding { - __le32 rate; - __u8 format; -#define GB_SERIAL_1_STOP_BITS 0 -#define GB_SERIAL_1_5_STOP_BITS 1 -#define GB_SERIAL_2_STOP_BITS 2 - - __u8 parity; -#define GB_SERIAL_NO_PARITY 0 -#define GB_SERIAL_ODD_PARITY 1 -#define GB_SERIAL_EVEN_PARITY 2 -#define GB_SERIAL_MARK_PARITY 3 -#define GB_SERIAL_SPACE_PARITY 4 - - __u8 data; -}; - -struct gb_uart_set_line_coding_request { - struct gb_serial_line_coding line_coding; -}; - -/* output control lines */ -#define GB_UART_CTRL_DTR 0x01 -#define GB_UART_CTRL_RTS 0x02 - -struct gb_uart_set_control_line_state_request { - __le16 control; -}; - -struct gb_uart_set_break_request { - __u8 state; -}; - -/* input control lines and line errors */ -#define GB_UART_CTRL_DCD 0x01 -#define GB_UART_CTRL_DSR 0x02 -#define GB_UART_CTRL_BRK 0x04 -#define GB_UART_CTRL_RI 0x08 - -#define GB_UART_CTRL_FRAMING 0x10 -#define GB_UART_CTRL_PARITY 0x20 -#define GB_UART_CTRL_OVERRUN 0x40 - -struct gb_uart_serial_state_request { - __u16 control; -}; - struct gb_tty { struct tty_port port; struct gb_connection *connection; -- cgit v1.2.3-59-g8ed1b From 11fca140c92555f299808968061e81deb7c11821 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2015 13:40:45 +0100 Subject: greybus: uart: Tidy naming convention to more closely match spec Update tabs and naming of structures to match the naming used in the greybus specification more closely. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 51 +++++++++++++++-------------- drivers/staging/greybus/uart.c | 24 +++++++++----- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 04f3b222a111..642f942f1c38 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -561,35 +561,38 @@ struct gb_svc_conn_destroy_request { #define GB_UART_TYPE_SET_BREAK 0x06 #define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ +/* Represents data from AP -> Module */ struct gb_uart_send_data_request { __le16 size; __u8 data[0]; }; -struct gb_serial_line_coding { +/* Represents data from Module -> AP */ +struct gb_uart_recv_data_request { + __le16 size; + __u8 data[0]; +}; + +struct gb_uart_set_line_coding_request { __le32 rate; __u8 format; -#define GB_SERIAL_1_STOP_BITS 0 -#define GB_SERIAL_1_5_STOP_BITS 1 -#define GB_SERIAL_2_STOP_BITS 2 +#define GB_SERIAL_1_STOP_BITS 0 +#define GB_SERIAL_1_5_STOP_BITS 1 +#define GB_SERIAL_2_STOP_BITS 2 __u8 parity; -#define GB_SERIAL_NO_PARITY 0 -#define GB_SERIAL_ODD_PARITY 1 -#define GB_SERIAL_EVEN_PARITY 2 -#define GB_SERIAL_MARK_PARITY 3 -#define GB_SERIAL_SPACE_PARITY 4 - - __u8 data; -}; +#define GB_SERIAL_NO_PARITY 0 +#define GB_SERIAL_ODD_PARITY 1 +#define GB_SERIAL_EVEN_PARITY 2 +#define GB_SERIAL_MARK_PARITY 3 +#define GB_SERIAL_SPACE_PARITY 4 -struct gb_uart_set_line_coding_request { - struct gb_serial_line_coding line_coding; + __u8 data_bits; }; /* output control lines */ -#define GB_UART_CTRL_DTR 0x01 -#define GB_UART_CTRL_RTS 0x02 +#define GB_UART_CTRL_DTR 0x01 +#define GB_UART_CTRL_RTS 0x02 struct gb_uart_set_control_line_state_request { __le16 control; @@ -600,14 +603,14 @@ struct gb_uart_set_break_request { }; /* input control lines and line errors */ -#define GB_UART_CTRL_DCD 0x01 -#define GB_UART_CTRL_DSR 0x02 -#define GB_UART_CTRL_BRK 0x04 -#define GB_UART_CTRL_RI 0x08 - -#define GB_UART_CTRL_FRAMING 0x10 -#define GB_UART_CTRL_PARITY 0x20 -#define GB_UART_CTRL_OVERRUN 0x40 +#define GB_UART_CTRL_DCD 0x01 +#define GB_UART_CTRL_DSR 0x02 +#define GB_UART_CTRL_BRK 0x04 +#define GB_UART_CTRL_RI 0x08 + +#define GB_UART_CTRL_FRAMING 0x10 +#define GB_UART_CTRL_PARITY 0x20 +#define GB_UART_CTRL_OVERRUN 0x40 struct gb_uart_serial_state_request { __u16 control; diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index a4c0127687b7..673eec4fe963 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -33,6 +33,13 @@ #define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ #define GB_NAME "ttyGB" +struct gb_tty_line_coding { + __le32 rate; + __u8 format; + __u8 parity; + __u8 data_bits; +}; + struct gb_tty { struct tty_port port; struct gb_connection *connection; @@ -50,10 +57,9 @@ struct gb_tty { u8 version_minor; unsigned int ctrlin; /* input control lines */ unsigned int ctrlout; /* output control lines */ - struct gb_serial_line_coding line_coding; + struct gb_tty_line_coding line_coding; }; - static struct tty_driver *gb_tty_driver; static DEFINE_IDR(tty_minors); static DEFINE_MUTEX(table_lock); @@ -87,7 +93,7 @@ static int send_line_coding(struct gb_tty *tty) { struct gb_uart_set_line_coding_request request; - memcpy(&request.line_coding, &tty->line_coding, + memcpy(&request, &tty->line_coding, sizeof(tty->line_coding)); return gb_operation_sync(tty->connection, GB_UART_TYPE_SET_LINE_CODING, &request, sizeof(request), NULL, 0); @@ -245,7 +251,7 @@ static void gb_tty_set_termios(struct tty_struct *tty, { struct gb_tty *gb_tty = tty->driver_data; struct ktermios *termios = &tty->termios; - struct gb_serial_line_coding newline; + struct gb_tty_line_coding newline; int newctrl = gb_tty->ctrlout; newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); @@ -256,17 +262,17 @@ static void gb_tty_set_termios(struct tty_struct *tty, switch (termios->c_cflag & CSIZE) { case CS5: - newline.data = 5; + newline.data_bits = 5; break; case CS6: - newline.data = 6; + newline.data_bits = 6; break; case CS7: - newline.data = 7; + newline.data_bits = 7; break; case CS8: default: - newline.data = 8; + newline.data_bits = 8; break; } @@ -571,7 +577,7 @@ static int gb_uart_connection_init(struct gb_connection *connection) gb_tty->line_coding.rate = cpu_to_le32(9600); gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; - gb_tty->line_coding.data = 8; + gb_tty->line_coding.data_bits = 8; send_line_coding(gb_tty); tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, -- cgit v1.2.3-59-g8ed1b From f5537d46cb2e062ca4a67ac86168b2bff9895615 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2015 13:40:46 +0100 Subject: greybus: uart: Reduce UART count from 255 to 16 Arbitrary number 255 is both not aligned and probably too big. Move the UART count down to 16 which is still large but, more realistic. 8 may be too few for future testing setups, 16 should accomodate any. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 673eec4fe963..4cad629e3cb2 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -30,7 +30,7 @@ #include "greybus.h" -#define GB_NUM_MINORS 255 /* 255 is enough for anyone... */ +#define GB_NUM_MINORS 16 /* 16 is is more than enough */ #define GB_NAME "ttyGB" struct gb_tty_line_coding { -- cgit v1.2.3-59-g8ed1b From f95ad78c72e72bcc922e75b80b35c6e9549b9d72 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2015 13:40:47 +0100 Subject: greybus: uart: Update uart.c to register tty ports For each new UART connection we need to do a tty_port_init else we'll crash when trying to access the tty mutex later on. Base the TTY major/minor numbers on non-zero values. Supply an empty operations structure for the newly regitered port. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 4cad629e3cb2..c47667d0bec5 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -522,6 +522,7 @@ static const struct tty_operations gb_ops = { .tiocmset = gb_tty_tiocmset, }; +static struct tty_port_operations null_ops = { }; static int gb_tty_init(void); static void gb_tty_exit(void); @@ -545,6 +546,7 @@ static int gb_uart_connection_init(struct gb_connection *connection) gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); if (!gb_tty) return -ENOMEM; + gb_tty->connection = connection; connection->private = gb_tty; @@ -571,6 +573,9 @@ static int gb_uart_connection_init(struct gb_connection *connection) init_waitqueue_head(&gb_tty->wioctl); mutex_init(&gb_tty->mutex); + tty_port_init(&gb_tty->port); + gb_tty->port.ops = &null_ops; + send_control(gb_tty, gb_tty->ctrlout); /* initialize the uart to be 9600n81 */ @@ -589,6 +594,7 @@ static int gb_uart_connection_init(struct gb_connection *connection) return 0; error: + tty_port_destroy(&gb_tty->port); release_minor(gb_tty); error_version: connection->private = NULL; @@ -623,7 +629,7 @@ static void gb_uart_connection_exit(struct gb_connection *connection) /* FIXME - free transmit / receive buffers */ tty_port_put(&gb_tty->port); - + tty_port_destroy(&gb_tty->port); kfree(gb_tty); /* If last device is gone, tear down the tty structures */ -- cgit v1.2.3-59-g8ed1b From 563bd79b2ddd12dd9da89635dc218d6c706a4130 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2015 13:40:48 +0100 Subject: greybus: uart: send_data should return size or error gb_operation_sync returns 0 on success but the calling function expects the number of bytes written on success or a negative errno Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index c47667d0bec5..928b83dbecf7 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -71,7 +71,7 @@ define_get_version(gb_tty, UART); static int send_data(struct gb_tty *tty, u16 size, const u8 *data) { struct gb_uart_send_data_request *request; - int retval; + int ret; if (!data || !size) return 0; @@ -82,11 +82,13 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) request->size = cpu_to_le16(size); memcpy(&request->data[0], data, size); - retval = gb_operation_sync(tty->connection, GB_UART_TYPE_SEND_DATA, - request, sizeof(*request) + size, NULL, 0); - + ret = gb_operation_sync(tty->connection, GB_UART_TYPE_SEND_DATA, + request, sizeof(*request) + size, NULL, 0); kfree(request); - return retval; + if (ret) + return ret; + else + return size; } static int send_line_coding(struct gb_tty *tty) -- cgit v1.2.3-59-g8ed1b From dd1c64ede97f163e5ab2fdebfe3f4095f4638120 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2015 13:40:49 +0100 Subject: greybus: uart: kmalloc for send_data once only Make kmalloc for the send buffer a one time alloc based on the MTU for a given greybus link. The write_room for an gb_operation_sync then will be the size of the buffer we use for a single operation. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 928b83dbecf7..ed03ba44933b 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -42,6 +42,8 @@ struct gb_tty_line_coding { struct gb_tty { struct tty_port port; + void *buffer; + u32 buffer_payload_max; struct gb_connection *connection; u16 cport_id; unsigned int minor; @@ -76,15 +78,13 @@ static int send_data(struct gb_tty *tty, u16 size, const u8 *data) if (!data || !size) return 0; - request = kmalloc(sizeof(*request) + size, GFP_KERNEL); - if (!request) - return -ENOMEM; - + if (size > tty->buffer_payload_max) + size = tty->buffer_payload_max; + request = tty->buffer; request->size = cpu_to_le16(size); memcpy(&request->data[0], data, size); ret = gb_operation_sync(tty->connection, GB_UART_TYPE_SEND_DATA, request, sizeof(*request) + size, NULL, 0); - kfree(request); if (ret) return ret; else @@ -227,17 +227,13 @@ static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, static int gb_tty_write_room(struct tty_struct *tty) { -// struct gb_tty *gb_tty = tty->driver_data; + struct gb_tty *gb_tty = tty->driver_data; - // FIXME - how much do we want to say we have room for? - return 0; + return gb_tty->buffer_payload_max; } static int gb_tty_chars_in_buffer(struct tty_struct *tty) { -// struct gb_tty *gb_tty = tty->driver_data; - - // FIXME - how many left to send? return 0; } @@ -549,6 +545,19 @@ static int gb_uart_connection_init(struct gb_connection *connection) if (!gb_tty) return -ENOMEM; + gb_tty->buffer_payload_max = + gb_operation_get_payload_size_max(connection); + if (!gb_tty->buffer_payload_max) { + kfree(gb_tty); + return -EINVAL; + } + + gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); + if (!gb_tty->buffer) { + kfree(gb_tty); + return -ENOMEM; + } + gb_tty->connection = connection; connection->private = gb_tty; @@ -600,6 +609,7 @@ error: release_minor(gb_tty); error_version: connection->private = NULL; + kfree(gb_tty->buffer); kfree(gb_tty); return retval; } @@ -632,6 +642,7 @@ static void gb_uart_connection_exit(struct gb_connection *connection) tty_port_put(&gb_tty->port); tty_port_destroy(&gb_tty->port); + kfree(gb_tty->buffer); kfree(gb_tty); /* If last device is gone, tear down the tty structures */ -- cgit v1.2.3-59-g8ed1b From 1c087015b9ee80829a6e34e36715c668493eb607 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2015 13:40:50 +0100 Subject: greybus: uart: Add gb_uart_request_recv for receiving async UART data gb_uart_request_recv job in life is to process unsolicited greybus mesages from the UART. Hook the incoming UART data and pass to the TTY layer. Line-state changes still TBD. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 43 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index ed03ba44933b..2d19d397fb72 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -70,6 +70,47 @@ static atomic_t reference_count = ATOMIC_INIT(0); /* Define get_version() routine */ define_get_version(gb_tty, UART); +static int gb_uart_request_recv(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_tty *gb_tty = connection->private; + struct gb_message *request = op->request; + struct gb_uart_recv_data_request *receive_data; + struct gb_uart_serial_state_request *serial_state; + struct tty_port *port = &gb_tty->port; + int count; + int ret = 0; + + switch (type) { + case GB_UART_TYPE_RECEIVE_DATA: + receive_data = request->payload; + count = gb_tty->buffer_payload_max - sizeof(*receive_data); + if (!receive_data->size || receive_data->size > count) + return -EINVAL; + + count = tty_insert_flip_string(port, receive_data->data, + receive_data->size); + if (count != receive_data->size) { + dev_err(&connection->dev, + "UART: RX 0x%08x bytes only wrote 0x%08x\n", + receive_data->size, count); + } + if (count) + tty_flip_buffer_push(port); + break; + case GB_UART_TYPE_SERIAL_STATE: + serial_state = request->payload; + /* TODO: Parse state change and translate to tty API. */ + break; + default: + dev_err(&connection->dev, + "unsupported unsolicited request: %02x\n", type); + ret = -EINVAL; + } + + return ret; +} + static int send_data(struct gb_tty *tty, u16 size, const u8 *data) { struct gb_uart_send_data_request *request; @@ -703,7 +744,7 @@ static struct gb_protocol uart_protocol = { .minor = 1, .connection_init = gb_uart_connection_init, .connection_exit = gb_uart_connection_exit, - .request_recv = NULL, /* FIXME we have 2 types of requests!!! */ + .request_recv = gb_uart_request_recv, }; gb_gpbridge_protocol_driver(uart_protocol); -- cgit v1.2.3-59-g8ed1b From 62229a1bda8abdc54dabada79798885f4f2da79c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2015 13:40:51 +0100 Subject: greybus: uart: Remove magic numbers make struct gb_tty variable names consistent Use defines for the data format command. Tidy up naming of gb_tty variables. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 2d19d397fb72..37bb2e2ddf59 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -142,28 +142,28 @@ static int send_line_coding(struct gb_tty *tty) &request, sizeof(request), NULL, 0); } -static int send_control(struct gb_tty *tty, u16 control) +static int send_control(struct gb_tty *gb_tty, u16 control) { struct gb_uart_set_control_line_state_request request; request.control = cpu_to_le16(control); - return gb_operation_sync(tty->connection, + return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SET_CONTROL_LINE_STATE, &request, sizeof(request), NULL, 0); } -static int send_break(struct gb_tty *tty, u8 state) +static int send_break(struct gb_tty *gb_tty, u8 state) { struct gb_uart_set_break_request request; if ((state != 0) && (state != 1)) { - dev_err(&tty->connection->dev, + dev_err(&gb_tty->connection->dev, "invalid break state of %d\n", state); return -EINVAL; } request.state = state; - return gb_operation_sync(tty->connection, GB_UART_TYPE_SET_BREAK, + return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SET_BREAK, &request, sizeof(request), NULL, 0); } @@ -294,7 +294,8 @@ static void gb_tty_set_termios(struct tty_struct *tty, int newctrl = gb_tty->ctrlout; newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); - newline.format = termios->c_cflag & CSTOPB ? 2 : 0; + newline.format = termios->c_cflag & CSTOPB ? + GB_SERIAL_2_STOP_BITS : GB_SERIAL_1_STOP_BITS; newline.parity = termios->c_cflag & PARENB ? (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; -- cgit v1.2.3-59-g8ed1b From 4cfabf09dbcbd1ca44567542247925f02f71e656 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Jun 2015 09:58:21 +0530 Subject: greybus: operation: Remove unused variable 'buffer' 'buffer' isn't used in this function, remove it. Reviewed-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 4f7a555e4b96..88b9f6a40c79 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -221,9 +221,7 @@ static void gb_operation_message_init(struct greybus_host_device *hd, size_t payload_size, u8 type) { struct gb_operation_msg_hdr *header; - u8 *buffer; - buffer = message->buffer; header = message->buffer; message->header = header; -- cgit v1.2.3-59-g8ed1b From d7353ceadaf96a60b4163177ec9f8202655f1ace Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Jun 2015 10:18:01 +0530 Subject: greybus: s/bundle_cport_id/intf_cport_id This isn't unique just for the bundle but the complete interface. Its wrong to call it bundle_cport_id. Lets name it intf_cport_id to make things clear. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-pcm.c | 4 ++-- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/connection.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c index c1b6aa551ce1..4d2f0ab132ee 100644 --- a/drivers/staging/greybus/audio-pcm.c +++ b/drivers/staging/greybus/audio-pcm.c @@ -52,7 +52,7 @@ static void gb_pcm_work(struct work_struct *work) if (snd_dev->cport_active) { ret = gb_i2s_mgmt_deactivate_cport( snd_dev->mgmt_connection, - snd_dev->i2s_tx_connection->bundle_cport_id); + snd_dev->i2s_tx_connection->intf_cport_id); if (ret) /* XXX Do what else with failure? */ pr_err("deactivate_cport failed: %d\n", ret); @@ -62,7 +62,7 @@ static void gb_pcm_work(struct work_struct *work) return; } else if (!snd_dev->cport_active) { ret = gb_i2s_mgmt_activate_cport(snd_dev->mgmt_connection, - snd_dev->i2s_tx_connection->bundle_cport_id); + snd_dev->i2s_tx_connection->intf_cport_id); if (ret) pr_err("activate_cport failed: %d\n", ret); diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index e87521ec526d..6f0d3e98f800 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -184,7 +184,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, } connection->bundle = bundle; - connection->bundle_cport_id = cport_id; + connection->intf_cport_id = cport_id; connection->state = GB_CONNECTION_STATE_DISABLED; connection->dev.parent = &bundle->dev; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 8992b75394a5..d39d476d6492 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -25,7 +25,7 @@ struct gb_connection { struct gb_bundle *bundle; struct device dev; u16 hd_cport_id; - u16 bundle_cport_id; + u16 intf_cport_id; struct list_head hd_links; struct list_head bundle_links; -- cgit v1.2.3-59-g8ed1b From 4a0418700871936cddec3a2b3c5a029ec0b2d6a3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sun, 7 Jun 2015 09:13:15 +0530 Subject: greybus: Generate greybus wide unique ids for endo devices Currently we name the endo device as "endo". And it shows up with the same name in sysfs directory: /sys/bus/greybus/devices/. But each device in kernel should be represented by a unique id in kernel, and "endo" isn't unique. Lets generate unique ids for endo devices. The ida mechanism for allocating ids may be overkill but it works. Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 ++++ drivers/staging/greybus/endo.c | 39 ++++++++++++++++++++++++++++++++++++++- drivers/staging/greybus/endo.h | 1 + 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 0a24822ac4ab..9bbbf374c8c7 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -18,6 +18,8 @@ #include "greybus.h" +extern struct ida greybus_endo_id_map; + /* Allow greybus to be disabled at boot if needed */ static bool nogreybus; #ifdef MODULE @@ -263,6 +265,8 @@ static int __init gb_init(void) goto error_bus; } + ida_init(&greybus_endo_id_map); + retval = gb_ap_init(); if (retval) { pr_err("gb_ap_init failed\n"); diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 4f9ae4b2a8e3..b89b14f59fb2 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -43,6 +43,8 @@ #define max_endo_interface_id(endo_layout) \ (4 + ((endo_layout)->max_ribs + 1) * 2) +struct ida greybus_endo_id_map; + /* endo sysfs attributes */ static ssize_t serial_number_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -432,18 +434,51 @@ static int create_modules(struct gb_endo *endo) return 0; } +/* + * Allocate an available Id to uniquely identify the endo device. The lowest + * available id is returned, so the first call is guaranteed to allocate endo Id + * 0. + * + * Assigns the endo's id and returns 0 if successful. + * Returns error otherwise. + */ +static int gb_endo_id_alloc(struct gb_endo *endo) +{ + int id; + + id = ida_simple_get(&greybus_endo_id_map, 0, 0, GFP_ATOMIC); + if (id < 0) + return id; + + endo->dev_id = (u16)id; + + return 0; +} + +/* + * Free a previously-allocated Endo Id. + */ +static void gb_endo_id_free(struct gb_endo *endo) +{ + ida_simple_remove(&greybus_endo_id_map, endo->dev_id); +} + static int gb_endo_register(struct greybus_host_device *hd, struct gb_endo *endo) { int retval; + retval = gb_endo_id_alloc(endo); + if (retval) + return retval; + endo->dev.parent = hd->parent; - endo->dev.init_name = "endo"; endo->dev.bus = &greybus_bus_type; endo->dev.type = &greybus_endo_type; endo->dev.groups = endo_groups; endo->dev.dma_mask = hd->parent->dma_mask; device_initialize(&endo->dev); + dev_set_name(&endo->dev, "endo%hu", endo->dev_id); // FIXME // Get the version and serial number from the SVC, right now we are @@ -456,6 +491,7 @@ static int gb_endo_register(struct greybus_host_device *hd, dev_err(hd->parent, "failed to add endo device of id 0x%04x\n", endo->id); put_device(&endo->dev); + gb_endo_id_free(endo); } return retval; @@ -511,6 +547,7 @@ void gb_endo_remove(struct gb_endo *endo) /* remove all modules for this endo */ gb_module_remove_all(endo); + gb_endo_id_free(endo); device_unregister(&endo->dev); } diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index e211fb7d53bc..2547b63e59bb 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -40,6 +40,7 @@ struct gb_endo { struct device dev; struct endo_layout layout; struct gb_svc_info svc_info; + u16 dev_id; u16 id; u8 ap_intf_id; }; -- cgit v1.2.3-59-g8ed1b From cf6b62d904f184fd13dbba56d77e9a8fbf98eda7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sun, 7 Jun 2015 09:13:16 +0530 Subject: greybus: Prefix module-id with endo id Prefix module-id with endo-id to uniquely identify it for the entire kernel. Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 07b7e444d766..d7706544f3be 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -144,7 +144,7 @@ struct gb_module *gb_module_create(struct device *parent, u8 module_id) module->dev.groups = module_groups; module->dev.dma_mask = parent->dma_mask; device_initialize(&module->dev); - dev_set_name(&module->dev, "%d", module_id); + dev_set_name(&module->dev, "%s:%hhu", dev_name(parent), module_id); retval = device_add(&module->dev); if (retval) { -- cgit v1.2.3-59-g8ed1b From 0dac67c84c4d1e4d2ab510cbe7fafb9bac7002ea Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 8 Jun 2015 12:05:10 -0500 Subject: greybus: svc: driver is basic to Greybus (not GP Bridge) The SVC protocol driver should have been defined as a basic Greybus protocol driver, not a GP Bridge protocol driver. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 4dc10e3778c4..f02283810231 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -245,4 +245,4 @@ static struct gb_protocol svc_protocol = { .request_recv = gb_svc_request_recv, }; -gb_gpbridge_protocol_driver(svc_protocol); +gb_protocol_driver(&svc_protocol); -- cgit v1.2.3-59-g8ed1b From 8bd0ae6e7295a06f8e64dcca1a91bb14ca6c07b0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 8 Jun 2015 12:05:11 -0500 Subject: greybus: connection: make gb_connection_hd_find() private Give gb_connection_hd_find() static scope; it's never used outside "connection.c". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ++-- drivers/staging/greybus/connection.h | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 6f0d3e98f800..a774f677279a 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -14,8 +14,8 @@ static DEFINE_SPINLOCK(gb_connections_lock); -struct gb_connection *gb_connection_hd_find(struct greybus_host_device *hd, - u16 cport_id) +static struct gb_connection * +gb_connection_hd_find(struct greybus_host_device *hd, u16 cport_id) { struct gb_connection *connection = NULL; unsigned long flags; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index d39d476d6492..acf91835486d 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -51,9 +51,6 @@ void gb_connection_destroy(struct gb_connection *connection); int gb_connection_init(struct gb_connection *connection); void gb_connection_exit(struct gb_connection *connection); -struct gb_connection *gb_connection_hd_find(struct greybus_host_device *hd, - u16 cport_id); - void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); -- cgit v1.2.3-59-g8ed1b From 81c3a77207450cb4187e8af700797e7d3209a568 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 8 Jun 2015 12:05:12 -0500 Subject: greybus: uart: properly interpret receive data size In gb_uart_request_recv(), the receive data size is in little-endian format. Do the proper byte swapping of that value before using it. Found by "make check". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 37bb2e2ddf59..6db49930cf40 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -78,6 +78,7 @@ static int gb_uart_request_recv(u8 type, struct gb_operation *op) struct gb_uart_recv_data_request *receive_data; struct gb_uart_serial_state_request *serial_state; struct tty_port *port = &gb_tty->port; + u16 recv_data_size; int count; int ret = 0; @@ -85,15 +86,16 @@ static int gb_uart_request_recv(u8 type, struct gb_operation *op) case GB_UART_TYPE_RECEIVE_DATA: receive_data = request->payload; count = gb_tty->buffer_payload_max - sizeof(*receive_data); - if (!receive_data->size || receive_data->size > count) + recv_data_size = le16_to_cpu(receive_data->size); + if (!recv_data_size || recv_data_size > count) return -EINVAL; count = tty_insert_flip_string(port, receive_data->data, - receive_data->size); - if (count != receive_data->size) { + recv_data_size); + if (count != recv_data_size) { dev_err(&connection->dev, "UART: RX 0x%08x bytes only wrote 0x%08x\n", - receive_data->size, count); + recv_data_size, count); } if (count) tty_flip_buffer_push(port); -- cgit v1.2.3-59-g8ed1b From fd1c2e541cbc48fe4a49e9fe1e98fd02af4bcf84 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 8 Jun 2015 12:05:13 -0500 Subject: greybus: core: rename greybus_deregister() Rename greybus_deregister() to be greybus_deregister_driver(), so its name mirrors the greybus_register_driver() function it matches. Define greybus_deregister() to be a trivial macro. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 ++-- drivers/staging/greybus/greybus.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9bbbf374c8c7..23e0359d18c2 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -155,11 +155,11 @@ int greybus_register_driver(struct greybus_driver *driver, struct module *owner, } EXPORT_SYMBOL_GPL(greybus_register_driver); -void greybus_deregister(struct greybus_driver *driver) +void greybus_deregister_driver(struct greybus_driver *driver) { driver_unregister(&driver->driver); } -EXPORT_SYMBOL_GPL(greybus_deregister); +EXPORT_SYMBOL_GPL(greybus_deregister_driver); static DEFINE_MUTEX(hd_mutex); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 86b2f9c67d0b..05eab4c5f9a0 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -132,11 +132,13 @@ struct greybus_driver { /* Don't call these directly, use the module_greybus_driver() macro instead */ int greybus_register_driver(struct greybus_driver *driver, struct module *module, const char *mod_name); -void greybus_deregister(struct greybus_driver *driver); +void greybus_deregister_driver(struct greybus_driver *driver); /* define to get proper THIS_MODULE and KBUILD_MODNAME values */ #define greybus_register(driver) \ greybus_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) +#define greybus_deregister(driver) \ + greybus_deregister_driver(driver) /** * module_greybus_driver() - Helper macro for registering a Greybus driver -- cgit v1.2.3-59-g8ed1b From d51c0ffb5cd1e92884e847f2cac6935c6d866779 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 8 Jun 2015 12:05:14 -0500 Subject: greybus: manifest: clean up a few pr_err() calls Provide a little more information in two pr_err() calls. Also enclose a reported condition in parentheses, to match the style used everywhere else in the file. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 597ba7077668..2022183a6e44 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -81,13 +81,14 @@ static int identify_descriptor(struct gb_interface *intf, size_t expected_size; if (size < sizeof(*desc_header)) { - pr_err("manifest too small\n"); + pr_err("manifest too small (%zu < %zu)\n", + size, sizeof(*desc_header)); return -EINVAL; /* Must at least have header */ } desc_size = le16_to_cpu(desc_header->size); if (desc_size > size) { - pr_err("descriptor too big\n"); + pr_err("descriptor too big (%zu > %zu)\n", desc_size, size); return -EINVAL; } @@ -374,7 +375,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) header = &manifest->header; manifest_size = le16_to_cpu(header->size); if (manifest_size != size) { - pr_err("manifest size mismatch %zu != %hu\n", + pr_err("manifest size mismatch (%zu != %hu)\n", size, manifest_size); return false; } -- cgit v1.2.3-59-g8ed1b From 928f2abd5ff12fa4851b762df7c32e749e846b7c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Jun 2015 18:16:45 +0530 Subject: greybus: Tear down devices in the reverse order Normally, its a good practice to free resources in the reverse order in which they are allocated, so that all the dependencies can be sorted out properly. This is true while creating/destroying devices as well. For example consider this scenario (I faced a crash with control protocol due to this). For a new module, we will first create a bundle+connection for the control cport and then create other bundles/connections after parsing manifest. And while destroying interface on module hot unplug, we are removing the devices in the order they are added. And so the bundle/connection for the control cport are destroyed first. But, control cport was still required while destroying other bundles/connections. To solve this problem, lets destroy the resources in the reverse order in which they are added. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 2 +- drivers/staging/greybus/connection.c | 4 ++-- drivers/staging/greybus/interface.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 2047e173ebd9..a6b1b347097a 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -196,7 +196,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, } spin_lock_irq(&gb_bundles_lock); - list_add_tail(&bundle->links, &intf->bundles); + list_add(&bundle->links, &intf->bundles); spin_unlock_irq(&gb_bundles_lock); return bundle; diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index a774f677279a..5ab744b14a0d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -213,8 +213,8 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, "protocol 0x%02hhx handler not found\n", protocol_id); spin_lock_irq(&gb_connections_lock); - list_add_tail(&connection->hd_links, &hd->connections); - list_add_tail(&connection->bundle_links, &bundle->connections); + list_add(&connection->hd_links, &hd->connections); + list_add(&connection->bundle_links, &bundle->connections); spin_unlock_irq(&gb_connections_lock); atomic_set(&connection->op_cycle, 0); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 7a4c7dc2e941..3483f848240b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -122,7 +122,7 @@ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, } spin_lock_irq(&gb_interfaces_lock); - list_add_tail(&intf->links, &hd->interfaces); + list_add(&intf->links, &hd->interfaces); spin_unlock_irq(&gb_interfaces_lock); return intf; -- cgit v1.2.3-59-g8ed1b From f2e2b06fefe448de14cc009f865d879a8c334f4d Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Tue, 9 Jun 2015 20:28:17 +0700 Subject: greybus: endo: move endo id map declaration to endo.h There is a sparse warning. The endo id map is also used in endo.c. Should define in endo.h Signed-off-by: Phong Tran Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 -- drivers/staging/greybus/endo.h | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 23e0359d18c2..a4d7057a061c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -18,8 +18,6 @@ #include "greybus.h" -extern struct ida greybus_endo_id_map; - /* Allow greybus to be disabled at boot if needed */ static bool nogreybus; #ifdef MODULE diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index 2547b63e59bb..63dbadf5589f 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -46,6 +46,7 @@ struct gb_endo { }; #define to_gb_endo(d) container_of(d, struct gb_endo, dev) +extern struct ida greybus_endo_id_map; /* Greybus "private" definitions */ struct greybus_host_device; -- cgit v1.2.3-59-g8ed1b From 61b65a394cdbf5ed9117ad0da184a3f90a9b914a Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Tue, 9 Jun 2015 20:28:18 +0700 Subject: greybus: uart: remove the redundant unregister chrdev The unregister_chrdev_region() does twice here. The chrdev region was unregistered inside tty_unregister_driver(). Signed-off-by: Phong Tran Reviewed-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 6db49930cf40..7e94632a581b 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -732,12 +732,8 @@ fail_unregister_dev: static void gb_tty_exit(void) { - int major = MAJOR(gb_tty_driver->major); - int minor = gb_tty_driver->minor_start; - tty_unregister_driver(gb_tty_driver); put_tty_driver(gb_tty_driver); - unregister_chrdev_region(MKDEV(major, minor), GB_NUM_MINORS); } static struct gb_protocol uart_protocol = { -- cgit v1.2.3-59-g8ed1b From cce310367603f8a91be0078bb6ddef2fecaf64ad Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 9 Jun 2015 13:47:48 -0700 Subject: greybus: es?: decrease buffer size to 2k The firmware is having a hard time with 4k buffers and memory allocation, so decrease the size on the host side to 2k. Also move away from using PAGE_SIZE to denote 4k as that's not the case on all architectures, and someone, someday, might get a rude surprise. Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 2 +- drivers/staging/greybus/es2.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 2acfe74fc7b7..45241c37f1d4 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -24,7 +24,7 @@ /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) -#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE +#define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { /* Made up numbers for the SVC USB Bridge in ES1 */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 4f676cf3c583..b5b395f5c968 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -24,7 +24,7 @@ /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) -#define ES1_GBUF_MSG_SIZE_MAX PAGE_SIZE +#define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { /* Made up numbers for the SVC USB Bridge in ES2 */ -- cgit v1.2.3-59-g8ed1b From 45f427a02ecee25885542ad1e449b1ae72b85a53 Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Tue, 9 Jun 2015 15:29:35 -0700 Subject: greybus: gb-audio: Reset sample count when CPort deactivated The sample count placed in Greybus I2S audio messages should be reset every time a new audio stream is set up. However, the current code does not do the reset so make it so it does. Signed-off-by: Mark Greer Acked-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c index 4d2f0ab132ee..fa5e85dfc83b 100644 --- a/drivers/staging/greybus/audio-pcm.c +++ b/drivers/staging/greybus/audio-pcm.c @@ -57,6 +57,7 @@ static void gb_pcm_work(struct work_struct *work) pr_err("deactivate_cport failed: %d\n", ret); snd_dev->cport_active = false; + snd_dev->send_data_sample_count = 0; } return; -- cgit v1.2.3-59-g8ed1b From 190241a362480e193e7b5f7a9fd9ff492c4acb31 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:49 -0500 Subject: greybus: drop some unnecessary headers There's no need to include anything other than "greybus.h" in "connection.c". Same thing in "core.c" and "manifest.c" and "svc.c". Some files need headers included, but most come along with "greybus.h". Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 3 --- drivers/staging/greybus/core.c | 7 ------- drivers/staging/greybus/es1.c | 5 ----- drivers/staging/greybus/es2.c | 5 ----- drivers/staging/greybus/manifest.c | 2 -- drivers/staging/greybus/svc.c | 5 ----- 6 files changed, 27 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 5ab744b14a0d..8f528c1640e1 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -7,9 +7,6 @@ * Released under the GPLv2 only. */ -#include - -#include "kernel_ver.h" #include "greybus.h" static DEFINE_SPINLOCK(gb_connections_lock); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index a4d7057a061c..e7a280cfbe81 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -9,13 +9,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include - #include "greybus.h" /* Allow greybus to be disabled at boot if needed */ diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 45241c37f1d4..56c80c6ddb3a 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -6,16 +6,11 @@ * * Released under the GPLv2 only. */ -#include -#include #include -#include -#include #include #include #include #include -#include #include #include "greybus.h" diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index b5b395f5c968..5257779f7aed 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -6,16 +6,11 @@ * * Released under the GPLv2 only. */ -#include -#include #include -#include -#include #include #include #include #include -#include #include #include "greybus.h" diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 2022183a6e44..f4fe4f37cda6 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -9,8 +9,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include - #include "greybus.h" static const char *get_descriptor_type_string(u8 type) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index f02283810231..ba85e81964c4 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -7,12 +7,7 @@ * Released under the GPLv2 only. */ -#include -#include -#include - #include "greybus.h" -#include "greybus_protocols.h" struct gb_svc { struct gb_connection *connection; -- cgit v1.2.3-59-g8ed1b From 47ed2c92406f94ac0f122ebf0e05fc63d3f0c02a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:50 -0500 Subject: greybus: tag core init and exit functions The top-level functions gb_init() and gb_exit() are tagged with __init and __exit, respectively. These functions call a few other functions that are similarly used only at initialization and termination time. So mark those functions accordingly. Note that, because gb_ap_exit() and gb_debugfs_cleanup() are called by gb_init() in error paths, these functions cannot be declared with the __exit attribute. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 2 +- drivers/staging/greybus/debugfs.c | 2 +- drivers/staging/greybus/greybus.h | 4 ++-- drivers/staging/greybus/operation.c | 4 ++-- drivers/staging/greybus/operation.h | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 843ddc24f41c..07375cc96bfb 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -354,7 +354,7 @@ int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size) } EXPORT_SYMBOL_GPL(greybus_svc_in); -int gb_ap_init(void) +int __init gb_ap_init(void) { ap_workqueue = alloc_ordered_workqueue("greybus_ap", 0); if (!ap_workqueue) diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c index 59c570964b01..725565de5d43 100644 --- a/drivers/staging/greybus/debugfs.c +++ b/drivers/staging/greybus/debugfs.c @@ -15,7 +15,7 @@ static struct dentry *gb_debug_root; -void gb_debugfs_init(void) +void __init gb_debugfs_init(void) { gb_debug_root = debugfs_create_dir("greybus", NULL); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 05eab4c5f9a0..cf7c4419aad7 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -154,9 +154,9 @@ void greybus_deregister_driver(struct greybus_driver *driver); int greybus_disabled(void); int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); -int gb_ap_init(void); +int gb_ap_init(void) __init; void gb_ap_exit(void); -void gb_debugfs_init(void); +void gb_debugfs_init(void) __init; void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 88b9f6a40c79..49bc628e621b 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -932,7 +932,7 @@ int gb_operation_sync(struct gb_connection *connection, int type, } EXPORT_SYMBOL_GPL(gb_operation_sync); -int gb_operation_init(void) +int __init gb_operation_init(void) { gb_message_cache = kmem_cache_create("gb_message_cache", sizeof(struct gb_message), 0, 0, NULL); @@ -959,7 +959,7 @@ err_destroy_message_cache: return -ENOMEM; } -void gb_operation_exit(void) +void __exit gb_operation_exit(void) { destroy_workqueue(gb_operation_workqueue); gb_operation_workqueue = NULL; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 0199976c9b5f..fcd6566f4bc8 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -158,7 +158,7 @@ int gb_operation_sync(struct gb_connection *connection, int type, void *request, int request_size, void *response, int response_size); -int gb_operation_init(void); -void gb_operation_exit(void); +int gb_operation_init(void) __init; +void gb_operation_exit(void) __exit; #endif /* !__OPERATION_H */ -- cgit v1.2.3-59-g8ed1b From f35ab903ef6dad781a3de25ab37850499d2a39d4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:51 -0500 Subject: greybus: endo: define endo_init() and endo_exit() Define init and exit functions to do one-time setup and teardown of endo-related functionality. Currently they're place holders; the next patch will populate them. Note that we now call gb_operation_exit() from gb_init(), so we can no longer mark that function with __exit. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 16 ++++++++++++---- drivers/staging/greybus/endo.c | 8 ++++++++ drivers/staging/greybus/endo.h | 3 +++ drivers/staging/greybus/operation.c | 2 +- drivers/staging/greybus/operation.h | 2 +- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index e7a280cfbe81..8da120bcdf3d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -252,7 +252,7 @@ static int __init gb_init(void) retval = bus_register(&greybus_bus_type); if (retval) { - pr_err("bus_register failed\n"); + pr_err("bus_register failed (%d)\n", retval); goto error_bus; } @@ -260,18 +260,26 @@ static int __init gb_init(void) retval = gb_ap_init(); if (retval) { - pr_err("gb_ap_init failed\n"); + pr_err("gb_ap_init failed (%d)\n", retval); goto error_ap; } retval = gb_operation_init(); if (retval) { - pr_err("gb_operation_init failed\n"); + pr_err("gb_operation_init failed (%d)\n", retval); goto error_operation; } + retval = gb_endo_init(); + if (retval) { + pr_err("gb_endo_init failed (%d)\n", retval); + goto error_endo; + } + return 0; /* Success */ +error_endo: + gb_operation_exit(); error_operation: gb_ap_exit(); error_ap: @@ -285,12 +293,12 @@ module_init(gb_init); static void __exit gb_exit(void) { + gb_endo_exit(); gb_operation_exit(); gb_ap_exit(); bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); } module_exit(gb_exit); - MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index b89b14f59fb2..37bd8ae15bfe 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -551,3 +551,11 @@ void gb_endo_remove(struct gb_endo *endo) device_unregister(&endo->dev); } +int __init gb_endo_init(void) +{ + return 0; +} + +void __exit gb_endo_exit(void) +{ +} diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index 63dbadf5589f..0ff40e990ab8 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -51,6 +51,9 @@ extern struct ida greybus_endo_id_map; /* Greybus "private" definitions */ struct greybus_host_device; +int gb_endo_init(void) __init; +void gb_endo_exit(void) __exit; + struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id, u8 ap_intf_id); void gb_endo_remove(struct gb_endo *endo); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 49bc628e621b..a713dafabf6e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -959,7 +959,7 @@ err_destroy_message_cache: return -ENOMEM; } -void __exit gb_operation_exit(void) +void gb_operation_exit(void) { destroy_workqueue(gb_operation_workqueue); gb_operation_workqueue = NULL; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index fcd6566f4bc8..6eed6bc62148 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -159,6 +159,6 @@ int gb_operation_sync(struct gb_connection *connection, int type, void *response, int response_size); int gb_operation_init(void) __init; -void gb_operation_exit(void) __exit; +void gb_operation_exit(void); #endif /* !__OPERATION_H */ -- cgit v1.2.3-59-g8ed1b From 79dda60987bc8df963c910bbcbfd9dc11bfada03 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:52 -0500 Subject: greybus: endo: clean up id assignment code Recently code was added (back) to assign a unique id to each endo, so satisfy uniqueness requirements of the Linux device subsystem. An ID allocator is used to manage the space of IDs. Now that we have gb_endo_init(), we can initialize the map there, and fully hide the ID map within "endo.c". The original functions gb_endo_id_alloc() and gb_endo_id_free() provided a nice abstract interface, but the direct ID allocation calls are quite simple, so just call them directly. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 -- drivers/staging/greybus/endo.c | 41 +++++++---------------------------------- drivers/staging/greybus/endo.h | 2 -- 3 files changed, 7 insertions(+), 38 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 8da120bcdf3d..18b5d5f48398 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -256,8 +256,6 @@ static int __init gb_init(void) goto error_bus; } - ida_init(&greybus_endo_id_map); - retval = gb_ap_init(); if (retval) { pr_err("gb_ap_init failed (%d)\n", retval); diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 37bd8ae15bfe..c77861463883 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -43,7 +43,7 @@ #define max_endo_interface_id(endo_layout) \ (4 + ((endo_layout)->max_ribs + 1) * 2) -struct ida greybus_endo_id_map; +static struct ida greybus_endo_id_map; /* endo sysfs attributes */ static ssize_t serial_number_show(struct device *dev, @@ -434,42 +434,13 @@ static int create_modules(struct gb_endo *endo) return 0; } -/* - * Allocate an available Id to uniquely identify the endo device. The lowest - * available id is returned, so the first call is guaranteed to allocate endo Id - * 0. - * - * Assigns the endo's id and returns 0 if successful. - * Returns error otherwise. - */ -static int gb_endo_id_alloc(struct gb_endo *endo) -{ - int id; - - id = ida_simple_get(&greybus_endo_id_map, 0, 0, GFP_ATOMIC); - if (id < 0) - return id; - - endo->dev_id = (u16)id; - - return 0; -} - -/* - * Free a previously-allocated Endo Id. - */ -static void gb_endo_id_free(struct gb_endo *endo) -{ - ida_simple_remove(&greybus_endo_id_map, endo->dev_id); -} - static int gb_endo_register(struct greybus_host_device *hd, struct gb_endo *endo) { int retval; - retval = gb_endo_id_alloc(endo); - if (retval) + retval = ida_simple_get(&greybus_endo_id_map, 0, 0, GFP_ATOMIC); + if (retval < 0) return retval; endo->dev.parent = hd->parent; @@ -491,7 +462,7 @@ static int gb_endo_register(struct greybus_host_device *hd, dev_err(hd->parent, "failed to add endo device of id 0x%04x\n", endo->id); put_device(&endo->dev); - gb_endo_id_free(endo); + ida_simple_remove(&greybus_endo_id_map, endo->dev_id); } return retval; @@ -547,12 +518,14 @@ void gb_endo_remove(struct gb_endo *endo) /* remove all modules for this endo */ gb_module_remove_all(endo); - gb_endo_id_free(endo); + ida_simple_remove(&greybus_endo_id_map, endo->dev_id); device_unregister(&endo->dev); } int __init gb_endo_init(void) { + ida_init(&greybus_endo_id_map); + return 0; } diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index 0ff40e990ab8..d9f4976ac63d 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -46,8 +46,6 @@ struct gb_endo { }; #define to_gb_endo(d) container_of(d, struct gb_endo, dev) -extern struct ida greybus_endo_id_map; - /* Greybus "private" definitions */ struct greybus_host_device; -- cgit v1.2.3-59-g8ed1b From d393c98f11a8a3355387d52386c885bc89970176 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:53 -0500 Subject: greybus: manifest: really minor cleanups This patch incorporates some very small cleanups to "manifest.c": - Rearrange code a bit in gb_manifest_parse() that ensures a manifest is big enough to hold a header. If the manifest is exactly the size of a header, the error reported will now be "...must have 1 interface..." rather than "short manifest". - Fix the function comment for gb_manifest_parse_cports(). - Use "an interface," not "a interface," and don't capitalize. - Delete some braces when getting interface product string. - A few other minor changes to comments and white space. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index f4fe4f37cda6..b937ddecbe41 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -67,8 +67,8 @@ static void release_manifest_descriptors(struct gb_interface *intf) * we expect to see. (It could be bigger, perhaps for a new version * of the format.) * - * Returns the number of bytes consumed by the descriptor, or a - * negative errno. + * Returns the (non-zero) number of bytes consumed by the descriptor, + * or a negative errno. */ static int identify_descriptor(struct gb_interface *intf, struct greybus_descriptor *desc, size_t size) @@ -135,11 +135,11 @@ static int identify_descriptor(struct gb_interface *intf, return -ENOMEM; descriptor->size = desc_size; - descriptor->data = (u8 *)desc + sizeof(*desc_header); + descriptor->data = (char *)desc + sizeof(*desc_header); descriptor->type = desc_header->type; list_add_tail(&descriptor->links, &intf->manifest_descs); - /* desc_size is is positive and is known to fit in a signed int */ + /* desc_size is positive and is known to fit in a signed int */ return desc_size; } @@ -167,7 +167,6 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) return NULL; list_for_each_entry(descriptor, &intf->manifest_descs, links) { - if (descriptor->type != GREYBUS_TYPE_STRING) continue; @@ -181,7 +180,7 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) return ERR_PTR(-ENOENT); /* Allocate an extra byte so we can guarantee it's NUL-terminated */ - string = kmemdup(&desc_string->string, (size_t)desc_string->length + 1, + string = kmemdup(&desc_string->string, desc_string->length + 1, GFP_KERNEL); if (!string) return ERR_PTR(-ENOMEM); @@ -194,9 +193,10 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) } /* - * Find cport descriptors in the manifest and set up data structures - * for the functions that use them. Returns the number of bundles - * set up for the given interface, or 0 if there is an error. + * Find cport descriptors in the manifest associated with the given + * bundle, and set up data structures for the functions that use + * them. Returns the number of cports set up for the bundle, or 0 + * if there is an error. */ static u32 gb_manifest_parse_cports(struct gb_interface *intf, struct gb_bundle *bundle) @@ -292,11 +292,9 @@ static bool gb_manifest_parse_interface(struct gb_interface *intf, if (IS_ERR(intf->vendor_string)) return false; - intf->product_string = gb_string_get(intf, - desc_intf->product_stringid); - if (IS_ERR(intf->product_string)) { + intf->product_string = gb_string_get(intf, desc_intf->product_stringid); + if (IS_ERR(intf->product_string)) goto out_free_vendor_string; - } // FIXME // Vendor, Product and Unique id must come via control protocol @@ -325,7 +323,7 @@ out_free_vendor_string: } /* - * Parse a buffer containing a Interface manifest. + * Parse a buffer containing an interface manifest. * * If we find anything wrong with the content/format of the buffer * we reject it. @@ -337,7 +335,7 @@ out_free_vendor_string: * the descriptors it contains, keeping track for each its type * and the location size of its data in the buffer. * - * Next we scan the descriptors, looking for a interface descriptor; + * Next we scan the descriptors, looking for an interface descriptor; * there must be exactly one of those. When found, we record the * information it contains, and then remove that descriptor (and any * string descriptors it refers to) from further consideration. @@ -363,8 +361,8 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) return false; /* we have to have at _least_ the manifest header */ - if (size <= sizeof(manifest->header)) { - pr_err("short manifest (%zu)\n", size); + if (size < sizeof(*header)) { + pr_err("short manifest (%zu < %zu)\n", size, sizeof(*header)); return false; } -- cgit v1.2.3-59-g8ed1b From c46839d1cc418aad43c66344888cd266b689acf9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:54 -0500 Subject: greybus: manifest: use bundle's embedded interface pointer An initialized bundle structure contains a pointer to its interface. Because of this there's no need to provide the interface pointer to gb_manifest_parse_cports(). This also precludes the possibility of passing a bad interface pointer. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index b937ddecbe41..d20cd80e6cdd 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -198,9 +198,9 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) * them. Returns the number of cports set up for the bundle, or 0 * if there is an error. */ -static u32 gb_manifest_parse_cports(struct gb_interface *intf, - struct gb_bundle *bundle) +static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) { + struct gb_interface *intf = bundle->intf; u32 count = 0; while (true) { @@ -270,7 +270,7 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) return 0; /* Error */ /* Now go set up this bundle's functions and cports */ - if (!gb_manifest_parse_cports(intf, bundle)) + if (!gb_manifest_parse_cports(bundle)) return 0; /* Error parsing cports */ count++; -- cgit v1.2.3-59-g8ed1b From a6b13eb648fd3d03d45e722f81f26eed9bdecb87 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:55 -0500 Subject: greybus: manifest: rework cport parsing Rework the the code that parses the manifest for CPorts associated with a bundle so it only touches each manifest descriptor once. (Previously the list was scanned from the beginning repeatedly until all bundle CPorts were found.) Shorten the name of the descriptor variable, to avoid line wrap. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index d20cd80e6cdd..1b79161bd78f 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -201,27 +201,23 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) { struct gb_interface *intf = bundle->intf; + struct manifest_desc *desc; + struct manifest_desc *next; + u8 bundle_id = bundle->id; u32 count = 0; - while (true) { - struct manifest_desc *descriptor; + /* Set up all cport descriptors associated with this bundle */ + list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { struct greybus_descriptor_cport *desc_cport; u8 protocol_id; u16 cport_id; - bool found = false; - /* Find a cport descriptor */ - list_for_each_entry(descriptor, &intf->manifest_descs, links) { - if (descriptor->type == GREYBUS_TYPE_CPORT) { - desc_cport = descriptor->data; - if (desc_cport->bundle == bundle->id) { - found = true; - break; - } - } - } - if (!found) - break; + if (desc->type != GREYBUS_TYPE_CPORT) + continue; + + desc_cport = desc->data; + if (desc_cport->bundle != bundle_id) + continue; /* Found one. Set up its function structure */ protocol_id = desc_cport->protocol_id; @@ -230,8 +226,9 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) return 0; /* Error */ count++; + /* Release the cport descriptor */ - release_manifest_descriptor(descriptor); + release_manifest_descriptor(desc); } return count; -- cgit v1.2.3-59-g8ed1b From c27a253fc0481b46759082c72d196777ea459a6e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:56 -0500 Subject: greybus: manifest: rework bundle parsing Rework the the code that parses the manifest for bundles so it only touches each manifest descriptor once. (Previously the list was scanned from the beginning repeatedly until all bundles were found.) Shorten the name of the descriptor variable, to avoid line wrap. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 1b79161bd78f..9881b7a1d92d 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -241,26 +241,19 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) */ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) { + struct manifest_desc *desc; + struct manifest_desc *next; u32 count = 0; - while (true) { - struct manifest_desc *descriptor; + list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { struct greybus_descriptor_bundle *desc_bundle; struct gb_bundle *bundle; - bool found = false; - - /* Find an bundle descriptor */ - list_for_each_entry(descriptor, &intf->manifest_descs, links) { - if (descriptor->type == GREYBUS_TYPE_BUNDLE) { - found = true; - break; - } - } - if (!found) - break; + + if (desc->type != GREYBUS_TYPE_BUNDLE) + continue; /* Found one. Set up its bundle structure*/ - desc_bundle = descriptor->data; + desc_bundle = desc->data; bundle = gb_bundle_create(intf, desc_bundle->id, desc_bundle->class); if (!bundle) @@ -273,7 +266,7 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) count++; /* Done with this bundle descriptor */ - release_manifest_descriptor(descriptor); + release_manifest_descriptor(desc); } return count; -- cgit v1.2.3-59-g8ed1b From 8267616b3ef73b22c9ed5b87905c13fc332fe507 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:57 -0500 Subject: greybus: bundle: check for duplicate bundle ids Check at bundle creation time to ensure we're not creating a bundle with an id that's the same as one that's already been created. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index a6b1b347097a..6e9d03ac7e35 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -165,6 +165,16 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, struct gb_bundle *bundle; int retval; + /* + * Reject any attempt to reuse a bundle id. We initialize + * these serially, so there's no need to worry about keeping + * the interface bundle list locked here. + */ + if (gb_bundle_find(intf, bundle_id)) { + pr_err("duplicate bundle id 0x%02hhx\n", bundle_id); + return NULL; + } + bundle = kzalloc(sizeof(*bundle), GFP_KERNEL); if (!bundle) return NULL; -- cgit v1.2.3-59-g8ed1b From f5c2be9e9bd934973d3e51b933bf7c03f85d2010 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 9 Jun 2015 17:42:58 -0500 Subject: greybus: connection: check for duplicate cport ids Check at connection creation time for an attempt to create a connection with an interface CPort ID that's the same as one that's already been created. Define a new helper function to look for such a duplicate. The check for a duplicate is only performed at initialization time, and CPorts are initialized serially for each bundle, so there's no need to acquire the list lock for this search. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 8f528c1640e1..ab6c60ec874c 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -11,10 +11,22 @@ static DEFINE_SPINLOCK(gb_connections_lock); +/* This is only used at initialization time; no locking is required. */ +static struct gb_connection * +gb_connection_intf_find(struct greybus_host_device *hd, u16 cport_id) +{ + struct gb_connection *connection; + + list_for_each_entry(connection, &hd->connections, hd_links) + if (connection->intf_cport_id == cport_id) + return connection; + return NULL; +} + static struct gb_connection * gb_connection_hd_find(struct greybus_host_device *hd, u16 cport_id) { - struct gb_connection *connection = NULL; + struct gb_connection *connection; unsigned long flags; spin_lock_irqsave(&gb_connections_lock, flags); @@ -22,7 +34,7 @@ gb_connection_hd_find(struct greybus_host_device *hd, u16 cport_id) if (connection->hd_cport_id == cport_id) goto found; connection = NULL; - found: +found: spin_unlock_irqrestore(&gb_connections_lock, flags); return connection; @@ -159,21 +171,29 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id) { struct gb_connection *connection; - struct greybus_host_device *hd; + struct greybus_host_device *hd = bundle->intf->hd; int retval; u8 major = 0; u8 minor = 1; + /* + * If a manifest tries to reuse a cport, reject it. We + * initialize connections serially so we don't need to worry + * about holding the connection lock. + */ + if (gb_connection_intf_find(hd, cport_id)) { + pr_err("duplicate interface cport id 0x%04hx\n", cport_id); + return NULL; + } + connection = kzalloc(sizeof(*connection), GFP_KERNEL); if (!connection) return NULL; + connection->hd = hd; connection->protocol_id = protocol_id; connection->major = major; connection->minor = minor; - - hd = bundle->intf->hd; - connection->hd = hd; if (!gb_connection_hd_cport_id_alloc(connection)) { gb_protocol_put(connection->protocol); kfree(connection); -- cgit v1.2.3-59-g8ed1b From 0ffacf3b8ae169a26c941345b7fa54e1624f6b95 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 11 Jun 2015 16:39:42 +0530 Subject: greybus: endo: Fix compilation warning WARNING: /home/viresh/work/repos/ara/greybus/greybus.o(.init.text+0xb8): Section mismatch in reference from the function init_module() to the function .exit.text:gb_endo_exit() The function __init init_module() references a function __exit gb_endo_exit(). Fix it by removing __exit from endo_exit(). Fixes: cf64356c5151 ("endo: define endo_init() and endo_exit()") Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 2 +- drivers/staging/greybus/endo.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index c77861463883..baa4aa581096 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -529,6 +529,6 @@ int __init gb_endo_init(void) return 0; } -void __exit gb_endo_exit(void) +void gb_endo_exit(void) { } diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index d9f4976ac63d..ad5728c5b4a0 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -50,7 +50,7 @@ struct gb_endo { struct greybus_host_device; int gb_endo_init(void) __init; -void gb_endo_exit(void) __exit; +void gb_endo_exit(void); struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id, u8 ap_intf_id); -- cgit v1.2.3-59-g8ed1b From 3d0421e0ab5315051b949ea70dee37c15e860706 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Jun 2015 09:22:51 -0700 Subject: greybus: remove __init from .h files __init does not belong in a .h file, as it does not do anything there, so remove all instances of it. Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.h | 2 +- drivers/staging/greybus/greybus.h | 4 ++-- drivers/staging/greybus/operation.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index ad5728c5b4a0..3622428552a3 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -49,7 +49,7 @@ struct gb_endo { /* Greybus "private" definitions */ struct greybus_host_device; -int gb_endo_init(void) __init; +int gb_endo_init(void); void gb_endo_exit(void); struct gb_endo *gb_endo_create(struct greybus_host_device *hd, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index cf7c4419aad7..05eab4c5f9a0 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -154,9 +154,9 @@ void greybus_deregister_driver(struct greybus_driver *driver); int greybus_disabled(void); int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); -int gb_ap_init(void) __init; +int gb_ap_init(void); void gb_ap_exit(void); -void gb_debugfs_init(void) __init; +void gb_debugfs_init(void); void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 6eed6bc62148..0199976c9b5f 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -158,7 +158,7 @@ int gb_operation_sync(struct gb_connection *connection, int type, void *request, int request_size, void *response, int response_size); -int gb_operation_init(void) __init; +int gb_operation_init(void); void gb_operation_exit(void); #endif /* !__OPERATION_H */ -- cgit v1.2.3-59-g8ed1b From 55a8e3550364a35bc5d40fa0e445049a5df62627 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Wed, 10 Jun 2015 21:03:17 +0700 Subject: greybus: uart: fix the clean up while uart initiates connection unsucessfully There is lack of unregister and free the tty driver. This patch fixes it. Signed-off-by: Phong Tran Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 7e94632a581b..20928702a5b2 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -586,20 +586,22 @@ static int gb_uart_connection_init(struct gb_connection *connection) } gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); - if (!gb_tty) - return -ENOMEM; + if (!gb_tty) { + retval = -ENOMEM; + goto error_alloc; + } gb_tty->buffer_payload_max = gb_operation_get_payload_size_max(connection); if (!gb_tty->buffer_payload_max) { - kfree(gb_tty); - return -EINVAL; + retval = -EINVAL; + goto error_payload; } gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); if (!gb_tty->buffer) { - kfree(gb_tty); - return -ENOMEM; + retval = -ENOMEM; + goto error_payload; } gb_tty->connection = connection; @@ -654,7 +656,11 @@ error: error_version: connection->private = NULL; kfree(gb_tty->buffer); +error_payload: kfree(gb_tty); +error_alloc: + if (atomic_dec_return(&reference_count) == 0) + gb_tty_exit(); return retval; } -- cgit v1.2.3-59-g8ed1b From 856618f3fb6506f277adf060717590d03d3fb559 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 12 Jun 2015 10:21:06 -0500 Subject: greybus: connection: remove extra kfree() call When an error occurs in the device_add() call for a connection, the device reference is dropped as required. Because that's the device's only reference, that will also lead to gb_connection_release() being called, which frees the connection structure. Right now we're then making an extra request to free the connection, which is wrong. Fix that. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ab6c60ec874c..bf5fa101e541 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -219,7 +219,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, gb_connection_hd_cport_id_free(connection); gb_protocol_put(connection->protocol); put_device(&connection->dev); - kfree(connection); + return NULL; } -- cgit v1.2.3-59-g8ed1b From deb58ca8299598f81123dd92456c0cc3a9a38555 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 12 Jun 2015 10:21:07 -0500 Subject: greybus: connection: drop unneeded gb_protocol_put() calls Although a connection records its protocol id when it gets created, its protocol handler doesn't actually get assigned until gb_connection_bind_protocol() is called. In gb_connection_create() there are some error paths in which a reference to the connection's protocol is released before the protocol handler has been associated with the connection. Get rid of those calls. As a result, we will never pass a null protocol pointer to gb_protocol_put(). Add a precautionary warning in that function in the event that ever occurs. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 3 +-- drivers/staging/greybus/protocol.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index bf5fa101e541..abd74116cf62 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -195,7 +195,6 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, connection->major = major; connection->minor = minor; if (!gb_connection_hd_cport_id_alloc(connection)) { - gb_protocol_put(connection->protocol); kfree(connection); return NULL; } @@ -217,7 +216,6 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, pr_err("failed to add connection device for cport 0x%04hx\n", cport_id); gb_connection_hd_cport_id_free(connection); - gb_protocol_put(connection->protocol); put_device(&connection->dev); return NULL; @@ -264,6 +262,7 @@ void gb_connection_destroy(struct gb_connection *connection) gb_connection_hd_cport_id_free(connection); gb_protocol_put(connection->protocol); + connection->protocol = NULL; device_unregister(&connection->dev); } diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index eed77c3f3a7f..6aebbbd585e1 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -196,7 +196,7 @@ void gb_protocol_put(struct gb_protocol *protocol) u8 minor; u8 protocol_count; - if (!protocol) + if (WARN_ON(!protocol)) return; id = protocol->id; -- cgit v1.2.3-59-g8ed1b From f9b0366f16c3926e92e7d108fe8666fac671d026 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 12 Jun 2015 10:21:08 -0500 Subject: greybus: connection: un-abstract host cport id allocation I did this recently for the endo id allocation code. It's clearer now that the allocation of a CPort ID to use for the AP side of a connection is not very complicated, and it happens in a pretty controlled environment. The functions that abstract getting and releasing those ids don't really add that much value. This patch removes gb_connection_hd_cport_id_alloc() and gb_connection_hd_cport_id_free(), and just open-codes their activity in the few places they are called. It is obvious now that the CPort ID allocation isn't done in atomic context, so we can change the ida_simple_get() call to use GFP_KERNEL. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 61 ++++++++++++------------------------ 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index abd74116cf62..7236e47ea9c8 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -59,39 +59,6 @@ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, } EXPORT_SYMBOL_GPL(greybus_data_rcvd); -/* - * Allocate an available CPort Id for use for the host side of the - * given connection. The lowest-available id is returned, so the - * first call is guaranteed to allocate CPort Id 0. - * - * Assigns the connection's hd_cport_id and returns true if successful. - * Returns false otherwise. - */ -static bool gb_connection_hd_cport_id_alloc(struct gb_connection *connection) -{ - struct ida *ida = &connection->hd->cport_id_map; - int id; - - id = ida_simple_get(ida, 0, HOST_DEV_CPORT_ID_MAX, GFP_ATOMIC); - if (id < 0) - return false; - - connection->hd_cport_id = (u16)id; - - return true; -} - -/* - * Free a previously-allocated CPort Id on the given host device. - */ -static void gb_connection_hd_cport_id_free(struct gb_connection *connection) -{ - struct ida *ida = &connection->hd->cport_id_map; - - ida_simple_remove(ida, connection->hd_cport_id); - connection->hd_cport_id = CPORT_ID_BAD; -} - static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -172,6 +139,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, { struct gb_connection *connection; struct greybus_host_device *hd = bundle->intf->hd; + struct ida *id_map = &hd->cport_id_map; int retval; u8 major = 0; u8 minor = 1; @@ -190,17 +158,20 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, if (!connection) return NULL; + retval = ida_simple_get(id_map, 0, HOST_DEV_CPORT_ID_MAX, GFP_KERNEL); + if (retval < 0) { + kfree(connection); + return NULL; + } + connection->hd_cport_id = (u16)retval; + connection->intf_cport_id = cport_id; connection->hd = hd; + connection->protocol_id = protocol_id; connection->major = major; connection->minor = minor; - if (!gb_connection_hd_cport_id_alloc(connection)) { - kfree(connection); - return NULL; - } connection->bundle = bundle; - connection->intf_cport_id = cport_id; connection->state = GB_CONNECTION_STATE_DISABLED; connection->dev.parent = &bundle->dev; @@ -213,10 +184,14 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, retval = device_add(&connection->dev); if (retval) { + struct ida *id_map = &connection->hd->cport_id_map; + + ida_simple_remove(id_map, connection->hd_cport_id); + connection->hd_cport_id = CPORT_ID_BAD; + put_device(&connection->dev); + pr_err("failed to add connection device for cport 0x%04hx\n", cport_id); - gb_connection_hd_cport_id_free(connection); - put_device(&connection->dev); return NULL; } @@ -245,6 +220,7 @@ void gb_connection_destroy(struct gb_connection *connection) { struct gb_operation *operation; struct gb_operation *next; + struct ida *id_map; if (WARN_ON(!connection)) return; @@ -260,10 +236,13 @@ void gb_connection_destroy(struct gb_connection *connection) list_del(&connection->hd_links); spin_unlock_irq(&gb_connections_lock); - gb_connection_hd_cport_id_free(connection); gb_protocol_put(connection->protocol); connection->protocol = NULL; + id_map = &connection->hd->cport_id_map; + ida_simple_remove(id_map, connection->hd_cport_id); + connection->hd_cport_id = CPORT_ID_BAD; + device_unregister(&connection->dev); } -- cgit v1.2.3-59-g8ed1b From 52e8ce317fe51dea86777b164b63e41e50f8a7bf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 12 Jun 2015 10:21:09 -0500 Subject: greybus: manifest: clean up properly when parsing cports Currently, if an error occurs creating a connection, we simply return an error without cleaning up any of the connections that had already been successfully set up. Add code to destroy connections that have been created in the event an error occurs. Add a check to ensure the bundle's list of connections was empty before parsing for CPorts begins. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 9881b7a1d92d..0b509cd38511 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -201,11 +201,16 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) { struct gb_interface *intf = bundle->intf; + struct gb_connection *connection; + struct gb_connection *connection_next; struct manifest_desc *desc; struct manifest_desc *next; u8 bundle_id = bundle->id; u32 count = 0; + if (WARN_ON(!list_empty(&bundle->connections))) + return 0; + /* Set up all cport descriptors associated with this bundle */ list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { struct greybus_descriptor_cport *desc_cport; @@ -223,7 +228,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) protocol_id = desc_cport->protocol_id; cport_id = le16_to_cpu(desc_cport->id); if (!gb_connection_create(bundle, cport_id, protocol_id)) - return 0; /* Error */ + goto cleanup; count++; @@ -232,6 +237,14 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) } return count; +cleanup: + /* An error occurred; undo any changes we've made */ + list_for_each_entry_safe(connection, connection_next, + &bundle->connections, bundle_links) { + gb_connection_destroy(connection); + count--; + } + return 0; /* Error; count should also be 0 */ } /* -- cgit v1.2.3-59-g8ed1b From bc942083585da78a5287089023aebcc8952c21f4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 12 Jun 2015 10:21:10 -0500 Subject: greybus: bundle: refactor gb_bundle_find() Rearrange gb_bundle_find() so it follows the pattern used by gb_connection_find(). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 6e9d03ac7e35..89568b2b0adf 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -283,13 +283,13 @@ struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id) spin_lock_irq(&gb_bundles_lock); list_for_each_entry(bundle, &intf->bundles, links) - if (bundle->id == bundle_id) { - spin_unlock_irq(&gb_bundles_lock); - return bundle; - } + if (bundle->id == bundle_id) + goto found; + bundle = NULL; +found: spin_unlock_irq(&gb_bundles_lock); - return NULL; + return bundle; } static int gb_bundle_connections_init(struct gb_bundle *bundle) -- cgit v1.2.3-59-g8ed1b From fe53b45ca8143e7f1073ff31d7c4cfb4e92dc824 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 12 Jun 2015 10:21:11 -0500 Subject: greybus: bundle: fix gb_bundle_destroy() Currently gb_bundle_destroy() takes an interface as an argument, and really doesn't do what a function by that name should do. What it now does is delete all bundles associated with a given interface. What it should do is destroy a single bundle. Move the looping logic out of gb_bundle_destroy() and into its caller, gb_interface_destroy(). Pass each bundle in an interface to gb_bundle_destroy(), which will do what's required to destroy a single bundle (including removing it from its interface's bundle list under protection of the lock). Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 18 ++++-------------- drivers/staging/greybus/bundle.h | 2 +- drivers/staging/greybus/interface.c | 6 ++++-- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 89568b2b0adf..8d0e86fc2cfc 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -215,24 +215,14 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, /* * Tear down a previously set up bundle. */ -void gb_bundle_destroy(struct gb_interface *intf) +void gb_bundle_destroy(struct gb_bundle *bundle) { - LIST_HEAD(list); - struct gb_bundle *bundle; - struct gb_bundle *temp; - - if (WARN_ON(!intf)) - return; - spin_lock_irq(&gb_bundles_lock); - list_splice_init(&intf->bundles, &list); + list_del(&bundle->links); spin_unlock_irq(&gb_bundles_lock); - list_for_each_entry_safe(bundle, temp, &list, links) { - list_del(&bundle->links); - gb_bundle_connections_exit(bundle); - device_unregister(&bundle->dev); - } + gb_bundle_connections_exit(bundle); + device_unregister(&bundle->dev); } int gb_bundle_init(struct gb_bundle *bundle, u8 device_id) diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 5c12c72ddcec..887883dabfc0 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -31,7 +31,7 @@ struct gb_bundle { /* Greybus "private" definitions" */ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, u8 class); -void gb_bundle_destroy(struct gb_interface *intf); +void gb_bundle_destroy(struct gb_bundle *bundle); int gb_bundle_init(struct gb_bundle *bundle, u8 device_id); int gb_bundles_init(struct gb_interface *intf, u8 device_id); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 3483f848240b..5b1621ccf724 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -141,6 +141,8 @@ put_module: static void gb_interface_destroy(struct gb_interface *intf) { struct gb_module *module; + struct gb_bundle *bundle; + struct gb_bundle *next; if (WARN_ON(!intf)) return; @@ -149,11 +151,11 @@ static void gb_interface_destroy(struct gb_interface *intf) list_del(&intf->links); spin_unlock_irq(&gb_interfaces_lock); - gb_bundle_destroy(intf); + list_for_each_entry_safe(bundle, next, &intf->bundles, links) + gb_bundle_destroy(bundle); kfree(intf->product_string); kfree(intf->vendor_string); - /* kref_put(module->hd); */ module = intf->module; device_unregister(&intf->dev); -- cgit v1.2.3-59-g8ed1b From 2a64fb0e1e13eee53e56805d8a1f0ff7bbf57306 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 12 Jun 2015 10:21:12 -0500 Subject: greybus: manifest: clean up properly when parsing bundles Currently, if an error occurs creating a bundle, we simply return an error without cleaning up any of the bundles that had already been successfully set up. Add code to destroy bundles that have been created in the event an error occurs. Add a check to ensure the interface's list of bundles was empty before parsing for bundles begins. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 0b509cd38511..e329f3763176 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -256,11 +256,15 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) { struct manifest_desc *desc; struct manifest_desc *next; + struct gb_bundle *bundle; + struct gb_bundle *bundle_next; u32 count = 0; + if (WARN_ON(!list_empty(&intf->bundles))) + return 0; + list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { struct greybus_descriptor_bundle *desc_bundle; - struct gb_bundle *bundle; if (desc->type != GREYBUS_TYPE_BUNDLE) continue; @@ -270,11 +274,11 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) bundle = gb_bundle_create(intf, desc_bundle->id, desc_bundle->class); if (!bundle) - return 0; /* Error */ + goto cleanup; /* Now go set up this bundle's functions and cports */ if (!gb_manifest_parse_cports(bundle)) - return 0; /* Error parsing cports */ + goto cleanup; count++; @@ -283,6 +287,13 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) } return count; +cleanup: + /* An error occurred; undo any changes we've made */ + list_for_each_entry_safe(bundle, bundle_next, &intf->bundles, links) { + gb_bundle_destroy(bundle); + count--; + } + return 0; /* Error; count should also be 0 */ } static bool gb_manifest_parse_interface(struct gb_interface *intf, -- cgit v1.2.3-59-g8ed1b From 821c620afa1ad29be6a85a9a3f691e32e973a317 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 13 Jun 2015 11:02:07 -0500 Subject: greybus: introduce cport_id_valid() Define a public predicate that defines whether a CPort ID is valid. Use it in the message_send() routine, and make the message reported more accurately reflect the error. Also use it to check whether the CPort ID in a received message is valid; if it is not, just drop the message. Get rid of local variable "buffer" in message_send(); it adds no value. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 18 ++++++++++-------- drivers/staging/greybus/es2.c | 18 ++++++++++-------- drivers/staging/greybus/greybus.h | 5 +++++ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 56c80c6ddb3a..e68ee48e8189 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -176,21 +176,17 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, { struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; - void *buffer; size_t buffer_size; int retval; struct urb *urb; - buffer = message->buffer; - buffer_size = sizeof(*message->header) + message->payload_size; - /* * The data actually transferred will include an indication * of where the data should be sent. Do one last check of * the target CPort id before filling it in. */ - if (cport_id == CPORT_ID_BAD) { - pr_err("request to send inbound data buffer\n"); + if (!cport_id_valid(cport_id)) { + pr_err("invalid destination cport 0x%02x\n", cport_id); return ERR_PTR(-EINVAL); } @@ -205,9 +201,11 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, */ put_unaligned_le16(cport_id, message->header->pad); + buffer_size = sizeof(*message->header) + message->payload_size; + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), - buffer, buffer_size, + message->buffer, buffer_size, cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); if (retval) { @@ -371,8 +369,12 @@ static void cport_in_callback(struct urb *urb) cport_id = get_unaligned_le16(header->pad); put_unaligned_le16(0, header->pad); - greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, + if (cport_id_valid(cport_id)) + greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); + else + dev_err(dev, "%s: invalid cport id 0x%02x received\n", + __func__, cport_id); exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 5257779f7aed..daa2e5176045 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -176,21 +176,17 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, { struct es1_ap_dev *es1 = hd_to_es1(hd); struct usb_device *udev = es1->usb_dev; - void *buffer; size_t buffer_size; int retval; struct urb *urb; - buffer = message->buffer; - buffer_size = sizeof(*message->header) + message->payload_size; - /* * The data actually transferred will include an indication * of where the data should be sent. Do one last check of * the target CPort id before filling it in. */ - if (cport_id == CPORT_ID_BAD) { - pr_err("request to send inbound data buffer\n"); + if (!cport_id_valid(cport_id)) { + pr_err("invalid destination cport 0x%02x\n", cport_id); return ERR_PTR(-EINVAL); } @@ -205,9 +201,11 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, */ put_unaligned_le16(cport_id, message->header->pad); + buffer_size = sizeof(*message->header) + message->payload_size; + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out_endpoint), - buffer, buffer_size, + message->buffer, buffer_size, cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); if (retval) { @@ -371,8 +369,12 @@ static void cport_in_callback(struct urb *urb) cport_id = get_unaligned_le16(header->pad); put_unaligned_le16(0, header->pad); - greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, + if (cport_id_valid(cport_id)) + greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); + else + dev_err(dev, "%s: invalid cport id 0x%02x received\n", + __func__, cport_id); exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 05eab4c5f9a0..518f142b6b5f 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -196,5 +196,10 @@ static inline int is_gb_connection(const struct device *dev) return dev->type == &greybus_connection_type; } +static inline bool cport_id_valid(u16 cport_id) +{ + return cport_id != CPORT_ID_BAD; +} + #endif /* __KERNEL__ */ #endif /* __LINUX_GREYBUS_H */ -- cgit v1.2.3-59-g8ed1b From d29b3d631e572400b45b5f9e48e432493663b0fc Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 13 Jun 2015 11:02:08 -0500 Subject: greybus: esX: encapsulate packing cport id into header For the ES1 and ES2 host interfaces we encode the CPort ID over which the message should be sent within the message itself. The CPort ID is recorded in unused pad bytes found in the operation message header in order to avoid introducing misaligned messages. This patch defines some helper routines to abstract this activity. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 42 ++++++++++++++++++++++++++++++++---------- drivers/staging/greybus/es2.c | 42 ++++++++++++++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index e68ee48e8189..945dbe658ed1 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -166,6 +166,32 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb) usb_free_urb(urb); } +/* + * We (ab)use the operation-message header pad bytes to transfer the + * cport id in order to minimise overhead. + */ +static void +gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) +{ + put_unaligned_le16(cport_id, header->pad); +} + +/* Clear the pad bytes used for the CPort id */ +static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) +{ + put_unaligned_le16(0, header->pad); +} + +/* Extract the CPort id packed into the header, and clear it */ +static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) +{ + u16 cport_id = get_unaligned_le16(header->pad); + + gb_message_cport_clear(header); + + return cport_id; +} + /* * Returns an opaque cookie value if successful, or a pointer coded * error otherwise. If the caller wishes to cancel the in-flight @@ -195,11 +221,8 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, if (!urb) return ERR_PTR(-ENOMEM); - /* - * We (ab)use the operation-message header pad bytes to transfer the - * cport id in order to minimise overhead. - */ - put_unaligned_le16(cport_id, message->header->pad); + /* Pack the cport id into the message header */ + gb_message_cport_pack(message->header, cport_id); buffer_size = sizeof(*message->header) + message->payload_size; @@ -211,7 +234,7 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, if (retval) { pr_err("error %d submitting URB\n", retval); free_urb(es1, urb); - put_unaligned_le16(0, message->header->pad); + gb_message_cport_clear(message->header); return ERR_PTR(retval); } @@ -365,9 +388,9 @@ static void cport_in_callback(struct urb *urb) goto exit; } + /* Extract the CPort id, which is packed in the message header */ header = urb->transfer_buffer; - cport_id = get_unaligned_le16(header->pad); - put_unaligned_le16(0, header->pad); + cport_id = gb_message_cport_unpack(header); if (cport_id_valid(cport_id)) greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, @@ -390,8 +413,7 @@ static void cport_out_callback(struct urb *urb) struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); - /* Clear the pad bytes used for the cport id */ - put_unaligned_le16(0, message->header->pad); + gb_message_cport_clear(message->header); /* * Tell the submitter that the message send (attempt) is diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index daa2e5176045..d6966bf0a262 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -166,6 +166,32 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb) usb_free_urb(urb); } +/* + * We (ab)use the operation-message header pad bytes to transfer the + * cport id in order to minimise overhead. + */ +static void +gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) +{ + put_unaligned_le16(cport_id, header->pad); +} + +/* Clear the pad bytes used for the CPort id */ +static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) +{ + put_unaligned_le16(0, header->pad); +} + +/* Extract the CPort id packed into the header, and clear it */ +static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) +{ + u16 cport_id = get_unaligned_le16(header->pad); + + gb_message_cport_clear(header); + + return cport_id; +} + /* * Returns an opaque cookie value if successful, or a pointer coded * error otherwise. If the caller wishes to cancel the in-flight @@ -195,11 +221,8 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, if (!urb) return ERR_PTR(-ENOMEM); - /* - * We (ab)use the operation-message header pad bytes to transfer the - * cport id in order to minimise overhead. - */ - put_unaligned_le16(cport_id, message->header->pad); + /* Pack the cport id into the message header */ + gb_message_cport_pack(message->header, cport_id); buffer_size = sizeof(*message->header) + message->payload_size; @@ -211,7 +234,7 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, if (retval) { pr_err("error %d submitting URB\n", retval); free_urb(es1, urb); - put_unaligned_le16(0, message->header->pad); + gb_message_cport_clear(message->header); return ERR_PTR(retval); } @@ -365,9 +388,9 @@ static void cport_in_callback(struct urb *urb) goto exit; } + /* Extract the CPort id, which is packed in the message header */ header = urb->transfer_buffer; - cport_id = get_unaligned_le16(header->pad); - put_unaligned_le16(0, header->pad); + cport_id = gb_message_cport_unpack(header); if (cport_id_valid(cport_id)) greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, @@ -390,8 +413,7 @@ static void cport_out_callback(struct urb *urb) struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); - /* Clear the pad bytes used for the cport id */ - put_unaligned_le16(0, message->header->pad); + gb_message_cport_clear(message->header); /* * Tell the submitter that the message send (attempt) is -- cgit v1.2.3-59-g8ed1b From fb690ca96f9a7aff8bc8135ce23606e243f4cbae Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 13 Jun 2015 11:02:09 -0500 Subject: greybus: rename HOST_DEV_CPORT_ID_MAX We limit the number of host-side CPorts to a fixed maximum (which is less than the 4096 that UniPro allows). This patch imposes a similar limit on the CPort IDs defined by modules (signaling an error if one too large is found in a manifest). It seems reasonable to use the same value for both limits. Change the name of the constant that defines the host limit and use it for both. Update cport_id_valid() to enforce the maximum. (Ultimately we should impose a limit like this; this change is being made in preparation for supporting multiple connections over a single CPort.) Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/greybus.h | 12 ++++++------ drivers/staging/greybus/manifest.c | 5 ++++- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7236e47ea9c8..9467aaba2b32 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -158,7 +158,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, if (!connection) return NULL; - retval = ida_simple_get(id_map, 0, HOST_DEV_CPORT_ID_MAX, GFP_KERNEL); + retval = ida_simple_get(id_map, 0, CPORT_ID_MAX, GFP_KERNEL); if (retval < 0) { kfree(connection); return NULL; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 18b5d5f48398..d4fffecb1abc 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -246,7 +246,7 @@ static int __init gb_init(void) if (greybus_disabled()) return -ENODEV; - BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD); + BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD); gb_debugfs_init(); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 518f142b6b5f..5c6f9607cf73 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -52,14 +52,14 @@ .serial_number = (s), /* XXX I couldn't get my Kconfig file to be noticed for out-of-tree build */ -#ifndef CONFIG_HOST_DEV_CPORT_ID_MAX -#define CONFIG_HOST_DEV_CPORT_ID_MAX 128 -#endif /* !CONFIG_HOST_DEV_CPORT_ID_MAX */ +#ifndef CONFIG_CPORT_ID_MAX +#define CONFIG_CPORT_ID_MAX 128 +#endif /* !CONFIG_CPORT_ID_MAX */ /* Maximum number of CPorts usable by a host device */ /* XXX This should really be determined by the AP module manifest */ -#define HOST_DEV_CPORT_ID_MAX CONFIG_HOST_DEV_CPORT_ID_MAX -#define CPORT_ID_BAD U16_MAX /* UniPro max id is 4095 */ +#define CPORT_ID_MAX CONFIG_CPORT_ID_MAX +#define CPORT_ID_BAD U16_MAX /* UniPro max id is 4095 */ /* For SP1 hardware, we are going to "hardcode" each device to have all logical * blocks in order to be able to address them as one unified "unit". Then @@ -198,7 +198,7 @@ static inline int is_gb_connection(const struct device *dev) static inline bool cport_id_valid(u16 cport_id) { - return cport_id != CPORT_ID_BAD; + return cport_id != CPORT_ID_BAD && cport_id <= CPORT_ID_MAX; } #endif /* __KERNEL__ */ diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index e329f3763176..ad55a73b737b 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -224,9 +224,12 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) if (desc_cport->bundle != bundle_id) continue; + cport_id = le16_to_cpu(desc_cport->id); + if (cport_id > CPORT_ID_MAX) + goto cleanup; + /* Found one. Set up its function structure */ protocol_id = desc_cport->protocol_id; - cport_id = le16_to_cpu(desc_cport->id); if (!gb_connection_create(bundle, cport_id, protocol_id)) goto cleanup; -- cgit v1.2.3-59-g8ed1b From 88d18a975ddac4fa849909c2ae178b0c126f7f8e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 13 Jun 2015 11:02:10 -0500 Subject: greybus: reserve host cport id 0 For ES1 and ES2, we use pad bytes in an operation message header to encode the CPort ID used for transferring the message. The pad bytes should otherwise be zero, and we ensure this as the message is passed to or from the upper layer. If host-side CPort ID 0 is used, we have no way of knowing whether the CPort field has been "packed" into the header. To allow detection of this, reserve host CPort id 0. Update cport_id_valid() to treat 0 as invalid. (CPort ID 0 is reserved by one of the UniPro standards. We'll assume for now that we never use it for Greybus.) Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 10 +++++++++- drivers/staging/greybus/greybus.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index d4fffecb1abc..bc9c1ebf7983 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -170,6 +170,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver size_t buffer_size_max) { struct greybus_host_device *hd; + int ret; /* * Validate that the driver implements all of the callbacks @@ -200,12 +201,19 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver if (!hd) return ERR_PTR(-ENOMEM); + ida_init(&hd->cport_id_map); + /* Reserve CPort id 0 */ + ret = ida_simple_get(&hd->cport_id_map, 0, 1, GFP_KERNEL); + if (ret < 0) { + kfree(hd); + return ERR_PTR(ret); + } + kref_init(&hd->kref); hd->parent = parent; hd->driver = driver; INIT_LIST_HEAD(&hd->interfaces); INIT_LIST_HEAD(&hd->connections); - ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; return hd; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 5c6f9607cf73..d727dea6847e 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -198,7 +198,7 @@ static inline int is_gb_connection(const struct device *dev) static inline bool cport_id_valid(u16 cport_id) { - return cport_id != CPORT_ID_BAD && cport_id <= CPORT_ID_MAX; + return cport_id && cport_id != CPORT_ID_BAD && cport_id <= CPORT_ID_MAX; } #endif /* __KERNEL__ */ -- cgit v1.2.3-59-g8ed1b From 4bc1389de9fcaaaf2f93e278f588858fb4fe2038 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 13 Jun 2015 11:02:11 -0500 Subject: greybus: esX: use one byte to encode cport ids in header We now limit the maximum value for both host and module CPort ids, and we know they can always be represented in a single byte. Make use of this by using only one of the two pad bytes for encoding the CPort id in a message header. (Note that we have never used a CPort higher than 255. Encoding such a small CPort id in little endian 2-byte format has the same result as what is done here.) Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 9 ++++++--- drivers/staging/greybus/es2.c | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 945dbe658ed1..d6824b6a83b4 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -173,19 +173,19 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb) static void gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) { - put_unaligned_le16(cport_id, header->pad); + header->pad[0] = cport_id; } /* Clear the pad bytes used for the CPort id */ static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) { - put_unaligned_le16(0, header->pad); + header->pad[0] = 0; } /* Extract the CPort id packed into the header, and clear it */ static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) { - u16 cport_id = get_unaligned_le16(header->pad); + u16 cport_id = header->pad[0]; gb_message_cport_clear(header); @@ -574,6 +574,9 @@ static int ap_probe(struct usb_interface *interface, u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; + /* We need to fit a CPort ID in one byte of a message header */ + BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); + udev = usb_get_dev(interface_to_usbdev(interface)); hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index d6966bf0a262..450a0162e8ad 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -173,19 +173,19 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb) static void gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) { - put_unaligned_le16(cport_id, header->pad); + header->pad[0] = cport_id; } /* Clear the pad bytes used for the CPort id */ static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) { - put_unaligned_le16(0, header->pad); + header->pad[0] = 0; } /* Extract the CPort id packed into the header, and clear it */ static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) { - u16 cport_id = get_unaligned_le16(header->pad); + u16 cport_id = header->pad[0]; gb_message_cport_clear(header); @@ -574,6 +574,9 @@ static int ap_probe(struct usb_interface *interface, u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; + /* We need to fit a CPort ID in one byte of a message header */ + BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); + udev = usb_get_dev(interface_to_usbdev(interface)); hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); -- cgit v1.2.3-59-g8ed1b From 799baa24dbe449154dcde3bb607d254b8162f901 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 15 Jun 2015 18:08:10 +0200 Subject: greybus: es1.c: Don't use magic value for USB control request Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 56c80c6ddb3a..9ae824abaf27 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -46,6 +46,12 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); */ #define NUM_CPORT_OUT_URB 8 +/* vendor request AP message */ +#define REQUEST_SVC 0x01 + +/* vendor request APB1 log */ +#define REQUEST_LOG 0x02 + /** * es1_ap_dev - ES1 USB Bridge to AP structure * @usb_dev: pointer to the USB device we are. @@ -103,7 +109,7 @@ static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) retval = usb_control_msg(es1->usb_dev, usb_sndctrlpipe(es1->usb_dev, es1->control_endpoint), - 0x01, /* vendor request AP message */ + REQUEST_SVC, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, (char *)svc_msg, @@ -410,7 +416,7 @@ static void apb1_log_get(struct es1_ap_dev *es1, char *buf) retval = usb_control_msg(es1->usb_dev, usb_rcvctrlpipe(es1->usb_dev, es1->control_endpoint), - 0x02, /* vendor request APB1 log */ + REQUEST_LOG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, buf, -- cgit v1.2.3-59-g8ed1b From 611c17390e7b47b3f311a9304439eaa4de94dbae Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 15 Jun 2015 18:08:11 +0200 Subject: greybus: es2.c: Don't use magic value for USB control request Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 5257779f7aed..a35c79a32420 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -46,6 +46,12 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); */ #define NUM_CPORT_OUT_URB 8 +/* vendor request AP message */ +#define REQUEST_SVC 0x01 + +/* vendor request APB1 log */ +#define REQUEST_LOG 0x02 + /** * es1_ap_dev - ES1 USB Bridge to AP structure * @usb_dev: pointer to the USB device we are. @@ -103,7 +109,7 @@ static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) retval = usb_control_msg(es1->usb_dev, usb_sndctrlpipe(es1->usb_dev, es1->control_endpoint), - 0x01, /* vendor request AP message */ + REQUEST_SVC, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, (char *)svc_msg, @@ -410,7 +416,7 @@ static void apb1_log_get(struct es1_ap_dev *es1, char *buf) retval = usb_control_msg(es1->usb_dev, usb_rcvctrlpipe(es1->usb_dev, es1->control_endpoint), - 0x02, /* vendor request APB1 log */ + REQUEST_LOG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, buf, -- cgit v1.2.3-59-g8ed1b From ddc09acd469523dc4a15bce30103743d243bc9ba Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 15 Jun 2015 18:08:12 +0200 Subject: greybus: es2.c: create dedicated struct for cport_in and cport_out Instead of keep cport buffers, urbs and endpoints in es1_ap_dev, move them in two dedicated struct (es1_cport_in and es1_cport_out), in order to ease the migration to es2 (increase the number of endpoint). Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 49 ++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index a35c79a32420..9b6c3cf69289 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -52,6 +52,24 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* vendor request APB1 log */ #define REQUEST_LOG 0x02 +/* + * @endpoint: bulk in endpoint for CPort data + * @urb: array of urbs for the CPort in messages + * @buffer: array of buffers for the @cport_in_urb urbs + */ +struct es1_cport_in { + __u8 endpoint; + struct urb *urb[NUM_CPORT_IN_URB]; + u8 *buffer[NUM_CPORT_IN_URB]; +}; + +/* + * @endpoint: bulk out endpoint for CPort data + */ +struct es1_cport_out { + __u8 endpoint; +}; + /** * es1_ap_dev - ES1 USB Bridge to AP structure * @usb_dev: pointer to the USB device we are. @@ -59,12 +77,11 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); * @hd: pointer to our greybus_host_device structure * @control_endpoint: endpoint to send data to SVC * @svc_endpoint: endpoint for SVC data in - * @cport_in_endpoint: bulk in endpoint for CPort data - * @cport-out_endpoint: bulk out endpoint for CPort data + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint * @svc_urb: urb for SVC messages coming in on @svc_endpoint - * @cport_in_urb: array of urbs for the CPort in messages - * @cport_in_buffer: array of buffers for the @cport_in_urb urbs + * @cport_in: endpoint, urbs and buffer for cport in messages + * @cport_out: endpoint for for cport out messages * @cport_out_urb: array of urbs for the CPort out messages * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or * not. @@ -77,14 +94,12 @@ struct es1_ap_dev { __u8 control_endpoint; __u8 svc_endpoint; - __u8 cport_in_endpoint; - __u8 cport_out_endpoint; u8 *svc_buffer; struct urb *svc_urb; - struct urb *cport_in_urb[NUM_CPORT_IN_URB]; - u8 *cport_in_buffer[NUM_CPORT_IN_URB]; + struct es1_cport_in cport_in; + struct es1_cport_out cport_out; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; spinlock_t cport_out_urb_lock; @@ -212,7 +227,7 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, put_unaligned_le16(cport_id, message->header->pad); usb_fill_bulk_urb(urb, udev, - usb_sndbulkpipe(udev, es1->cport_out_endpoint), + usb_sndbulkpipe(udev, es1->cport_out.endpoint), buffer, buffer_size, cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); @@ -302,14 +317,14 @@ static void ap_disconnect(struct usb_interface *interface) } for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb = es1->cport_in_urb[i]; + struct urb *urb = es1->cport_in.urb[i]; if (!urb) break; usb_kill_urb(urb); usb_free_urb(urb); - kfree(es1->cport_in_buffer[i]); - es1->cport_in_buffer[i] = NULL; + kfree(es1->cport_in.buffer[i]); + es1->cport_in.buffer[i] = NULL; } usb_kill_urb(es1->svc_urb); @@ -585,10 +600,10 @@ static int ap_probe(struct usb_interface *interface, svc_interval = endpoint->bInterval; int_in_found = true; } else if (usb_endpoint_is_bulk_in(endpoint)) { - es1->cport_in_endpoint = endpoint->bEndpointAddress; + es1->cport_in.endpoint = endpoint->bEndpointAddress; bulk_in_found = true; } else if (usb_endpoint_is_bulk_out(endpoint)) { - es1->cport_out_endpoint = endpoint->bEndpointAddress; + es1->cport_out.endpoint = endpoint->bEndpointAddress; bulk_out_found = true; } else { dev_err(&udev->dev, @@ -630,11 +645,11 @@ static int ap_probe(struct usb_interface *interface, goto error; usb_fill_bulk_urb(urb, udev, - usb_rcvbulkpipe(udev, es1->cport_in_endpoint), + usb_rcvbulkpipe(udev, es1->cport_in.endpoint), buffer, ES1_GBUF_MSG_SIZE_MAX, cport_in_callback, hd); - es1->cport_in_urb[i] = urb; - es1->cport_in_buffer[i] = buffer; + es1->cport_in.urb[i] = urb; + es1->cport_in.buffer[i] = buffer; retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) goto error; -- cgit v1.2.3-59-g8ed1b From 606addd2847ccc56a70fe90d6861081f9a1cf7c1 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 15 Jun 2015 18:08:13 +0200 Subject: greybus: es2.c: Increase the number of bulk endpoints ES2 support 16 endpoints. Update es2.c to allocate endpoints, urbs and buffers for these new endpoints. Currently, they are not yet used and es2.c is working in legacy mode (only original endpoints are used). Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 95 ++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 9b6c3cf69289..a1cf1ac2f0aa 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -34,6 +34,9 @@ static struct dentry *apb1_log_enable_dentry; static struct task_struct *apb1_log_task; static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); +/* Number of bulk in and bulk out couple */ +#define NUM_BULKS 7 + /* * Number of CPort IN urbs in flight at any point in time. * Adjust if we are having stalls in the USB buffer due to not enough urbs in @@ -44,7 +47,7 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* Number of CPort OUT urbs in flight at any point in time. * Adjust if we get messages saying we are out of urbs in the system log. */ -#define NUM_CPORT_OUT_URB 8 +#define NUM_CPORT_OUT_URB (8 * NUM_BULKS) /* vendor request AP message */ #define REQUEST_SVC 0x01 @@ -98,8 +101,8 @@ struct es1_ap_dev { u8 *svc_buffer; struct urb *svc_urb; - struct es1_cport_in cport_in; - struct es1_cport_out cport_out; + struct es1_cport_in cport_in[NUM_BULKS]; + struct es1_cport_out cport_out[NUM_BULKS]; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; spinlock_t cport_out_urb_lock; @@ -201,6 +204,7 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, size_t buffer_size; int retval; struct urb *urb; + int bulk_ep_set = 0; buffer = message->buffer; buffer_size = sizeof(*message->header) + message->payload_size; @@ -227,7 +231,8 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, put_unaligned_le16(cport_id, message->header->pad); usb_fill_bulk_urb(urb, udev, - usb_sndbulkpipe(udev, es1->cport_out.endpoint), + usb_sndbulkpipe(udev, + es1->cport_out[bulk_ep_set].endpoint), buffer, buffer_size, cport_out_callback, message); retval = usb_submit_urb(urb, gfp_mask); @@ -296,6 +301,7 @@ static void ap_disconnect(struct usb_interface *interface) { struct es1_ap_dev *es1; struct usb_device *udev; + int bulk_in; int i; es1 = usb_get_intfdata(interface); @@ -316,15 +322,18 @@ static void ap_disconnect(struct usb_interface *interface) es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb = es1->cport_in.urb[i]; - - if (!urb) - break; - usb_kill_urb(urb); - usb_free_urb(urb); - kfree(es1->cport_in.buffer[i]); - es1->cport_in.buffer[i] = NULL; + for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { + struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb = cport_in->urb[i]; + + if (!urb) + break; + usb_kill_urb(urb); + usb_free_urb(urb); + kfree(cport_in->buffer[i]); + cport_in->buffer[i] = NULL; + } } usb_kill_urb(es1->svc_urb); @@ -563,8 +572,8 @@ static int ap_probe(struct usb_interface *interface, struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; bool int_in_found = false; - bool bulk_in_found = false; - bool bulk_out_found = false; + int bulk_in = 0; + int bulk_out = 0; int retval = -ENOMEM; int i; u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC @@ -600,11 +609,11 @@ static int ap_probe(struct usb_interface *interface, svc_interval = endpoint->bInterval; int_in_found = true; } else if (usb_endpoint_is_bulk_in(endpoint)) { - es1->cport_in.endpoint = endpoint->bEndpointAddress; - bulk_in_found = true; + es1->cport_in[bulk_in++].endpoint = + endpoint->bEndpointAddress; } else if (usb_endpoint_is_bulk_out(endpoint)) { - es1->cport_out.endpoint = endpoint->bEndpointAddress; - bulk_out_found = true; + es1->cport_out[bulk_out++].endpoint = + endpoint->bEndpointAddress; } else { dev_err(&udev->dev, "Unknown endpoint type found, address %x\n", @@ -612,8 +621,8 @@ static int ap_probe(struct usb_interface *interface, } } if ((int_in_found == false) || - (bulk_in_found == false) || - (bulk_out_found == false)) { + (bulk_in == 0) || + (bulk_out == 0)) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } @@ -633,26 +642,30 @@ static int ap_probe(struct usb_interface *interface, hd, svc_interval); /* Allocate buffers for our cport in messages and start them up */ - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb; - u8 *buffer; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - goto error; - buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); - if (!buffer) - goto error; - - usb_fill_bulk_urb(urb, udev, - usb_rcvbulkpipe(udev, es1->cport_in.endpoint), - buffer, ES1_GBUF_MSG_SIZE_MAX, - cport_in_callback, hd); - es1->cport_in.urb[i] = urb; - es1->cport_in.buffer[i] = buffer; - retval = usb_submit_urb(urb, GFP_KERNEL); - if (retval) - goto error; + for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { + struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb; + u8 *buffer; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto error; + buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); + if (!buffer) + goto error; + + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, + cport_in->endpoint), + buffer, ES1_GBUF_MSG_SIZE_MAX, + cport_in_callback, hd); + cport_in->urb[i] = urb; + cport_in->buffer[i] = buffer; + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) + goto error; + } } /* Allocate urbs for our CPort OUT messages */ -- cgit v1.2.3-59-g8ed1b From fc1a536e603f5e096ae82c1a4195af41f0f5ee99 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 15 Jun 2015 18:08:14 +0200 Subject: greybus: es2.c: add a control request for endpoints mapping ES2 give us more endpoints. Use them to map one cport to two endpoints (in and out). Because there is more cports than endpoints, we still need to mux other cports traffic on 2 endpoints. Firmware currently assumes these endpoints are 2 and 3. By default, all cports are muxed. To map one cport to 2 endpoints, use map_cport_to_ep(). Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 78 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index a1cf1ac2f0aa..4e99044c1fbe 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -34,6 +34,9 @@ static struct dentry *apb1_log_enable_dentry; static struct task_struct *apb1_log_task; static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); +/* Number of cport present on USB bridge */ +#define CPORT_MAX 44 + /* Number of bulk in and bulk out couple */ #define NUM_BULKS 7 @@ -55,6 +58,9 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* vendor request APB1 log */ #define REQUEST_LOG 0x02 +/* vendor request to map a cport to bulk in and bulk out endpoints */ +#define REQUEST_EP_MAPPING 0x03 + /* * @endpoint: bulk in endpoint for CPort data * @urb: array of urbs for the CPort in messages @@ -106,6 +112,14 @@ struct es1_ap_dev { struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; spinlock_t cport_out_urb_lock; + + int cport_to_ep[CPORT_MAX]; +}; + +struct cport_to_ep { + __le16 cport_id; + __u8 endpoint_in; + __u8 endpoint_out; }; static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) @@ -117,6 +131,13 @@ static void cport_out_callback(struct urb *urb); static void usb_log_enable(struct es1_ap_dev *es1); static void usb_log_disable(struct es1_ap_dev *es1); +static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) +{ + if (cport_id >= CPORT_MAX) + return 0; + return es1->cport_to_ep[cport_id]; +} + #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) { @@ -139,6 +160,60 @@ static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) return 0; } +static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set) +{ + int i; + + for (i = 0; i < CPORT_MAX; i++) { + if (es1->cport_to_ep[i] == bulk_ep_set) + return 1; + } + return 0; +} + +int map_cport_to_ep(struct es1_ap_dev *es1, + u16 cport_id, int bulk_ep_set) +{ + int retval; + struct cport_to_ep *cport_to_ep; + + if (bulk_ep_set == 0 || bulk_ep_set >= NUM_BULKS) + return -EINVAL; + if (cport_id >= CPORT_MAX) + return -EINVAL; + if (bulk_ep_set && ep_in_use(es1, bulk_ep_set)) + return -EINVAL; + + cport_to_ep = kmalloc(sizeof(*cport_to_ep), GFP_KERNEL); + if (!cport_to_ep) + return -ENOMEM; + + es1->cport_to_ep[cport_id] = bulk_ep_set; + cport_to_ep->cport_id = cpu_to_le16(cport_id); + cport_to_ep->endpoint_in = es1->cport_in[bulk_ep_set].endpoint; + cport_to_ep->endpoint_out = es1->cport_out[bulk_ep_set].endpoint; + + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + REQUEST_EP_MAPPING, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + (char *)cport_to_ep, + sizeof(*cport_to_ep), + ES1_TIMEOUT); + if (retval == sizeof(*cport_to_ep)) + retval = 0; + kfree(cport_to_ep); + + return retval; +} + +int unmap_cport(struct es1_ap_dev *es1, u16 cport_id) +{ + return map_cport_to_ep(es1, cport_id, 0); +} + static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) { struct urb *urb = NULL; @@ -204,7 +279,7 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, size_t buffer_size; int retval; struct urb *urb; - int bulk_ep_set = 0; + int bulk_ep_set; buffer = message->buffer; buffer_size = sizeof(*message->header) + message->payload_size; @@ -230,6 +305,7 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, */ put_unaligned_le16(cport_id, message->header->pad); + bulk_ep_set = cport_to_ep(es1, cport_id); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, es1->cport_out[bulk_ep_set].endpoint), -- cgit v1.2.3-59-g8ed1b From fd7b435f21239a022057b014aff0836403bfa54a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 16 Jun 2015 19:43:05 -0700 Subject: greybus: Revert "greybus: reserve host cport id 0" This reverts commit 698d4bd3e7541a660a3c3665f0af9e787650a239 as Alex says it is broken. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 10 +--------- drivers/staging/greybus/greybus.h | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index bc9c1ebf7983..d4fffecb1abc 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -170,7 +170,6 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver size_t buffer_size_max) { struct greybus_host_device *hd; - int ret; /* * Validate that the driver implements all of the callbacks @@ -201,19 +200,12 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver if (!hd) return ERR_PTR(-ENOMEM); - ida_init(&hd->cport_id_map); - /* Reserve CPort id 0 */ - ret = ida_simple_get(&hd->cport_id_map, 0, 1, GFP_KERNEL); - if (ret < 0) { - kfree(hd); - return ERR_PTR(ret); - } - kref_init(&hd->kref); hd->parent = parent; hd->driver = driver; INIT_LIST_HEAD(&hd->interfaces); INIT_LIST_HEAD(&hd->connections); + ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; return hd; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index d727dea6847e..5c6f9607cf73 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -198,7 +198,7 @@ static inline int is_gb_connection(const struct device *dev) static inline bool cport_id_valid(u16 cport_id) { - return cport_id && cport_id != CPORT_ID_BAD && cport_id <= CPORT_ID_MAX; + return cport_id != CPORT_ID_BAD && cport_id <= CPORT_ID_MAX; } #endif /* __KERNEL__ */ -- cgit v1.2.3-59-g8ed1b From 3ee2266c720520dd2bcf90974f184a22d6e53d06 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 16 Jun 2015 11:28:11 +0100 Subject: greybus: uart: Latch modem control signals for tciomget Latch signals coming from UART module for - GB_UART_CTRL_DCD - GB_UART_CTRL_DSR - GB_UART_CTRL_RI Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 20928702a5b2..3b06cd46694b 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -102,7 +102,7 @@ static int gb_uart_request_recv(u8 type, struct gb_operation *op) break; case GB_UART_TYPE_SERIAL_STATE: serial_state = request->payload; - /* TODO: Parse state change and translate to tty API. */ + gb_tty->ctrlin = le16_to_cpu(serial_state->control); break; default: dev_err(&connection->dev, -- cgit v1.2.3-59-g8ed1b From cdb5781d9fd5f9728a799a26590d2da5794fb1b0 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 18 Jun 2015 16:41:58 +0100 Subject: greybus: uart: Fix sparse warning Greg reported sparse picked up the following warning: /home/gregkh/ara/greybus/uart.c:105:34: warning: cast to restricted __le16 This is due to the control variable in gb_uart_serial_state_request which needs to be declared __le16 not __u16. Signed-off-by: Bryan O'Donoghue Reported-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 642f942f1c38..684ad03bc3f6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -613,7 +613,7 @@ struct gb_uart_set_break_request { #define GB_UART_CTRL_OVERRUN 0x40 struct gb_uart_serial_state_request { - __u16 control; + __le16 control; }; #endif /* __GREYBUS_PROTOCOLS_H */ -- cgit v1.2.3-59-g8ed1b From 656f22e94ec793252c7d1b220da5566042a62040 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Mon, 22 Jun 2015 14:03:51 +0100 Subject: greybus: makefile: add MMC to the required option list Add MMC to the list of options that shall be enable. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 42804547ba24..1715f452a677 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -49,7 +49,7 @@ INSTALL_MOD_PATH ?= /.. PWD := $(shell pwd) # kernel config option that shall be enable -CONFIG_OPTIONS_ENABLE := SYSFS SPI USB SND_SOC +CONFIG_OPTIONS_ENABLE := SYSFS SPI USB SND_SOC MMC # kernel config option that shall be disable CONFIG_OPTIONS_DISABLE := -- cgit v1.2.3-59-g8ed1b From 3b6ecd6de6b4d8aad200d256b0c09d536a15df29 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Mon, 22 Jun 2015 14:03:52 +0100 Subject: greybus: sdio: extend sdio implementation Extend sdio implementation, as it for now it was basically stubs. This implementation is compile tested only since there is no fw or simulation support yet. Next step is to add sdio support to gbsim and test it with success using the mmc_test facility. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 160 +++++++ drivers/staging/greybus/sdio.c | 677 ++++++++++++++++++++++++++-- 2 files changed, 808 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 684ad03bc3f6..c64f6cb10f9b 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -616,5 +616,165 @@ struct gb_uart_serial_state_request { __le16 control; }; +/* SDIO */ +/* Version of the Greybus sdio protocol we support */ +#define GB_SDIO_VERSION_MAJOR 0x00 +#define GB_SDIO_VERSION_MINOR 0x01 + +/* Version of the Greybus SDIO protocol we support */ +#define GB_SDIO_VERSION_MAJOR 0x00 +#define GB_SDIO_VERSION_MINOR 0x01 + +/* Greybus SDIO operation types */ +#define GB_SDIO_TYPE_INVALID 0x00 +#define GB_SDIO_TYPE_PROTOCOL_VERSION 0x01 +#define GB_SDIO_TYPE_GET_CAPABILITIES 0x02 +#define GB_SDIO_TYPE_SET_IOS 0x03 +#define GB_SDIO_TYPE_COMMAND 0x04 +#define GB_SDIO_TYPE_TRANSFER 0x05 +#define GB_SDIO_TYPE_EVENT 0x06 + +/* get caps response: request has no payload */ +struct gb_sdio_get_caps_response { + __le32 caps; +#define GB_SDIO_CAP_NONREMOVABLE 0x00000001 +#define GB_SDIO_CAP_4_BIT_DATA 0x00000002 +#define GB_SDIO_CAP_8_BIT_DATA 0x00000004 +#define GB_SDIO_CAP_MMC_HS 0x00000008 +#define GB_SDIO_CAP_SD_HS 0x00000010 +#define GB_SDIO_CAP_ERASE 0x00000020 +#define GB_SDIO_CAP_1_2V_DDR 0x00000040 +#define GB_SDIO_CAP_1_8V_DDR 0x00000080 +#define GB_SDIO_CAP_POWER_OFF_CARD 0x00000100 +#define GB_SDIO_CAP_UHS_SDR12 0x00000200 +#define GB_SDIO_CAP_UHS_SDR25 0x00000400 +#define GB_SDIO_CAP_UHS_SDR50 0x00000800 +#define GB_SDIO_CAP_UHS_SDR104 0x00001000 +#define GB_SDIO_CAP_UHS_DDR50 0x00002000 +#define GB_SDIO_CAP_DRIVER_TYPE_A 0x00004000 +#define GB_SDIO_CAP_DRIVER_TYPE_C 0x00008000 +#define GB_SDIO_CAP_DRIVER_TYPE_D 0x00010000 +#define GB_SDIO_CAP_HS200_1_2V 0x00020000 +#define GB_SDIO_CAP_HS200_1_8V 0x00040000 +#define GB_SDIO_CAP_HS400_1_2V 0x00080000 +#define GB_SDIO_CAP_HS400_1_8V 0x00100000 + + /* see possible values below at vdd */ + __le32 ocr; + __le16 max_blk_count; + __le16 max_blk_size; +}; + +/* set ios request: response has no payload */ +struct gb_sdio_set_ios_request { + __le32 clock; + __le32 vdd; +#define GB_SDIO_VDD_165_195 0x00000001 +#define GB_SDIO_VDD_20_21 0x00000002 +#define GB_SDIO_VDD_21_22 0x00000004 +#define GB_SDIO_VDD_22_23 0x00000008 +#define GB_SDIO_VDD_23_24 0x00000010 +#define GB_SDIO_VDD_24_25 0x00000020 +#define GB_SDIO_VDD_25_26 0x00000040 +#define GB_SDIO_VDD_26_27 0x00000080 +#define GB_SDIO_VDD_27_28 0x00000100 +#define GB_SDIO_VDD_28_29 0x00000200 +#define GB_SDIO_VDD_29_30 0x00000400 +#define GB_SDIO_VDD_30_31 0x00000800 +#define GB_SDIO_VDD_31_32 0x00001000 +#define GB_SDIO_VDD_32_33 0x00002000 +#define GB_SDIO_VDD_33_34 0x00004000 +#define GB_SDIO_VDD_34_35 0x00008000 +#define GB_SDIO_VDD_35_36 0x00010000 + + __u8 bus_mode; +#define GB_SDIO_BUSMODE_OPENDRAIN 0x00 +#define GB_SDIO_BUSMODE_PUSHPULL 0x01 + + __u8 power_mode; +#define GB_SDIO_POWER_OFF 0x00 +#define GB_SDIO_POWER_UP 0x01 +#define GB_SDIO_POWER_ON 0x02 +#define GB_SDIO_POWER_UNDEFINED 0x03 + + __u8 bus_width; +#define GB_SDIO_BUS_WIDTH_1 0x00 +#define GB_SDIO_BUS_WIDTH_4 0x02 +#define GB_SDIO_BUS_WIDTH_8 0x03 + + __u8 timing; +#define GB_SDIO_TIMING_LEGACY 0x00 +#define GB_SDIO_TIMING_MMC_HS 0x01 +#define GB_SDIO_TIMING_SD_HS 0x02 +#define GB_SDIO_TIMING_UHS_SDR12 0x03 +#define GB_SDIO_TIMING_UHS_SDR25 0x04 +#define GB_SDIO_TIMING_UHS_SDR50 0x05 +#define GB_SDIO_TIMING_UHS_SDR104 0x06 +#define GB_SDIO_TIMING_UHS_DDR50 0x07 +#define GB_SDIO_TIMING_MMC_DDR52 0x08 +#define GB_SDIO_TIMING_MMC_HS200 0x09 +#define GB_SDIO_TIMING_MMC_HS400 0x0A + + __u8 signal_voltage; +#define GB_SDIO_SIGNAL_VOLTAGE_330 0x00 +#define GB_SDIO_SIGNAL_VOLTAGE_180 0x01 +#define GB_SDIO_SIGNAL_VOLTAGE_120 0x02 + + __u8 drv_type; +#define GB_SDIO_SET_DRIVER_TYPE_B 0x00 +#define GB_SDIO_SET_DRIVER_TYPE_A 0x01 +#define GB_SDIO_SET_DRIVER_TYPE_C 0x02 +#define GB_SDIO_SET_DRIVER_TYPE_D 0x03 +}; + +/* command request */ +struct gb_sdio_command_request { + __u8 cmd; + __u8 cmd_flags; +#define GB_SDIO_RSP_NONE 0x00 +#define GB_SDIO_RSP_R1_R5_R6_R7 0x01 +#define GB_SDIO_RSP_R1B 0x02 +#define GB_SDIO_RSP_R2 0x03 +#define GB_SDIO_RSP_R3_R4 0x04 + + __u8 cmd_type; +#define GB_SDIO_CMD_AC 0x00 +#define GB_SDIO_CMD_ADTC 0x01 +#define GB_SDIO_CMD_BCR 0x02 +#define GB_SDIO_CMD_BC 0x03 + + __le32 cmd_arg; +}; + +struct gb_sdio_command_response { + __le32 resp[4]; +}; + +/* transfer request */ +struct gb_sdio_transfer_request { + __u8 data_flags; +#define GB_SDIO_DATA_WRITE 0x01 +#define GB_SDIO_DATA_READ 0x02 +#define GB_SDIO_DATA_STREAM 0x04 + + __le16 data_blocks; + __le16 data_blksz; + __u8 data[0]; +}; + +struct gb_sdio_transfer_response { + __le16 data_blocks; + __le16 data_blksz; + __u8 data[0]; +}; + +/* event request: generated by module and is defined as unidirectional */ +struct gb_sdio_event_request { + __u8 event; +#define GB_SDIO_CARD_INSERTED 0x01 +#define GB_SDIO_CARD_REMOVED 0x02 +#define GB_SDIO_WP 0x04 +}; + #endif /* __GREYBUS_PROTOCOLS_H */ diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 9a0348c62478..1fd17c89b100 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -1,51 +1,626 @@ /* * SD/MMC Greybus driver. * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ #include -#include +#include #include +#include +#include +#include #include "greybus.h" struct gb_sdio_host { - struct gb_connection *connection; - struct mmc_host *mmc; - struct mmc_request *mrq; - // FIXME - some lock? + struct gb_connection *connection; + u8 version_major; + u8 version_minor; + struct mmc_host *mmc; + struct mmc_request *mrq; + struct mutex lock; /* lock for this host */ + size_t data_max; + void *xfer_buffer; + spinlock_t xfer; /* lock to cancel ongoing transfer */ + bool xfer_stop; + struct work_struct mrqwork; + bool removed; + bool card_present; + bool read_only; }; -static void gb_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) +static struct workqueue_struct *gb_sdio_mrq_workqueue; + +/* Define get_version() routine */ +define_get_version(gb_sdio_host, SDIO); + +static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) { - // FIXME - do something here... + u32 caps = 0; + u32 caps2 = 0; + + caps = (r & GB_SDIO_CAP_NONREMOVABLE ? MMC_CAP_NONREMOVABLE : 0) | + (r & GB_SDIO_CAP_4_BIT_DATA ? MMC_CAP_4_BIT_DATA : 0) | + (r & GB_SDIO_CAP_8_BIT_DATA ? MMC_CAP_8_BIT_DATA : 0) | + (r & GB_SDIO_CAP_MMC_HS ? MMC_CAP_MMC_HIGHSPEED : 0) | + (r & GB_SDIO_CAP_SD_HS ? MMC_CAP_SD_HIGHSPEED : 0) | + (r & GB_SDIO_CAP_ERASE ? MMC_CAP_ERASE : 0) | + (r & GB_SDIO_CAP_1_2V_DDR ? MMC_CAP_1_2V_DDR : 0) | + (r & GB_SDIO_CAP_1_8V_DDR ? MMC_CAP_1_8V_DDR : 0) | + (r & GB_SDIO_CAP_POWER_OFF_CARD ? MMC_CAP_POWER_OFF_CARD : 0) | + (r & GB_SDIO_CAP_UHS_SDR12 ? MMC_CAP_UHS_SDR12 : 0) | + (r & GB_SDIO_CAP_UHS_SDR25 ? MMC_CAP_UHS_SDR25 : 0) | + (r & GB_SDIO_CAP_UHS_SDR50 ? MMC_CAP_UHS_SDR50 : 0) | + (r & GB_SDIO_CAP_UHS_SDR104 ? MMC_CAP_UHS_SDR104 : 0) | + (r & GB_SDIO_CAP_UHS_DDR50 ? MMC_CAP_UHS_DDR50 : 0) | + (r & GB_SDIO_CAP_DRIVER_TYPE_A ? MMC_CAP_DRIVER_TYPE_A : 0) | + (r & GB_SDIO_CAP_DRIVER_TYPE_C ? MMC_CAP_DRIVER_TYPE_C : 0) | + (r & GB_SDIO_CAP_DRIVER_TYPE_D ? MMC_CAP_DRIVER_TYPE_D : 0); + + caps2 = (r & GB_SDIO_CAP_HS200_1_2V ? MMC_CAP2_HS200_1_2V_SDR : 0) | + (r & GB_SDIO_CAP_HS200_1_8V ? MMC_CAP2_HS200_1_8V_SDR : 0) | + (r & GB_SDIO_CAP_HS400_1_2V ? MMC_CAP2_HS400_1_2V : 0) | + (r & GB_SDIO_CAP_HS400_1_8V ? MMC_CAP2_HS400_1_8V : 0); + + host->mmc->caps = caps; + host->mmc->caps2 = caps2; + + if (caps & MMC_CAP_NONREMOVABLE) + host->card_present = true; } -static void gb_sd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +static int gb_sdio_get_caps(struct gb_sdio_host *host) { - // FIXME - do something here... + struct gb_sdio_get_caps_response response; + struct mmc_host *mmc = host->mmc; + u16 data_max; + u32 blksz; + u32 r; + int ret; + + ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_GET_CAPABILITIES, + NULL, 0, &response, sizeof(response)); + if (ret < 0) + return ret; + r = le32_to_cpu(response.caps); + + _gb_sdio_set_host_caps(host, r); + + /* get the max block size that could fit our payload */ + data_max = gb_operation_get_payload_size_max(host->connection); + data_max = min(data_max - sizeof(struct gb_sdio_transfer_request), + data_max - sizeof(struct gb_sdio_transfer_response)); + + blksz = min(le16_to_cpu(response.max_blk_size), data_max); + blksz = max_t(u32, 512, blksz); + + mmc->max_blk_size = rounddown_pow_of_two(blksz); + mmc->max_blk_count = le16_to_cpu(response.max_blk_count); + host->data_max = data_max; + + /* get ocr supported values */ + mmc->ocr_avail = le32_to_cpu(response.ocr); + mmc->ocr_avail_sdio = mmc->ocr_avail; + mmc->ocr_avail_sd = mmc->ocr_avail; + mmc->ocr_avail_mmc = mmc->ocr_avail; + + return 0; } -static int gb_sd_get_ro(struct mmc_host *mmc) +static int gb_sdio_event_recv(u8 type, struct gb_operation *op) { - // FIXME - do something here... + struct gb_connection *connection = op->connection; + struct gb_sdio_host *host = connection->private; + struct gb_message *request; + struct gb_sdio_event_request *payload; + u8 state_changed = 0; + u8 event; + + if (type != GB_SDIO_TYPE_EVENT) { + dev_err(&connection->dev, + "unsupported unsolicited event: %u\n", type); + return -EINVAL; + } + + request = op->request; + + if (request->payload_size != sizeof(*payload)) { + dev_err(mmc_dev(host->mmc), "wrong event size received\n"); + return -EINVAL; + } + + payload = request->payload; + event = payload->event; + + switch (event) { + case GB_SDIO_CARD_INSERTED: + if (!mmc_card_is_removable(host->mmc)) + return 0; + if (host->card_present) + return 0; + host->card_present = true; + state_changed = 1; + break; + case GB_SDIO_CARD_REMOVED: + if (!mmc_card_is_removable(host->mmc)) + return 0; + if (!(host->card_present)) + return 0; + host->card_present = false; + state_changed = 1; + break; + case GB_SDIO_WP: + host->read_only = true; + break; + default: + dev_err(mmc_dev(host->mmc), "wrong event received %d\n", event); + return -EINVAL; + } + + if (state_changed) { + dev_info(mmc_dev(host->mmc), "card %s now event\n", + (host->card_present ? "inserted" : "removed")); + mmc_detect_change(host->mmc, 0); + } + return 0; } -static const struct mmc_host_ops gb_sd_ops = { - .request = gb_sd_request, - .set_ios = gb_sd_set_ios, - .get_ro = gb_sd_get_ro, +static int gb_sdio_set_ios(struct gb_sdio_host *host, + struct gb_sdio_set_ios_request *request) +{ + return gb_operation_sync(host->connection, GB_SDIO_TYPE_SET_IOS, + request, sizeof(*request), NULL, 0); +} + +static int _gb_sdio_send(struct gb_sdio_host *host, struct mmc_data *data, + size_t len, u16 nblocks, off_t skip) +{ + struct gb_sdio_transfer_request *request; + struct gb_sdio_transfer_response response; + struct scatterlist *sg = data->sg; + unsigned int sg_len = data->sg_len; + size_t copied; + u16 send_blksz; + u16 send_blocks; + int ret; + + WARN_ON(len > host->data_max); + + request = host->xfer_buffer; + request->data_flags = (data->flags >> 8); + request->data_blocks = cpu_to_le16(nblocks); + request->data_blksz = cpu_to_le16(data->blksz); + + copied = sg_pcopy_to_buffer(sg, sg_len, &request->data[0] + skip, len, + skip); + + if (copied != len) + return -EINVAL; + + ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_TRANSFER, + request, len, &response, sizeof(response)); + if (ret < 0) + return ret; + + send_blocks = le16_to_cpu(response.data_blocks); + send_blksz = le16_to_cpu(response.data_blksz); + + if (len != send_blksz * send_blocks) + return -EINVAL; + + return ret; +} + +static int _gb_sdio_recv(struct gb_sdio_host *host, struct mmc_data *data, + size_t len, u16 nblocks, off_t skip) +{ + struct gb_sdio_transfer_request request; + struct gb_sdio_transfer_response *response; + struct scatterlist *sg = data->sg; + unsigned int sg_len = data->sg_len; + size_t copied; + u16 recv_blksz; + u16 recv_blocks; + int ret; + + WARN_ON(len > host->data_max); + + request.data_flags = (data->flags >> 8); + request.data_blocks = cpu_to_le16(nblocks); + request.data_blksz = cpu_to_le16(data->blksz); + + response = host->xfer_buffer; + + ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_TRANSFER, + &request, sizeof(request), response, len); + if (ret < 0) + return ret; + + recv_blocks = le16_to_cpu(response->data_blocks); + recv_blksz = le16_to_cpu(response->data_blksz); + + if (len != recv_blksz * recv_blocks) + return -EINVAL; + + copied = sg_pcopy_from_buffer(sg, sg_len, &response->data[0] + skip, + len, skip); + if (copied != len) + return -EINVAL; + + return 0; +} + +static int gb_sdio_transfer(struct gb_sdio_host *host, struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + size_t left, len; + off_t skip = 0; + int ret = 0; + u16 nblocks; + + left = data->blksz * data->blocks; + + while (left) { + /* check is a stop transmission is pending */ + spin_lock(&host->xfer); + if (host->xfer_stop) { + host->xfer_stop = false; + spin_unlock(&host->xfer); + ret = -EINTR; + goto out; + } + spin_unlock(&host->xfer); + len = min(left, host->data_max); + nblocks = do_div(len, data->blksz); + len = nblocks * data->blksz; + + if (data->flags & MMC_DATA_READ) { + ret = _gb_sdio_recv(host, data, len, nblocks, skip); + if (ret < 0) + goto out; + } else { + ret = _gb_sdio_send(host, data, len, nblocks, skip); + if (ret < 0) + goto out; + } + data->bytes_xfered += len; + left -= len; + skip += len; + } + +out: + data->error = ret; + return ret; +} + +static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd) +{ + struct gb_sdio_command_request request; + struct gb_sdio_command_response response; + u8 cmd_flags; + u8 cmd_type; + int i; + int ret = 0; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + cmd_flags = GB_SDIO_RSP_NONE; + break; + case MMC_RSP_R1: + cmd_flags = GB_SDIO_RSP_R1_R5_R6_R7; + break; + case MMC_RSP_R1B: + cmd_flags = GB_SDIO_RSP_R1B; + break; + case MMC_RSP_R2: + cmd_flags = GB_SDIO_RSP_R2; + break; + case MMC_RSP_R3: + cmd_flags = GB_SDIO_RSP_R3_R4; + default: + dev_err(mmc_dev(host->mmc), "cmd flag invalid %04x\n", + mmc_resp_type(cmd)); + ret = -EINVAL; + goto out; + } + + switch (mmc_cmd_type(cmd)) { + case MMC_CMD_BC: + cmd_type = GB_SDIO_CMD_BC; + break; + case MMC_CMD_BCR: + cmd_type = GB_SDIO_CMD_BCR; + break; + case MMC_CMD_AC: + cmd_type = GB_SDIO_CMD_AC; + break; + case MMC_CMD_ADTC: + cmd_type = GB_SDIO_CMD_ADTC; + break; + default: + dev_err(mmc_dev(host->mmc), "cmd type invalid %04x\n", + mmc_cmd_type(cmd)); + ret = -EINVAL; + goto out; + } + + request.cmd = cmd->opcode; + request.cmd_flags = cmd_flags; + request.cmd_type = cmd_type; + request.cmd_arg = cpu_to_le32(cmd->arg); + + ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_COMMAND, + &request, sizeof(request), &response, + sizeof(response)); + if (ret < 0) + goto out; + + /* no response expected */ + if (cmd_flags & GB_SDIO_RSP_NONE) + goto out; + + /* long response expected */ + if (cmd_flags & GB_SDIO_RSP_R2) + for (i = 0; i < 4; i++) + cmd->resp[i] = le32_to_cpu(response.resp[i]); + else + cmd->resp[0] = le32_to_cpu(response.resp[0]); + +out: + cmd->error = ret; + return ret; +} + +static void gb_sdio_mrq_work(struct work_struct *work) +{ + struct gb_sdio_host *host; + struct mmc_request *mrq; + int ret; + + host = container_of(work, struct gb_sdio_host, mrqwork); + + mutex_lock(&host->lock); + if (host->removed) { + mrq->cmd->error = -ESHUTDOWN; + goto done; + } + + mrq = host->mrq; + + if (mrq->sbc) { + ret = gb_sdio_command(host, mrq->sbc); + if (ret < 0) + goto done; + } + + ret = gb_sdio_command(host, mrq->cmd); + if (ret < 0) + goto done; + + if (mrq->data) { + ret = gb_sdio_transfer(host, host->mrq); + if (ret < 0) + goto done; + } + + if (mrq->data->stop) { + ret = gb_sdio_command(host, mrq->data->stop); + if (ret < 0) + goto done; + } + +done: + mrq = NULL; + mutex_unlock(&host->lock); + mmc_request_done(host->mmc, mrq); +} + +static void gb_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct gb_sdio_host *host = mmc_priv(mmc); + struct mmc_command *cmd = mrq->cmd; + + /* Check if it is a cancel to ongoing transfer */ + if (cmd->opcode == MMC_STOP_TRANSMISSION) { + spin_lock(&host->xfer); + host->xfer_stop = true; + spin_unlock(&host->xfer); + } + + mutex_lock(&host->lock); + + WARN_ON(host->mrq); + host->mrq = mrq; + + if (host->removed) { + mrq->cmd->error = -ESHUTDOWN; + goto out; + } + if (!host->card_present) { + mrq->cmd->error = -ENOMEDIUM; + goto out; + } + + queue_work(gb_sdio_mrq_workqueue, &host->mrqwork); + + mutex_unlock(&host->lock); + return; + +out: + host->mrq = NULL; + mutex_unlock(&host->lock); + mmc_request_done(mmc, mrq); +} + +static void gb_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct gb_sdio_host *host = mmc_priv(mmc); + struct gb_sdio_set_ios_request request; + int ret; + u8 power_mode; + u8 bus_width; + u8 timing; + u8 signal_voltage; + u8 drv_type; + + mutex_lock(&host->lock); + request.clock = cpu_to_le32(ios->clock); + request.vdd = cpu_to_le32(1 << ios->vdd); + + request.bus_mode = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN ? + GB_SDIO_BUSMODE_OPENDRAIN : + GB_SDIO_BUSMODE_PUSHPULL); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + power_mode = GB_SDIO_POWER_OFF; + break; + case MMC_POWER_UP: + power_mode = GB_SDIO_POWER_UP; + break; + case MMC_POWER_ON: + power_mode = GB_SDIO_POWER_ON; + break; + case MMC_POWER_UNDEFINED: + default: + power_mode = GB_SDIO_POWER_UNDEFINED; + break; + } + request.power_mode = power_mode; + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_1: + bus_width = GB_SDIO_BUS_WIDTH_1; + break; + case MMC_BUS_WIDTH_4: + default: + bus_width = GB_SDIO_BUS_WIDTH_4; + break; + case MMC_BUS_WIDTH_8: + bus_width = GB_SDIO_BUS_WIDTH_8; + break; + } + request.bus_width = bus_width; + + switch (ios->timing) { + case MMC_TIMING_LEGACY: + default: + timing = GB_SDIO_TIMING_LEGACY; + break; + case MMC_TIMING_MMC_HS: + timing = GB_SDIO_TIMING_MMC_HS; + break; + case MMC_TIMING_SD_HS: + timing = GB_SDIO_TIMING_SD_HS; + break; + case MMC_TIMING_UHS_SDR12: + timing = GB_SDIO_TIMING_UHS_SDR12; + break; + case MMC_TIMING_UHS_SDR25: + timing = GB_SDIO_TIMING_UHS_SDR25; + break; + case MMC_TIMING_UHS_SDR50: + timing = GB_SDIO_TIMING_UHS_SDR50; + break; + case MMC_TIMING_UHS_SDR104: + timing = GB_SDIO_TIMING_UHS_SDR104; + break; + case MMC_TIMING_UHS_DDR50: + timing = GB_SDIO_TIMING_UHS_DDR50; + break; + case MMC_TIMING_MMC_DDR52: + timing = GB_SDIO_TIMING_MMC_DDR52; + break; + case MMC_TIMING_MMC_HS200: + timing = GB_SDIO_TIMING_MMC_HS200; + break; + case MMC_TIMING_MMC_HS400: + timing = GB_SDIO_TIMING_MMC_HS400; + break; + } + request.timing = timing; + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + signal_voltage = GB_SDIO_SIGNAL_VOLTAGE_330; + break; + case MMC_SIGNAL_VOLTAGE_180: + default: + signal_voltage = GB_SDIO_SIGNAL_VOLTAGE_180; + break; + case MMC_SIGNAL_VOLTAGE_120: + signal_voltage = GB_SDIO_SIGNAL_VOLTAGE_120; + break; + } + request.signal_voltage = signal_voltage; + + switch (ios->drv_type) { + case MMC_SET_DRIVER_TYPE_A: + drv_type = GB_SDIO_SET_DRIVER_TYPE_A; + break; + case MMC_SET_DRIVER_TYPE_C: + drv_type = GB_SDIO_SET_DRIVER_TYPE_C; + break; + case MMC_SET_DRIVER_TYPE_D: + drv_type = GB_SDIO_SET_DRIVER_TYPE_D; + break; + case MMC_SET_DRIVER_TYPE_B: + default: + drv_type = GB_SDIO_SET_DRIVER_TYPE_B; + break; + } + request.drv_type = drv_type; + + ret = gb_sdio_set_ios(host, &request); + if (ret < 0) + goto out; + + memcpy(&mmc->ios, ios, sizeof(mmc->ios)); + +out: + mutex_unlock(&host->lock); +} + +static int gb_mmc_get_ro(struct mmc_host *mmc) +{ + struct gb_sdio_host *host = mmc_priv(mmc); + + mutex_lock(&host->lock); + if (host->removed) + return -ESHUTDOWN; + mutex_unlock(&host->lock); + return host->card_present; +} + +static int gb_mmc_get_cd(struct mmc_host *mmc) +{ + struct gb_sdio_host *host = mmc_priv(mmc); + + mutex_lock(&host->lock); + if (host->removed) + return -ESHUTDOWN; + mutex_unlock(&host->lock); + return host->read_only; +} + +static const struct mmc_host_ops gb_sdio_ops = { + .request = gb_mmc_request, + .set_ios = gb_mmc_set_ios, + .get_ro = gb_mmc_get_ro, + .get_cd = gb_mmc_get_cd, }; static int gb_sdio_connection_init(struct gb_connection *connection) { struct mmc_host *mmc; struct gb_sdio_host *host; + size_t max_buffer; + int ret = 0; mmc = mmc_alloc_host(sizeof(*host), &connection->dev); if (!mmc) @@ -54,38 +629,82 @@ static int gb_sdio_connection_init(struct gb_connection *connection) host = mmc_priv(mmc); host->mmc = mmc; - mmc->ops = &gb_sd_ops; - // FIXME - set up size limits we can handle. - // FIXME - register the host controller. - host->connection = connection; connection->private = host; - return 0; + + ret = get_version(host); + if (ret < 0) + goto free_mmc; + + ret = gb_sdio_get_caps(host); + if (ret < 0) + goto free_mmc; + + mmc->ops = &gb_sdio_ops; + + /* for now we just make a map 1:1 between max blocks and segments */ + mmc->max_segs = host->mmc->max_blk_count; + mmc->max_seg_size = host->mmc->max_blk_size; + + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + + max_buffer = gb_operation_get_payload_size_max(host->connection); + host->xfer_buffer = kzalloc(max_buffer, GFP_KERNEL); + if (!host->xfer_buffer) { + ret = -ENOMEM; + goto free_mmc; + } + mutex_init(&host->lock); + spin_lock_init(&host->xfer); + gb_sdio_mrq_workqueue = alloc_workqueue("gb_sdio_mrq", 0, 1); + INIT_WORK(&host->mrqwork, gb_sdio_mrq_work); + + ret = mmc_add_host(mmc); + if (ret < 0) + goto free_work; + + return ret; + +free_work: + destroy_workqueue(gb_sdio_mrq_workqueue); + kfree(host->xfer_buffer); + +free_mmc: + connection->private = NULL; + mmc_free_host(mmc); + + return ret; } static void gb_sdio_connection_exit(struct gb_connection *connection) { struct mmc_host *mmc; - struct gb_sdio_host *host; + struct gb_sdio_host *host = connection->private; - host = connection->private; if (!host) return; + mutex_lock(&host->lock); + host->removed = true; mmc = host->mmc; - mmc_remove_host(mmc); - mmc_free_host(mmc); connection->private = NULL; + mutex_unlock(&host->lock); + + flush_workqueue(gb_sdio_mrq_workqueue); + destroy_workqueue(gb_sdio_mrq_workqueue); + mmc_free_host(mmc); + mmc_remove_host(mmc); + kfree(host->xfer_buffer); } static struct gb_protocol sdio_protocol = { .name = "sdio", .id = GREYBUS_PROTOCOL_SDIO, - .major = 0, - .minor = 1, + .major = GB_SDIO_VERSION_MAJOR, + .minor = GB_SDIO_VERSION_MINOR, .connection_init = gb_sdio_connection_init, .connection_exit = gb_sdio_connection_exit, - .request_recv = NULL, /* no incoming requests */ + .request_recv = gb_sdio_event_recv, }; gb_gpbridge_protocol_driver(sdio_protocol); -- cgit v1.2.3-59-g8ed1b From b08488bea5929f14b1e08d701652427bf63a9b47 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 22 Jun 2015 16:36:47 +0530 Subject: greybus: manifest: Allow bundles/connections list to be non-empty at manifest parsing A connection and a bundle will be created for interfaces at the very beginning for control protocol's functioning. And so the list of bundles and connections for a interface will be non-empty by the time manifest is parsed. Currently we are firing a WARN when these lists are found to be non-empty. Lets fix that to contain single bundle and connection for control protocol. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index ad55a73b737b..377c449d5785 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -208,9 +208,6 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) u8 bundle_id = bundle->id; u32 count = 0; - if (WARN_ON(!list_empty(&bundle->connections))) - return 0; - /* Set up all cport descriptors associated with this bundle */ list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { struct greybus_descriptor_cport *desc_cport; @@ -263,9 +260,6 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) struct gb_bundle *bundle_next; u32 count = 0; - if (WARN_ON(!list_empty(&intf->bundles))) - return 0; - list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { struct greybus_descriptor_bundle *desc_bundle; -- cgit v1.2.3-59-g8ed1b From cdee4f7505dbb06671a41f63b295d3d7680d760c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 22 Jun 2015 16:42:26 +0530 Subject: greybus: Add control protocol support Add control protocol driver that is responsible for handling operations on control CPort. The AP also needs to support incoming requests on its control port. Features not implemented yet are marked as TODO for now. NOTE: This also fixes cport-bundle-id to 0 and cport-id to 2 for control protocol. Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/control.c | 150 ++++++++++++++++++++++++++++ drivers/staging/greybus/control.h | 27 +++++ drivers/staging/greybus/core.c | 9 ++ drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/greybus_protocols.h | 39 ++++++++ drivers/staging/greybus/interface.h | 1 + 7 files changed, 228 insertions(+) create mode 100644 drivers/staging/greybus/control.c create mode 100644 drivers/staging/greybus/control.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1715f452a677..4f66ff3a9713 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -8,6 +8,7 @@ greybus-y := core.o \ bundle.o \ connection.o \ protocol.o \ + control.o \ operation.o gb-phy-y := gpbridge.o \ diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c new file mode 100644 index 000000000000..c19814d311ec --- /dev/null +++ b/drivers/staging/greybus/control.c @@ -0,0 +1,150 @@ +/* + * Greybus CPort control protocol. + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include "greybus.h" + +/* Define get_version() routine */ +define_get_version(gb_control, CONTROL); + +/* Get Manifest's size from the interface */ +int gb_control_get_manifest_size_operation(struct gb_interface *intf) +{ + struct gb_control_get_manifest_size_response response; + struct gb_connection *connection = intf->control->connection; + int ret; + + ret = gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST_SIZE, + NULL, 0, &response, sizeof(response)); + if (ret) { + dev_err(&connection->dev, + "%s: Manifest size get operation failed (%d)\n", + __func__, ret); + return ret; + } + + return le16_to_cpu(response.size); +} + +/* Reads Manifest from the interface */ +int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, + size_t size) +{ + struct gb_connection *connection = intf->control->connection; + + return gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST, + NULL, 0, manifest, size); +} + +int gb_control_connected_operation(struct gb_control *control, u16 cport_id) +{ + struct gb_control_connected_request request; + + request.cport_id = cpu_to_le16(cport_id); + return gb_operation_sync(control->connection, GB_CONTROL_TYPE_CONNECTED, + &request, sizeof(request), NULL, 0); +} + +int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id) +{ + struct gb_control_disconnected_request request; + + request.cport_id = cpu_to_le16(cport_id); + return gb_operation_sync(control->connection, + GB_CONTROL_TYPE_DISCONNECTED, &request, + sizeof(request), NULL, 0); +} + +static int gb_control_request_recv(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_protocol_version_response *version; + + switch (type) { + case GB_CONTROL_TYPE_PROBE_AP: + // TODO + // Send authenticated block of data, confirming this module is + // an AP. + break; + case GB_CONTROL_TYPE_PROTOCOL_VERSION: + if (!gb_operation_response_alloc(op, sizeof(*version))) { + dev_err(&connection->dev, + "%s: error allocating response\n", __func__); + return -ENOMEM; + } + + version = op->response->payload; + version->major = GB_CONTROL_VERSION_MAJOR; + version->minor = GB_CONTROL_VERSION_MINOR; + break; + case GB_CONTROL_TYPE_CONNECTED: + case GB_CONTROL_TYPE_DISCONNECTED: + break; + default: + WARN_ON(1); + break; + } + + return 0; +} + +static int gb_control_connection_init(struct gb_connection *connection) +{ + struct gb_control *control; + int ret; + + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!control) + return -ENOMEM; + + control->connection = connection; + connection->private = control; + + ret = get_version(control); + if (ret) + kfree(control); + + /* Set interface's control connection */ + connection->bundle->intf->control = control; + + return ret; +} + +static void gb_control_connection_exit(struct gb_connection *connection) +{ + struct gb_control *control = connection->private; + + if (WARN_ON(connection->bundle->intf->control != control)) + return; + + connection->bundle->intf->control = NULL; + kfree(control); +} + +static struct gb_protocol control_protocol = { + .name = "control", + .id = GREYBUS_PROTOCOL_CONTROL, + .major = 0, + .minor = 1, + .connection_init = gb_control_connection_init, + .connection_exit = gb_control_connection_exit, + .request_recv = gb_control_request_recv, +}; + +int gb_control_protocol_init(void) +{ + return gb_protocol_register(&control_protocol); +} + +void gb_control_protocol_exit(void) +{ + gb_protocol_deregister(&control_protocol); +} diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h new file mode 100644 index 000000000000..6e41a2b4c70d --- /dev/null +++ b/drivers/staging/greybus/control.h @@ -0,0 +1,27 @@ +/* + * Greybus CPort control protocol + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __CONTROL_H +#define __CONTROL_H + +struct gb_control { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; +}; + +int gb_control_connected_operation(struct gb_control *control, u16 cport_id); +int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id); +int gb_control_get_manifest_size_operation(struct gb_interface *intf); +int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, + size_t size); + +int gb_control_protocol_init(void); +void gb_control_protocol_exit(void); +#endif /* __CONTROL_H */ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index d4fffecb1abc..8d16e10b7e94 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -274,8 +274,16 @@ static int __init gb_init(void) goto error_endo; } + retval = gb_control_protocol_init(); + if (retval) { + pr_err("gb_control_protocol_init failed\n"); + goto error_control; + } + return 0; /* Success */ +error_control: + gb_endo_exit(); error_endo: gb_operation_exit(); error_operation: @@ -291,6 +299,7 @@ module_init(gb_init); static void __exit gb_exit(void) { + gb_control_protocol_exit(); gb_endo_exit(); gb_operation_exit(); gb_ap_exit(); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 5c6f9607cf73..6874939e6d1e 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -28,6 +28,7 @@ #include "endo.h" #include "svc.h" #include "module.h" +#include "control.h" #include "interface.h" #include "bundle.h" #include "connection.h" diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c64f6cb10f9b..9f9b722de7df 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -52,6 +52,45 @@ #ifndef __GREYBUS_PROTOCOLS_H #define __GREYBUS_PROTOCOLS_H +/* Control Protocol */ + +/* Bundle-id and cport-id for control cport */ +#define GB_CONTROL_BUNDLE_ID 0 +#define GB_CONTROL_CPORT_ID 2 + +/* Version of the Greybus control protocol we support */ +#define GB_CONTROL_VERSION_MAJOR 0x00 +#define GB_CONTROL_VERSION_MINOR 0x01 + +/* Greybus control request types */ +#define GB_CONTROL_TYPE_INVALID 0x00 +#define GB_CONTROL_TYPE_PROTOCOL_VERSION 0x01 +#define GB_CONTROL_TYPE_PROBE_AP 0x02 +#define GB_CONTROL_TYPE_GET_MANIFEST_SIZE 0x03 +#define GB_CONTROL_TYPE_GET_MANIFEST 0x04 +#define GB_CONTROL_TYPE_CONNECTED 0x05 +#define GB_CONTROL_TYPE_DISCONNECTED 0x06 + +/* Control protocol manifest get size request has no payload*/ +struct gb_control_get_manifest_size_response { + __le16 size; +}; + +/* Control protocol manifest get request has no payload */ +struct gb_control_get_manifest_response { + __u8 data[0]; +}; + +/* Control protocol [dis]connected request */ +struct gb_control_connected_request { + __le16 cport_id; +}; + +struct gb_control_disconnected_request { + __le16 cport_id; +}; +/* Control protocol [dis]connected response has no payload */ + /* I2C */ /* Version of the Greybus i2c protocol we support */ diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 88a7a8056806..9c566b237aba 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -13,6 +13,7 @@ /* Greybus "public" definitions" */ struct gb_interface { struct device dev; + struct gb_control *control; struct list_head bundles; struct list_head links; /* greybus_host_device->interfaces */ -- cgit v1.2.3-59-g8ed1b From 6c68da264b080f749e8848b0227e6bb7d7c72f21 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 22 Jun 2015 16:42:27 +0530 Subject: greybus: interface: Get manifest using Control protocol Control protocol is ready to be used for fetching manifest. Lets do it. This changes few things: - Creates/initializes bundle/connection for control protocol initially and skips doing the same later. - Manifest is parsed at link-up now, instead of hotplug which was the case earlier. This is because we need device_id (provided during link-up) for registering bundle. - Manifest is fetched using control protocol. So the sequence of events is: Event Previously Now ----- ---------- --- Interface Hotplug create intf create intf parse mfst Interface Link Up init bundles create control conn get mfst size get mfst parse mfst init bundles Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 7 ++- drivers/staging/greybus/bundle.c | 4 ++ drivers/staging/greybus/interface.c | 100 ++++++++++++++++++++++++++++-------- drivers/staging/greybus/interface.h | 5 +- drivers/staging/greybus/manifest.c | 14 +++++ 5 files changed, 104 insertions(+), 26 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 07375cc96bfb..113fd878c634 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -146,10 +146,10 @@ static void svc_management(struct svc_function_unipro_management *management, management->link_up.interface_id); return; } - ret = gb_bundles_init(intf, management->link_up.device_id); + ret = gb_interface_init(intf, management->link_up.device_id); if (ret) { dev_err(hd->parent, - "error %d initializing bundles for interface %hhu\n", + "error %d initializing interface %hhu\n", ret, management->link_up.interface_id); return; } @@ -175,8 +175,7 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, return; } dev_dbg(hd->parent, "interface id %d added\n", interface_id); - gb_interface_add(hd, interface_id, hotplug->data, - payload_length - 0x02); + gb_interface_create(hd, interface_id); break; case SVC_HOTUNPLUG_EVENT: diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 8d0e86fc2cfc..a5172e5f64b2 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -230,6 +230,10 @@ int gb_bundle_init(struct gb_bundle *bundle, u8 device_id) struct gb_interface *intf = bundle->intf; int ret; + /* Don't reinitialize control cport's bundle */ + if (intf->control && bundle->id == GB_CONTROL_BUNDLE_ID) + return 0; + bundle->device_id = device_id; ret = svc_set_route_send(bundle, intf->hd); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 5b1621ccf724..901c4acc2421 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -66,6 +66,36 @@ struct device_type greybus_interface_type = { .release = gb_interface_release, }; +/* + * Create kernel structures corresponding to a bundle and connection for + * managing control CPort. Also initialize the bundle, which will request SVC to + * set route and will initialize the control protocol for this connection. + */ +static int gb_create_control_connection(struct gb_interface *intf, u8 device_id) +{ + struct gb_bundle *bundle; + int ret; + + bundle = gb_bundle_create(intf, GB_CONTROL_BUNDLE_ID, + GREYBUS_CLASS_CONTROL); + if (!bundle) + return -EINVAL; + + if (!gb_connection_create(bundle, GB_CONTROL_CPORT_ID, + GREYBUS_PROTOCOL_CONTROL)) + return -EINVAL; + + ret = gb_bundle_init(bundle, device_id); + if (ret) { + dev_err(&intf->dev, + "error %d initializing bundles for interface %hu\n", + ret, intf->interface_id); + return ret; + } + + return 0; +} + /* * A Greybus module represents a user-replicable component on an Ara * phone. An interface is the physical connection on that module. A @@ -78,8 +108,8 @@ struct device_type greybus_interface_type = { * Returns a pointer to the new interfce or a null pointer if a * failure occurs due to memory exhaustion. */ -static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, - u8 interface_id) +struct gb_interface *gb_interface_create(struct greybus_host_device *hd, + u8 interface_id) { struct gb_module *module; struct gb_interface *intf; @@ -165,29 +195,60 @@ static void gb_interface_destroy(struct gb_interface *intf) /** * gb_interface_add * - * Pass in a buffer that _should_ contain a Greybus manifest - * and register a greybus device structure with the kernel core. + * Create connection for control CPort and then request/parse manifest. + * Finally initialize all the bundles to set routes via SVC and initialize all + * connections. */ -void gb_interface_add(struct greybus_host_device *hd, u8 interface_id, u8 *data, - int size) +int gb_interface_init(struct gb_interface *intf, u8 device_id) { - struct gb_interface *intf; + int ret, size; + void *manifest; + + /* Establish control CPort connection */ + ret = gb_create_control_connection(intf, device_id); + if (ret) { + dev_err(&intf->dev, "Failed to create control CPort connection (%d)\n", ret); + return ret; + } - intf = gb_interface_create(hd, interface_id); - if (!intf) { - dev_err(hd->parent, "failed to create interface\n"); - return; + /* Get manifest size using control protocol on CPort */ + size = gb_control_get_manifest_size_operation(intf); + if (size <= 0) { + dev_err(&intf->dev, "%s: Failed to get manifest size (%d)\n", + __func__, size); + if (size) + return size; + else + return -EINVAL; + } + + manifest = kmalloc(size, GFP_KERNEL); + if (!manifest) + return -ENOMEM; + + /* Get manifest using control protocol on CPort */ + ret = gb_control_get_manifest_operation(intf, manifest, size); + if (ret) { + dev_err(&intf->dev, "%s: Failed to get manifest\n", __func__); + goto free_manifest; } /* - * Parse the manifest and build up our data structures - * representing what's in it. + * Parse the manifest and build up our data structures representing + * what's in it. */ - if (!gb_manifest_parse(intf, data, size)) { - dev_err(hd->parent, "manifest error\n"); - goto err_parse; + if (!gb_manifest_parse(intf, manifest, size)) { + dev_err(&intf->dev, "%s: Failed to parse manifest\n", __func__); + ret = -EINVAL; + goto free_manifest; } + ret = gb_bundles_init(intf, device_id); + if (ret) + dev_err(&intf->dev, + "Error %d initializing bundles for interface %hu\n", + ret, intf->interface_id); + /* * XXX * We've successfully parsed the manifest. Now we need to @@ -197,10 +258,9 @@ void gb_interface_add(struct greybus_host_device *hd, u8 interface_id, u8 *data, * configuring the switch to allow them to communicate). */ - return; - -err_parse: - gb_interface_destroy(intf); +free_manifest: + kfree(manifest); + return ret; } void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 9c566b237aba..90dbff13e977 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -48,8 +48,9 @@ static inline void *gb_interface_get_drvdata(struct gb_interface *intf) struct gb_interface *gb_interface_find(struct greybus_host_device *hd, u8 interface_id); -void gb_interface_add(struct greybus_host_device *hd, u8 interface_id, u8 *data, - int size); +struct gb_interface *gb_interface_create(struct greybus_host_device *hd, + u8 interface_id); +int gb_interface_init(struct gb_interface *intf, u8 device_id); void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id); void gb_interfaces_remove(struct greybus_host_device *hd); diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 377c449d5785..bd5753f8c384 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -225,11 +225,17 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) if (cport_id > CPORT_ID_MAX) goto cleanup; + /* Don't recreate connection for control cport */ + if (cport_id == GB_CONTROL_CPORT_ID) + goto release_descriptor; + /* Found one. Set up its function structure */ protocol_id = desc_cport->protocol_id; + if (!gb_connection_create(bundle, cport_id, protocol_id)) goto cleanup; +release_descriptor: count++; /* Release the cport descriptor */ @@ -268,11 +274,19 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) /* Found one. Set up its bundle structure*/ desc_bundle = desc->data; + + /* Don't recreate bundle for control cport */ + if (desc_bundle->id == GB_CONTROL_BUNDLE_ID) { + bundle = intf->control->connection->bundle; + goto parse_cports; + } + bundle = gb_bundle_create(intf, desc_bundle->id, desc_bundle->class); if (!bundle) goto cleanup; +parse_cports: /* Now go set up this bundle's functions and cports */ if (!gb_manifest_parse_cports(bundle)) goto cleanup; -- cgit v1.2.3-59-g8ed1b From 87c8eb8d09387de7263a7c2c75536db4aa50e056 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 22 Jun 2015 16:42:28 +0530 Subject: greybus: hotplug function doesn't need to contain manifest now Manifest is fetched with control protocol now and so we don't need space for it in hotplug data. Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 6 ++---- drivers/staging/greybus/svc_msg.h | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 113fd878c634..869f934732fc 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -167,8 +167,7 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, switch (hotplug->hotplug_event) { case SVC_HOTPLUG_EVENT: /* Add a new interface to the system */ - if (payload_length < 0x03) { - /* Hotplug message is at least 3 bytes big */ + if (payload_length != sizeof(*hotplug)) { dev_err(hd->parent, "Illegal size of svc hotplug message %d\n", payload_length); @@ -180,8 +179,7 @@ static void svc_hotplug(struct svc_function_hotplug *hotplug, case SVC_HOTUNPLUG_EVENT: /* Remove a interface from the system */ - if (payload_length != 0x02) { - /* Hotunplug message is only 2 bytes big */ + if (payload_length != sizeof(*hotplug)) { dev_err(hd->parent, "Illegal size of svc hotunplug message %d\n", payload_length); diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h index fadc1d70952e..3c628c5d6e38 100644 --- a/drivers/staging/greybus/svc_msg.h +++ b/drivers/staging/greybus/svc_msg.h @@ -82,7 +82,6 @@ enum svc_function_hotplug_event { struct svc_function_hotplug { __u8 hotplug_event; /* enum svc_function_hotplug_event */ __u8 interface_id; /* Interface id within the Endo */ - __u8 data[0]; } __packed; enum svc_function_power_type { -- cgit v1.2.3-59-g8ed1b From f18327e8ebf4738140e9b33a2de99bb8e526a269 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 22 Jun 2015 16:42:29 +0530 Subject: greybus: connection: send [dis]connected events over control CPort The AP needs to send connected and disconnection events to all interfaces, before a CPort (other than control CPort) can be used. For now do it which we initialize the connection, but it should be moved to operations code later. Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 9467aaba2b32..564011ac7894 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -248,6 +248,7 @@ void gb_connection_destroy(struct gb_connection *connection) int gb_connection_init(struct gb_connection *connection) { + int cport_id = connection->intf_cport_id; int ret; if (!connection->protocol) { @@ -255,6 +256,22 @@ int gb_connection_init(struct gb_connection *connection) return 0; } + /* + * Inform Interface about Active CPorts. We don't need to do this + * operation for control cport. + */ + if (cport_id != GB_CONTROL_CPORT_ID) { + struct gb_control *control = connection->bundle->intf->control; + + ret = gb_control_connected_operation(control, cport_id); + if (ret) { + dev_warn(&connection->dev, + "Failed to connect CPort-%d (%d)\n", + cport_id, ret); + return 0; + } + } + /* Need to enable the connection to initialize it */ connection->state = GB_CONNECTION_STATE_ENABLED; ret = connection->protocol->connection_init(connection); @@ -266,6 +283,8 @@ int gb_connection_init(struct gb_connection *connection) void gb_connection_exit(struct gb_connection *connection) { + int cport_id = connection->intf_cport_id; + if (!connection->protocol) { dev_warn(&connection->dev, "exit without protocol.\n"); return; @@ -276,4 +295,19 @@ void gb_connection_exit(struct gb_connection *connection) connection->state = GB_CONNECTION_STATE_DESTROYING; connection->protocol->connection_exit(connection); + + /* + * Inform Interface about In-active CPorts. We don't need to do this + * operation for control cport. + */ + if (cport_id != GB_CONTROL_CPORT_ID) { + struct gb_control *control = connection->bundle->intf->control; + int ret; + + ret = gb_control_disconnected_operation(control, cport_id); + if (ret) + dev_warn(&connection->dev, + "Failed to disconnect CPort-%d (%d)\n", + cport_id, ret); + } } -- cgit v1.2.3-59-g8ed1b From 730a2f6d1066e7771d5b85b8e9ec5ed09b153d01 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 22 Jun 2015 16:42:30 +0530 Subject: greybus: control: Warn if non-control cport/bundles have control protocol/classes It is possible that (by mistake) the manifest contains non-control cports with their protocol set as control-protocol or non-control bundle with their class set as control-class. Catch such cases, WARN for them and finally ignore them. Also WARN if the control cport doesn't have its protocol as control-protocol and control bundle doesn't have its class as control-class. Reviewed-by: Alex Elder Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index bd5753f8c384..ce4e89c6d13e 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -225,12 +225,20 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) if (cport_id > CPORT_ID_MAX) goto cleanup; + /* Found one. Set up its function structure */ + protocol_id = desc_cport->protocol_id; + /* Don't recreate connection for control cport */ - if (cport_id == GB_CONTROL_CPORT_ID) + if (cport_id == GB_CONTROL_CPORT_ID) { + /* This should have protocol set to control protocol*/ + WARN_ON(protocol_id != GREYBUS_PROTOCOL_CONTROL); + goto release_descriptor; + } - /* Found one. Set up its function structure */ - protocol_id = desc_cport->protocol_id; + /* Nothing else should have its protocol as control protocol */ + if (WARN_ON(protocol_id == GREYBUS_PROTOCOL_CONTROL)) + goto release_descriptor; if (!gb_connection_create(bundle, cport_id, protocol_id)) goto cleanup; @@ -277,10 +285,17 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) /* Don't recreate bundle for control cport */ if (desc_bundle->id == GB_CONTROL_BUNDLE_ID) { + /* This should have class set to control class */ + WARN_ON(desc_bundle->class != GREYBUS_CLASS_CONTROL); + bundle = intf->control->connection->bundle; goto parse_cports; } + /* Nothing else should have its class set to control class */ + if (WARN_ON(desc_bundle->class == GREYBUS_CLASS_CONTROL)) + goto release_descriptor; + bundle = gb_bundle_create(intf, desc_bundle->id, desc_bundle->class); if (!bundle) @@ -291,6 +306,7 @@ parse_cports: if (!gb_manifest_parse_cports(bundle)) goto cleanup; +release_descriptor: count++; /* Done with this bundle descriptor */ -- cgit v1.2.3-59-g8ed1b From 5656ab99a0ff9136613ab1da509ccd6d7652d173 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 24 Jun 2015 23:20:26 +0100 Subject: greybus: sdio: fix defines for older kernels Some of the options for mmc host, are not defined in older kernels. MMC_CAP2_HS400_1_2V, MMC_CAP2_HS400_1_8V, MMC_TIMING_MMC_DDR52 and MMC_TIMING_MMC_HS400. To not use them for older versions. Signed-off-by: Rui Miguel Silva Tested-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 8 ++++++++ drivers/staging/greybus/sdio.c | 14 +++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 8b8e712df48e..f95f302b2b2d 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -127,4 +127,12 @@ static inline void sysfs_remove_groups(struct kobject *kobj, } #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) +#define MMC_HS400_SUPPORTED +#define MMC_DDR52_DEFINED +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) +#define MMC_POWER_UNDEFINED_SUPPORTED +#endif #endif /* __GREYBUS_KERNEL_VER_H */ diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 1fd17c89b100..16abf7cafd90 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -62,9 +62,11 @@ static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) (r & GB_SDIO_CAP_DRIVER_TYPE_D ? MMC_CAP_DRIVER_TYPE_D : 0); caps2 = (r & GB_SDIO_CAP_HS200_1_2V ? MMC_CAP2_HS200_1_2V_SDR : 0) | - (r & GB_SDIO_CAP_HS200_1_8V ? MMC_CAP2_HS200_1_8V_SDR : 0) | +#ifdef MMC_HS400_SUPPORTED (r & GB_SDIO_CAP_HS400_1_2V ? MMC_CAP2_HS400_1_2V : 0) | - (r & GB_SDIO_CAP_HS400_1_8V ? MMC_CAP2_HS400_1_8V : 0); + (r & GB_SDIO_CAP_HS400_1_8V ? MMC_CAP2_HS400_1_8V : 0) | +#endif + (r & GB_SDIO_CAP_HS200_1_8V ? MMC_CAP2_HS200_1_8V_SDR : 0); host->mmc->caps = caps; host->mmc->caps2 = caps2; @@ -478,6 +480,7 @@ static void gb_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_OFF: + default: power_mode = GB_SDIO_POWER_OFF; break; case MMC_POWER_UP: @@ -486,10 +489,11 @@ static void gb_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_POWER_ON: power_mode = GB_SDIO_POWER_ON; break; +#ifdef MMC_POWER_UNDEFINED_SUPPORTED case MMC_POWER_UNDEFINED: - default: power_mode = GB_SDIO_POWER_UNDEFINED; break; +#endif } request.power_mode = power_mode; @@ -533,15 +537,19 @@ static void gb_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_TIMING_UHS_DDR50: timing = GB_SDIO_TIMING_UHS_DDR50; break; +#ifdef MMC_DDR52_DEFINED case MMC_TIMING_MMC_DDR52: timing = GB_SDIO_TIMING_MMC_DDR52; break; +#endif case MMC_TIMING_MMC_HS200: timing = GB_SDIO_TIMING_MMC_HS200; break; +#ifdef MMC_HS400_SUPPORTED case MMC_TIMING_MMC_HS400: timing = GB_SDIO_TIMING_MMC_HS400; break; +#endif } request.timing = timing; -- cgit v1.2.3-59-g8ed1b From 8a01b408a6b071862efadf905bad60d10612e78a Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 24 Jun 2015 23:20:27 +0100 Subject: greybus: kernel_ver: add sg copy operations for kernel < 3.11 For older kernel, < 3.11, no copy to/from buffer with skip support was defined. This could break builds for this versions of kernel. Add them here. Signed-off-by: Rui Miguel Silva Tested-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 100 +++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index f95f302b2b2d..4fb949ba9b51 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -135,4 +135,104 @@ static inline void sysfs_remove_groups(struct kobject *kobj, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) #define MMC_POWER_UNDEFINED_SUPPORTED #endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) +#include +static inline bool sg_miter_get_next_page(struct sg_mapping_iter *miter) +{ + if (!miter->__remaining) { + struct scatterlist *sg; + unsigned long pgoffset; + + if (!__sg_page_iter_next(&miter->piter)) + return false; + + sg = miter->piter.sg; + pgoffset = miter->piter.sg_pgoffset; + + miter->__offset = pgoffset ? 0 : sg->offset; + miter->__remaining = sg->offset + sg->length - + (pgoffset << PAGE_SHIFT) - miter->__offset; + miter->__remaining = min_t(unsigned long, miter->__remaining, + PAGE_SIZE - miter->__offset); + } + + return true; +} + +static inline bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset) +{ + sg_miter_stop(miter); + + while (offset) { + off_t consumed; + + if (!sg_miter_get_next_page(miter)) + return false; + + consumed = min_t(off_t, offset, miter->__remaining); + miter->__offset += consumed; + miter->__remaining -= consumed; + offset -= consumed; + } + + return true; +} + +static inline size_t _sg_copy_buffer(struct scatterlist *sgl, + unsigned int nents, void *buf, + size_t buflen, off_t skip, + bool to_buffer) +{ + unsigned int offset = 0; + struct sg_mapping_iter miter; + unsigned long flags; + unsigned int sg_flags = SG_MITER_ATOMIC; + + if (to_buffer) + sg_flags |= SG_MITER_FROM_SG; + else + sg_flags |= SG_MITER_TO_SG; + + sg_miter_start(&miter, sgl, nents, sg_flags); + + if (!sg_miter_skip(&miter, skip)) + return false; + + local_irq_save(flags); + + while (sg_miter_next(&miter) && offset < buflen) { + unsigned int len; + + len = min(miter.length, buflen - offset); + + if (to_buffer) + memcpy(buf + offset, miter.addr, len); + else + memcpy(miter.addr, buf + offset, len); + + offset += len; + } + + sg_miter_stop(&miter); + + local_irq_restore(flags); + return offset; +} + +static inline size_t sg_pcopy_to_buffer(struct scatterlist *sgl, + unsigned int nents, void *buf, + size_t buflen, off_t skip) +{ + return _sg_copy_buffer(sgl, nents, buf, buflen, skip, true); +} + +static inline size_t sg_pcopy_from_buffer(struct scatterlist *sgl, + unsigned int nents, void *buf, + size_t buflen, off_t skip) +{ + return _sg_copy_buffer(sgl, nents, buf, buflen, skip, false); +} +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ -- cgit v1.2.3-59-g8ed1b From 86f918ee7f941c383ee32ad28dca6e48a04b6074 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2015 14:17:41 +0200 Subject: greybus: esx: fix null-deref on hotplug events We must be prepared to receive hotplug events as soon as we submit the SVC URB. Since commit 2eb8a6a947d7 ("core: don't set up endo until host device is initialized") this is no longer the case as the endo would not have been setup, something which may lead to a null-pointer dereference in endo_get_module_id() when the interface is created (see oops below with an added dev_dbg for hd->endo). Fix this by setting up the endo before submitting the SVC URB. [ 28.810610] gb_interface_create - hd->endo = (null) [ 28.816020] Unable to handle kernel NULL pointer dereference at virtual address 0000022b [ 28.824952] pgd = c0004000 [ 28.827880] [0000022b] *pgd=00000000 [ 28.831913] Internal error: Oops: 17 [#1] PREEMPT ARM [ 28.837183] Modules linked in: gb_es1(O+) greybus(O) netconsole [ 28.843419] CPU: 0 PID: 21 Comm: kworker/u2:1 Tainted: G O 4.1.0-rc7 #12 [ 28.851576] Hardware name: Generic AM33XX (Flattened Device Tree) [ 28.857978] Workqueue: greybus_ap ap_process_event [greybus] [ 28.863890] task: cf2961c0 ti: cf29c000 task.ti: cf29c000 [ 28.869529] PC is at endo_get_module_id+0x18/0x88 [greybus] [ 28.875355] LR is at gb_interface_add+0x88/0x204 [greybus] [ 28.881070] pc : [] lr : [] psr: 20070013 [ 28.881070] sp : cf29de08 ip : cf29de18 fp : cf29de14 [ 28.893021] r10: 00000001 r9 : 0000005a r8 : cd813ec6 [ 28.898461] r7 : 00000058 r6 : cf7fa200 r5 : 00000001 r4 : cf7fa20c [ 28.905261] r3 : 00000000 r2 : cf2961c0 r1 : 00000001 r0 : 00000000 [ 28.912067] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel [ 28.919677] Control: 10c5387d Table: 8f508019 DAC: 00000015 [ 28.925663] Process kworker/u2:1 (pid: 21, stack limit = 0xcf29c210) [ 28.932279] Stack: (0xcf29de08 to 0xcf29e000) [ 28.936823] de00: cf29de44 cf29de18 bf005dac bf0052c8 00000058 cd813ec0 [ 28.945349] de20: cf58b60c bf00afe0 cf7fa200 cf58b600 0000005a 00000001 cf29de84 cf29de48 [ 28.953865] de40: bf004844 bf005d30 00000000 cf02d800 cf29de6c cf29de60 c00759a0 cf58b60c [ 28.962389] de60: cf2742c0 cf02d800 cf0c6000 cf29dea8 c07b745c 00000000 cf29dee4 cf29de88 [ 28.970908] de80: c005943c bf004560 00000001 00000000 c0059354 cf02d800 c0059c0c 00000001 [ 28.979426] dea0: 00000000 00000000 bf00b314 00000000 00000000 bf009144 c04e3710 cf02d800 [ 28.987945] dec0: cf2742d8 cf02d830 00000088 c0059bd0 00000000 cf2742c0 cf29df24 cf29dee8 [ 28.996464] dee0: c0059b78 c0059248 cf29c000 cf245d40 c0776890 c07b6bf3 00000000 00000000 [ 29.004983] df00: cf245d40 cf2742c0 c0059b20 00000000 00000000 00000000 cf29dfac cf29df28 [ 29.013502] df20: c005fe90 c0059b2c c07812d0 00000000 cf29df4c cf2742c0 00000000 00000001 [ 29.022025] df40: dead4ead ffffffff ffffffff c07c86b0 00000000 00000000 c05fd8e8 cf29df5c [ 29.030542] df60: cf29df5c 00000000 00000001 dead4ead ffffffff ffffffff c07c86b0 00000000 [ 29.039062] df80: 00000000 c05fd8e8 cf29df88 cf29df88 cf245d40 c005fd98 00000000 00000000 [ 29.047581] dfa0: 00000000 cf29dfb0 c00108f8 c005fda4 00000000 00000000 00000000 00000000 [ 29.056105] dfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 29.064623] dfe0: 00000000 00000000 00000000 00000000 00000013 00000000 ffff0000 ffff0000 [ 29.073178] [] (endo_get_module_id [greybus]) from [] (gb_interface_add+0x88/0x204 [greybus]) [ 29.083887] [] (gb_interface_add [greybus]) from [] (ap_process_event+0x2f0/0x4d8 [greybus]) [ 29.094527] [] (ap_process_event [greybus]) from [] (process_one_work+0x200/0x8e4) [ 29.104228] [] (process_one_work) from [] (worker_thread+0x58/0x500) [ 29.112668] [] (worker_thread) from [] (kthread+0xf8/0x110) [ 29.120295] [] (kthread) from [] (ret_from_fork+0x14/0x3c) [ 29.127825] Code: e24cb004 e52de004 e8bd4000 e3510000 (e5d0c22b) [ 29.137481] ---[ end trace ad95c3c26bdc98ce ]--- Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 19 +++++++++---------- drivers/staging/greybus/es2.c | 19 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index f9a6c54cca7a..067b4c13ed64 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -679,16 +679,6 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* Start up our svc urb, which allows events to start flowing */ - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; - - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", - (S_IWUSR | S_IRUGO), - gb_debugfs_get(), es1, - &apb1_log_enable_fops); - /* * XXX Soon this will be initiated later, with a combination * XXX of a Control protocol probe operation and a @@ -700,6 +690,15 @@ static int ap_probe(struct usb_interface *interface, if (retval) goto error; + /* Start up our svc urb, which allows events to start flowing */ + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", + (S_IWUSR | S_IRUGO), + gb_debugfs_get(), es1, + &apb1_log_enable_fops); return 0; error: ap_disconnect(interface); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 274d1d419320..97fa2e0c3673 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -783,16 +783,6 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* Start up our svc urb, which allows events to start flowing */ - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; - - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", - (S_IWUSR | S_IRUGO), - gb_debugfs_get(), es1, - &apb1_log_enable_fops); - /* * XXX Soon this will be initiated later, with a combination * XXX of a Control protocol probe operation and a @@ -804,6 +794,15 @@ static int ap_probe(struct usb_interface *interface, if (retval) goto error; + /* Start up our svc urb, which allows events to start flowing */ + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", + (S_IWUSR | S_IRUGO), + gb_debugfs_get(), es1, + &apb1_log_enable_fops); return 0; error: ap_disconnect(interface); -- cgit v1.2.3-59-g8ed1b From f4706b848ec6c58f797604112d30272db8d5e8dd Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Fri, 26 Jun 2015 16:38:46 -0700 Subject: greybus: gb-audio: Ensure i2c adapter struct exists before using The current audio code uses i2c_get_adapter() without checking that a non-NULL pointer is returned (i.e., that the i2c device actually exists). When that happens, the system panics. Fix the potential panic by erroring out with -ENODEV when i2c_get_adapter() returns NULL. CC: John Stultz Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index a077c2bbaad6..9f5f95913e8f 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -167,6 +167,7 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) struct asoc_simple_card_info *simple_card; #if USE_RT5645 struct i2c_board_info rt5647_info; + struct i2c_adapter *i2c_adap; #endif unsigned long flags; int ret; @@ -234,8 +235,14 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) rt5647_info.addr = RT5647_I2C_ADDR; strlcpy(rt5647_info.type, "rt5647", I2C_NAME_SIZE); - snd_dev->rt5647 = i2c_new_device(i2c_get_adapter(RT5647_I2C_ADAPTER_NR), - &rt5647_info); + i2c_adap = i2c_get_adapter(RT5647_I2C_ADAPTER_NR); + if (!i2c_adap) { + pr_err("codec unavailable\n"); + ret = -ENODEV; + goto out_get_ver; + } + + snd_dev->rt5647 = i2c_new_device(i2c_adap, &rt5647_info); if (!snd_dev->rt5647) { pr_err("can't create rt5647 i2c device\n"); goto out_get_ver; -- cgit v1.2.3-59-g8ed1b From 153f4784b428b3b5ef9d43ab9f8fa5322bc3717c Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Fri, 26 Jun 2015 21:05:11 +0700 Subject: greybus: sdio: remove the redefine of sdio major and minor The macro of sdio version major and minor is defined twice. This patch remove the redundant one. Signed-off-by: Phong Tran Reviewed-by: Rui Miguel Silva Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9f9b722de7df..83f8dc209727 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -660,10 +660,6 @@ struct gb_uart_serial_state_request { #define GB_SDIO_VERSION_MAJOR 0x00 #define GB_SDIO_VERSION_MINOR 0x01 -/* Version of the Greybus SDIO protocol we support */ -#define GB_SDIO_VERSION_MAJOR 0x00 -#define GB_SDIO_VERSION_MINOR 0x01 - /* Greybus SDIO operation types */ #define GB_SDIO_TYPE_INVALID 0x00 #define GB_SDIO_TYPE_PROTOCOL_VERSION 0x01 -- cgit v1.2.3-59-g8ed1b From 9b86bdf96ef79e5e286628cb2e3ea6639db71904 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Fri, 26 Jun 2015 21:05:12 +0700 Subject: greybus: sdio: change the order of remove and free mmc host The mmc host should be removed frist. Then it will be freed. Signed-off-by: Phong Tran Reviewed-by: Rui Miguel Silva Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 16abf7cafd90..cf12592d3468 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -700,8 +700,8 @@ static void gb_sdio_connection_exit(struct gb_connection *connection) flush_workqueue(gb_sdio_mrq_workqueue); destroy_workqueue(gb_sdio_mrq_workqueue); - mmc_free_host(mmc); mmc_remove_host(mmc); + mmc_free_host(mmc); kfree(host->xfer_buffer); } -- cgit v1.2.3-59-g8ed1b From 93a99e8a105aeaec432995196c21ffedcd0c0cad Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Fri, 26 Jun 2015 21:05:13 +0700 Subject: greybus: sdio: correct the usage of mmc request in work queues handler The mmc request should assigned before use. Then It should avoid freeing before using in mmc_request_done(). Signed-off-by: Phong Tran Reviewed-by: Rui Miguel Silva Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index cf12592d3468..30ebd42da287 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -387,13 +387,18 @@ static void gb_sdio_mrq_work(struct work_struct *work) host = container_of(work, struct gb_sdio_host, mrqwork); mutex_lock(&host->lock); + mrq = host->mrq; + if (!mrq) { + mutex_unlock(&host->lock); + dev_err(mmc_dev(host->mmc), "mmc request is NULL"); + return; + } + if (host->removed) { mrq->cmd->error = -ESHUTDOWN; goto done; } - mrq = host->mrq; - if (mrq->sbc) { ret = gb_sdio_command(host, mrq->sbc); if (ret < 0) @@ -417,7 +422,7 @@ static void gb_sdio_mrq_work(struct work_struct *work) } done: - mrq = NULL; + host->mrq = NULL; mutex_unlock(&host->lock); mmc_request_done(host->mmc, mrq); } -- cgit v1.2.3-59-g8ed1b From dccbe40ff97ac24ad8e99486df1cc2143bb99ff7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 30 Jun 2015 11:40:18 +0530 Subject: greybus: define more greybus classes There are new protocols defined which don't belong to any existing class, add more classes to support them. Reported-by: Alexandre Bailon Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index d6175cec6c17..f84cfebd2316 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -64,6 +64,9 @@ enum greybus_class_type { GREYBUS_CLASS_DISPLAY = 0x0c, GREYBUS_CLASS_CAMERA = 0x0d, GREYBUS_CLASS_SENSOR = 0x0e, + GREYBUS_CLASS_LIGHTS = 0x0f, + GREYBUS_CLASS_VIBRATOR = 0x10, + GREYBUS_CLASS_LOOPBACK = 0x11, GREYBUS_CLASS_VENDOR = 0xff, }; -- cgit v1.2.3-59-g8ed1b From 802362d4a621a03b394b3082b40e5471bf5268ad Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 29 Jun 2015 18:09:13 +0100 Subject: greybus: uart: Relocate UART parity/overrun/framing/break signals Parity/overrun/framing and break signals have been moved to the receive-data message to more easily associate the signals with the TTY API. Update the definitions in the protocol header and add a flags field to the receive-data structure to facilitate transmission of those signal with the receive-data message. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 83f8dc209727..44522b01ba32 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -606,9 +606,16 @@ struct gb_uart_send_data_request { __u8 data[0]; }; +/* recv-data-request flags */ +#define GB_UART_RECV_FLAG_FRAMING 0x01 /* Framing error */ +#define GB_UART_RECV_FLAG_PARITY 0x02 /* Parity error */ +#define GB_UART_RECV_FLAG_OVERRUN 0x04 /* Overrun error */ +#define GB_UART_RECV_FLAG_BREAK 0x08 /* Break */ + /* Represents data from Module -> AP */ struct gb_uart_recv_data_request { __le16 size; + __u8 flags; __u8 data[0]; }; @@ -644,12 +651,7 @@ struct gb_uart_set_break_request { /* input control lines and line errors */ #define GB_UART_CTRL_DCD 0x01 #define GB_UART_CTRL_DSR 0x02 -#define GB_UART_CTRL_BRK 0x04 -#define GB_UART_CTRL_RI 0x08 - -#define GB_UART_CTRL_FRAMING 0x10 -#define GB_UART_CTRL_PARITY 0x20 -#define GB_UART_CTRL_OVERRUN 0x40 +#define GB_UART_CTRL_RI 0x04 struct gb_uart_serial_state_request { __le16 control; -- cgit v1.2.3-59-g8ed1b From 4c025cf4168fe679b8a56eed210349458a142d07 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 29 Jun 2015 18:09:14 +0100 Subject: greybus: uart: Add support for UART error signals After reviewing the UART specification for greybus break, parity, framing and over-run errors were moved to the receive-data message. This patch implements that specification change in the UART protocol driver. Matching code in gbsim has been tested with this change. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 59 ++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 3b06cd46694b..73e3c992e103 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -70,35 +70,56 @@ static atomic_t reference_count = ATOMIC_INIT(0); /* Define get_version() routine */ define_get_version(gb_tty, UART); +static int gb_uart_receive_data(struct gb_tty *gb_tty, + struct gb_connection *connection, + struct gb_uart_recv_data_request *receive_data) +{ + struct tty_port *port = &gb_tty->port; + u16 recv_data_size; + int count; + unsigned long tty_flags = TTY_NORMAL; + + count = gb_tty->buffer_payload_max - sizeof(*receive_data); + recv_data_size = le16_to_cpu(receive_data->size); + if (!recv_data_size || recv_data_size > count) + return -EINVAL; + + if (receive_data->flags) { + if (receive_data->flags & GB_UART_RECV_FLAG_BREAK) + tty_flags = TTY_BREAK; + else if (receive_data->flags & GB_UART_RECV_FLAG_PARITY) + tty_flags = TTY_PARITY; + else if (receive_data->flags & GB_UART_RECV_FLAG_FRAMING) + tty_flags = TTY_FRAME; + + /* overrun is special, not associated with a char */ + if (receive_data->flags & GB_UART_RECV_FLAG_OVERRUN) + tty_insert_flip_char(port, 0, TTY_OVERRUN); + } + count = tty_insert_flip_string_fixed_flag(port, receive_data->data, + tty_flags, recv_data_size); + if (count != recv_data_size) { + dev_err(&connection->dev, + "UART: RX 0x%08x bytes only wrote 0x%08x\n", + recv_data_size, count); + } + if (count) + tty_flip_buffer_push(port); + return 0; +} + static int gb_uart_request_recv(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_tty *gb_tty = connection->private; struct gb_message *request = op->request; - struct gb_uart_recv_data_request *receive_data; struct gb_uart_serial_state_request *serial_state; - struct tty_port *port = &gb_tty->port; - u16 recv_data_size; - int count; int ret = 0; switch (type) { case GB_UART_TYPE_RECEIVE_DATA: - receive_data = request->payload; - count = gb_tty->buffer_payload_max - sizeof(*receive_data); - recv_data_size = le16_to_cpu(receive_data->size); - if (!recv_data_size || recv_data_size > count) - return -EINVAL; - - count = tty_insert_flip_string(port, receive_data->data, - recv_data_size); - if (count != recv_data_size) { - dev_err(&connection->dev, - "UART: RX 0x%08x bytes only wrote 0x%08x\n", - recv_data_size, count); - } - if (count) - tty_flip_buffer_push(port); + ret = gb_uart_receive_data(gb_tty, connection, + request->payload); break; case GB_UART_TYPE_SERIAL_STATE: serial_state = request->payload; -- cgit v1.2.3-59-g8ed1b From ba4b099ca8e30b064d06bf9fc3f1a2bbcb8e1818 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 29 Jun 2015 18:09:15 +0100 Subject: greybus: uart: Update UART to reflect field size changes The greybus UART protocol specification was updated to reduce the size of the control field in serial-state-request and line-state-request. This patch updates the kernel protocol driver to reflect the specification changes. Once applied gbsim changes will be also be updated automatically since gbsim depends on the header being modified directly. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 ++-- drivers/staging/greybus/uart.c | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 44522b01ba32..6f8c15ea3900 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -641,7 +641,7 @@ struct gb_uart_set_line_coding_request { #define GB_UART_CTRL_RTS 0x02 struct gb_uart_set_control_line_state_request { - __le16 control; + __u8 control; }; struct gb_uart_set_break_request { @@ -654,7 +654,7 @@ struct gb_uart_set_break_request { #define GB_UART_CTRL_RI 0x04 struct gb_uart_serial_state_request { - __le16 control; + __u8 control; }; /* SDIO */ diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 73e3c992e103..7abcd1c05bda 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -57,8 +57,8 @@ struct gb_tty { struct mutex mutex; u8 version_major; u8 version_minor; - unsigned int ctrlin; /* input control lines */ - unsigned int ctrlout; /* output control lines */ + u8 ctrlin; /* input control lines */ + u8 ctrlout; /* output control lines */ struct gb_tty_line_coding line_coding; }; @@ -123,7 +123,7 @@ static int gb_uart_request_recv(u8 type, struct gb_operation *op) break; case GB_UART_TYPE_SERIAL_STATE: serial_state = request->payload; - gb_tty->ctrlin = le16_to_cpu(serial_state->control); + gb_tty->ctrlin = serial_state->control; break; default: dev_err(&connection->dev, @@ -165,11 +165,11 @@ static int send_line_coding(struct gb_tty *tty) &request, sizeof(request), NULL, 0); } -static int send_control(struct gb_tty *gb_tty, u16 control) +static int send_control(struct gb_tty *gb_tty, u8 control) { struct gb_uart_set_control_line_state_request request; - request.control = cpu_to_le16(control); + request.control = control; return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SET_CONTROL_LINE_STATE, &request, sizeof(request), NULL, 0); @@ -314,7 +314,7 @@ static void gb_tty_set_termios(struct tty_struct *tty, struct gb_tty *gb_tty = tty->driver_data; struct ktermios *termios = &tty->termios; struct gb_tty_line_coding newline; - int newctrl = gb_tty->ctrlout; + u8 newctrl = gb_tty->ctrlout; newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); newline.format = termios->c_cflag & CSTOPB ? @@ -376,7 +376,7 @@ static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct gb_tty *gb_tty = tty->driver_data; - unsigned int newctrl = gb_tty->ctrlout; + u8 newctrl = gb_tty->ctrlout; set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); -- cgit v1.2.3-59-g8ed1b From 7eb8919b0f984b4b2e3764976383d4a98dbc82e2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Jul 2015 12:13:50 +0530 Subject: greybus: svc: Fix doc-style comment Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ba85e81964c4..cd87045ba104 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -124,7 +124,7 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) * XXX continue processing the request. There's no need * XXX for the SVC to wait. In fact, it might be best to * XXX have the SVC get acknowledgement before we proceed. - * */ + */ intf_id = hotplug->intf_id; unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); -- cgit v1.2.3-59-g8ed1b From 06e305f1e377a6a4e658bd165e20af4c099fb76a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Jul 2015 12:13:51 +0530 Subject: greybus: svc: Use macro's for major/minor numbers We have already defined macro's for SVC's major/minor numbers, lets use them. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index cd87045ba104..75b333783941 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -233,8 +233,8 @@ static void gb_svc_connection_exit(struct gb_connection *connection) static struct gb_protocol svc_protocol = { .name = "svc", .id = GREYBUS_PROTOCOL_SVC, - .major = 0, - .minor = 1, + .major = GB_SVC_VERSION_MAJOR, + .minor = GB_SVC_VERSION_MINOR, .connection_init = gb_svc_connection_init, .connection_exit = gb_svc_connection_exit, .request_recv = gb_svc_request_recv, -- cgit v1.2.3-59-g8ed1b From e18822e3d02c8f296fe42e0e8bda7e2e626a4714 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Jul 2015 12:13:52 +0530 Subject: greybus: Rename gb_gpbridge_protocol_driver() as gb_builtin_protocol_driver() This macro is also required by core protocols like control and svc, and hence the 'gpbridge' name doesn't fit anymore. Lets call this macro gb_builtin_protocol_driver(). Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 2 +- drivers/staging/greybus/hid.c | 2 +- drivers/staging/greybus/i2c.c | 2 +- drivers/staging/greybus/protocol.h | 2 +- drivers/staging/greybus/pwm.c | 2 +- drivers/staging/greybus/sdio.c | 2 +- drivers/staging/greybus/spi.c | 2 +- drivers/staging/greybus/uart.c | 2 +- drivers/staging/greybus/usb.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 95909149eba2..6539530a178c 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -725,4 +725,4 @@ static struct gb_protocol gpio_protocol = { .request_recv = gb_gpio_request_recv, }; -gb_gpbridge_protocol_driver(gpio_protocol); +gb_builtin_protocol_driver(gpio_protocol); diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 2f6e68c1be88..1214b7a0a631 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -529,4 +529,4 @@ static struct gb_protocol hid_protocol = { .request_recv = gb_hid_irq_handler, }; -gb_gpbridge_protocol_driver(hid_protocol); +gb_builtin_protocol_driver(hid_protocol); diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 01afca8408a0..5eb7703599d6 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -356,4 +356,4 @@ static struct gb_protocol i2c_protocol = { .request_recv = NULL, /* no incoming requests */ }; -gb_gpbridge_protocol_driver(i2c_protocol); +gb_builtin_protocol_driver(i2c_protocol); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index f6739f3332cd..3f226e3bbe06 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -103,7 +103,7 @@ static void __exit protocol_exit(void) \ module_exit(protocol_exit) /* __protocol: string matching name of struct gb_protocol */ -#define gb_gpbridge_protocol_driver(__protocol) \ +#define gb_builtin_protocol_driver(__protocol) \ int __init gb_##__protocol##_init(void) \ { \ return gb_protocol_register(&__protocol); \ diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 5dfeb0e761c1..be7131a41a97 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -246,4 +246,4 @@ static struct gb_protocol pwm_protocol = { .request_recv = NULL, /* no incoming requests */ }; -gb_gpbridge_protocol_driver(pwm_protocol); +gb_builtin_protocol_driver(pwm_protocol); diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 30ebd42da287..f028e14fc918 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -720,4 +720,4 @@ static struct gb_protocol sdio_protocol = { .request_recv = gb_sdio_event_recv, }; -gb_gpbridge_protocol_driver(sdio_protocol); +gb_builtin_protocol_driver(sdio_protocol); diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 78a7f85a4bf6..374361889666 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -347,4 +347,4 @@ static struct gb_protocol spi_protocol = { .request_recv = NULL, }; -gb_gpbridge_protocol_driver(spi_protocol); +gb_builtin_protocol_driver(spi_protocol); diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 7abcd1c05bda..e2a456f8105c 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -773,4 +773,4 @@ static struct gb_protocol uart_protocol = { .request_recv = gb_uart_request_recv, }; -gb_gpbridge_protocol_driver(uart_protocol); +gb_builtin_protocol_driver(uart_protocol); diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 609b7cc66768..888f514921b6 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -362,4 +362,4 @@ static struct gb_protocol usb_protocol = { .request_recv = NULL, /* FIXME we have requests!!! */ }; -gb_gpbridge_protocol_driver(usb_protocol); +gb_builtin_protocol_driver(usb_protocol); -- cgit v1.2.3-59-g8ed1b From 463e8736a3a5038732f681a7f1e9f6975d25b3fd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Jul 2015 12:13:53 +0530 Subject: greybus: control: Use gb_builtin_protocol_driver() No need to write simple init/exit routines, use gb_builtin_protocol_driver(). Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index c19814d311ec..d7870fc83ed2 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -138,13 +138,4 @@ static struct gb_protocol control_protocol = { .connection_exit = gb_control_connection_exit, .request_recv = gb_control_request_recv, }; - -int gb_control_protocol_init(void) -{ - return gb_protocol_register(&control_protocol); -} - -void gb_control_protocol_exit(void) -{ - gb_protocol_deregister(&control_protocol); -} +gb_builtin_protocol_driver(control_protocol); -- cgit v1.2.3-59-g8ed1b From b758d68618ffda2f6c1c1d40f90350429e9f7092 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Jul 2015 12:13:54 +0530 Subject: greybus: connection: bind protocol after the connection is operational We may bind protocol with a connection from gb_connection_create(), if bundle's device_id is already set. That's not the case until now. But if the protocol is initialized with a call to protocol->connection_init() from this place, kernel will crash. This will happen because the connection isn't fully initialized yet, for example its operation list isn't initialized yet. And as soon as the protocol driver tries to send a request to the module from its connection_init() callback, we will add an operation to this uninitialized list. And it will crash while doing: prev->next = new; Try to bind the connection with a protocol only after the connection is ready for operations. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 564011ac7894..6cd9fe2df034 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -196,12 +196,6 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, return NULL; } - /* XXX Will have to establish connections to get version */ - gb_connection_bind_protocol(connection); - if (!connection->protocol) - dev_warn(&bundle->dev, - "protocol 0x%02hhx handler not found\n", protocol_id); - spin_lock_irq(&gb_connections_lock); list_add(&connection->hd_links, &hd->connections); list_add(&connection->bundle_links, &bundle->connections); @@ -210,6 +204,12 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, atomic_set(&connection->op_cycle, 0); INIT_LIST_HEAD(&connection->operations); + /* XXX Will have to establish connections to get version */ + gb_connection_bind_protocol(connection); + if (!connection->protocol) + dev_warn(&bundle->dev, + "protocol 0x%02hhx handler not found\n", protocol_id); + return connection; } -- cgit v1.2.3-59-g8ed1b From c3a1617099cb57da038297cfc40abedb1fe7a287 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Jul 2015 12:13:56 +0530 Subject: greybus: connection: intf_cport_id is local to an interface intf_cport_id is local to an interface and same intf_cport_id can be used for two separate interface blocks. For finding a connection for an interface, we are matching intf_cport_id of the connection with cport_id, passed as argument. This is wrong, as it can match for a connection on a different interface block. Fix it by also comparing interface block for which connection is requested. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 6cd9fe2df034..85b2a3370744 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -13,12 +13,14 @@ static DEFINE_SPINLOCK(gb_connections_lock); /* This is only used at initialization time; no locking is required. */ static struct gb_connection * -gb_connection_intf_find(struct greybus_host_device *hd, u16 cport_id) +gb_connection_intf_find(struct gb_interface *intf, u16 cport_id) { + struct greybus_host_device *hd = intf->hd; struct gb_connection *connection; list_for_each_entry(connection, &hd->connections, hd_links) - if (connection->intf_cport_id == cport_id) + if (connection->bundle->intf == intf && + connection->intf_cport_id == cport_id) return connection; return NULL; } @@ -149,7 +151,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, * initialize connections serially so we don't need to worry * about holding the connection lock. */ - if (gb_connection_intf_find(hd, cport_id)) { + if (gb_connection_intf_find(bundle->intf, cport_id)) { pr_err("duplicate interface cport id 0x%04hx\n", cport_id); return NULL; } -- cgit v1.2.3-59-g8ed1b From 7e9017d482a2678ed32e7f60e6bfea10bab2dfa1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Jul 2015 12:13:57 +0530 Subject: greybus: svc: Drop svc_set_route_send() The responsibility of this routine is to configure ARA switch to establish a connection between a cport on the AP and a cport on an interface. The SVC protocol is responsible for such requests and we already have connection_{create|destroy}_operation() to take care of this. Moreover, this request is not served by the firmware or gbsim today and they just print a debug message on this request. And so it is safe to drop svc_set_route_send() function and fix the required functionality of configuring the switch in svc protocol driver later. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/ap.c | 18 ------------------ drivers/staging/greybus/bundle.c | 6 ------ drivers/staging/greybus/greybus.h | 3 --- 3 files changed, 27 deletions(-) diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c index 869f934732fc..fc238170ad12 100644 --- a/drivers/staging/greybus/ap.c +++ b/drivers/staging/greybus/ap.c @@ -60,24 +60,6 @@ static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) return retval; } - -int svc_set_route_send(struct gb_bundle *bundle, - struct greybus_host_device *hd) -{ - struct svc_msg *svc_msg; - - svc_msg = svc_msg_alloc(SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT); - if (!svc_msg) - return -ENOMEM; - - svc_msg->header.message_type = SVC_MSG_DATA; - svc_msg->header.payload_length = - cpu_to_le16(sizeof(struct svc_function_unipro_set_route)); - svc_msg->management.set_route.device_id = bundle->device_id; - - return svc_msg_send(svc_msg, hd); -} - static void svc_handshake(struct svc_function_handshake *handshake, int payload_length, struct greybus_host_device *hd) { diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index a5172e5f64b2..e636230144c7 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -236,12 +236,6 @@ int gb_bundle_init(struct gb_bundle *bundle, u8 device_id) bundle->device_id = device_id; - ret = svc_set_route_send(bundle, intf->hd); - if (ret) { - dev_err(intf->hd->parent, "failed to set route (%d)\n", ret); - return ret; - } - ret = gb_bundle_connections_init(bundle); if (ret) { dev_err(intf->hd->parent, "interface bundle init error %d\n", diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 6874939e6d1e..c1157df9230b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -163,9 +163,6 @@ struct dentry *gb_debugfs_get(void); extern struct bus_type greybus_bus_type; -int svc_set_route_send(struct gb_bundle *bundle, - struct greybus_host_device *hd); - extern struct device_type greybus_endo_type; extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; -- cgit v1.2.3-59-g8ed1b From c3add7883c70b625b99c37ca89e6ec8733037ce3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Jul 2015 12:13:58 +0530 Subject: greybus: interface: device_id belongs to the interface While introducing bundles, the device_id also got moved to the bundle, whereas it identifies an interface block to the AP. Move it back to interface instead of bundle. Calls to gb_bundle(s)_init() are dropped as connections will be initialized while they are created now, as device_id will be valid. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs-bus-greybus | 14 ++++++------ drivers/staging/greybus/bundle.c | 21 +++-------------- drivers/staging/greybus/bundle.h | 5 ++--- drivers/staging/greybus/connection.c | 10 ++++----- drivers/staging/greybus/interface.c | 26 ++++++++-------------- drivers/staging/greybus/interface.h | 1 + 6 files changed, 27 insertions(+), 50 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 0268dd24d2a4..808fde96f56b 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -105,6 +105,13 @@ Contact: Greg Kroah-Hartman Description: Vendor ID string of a Greybus interface block. +What: /sys/bus/greybus/device/.../device_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The device ID of a Greybus interface block. + What: /sys/bus/greybus/device/.../state Date: October 2015 KernelVersion: 4.XX @@ -126,13 +133,6 @@ Contact: Greg Kroah-Hartman Description: The protocol ID of a Greybus connection. -What: /sys/bus/greybus/device/.../device_id -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The device ID of a Greybus bundle. - What: /sys/bus/greybus/device/.../state Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index e636230144c7..c6694aebb65b 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -13,15 +13,6 @@ static void gb_bundle_connections_exit(struct gb_bundle *bundle); static int gb_bundle_connections_init(struct gb_bundle *bundle); -static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct gb_bundle *bundle = to_gb_bundle(dev); - - return sprintf(buf, "%d\n", bundle->device_id); -} -static DEVICE_ATTR_RO(device_id); - static ssize_t class_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -61,7 +52,6 @@ static DEVICE_ATTR_RW(state); static struct attribute *bundle_attrs[] = { - &dev_attr_device_id.attr, &dev_attr_class.attr, &dev_attr_state.attr, NULL, @@ -184,9 +174,6 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, bundle->class = class; INIT_LIST_HEAD(&bundle->connections); - /* Invalid device id to start with */ - bundle->device_id = GB_DEVICE_ID_BAD; - /* Build up the bundle device structures and register it with the * driver core */ bundle->dev.parent = &intf->dev; @@ -225,7 +212,7 @@ void gb_bundle_destroy(struct gb_bundle *bundle) device_unregister(&bundle->dev); } -int gb_bundle_init(struct gb_bundle *bundle, u8 device_id) +int gb_bundle_init(struct gb_bundle *bundle) { struct gb_interface *intf = bundle->intf; int ret; @@ -234,8 +221,6 @@ int gb_bundle_init(struct gb_bundle *bundle, u8 device_id) if (intf->control && bundle->id == GB_CONTROL_BUNDLE_ID) return 0; - bundle->device_id = device_id; - ret = gb_bundle_connections_init(bundle); if (ret) { dev_err(intf->hd->parent, "interface bundle init error %d\n", @@ -247,13 +232,13 @@ int gb_bundle_init(struct gb_bundle *bundle, u8 device_id) return 0; } -int gb_bundles_init(struct gb_interface *intf, u8 device_id) +int gb_bundles_init(struct gb_interface *intf) { struct gb_bundle *bundle; int ret = 0; list_for_each_entry(bundle, &intf->bundles, links) { - ret = gb_bundle_init(bundle, device_id); + ret = gb_bundle_init(bundle); if (ret) { dev_err(intf->hd->parent, "Failed to initialize bundle %hhu\n", diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 887883dabfc0..60033b82ab3f 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -18,7 +18,6 @@ struct gb_bundle { struct gb_interface *intf; u8 id; u8 class; - u8 device_id; struct list_head connections; u8 *state; @@ -32,8 +31,8 @@ struct gb_bundle { struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, u8 class); void gb_bundle_destroy(struct gb_bundle *bundle); -int gb_bundle_init(struct gb_bundle *bundle, u8 device_id); -int gb_bundles_init(struct gb_interface *intf, u8 device_id); +int gb_bundle_init(struct gb_bundle *bundle); +int gb_bundles_init(struct gb_interface *intf); struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id); void gb_bundle_bind_protocols(void); diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 85b2a3370744..2e306d13ac28 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -102,7 +102,7 @@ struct device_type greybus_connection_type = { void gb_connection_bind_protocol(struct gb_connection *connection) { - struct gb_bundle *bundle; + struct gb_interface *intf; struct gb_protocol *protocol; /* If we already have a protocol bound here, just return */ @@ -117,11 +117,11 @@ void gb_connection_bind_protocol(struct gb_connection *connection) connection->protocol = protocol; /* - * If we have a valid device_id for the bundle, then we have an active - * device, so bring up the connection at the same time. + * If we have a valid device_id for the interface block, then we have an + * active device, so bring up the connection at the same time. * */ - bundle = connection->bundle; - if (bundle->device_id != GB_DEVICE_ID_BAD) + intf = connection->bundle->intf; + if (intf->device_id != GB_DEVICE_ID_BAD) gb_connection_init(connection); } diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 901c4acc2421..f9fd479bfd8b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -20,6 +20,7 @@ static ssize_t field##_show(struct device *dev, \ } \ static DEVICE_ATTR_RO(field) +gb_interface_attr(device_id, d); gb_interface_attr(vendor, x); gb_interface_attr(product, x); gb_interface_attr(unique_id, llX); @@ -27,6 +28,7 @@ gb_interface_attr(vendor_string, s); gb_interface_attr(product_string, s); static struct attribute *interface_attrs[] = { + &dev_attr_device_id.attr, &dev_attr_vendor.attr, &dev_attr_product.attr, &dev_attr_unique_id.attr, @@ -71,10 +73,9 @@ struct device_type greybus_interface_type = { * managing control CPort. Also initialize the bundle, which will request SVC to * set route and will initialize the control protocol for this connection. */ -static int gb_create_control_connection(struct gb_interface *intf, u8 device_id) +static int gb_create_control_connection(struct gb_interface *intf) { struct gb_bundle *bundle; - int ret; bundle = gb_bundle_create(intf, GB_CONTROL_BUNDLE_ID, GREYBUS_CLASS_CONTROL); @@ -85,14 +86,6 @@ static int gb_create_control_connection(struct gb_interface *intf, u8 device_id) GREYBUS_PROTOCOL_CONTROL)) return -EINVAL; - ret = gb_bundle_init(bundle, device_id); - if (ret) { - dev_err(&intf->dev, - "error %d initializing bundles for interface %hu\n", - ret, intf->interface_id); - return ret; - } - return 0; } @@ -136,6 +129,9 @@ struct gb_interface *gb_interface_create(struct greybus_host_device *hd, INIT_LIST_HEAD(&intf->bundles); INIT_LIST_HEAD(&intf->manifest_descs); + /* Invalid device id to start with */ + intf->device_id = GB_DEVICE_ID_BAD; + intf->dev.parent = &module->dev; intf->dev.bus = &greybus_bus_type; intf->dev.type = &greybus_interface_type; @@ -204,8 +200,10 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) int ret, size; void *manifest; + intf->device_id = device_id; + /* Establish control CPort connection */ - ret = gb_create_control_connection(intf, device_id); + ret = gb_create_control_connection(intf); if (ret) { dev_err(&intf->dev, "Failed to create control CPort connection (%d)\n", ret); return ret; @@ -243,12 +241,6 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) goto free_manifest; } - ret = gb_bundles_init(intf, device_id); - if (ret) - dev_err(&intf->dev, - "Error %d initializing bundles for interface %hu\n", - ret, intf->interface_id); - /* * XXX * We've successfully parsed the manifest. Now we need to diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 90dbff13e977..86eb8947a3d6 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -19,6 +19,7 @@ struct gb_interface { struct list_head links; /* greybus_host_device->interfaces */ struct list_head manifest_descs; u8 interface_id; /* Physical location within the Endo */ + u8 device_id; /* Device id allocated for the interface block by the SVC */ /* Information taken from the manifest descriptor */ u16 vendor; -- cgit v1.2.3-59-g8ed1b From 5677d48b9735ca43f546d8e21dd36a993b770090 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 1 Jul 2015 12:13:59 +0530 Subject: greybus: bundles: remove gb_bundle(s)_init() They aren't used anymore, remove them. This also gets rid of gb_bundle_connections_init(). Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 54 ---------------------------------------- drivers/staging/greybus/bundle.h | 2 -- 2 files changed, 56 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index c6694aebb65b..694bcce67bf2 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -10,8 +10,6 @@ #include "greybus.h" static void gb_bundle_connections_exit(struct gb_bundle *bundle); -static int gb_bundle_connections_init(struct gb_bundle *bundle); - static ssize_t class_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -212,44 +210,6 @@ void gb_bundle_destroy(struct gb_bundle *bundle) device_unregister(&bundle->dev); } -int gb_bundle_init(struct gb_bundle *bundle) -{ - struct gb_interface *intf = bundle->intf; - int ret; - - /* Don't reinitialize control cport's bundle */ - if (intf->control && bundle->id == GB_CONTROL_BUNDLE_ID) - return 0; - - ret = gb_bundle_connections_init(bundle); - if (ret) { - dev_err(intf->hd->parent, "interface bundle init error %d\n", - ret); - /* XXX clear route */ - return ret; - } - - return 0; -} - -int gb_bundles_init(struct gb_interface *intf) -{ - struct gb_bundle *bundle; - int ret = 0; - - list_for_each_entry(bundle, &intf->bundles, links) { - ret = gb_bundle_init(bundle); - if (ret) { - dev_err(intf->hd->parent, - "Failed to initialize bundle %hhu\n", - bundle->id); - break; - } - } - - return ret; -} - struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id) { struct gb_bundle *bundle; @@ -265,20 +225,6 @@ found: return bundle; } -static int gb_bundle_connections_init(struct gb_bundle *bundle) -{ - struct gb_connection *connection; - int ret = 0; - - list_for_each_entry(connection, &bundle->connections, bundle_links) { - ret = gb_connection_init(connection); - if (ret) - break; - } - - return ret; -} - static void gb_bundle_connections_exit(struct gb_bundle *bundle) { struct gb_connection *connection; diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 60033b82ab3f..9134df7930c0 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -31,8 +31,6 @@ struct gb_bundle { struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, u8 class); void gb_bundle_destroy(struct gb_bundle *bundle); -int gb_bundle_init(struct gb_bundle *bundle); -int gb_bundles_init(struct gb_interface *intf); struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id); void gb_bundle_bind_protocols(void); -- cgit v1.2.3-59-g8ed1b From 3e136cc9e05e1a34d8602a4d4e31c9d93ccbbdf7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:21 +0200 Subject: greybus: operation/esx: fix message-cancellation lifetime bugs The current host-controller message-cancellation implementation suffer from a lifetime bug as dynamically allocated URBs would complete and be deallocated while being unlinked as part of cancellation. The current locking is also insufficient to prevent the related race where the URB is deallocated before being unlinked. Fix this by pushing the cancellation implementation from greybus core down to the host-controller drivers, and replace the "cookie" pointer with a hcpriv field that those drivers can use to maintain their state with the required locking and reference counting in place. Specifically the drivers need to acquire a reference to the URB under a lock before calling usb_kill_urb as part of cancellation. Note that this also removes the insufficient gb_message_mutex, which also effectively prevented us from implementing support for submissions from atomic context. Instead the host-controller drivers must now explicitly make sure that the pre-allocated URBs are not reused while cancellation is in progress. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 81 +++++++++++++++++++++++++++---------- drivers/staging/greybus/es2.c | 81 +++++++++++++++++++++++++++---------- drivers/staging/greybus/greybus.h | 4 +- drivers/staging/greybus/operation.c | 27 ++----------- drivers/staging/greybus/operation.h | 9 +++-- 5 files changed, 130 insertions(+), 72 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 067b4c13ed64..0cb7a3c7ef72 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -68,6 +68,8 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); * @cport_out_urb: array of urbs for the CPort out messages * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or * not. + * @cport_out_urb_cancelled: array of flags indicating whether the + * corresponding @cport_out_urb is being cancelled * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" */ struct es1_ap_dev { @@ -87,6 +89,7 @@ struct es1_ap_dev { u8 *cport_in_buffer[NUM_CPORT_IN_URB]; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; + bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; spinlock_t cport_out_urb_lock; }; @@ -131,7 +134,8 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) /* Look in our pool of allocated urbs first, as that's the "fastest" */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (es1->cport_out_urb_busy[i] == false) { + if (es1->cport_out_urb_busy[i] == false && + es1->cport_out_urb_cancelled[i] == false) { es1->cport_out_urb_busy[i] = true; urb = es1->cport_out_urb[i]; break; @@ -199,11 +203,10 @@ static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) } /* - * Returns an opaque cookie value if successful, or a pointer coded - * error otherwise. If the caller wishes to cancel the in-flight - * buffer, it must supply the returned cookie to the cancel routine. + * Returns zero if the message was successfully queued, or a negative errno + * otherwise. */ -static void *message_send(struct greybus_host_device *hd, u16 cport_id, +static int message_send(struct greybus_host_device *hd, u16 cport_id, struct gb_message *message, gfp_t gfp_mask) { struct es1_ap_dev *es1 = hd_to_es1(hd); @@ -211,6 +214,7 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, size_t buffer_size; int retval; struct urb *urb; + unsigned long flags; /* * The data actually transferred will include an indication @@ -219,13 +223,17 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, */ if (!cport_id_valid(cport_id)) { pr_err("invalid destination cport 0x%02x\n", cport_id); - return ERR_PTR(-EINVAL); + return -EINVAL; } /* Find a free urb */ urb = next_free_urb(es1, gfp_mask); if (!urb) - return ERR_PTR(-ENOMEM); + return -ENOMEM; + + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + message->hcpriv = urb; + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); /* Pack the cport id into the message header */ gb_message_cport_pack(message->header, cport_id); @@ -239,30 +247,56 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); + + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + message->hcpriv = NULL; + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + free_urb(es1, urb); gb_message_cport_clear(message->header); - return ERR_PTR(retval); + + return retval; } - return urb; + return 0; } /* - * The cookie value supplied is the value that message_send() - * returned to its caller. It identifies the message that should be - * canceled. This function must also handle (which is to say, - * ignore) a null cookie value. + * Can not be called in atomic context. */ -static void message_cancel(void *cookie) +static void message_cancel(struct gb_message *message) { + struct greybus_host_device *hd = message->operation->connection->hd; + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct urb *urb; + int i; - /* - * We really should be defensive and track all outstanding - * (sent) messages rather than trusting the cookie provided - * is valid. For the time being, this will do. - */ - if (cookie) - usb_kill_urb(cookie); + might_sleep(); + + spin_lock_irq(&es1->cport_out_urb_lock); + urb = message->hcpriv; + + /* Prevent dynamically allocated urb from being deallocated. */ + usb_get_urb(urb); + + /* Prevent pre-allocated urb from being reused. */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (urb == es1->cport_out_urb[i]) { + es1->cport_out_urb_cancelled[i] = true; + break; + } + } + spin_unlock_irq(&es1->cport_out_urb_lock); + + usb_kill_urb(urb); + + if (i < NUM_CPORT_OUT_URB) { + spin_lock_irq(&es1->cport_out_urb_lock); + es1->cport_out_urb_cancelled[i] = false; + spin_unlock_irq(&es1->cport_out_urb_lock); + } + + usb_free_urb(urb); } static struct greybus_host_driver es1_driver = { @@ -418,6 +452,7 @@ static void cport_out_callback(struct urb *urb) struct greybus_host_device *hd = message->operation->connection->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); + unsigned long flags; gb_message_cport_clear(message->header); @@ -427,6 +462,10 @@ static void cport_out_callback(struct urb *urb) */ greybus_message_sent(hd, message, status); + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + message->hcpriv = NULL; + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + free_urb(es1, urb); } diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 97fa2e0c3673..323721a2e2aa 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -94,6 +94,8 @@ struct es1_cport_out { * @cport_out_urb: array of urbs for the CPort out messages * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or * not. + * @cport_out_urb_cancelled: array of flags indicating whether the + * corresponding @cport_out_urb is being cancelled * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" */ struct es1_ap_dev { @@ -111,6 +113,7 @@ struct es1_ap_dev { struct es1_cport_out cport_out[NUM_BULKS]; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; + bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; spinlock_t cport_out_urb_lock; int cport_to_ep[CPORT_MAX]; @@ -224,7 +227,8 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) /* Look in our pool of allocated urbs first, as that's the "fastest" */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (es1->cport_out_urb_busy[i] == false) { + if (es1->cport_out_urb_busy[i] == false && + es1->cport_out_urb_cancelled[i] == false) { es1->cport_out_urb_busy[i] = true; urb = es1->cport_out_urb[i]; break; @@ -292,11 +296,10 @@ static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) } /* - * Returns an opaque cookie value if successful, or a pointer coded - * error otherwise. If the caller wishes to cancel the in-flight - * buffer, it must supply the returned cookie to the cancel routine. + * Returns zero if the message was successfully queued, or a negative errno + * otherwise. */ -static void *message_send(struct greybus_host_device *hd, u16 cport_id, +static int message_send(struct greybus_host_device *hd, u16 cport_id, struct gb_message *message, gfp_t gfp_mask) { struct es1_ap_dev *es1 = hd_to_es1(hd); @@ -305,6 +308,7 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, int retval; struct urb *urb; int bulk_ep_set; + unsigned long flags; /* * The data actually transferred will include an indication @@ -313,13 +317,17 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, */ if (!cport_id_valid(cport_id)) { pr_err("invalid destination cport 0x%02x\n", cport_id); - return ERR_PTR(-EINVAL); + return -EINVAL; } /* Find a free urb */ urb = next_free_urb(es1, gfp_mask); if (!urb) - return ERR_PTR(-ENOMEM); + return -ENOMEM; + + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + message->hcpriv = urb; + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); /* Pack the cport id into the message header */ gb_message_cport_pack(message->header, cport_id); @@ -335,30 +343,56 @@ static void *message_send(struct greybus_host_device *hd, u16 cport_id, retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); + + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + message->hcpriv = NULL; + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + free_urb(es1, urb); gb_message_cport_clear(message->header); - return ERR_PTR(retval); + + return retval; } - return urb; + return 0; } /* - * The cookie value supplied is the value that message_send() - * returned to its caller. It identifies the message that should be - * canceled. This function must also handle (which is to say, - * ignore) a null cookie value. + * Can not be called in atomic context. */ -static void message_cancel(void *cookie) +static void message_cancel(struct gb_message *message) { + struct greybus_host_device *hd = message->operation->connection->hd; + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct urb *urb; + int i; - /* - * We really should be defensive and track all outstanding - * (sent) messages rather than trusting the cookie provided - * is valid. For the time being, this will do. - */ - if (cookie) - usb_kill_urb(cookie); + might_sleep(); + + spin_lock_irq(&es1->cport_out_urb_lock); + urb = message->hcpriv; + + /* Prevent dynamically allocated urb from being deallocated. */ + usb_get_urb(urb); + + /* Prevent pre-allocated urb from being reused. */ + for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { + if (urb == es1->cport_out_urb[i]) { + es1->cport_out_urb_cancelled[i] = true; + break; + } + } + spin_unlock_irq(&es1->cport_out_urb_lock); + + usb_kill_urb(urb); + + if (i < NUM_CPORT_OUT_URB) { + spin_lock_irq(&es1->cport_out_urb_lock); + es1->cport_out_urb_cancelled[i] = false; + spin_unlock_irq(&es1->cport_out_urb_lock); + } + + usb_free_urb(urb); } static struct greybus_host_driver es1_driver = { @@ -518,6 +552,7 @@ static void cport_out_callback(struct urb *urb) struct greybus_host_device *hd = message->operation->connection->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); + unsigned long flags; gb_message_cport_clear(message->header); @@ -527,6 +562,10 @@ static void cport_out_callback(struct urb *urb) */ greybus_message_sent(hd, message, status); + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + message->hcpriv = NULL; + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + free_urb(es1, urb); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index c1157df9230b..e795016106c2 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -81,9 +81,9 @@ struct svc_msg; struct greybus_host_driver { size_t hd_priv_size; - void *(*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, + int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); - void (*message_cancel)(void *cookie); + void (*message_cancel)(struct gb_message *message); int (*submit_svc)(struct svc_msg *svc_msg, struct greybus_host_device *hd); }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index a713dafabf6e..b125bde32249 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -23,9 +23,6 @@ static struct kmem_cache *gb_message_cache; /* Workqueue to handle Greybus operation completions. */ static struct workqueue_struct *gb_operation_workqueue; -/* Protects the cookie representing whether a message is in flight */ -static DEFINE_MUTEX(gb_message_mutex); - /* * Protects access to connection operations lists, as well as * updates to operation->errno. @@ -135,21 +132,11 @@ gb_operation_find(struct gb_connection *connection, u16 operation_id) static int gb_message_send(struct gb_message *message) { struct gb_connection *connection = message->operation->connection; - int ret = 0; - void *cookie; - mutex_lock(&gb_message_mutex); - cookie = connection->hd->driver->message_send(connection->hd, + return connection->hd->driver->message_send(connection->hd, connection->hd_cport_id, message, GFP_KERNEL); - if (IS_ERR(cookie)) - ret = PTR_ERR(cookie); - else - message->cookie = cookie; - mutex_unlock(&gb_message_mutex); - - return ret; } /* @@ -157,14 +144,9 @@ static int gb_message_send(struct gb_message *message) */ static void gb_message_cancel(struct gb_message *message) { - mutex_lock(&gb_message_mutex); - if (message->cookie) { - struct greybus_host_device *hd; + struct greybus_host_device *hd = message->operation->connection->hd; - hd = message->operation->connection->hd; - hd->driver->message_cancel(message->cookie); - } - mutex_unlock(&gb_message_mutex); + hd->driver->message_cancel(message); } static void gb_operation_request_handle(struct gb_operation *operation) @@ -719,9 +701,6 @@ void greybus_message_sent(struct greybus_host_device *hd, { struct gb_operation *operation; - /* Get the message and record that it is no longer in flight */ - message->cookie = NULL; - /* * If the message was a response, we just need to drop our * reference to the operation. If an error occurred, report diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 0199976c9b5f..ad4574b4bfdf 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -73,19 +73,20 @@ struct gb_operation_msg_hdr { #define GB_OPERATION_MESSAGE_SIZE_MAX U16_MAX /* - * Protocol code should only examine the payload and payload_size - * fields. All other fields are intended to be private to the - * operations core code. + * Protocol code should only examine the payload and payload_size fields, and + * host-controller drivers may use the hcpriv field. All other fields are + * intended to be private to the operations core code. */ struct gb_message { struct gb_operation *operation; - void *cookie; struct gb_operation_msg_hdr *header; void *payload; size_t payload_size; void *buffer; + + void *hcpriv; }; /* -- cgit v1.2.3-59-g8ed1b From e420721b47ef5b0d521584d4efc89ff64bd0cd74 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:22 +0200 Subject: greybus: operation: allow atomic operation allocations Add gfp mask argument to gb_operation_create to allow operations to be allocated in atomic context. Signed-off-by: Johan Hovold Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 3 ++- drivers/staging/greybus/i2c.c | 2 +- drivers/staging/greybus/operation.c | 23 ++++++++--------------- drivers/staging/greybus/operation.h | 3 ++- drivers/staging/greybus/spi.c | 2 +- drivers/staging/greybus/usb.c | 3 ++- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 1214b7a0a631..a367fd5fad70 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -128,7 +128,8 @@ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, int ret, size = sizeof(*request) + len - 1; operation = gb_operation_create(ghid->connection, - GB_HID_TYPE_SET_REPORT, size, 0); + GB_HID_TYPE_SET_REPORT, size, 0, + GFP_KERNEL); if (!operation) return -ENOMEM; diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 5eb7703599d6..9514e69d0d4b 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -146,7 +146,7 @@ gb_i2c_operation_create(struct gb_connection *connection, /* Response consists only of incoming data */ operation = gb_operation_create(connection, GB_I2C_TYPE_TRANSFER, - request_size, data_in_size); + request_size, data_in_size, GFP_KERNEL); if (!operation) return NULL; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b125bde32249..4019b030e31c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -409,22 +409,13 @@ EXPORT_SYMBOL_GPL(gb_operation_response_alloc); */ static struct gb_operation * gb_operation_create_common(struct gb_connection *connection, u8 type, - size_t request_size, size_t response_size) + size_t request_size, size_t response_size, + gfp_t gfp_flags) { struct greybus_host_device *hd = connection->hd; struct gb_operation *operation; unsigned long flags; - gfp_t gfp_flags; - /* - * An incoming request will pass an invalid operation type, - * because the header will get overwritten anyway. These - * occur in interrupt context, so we must use GFP_ATOMIC. - */ - if (type == GB_OPERATION_TYPE_INVALID) - gfp_flags = GFP_ATOMIC; - else - gfp_flags = GFP_KERNEL; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) return NULL; @@ -472,7 +463,8 @@ err_cache: */ struct gb_operation *gb_operation_create(struct gb_connection *connection, u8 type, size_t request_size, - size_t response_size) + size_t response_size, + gfp_t gfp) { if (WARN_ON_ONCE(type == GB_OPERATION_TYPE_INVALID)) return NULL; @@ -480,7 +472,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, type &= ~GB_MESSAGE_TYPE_RESPONSE; return gb_operation_create_common(connection, type, - request_size, response_size); + request_size, response_size, gfp); } EXPORT_SYMBOL_GPL(gb_operation_create); @@ -504,7 +496,7 @@ gb_operation_create_incoming(struct gb_connection *connection, u16 id, operation = gb_operation_create_common(connection, GB_OPERATION_TYPE_INVALID, - request_size, 0); + request_size, 0, GFP_ATOMIC); if (operation) { operation->id = id; operation->type = type; @@ -888,7 +880,8 @@ int gb_operation_sync(struct gb_connection *connection, int type, return -EINVAL; operation = gb_operation_create(connection, type, - request_size, response_size); + request_size, response_size, + GFP_KERNEL); if (!operation) return -ENOMEM; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index ad4574b4bfdf..395664835eac 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -134,7 +134,8 @@ int gb_operation_result(struct gb_operation *operation); size_t gb_operation_get_payload_size_max(struct gb_connection *connection); struct gb_operation *gb_operation_create(struct gb_connection *connection, u8 type, size_t request_size, - size_t response_size); + size_t response_size, + gfp_t gfp); void gb_operation_get(struct gb_operation *operation); void gb_operation_put(struct gb_operation *operation); static inline void gb_operation_destroy(struct gb_operation *operation) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 374361889666..306fb074c183 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -90,7 +90,7 @@ gb_spi_operation_create(struct gb_connection *connection, /* Response consists only of incoming data */ operation = gb_operation_create(connection, GB_SPI_TYPE_TRANSFER, - request_size, rx_size); + request_size, rx_size, GFP_KERNEL); if (!operation) return NULL; diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 888f514921b6..e49fffdca53b 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -131,7 +131,8 @@ static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) operation = gb_operation_create(dev->connection, GB_USB_TYPE_URB_ENQUEUE, sizeof(*request) + - urb->transfer_buffer_length, 0); + urb->transfer_buffer_length, 0, + GFP_KERNEL); if (!operation) return -ENODEV; -- cgit v1.2.3-59-g8ed1b From a52c4352aef85538d194a9714fe742b18e6fb80f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:23 +0200 Subject: greybus: operation: allow atomic request submissions Add gfp mask argument to gb_operation_request_send in order to allow submissions from atomic context. Note that responses are currently always sent from non-atomic context as incoming requests are processed in a work queue. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 12 +++++++----- drivers/staging/greybus/operation.h | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 4019b030e31c..eee315c5861d 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -129,7 +129,7 @@ gb_operation_find(struct gb_connection *connection, u16 operation_id) return found ? operation : NULL; } -static int gb_message_send(struct gb_message *message) +static int gb_message_send(struct gb_message *message, gfp_t gfp) { struct gb_connection *connection = message->operation->connection; @@ -563,7 +563,8 @@ static void gb_operation_sync_callback(struct gb_operation *operation) * dropping the initial reference to the operation. */ int gb_operation_request_send(struct gb_operation *operation, - gb_operation_callback callback) + gb_operation_callback callback, + gfp_t gfp) { struct gb_connection *connection = operation->connection; struct gb_operation_msg_hdr *header; @@ -601,7 +602,7 @@ int gb_operation_request_send(struct gb_operation *operation, /* All set, send the request */ gb_operation_result_set(operation, -EINPROGRESS); - ret = gb_message_send(operation->request); + ret = gb_message_send(operation->request, gfp); if (ret) gb_operation_put(operation); @@ -620,7 +621,8 @@ int gb_operation_request_send_sync(struct gb_operation *operation) int ret; unsigned long timeout; - ret = gb_operation_request_send(operation, gb_operation_sync_callback); + ret = gb_operation_request_send(operation, gb_operation_sync_callback, + GFP_KERNEL); if (ret) return ret; @@ -677,7 +679,7 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) /* Fill in the response header and send it */ operation->response->header->result = gb_operation_errno_map(errno); - ret = gb_message_send(operation->response); + ret = gb_message_send(operation->response, GFP_KERNEL); if (ret) gb_operation_put(operation); diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 395664835eac..40632238845e 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -147,7 +147,8 @@ bool gb_operation_response_alloc(struct gb_operation *operation, size_t response_size); int gb_operation_request_send(struct gb_operation *operation, - gb_operation_callback callback); + gb_operation_callback callback, + gfp_t gfp); int gb_operation_request_send_sync(struct gb_operation *operation); int gb_operation_response_send(struct gb_operation *operation, int errno); -- cgit v1.2.3-59-g8ed1b From abb722e79a07b0a438a3782fd3f7d8c85f541d5f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:24 +0200 Subject: greybus: operation: make response helper static Since commit 46ce118a2678 ("operation: refactor response handling") sending operation responses is handled by greybus core so there is currently no need to export the response helper. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 7 +++++-- drivers/staging/greybus/operation.h | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index eee315c5861d..3392b425a6c2 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -29,6 +29,9 @@ static struct workqueue_struct *gb_operation_workqueue; */ static DEFINE_SPINLOCK(gb_operations_lock); +static int gb_operation_response_send(struct gb_operation *operation, + int errno); + /* * Set an operation's result. * @@ -649,7 +652,8 @@ EXPORT_SYMBOL_GPL(gb_operation_request_send_sync); * it can simply supply the result errno; this function will * allocate the response message if necessary. */ -int gb_operation_response_send(struct gb_operation *operation, int errno) +static int gb_operation_response_send(struct gb_operation *operation, + int errno) { struct gb_connection *connection = operation->connection; int ret; @@ -685,7 +689,6 @@ int gb_operation_response_send(struct gb_operation *operation, int errno) return ret; } -EXPORT_SYMBOL_GPL(gb_operation_response_send); /* * This function is called when a message send request has completed. diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 40632238845e..3bf757195fa3 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -150,7 +150,6 @@ int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback, gfp_t gfp); int gb_operation_request_send_sync(struct gb_operation *operation); -int gb_operation_response_send(struct gb_operation *operation, int errno); void gb_operation_cancel(struct gb_operation *operation, int errno); -- cgit v1.2.3-59-g8ed1b From 73f9d73f124ccba16403971b5101d4a947161481 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:25 +0200 Subject: greybus: operation: fix cancellation use-after-free The final reference of an operation will be put after its completion handler has run, so we must not drop the reference if it has already been scheduled to avoid use-after-free. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 3392b425a6c2..7adfa6346288 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -846,8 +846,8 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) gb_message_cancel(operation->request); if (operation->response) gb_message_cancel(operation->response); + gb_operation_put(operation); } - gb_operation_put(operation); } EXPORT_SYMBOL_GPL(gb_operation_cancel); -- cgit v1.2.3-59-g8ed1b From 710067e2ef42473c823fc8176ca9536b1a42c491 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:26 +0200 Subject: greybus: operation: add incoming-operation flag Add flag field to struct gb_operation, and a first flag GB_OPERATION_FLAG_INCOMING to identify incoming operations. Pass an initial set of flags when allocating new operations, and use these to identify incoming operations rather than overloading the meaning of GB_OPERATION_TYPE_INVALID. This also allows us to set the type for all operations during allocation. Also add convenience helper to identify incoming operations. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 17 +++++++++-------- drivers/staging/greybus/operation.h | 11 ++++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 7adfa6346288..ad0959985aa3 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -413,7 +413,7 @@ EXPORT_SYMBOL_GPL(gb_operation_response_alloc); static struct gb_operation * gb_operation_create_common(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size, - gfp_t gfp_flags) + unsigned long op_flags, gfp_t gfp_flags) { struct greybus_host_device *hd = connection->hd; struct gb_operation *operation; @@ -431,11 +431,13 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, operation->request->operation = operation; /* Allocate the response buffer for outgoing operations */ - if (type != GB_OPERATION_TYPE_INVALID) { + if (!(op_flags & GB_OPERATION_FLAG_INCOMING)) { if (!gb_operation_response_alloc(operation, response_size)) goto err_request; - operation->type = type; } + + operation->flags = op_flags; + operation->type = type; operation->errno = -EBADR; /* Initial value--means "never set" */ INIT_WORK(&operation->work, gb_operation_work); @@ -475,7 +477,7 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, type &= ~GB_MESSAGE_TYPE_RESPONSE; return gb_operation_create_common(connection, type, - request_size, response_size, gfp); + request_size, response_size, 0, gfp); } EXPORT_SYMBOL_GPL(gb_operation_create); @@ -493,16 +495,15 @@ gb_operation_create_incoming(struct gb_connection *connection, u16 id, { struct gb_operation *operation; size_t request_size; + unsigned long flags = GB_OPERATION_FLAG_INCOMING; /* Caller has made sure we at least have a message header. */ request_size = size - sizeof(struct gb_operation_msg_hdr); - operation = gb_operation_create_common(connection, - GB_OPERATION_TYPE_INVALID, - request_size, 0, GFP_ATOMIC); + operation = gb_operation_create_common(connection, type, + request_size, 0, flags, GFP_ATOMIC); if (operation) { operation->id = id; - operation->type = type; memcpy(operation->request->header, data, size); } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 3bf757195fa3..b6bbc8419a3a 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -89,6 +89,8 @@ struct gb_message { void *hcpriv; }; +#define GB_OPERATION_FLAG_INCOMING BIT(0) + /* * A Greybus operation is a remote procedure call performed over a * connection between two UniPro interfaces. @@ -113,8 +115,9 @@ struct gb_operation { struct gb_connection *connection; struct gb_message *request; struct gb_message *response; - u8 type; + unsigned long flags; + u8 type; u16 id; int errno; /* Operation result */ @@ -126,6 +129,12 @@ struct gb_operation { struct list_head links; /* connection->operations */ }; +static inline bool +gb_operation_is_incoming(struct gb_operation *operation) +{ + return operation->flags & GB_OPERATION_FLAG_INCOMING; +} + void gb_connection_recv(struct gb_connection *connection, void *data, size_t size); -- cgit v1.2.3-59-g8ed1b From e3398811c3c3b90501ca67c5de683953efe6cdde Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:27 +0200 Subject: greybus: operation: add unidirectional-operation flag Add flag to identify unidirectional operations. Use convenience helper rather than open coding the identification when suppressing response messages. Signed-off-by: Johan Hovold Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 ++++- drivers/staging/greybus/operation.h | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index ad0959985aa3..b32010ba7bc1 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -500,6 +500,9 @@ gb_operation_create_incoming(struct gb_connection *connection, u16 id, /* Caller has made sure we at least have a message header. */ request_size = size - sizeof(struct gb_operation_msg_hdr); + if (!id) + flags |= GB_OPERATION_FLAG_UNIDIRECTIONAL; + operation = gb_operation_create_common(connection, type, request_size, 0, flags, GFP_ATOMIC); if (operation) { @@ -666,7 +669,7 @@ static int gb_operation_response_send(struct gb_operation *operation, } /* Sender of request does not care about response. */ - if (!operation->id) + if (gb_operation_is_unidirectional(operation)) return 0; if (!operation->response) { diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index b6bbc8419a3a..07e43a771f8b 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -90,6 +90,7 @@ struct gb_message { }; #define GB_OPERATION_FLAG_INCOMING BIT(0) +#define GB_OPERATION_FLAG_UNIDIRECTIONAL BIT(1) /* * A Greybus operation is a remote procedure call performed over a @@ -135,6 +136,12 @@ gb_operation_is_incoming(struct gb_operation *operation) return operation->flags & GB_OPERATION_FLAG_INCOMING; } +static inline bool +gb_operation_is_unidirectional(struct gb_operation *operation) +{ + return operation->flags & GB_OPERATION_FLAG_UNIDIRECTIONAL; +} + void gb_connection_recv(struct gb_connection *connection, void *data, size_t size); -- cgit v1.2.3-59-g8ed1b From 2593261d5ca39b0f1afe926ffab907daeb246086 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:28 +0200 Subject: greybus: operation: remove obsolete operation-field comments Remove obsolete operation-field comments. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 07e43a771f8b..c16f64dd9784 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -123,8 +123,8 @@ struct gb_operation { int errno; /* Operation result */ struct work_struct work; - gb_operation_callback callback; /* If asynchronous */ - struct completion completion; /* Used if no callback */ + gb_operation_callback callback; + struct completion completion; struct kref kref; struct list_head links; /* connection->operations */ -- cgit v1.2.3-59-g8ed1b From 1635304d6384e258d197ca1bc5f2f6418eae28e2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:29 +0200 Subject: greybus: operation: drop redundant oom message Drop redundant OOM message, which would already have been logged by the memory subsystem. Also remove a FIXME about possibly sending a pre-allocated response, which is neither possible or desirable. If we ever run out of memory we have bigger problems then getting a response back to firmware. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b32010ba7bc1..d6e1db87e30a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -673,12 +673,8 @@ static int gb_operation_response_send(struct gb_operation *operation, return 0; if (!operation->response) { - if (!gb_operation_response_alloc(operation, 0)) { - dev_err(&connection->dev, - "error allocating response\n"); - /* XXX Respond with pre-allocated -ENOMEM? */ + if (!gb_operation_response_alloc(operation, 0)) return -ENOMEM; - } } /* Reference will be dropped when message has been sent. */ -- cgit v1.2.3-59-g8ed1b From fde7382b47c5fbb64be81420d267f1e314cfee94 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:30 +0200 Subject: greybus: operation: allocate response before setting result Make sure to allocate a response message before setting the operation result. This is needed to handle cancellation of incoming operations. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d6e1db87e30a..881dddc51abd 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -662,6 +662,12 @@ static int gb_operation_response_send(struct gb_operation *operation, struct gb_connection *connection = operation->connection; int ret; + if (!operation->response && + !gb_operation_is_unidirectional(operation)) { + if (!gb_operation_response_alloc(operation, 0)) + return -ENOMEM; + } + /* Record the result */ if (!gb_operation_result_set(operation, errno)) { dev_err(&connection->dev, "request result already set\n"); @@ -672,11 +678,6 @@ static int gb_operation_response_send(struct gb_operation *operation, if (gb_operation_is_unidirectional(operation)) return 0; - if (!operation->response) { - if (!gb_operation_response_alloc(operation, 0)) - return -ENOMEM; - } - /* Reference will be dropped when message has been sent. */ gb_operation_get(operation); -- cgit v1.2.3-59-g8ed1b From 188f9785cfc5b1dc306ef1ca36e4c8dd203ccf56 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Jul 2015 12:37:31 +0200 Subject: greybus: operation: fix cancellation of responses An operation with a response in-flight will already have set the operation result and would therefore never be cancelled by the current implementation. Note that the reference taken when sending the result will be dropped in the message completion callback. Also note that an incoming, non-unidirectional messages will always have an allocated response if its result has been set. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 881dddc51abd..9e2ff7dd278a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -845,9 +845,10 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) { if (gb_operation_result_set(operation, errno)) { gb_message_cancel(operation->request); - if (operation->response) - gb_message_cancel(operation->response); gb_operation_put(operation); + } else if (gb_operation_is_incoming(operation)) { + if (!gb_operation_is_unidirectional(operation)) + gb_message_cancel(operation->response); } } EXPORT_SYMBOL_GPL(gb_operation_cancel); -- cgit v1.2.3-59-g8ed1b From 3a97cddfe82b9e668d6fc7cd2e6ce7540ec06554 Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 3 Jul 2015 00:27:12 -0400 Subject: greybus: makefile: fix unused cflags The ccflags variable was spelled wrong, so no extra compilation flags could be specified. The proper flag is 'ccflags-y.' Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 4f66ff3a9713..163cd13e7545 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -66,7 +66,7 @@ $(foreach opt,$(CONFIG_OPTIONS_DISABLE),$(if $(filter m y, $(CONFIG_$(opt))), \ endif # add -Wall to try to catch everything we can. -ccFlags-y := -Wall +ccflags-y := -Wall all: module -- cgit v1.2.3-59-g8ed1b From 513926501dc0d43ce78fd72d00acc9be7fa8c405 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 2 Jul 2015 19:32:05 +0100 Subject: greybus: examples: add manifest file for sdio Add example manifest file for sdio. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- .../greybus/examples/IID1-simple-sdio-module.mnfs | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 drivers/staging/greybus/examples/IID1-simple-sdio-module.mnfs diff --git a/drivers/staging/greybus/examples/IID1-simple-sdio-module.mnfs b/drivers/staging/greybus/examples/IID1-simple-sdio-module.mnfs new file mode 100644 index 000000000000..10ec8b488768 --- /dev/null +++ b/drivers/staging/greybus/examples/IID1-simple-sdio-module.mnfs @@ -0,0 +1,53 @@ +; +; Simple GPIO Interface Manifest +; +; Copyright 2014 Google Inc. +; Copyright 2014 Linaro Ltd. +; +; Provided under the three clause BSD license found in the LICENSE file. +; + +[manifest-header] +version-major = 0 +version-minor = 1 + +[interface-descriptor] +vendor-string-id = 1 +product-string-id = 2 + +; Interface vendor string (id can't be 0) +[string-descriptor "1"] +id = 1 +string = Project Ara + +; Interface product string (id can't be 0) +[string-descriptor "2"] +id = 2 +string = Simple SDIO Interface + +; Control cport and bundle are optional. +; - Control cport's id must be 2 and its bundle number must be 0. +; - No other bundle or control cport may use these values. +; - Class and protocol of bundle and cport must be marked as 0x00. +; +;Control protocol on CPort 2 +[cport-descriptor "2"] +bundle = 0 +id = 2 +protocol = 0x00 + +;Control protocol Bundle 0 +[bundle-descriptor "0"] +class = 0 +id = 0 + +; GPIO protocol on CPort 1 +[cport-descriptor "1"] +bundle = 1 +id = 1 +protocol = 0x07 + +; Bundle 1 +[bundle-descriptor "1"] +class = 7 +id = 1 -- cgit v1.2.3-59-g8ed1b From df22363d2ada5ef4708310208ff10649adb0b08d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 5 Jul 2015 18:11:21 -0700 Subject: greybus: Revert "examples: add manifest file for sdio" This reverts commit 22690d72ae145cf12ae3df033670ed8ad7ecdde7. It was meant for the gbsim repo, not this one :( Signed-off-by: Greg Kroah-Hartman --- .../greybus/examples/IID1-simple-sdio-module.mnfs | 53 ---------------------- 1 file changed, 53 deletions(-) delete mode 100644 drivers/staging/greybus/examples/IID1-simple-sdio-module.mnfs diff --git a/drivers/staging/greybus/examples/IID1-simple-sdio-module.mnfs b/drivers/staging/greybus/examples/IID1-simple-sdio-module.mnfs deleted file mode 100644 index 10ec8b488768..000000000000 --- a/drivers/staging/greybus/examples/IID1-simple-sdio-module.mnfs +++ /dev/null @@ -1,53 +0,0 @@ -; -; Simple GPIO Interface Manifest -; -; Copyright 2014 Google Inc. -; Copyright 2014 Linaro Ltd. -; -; Provided under the three clause BSD license found in the LICENSE file. -; - -[manifest-header] -version-major = 0 -version-minor = 1 - -[interface-descriptor] -vendor-string-id = 1 -product-string-id = 2 - -; Interface vendor string (id can't be 0) -[string-descriptor "1"] -id = 1 -string = Project Ara - -; Interface product string (id can't be 0) -[string-descriptor "2"] -id = 2 -string = Simple SDIO Interface - -; Control cport and bundle are optional. -; - Control cport's id must be 2 and its bundle number must be 0. -; - No other bundle or control cport may use these values. -; - Class and protocol of bundle and cport must be marked as 0x00. -; -;Control protocol on CPort 2 -[cport-descriptor "2"] -bundle = 0 -id = 2 -protocol = 0x00 - -;Control protocol Bundle 0 -[bundle-descriptor "0"] -class = 0 -id = 0 - -; GPIO protocol on CPort 1 -[cport-descriptor "1"] -bundle = 1 -id = 1 -protocol = 0x07 - -; Bundle 1 -[bundle-descriptor "1"] -class = 7 -id = 1 -- cgit v1.2.3-59-g8ed1b From ef0cc0ec8d80a18c83aec41321ebeba639cfa09a Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 2 Jul 2015 19:11:30 +0100 Subject: greybus: sdio: split cmd_flags to there meaning Instead of using values in the command cmd_flags field use the real flags in a bit mask. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 9 +++++---- drivers/staging/greybus/sdio.c | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 6f8c15ea3900..498f6e9f44d2 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -769,10 +769,11 @@ struct gb_sdio_command_request { __u8 cmd; __u8 cmd_flags; #define GB_SDIO_RSP_NONE 0x00 -#define GB_SDIO_RSP_R1_R5_R6_R7 0x01 -#define GB_SDIO_RSP_R1B 0x02 -#define GB_SDIO_RSP_R2 0x03 -#define GB_SDIO_RSP_R3_R4 0x04 +#define GB_SDIO_RSP_PRESENT 0x01 +#define GB_SDIO_RSP_136 0x02 +#define GB_SDIO_RSP_CRC 0x04 +#define GB_SDIO_RSP_BUSY 0x08 +#define GB_SDIO_RSP_OPCODE 0x10 __u8 cmd_type; #define GB_SDIO_CMD_AC 0x00 diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index f028e14fc918..e842cae7a5bd 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -38,6 +38,14 @@ static struct workqueue_struct *gb_sdio_mrq_workqueue; /* Define get_version() routine */ define_get_version(gb_sdio_host, SDIO); +#define GB_SDIO_RSP_R1_R5_R6_R7 (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ + GB_SDIO_RSP_OPCODE) +#define GB_SDIO_RSP_R3_R4 (GB_SDIO_RSP_PRESENT) +#define GB_SDIO_RSP_R2 (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ + GB_SDIO_RSP_136) +#define GB_SDIO_RSP_R1B (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ + GB_SDIO_RSP_OPCODE | GB_SDIO_RSP_BUSY) + static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) { u32 caps = 0; -- cgit v1.2.3-59-g8ed1b From c36d31cbc57635f7e66176d84d7b8688796a01d3 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 2 Jul 2015 19:11:31 +0100 Subject: greybus: sdio: rework of event handler Between the time connection with module is up and the host is added, we can receive events (card inserted/removed, write protection switch), so until the setup is complete we queue the events received and handle them after. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 86 +++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index e842cae7a5bd..53cb46f6f7c8 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -28,6 +28,7 @@ struct gb_sdio_host { spinlock_t xfer; /* lock to cancel ongoing transfer */ bool xfer_stop; struct work_struct mrqwork; + u8 queued_events; bool removed; bool card_present; bool read_only; @@ -121,54 +122,40 @@ static int gb_sdio_get_caps(struct gb_sdio_host *host) return 0; } -static int gb_sdio_event_recv(u8 type, struct gb_operation *op) +static void _gb_queue_event(struct gb_sdio_host *host, u8 event) { - struct gb_connection *connection = op->connection; - struct gb_sdio_host *host = connection->private; - struct gb_message *request; - struct gb_sdio_event_request *payload; - u8 state_changed = 0; - u8 event; - - if (type != GB_SDIO_TYPE_EVENT) { - dev_err(&connection->dev, - "unsupported unsolicited event: %u\n", type); - return -EINVAL; - } - - request = op->request; + if (event & GB_SDIO_CARD_INSERTED) + host->queued_events &= ~GB_SDIO_CARD_REMOVED; + else if (event & GB_SDIO_CARD_REMOVED) + host->queued_events &= ~GB_SDIO_CARD_INSERTED; - if (request->payload_size != sizeof(*payload)) { - dev_err(mmc_dev(host->mmc), "wrong event size received\n"); - return -EINVAL; - } + host->queued_events |= event; +} - payload = request->payload; - event = payload->event; +static int _gb_sdio_process_events(struct gb_sdio_host *host, u8 event) +{ + u8 state_changed = 0; - switch (event) { - case GB_SDIO_CARD_INSERTED: + if (event & GB_SDIO_CARD_INSERTED) { if (!mmc_card_is_removable(host->mmc)) return 0; if (host->card_present) return 0; host->card_present = true; state_changed = 1; - break; - case GB_SDIO_CARD_REMOVED: + } + + if (event & GB_SDIO_CARD_REMOVED) { if (!mmc_card_is_removable(host->mmc)) return 0; if (!(host->card_present)) return 0; host->card_present = false; state_changed = 1; - break; - case GB_SDIO_WP: + } + + if (event & GB_SDIO_WP) { host->read_only = true; - break; - default: - dev_err(mmc_dev(host->mmc), "wrong event received %d\n", event); - return -EINVAL; } if (state_changed) { @@ -180,6 +167,39 @@ static int gb_sdio_event_recv(u8 type, struct gb_operation *op) return 0; } +static int gb_sdio_event_recv(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_sdio_host *host = connection->private; + struct gb_message *request; + struct gb_sdio_event_request *payload; + int ret = 0; + u8 event; + + if (type != GB_SDIO_TYPE_EVENT) { + dev_err(&connection->dev, + "unsupported unsolicited event: %u\n", type); + return -EINVAL; + } + + request = op->request; + + if (request->payload_size != sizeof(*payload)) { + dev_err(mmc_dev(host->mmc), "wrong event size received\n"); + return -EINVAL; + } + + payload = request->payload; + event = payload->event; + + if (host->removed) + _gb_queue_event(host, event); + else + ret = _gb_sdio_process_events(host, event); + + return ret; +} + static int gb_sdio_set_ios(struct gb_sdio_host *host, struct gb_sdio_set_ios_request *request) { @@ -649,6 +669,7 @@ static int gb_sdio_connection_init(struct gb_connection *connection) host = mmc_priv(mmc); host->mmc = mmc; + host->removed = true; host->connection = connection; connection->private = host; @@ -683,6 +704,9 @@ static int gb_sdio_connection_init(struct gb_connection *connection) ret = mmc_add_host(mmc); if (ret < 0) goto free_work; + host->removed = false; + ret = _gb_sdio_process_events(host, host->queued_events); + host->queued_events = 0; return ret; -- cgit v1.2.3-59-g8ed1b From 08ccc9be680debdeb9eb2dcaa83c5fc7cd71f487 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 2 Jul 2015 19:11:32 +0100 Subject: greybus: sdio: fix return of get_cd and get_ro Functions were returning the wrong flag for the expected value. Swap them. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 53cb46f6f7c8..f6adf0908d2e 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -635,7 +635,7 @@ static int gb_mmc_get_ro(struct mmc_host *mmc) if (host->removed) return -ESHUTDOWN; mutex_unlock(&host->lock); - return host->card_present; + return host->read_only; } static int gb_mmc_get_cd(struct mmc_host *mmc) @@ -646,7 +646,7 @@ static int gb_mmc_get_cd(struct mmc_host *mmc) if (host->removed) return -ESHUTDOWN; mutex_unlock(&host->lock); - return host->read_only; + return host->card_present; } static const struct mmc_host_ops gb_sdio_ops = { -- cgit v1.2.3-59-g8ed1b From 7a5cd5aea29a6fa0d99fb3b667b96b8d5f4e596c Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 2 Jul 2015 19:11:33 +0100 Subject: greybus: sdio: fix call to stop command if no data exist If data is not available the stop command could dereference NULL. Fetch the stop command directly from the request instead. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index f6adf0908d2e..8dd833eb9301 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -443,8 +443,8 @@ static void gb_sdio_mrq_work(struct work_struct *work) goto done; } - if (mrq->data->stop) { - ret = gb_sdio_command(host, mrq->data->stop); + if (mrq->stop) { + ret = gb_sdio_command(host, mrq->stop); if (ret < 0) goto done; } -- cgit v1.2.3-59-g8ed1b From 882edf59fcfa400547bdb509e67f2266471f838d Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 2 Jul 2015 19:11:34 +0100 Subject: greybus: sdio: pass only data pointer to tranfer funtion No need to pass the all request to the transfer related funtctions. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 8dd833eb9301..8e7b7ba341dc 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -285,9 +285,8 @@ static int _gb_sdio_recv(struct gb_sdio_host *host, struct mmc_data *data, return 0; } -static int gb_sdio_transfer(struct gb_sdio_host *host, struct mmc_request *mrq) +static int gb_sdio_transfer(struct gb_sdio_host *host, struct mmc_data *data) { - struct mmc_data *data = mrq->data; size_t left, len; off_t skip = 0; int ret = 0; @@ -438,7 +437,7 @@ static void gb_sdio_mrq_work(struct work_struct *work) goto done; if (mrq->data) { - ret = gb_sdio_transfer(host, host->mrq); + ret = gb_sdio_transfer(host, host->mrq->data); if (ret < 0) goto done; } -- cgit v1.2.3-59-g8ed1b From 73f4a5217968d51d9685732512d268c71b78a347 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 2 Jul 2015 19:11:35 +0100 Subject: greybus: sdio: check number of blocks in transfer Before initiating a transfers, check if the command (for single block) match the number of blocks in the request. While at it, fix also a missing break. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 8e7b7ba341dc..544dbda82460 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -47,6 +47,14 @@ define_get_version(gb_sdio_host, SDIO); #define GB_SDIO_RSP_R1B (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ GB_SDIO_RSP_OPCODE | GB_SDIO_RSP_BUSY) +static inline bool single_op(struct mmc_command *cmd) +{ + uint32_t opcode = cmd->opcode; + + return opcode == MMC_WRITE_BLOCK || + opcode == MMC_READ_SINGLE_BLOCK; +} + static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) { u32 caps = 0; @@ -292,6 +300,11 @@ static int gb_sdio_transfer(struct gb_sdio_host *host, struct mmc_data *data) int ret = 0; u16 nblocks; + if (single_op(data->mrq->cmd) && data->blocks > 1) { + ret = -ETIMEDOUT; + goto out; + } + left = data->blksz * data->blocks; while (left) { @@ -351,6 +364,7 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd) break; case MMC_RSP_R3: cmd_flags = GB_SDIO_RSP_R3_R4; + break; default: dev_err(mmc_dev(host->mmc), "cmd flag invalid %04x\n", mmc_resp_type(cmd)); -- cgit v1.2.3-59-g8ed1b From f85451d8f58cd6df8643838b249016449f4d6218 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 2 Jul 2015 19:11:36 +0100 Subject: greybus: sdio: add need poll to host caps As we do not have, yet, a event callback to notify core about changes we add the MMC_CAP_NEEDS_POLL capability to the rest of the caps received from the module. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 544dbda82460..33d5fe69ed63 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -85,7 +85,7 @@ static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) #endif (r & GB_SDIO_CAP_HS200_1_8V ? MMC_CAP2_HS200_1_8V_SDR : 0); - host->mmc->caps = caps; + host->mmc->caps = caps | MMC_CAP_NEEDS_POLL; host->mmc->caps2 = caps2; if (caps & MMC_CAP_NONREMOVABLE) -- cgit v1.2.3-59-g8ed1b From 9ddf133371fd87ddf543e8b26389ca26f2d9fc3c Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 2 Jul 2015 19:11:37 +0100 Subject: greybus: sdio: fix transfer buffer handling and blocks counting Fix copy to/from scatterlist destination buffer offset, fix calculation of blocks to be transfer and make a more verbose out of error when the blocks receive/send do not match. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 33d5fe69ed63..a709bd64f824 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -234,22 +234,25 @@ static int _gb_sdio_send(struct gb_sdio_host *host, struct mmc_data *data, request->data_blocks = cpu_to_le16(nblocks); request->data_blksz = cpu_to_le16(data->blksz); - copied = sg_pcopy_to_buffer(sg, sg_len, &request->data[0] + skip, len, - skip); + copied = sg_pcopy_to_buffer(sg, sg_len, &request->data[0], len, skip); if (copied != len) return -EINVAL; ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_TRANSFER, - request, len, &response, sizeof(response)); + request, len + sizeof(*request), + &response, sizeof(response)); if (ret < 0) return ret; send_blocks = le16_to_cpu(response.data_blocks); send_blksz = le16_to_cpu(response.data_blksz); - if (len != send_blksz * send_blocks) + if (len != send_blksz * send_blocks) { + dev_err(mmc_dev(host->mmc), "send: size received: %zu != %d\n", + len, send_blksz * send_blocks); return -EINVAL; + } return ret; } @@ -275,18 +278,22 @@ static int _gb_sdio_recv(struct gb_sdio_host *host, struct mmc_data *data, response = host->xfer_buffer; ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_TRANSFER, - &request, sizeof(request), response, len); + &request, sizeof(request), response, len + + sizeof(*response)); if (ret < 0) return ret; recv_blocks = le16_to_cpu(response->data_blocks); recv_blksz = le16_to_cpu(response->data_blksz); - if (len != recv_blksz * recv_blocks) + if (len != recv_blksz * recv_blocks) { + dev_err(mmc_dev(host->mmc), "recv: size received: %d != %zu\n", + recv_blksz * recv_blocks, len); return -EINVAL; + } - copied = sg_pcopy_from_buffer(sg, sg_len, &response->data[0] + skip, - len, skip); + copied = sg_pcopy_from_buffer(sg, sg_len, &response->data[0], len, + skip); if (copied != len) return -EINVAL; @@ -318,7 +325,7 @@ static int gb_sdio_transfer(struct gb_sdio_host *host, struct mmc_data *data) } spin_unlock(&host->xfer); len = min(left, host->data_max); - nblocks = do_div(len, data->blksz); + nblocks = len / data->blksz; len = nblocks * data->blksz; if (data->flags & MMC_DATA_READ) { -- cgit v1.2.3-59-g8ed1b From 6fa1adbcdd28f27b639954e5bb202a2de281679a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 3 Jul 2015 17:00:25 +0530 Subject: greybus: connection: Exit connection before destroying it gb_connection_create() can initialize a connection if bundle->device id is valid. And so the connection must be destroyed by calling gb_connection_exit() before gb_connection_destroy(). This wasn't done in the code that is parsing the manifest. Fix it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index ce4e89c6d13e..bea565f1c1d1 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -255,6 +255,7 @@ cleanup: /* An error occurred; undo any changes we've made */ list_for_each_entry_safe(connection, connection_next, &bundle->connections, bundle_links) { + gb_connection_exit(connection); gb_connection_destroy(connection); count--; } -- cgit v1.2.3-59-g8ed1b From b950dc28bd47949fed64afa28c01c90be5dca43b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 3 Jul 2015 17:00:26 +0530 Subject: greybus: interface: Fix comment mistake (s/add/init) Function's name is gb_interface_init(), not gb_interface_add(). Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index f9fd479bfd8b..1e5e6290913f 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -189,7 +189,7 @@ static void gb_interface_destroy(struct gb_interface *intf) } /** - * gb_interface_add + * gb_interface_init * * Create connection for control CPort and then request/parse manifest. * Finally initialize all the bundles to set routes via SVC and initialize all -- cgit v1.2.3-59-g8ed1b From 87c016ee908b8727f8d812bf3a36844be20d3410 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 3 Jul 2015 17:00:27 +0530 Subject: greybus: connection: Use connection->dev for printing messages We failed to bind a protocol for the connection, not for bundle. And so connection's 'dev' must be used for printing message. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2e306d13ac28..6994a84439f8 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -209,7 +209,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, /* XXX Will have to establish connections to get version */ gb_connection_bind_protocol(connection); if (!connection->protocol) - dev_warn(&bundle->dev, + dev_warn(&connection->dev, "protocol 0x%02hhx handler not found\n", protocol_id); return connection; -- cgit v1.2.3-59-g8ed1b From ab69c4cea3b4ddf6bce4db810db37a3911b08e15 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 3 Jul 2015 17:00:29 +0530 Subject: greybus: svc: SVC is part of greybus core Its an essential part of greybus core and shouldn't be present as a separate module. Make it part of greybus.ko module. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 3 +-- drivers/staging/greybus/core.c | 9 +++++++++ drivers/staging/greybus/svc.c | 3 +-- drivers/staging/greybus/svc.h | 2 ++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 163cd13e7545..1467c5b3fcd8 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -9,6 +9,7 @@ greybus-y := core.o \ connection.o \ protocol.o \ control.o \ + svc.o \ operation.o gb-phy-y := gpbridge.o \ @@ -26,7 +27,6 @@ gb-phy-y := gpbridge.o \ audio-gb-cmds.o # Prefix all modules with gb- -gb-svc-y := svc.o gb-vibrator-y := vibrator.o gb-battery-y := battery.o gb-loopback-y := loopback.o @@ -35,7 +35,6 @@ gb-es1-y := es1.o gb-es2-y := es2.o obj-m += greybus.o -obj-m += gb-svc.o obj-m += gb-phy.o obj-m += gb-vibrator.o obj-m += gb-battery.o diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 8d16e10b7e94..925e1dc00eff 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -280,8 +280,16 @@ static int __init gb_init(void) goto error_control; } + retval = gb_svc_protocol_init(); + if (retval) { + pr_err("gb_svc_protocol_init failed\n"); + goto error_svc; + } + return 0; /* Success */ +error_svc: + gb_control_protocol_exit(); error_control: gb_endo_exit(); error_endo: @@ -299,6 +307,7 @@ module_init(gb_init); static void __exit gb_exit(void) { + gb_svc_protocol_exit(); gb_control_protocol_exit(); gb_endo_exit(); gb_operation_exit(); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 75b333783941..8a5a9b7c375d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -239,5 +239,4 @@ static struct gb_protocol svc_protocol = { .connection_exit = gb_svc_connection_exit, .request_recv = gb_svc_request_recv, }; - -gb_protocol_driver(&svc_protocol); +gb_builtin_protocol_driver(svc_protocol); diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index e26e63707e3d..b039aea4c409 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -19,4 +19,6 @@ int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, int gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); +int gb_svc_protocol_init(void); +void gb_svc_protocol_exit(void); #endif /* __SVC_H */ -- cgit v1.2.3-59-g8ed1b From 6ceb8fdeaec665642de944ee47d11440e72a5e26 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 3 Jul 2015 17:00:30 +0530 Subject: greybus: svc: save pointer to struct gb_svc in struct gb_interface Its another special protocol (just like control protocol) and is required to be accessed from other files, lets save a pointer to it in interface structure. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/svc.c | 13 ++++++------- drivers/staging/greybus/svc.h | 7 ++++++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 86eb8947a3d6..c2bcb92dacdb 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -14,6 +14,7 @@ struct gb_interface { struct device dev; struct gb_control *control; + struct gb_svc *svc; struct list_head bundles; struct list_head links; /* greybus_host_device->interfaces */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 8a5a9b7c375d..ce789c909c5f 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -9,12 +9,6 @@ #include "greybus.h" -struct gb_svc { - struct gb_connection *connection; - u8 version_major; - u8 version_minor; -}; - /* Define get_version() routine */ define_get_version(gb_svc, SVC); @@ -217,6 +211,9 @@ static int gb_svc_connection_init(struct gb_connection *connection) if (ret) kfree(svc); + /* Set interface's svc connection */ + connection->bundle->intf->svc = svc; + return ret; } @@ -224,9 +221,11 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; - if (!svc) + if (WARN_ON(connection->bundle->intf->svc != svc)) return; + connection->bundle->intf->svc = NULL; + kfree(svc); } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index b039aea4c409..ebabe5ff7c6d 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -10,7 +10,12 @@ #ifndef __SVC_H #define __SVC_H -struct gb_svc; +struct gb_svc { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; +}; + int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); -- cgit v1.2.3-59-g8ed1b From 5c1ac6945526c76258869c8c04632ab5ae61bdab Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Jul 2015 10:44:09 -0700 Subject: greybus: properly cleanup ida and idr structures when shutting down idr and ida structures have internal memory allocated that needs to be freed when modules are removed. So call the proper idr_destroy() or ida_destroy() functions on the module exit path to free the memory. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/raw.c | 1 + drivers/staging/greybus/uart.c | 1 + drivers/staging/greybus/vibrator.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index d93d052a8a7e..3be96db2588b 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -364,6 +364,7 @@ static void __exit raw_exit(void) gb_protocol_deregister(&raw_protocol); unregister_chrdev_region(MKDEV(raw_major, 0), NUM_MINORS); class_destroy(raw_class); + ida_destroy(&minors); } module_exit(raw_exit); diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index e2a456f8105c..3932f8667d31 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -761,6 +761,7 @@ static void gb_tty_exit(void) { tty_unregister_driver(gb_tty_driver); put_tty_driver(gb_tty_driver); + idr_destroy(&tty_minors); } static struct gb_protocol uart_protocol = { diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 20f09bba5fac..62b3552006fc 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -190,6 +190,7 @@ static __exit void protocol_exit(void) { gb_protocol_deregister(&vibrator_protocol); class_unregister(&vibrator_class); + ida_destroy(&minors); } module_exit(protocol_exit); -- cgit v1.2.3-59-g8ed1b From 85109f7ddde4b1842bfb08741a8b461e31ef2c4f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 9 Jul 2015 15:17:58 +0200 Subject: greybus: operation: fix operation-destroy race Make sure to acquire the connection-list lock atomically when releasing the final reference. This allows the list to be traversed and references to be acquired (while holding the lock) without racing with the destructor. Suggested-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 9e2ff7dd278a..f8d7df9ad3c4 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -528,14 +528,12 @@ EXPORT_SYMBOL_GPL(gb_operation_get); static void _gb_operation_destroy(struct kref *kref) { struct gb_operation *operation; - unsigned long flags; operation = container_of(kref, struct gb_operation, kref); /* XXX Make sure it's not in flight */ - spin_lock_irqsave(&gb_operations_lock, flags); list_del(&operation->links); - spin_unlock_irqrestore(&gb_operations_lock, flags); + spin_unlock(&gb_operations_lock); if (operation->response) gb_operation_message_free(operation->response); @@ -550,8 +548,11 @@ static void _gb_operation_destroy(struct kref *kref) */ void gb_operation_put(struct gb_operation *operation) { - if (!WARN_ON(!operation)) - kref_put(&operation->kref, _gb_operation_destroy); + if (WARN_ON(!operation)) + return; + + kref_put_spinlock_irqsave(&operation->kref, _gb_operation_destroy, + &gb_operations_lock); } EXPORT_SYMBOL_GPL(gb_operation_put); -- cgit v1.2.3-59-g8ed1b From 0581f28efb86d8eb7e7f6baf712578477f7c868e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 9 Jul 2015 15:17:59 +0200 Subject: greybus: operation: fix use-after-free in response receive path Fix potential use-after-free in response receive path, due to lack of reference counting when looking up operations on a connection. Make sure to acquire a reference to the operation while holding the connection-list lock. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f8d7df9ad3c4..8f99c8e17d70 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -114,6 +114,10 @@ int gb_operation_result(struct gb_operation *operation) } EXPORT_SYMBOL_GPL(gb_operation_result); +/* + * Looks up an operation on a connection and returns a refcounted pointer if + * found, or NULL otherwise. + */ static struct gb_operation * gb_operation_find(struct gb_connection *connection, u16 operation_id) { @@ -124,6 +128,7 @@ gb_operation_find(struct gb_connection *connection, u16 operation_id) spin_lock_irqsave(&gb_operations_lock, flags); list_for_each_entry(operation, &connection->operations, links) if (operation->id == operation_id) { + gb_operation_get(operation); found = true; break; } @@ -795,6 +800,8 @@ static void gb_connection_recv_response(struct gb_connection *connection, /* The rest will be handled in work queue context */ if (gb_operation_result_set(operation, errno)) queue_work(gb_operation_workqueue, &operation->work); + + gb_operation_put(operation); } /* -- cgit v1.2.3-59-g8ed1b From 048a7ffe274280c27fc572ba3ec7150eba6efc40 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 9 Jul 2015 15:18:00 +0200 Subject: greybus: operation: fix outgoing-response corruption Fix potential corruption of outgoing responses by verifying that the operations is indeed outgoing when receiving a response. Failure to do so could lead to an incoming response corrupting an outgoing response that uses the same operation id. Reported-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 8f99c8e17d70..85394624395e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -115,11 +115,11 @@ int gb_operation_result(struct gb_operation *operation) EXPORT_SYMBOL_GPL(gb_operation_result); /* - * Looks up an operation on a connection and returns a refcounted pointer if - * found, or NULL otherwise. + * Looks up an outgoing operation on a connection and returns a refcounted + * pointer if found, or NULL otherwise. */ static struct gb_operation * -gb_operation_find(struct gb_connection *connection, u16 operation_id) +gb_operation_find_outgoing(struct gb_connection *connection, u16 operation_id) { struct gb_operation *operation; unsigned long flags; @@ -127,7 +127,8 @@ gb_operation_find(struct gb_connection *connection, u16 operation_id) spin_lock_irqsave(&gb_operations_lock, flags); list_for_each_entry(operation, &connection->operations, links) - if (operation->id == operation_id) { + if (operation->id == operation_id && + !gb_operation_is_incoming(operation)) { gb_operation_get(operation); found = true; break; @@ -778,7 +779,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, int errno = gb_operation_status_map(result); size_t message_size; - operation = gb_operation_find(connection, operation_id); + operation = gb_operation_find_outgoing(connection, operation_id); if (!operation) { dev_err(&connection->dev, "operation not found\n"); return; -- cgit v1.2.3-59-g8ed1b From e4340b13fd2cf9bcf1f0a40ae15de454ffdb74f8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 9 Jul 2015 15:18:01 +0200 Subject: greybus: operation: fix incoming-response corruption Make sure not to update the response message buffer for an operation that is already scheduled for completion. Currently if we get two incoming responses with the same id, the second one would overwrite the response message buffer. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 85394624395e..b78c55fac8cc 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -796,11 +796,12 @@ static void gb_connection_recv_response(struct gb_connection *connection, /* We must ignore the payload if a bad status is returned */ if (errno) size = sizeof(*message->header); - memcpy(message->header, data, size); /* The rest will be handled in work queue context */ - if (gb_operation_result_set(operation, errno)) + if (gb_operation_result_set(operation, errno)) { + memcpy(message->header, data, size); queue_work(gb_operation_workqueue, &operation->work); + } gb_operation_put(operation); } -- cgit v1.2.3-59-g8ed1b From e99f305bb7cc3e22c508440d2a52e8c84c9b507a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 9 Jul 2015 10:56:30 +0530 Subject: greybus: Cleanup connection leftovers This wouldn't happen normally, but in a buggy corner case it is possible that all the connections aren't removed properly and they are still present after the interfaces and endo are removed. Warn in such a case and cleanup connections, so that /sys/bus/greybus/ can be removed while removing greybus modules. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 10 ++++++++++ drivers/staging/greybus/connection.h | 1 + drivers/staging/greybus/core.c | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 6994a84439f8..65a2bd5891d9 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -313,3 +313,13 @@ void gb_connection_exit(struct gb_connection *connection) cport_id, ret); } } + +void gb_hd_connections_exit(struct greybus_host_device *hd) +{ + struct gb_connection *connection; + + list_for_each_entry(connection, &hd->connections, hd_links) { + gb_connection_exit(connection); + gb_connection_destroy(connection); + } +} diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index acf91835486d..ad699db0dfd8 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -50,6 +50,7 @@ void gb_connection_destroy(struct gb_connection *connection); int gb_connection_init(struct gb_connection *connection); void gb_connection_exit(struct gb_connection *connection); +void gb_hd_connections_exit(struct greybus_host_device *hd); void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 925e1dc00eff..3b1be2dd2359 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -235,6 +235,13 @@ void greybus_remove_hd(struct greybus_host_device *hd) */ gb_interfaces_remove(hd); gb_endo_remove(hd->endo); + + /* + * Make sure there are no leftovers that can potentially corrupt sysfs. + */ + if (WARN_ON(!list_empty(&hd->connections))) + gb_hd_connections_exit(hd); + kref_put_mutex(&hd->kref, free_hd, &hd_mutex); } EXPORT_SYMBOL_GPL(greybus_remove_hd); -- cgit v1.2.3-59-g8ed1b From f9f971a2e5b5094cf12480976fb1765376414eac Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 13 Jul 2015 20:20:45 +0100 Subject: greybus: loopback: Move loopback operation definitions In order to have one point of type definition for gbsim move the loopback operation definitions to greybus_protocols.h. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 21 +++++++++++++++++++++ drivers/staging/greybus/loopback.c | 20 -------------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 498f6e9f44d2..9823d16c3b40 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -657,6 +657,27 @@ struct gb_uart_serial_state_request { __u8 control; }; +/* Loopback */ + +/* Version of the Greybus loopback protocol we support */ +#define GB_LOOPBACK_VERSION_MAJOR 0x00 +#define GB_LOOPBACK_VERSION_MINOR 0x01 + +/* Greybus loopback request types */ +#define GB_LOOPBACK_TYPE_INVALID 0x00 +#define GB_LOOPBACK_TYPE_PROTOCOL_VERSION 0x01 +#define GB_LOOPBACK_TYPE_PING 0x02 +#define GB_LOOPBACK_TYPE_TRANSFER 0x03 + +struct gb_loopback_transfer_request { + __le32 len; + __u8 data[0]; +}; + +struct gb_loopback_transfer_response { + __u8 data[0]; +}; + /* SDIO */ /* Version of the Greybus sdio protocol we support */ #define GB_SDIO_VERSION_MAJOR 0x00 diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index eeec3323ad6d..59c437bdd58c 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -45,16 +45,6 @@ struct gb_loopback { u32 error; }; -/* Version of the Greybus loopback protocol we support */ -#define GB_LOOPBACK_VERSION_MAJOR 0x00 -#define GB_LOOPBACK_VERSION_MINOR 0x01 - -/* Greybus loopback request types */ -#define GB_LOOPBACK_TYPE_INVALID 0x00 -#define GB_LOOPBACK_TYPE_PROTOCOL_VERSION 0x01 -#define GB_LOOPBACK_TYPE_PING 0x02 -#define GB_LOOPBACK_TYPE_TRANSFER 0x03 - /* Current function (type of traffic generated by the loopback thread) */ #define GB_LOOPBACK_FN_NONE 0x00 #define GB_LOOPBACK_FN_PING 0x01 @@ -175,16 +165,6 @@ static struct attribute *loopback_attrs[] = { }; ATTRIBUTE_GROUPS(loopback); -struct gb_loopback_transfer_request { - __le32 len; - __u8 data[0]; -}; - -struct gb_loopback_transfer_response { - __u8 data[0]; -}; - - static int gb_loopback_transfer(struct gb_loopback *gb, struct timeval *tping, u32 len) { -- cgit v1.2.3-59-g8ed1b From a0ee3d3ea6c1b8d731ca68c78db53035862ab3ae Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 13 Jul 2015 20:20:46 +0100 Subject: greybus: loopback: remove magic number in state-machine Magic number 2 used instead of define GB_LOOPBACK_TYPE_PING, remove and and use the define instead. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 59c437bdd58c..9ede48058666 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -356,7 +356,7 @@ static int gb_loopback_fn(void *data) gb->elapsed_nsecs = timeval_to_ns(&gb->te) - timeval_to_ns(&gb->ts); gb_loopback_freq_update(gb); - if (gb->type == 2) + if (gb->type == GB_LOOPBACK_TYPE_PING) gb_loopback_bw_update(gb, error); gb_loopback_latency_update(gb, &tlat); if (gb->elapsed_nsecs >= NSEC_PER_SEC) -- cgit v1.2.3-59-g8ed1b From 74a240a029fd0fa692bd857f15ceb6eda4897c5d Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 13 Jul 2015 20:20:47 +0100 Subject: greybus: loopback: remove spurious pr_err in sysfs store Dangling debug code removed. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 9ede48058666..6cd4acf93bf6 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -107,7 +107,6 @@ static ssize_t field##_store(struct device *dev, \ struct gb_loopback *gb = \ (struct gb_loopback *)connection->private; \ ret = sscanf(buf, "%"#type, &gb->field); \ - pr_err("%s = %"#type"\n", #field, gb->field); \ if (ret != 1) \ return -EINVAL; \ gb_loopback_check_attr(gb); \ -- cgit v1.2.3-59-g8ed1b From a598f4384d9e95532781adc316793983ed27139d Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 13 Jul 2015 20:20:48 +0100 Subject: greybus: loopback: make loopback type input equivalent to protocol type Sepcifying loopback operation type with a type value that is internal to the loopback driver is non-intunitive and requires reading code to understand. Remove confusing duplicate definitions and update code to accept the greybus-specification function identity defintiions as the appropriate type values for initiating loopback operations. See greybus-spec section 10.16.1 'Greybus Loopback Message Types' for a full list of valid messages to set this type field to. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 6cd4acf93bf6..bdbebd6dbede 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -45,12 +45,6 @@ struct gb_loopback { u32 error; }; -/* Current function (type of traffic generated by the loopback thread) */ -#define GB_LOOPBACK_FN_NONE 0x00 -#define GB_LOOPBACK_FN_PING 0x01 -#define GB_LOOPBACK_FN_XFER 0x02 -#define GB_LOOPBACK_FN_COUNT 0x03 - #define GB_LOOPBACK_MS_WAIT_MAX 1000 #define GB_LOOPBACK_SIZE_MAX SZ_4K @@ -117,10 +111,16 @@ static DEVICE_ATTR_RW(field) static void gb_loopback_reset_stats(struct gb_loopback *gb); static void gb_loopback_check_attr(struct gb_loopback *gb) { + switch (gb->type) { + case GB_LOOPBACK_TYPE_PING: + case GB_LOOPBACK_TYPE_TRANSFER: + break; + default: + gb->type = 0; + break; + } if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; - if (gb->type >= GB_LOOPBACK_FN_COUNT) - gb->type = GB_LOOPBACK_FN_NONE; if (gb->size > GB_LOOPBACK_SIZE_MAX) gb->size = GB_LOOPBACK_SIZE_MAX; gb->error = 0; @@ -337,13 +337,13 @@ static int gb_loopback_fn(void *data) struct gb_loopback *gb = (struct gb_loopback *)data; while (!kthread_should_stop()) { - if (gb->type == GB_LOOPBACK_FN_NONE) { + if (!gb->type) { msleep(1000); continue; } - if (gb->type == GB_LOOPBACK_FN_PING) + if (gb->type == GB_LOOPBACK_TYPE_PING) error = gb_loopback_ping(gb, &tlat); - else if (gb->type == GB_LOOPBACK_FN_XFER) + else if (gb->type == GB_LOOPBACK_TYPE_TRANSFER) error = gb_loopback_transfer(gb, &tlat, gb->size); if (error) gb->error++; -- cgit v1.2.3-59-g8ed1b From 384a7a3c4f8bf96a6b2e0bddafec41f929b50958 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 13 Jul 2015 20:20:49 +0100 Subject: greybus: loopback: add sink to loopback protocol Loopback sink command will operate as an amalgam of the ping and tranfer operations. Sink will send an ACK'd variable size operation over greybus. Unlike the transfer type which transmits the received data back, the sink type will simply ACK without sending the received data back. This patch adds the kernel side of the sink command. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 1 + drivers/staging/greybus/loopback.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9823d16c3b40..d3cce68e8954 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -668,6 +668,7 @@ struct gb_uart_serial_state_request { #define GB_LOOPBACK_TYPE_PROTOCOL_VERSION 0x01 #define GB_LOOPBACK_TYPE_PING 0x02 #define GB_LOOPBACK_TYPE_TRANSFER 0x03 +#define GB_LOOPBACK_TYPE_SINK 0x04 struct gb_loopback_transfer_request { __le32 len; diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index bdbebd6dbede..678690fbbc93 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -114,6 +114,7 @@ static void gb_loopback_check_attr(struct gb_loopback *gb) switch (gb->type) { case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: + case GB_LOOPBACK_TYPE_SINK: break; default: gb->type = 0; @@ -164,6 +165,31 @@ static struct attribute *loopback_attrs[] = { }; ATTRIBUTE_GROUPS(loopback); +static int gb_loopback_sink(struct gb_loopback *gb, + struct timeval *tping, u32 len) +{ + struct timeval ts, te; + u64 elapsed_nsecs; + struct gb_loopback_transfer_request *request; + int retval; + + request = kmalloc(len + sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->len = cpu_to_le32(len); + + do_gettimeofday(&ts); + retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_SINK, + request, len + sizeof(*request), NULL, 0); + do_gettimeofday(&te); + elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); + *tping = ns_to_timeval(elapsed_nsecs); + + kfree(request); + return retval; +} + static int gb_loopback_transfer(struct gb_loopback *gb, struct timeval *tping, u32 len) { @@ -235,6 +261,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) "module-initiated version operation\n"); return -EINVAL; case GB_LOOPBACK_TYPE_PING: + case GB_LOOPBACK_TYPE_SINK: return 0; case GB_LOOPBACK_TYPE_TRANSFER: if (operation->request->payload_size < sizeof(*request)) { @@ -345,6 +372,8 @@ static int gb_loopback_fn(void *data) error = gb_loopback_ping(gb, &tlat); else if (gb->type == GB_LOOPBACK_TYPE_TRANSFER) error = gb_loopback_transfer(gb, &tlat, gb->size); + else if (gb->type == GB_LOOPBACK_TYPE_SINK) + error = gb_loopback_sink(gb, &tlat, gb->size); if (error) gb->error++; if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) { -- cgit v1.2.3-59-g8ed1b From c3bba87a7ab57e636a8c96ddd36fdedfefe4848c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 13 Jul 2015 20:20:50 +0100 Subject: greybus: loopback: truncate maximum loop data to link size Get maximum payload by way of gb_operation_get_payload_size_max() and truncate any requested loopback size greater than this value. RX of data from firmware over the specified size will not be accepted. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 678690fbbc93..f490648648ce 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -34,6 +34,7 @@ struct gb_loopback { int type; u32 size; + u32 size_max; int ms_wait; struct gb_loopback_stats latency; @@ -46,7 +47,6 @@ struct gb_loopback { }; #define GB_LOOPBACK_MS_WAIT_MAX 1000 -#define GB_LOOPBACK_SIZE_MAX SZ_4K /* Define get_version() routine */ define_get_version(gb_loopback, LOOPBACK); @@ -122,8 +122,8 @@ static void gb_loopback_check_attr(struct gb_loopback *gb) } if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; - if (gb->size > GB_LOOPBACK_SIZE_MAX) - gb->size = GB_LOOPBACK_SIZE_MAX; + if (gb->size > gb->size_max) + gb->size = gb->size_max; gb->error = 0; gb_loopback_reset_stats(gb); } @@ -182,6 +182,7 @@ static int gb_loopback_sink(struct gb_loopback *gb, do_gettimeofday(&ts); retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_SINK, request, len + sizeof(*request), NULL, 0); + do_gettimeofday(&te); elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); *tping = ns_to_timeval(elapsed_nsecs); @@ -250,6 +251,7 @@ static int gb_loopback_ping(struct gb_loopback *gb, struct timeval *tping) static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) { struct gb_connection *connection = operation->connection; + struct gb_loopback *gb = connection->private; struct gb_loopback_transfer_request *request; struct gb_loopback_transfer_response *response; u32 len; @@ -273,6 +275,13 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) } request = operation->request->payload; len = le32_to_cpu(request->len); + if (len > gb->size_max) { + dev_err(&connection->dev, + "transfer request too large (%zu > %zu)\n", + len, gb->size_max); + return -EINVAL; + } + if (len) { if (!gb_operation_response_alloc(operation, len)) { dev_err(&connection->dev, @@ -416,6 +425,14 @@ static int gb_loopback_connection_init(struct gb_connection *connection) if (retval) goto out_get_ver; + /* Calculate maximum payload */ + gb->size_max = gb_operation_get_payload_size_max(connection); + if (gb->size_max <= sizeof(struct gb_loopback_transfer_request)) { + retval = -EINVAL; + goto out_get_ver; + } + gb->size_max -= sizeof(struct gb_loopback_transfer_request); + gb_loopback_reset_stats(gb); gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { -- cgit v1.2.3-59-g8ed1b From f7908e4d6def2cbeb16af29abceb1c6944d91f0c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 13 Jul 2015 20:20:51 +0100 Subject: greybus: loopback: update throughput metrics to improve granularity Throughput capture should account for the entire size of the data going out on the wire. In addition throughput should be captured for each supported loopback operation. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index f490648648ce..b92232b7a881 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -340,10 +340,26 @@ static void gb_loopback_freq_update(struct gb_loopback *gb) gb_loopback_update_stats(&gb->frequency, gb->elapsed_nsecs); } -static void gb_loopback_bw_update(struct gb_loopback *gb, int error) +static void gb_loopback_throughput_update(struct gb_loopback *gb) { - if (!error) - gb->throughput.sum += gb->size * 2; + u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2; + + switch (gb->type) { + case GB_LOOPBACK_TYPE_PING: + break; + case GB_LOOPBACK_TYPE_SINK: + aggregate_size += sizeof(struct gb_loopback_transfer_request) + + gb->size; + break; + case GB_LOOPBACK_TYPE_TRANSFER: + aggregate_size += sizeof(struct gb_loopback_transfer_request) + + sizeof(struct gb_loopback_transfer_response) + + gb->size * 2; + break; + default: + return; + } + gb->throughput.sum += aggregate_size; gb_loopback_update_stats(&gb->throughput, gb->elapsed_nsecs); } @@ -393,8 +409,7 @@ static int gb_loopback_fn(void *data) gb->elapsed_nsecs = timeval_to_ns(&gb->te) - timeval_to_ns(&gb->ts); gb_loopback_freq_update(gb); - if (gb->type == GB_LOOPBACK_TYPE_PING) - gb_loopback_bw_update(gb, error); + gb_loopback_throughput_update(gb); gb_loopback_latency_update(gb, &tlat); if (gb->elapsed_nsecs >= NSEC_PER_SEC) gb->ts = gb->te; -- cgit v1.2.3-59-g8ed1b From 52af141b8224e428ee135bbeb508646b40d03fb5 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 14 Jul 2015 00:53:12 +0100 Subject: greybus: loopback: fix broken tabs in greybus_protocols.h Copy/paste of code from one file to another missed inappropriate use of tab after define. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index d3cce68e8954..b32d6ab2a408 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -660,14 +660,14 @@ struct gb_uart_serial_state_request { /* Loopback */ /* Version of the Greybus loopback protocol we support */ -#define GB_LOOPBACK_VERSION_MAJOR 0x00 -#define GB_LOOPBACK_VERSION_MINOR 0x01 +#define GB_LOOPBACK_VERSION_MAJOR 0x00 +#define GB_LOOPBACK_VERSION_MINOR 0x01 /* Greybus loopback request types */ -#define GB_LOOPBACK_TYPE_INVALID 0x00 -#define GB_LOOPBACK_TYPE_PROTOCOL_VERSION 0x01 -#define GB_LOOPBACK_TYPE_PING 0x02 -#define GB_LOOPBACK_TYPE_TRANSFER 0x03 +#define GB_LOOPBACK_TYPE_INVALID 0x00 +#define GB_LOOPBACK_TYPE_PROTOCOL_VERSION 0x01 +#define GB_LOOPBACK_TYPE_PING 0x02 +#define GB_LOOPBACK_TYPE_TRANSFER 0x03 #define GB_LOOPBACK_TYPE_SINK 0x04 struct gb_loopback_transfer_request { -- cgit v1.2.3-59-g8ed1b From e51eafebf83b236d4d43ed13b809c504e2d38d54 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 14 Jul 2015 00:53:13 +0100 Subject: greybus: loopback: fix 64bit printf format error Last patchset missed compilation on 64 bit contained warning. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index b92232b7a881..f07fc0a837d5 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -34,7 +34,7 @@ struct gb_loopback { int type; u32 size; - u32 size_max; + size_t size_max; int ms_wait; struct gb_loopback_stats latency; @@ -254,7 +254,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) struct gb_loopback *gb = connection->private; struct gb_loopback_transfer_request *request; struct gb_loopback_transfer_response *response; - u32 len; + size_t len; /* By convention, the AP initiates the version operation */ switch (type) { -- cgit v1.2.3-59-g8ed1b From a5192032a2a9475c837c2b4b6fc3df02d617c7b0 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 14 Jul 2015 02:10:18 +0100 Subject: greybus: uart: fix typo in defintion Fixing needless redefinition of operation types in gbsim reveals this typo GB_UART_TYPE_SET_BREAK -> GB_UART_TYPE_SEND_BREAK. This patch should be applied in lock-step to the patch to gbsim 'gbsim/uart: remove unnecessary redefinition of operation types' since gbsim does not contain the typo. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 +- drivers/staging/greybus/uart.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b32d6ab2a408..839f3a6c88c6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -597,7 +597,7 @@ struct gb_svc_conn_destroy_request { #define GB_UART_TYPE_RECEIVE_DATA 0x03 /* Unsolicited data */ #define GB_UART_TYPE_SET_LINE_CODING 0x04 #define GB_UART_TYPE_SET_CONTROL_LINE_STATE 0x05 -#define GB_UART_TYPE_SET_BREAK 0x06 +#define GB_UART_TYPE_SEND_BREAK 0x06 #define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ /* Represents data from AP -> Module */ diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 3932f8667d31..0166c4cdb451 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -186,7 +186,7 @@ static int send_break(struct gb_tty *gb_tty, u8 state) } request.state = state; - return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SET_BREAK, + return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SEND_BREAK, &request, sizeof(request), NULL, 0); } -- cgit v1.2.3-59-g8ed1b From fffc151381473384629d78cb89b7f7bbb9dc53b0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:23 +0200 Subject: greybus: operation: fix another cancellation use-after-free An incoming operation could already be scheduled even if gb_operation_result_set succeeds as its initial status is -EINPROGRESS. Avoid potential use-after-free by never dropping the reference count for incoming operations as part of cancellation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b78c55fac8cc..41aec7647b2b 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -853,12 +853,17 @@ void gb_connection_recv(struct gb_connection *connection, */ void gb_operation_cancel(struct gb_operation *operation, int errno) { - if (gb_operation_result_set(operation, errno)) { - gb_message_cancel(operation->request); - gb_operation_put(operation); - } else if (gb_operation_is_incoming(operation)) { - if (!gb_operation_is_unidirectional(operation)) + if (gb_operation_is_incoming(operation)) { + /* Cancel response if it has been allocated */ + if (!gb_operation_result_set(operation, errno) && + !gb_operation_is_unidirectional(operation)) { gb_message_cancel(operation->response); + } + } else { + if (gb_operation_result_set(operation, errno)) { + gb_message_cancel(operation->request); + gb_operation_put(operation); + } } } EXPORT_SYMBOL_GPL(gb_operation_cancel); -- cgit v1.2.3-59-g8ed1b From 9a586bd2bb80a268345cc8ccfa702413359ece06 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:24 +0200 Subject: greybus: operation: clean up create-incoming error path Clean up gb_operation_create_incoming error path by returning immediately on allocation failures. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 41aec7647b2b..081694c8a1c2 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -511,10 +511,11 @@ gb_operation_create_incoming(struct gb_connection *connection, u16 id, operation = gb_operation_create_common(connection, type, request_size, 0, flags, GFP_ATOMIC); - if (operation) { - operation->id = id; - memcpy(operation->request->header, data, size); - } + if (!operation) + return NULL; + + operation->id = id; + memcpy(operation->request->header, data, size); return operation; } -- cgit v1.2.3-59-g8ed1b From 3eeac7e37ce9856e53693772dfe81a79b57b5a00 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:25 +0200 Subject: greybus: operation: add active counter Add active counter to track operations that are in use. Note that the active count is always less than the reference count. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 28 ++++++++++++++++++++++++++-- drivers/staging/greybus/operation.h | 2 ++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 081694c8a1c2..abe44c18fb9e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -32,6 +32,18 @@ static DEFINE_SPINLOCK(gb_operations_lock); static int gb_operation_response_send(struct gb_operation *operation, int errno); +/* Caller holds operation reference. */ +static inline void gb_operation_get_active(struct gb_operation *operation) +{ + atomic_inc(&operation->active); +} + +/* Caller holds operation reference. */ +static inline void gb_operation_put_active(struct gb_operation *operation) +{ + atomic_dec(&operation->active); +} + /* * Set an operation's result. * @@ -204,6 +216,7 @@ static void gb_operation_work(struct work_struct *work) operation->callback(operation); + gb_operation_put_active(operation); gb_operation_put(operation); } @@ -449,6 +462,7 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, INIT_WORK(&operation->work, gb_operation_work); init_completion(&operation->completion); kref_init(&operation->kref); + atomic_set(&operation->active, 0); spin_lock_irqsave(&gb_operations_lock, flags); list_add_tail(&operation->links, &connection->operations); @@ -597,6 +611,7 @@ int gb_operation_request_send(struct gb_operation *operation, * It'll be dropped when the operation completes. */ gb_operation_get(operation); + gb_operation_get_active(operation); /* * Record the callback function, which is executed in @@ -618,8 +633,10 @@ int gb_operation_request_send(struct gb_operation *operation, gb_operation_result_set(operation, -EINPROGRESS); ret = gb_message_send(operation->request, gfp); - if (ret) + if (ret) { + gb_operation_put_active(operation); gb_operation_put(operation); + } return ret; } @@ -688,13 +705,16 @@ static int gb_operation_response_send(struct gb_operation *operation, /* Reference will be dropped when message has been sent. */ gb_operation_get(operation); + gb_operation_get_active(operation); /* Fill in the response header and send it */ operation->response->header->result = gb_operation_errno_map(errno); ret = gb_message_send(operation->response, GFP_KERNEL); - if (ret) + if (ret) { + gb_operation_put_active(operation); gb_operation_put(operation); + } return ret; } @@ -723,6 +743,7 @@ void greybus_message_sent(struct greybus_host_device *hd, dev_err(&operation->connection->dev, "error sending response: %d\n", status); } + gb_operation_put_active(operation); gb_operation_put(operation); } else if (status) { if (gb_operation_result_set(operation, status)) @@ -751,6 +772,8 @@ static void gb_connection_recv_request(struct gb_connection *connection, return; /* XXX Respond with pre-allocated ENOMEM */ } + gb_operation_get_active(operation); + /* * Incoming requests are handled by arranging for the * request handler to be the operation's callback function. @@ -863,6 +886,7 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) } else { if (gb_operation_result_set(operation, errno)) { gb_message_cancel(operation->request); + gb_operation_put_active(operation); gb_operation_put(operation); } } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index c16f64dd9784..b32386636f6e 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -127,6 +127,8 @@ struct gb_operation { struct completion completion; struct kref kref; + atomic_t active; + struct list_head links; /* connection->operations */ }; -- cgit v1.2.3-59-g8ed1b From fd7134a3cd0a1b3f91e66922534a796145c85eef Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:26 +0200 Subject: greybus: operation: make cancellation synchronous Make sure to wait for the operation to become inactive before returning after having cancelled an operation. This makes sure that any ongoing operation completion callbacks have finished. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 1 - drivers/staging/greybus/operation.c | 24 ++++++++++++++++++++++-- drivers/staging/greybus/operation.h | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 65a2bd5891d9..b9f9b11b1b65 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -227,7 +227,6 @@ void gb_connection_destroy(struct gb_connection *connection) if (WARN_ON(!connection)) return; - /* XXX Need to wait for any outstanding requests to complete */ if (WARN_ON(!list_empty(&connection->operations))) { list_for_each_entry_safe(operation, next, &connection->operations, links) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index abe44c18fb9e..5e8ea0289053 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include "greybus.h" @@ -23,6 +25,9 @@ static struct kmem_cache *gb_message_cache; /* Workqueue to handle Greybus operation completions. */ static struct workqueue_struct *gb_operation_workqueue; +/* Wait queue for synchronous cancellations. */ +static DECLARE_WAIT_QUEUE_HEAD(gb_operation_cancellation_queue); + /* * Protects access to connection operations lists, as well as * updates to operation->errno. @@ -41,7 +46,15 @@ static inline void gb_operation_get_active(struct gb_operation *operation) /* Caller holds operation reference. */ static inline void gb_operation_put_active(struct gb_operation *operation) { - atomic_dec(&operation->active); + if (atomic_dec_and_test(&operation->active)) { + if (atomic_read(&operation->waiters)) + wake_up(&gb_operation_cancellation_queue); + } +} + +static inline bool gb_operation_is_active(struct gb_operation *operation) +{ + return atomic_read(&operation->active); } /* @@ -463,6 +476,7 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, init_completion(&operation->completion); kref_init(&operation->kref); atomic_set(&operation->active, 0); + atomic_set(&operation->waiters, 0); spin_lock_irqsave(&gb_operations_lock, flags); list_add_tail(&operation->links, &connection->operations); @@ -873,7 +887,8 @@ void gb_connection_recv(struct gb_connection *connection, } /* - * Cancel an operation, and record the given error to indicate why. + * Cancel an operation synchronously, and record the given error to indicate + * why. */ void gb_operation_cancel(struct gb_operation *operation, int errno) { @@ -890,6 +905,11 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) gb_operation_put(operation); } } + + atomic_inc(&operation->waiters); + wait_event(gb_operation_cancellation_queue, + !gb_operation_is_active(operation)); + atomic_dec(&operation->waiters); } EXPORT_SYMBOL_GPL(gb_operation_cancel); diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index b32386636f6e..c8aaf90a006a 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -128,6 +128,7 @@ struct gb_operation { struct kref kref; atomic_t active; + atomic_t waiters; struct list_head links; /* connection->operations */ }; -- cgit v1.2.3-59-g8ed1b From db1481ba7f36c2f5f74d19918bdf315a1ef8226b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:27 +0200 Subject: greybus: operation: complete operations on cancellation Make sure to call the operation completion callback also when the operation is being cancelled. The completion callback may need to release resources allocated at submission and the driver should be informed that the operation has failed due to cancellation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5e8ea0289053..1e181d59c9b7 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -901,8 +901,7 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) } else { if (gb_operation_result_set(operation, errno)) { gb_message_cancel(operation->request); - gb_operation_put_active(operation); - gb_operation_put(operation); + queue_work(gb_operation_workqueue, &operation->work); } } -- cgit v1.2.3-59-g8ed1b From c600e535a72d0d3ec4b3e8e323e5602cbc49004e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:28 +0200 Subject: greybus: operation: clean up operation work function Call request handler helper explicitly from the work function rather than overload the operation completion callback. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 1e181d59c9b7..0adb72773d8a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -212,9 +212,10 @@ static void gb_operation_request_handle(struct gb_operation *operation) } /* - * Complete an operation in non-atomic context. For incoming - * requests, the callback function is the request handler, and - * the operation result should be -EINPROGRESS at this point. + * Process operation work. + * + * For incoming requests, call the protocol request handler. The operation + * result should be -EINPROGRESS at this point. * * For outgoing requests, the operation result value should have * been set before queueing this. The operation callback function @@ -227,7 +228,10 @@ static void gb_operation_work(struct work_struct *work) operation = container_of(work, struct gb_operation, work); - operation->callback(operation); + if (gb_operation_is_incoming(operation)) + gb_operation_request_handle(operation); + else + operation->callback(operation); gb_operation_put_active(operation); gb_operation_put(operation); @@ -789,14 +793,9 @@ static void gb_connection_recv_request(struct gb_connection *connection, gb_operation_get_active(operation); /* - * Incoming requests are handled by arranging for the - * request handler to be the operation's callback function. - * - * The last thing the handler does is send a response - * message. The initial reference to the operation will be - * dropped when the handler returns. + * The initial reference to the operation will be dropped when the + * request handler returns. */ - operation->callback = gb_operation_request_handle; if (gb_operation_result_set(operation, -EINPROGRESS)) queue_work(gb_operation_workqueue, &operation->work); } -- cgit v1.2.3-59-g8ed1b From 886c6069bc0e16b7215b326831478ed700451038 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:29 +0200 Subject: greybus: operation: suppress response submission on connection tear down Suppress response submission on connection tear down as we do with requests. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 0adb72773d8a..2729b486d68d 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -705,6 +705,9 @@ static int gb_operation_response_send(struct gb_operation *operation, struct gb_connection *connection = operation->connection; int ret; + if (connection->state != GB_CONNECTION_STATE_ENABLED) + return -ENOTCONN; + if (!operation->response && !gb_operation_is_unidirectional(operation)) { if (!gb_operation_response_alloc(operation, 0)) -- cgit v1.2.3-59-g8ed1b From cad09a8f8ce843aa11f9a6a8a7aa5c6d6591147b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:30 +0200 Subject: greybus: connection: add connection-state locking Add locking, and the implied barriers, to connection-state updates. This will be used to fix a number of races in the operations and connection-tear-down implementations. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 24 ++++++++++++++++++++---- drivers/staging/greybus/connection.h | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b9f9b11b1b65..abc1f861ea28 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -65,8 +65,13 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_connection *connection = to_gb_connection(dev); + enum gb_connection_state state; - return sprintf(buf, "%d\n", connection->state); + spin_lock_irq(&connection->lock); + state = connection->state; + spin_unlock_irq(&connection->lock); + + return sprintf(buf, "%d\n", state); } static DEVICE_ATTR_RO(state); @@ -204,6 +209,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, spin_unlock_irq(&gb_connections_lock); atomic_set(&connection->op_cycle, 0); + spin_lock_init(&connection->lock); INIT_LIST_HEAD(&connection->operations); /* XXX Will have to establish connections to get version */ @@ -274,10 +280,16 @@ int gb_connection_init(struct gb_connection *connection) } /* Need to enable the connection to initialize it */ + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_ENABLED; + spin_unlock_irq(&connection->lock); + ret = connection->protocol->connection_init(connection); - if (ret) + if (ret) { + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_ERROR; + spin_unlock_irq(&connection->lock); + } return ret; } @@ -291,10 +303,14 @@ void gb_connection_exit(struct gb_connection *connection) return; } - if (connection->state != GB_CONNECTION_STATE_ENABLED) + spin_lock_irq(&connection->lock); + if (connection->state != GB_CONNECTION_STATE_ENABLED) { + spin_unlock_irq(&connection->lock); return; - + } connection->state = GB_CONNECTION_STATE_DESTROYING; + spin_unlock_irq(&connection->lock); + connection->protocol->connection_exit(connection); /* diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index ad699db0dfd8..c2b71fe7f397 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -35,6 +35,7 @@ struct gb_connection { u8 major; u8 minor; + spinlock_t lock; enum gb_connection_state state; atomic_t op_cycle; -- cgit v1.2.3-59-g8ed1b From 008974cb528d301895797fec0f7d4fd64d01fce0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:31 +0200 Subject: greybus: operation: fix connection tear down Fix connection tear down, which was done in an unsafe way that could result in use-after-free as the per-connection list of operations was iterated without any locking or refcounting. Specifically, the operations list was iterated without holding any locks or operation refcounts even though operations were being both removed from the list and deallocated during per-operation cancellation. Any operation completing during tear down could also cause corruption. Change the per-connection operation list to only contain active operations and use the recently introduced active counter to maintain the list. Add new helper that is called on connection tear down to cancel all outstanding operations in a safe way by using proper locks and making sure to hold a reference to any operation being cancelled. Note that by verifying the connection state before incrementing the active count we can make sure that all active operations have been cancelled and that no new ones have been started when the helper returns. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 37 ++++++++--- drivers/staging/greybus/connection.h | 2 +- drivers/staging/greybus/operation.c | 115 +++++++++++++++++++++++------------ drivers/staging/greybus/operation.h | 2 +- 4 files changed, 107 insertions(+), 49 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index abc1f861ea28..2d19082a7f35 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -221,23 +221,46 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, return connection; } +/* + * Cancel all active operations on a connection. + * + * Should only be called during connection tear down. + */ +static void gb_connection_cancel_operations(struct gb_connection *connection, + int errno) +{ + struct gb_operation *operation; + + spin_lock_irq(&connection->lock); + + WARN_ON(!list_empty(&connection->operations)); + + while (!list_empty(&connection->operations)) { + operation = list_last_entry(&connection->operations, + struct gb_operation, links); + gb_operation_get(operation); + spin_unlock_irq(&connection->lock); + + gb_operation_cancel(operation, errno); + gb_operation_put(operation); + + spin_lock_irq(&connection->lock); + } + spin_unlock_irq(&connection->lock); +} + /* * Tear down a previously set up connection. */ void gb_connection_destroy(struct gb_connection *connection) { - struct gb_operation *operation; - struct gb_operation *next; struct ida *id_map; if (WARN_ON(!connection)) return; - if (WARN_ON(!list_empty(&connection->operations))) { - list_for_each_entry_safe(operation, next, - &connection->operations, links) - gb_operation_cancel(operation, -ESHUTDOWN); - } + gb_connection_cancel_operations(connection, -ESHUTDOWN); + spin_lock_irq(&gb_connections_lock); list_del(&connection->bundle_links); list_del(&connection->hd_links); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index c2b71fe7f397..fb7a1fb290ac 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -37,9 +37,9 @@ struct gb_connection { spinlock_t lock; enum gb_connection_state state; + struct list_head operations; atomic_t op_cycle; - struct list_head operations; void *private; }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 2729b486d68d..5cd4665c645c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -29,32 +29,65 @@ static struct workqueue_struct *gb_operation_workqueue; static DECLARE_WAIT_QUEUE_HEAD(gb_operation_cancellation_queue); /* - * Protects access to connection operations lists, as well as - * updates to operation->errno. + * Protects updates to operation->errno. */ static DEFINE_SPINLOCK(gb_operations_lock); static int gb_operation_response_send(struct gb_operation *operation, int errno); -/* Caller holds operation reference. */ -static inline void gb_operation_get_active(struct gb_operation *operation) +/* + * Increment operation active count and add to connection list unless the + * connection is going away. + * + * Caller holds operation reference. + */ +static int gb_operation_get_active(struct gb_operation *operation) { - atomic_inc(&operation->active); + struct gb_connection *connection = operation->connection; + unsigned long flags; + + spin_lock_irqsave(&connection->lock, flags); + + if (connection->state != GB_CONNECTION_STATE_ENABLED) { + spin_unlock_irqrestore(&connection->lock, flags); + return -ENOTCONN; + } + + if (operation->active++ == 0) + list_add_tail(&operation->links, &connection->operations); + + spin_unlock_irqrestore(&connection->lock, flags); + + return 0; } /* Caller holds operation reference. */ -static inline void gb_operation_put_active(struct gb_operation *operation) +static void gb_operation_put_active(struct gb_operation *operation) { - if (atomic_dec_and_test(&operation->active)) { + struct gb_connection *connection = operation->connection; + unsigned long flags; + + spin_lock_irqsave(&connection->lock, flags); + if (--operation->active == 0) { + list_del(&operation->links); if (atomic_read(&operation->waiters)) wake_up(&gb_operation_cancellation_queue); } + spin_unlock_irqrestore(&connection->lock, flags); } -static inline bool gb_operation_is_active(struct gb_operation *operation) +static bool gb_operation_is_active(struct gb_operation *operation) { - return atomic_read(&operation->active); + struct gb_connection *connection = operation->connection; + unsigned long flags; + bool ret; + + spin_lock_irqsave(&connection->lock, flags); + ret = operation->active; + spin_unlock_irqrestore(&connection->lock, flags); + + return ret; } /* @@ -150,7 +183,7 @@ gb_operation_find_outgoing(struct gb_connection *connection, u16 operation_id) unsigned long flags; bool found = false; - spin_lock_irqsave(&gb_operations_lock, flags); + spin_lock_irqsave(&connection->lock, flags); list_for_each_entry(operation, &connection->operations, links) if (operation->id == operation_id && !gb_operation_is_incoming(operation)) { @@ -158,7 +191,7 @@ gb_operation_find_outgoing(struct gb_connection *connection, u16 operation_id) found = true; break; } - spin_unlock_irqrestore(&gb_operations_lock, flags); + spin_unlock_irqrestore(&connection->lock, flags); return found ? operation : NULL; } @@ -453,7 +486,6 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, { struct greybus_host_device *hd = connection->hd; struct gb_operation *operation; - unsigned long flags; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); if (!operation) @@ -479,13 +511,8 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, INIT_WORK(&operation->work, gb_operation_work); init_completion(&operation->completion); kref_init(&operation->kref); - atomic_set(&operation->active, 0); atomic_set(&operation->waiters, 0); - spin_lock_irqsave(&gb_operations_lock, flags); - list_add_tail(&operation->links, &connection->operations); - spin_unlock_irqrestore(&gb_operations_lock, flags); - return operation; err_request: @@ -570,10 +597,6 @@ static void _gb_operation_destroy(struct kref *kref) operation = container_of(kref, struct gb_operation, kref); - /* XXX Make sure it's not in flight */ - list_del(&operation->links); - spin_unlock(&gb_operations_lock); - if (operation->response) gb_operation_message_free(operation->response); gb_operation_message_free(operation->request); @@ -590,8 +613,7 @@ void gb_operation_put(struct gb_operation *operation) if (WARN_ON(!operation)) return; - kref_put_spinlock_irqsave(&operation->kref, _gb_operation_destroy, - &gb_operations_lock); + kref_put(&operation->kref, _gb_operation_destroy); } EXPORT_SYMBOL_GPL(gb_operation_put); @@ -621,15 +643,14 @@ int gb_operation_request_send(struct gb_operation *operation, if (!callback) return -EINVAL; - if (connection->state != GB_CONNECTION_STATE_ENABLED) - return -ENOTCONN; - /* * First, get an extra reference on the operation. * It'll be dropped when the operation completes. */ gb_operation_get(operation); - gb_operation_get_active(operation); + ret = gb_operation_get_active(operation); + if (ret) + goto err_put; /* * Record the callback function, which is executed in @@ -651,10 +672,15 @@ int gb_operation_request_send(struct gb_operation *operation, gb_operation_result_set(operation, -EINPROGRESS); ret = gb_message_send(operation->request, gfp); - if (ret) { - gb_operation_put_active(operation); - gb_operation_put(operation); - } + if (ret) + goto err_put_active; + + return 0; + +err_put_active: + gb_operation_put_active(operation); +err_put: + gb_operation_put(operation); return ret; } @@ -705,9 +731,6 @@ static int gb_operation_response_send(struct gb_operation *operation, struct gb_connection *connection = operation->connection; int ret; - if (connection->state != GB_CONNECTION_STATE_ENABLED) - return -ENOTCONN; - if (!operation->response && !gb_operation_is_unidirectional(operation)) { if (!gb_operation_response_alloc(operation, 0)) @@ -726,16 +749,23 @@ static int gb_operation_response_send(struct gb_operation *operation, /* Reference will be dropped when message has been sent. */ gb_operation_get(operation); - gb_operation_get_active(operation); + ret = gb_operation_get_active(operation); + if (ret) + goto err_put; /* Fill in the response header and send it */ operation->response->header->result = gb_operation_errno_map(errno); ret = gb_message_send(operation->response, GFP_KERNEL); - if (ret) { - gb_operation_put_active(operation); - gb_operation_put(operation); - } + if (ret) + goto err_put_active; + + return 0; + +err_put_active: + gb_operation_put_active(operation); +err_put: + gb_operation_put(operation); return ret; } @@ -785,6 +815,7 @@ static void gb_connection_recv_request(struct gb_connection *connection, void *data, size_t size) { struct gb_operation *operation; + int ret; operation = gb_operation_create_incoming(connection, operation_id, type, data, size); @@ -793,7 +824,11 @@ static void gb_connection_recv_request(struct gb_connection *connection, return; /* XXX Respond with pre-allocated ENOMEM */ } - gb_operation_get_active(operation); + ret = gb_operation_get_active(operation); + if (ret) { + gb_operation_put(operation); + return; + } /* * The initial reference to the operation will be dropped when the diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index c8aaf90a006a..26ecd66710a3 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -127,9 +127,9 @@ struct gb_operation { struct completion completion; struct kref kref; - atomic_t active; atomic_t waiters; + int active; struct list_head links; /* connection->operations */ }; -- cgit v1.2.3-59-g8ed1b From 7f1b67cd535673cef9b9c9fa772d04015f64bd8e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:32 +0200 Subject: greybus: connection: fix protocol tear-down race Make sure to cancel all active operations before calling protocol connection_exit to prevent use-after-free issues when the protocol state is being deallocated (e.g. late processing of already-queued requests or completions). Note that already-queued requests or completions will be processed as part of cancellation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2d19082a7f35..ac9b2d174805 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -232,9 +232,6 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, struct gb_operation *operation; spin_lock_irq(&connection->lock); - - WARN_ON(!list_empty(&connection->operations)); - while (!list_empty(&connection->operations)) { operation = list_last_entry(&connection->operations, struct gb_operation, links); @@ -259,8 +256,6 @@ void gb_connection_destroy(struct gb_connection *connection) if (WARN_ON(!connection)) return; - gb_connection_cancel_operations(connection, -ESHUTDOWN); - spin_lock_irq(&gb_connections_lock); list_del(&connection->bundle_links); list_del(&connection->hd_links); @@ -334,6 +329,8 @@ void gb_connection_exit(struct gb_connection *connection) connection->state = GB_CONNECTION_STATE_DESTROYING; spin_unlock_irq(&connection->lock); + gb_connection_cancel_operations(connection, -ESHUTDOWN); + connection->protocol->connection_exit(connection); /* -- cgit v1.2.3-59-g8ed1b From 3325a4ad7122acdbfae5360cafc7152b32eefd57 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:33 +0200 Subject: greybus: operation: fix operation look-up race Make sure to fully initialise the operation before adding it to the active list when sending a request. The operation should be fully initialised before adding it to the active list to avoid racing with operation look up when receiving a response, something which could potentially lead to a match against some earlier (or intermediate) value of the id field. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5cd4665c645c..f7b0aa970bbc 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -642,16 +642,6 @@ int gb_operation_request_send(struct gb_operation *operation, if (!callback) return -EINVAL; - - /* - * First, get an extra reference on the operation. - * It'll be dropped when the operation completes. - */ - gb_operation_get(operation); - ret = gb_operation_get_active(operation); - if (ret) - goto err_put; - /* * Record the callback function, which is executed in * non-atomic (workqueue) context when the final result @@ -668,9 +658,17 @@ int gb_operation_request_send(struct gb_operation *operation, header = operation->request->header; header->operation_id = cpu_to_le16(operation->id); - /* All set, send the request */ gb_operation_result_set(operation, -EINPROGRESS); + /* + * Get an extra reference on the operation. It'll be dropped when the + * operation completes. + */ + gb_operation_get(operation); + ret = gb_operation_get_active(operation); + if (ret) + goto err_put; + ret = gb_message_send(operation->request, gfp); if (ret) goto err_put_active; -- cgit v1.2.3-59-g8ed1b From 0eb8c1159839dcb6c97fba82e5a8698d9c30f815 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:34 +0200 Subject: greybus: operation: fix response-cancellation race Make sure the request handler has submitted the response before cancelling it during operation cancellation. This prevents cancelling not-yet-submitted messages. It currently also avoids us ending up with an active message on a stalled connection (e.g. due to E2EFC). Note that the call to gb_operation_result_set() is now redundant but is kept as a precaution to guarantee that a response has indeed been allocated as part of response submission. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f7b0aa970bbc..0576f197f58e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -928,10 +928,14 @@ void gb_connection_recv(struct gb_connection *connection, void gb_operation_cancel(struct gb_operation *operation, int errno) { if (gb_operation_is_incoming(operation)) { - /* Cancel response if it has been allocated */ - if (!gb_operation_result_set(operation, errno) && - !gb_operation_is_unidirectional(operation)) { - gb_message_cancel(operation->response); + if (!gb_operation_is_unidirectional(operation)) { + /* + * Make sure the request handler has submitted the + * response before cancelling it. + */ + flush_work(&operation->work); + if (!gb_operation_result_set(operation, errno)) + gb_message_cancel(operation->response); } } else { if (gb_operation_result_set(operation, errno)) { -- cgit v1.2.3-59-g8ed1b From 5a3be769e92ea993f8a8c27b89571c276d874733 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:35 +0200 Subject: greybus: operation: split incoming and outgoing cancellation Split incoming and outgoing operation-cancellation helpers. Incoming operations are only cancelled as part of connection tear down and is specifically not needed in the driver API. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ++++- drivers/staging/greybus/operation.c | 50 ++++++++++++++++++++++++------------ drivers/staging/greybus/operation.h | 1 + 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ac9b2d174805..81a5df0e3230 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -238,7 +238,11 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, gb_operation_get(operation); spin_unlock_irq(&connection->lock); - gb_operation_cancel(operation, errno); + if (gb_operation_is_incoming(operation)) + gb_operation_cancel_incoming(operation, errno); + else + gb_operation_cancel(operation, errno); + gb_operation_put(operation); spin_lock_irq(&connection->lock); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 0576f197f58e..17b07fb24006 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -922,26 +922,17 @@ void gb_connection_recv(struct gb_connection *connection, } /* - * Cancel an operation synchronously, and record the given error to indicate - * why. + * Cancel an outgoing operation synchronously, and record the given error to + * indicate why. */ void gb_operation_cancel(struct gb_operation *operation, int errno) { - if (gb_operation_is_incoming(operation)) { - if (!gb_operation_is_unidirectional(operation)) { - /* - * Make sure the request handler has submitted the - * response before cancelling it. - */ - flush_work(&operation->work); - if (!gb_operation_result_set(operation, errno)) - gb_message_cancel(operation->response); - } - } else { - if (gb_operation_result_set(operation, errno)) { - gb_message_cancel(operation->request); - queue_work(gb_operation_workqueue, &operation->work); - } + if (WARN_ON(gb_operation_is_incoming(operation))) + return; + + if (gb_operation_result_set(operation, errno)) { + gb_message_cancel(operation->request); + queue_work(gb_operation_workqueue, &operation->work); } atomic_inc(&operation->waiters); @@ -951,6 +942,31 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) } EXPORT_SYMBOL_GPL(gb_operation_cancel); +/* + * Cancel an incoming operation synchronously. Called during connection tear + * down. + */ +void gb_operation_cancel_incoming(struct gb_operation *operation, int errno) +{ + if (WARN_ON(!gb_operation_is_incoming(operation))) + return; + + if (!gb_operation_is_unidirectional(operation)) { + /* + * Make sure the request handler has submitted the response + * before cancelling it. + */ + flush_work(&operation->work); + if (!gb_operation_result_set(operation, errno)) + gb_message_cancel(operation->response); + } + + atomic_inc(&operation->waiters); + wait_event(gb_operation_cancellation_queue, + !gb_operation_is_active(operation)); + atomic_dec(&operation->waiters); +} + /** * gb_operation_sync: implement a "simple" synchronous gb operation. * @connection: the Greybus connection to send this to diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 26ecd66710a3..d7e59a3f4b47 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -171,6 +171,7 @@ int gb_operation_request_send(struct gb_operation *operation, int gb_operation_request_send_sync(struct gb_operation *operation); void gb_operation_cancel(struct gb_operation *operation, int errno); +void gb_operation_cancel_incoming(struct gb_operation *operation, int errno); void greybus_message_sent(struct greybus_host_device *hd, struct gb_message *message, int status); -- cgit v1.2.3-59-g8ed1b From 4f2c08aba792d4c778774e90e47865718eb1b7f8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:36 +0200 Subject: greybus: operation: allow drivers to define custom timeouts Add new interface gb_operation_request_send_sync_timeout, which allows drivers to define a custom operation timeout instead of the default one-second timeout. The timeout is expected to depend on protocol and operation and therefore needs to be configurable. Note that that a timeout of zero is used to wait indefinitely. Reviewed-by: Bryan O'Donoghue Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 19 +++++++++++-------- drivers/staging/greybus/operation.h | 12 +++++++++++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 17b07fb24006..63c4a5b8b0f1 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -16,9 +16,6 @@ #include "greybus.h" -/* The default amount of time a request is given to complete */ -#define OPERATION_TIMEOUT_DEFAULT 1000 /* milliseconds */ - static struct kmem_cache *gb_operation_cache; static struct kmem_cache *gb_message_cache; @@ -690,18 +687,24 @@ EXPORT_SYMBOL_GPL(gb_operation_request_send); * error is detected. The return value is the result of the * operation. */ -int gb_operation_request_send_sync(struct gb_operation *operation) +int gb_operation_request_send_sync_timeout(struct gb_operation *operation, + unsigned int timeout) { int ret; - unsigned long timeout; + unsigned long timeout_jiffies; ret = gb_operation_request_send(operation, gb_operation_sync_callback, GFP_KERNEL); if (ret) return ret; - timeout = msecs_to_jiffies(OPERATION_TIMEOUT_DEFAULT); - ret = wait_for_completion_interruptible_timeout(&operation->completion, timeout); + if (timeout) + timeout_jiffies = msecs_to_jiffies(timeout); + else + timeout_jiffies = MAX_SCHEDULE_TIMEOUT; + + ret = wait_for_completion_interruptible_timeout(&operation->completion, + timeout_jiffies); if (ret < 0) { /* Cancel the operation if interrupted */ gb_operation_cancel(operation, -ECANCELED); @@ -712,7 +715,7 @@ int gb_operation_request_send_sync(struct gb_operation *operation) return gb_operation_result(operation); } -EXPORT_SYMBOL_GPL(gb_operation_request_send_sync); +EXPORT_SYMBOL_GPL(gb_operation_request_send_sync_timeout); /* * Send a response for an incoming operation request. A non-zero diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index d7e59a3f4b47..f06dd11e0afb 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -14,6 +14,9 @@ struct gb_operation; +/* The default amount of time a request is given to complete */ +#define GB_OPERATION_TIMEOUT_DEFAULT 1000 /* milliseconds */ + /* * No protocol may define an operation that has numeric value 0x00. * It is reserved as an explicitly invalid value. @@ -168,7 +171,14 @@ bool gb_operation_response_alloc(struct gb_operation *operation, int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback, gfp_t gfp); -int gb_operation_request_send_sync(struct gb_operation *operation); +int gb_operation_request_send_sync_timeout(struct gb_operation *operation, + unsigned int timeout); +static inline int +gb_operation_request_send_sync(struct gb_operation *operation) +{ + return gb_operation_request_send_sync_timeout(operation, + GB_OPERATION_TIMEOUT_DEFAULT); +} void gb_operation_cancel(struct gb_operation *operation, int errno); void gb_operation_cancel_incoming(struct gb_operation *operation, int errno); -- cgit v1.2.3-59-g8ed1b From 129a06f541d3ff4228589b4b9f0a4681dafb0042 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 14 Jul 2015 15:43:37 +0200 Subject: greybus: operation: add gb_operation_sync_timeout helper Add gb_operation_sync_timeout convenience function, which allows drivers to configure the operation timeout. Reviewed-by: Bryan O'Donoghue Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 12 +++++++----- drivers/staging/greybus/operation.h | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 63c4a5b8b0f1..a99505cc21fa 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -978,6 +978,7 @@ void gb_operation_cancel_incoming(struct gb_operation *operation, int errno) * @request_size: size of @request * @response: pointer to a memory buffer to copy the response to * @response_size: the size of @response. + * @timeout: operation timeout in milliseconds * * This function implements a simple synchronous Greybus operation. It sends * the provided operation request and waits (sleeps) until the corresponding @@ -992,9 +993,10 @@ void gb_operation_cancel_incoming(struct gb_operation *operation, int errno) * * If there is an error, the response buffer is left alone. */ -int gb_operation_sync(struct gb_connection *connection, int type, - void *request, int request_size, - void *response, int response_size) +int gb_operation_sync_timeout(struct gb_connection *connection, int type, + void *request, int request_size, + void *response, int response_size, + unsigned int timeout) { struct gb_operation *operation; int ret; @@ -1012,7 +1014,7 @@ int gb_operation_sync(struct gb_connection *connection, int type, if (request_size) memcpy(operation->request->payload, request, request_size); - ret = gb_operation_request_send_sync(operation); + ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { dev_err(&connection->dev, "synchronous operation failed: %d\n", ret); @@ -1026,7 +1028,7 @@ int gb_operation_sync(struct gb_connection *connection, int type, return ret; } -EXPORT_SYMBOL_GPL(gb_operation_sync); +EXPORT_SYMBOL_GPL(gb_operation_sync_timeout); int __init gb_operation_init(void) { diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index f06dd11e0afb..685470349460 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -186,9 +186,19 @@ void gb_operation_cancel_incoming(struct gb_operation *operation, int errno); void greybus_message_sent(struct greybus_host_device *hd, struct gb_message *message, int status); -int gb_operation_sync(struct gb_connection *connection, int type, +int gb_operation_sync_timeout(struct gb_connection *connection, int type, + void *request, int request_size, + void *response, int response_size, + unsigned int timeout); + +static inline int gb_operation_sync(struct gb_connection *connection, int type, void *request, int request_size, - void *response, int response_size); + void *response, int response_size) +{ + return gb_operation_sync_timeout(connection, type, + request, request_size, response, response_size, + GB_OPERATION_TIMEOUT_DEFAULT); +} int gb_operation_init(void); void gb_operation_exit(void); -- cgit v1.2.3-59-g8ed1b From 93047af23c8389d1a63883dcb59cd8c2fd472b45 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 16 Jul 2015 11:44:01 +0200 Subject: greybus: kernel_ver: add list_last_entry for old kernels Add list_last_entry macro for kernels older than 3.13. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 4fb949ba9b51..c2e92df9f417 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -235,4 +235,9 @@ static inline size_t sg_pcopy_from_buffer(struct scatterlist *sgl, } #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ -- cgit v1.2.3-59-g8ed1b From 1c7658cf5165586acff901b7e6ef27d8d5f2818d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 17 Jul 2015 18:50:25 +0200 Subject: greybus: operation: fix atomic response allocation Response allocation also needs a GFP-flags argument as a response is allocated as part of an outgoing operation. Fixes: 9aa174d202e5 ("operation: allow atomic operation allocations") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 3 ++- drivers/staging/greybus/loopback.c | 3 ++- drivers/staging/greybus/operation.c | 11 ++++++----- drivers/staging/greybus/operation.h | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index d7870fc83ed2..a69a703a1848 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -75,7 +75,8 @@ static int gb_control_request_recv(u8 type, struct gb_operation *op) // an AP. break; case GB_CONTROL_TYPE_PROTOCOL_VERSION: - if (!gb_operation_response_alloc(op, sizeof(*version))) { + if (!gb_operation_response_alloc(op, sizeof(*version), + GFP_KERNEL)) { dev_err(&connection->dev, "%s: error allocating response\n", __func__); return -ENOMEM; diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index f07fc0a837d5..fe3a57bff99c 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -283,7 +283,8 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) } if (len) { - if (!gb_operation_response_alloc(operation, len)) { + if (!gb_operation_response_alloc(operation, len, + GFP_KERNEL)) { dev_err(&connection->dev, "error allocating response\n"); return -ENOMEM; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index a99505cc21fa..0fe50d80b2db 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -426,7 +426,7 @@ static u8 gb_operation_errno_map(int errno) } bool gb_operation_response_alloc(struct gb_operation *operation, - size_t response_size) + size_t response_size, gfp_t gfp) { struct greybus_host_device *hd = operation->connection->hd; struct gb_operation_msg_hdr *request_header; @@ -434,8 +434,7 @@ bool gb_operation_response_alloc(struct gb_operation *operation, u8 type; type = operation->type | GB_MESSAGE_TYPE_RESPONSE; - response = gb_operation_message_alloc(hd, type, response_size, - GFP_KERNEL); + response = gb_operation_message_alloc(hd, type, response_size, gfp); if (!response) return false; response->operation = operation; @@ -497,8 +496,10 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, /* Allocate the response buffer for outgoing operations */ if (!(op_flags & GB_OPERATION_FLAG_INCOMING)) { - if (!gb_operation_response_alloc(operation, response_size)) + if (!gb_operation_response_alloc(operation, response_size, + gfp_flags)) { goto err_request; + } } operation->flags = op_flags; @@ -734,7 +735,7 @@ static int gb_operation_response_send(struct gb_operation *operation, if (!operation->response && !gb_operation_is_unidirectional(operation)) { - if (!gb_operation_response_alloc(operation, 0)) + if (!gb_operation_response_alloc(operation, 0, GFP_KERNEL)) return -ENOMEM; } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 685470349460..00189e963a01 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -166,7 +166,7 @@ static inline void gb_operation_destroy(struct gb_operation *operation) } bool gb_operation_response_alloc(struct gb_operation *operation, - size_t response_size); + size_t response_size, gfp_t gfp); int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback, -- cgit v1.2.3-59-g8ed1b From b84abdcb419ff7e2e654381587c61b043a5bc661 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 17 Jul 2015 18:50:26 +0200 Subject: greybus: operation: fix atomic message submission The recently added GFP-flags argument to gb_message_send was never used. Fixes: 9218fac2a24d ("operation: allow atomic request submissions") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 0fe50d80b2db..f2d12e87a502 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -200,7 +200,7 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp) return connection->hd->driver->message_send(connection->hd, connection->hd_cport_id, message, - GFP_KERNEL); + gfp); } /* -- cgit v1.2.3-59-g8ed1b From 392bf9fb2d017c15152380b654b9bfe51a61bff7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:08 +0530 Subject: greybus: connections: comment style fix Replace '* *' with '*'. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 81a5df0e3230..edee5fb4a4b4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -124,7 +124,7 @@ void gb_connection_bind_protocol(struct gb_connection *connection) /* * If we have a valid device_id for the interface block, then we have an * active device, so bring up the connection at the same time. - * */ + */ intf = connection->bundle->intf; if (intf->device_id != GB_DEVICE_ID_BAD) gb_connection_init(connection); -- cgit v1.2.3-59-g8ed1b From 25366869577860c12f0dc8b28e2e34c6265a5a7a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:09 +0530 Subject: greybus: interface: drop stale comment We don't do what the comment says, drop it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 1e5e6290913f..dfc31a0d074d 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -70,8 +70,7 @@ struct device_type greybus_interface_type = { /* * Create kernel structures corresponding to a bundle and connection for - * managing control CPort. Also initialize the bundle, which will request SVC to - * set route and will initialize the control protocol for this connection. + * managing control CPort. */ static int gb_create_control_connection(struct gb_interface *intf) { -- cgit v1.2.3-59-g8ed1b From 948b3bd592817a7d6dd566823ac1279b37637f5c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:10 +0530 Subject: greybus: sync protocol and class definitions Class types aren't in sync with protocol types, lets keep them in sync for now. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index f84cfebd2316..9c4d7cae9bbb 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -67,6 +67,11 @@ enum greybus_class_type { GREYBUS_CLASS_LIGHTS = 0x0f, GREYBUS_CLASS_VIBRATOR = 0x10, GREYBUS_CLASS_LOOPBACK = 0x11, + GREYBUS_CLASS_I2S_RECEIVER = 0x12, + GREYBUS_CLASS_I2S_TRANSMITTER = 0x13, + GREYBUS_CLASS_SVC = 0x14, + /* ... */ + GREYBUS_CLASS_RAW = 0xfe, GREYBUS_CLASS_VENDOR = 0xff, }; -- cgit v1.2.3-59-g8ed1b From 6366d73fd49b68b5c1f4ebddc00d79e3cd7b6e3d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:11 +0530 Subject: greybus: protocol: move version_response structure to greybus_protocols.h Version response structure is also required by external entities like gbsim and so its structure should be moved to greybus_protocols.h. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 9 +++++++++ drivers/staging/greybus/protocol.h | 6 ------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 839f3a6c88c6..fe4b2dddace2 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -58,6 +58,15 @@ #define GB_CONTROL_BUNDLE_ID 0 #define GB_CONTROL_CPORT_ID 2 + +/* version request has no payload */ +struct gb_protocol_version_response { + __u8 major; + __u8 minor; +}; + +/* Control Protocol */ + /* Version of the Greybus control protocol we support */ #define GB_CONTROL_VERSION_MAJOR 0x00 #define GB_CONTROL_VERSION_MINOR 0x01 diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 3f226e3bbe06..758b36ef1f55 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -13,12 +13,6 @@ struct gb_connection; struct gb_operation; -/* version request has no payload */ -struct gb_protocol_version_response { - __u8 major; - __u8 minor; -}; - typedef int (*gb_connection_init_t)(struct gb_connection *); typedef void (*gb_connection_exit_t)(struct gb_connection *); typedef int (*gb_request_recv_t)(u8, struct gb_operation *); -- cgit v1.2.3-59-g8ed1b From c8dd60db6cae0800e44e68e4c2c0c1af136414be Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:12 +0530 Subject: greybus: Define device ids for AP/SVC/modules Define device-ids for AP, SVC and modules (starting id only). Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index fe4b2dddace2..0056c2a30f22 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -52,7 +52,12 @@ #ifndef __GREYBUS_PROTOCOLS_H #define __GREYBUS_PROTOCOLS_H -/* Control Protocol */ +/* Fixed IDs for control/svc protocols */ + +/* Device ID of SVC and AP */ +#define GB_DEVICE_ID_SVC 0 +#define GB_DEVICE_ID_AP 1 +#define GB_DEVICE_ID_MODULES_START 2 /* Bundle-id and cport-id for control cport */ #define GB_CONTROL_BUNDLE_ID 0 -- cgit v1.2.3-59-g8ed1b From ec320625be76641c9f4d594335330a5544716304 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:13 +0530 Subject: greybus: Define cport/bundle for SVC protocol For now, the plan is to use a single cport for both control and svc protocol. Defining separate macros for control and svc protocol's cport/bundle would make the code more flexible, in case we need two separate cports in future. Lets define cport/bundle for svc protocol as well. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 0056c2a30f22..44957bd8955b 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -59,11 +59,19 @@ #define GB_DEVICE_ID_AP 1 #define GB_DEVICE_ID_MODULES_START 2 -/* Bundle-id and cport-id for control cport */ +/* + * Bundle/cport for control/svc cport: The same bundle/cport is shared by both + * CONTROL and SVC protocols for communication between AP and SVC. + */ +#define GB_SVC_BUNDLE_ID 0 +#define GB_SVC_CPORT_ID 2 #define GB_CONTROL_BUNDLE_ID 0 #define GB_CONTROL_CPORT_ID 2 +/* Control Protocol */ + + /* version request has no payload */ struct gb_protocol_version_response { __u8 major; -- cgit v1.2.3-59-g8ed1b From e602df658888d0dc1d51ab907036c67b87ca9f3a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:14 +0530 Subject: greybus: connection: don't send connected/disconnected events for SVC connection SVC is also a special protocol (like control) and AP doesn't need to send (dis)connected events for its cport. Lets skip them. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index edee5fb4a4b4..9187a369bcbb 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -289,7 +289,7 @@ int gb_connection_init(struct gb_connection *connection) * Inform Interface about Active CPorts. We don't need to do this * operation for control cport. */ - if (cport_id != GB_CONTROL_CPORT_ID) { + if (cport_id != GB_CONTROL_CPORT_ID && cport_id != GB_SVC_CPORT_ID) { struct gb_control *control = connection->bundle->intf->control; ret = gb_control_connected_operation(control, cport_id); @@ -341,7 +341,7 @@ void gb_connection_exit(struct gb_connection *connection) * Inform Interface about In-active CPorts. We don't need to do this * operation for control cport. */ - if (cport_id != GB_CONTROL_CPORT_ID) { + if (cport_id != GB_CONTROL_CPORT_ID && cport_id != GB_SVC_CPORT_ID) { struct gb_control *control = connection->bundle->intf->control; int ret; -- cgit v1.2.3-59-g8ed1b From 007f979216bda08bb899c78d4f20c229d5fe6845 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:15 +0530 Subject: greybus: connection: Create gb_connection_create_range() to specify hd-cport-id range We need to allocate specific hd-cport-id for AP's control/svc protocols. Support that by splitting functionality of gb_connection_create() into a new routine, which takes range of hd_cport_id's to allocate from. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 14 +++++++++++--- drivers/staging/greybus/connection.h | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 9187a369bcbb..c6e1d701a676 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -141,8 +141,9 @@ void gb_connection_bind_protocol(struct gb_connection *connection) * Returns a pointer to the new connection if successful, or a null * pointer otherwise. */ -struct gb_connection *gb_connection_create(struct gb_bundle *bundle, - u16 cport_id, u8 protocol_id) +struct gb_connection * +gb_connection_create_range(struct gb_bundle *bundle, u16 cport_id, + u8 protocol_id, u32 ida_start, u32 ida_end) { struct gb_connection *connection; struct greybus_host_device *hd = bundle->intf->hd; @@ -165,7 +166,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, if (!connection) return NULL; - retval = ida_simple_get(id_map, 0, CPORT_ID_MAX, GFP_KERNEL); + retval = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); if (retval < 0) { kfree(connection); return NULL; @@ -221,6 +222,13 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, return connection; } +struct gb_connection *gb_connection_create(struct gb_bundle *bundle, + u16 cport_id, u8 protocol_id) +{ + return gb_connection_create_range(bundle, cport_id, protocol_id, 0, + CPORT_ID_MAX); +} + /* * Cancel all active operations on a connection. * diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index fb7a1fb290ac..bba14b527fed 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -47,6 +47,9 @@ struct gb_connection { struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id); +struct gb_connection *gb_connection_create_range(struct gb_bundle *bundle, + u16 cport_id, u8 protocol_id, u32 ida_start, + u32 ida_end); void gb_connection_destroy(struct gb_connection *connection); int gb_connection_init(struct gb_connection *connection); -- cgit v1.2.3-59-g8ed1b From 75662e5ca9e0d85fd3e94e51712e79c18c10064f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:16 +0530 Subject: greybus: connection: Allow a bundle-less connection We need a bundle-less connection for AP's SVC protocol, as that will be used much before the endo layout and interface-id of the AP is known to greybus core. This updates gb_connection_create_range() to take few more arguments, which were earlier fetched from the 'bundle' pointer. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 31 +++++++++++++++++++------------ drivers/staging/greybus/connection.h | 7 ++++--- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c6e1d701a676..feb6e496338b 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -107,7 +107,6 @@ struct device_type greybus_connection_type = { void gb_connection_bind_protocol(struct gb_connection *connection) { - struct gb_interface *intf; struct gb_protocol *protocol; /* If we already have a protocol bound here, just return */ @@ -125,8 +124,9 @@ void gb_connection_bind_protocol(struct gb_connection *connection) * If we have a valid device_id for the interface block, then we have an * active device, so bring up the connection at the same time. */ - intf = connection->bundle->intf; - if (intf->device_id != GB_DEVICE_ID_BAD) + if ((!connection->bundle && + connection->hd_cport_id == GB_SVC_CPORT_ID) || + connection->bundle->intf->device_id != GB_DEVICE_ID_BAD) gb_connection_init(connection); } @@ -142,11 +142,12 @@ void gb_connection_bind_protocol(struct gb_connection *connection) * pointer otherwise. */ struct gb_connection * -gb_connection_create_range(struct gb_bundle *bundle, u16 cport_id, - u8 protocol_id, u32 ida_start, u32 ida_end) +gb_connection_create_range(struct greybus_host_device *hd, + struct gb_bundle *bundle, struct device *parent, + u16 cport_id, u8 protocol_id, u32 ida_start, + u32 ida_end) { struct gb_connection *connection; - struct greybus_host_device *hd = bundle->intf->hd; struct ida *id_map = &hd->cport_id_map; int retval; u8 major = 0; @@ -157,7 +158,7 @@ gb_connection_create_range(struct gb_bundle *bundle, u16 cport_id, * initialize connections serially so we don't need to worry * about holding the connection lock. */ - if (gb_connection_intf_find(bundle->intf, cport_id)) { + if (bundle && gb_connection_intf_find(bundle->intf, cport_id)) { pr_err("duplicate interface cport id 0x%04hx\n", cport_id); return NULL; } @@ -182,13 +183,13 @@ gb_connection_create_range(struct gb_bundle *bundle, u16 cport_id, connection->bundle = bundle; connection->state = GB_CONNECTION_STATE_DISABLED; - connection->dev.parent = &bundle->dev; + connection->dev.parent = parent; connection->dev.bus = &greybus_bus_type; connection->dev.type = &greybus_connection_type; connection->dev.groups = connection_groups; device_initialize(&connection->dev); dev_set_name(&connection->dev, "%s:%d", - dev_name(&bundle->dev), cport_id); + dev_name(parent), cport_id); retval = device_add(&connection->dev); if (retval) { @@ -206,7 +207,12 @@ gb_connection_create_range(struct gb_bundle *bundle, u16 cport_id, spin_lock_irq(&gb_connections_lock); list_add(&connection->hd_links, &hd->connections); - list_add(&connection->bundle_links, &bundle->connections); + + if (bundle) + list_add(&connection->bundle_links, &bundle->connections); + else + INIT_LIST_HEAD(&connection->bundle_links); + spin_unlock_irq(&gb_connections_lock); atomic_set(&connection->op_cycle, 0); @@ -225,8 +231,9 @@ gb_connection_create_range(struct gb_bundle *bundle, u16 cport_id, struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id) { - return gb_connection_create_range(bundle, cport_id, protocol_id, 0, - CPORT_ID_MAX); + return gb_connection_create_range(bundle->intf->hd, bundle, + &bundle->dev, cport_id, protocol_id, + 0, CPORT_ID_MAX); } /* diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index bba14b527fed..d8fbce402955 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -47,9 +47,10 @@ struct gb_connection { struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id); -struct gb_connection *gb_connection_create_range(struct gb_bundle *bundle, - u16 cport_id, u8 protocol_id, u32 ida_start, - u32 ida_end); +struct gb_connection *gb_connection_create_range(struct greybus_host_device *hd, + struct gb_bundle *bundle, struct device *parent, + u16 cport_id, u8 protocol_id, u32 ida_start, + u32 ida_end); void gb_connection_destroy(struct gb_connection *connection); int gb_connection_init(struct gb_connection *connection); -- cgit v1.2.3-59-g8ed1b From 7a24a3f6ce507a3169bbc38fc70b9d0919a44cd5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:17 +0530 Subject: greybus: interface: Update gb_create_control_connection() to support SVC protocol We need to create bundle/connection for svc cport after the endo layout and interface id is known to the AP. gb_create_control_connection() can be reused for this, but it should be renamed to something more appropriate, as its not about control-connection anymore. Lets name it gb_create_bundle_connection(). Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 34 +++++++++++++++++++++++++++------- drivers/staging/greybus/interface.h | 2 +- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index dfc31a0d074d..6d6128570837 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -70,19 +70,39 @@ struct device_type greybus_interface_type = { /* * Create kernel structures corresponding to a bundle and connection for - * managing control CPort. + * managing control/svc CPort. */ -static int gb_create_control_connection(struct gb_interface *intf) +int gb_create_bundle_connection(struct gb_interface *intf, u8 class) { struct gb_bundle *bundle; + u32 ida_start, ida_end; + u8 bundle_id, protocol_id; + u16 cport_id; + + if (class == GREYBUS_CLASS_CONTROL) { + protocol_id = GREYBUS_PROTOCOL_CONTROL; + bundle_id = GB_CONTROL_BUNDLE_ID; + cport_id = GB_CONTROL_CPORT_ID; + ida_start = 0; + ida_end = CPORT_ID_MAX; + } else if (class == GREYBUS_CLASS_SVC) { + protocol_id = GREYBUS_PROTOCOL_SVC; + bundle_id = GB_SVC_BUNDLE_ID; + cport_id = GB_SVC_CPORT_ID; + ida_start = GB_SVC_CPORT_ID; + ida_end = GB_SVC_CPORT_ID + 1; + } else { + WARN_ON(1); + return -EINVAL; + } - bundle = gb_bundle_create(intf, GB_CONTROL_BUNDLE_ID, - GREYBUS_CLASS_CONTROL); + bundle = gb_bundle_create(intf, bundle_id, class); if (!bundle) return -EINVAL; - if (!gb_connection_create(bundle, GB_CONTROL_CPORT_ID, - GREYBUS_PROTOCOL_CONTROL)) + if (!gb_connection_create_range(bundle->intf->hd, bundle, &bundle->dev, + cport_id, protocol_id, ida_start, + ida_end)) return -EINVAL; return 0; @@ -202,7 +222,7 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) intf->device_id = device_id; /* Establish control CPort connection */ - ret = gb_create_control_connection(intf); + ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_CONTROL); if (ret) { dev_err(&intf->dev, "Failed to create control CPort connection (%d)\n", ret); return ret; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index c2bcb92dacdb..04d330c297e1 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -56,5 +56,5 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id); void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id); void gb_interfaces_remove(struct greybus_host_device *hd); - +int gb_create_bundle_connection(struct gb_interface *intf, u8 class); #endif /* __INTERFACE_H */ -- cgit v1.2.3-59-g8ed1b From d3d448406772e421fb7ed8efd4d02d96cb2ab6f9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:18 +0530 Subject: greybus: svc: Add helpers to create AP<->SVC connection SVC connection is required before the AP knows its position on the endo and type of endo. To enable message processing between the AP and SVC at this time, we need a partially initialized connection which can handle these messages. Once the AP receives more information from the SVC, it can discard this partially initialized connection and create a proper one, tied to a bundle and interface. Destroying the partially initialized connection is a bit tricky, as it is required to send a response to svc-hello. That part will be properly fixed separately. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/svc.c | 88 +++++++++++++++++++++++++++++++++++-- drivers/staging/greybus/svc.h | 3 ++ 5 files changed, 90 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index e795016106c2..2214f447df2b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -102,6 +102,7 @@ struct greybus_host_device { size_t buffer_size_max; struct gb_endo *endo; + struct gb_connection *initial_svc_connection; /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 6d6128570837..f1e2956b25a7 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -183,7 +183,7 @@ put_module: /* * Tear down a previously set up module. */ -static void gb_interface_destroy(struct gb_interface *intf) +void gb_interface_destroy(struct gb_interface *intf) { struct gb_module *module; struct gb_bundle *bundle; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 04d330c297e1..9a9260c43be4 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -53,6 +53,7 @@ struct gb_interface *gb_interface_find(struct greybus_host_device *hd, struct gb_interface *gb_interface_create(struct greybus_host_device *hd, u8 interface_id); int gb_interface_init(struct gb_interface *intf, u8 device_id); +void gb_interface_destroy(struct gb_interface *intf); void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id); void gb_interfaces_remove(struct greybus_host_device *hd); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ce789c909c5f..138238457981 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -12,6 +12,66 @@ /* Define get_version() routine */ define_get_version(gb_svc, SVC); +/* + * AP's SVC cport is required early to get messages from the SVC. This happens + * even before the Endo is created and hence any modules or interfaces. + * + * This is a temporary connection, used only at initial bootup. + */ +struct gb_connection * +gb_ap_svc_connection_create(struct greybus_host_device *hd) +{ + struct gb_connection *connection; + + connection = gb_connection_create_range(hd, NULL, hd->parent, + GB_SVC_CPORT_ID, + GREYBUS_PROTOCOL_SVC, + GB_SVC_CPORT_ID, + GB_SVC_CPORT_ID + 1); + + return connection; +} +EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create); + +/* + * We know endo-type and AP's interface id now, lets create a proper svc + * connection (and its interface/bundle) now and get rid of the initial + * 'partially' initialized one svc connection. + */ +static struct gb_interface * +gb_ap_interface_create(struct greybus_host_device *hd, + struct gb_connection *connection, u8 interface_id) +{ + struct gb_interface *intf; + struct device *dev = &hd->endo->dev; + int ret; + + intf = gb_interface_create(hd, interface_id); + if (!intf) { + dev_err(dev, "%s: Failed to create interface with id %hhu\n", + __func__, interface_id); + return NULL; + } + + intf->device_id = GB_DEVICE_ID_AP; + + /* + * XXX: Disable the initial svc connection here, but don't destroy it + * yet. We do need to send a response of 'svc-hello message' on that. + */ + + /* Establish new control CPort connection */ + ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_SVC); + if (ret) { + dev_err(&intf->dev, "%s: Failed to create svc connection (%d %d)\n", + __func__, interface_id, ret); + gb_interface_destroy(intf); + intf = NULL; + } + + return intf; +} + static int intf_device_id_operation(struct gb_svc *svc, u8 intf_id, u8 device_id) { @@ -207,6 +267,22 @@ static int gb_svc_connection_init(struct gb_connection *connection) svc->connection = connection; connection->private = svc; + + /* + * SVC connection is created twice: + * - before the interface-id of the AP and the endo type is known. + * - after receiving endo type and interface-id of the AP from the SVC. + * + * We should do light-weight initialization for the first case. + */ + if (!connection->bundle) { + WARN_ON(connection->hd->initial_svc_connection); + connection->hd->initial_svc_connection = connection; + return 0; + } + + ida_init(&greybus_svc_device_id_map); + ret = gb_svc_device_setup(svc); if (ret) kfree(svc); @@ -221,11 +297,15 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; - if (WARN_ON(connection->bundle->intf->svc != svc)) - return; - - connection->bundle->intf->svc = NULL; + if (connection->hd->initial_svc_connection == connection) { + connection->hd->initial_svc_connection = NULL; + } else { + if (WARN_ON(connection->bundle->intf->svc != svc)) + return; + connection->bundle->intf->svc = NULL; + } + connection->private = NULL; kfree(svc); } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index ebabe5ff7c6d..66497808ee62 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -26,4 +26,7 @@ int gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); + +struct gb_connection * +gb_ap_svc_connection_create(struct greybus_host_device *hd); #endif /* __SVC_H */ -- cgit v1.2.3-59-g8ed1b From ead35460b5aaeeff5e0376fbae761cc59feb2237 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:19 +0530 Subject: greybus: svc: Implement SVC requests Some of the SVC request are already partially implement, whereas few others are not. This patch implements and updates these requests. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 22 +++- drivers/staging/greybus/svc.c | 163 ++++++++++++++++++++++++++-- 2 files changed, 169 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 44957bd8955b..61fe9dce6ec0 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -556,12 +556,22 @@ struct gb_spi_transfer_response { /* Greybus SVC request types */ #define GB_SVC_TYPE_INVALID 0x00 #define GB_SVC_TYPE_PROTOCOL_VERSION 0x01 -#define GB_SVC_TYPE_INTF_DEVICE_ID 0x02 -#define GB_SVC_TYPE_INTF_HOTPLUG 0x03 -#define GB_SVC_TYPE_INTF_HOT_UNPLUG 0x04 -#define GB_SVC_TYPE_INTF_RESET 0x05 -#define GB_SVC_TYPE_CONN_CREATE 0x06 -#define GB_SVC_TYPE_CONN_DESTROY 0x07 +#define GB_SVC_TYPE_SVC_HELLO 0x02 +#define GB_SVC_TYPE_INTF_DEVICE_ID 0x03 +#define GB_SVC_TYPE_INTF_HOTPLUG 0x04 +#define GB_SVC_TYPE_INTF_HOT_UNPLUG 0x05 +#define GB_SVC_TYPE_INTF_RESET 0x06 +#define GB_SVC_TYPE_CONN_CREATE 0x07 +#define GB_SVC_TYPE_CONN_DESTROY 0x08 + +/* SVC version request/response have same payload as gb_protocol_version_response */ + +/* SVC protocol hello request */ +struct gb_svc_hello_request { + __le16 endo_id; + __u8 interface_id; +}; +/* hello response has no payload */ struct gb_svc_intf_device_id_request { __u8 intf_id; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 138238457981..ee467799d2d7 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -9,6 +9,8 @@ #include "greybus.h" +static struct ida greybus_svc_device_id_map; + /* Define get_version() routine */ define_get_version(gb_svc, SVC); @@ -154,22 +156,99 @@ int gb_svc_connection_destroy(struct gb_svc *svc, } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); +static int gb_svc_version_request(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_protocol_version_response *version; + struct device *dev = &connection->dev; + + version = op->request->payload; + + if (version->major > GB_SVC_VERSION_MAJOR) { + dev_err(&connection->dev, + "unsupported major version (%hhu > %hhu)\n", + version->major, GB_SVC_VERSION_MAJOR); + return -ENOTSUPP; + } + + if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) { + dev_err(dev, "%s: error allocating response\n", + __func__); + return -ENOMEM; + } + + version = op->response->payload; + version->major = GB_SVC_VERSION_MAJOR; + version->minor = GB_SVC_VERSION_MINOR; + return 0; +} + +static int gb_svc_hello(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct greybus_host_device *hd = connection->hd; + struct gb_svc_hello_request *hello_request; + struct device *dev = &connection->dev; + struct gb_interface *intf; + u16 endo_id; + u8 interface_id; + int ret; + + /* Hello message should be received only during early bootup */ + WARN_ON(hd->initial_svc_connection != connection); + + /* + * SVC sends information about the endo and interface-id on the hello + * request, use that to create an endo. + */ + if (op->request->payload_size != sizeof(*hello_request)) { + dev_err(dev, "%s: Illegal size of hello request (%d %d)\n", + __func__, op->request->payload_size, + sizeof(*hello_request)); + return -EINVAL; + } + + hello_request = op->request->payload; + endo_id = le16_to_cpu(hello_request->endo_id); + interface_id = hello_request->interface_id; + + /* Setup Endo */ + ret = greybus_endo_setup(hd, endo_id, interface_id); + if (ret) + return ret; + + /* + * Endo and its modules are ready now, fix AP's partially initialized + * svc protocol and its connection. + */ + intf = gb_ap_interface_create(hd, connection, interface_id); + if (!intf) { + gb_endo_remove(hd->endo); + return ret; + } + + return 0; +} + static int gb_svc_intf_hotplug_recv(struct gb_operation *op) { struct gb_message *request = op->request; - struct gb_svc_intf_hotplug_request *hotplug; - u8 intf_id; + struct gb_svc_intf_hotplug_request *hotplug = request->payload; + struct gb_svc *svc = op->connection->private; + struct greybus_host_device *hd = op->connection->bundle->intf->hd; + struct device *dev = &op->connection->dev; + struct gb_interface *intf; + u8 intf_id, device_id; u32 unipro_mfg_id; u32 unipro_prod_id; u32 ara_vend_id; u32 ara_prod_id; + int ret; if (request->payload_size < sizeof(*hotplug)) { - dev_err(&op->connection->dev, - "short hotplug request received\n"); + dev_err(dev, "%s: short hotplug request received\n", __func__); return -EINVAL; } - hotplug = request->payload; /* * Grab the information we need. @@ -185,15 +264,68 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id); ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id); - /* FIXME Set up the interface here; may required firmware download */ + // FIXME May require firmware download + intf = gb_interface_create(hd, intf_id); + if (!intf) { + dev_err(dev, "%s: Failed to create interface with id %hhu\n", + __func__, intf_id); + return -EINVAL; + } + + /* + * Create a device id for the interface: + * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC + * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP + * + * XXX Do we need to allocate device ID for SVC or the AP here? And what + * XXX about an AP with multiple interface blocks? + */ + device_id = ida_simple_get(&greybus_svc_device_id_map, + GB_DEVICE_ID_MODULES_START, 0, GFP_ATOMIC); + if (device_id < 0) { + ret = device_id; + dev_err(dev, "%s: Failed to allocate device id for interface with id %hhu (%d)\n", + __func__, intf_id, ret); + goto destroy_interface; + } + + ret = intf_device_id_operation(svc, intf_id, device_id); + if (ret) { + dev_err(dev, "%s: Device id operation failed, interface %hhu device_id %hhu (%d)\n", + __func__, intf_id, device_id, ret); + goto ida_put; + } + + ret = gb_interface_init(intf, device_id); + if (ret) { + dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", + __func__, intf_id, device_id, ret); + goto svc_id_free; + } return 0; + +svc_id_free: + /* + * XXX Should we tell SVC that this id doesn't belong to interface + * XXX anymore. + */ +ida_put: + ida_simple_remove(&greybus_svc_device_id_map, device_id); +destroy_interface: + gb_interface_remove(hd, intf_id); + + return ret; } static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) { struct gb_message *request = op->request; - struct gb_svc_intf_hot_unplug_request *hot_unplug; + struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload; + struct greybus_host_device *hd = op->connection->bundle->intf->hd; + struct device *dev = &op->connection->dev; + u8 device_id; + struct gb_interface *intf; u8 intf_id; if (request->payload_size < sizeof(*hot_unplug)) { @@ -201,14 +333,21 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) "short hot unplug request received\n"); return -EINVAL; } - hot_unplug = request->payload; intf_id = hot_unplug->intf_id; - /* FIXME Tear down the interface here */ + intf = gb_interface_find(hd, intf_id); + if (!intf) { + dev_err(dev, "%s: Couldn't find interface for id %hhu\n", + __func__, intf_id); + return -EINVAL; + } - return 0; + device_id = intf->device_id; + gb_interface_remove(hd, intf_id); + ida_simple_remove(&greybus_svc_device_id_map, device_id); + return 0; } static int gb_svc_intf_reset_recv(struct gb_operation *op) @@ -234,6 +373,10 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) static int gb_svc_request_recv(u8 type, struct gb_operation *op) { switch (type) { + case GB_SVC_TYPE_PROTOCOL_VERSION: + return gb_svc_version_request(op); + case GB_SVC_TYPE_SVC_HELLO: + return gb_svc_hello(op); case GB_SVC_TYPE_INTF_HOTPLUG: return gb_svc_intf_hotplug_recv(op); case GB_SVC_TYPE_INTF_HOT_UNPLUG: -- cgit v1.2.3-59-g8ed1b From 18d777cd6686dbaf30fd7b1f7bddc5090c60bc98 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 21 Jul 2015 17:44:20 +0530 Subject: greybus: svc: No need of a separate version request SVC already provided version to the AP on version-request, no need to ask for that again. Drop it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ee467799d2d7..c9bbdc04e2d7 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -11,9 +11,6 @@ static struct ida greybus_svc_device_id_map; -/* Define get_version() routine */ -define_get_version(gb_svc, SVC); - /* * AP's SVC cport is required early to get messages from the SVC. This happens * even before the Endo is created and hence any modules or interfaces. @@ -390,19 +387,9 @@ static int gb_svc_request_recv(u8 type, struct gb_operation *op) } } -/* - * Do initial setup of the SVC. - */ -static int gb_svc_device_setup(struct gb_svc *gb_svc) -{ - /* First thing we need to do is check the version */ - return get_version(gb_svc); -} - static int gb_svc_connection_init(struct gb_connection *connection) { struct gb_svc *svc; - int ret; svc = kzalloc(sizeof(*svc), GFP_KERNEL); if (!svc) @@ -426,14 +413,10 @@ static int gb_svc_connection_init(struct gb_connection *connection) ida_init(&greybus_svc_device_id_map); - ret = gb_svc_device_setup(svc); - if (ret) - kfree(svc); - /* Set interface's svc connection */ connection->bundle->intf->svc = svc; - return ret; + return 0; } static void gb_svc_connection_exit(struct gb_connection *connection) -- cgit v1.2.3-59-g8ed1b From b38fe3472d38933b9fb751918a09d65f1483ef8b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 21 Jul 2015 09:10:27 +0100 Subject: greybus: manifest: reserve control connection cport/bundle ids 5ae6906e ('interface: Get manifest using Control protocol') in gb_create_control_connection introduces the concept that the Control Protocol is at cport_id 2 and bundle_id 0. Currently the manifest parsing code does not enforce that concept and as a result it is possible for a manifest to declare the Control Protocol at a different address. Based on that change 6a6945c9684e ('greybus-spec/control: Formally define Control Protocol reserved addresses') makes the above coding convention a formal requirement of the greybus specification. This patch implements the change introduced in the specification @ 6a6945c9684e. This patch will reject a manifest if it doesn't match the critiera laid down in the spec. This patch makes three changes: - Changes gb_manifest_parse_cports so that only GB_CONTROL_CPORT_ID may have a protocol_id of GREYBUS_PROTOCOL_CONTROL, otherwise the manifest will be rejected. - Changes gb_manifest_parse_bundles so that only GB_CONTROL_BUNDLE_ID may have a class of GREYBUS_CLASS_CONTROL, otherwise the manifest will be rejected. - gb_connection_exit() and gb_connection_destroy() are removed from gb_manifest_parse_cports on error - since gb_manifest_parse_bundles() already has a call into gb_bundle_destroy() which will again call gb_connection_exit() and gb_connection_destroy() leading to an oops. Signed-off-by: Bryan O'Donoghue Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 58 +++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index bea565f1c1d1..a84c07133575 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -201,18 +201,16 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) { struct gb_interface *intf = bundle->intf; - struct gb_connection *connection; - struct gb_connection *connection_next; struct manifest_desc *desc; struct manifest_desc *next; u8 bundle_id = bundle->id; + u8 protocol_id; + u16 cport_id; u32 count = 0; /* Set up all cport descriptors associated with this bundle */ list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { struct greybus_descriptor_cport *desc_cport; - u8 protocol_id; - u16 cport_id; if (desc->type != GREYBUS_TYPE_CPORT) continue; @@ -223,25 +221,26 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) cport_id = le16_to_cpu(desc_cport->id); if (cport_id > CPORT_ID_MAX) - goto cleanup; + goto exit; /* Found one. Set up its function structure */ protocol_id = desc_cport->protocol_id; - /* Don't recreate connection for control cport */ + /* Validate declarations of the control protocol CPort */ if (cport_id == GB_CONTROL_CPORT_ID) { /* This should have protocol set to control protocol*/ - WARN_ON(protocol_id != GREYBUS_PROTOCOL_CONTROL); - + if (protocol_id != GREYBUS_PROTOCOL_CONTROL) + goto print_error_exit; + /* Don't recreate connection for control cport */ goto release_descriptor; } - /* Nothing else should have its protocol as control protocol */ - if (WARN_ON(protocol_id == GREYBUS_PROTOCOL_CONTROL)) - goto release_descriptor; + if (protocol_id == GREYBUS_PROTOCOL_CONTROL) { + goto print_error_exit; + } if (!gb_connection_create(bundle, cport_id, protocol_id)) - goto cleanup; + goto exit; release_descriptor: count++; @@ -251,14 +250,14 @@ release_descriptor: } return count; -cleanup: - /* An error occurred; undo any changes we've made */ - list_for_each_entry_safe(connection, connection_next, - &bundle->connections, bundle_links) { - gb_connection_exit(connection); - gb_connection_destroy(connection); - count--; - } +print_error_exit: + /* A control protocol parse error was encountered */ + dev_err(&bundle->dev, + "cport_id, protocol_id 0x%04hx,0x%04hx want 0x%04hx,0x%04hx\n", + cport_id, protocol_id, GB_CONTROL_CPORT_ID, + GREYBUS_PROTOCOL_CONTROL); +exit: + return 0; /* Error; count should also be 0 */ } @@ -287,15 +286,24 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) /* Don't recreate bundle for control cport */ if (desc_bundle->id == GB_CONTROL_BUNDLE_ID) { /* This should have class set to control class */ - WARN_ON(desc_bundle->class != GREYBUS_CLASS_CONTROL); + if (desc_bundle->class != GREYBUS_CLASS_CONTROL) { + dev_err(&intf->dev, + "bad class 0x%02x for control bundle\n", + desc_bundle->class); + goto cleanup; + } bundle = intf->control->connection->bundle; goto parse_cports; } /* Nothing else should have its class set to control class */ - if (WARN_ON(desc_bundle->class == GREYBUS_CLASS_CONTROL)) - goto release_descriptor; + if (desc_bundle->class == GREYBUS_CLASS_CONTROL) { + dev_err(&intf->dev, + "bundle 0x%02x cannot use control class\n", + desc_bundle->id); + goto cleanup; + } bundle = gb_bundle_create(intf, desc_bundle->id, desc_bundle->class); @@ -307,11 +315,9 @@ parse_cports: if (!gb_manifest_parse_cports(bundle)) goto cleanup; -release_descriptor: - count++; - /* Done with this bundle descriptor */ release_manifest_descriptor(desc); + count++; } return count; -- cgit v1.2.3-59-g8ed1b From 09fb10fe4fc04785eb8a0065c64dd9728ed71dbe Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 21 Jul 2015 09:10:28 +0100 Subject: greybus: manifest: convert pr_err to dev_err This patch converts a dangling pr_err in the manifest parsing error path to a dev_err in order to remain consistent with similar error messages elsewhere. Signed-off-by: Bryan O'Donoghue Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index a84c07133575..559b2ee6e434 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -355,7 +355,7 @@ static bool gb_manifest_parse_interface(struct gb_interface *intf, /* An interface must have at least one bundle descriptor */ if (!gb_manifest_parse_bundles(intf)) { - pr_err("manifest bundle descriptors not valid\n"); + dev_err(&intf->dev, "manifest bundle descriptors not valid\n"); goto out_err; } -- cgit v1.2.3-59-g8ed1b From 3156be7deec47564853d13308090828c35f89cb9 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 21 Jul 2015 23:50:06 +0100 Subject: greybus: loopback: timestamp seeding should not drop metrics In the current code if the ts variable is not initialized then any data already gathered in a previous loopback command is dropped instead of logged. Also the timestamping of ts is done after the greybus operation. This delayed time-stamping means that the delta between the before and after timestamps is incorrect and if a delay in-between loopback operations has been specified by the user, the ts timestamp will be very skewed indeed. - Move the ts initialization directly before the greybus operation. - Remove the continue statement on first initialization of the ts variable. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index fe3a57bff99c..2de939f298af 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -394,6 +394,8 @@ static int gb_loopback_fn(void *data) msleep(1000); continue; } + if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) + do_gettimeofday(&gb->ts); if (gb->type == GB_LOOPBACK_TYPE_PING) error = gb_loopback_ping(gb, &tlat); else if (gb->type == GB_LOOPBACK_TYPE_TRANSFER) @@ -402,10 +404,6 @@ static int gb_loopback_fn(void *data) error = gb_loopback_sink(gb, &tlat, gb->size); if (error) gb->error++; - if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) { - do_gettimeofday(&gb->ts); - continue; - } do_gettimeofday(&gb->te); gb->elapsed_nsecs = timeval_to_ns(&gb->te) - timeval_to_ns(&gb->ts); -- cgit v1.2.3-59-g8ed1b From 799a3f03572afa77913f2adc136c87a4b0c64850 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 21 Jul 2015 23:50:07 +0100 Subject: greybus: loopback: update loopback operation description comment Old comment needs updating to match 8a282c411ba0 ('greybus/loopback: make loopback type input equivalent to protocol type') Signed-off-by: Bryan O'Donoghue Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 2de939f298af..66385c91673e 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -137,10 +137,12 @@ gb_loopback_stats_attrs(throughput); gb_loopback_ro_attr(error, d); /* - * Type of loopback message to send + * Type of loopback message to send based on protocol type definitions * 0 => Don't send message - * 1 => Send ping message continuously (message without payload) - * 2 => Send transer message continuously (message with payload) + * 2 => Send ping message continuously (message without payload) + * 3 => Send transer message continuously (message with payload, + * payload returned in response) + * 4 => Send a sink message (message with payload, no payload in response) */ gb_loopback_attr(type, d); /* Size of transfer message payload: 0-4096 bytes */ -- cgit v1.2.3-59-g8ed1b From 00af6583d15038cfaa81a99632122b67d49de403 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 21 Jul 2015 23:50:08 +0100 Subject: greybus: loopback: run operations a set number of times In order to extract a meaningful set of data out of loopback metrics it makes sense to have the ability to specify how many times a loopback operation should run and then to stop when the threshold is hit. This will allow exactly the same loopback data pattern to be run again and again, which is a fundamental prerequisite to instrumenting and characterizing the loopback interface over time. This patch introduces a simple sysfs controlled variable iteration_max. iteration_max is the maximum number of iterations to run for a given set. iteration_count is used internally to count from zero to iteration_max if-and-only-if iteration_max is non zero. If iteration_max is zero then the original behaviour of running the test command ad infinitum is maintained. User-space should incrementally poll the iteration_count to determine when the sequence is finished. To accomplish this we move away from running as many commands as possible in a one second window and instead run a fixed number of commands and log the time it takes for those commands to complete in aggregate. Since we are no longer resetting counters every one second, the tracker variables have been moved from u32 to u64 to allow for reasonably long tests to gather reasonably large amounts of data, without fear of over-flowing the data-points. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 108 ++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 66385c91673e..65973948a091 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -18,11 +18,11 @@ #include "greybus.h" struct gb_loopback_stats { - u32 min; - u32 max; - u32 avg; - u32 sum; - u32 count; + u64 min; + u64 max; + u64 avg; + u64 sum; + u64 count; }; struct gb_loopback { @@ -34,6 +34,8 @@ struct gb_loopback { int type; u32 size; + u32 iteration_max; + u32 iteration_count; size_t size_max; int ms_wait; @@ -77,9 +79,9 @@ static ssize_t name##_##field##_show(struct device *dev, \ static DEVICE_ATTR_RO(name##_##field) #define gb_loopback_stats_attrs(field) \ - gb_loopback_ro_stats_attr(field, min, d); \ - gb_loopback_ro_stats_attr(field, max, d); \ - gb_loopback_ro_stats_attr(field, avg, d); + gb_loopback_ro_stats_attr(field, min, llu); \ + gb_loopback_ro_stats_attr(field, max, llu); \ + gb_loopback_ro_stats_attr(field, avg, llu); #define gb_loopback_attr(field, type) \ static ssize_t field##_show(struct device *dev, \ @@ -125,6 +127,7 @@ static void gb_loopback_check_attr(struct gb_loopback *gb) if (gb->size > gb->size_max) gb->size = gb->size_max; gb->error = 0; + gb->iteration_count = 0; gb_loopback_reset_stats(gb); } @@ -135,6 +138,7 @@ gb_loopback_stats_attrs(frequency); /* Quantity of data sent and received on this cport */ gb_loopback_stats_attrs(throughput); gb_loopback_ro_attr(error, d); +gb_loopback_ro_attr(iteration_count, u); /* * Type of loopback message to send based on protocol type definitions @@ -149,6 +153,8 @@ gb_loopback_attr(type, d); gb_loopback_attr(size, u); /* Time to wait between two messages: 0-1000 ms */ gb_loopback_attr(ms_wait, d); +/* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */ +gb_loopback_attr(iteration_max, u); #define dev_stats_attrs(name) \ &dev_attr_##name##_min.attr, \ @@ -162,6 +168,8 @@ static struct attribute *loopback_attrs[] = { &dev_attr_type.attr, &dev_attr_size.attr, &dev_attr_ms_wait.attr, + &dev_attr_iteration_count.attr, + &dev_attr_iteration_max.attr, &dev_attr_error.attr, NULL, }; @@ -313,38 +321,29 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) memset(&gb->ts, 0, sizeof(struct timeval)); } -static void gb_loopback_update_stats(struct gb_loopback_stats *stats, - u64 elapsed_nsecs) +static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u64 val) { - u32 avg; - u64 tmp; - - if (elapsed_nsecs >= NSEC_PER_SEC) { - if (!stats->count) { - tmp = elapsed_nsecs; - do_div(tmp, NSEC_PER_SEC); - avg = stats->sum * tmp; - } else { - avg = stats->sum / stats->count; - } - if (stats->min > avg) - stats->min = avg; - if (stats->max < avg) - stats->max = avg; - stats->avg = avg; - stats->count = 0; - stats->sum = 0; - } + if (stats->min > val) + stats->min = val; + if (stats->max < val) + stats->max = val; + stats->sum += val; + stats->count++; + stats->avg = stats->sum; + do_div(stats->avg, stats->count); } -static void gb_loopback_freq_update(struct gb_loopback *gb) +static void gb_loopback_frequency_update(struct gb_loopback *gb, u32 latency) { - gb->frequency.sum++; - gb_loopback_update_stats(&gb->frequency, gb->elapsed_nsecs); + u32 freq = USEC_PER_SEC; + + do_div(freq, latency); + gb_loopback_update_stats(&gb->frequency, freq); } -static void gb_loopback_throughput_update(struct gb_loopback *gb) +static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) { + u32 throughput; u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2; switch (gb->type) { @@ -362,27 +361,31 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb) default: return; } - gb->throughput.sum += aggregate_size; - gb_loopback_update_stats(&gb->throughput, gb->elapsed_nsecs); + + /* Calculate bytes per second */ + throughput = USEC_PER_SEC; + do_div(throughput, latency); + throughput *= aggregate_size; + gb_loopback_update_stats(&gb->throughput, throughput); } -static void gb_loopback_latency_update(struct gb_loopback *gb, +static void gb_loopback_calculate_stats(struct gb_loopback *gb, struct timeval *tlat) { u32 lat; u64 tmp; - tmp = timeval_to_ns(tlat); - do_div(tmp, NSEC_PER_MSEC); + /* Express latency in terms of microseconds */ + tmp = gb->elapsed_nsecs; + do_div(tmp, NSEC_PER_USEC); lat = tmp; - if (gb->latency.min > lat) - gb->latency.min = lat; - if (gb->latency.max < lat) - gb->latency.max = lat; - gb->latency.sum += lat; - gb->latency.count++; - gb_loopback_update_stats(&gb->latency, gb->elapsed_nsecs); + /* Log latency stastic */ + gb_loopback_update_stats(&gb->latency, lat); + + /* Log throughput and frequency using latency as benchmark */ + gb_loopback_throughput_update(gb, lat); + gb_loopback_frequency_update(gb, lat); } static int gb_loopback_fn(void *data) @@ -396,6 +399,14 @@ static int gb_loopback_fn(void *data) msleep(1000); continue; } + if (gb->iteration_max) { + if (gb->iteration_count < gb->iteration_max) { + gb->iteration_count++; + } else { + gb->type = 0; + continue; + } + } if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) do_gettimeofday(&gb->ts); if (gb->type == GB_LOOPBACK_TYPE_PING) @@ -409,11 +420,8 @@ static int gb_loopback_fn(void *data) do_gettimeofday(&gb->te); gb->elapsed_nsecs = timeval_to_ns(&gb->te) - timeval_to_ns(&gb->ts); - gb_loopback_freq_update(gb); - gb_loopback_throughput_update(gb); - gb_loopback_latency_update(gb, &tlat); - if (gb->elapsed_nsecs >= NSEC_PER_SEC) - gb->ts = gb->te; + gb_loopback_calculate_stats(gb, &tlat); + gb->ts = gb->te; if (gb->ms_wait) msleep(gb->ms_wait); -- cgit v1.2.3-59-g8ed1b From e140c75ed9f6897c682e7ac321ef64948f005e64 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 21 Jul 2015 23:50:09 +0100 Subject: greybus: loopback: add commentary to sysfs variables Add some missing comments. Add a TODO to look at doing iteration_count with KOBJ_CHANGE event instead of having user-space poll the value reported in iteration_count to determine when a test set is complete. Signed-off-by: Bryan O'Donoghue Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 65973948a091..cc19f1be7e2f 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -137,8 +137,11 @@ gb_loopback_stats_attrs(latency); gb_loopback_stats_attrs(frequency); /* Quantity of data sent and received on this cport */ gb_loopback_stats_attrs(throughput); +/* Number of errors encountered during loop */ gb_loopback_ro_attr(error, d); +/* The current index of the for (i = 0; i < iteration_max; i++) loop */ gb_loopback_ro_attr(iteration_count, u); +/* TODO iteration_count might be better with the KOBJ_CHANGE event */ /* * Type of loopback message to send based on protocol type definitions -- cgit v1.2.3-59-g8ed1b From 583cbf50e0a4c8918811f245860922353420a378 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 21 Jul 2015 23:50:10 +0100 Subject: greybus: loopback: rename frequency to requests The name frequency does not adequately describe the data-point tracking the number of greybus operations performed in a second by the loopback code. Firmware team is moving towards calling this variable requests-per-second or similar. This patch renames to keep the namespace consistent. Signed-off-by: Bryan O'Donoghue Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index cc19f1be7e2f..50eacd1e11a3 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -41,7 +41,7 @@ struct gb_loopback { struct gb_loopback_stats latency; struct gb_loopback_stats throughput; - struct gb_loopback_stats frequency; + struct gb_loopback_stats requests_per_second; struct timeval ts; struct timeval te; u64 elapsed_nsecs; @@ -133,8 +133,8 @@ static void gb_loopback_check_attr(struct gb_loopback *gb) /* Time to send and receive one message */ gb_loopback_stats_attrs(latency); -/* Number of packet sent per second on this cport */ -gb_loopback_stats_attrs(frequency); +/* Number of requests sent per second on this cport */ +gb_loopback_stats_attrs(requests_per_second); /* Quantity of data sent and received on this cport */ gb_loopback_stats_attrs(throughput); /* Number of errors encountered during loop */ @@ -166,7 +166,7 @@ gb_loopback_attr(iteration_max, u); static struct attribute *loopback_attrs[] = { dev_stats_attrs(latency), - dev_stats_attrs(frequency), + dev_stats_attrs(requests_per_second), dev_stats_attrs(throughput), &dev_attr_type.attr, &dev_attr_size.attr, @@ -320,7 +320,8 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) }; memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb->frequency, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb->requests_per_second, &reset, + sizeof(struct gb_loopback_stats)); memset(&gb->ts, 0, sizeof(struct timeval)); } @@ -336,12 +337,12 @@ static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u64 val) do_div(stats->avg, stats->count); } -static void gb_loopback_frequency_update(struct gb_loopback *gb, u32 latency) +static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency) { - u32 freq = USEC_PER_SEC; + u32 req = USEC_PER_SEC; - do_div(freq, latency); - gb_loopback_update_stats(&gb->frequency, freq); + do_div(req, latency); + gb_loopback_update_stats(&gb->requests_per_second, req); } static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) @@ -386,9 +387,9 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb, /* Log latency stastic */ gb_loopback_update_stats(&gb->latency, lat); - /* Log throughput and frequency using latency as benchmark */ + /* Log throughput and requests using latency as benchmark */ gb_loopback_throughput_update(gb, lat); - gb_loopback_frequency_update(gb, lat); + gb_loopback_requests_update(gb, lat); } static int gb_loopback_fn(void *data) -- cgit v1.2.3-59-g8ed1b From b4bd734ea60c4fd5a79c0e7fff4940686ff3cbc8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Jul 2015 17:49:17 +0200 Subject: greybus: operation: fix operation ordering Make the operation work queue single threaded. The operation work queue was meant to be single threaded, but due to a missing flag instead allowed one active task per CPU, something which could lead to requests being processed out of order on SMP systems. Signed-off-by: Johan Hovold Tested-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f2d12e87a502..e98fc656c98b 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -1043,7 +1043,8 @@ int __init gb_operation_init(void) if (!gb_operation_cache) goto err_destroy_message_cache; - gb_operation_workqueue = alloc_workqueue("greybus_operation", 0, 1); + gb_operation_workqueue = alloc_workqueue("greybus_operation", + WQ_UNBOUND, 1); if (!gb_operation_workqueue) goto err_operation; -- cgit v1.2.3-59-g8ed1b From a4e08469ebd913e8145b8ab3ad0b40cfdd451240 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Jul 2015 17:49:18 +0200 Subject: greybus: operation: clean up greybus_message_sent Add connection variable to greybus_message_sent. This will be put to more use by a follow-up up patch. Signed-off-by: Johan Hovold Tested-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index e98fc656c98b..6b87bcd63615 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -778,7 +778,8 @@ err_put: void greybus_message_sent(struct greybus_host_device *hd, struct gb_message *message, int status) { - struct gb_operation *operation; + struct gb_operation *operation = message->operation; + struct gb_connection *connection = operation->connection; /* * If the message was a response, we just need to drop our @@ -790,10 +791,9 @@ void greybus_message_sent(struct greybus_host_device *hd, * attempting to send it, record that as the result of * the operation and schedule its completion. */ - operation = message->operation; if (message == operation->response) { if (status) { - dev_err(&operation->connection->dev, + dev_err(&connection->dev, "error sending response: %d\n", status); } gb_operation_put_active(operation); -- cgit v1.2.3-59-g8ed1b From 4e2b1e46abf4eef013ab7297a215e21d3c22004e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 22 Jul 2015 17:49:19 +0200 Subject: greybus: connection: fix connection initialisation Make sure connection is fully initialised before registering the connection device and adding it to the host-device and bundle lists. Signed-off-by: Johan Hovold Tested-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index feb6e496338b..4663397cb7e3 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -183,6 +183,10 @@ gb_connection_create_range(struct greybus_host_device *hd, connection->bundle = bundle; connection->state = GB_CONNECTION_STATE_DISABLED; + atomic_set(&connection->op_cycle, 0); + spin_lock_init(&connection->lock); + INIT_LIST_HEAD(&connection->operations); + connection->dev.parent = parent; connection->dev.bus = &greybus_bus_type; connection->dev.type = &greybus_connection_type; @@ -215,10 +219,6 @@ gb_connection_create_range(struct greybus_host_device *hd, spin_unlock_irq(&gb_connections_lock); - atomic_set(&connection->op_cycle, 0); - spin_lock_init(&connection->lock); - INIT_LIST_HEAD(&connection->operations); - /* XXX Will have to establish connections to get version */ gb_connection_bind_protocol(connection); if (!connection->protocol) -- cgit v1.2.3-59-g8ed1b From 155b9f175e2c4b36bf433a1b58e90319ea890de8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 23 Jul 2015 10:30:47 +0530 Subject: greybus: svc: Fix build warning on 64bit systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix below warnings that only generate for a 64 bit system: greybus/svc.c:202:16: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘size_t {aka long unsigned int}’ [-Wformat=] dev_err(dev, "%s: Illegal size of hello request (%d %d)\n", ^ greybus/svc.c:202:16: warning: format ‘%d’ expects argument of type ‘int’, but argument 5 has type ‘long unsigned int’ [-Wformat=] Fixes: 9cbe73e0cf45 ("svc: Implement SVC requests") Reported-by: Greg KH Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index c9bbdc04e2d7..ebd282d33e66 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -199,7 +199,7 @@ static int gb_svc_hello(struct gb_operation *op) * request, use that to create an endo. */ if (op->request->payload_size != sizeof(*hello_request)) { - dev_err(dev, "%s: Illegal size of hello request (%d %d)\n", + dev_err(dev, "%s: Illegal size of hello request (%zu %zu)\n", __func__, op->request->payload_size, sizeof(*hello_request)); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 2975617421f843cac23eeb573a31d541f2aef9cf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 22 Jul 2015 18:21:04 -0700 Subject: greybus: connection: prevent oops for protocol_id sysfs file If we don't have a protocol assigned to a connection, don't oops when trying to read the "protocol_id" sysfs file. Fixes Jira SW-968. Reported-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/connection.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 4663397cb7e3..cac04dd71130 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -80,7 +80,10 @@ protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gb_connection *connection = to_gb_connection(dev); - return sprintf(buf, "%d\n", connection->protocol->id); + if (connection->protocol) + return sprintf(buf, "%d\n", connection->protocol->id); + else + return -EINVAL; } static DEVICE_ATTR_RO(protocol_id); -- cgit v1.2.3-59-g8ed1b From 7c63a827d088363d89883a3b24af9bd2a6926924 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 23 Jul 2015 10:50:00 +0200 Subject: greybus: connection: clean up connection creation Add variable for the host cport id rather than overload retval. Remove redundant automatic variable for the id map. Tested-by: Rui Miguel Silva Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index cac04dd71130..1820676343de 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -152,6 +152,7 @@ gb_connection_create_range(struct greybus_host_device *hd, { struct gb_connection *connection; struct ida *id_map = &hd->cport_id_map; + int hd_cport_id; int retval; u8 major = 0; u8 minor = 1; @@ -170,12 +171,12 @@ gb_connection_create_range(struct greybus_host_device *hd, if (!connection) return NULL; - retval = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); - if (retval < 0) { + hd_cport_id = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); + if (hd_cport_id < 0) { kfree(connection); return NULL; } - connection->hd_cport_id = (u16)retval; + connection->hd_cport_id = hd_cport_id; connection->intf_cport_id = cport_id; connection->hd = hd; @@ -200,8 +201,6 @@ gb_connection_create_range(struct greybus_host_device *hd, retval = device_add(&connection->dev); if (retval) { - struct ida *id_map = &connection->hd->cport_id_map; - ida_simple_remove(id_map, connection->hd_cport_id); connection->hd_cport_id = CPORT_ID_BAD; put_device(&connection->dev); -- cgit v1.2.3-59-g8ed1b From 10f9fa133a295fa0f2f36c1a481b7e8b82b12891 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 23 Jul 2015 10:50:01 +0200 Subject: greybus: connection: clean up creation error paths Clean up connection-creation error paths. Tested-by: Rui Miguel Silva Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1820676343de..555625c3fcef 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -167,15 +167,14 @@ gb_connection_create_range(struct greybus_host_device *hd, return NULL; } + hd_cport_id = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); + if (hd_cport_id < 0) + return NULL; + connection = kzalloc(sizeof(*connection), GFP_KERNEL); if (!connection) - return NULL; + goto err_remove_ida; - hd_cport_id = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); - if (hd_cport_id < 0) { - kfree(connection); - return NULL; - } connection->hd_cport_id = hd_cport_id; connection->intf_cport_id = cport_id; connection->hd = hd; @@ -201,14 +200,13 @@ gb_connection_create_range(struct greybus_host_device *hd, retval = device_add(&connection->dev); if (retval) { - ida_simple_remove(id_map, connection->hd_cport_id); connection->hd_cport_id = CPORT_ID_BAD; put_device(&connection->dev); pr_err("failed to add connection device for cport 0x%04hx\n", cport_id); - return NULL; + goto err_remove_ida; } spin_lock_irq(&gb_connections_lock); @@ -228,6 +226,11 @@ gb_connection_create_range(struct greybus_host_device *hd, "protocol 0x%02hhx handler not found\n", protocol_id); return connection; + +err_remove_ida: + ida_simple_remove(id_map, hd_cport_id); + + return NULL; } struct gb_connection *gb_connection_create(struct gb_bundle *bundle, -- cgit v1.2.3-59-g8ed1b From 5a5bc354c65d9e5f255f1861212ec5fa4852a891 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 23 Jul 2015 10:50:02 +0200 Subject: greybus: operation: use per-connection work queues Replace the global operation work queue with per-connection work queues. There is no need to keep operations strictly ordered across connections, something which only adds unnecessary latency. Tested-by: Rui Miguel Silva Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 10 ++++++++++ drivers/staging/greybus/connection.h | 2 ++ drivers/staging/greybus/operation.c | 24 +++++++----------------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 555625c3fcef..b88abed2e1ad 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -7,6 +7,8 @@ * Released under the GPLv2 only. */ +#include + #include "greybus.h" static DEFINE_SPINLOCK(gb_connections_lock); @@ -99,6 +101,7 @@ static void gb_connection_release(struct device *dev) { struct gb_connection *connection = to_gb_connection(dev); + destroy_workqueue(connection->wq); kfree(connection); } @@ -190,6 +193,11 @@ gb_connection_create_range(struct greybus_host_device *hd, spin_lock_init(&connection->lock); INIT_LIST_HEAD(&connection->operations); + connection->wq = alloc_workqueue("%s:%d", WQ_UNBOUND, 1, + dev_name(parent), cport_id); + if (!connection->wq) + goto err_free_connection; + connection->dev.parent = parent; connection->dev.bus = &greybus_bus_type; connection->dev.type = &greybus_connection_type; @@ -227,6 +235,8 @@ gb_connection_create_range(struct greybus_host_device *hd, return connection; +err_free_connection: + kfree(connection); err_remove_ida: ida_simple_remove(id_map, hd_cport_id); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index d8fbce402955..6b40c4aeeeac 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -39,6 +39,8 @@ struct gb_connection { enum gb_connection_state state; struct list_head operations; + struct workqueue_struct *wq; + atomic_t op_cycle; void *private; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 6b87bcd63615..69218aa4b7f1 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -19,9 +19,6 @@ static struct kmem_cache *gb_operation_cache; static struct kmem_cache *gb_message_cache; -/* Workqueue to handle Greybus operation completions. */ -static struct workqueue_struct *gb_operation_workqueue; - /* Wait queue for synchronous cancellations. */ static DECLARE_WAIT_QUEUE_HEAD(gb_operation_cancellation_queue); @@ -800,7 +797,7 @@ void greybus_message_sent(struct greybus_host_device *hd, gb_operation_put(operation); } else if (status) { if (gb_operation_result_set(operation, status)) - queue_work(gb_operation_workqueue, &operation->work); + queue_work(connection->wq, &operation->work); } } EXPORT_SYMBOL_GPL(greybus_message_sent); @@ -837,7 +834,7 @@ static void gb_connection_recv_request(struct gb_connection *connection, * request handler returns. */ if (gb_operation_result_set(operation, -EINPROGRESS)) - queue_work(gb_operation_workqueue, &operation->work); + queue_work(connection->wq, &operation->work); } /* @@ -877,7 +874,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, /* The rest will be handled in work queue context */ if (gb_operation_result_set(operation, errno)) { memcpy(message->header, data, size); - queue_work(gb_operation_workqueue, &operation->work); + queue_work(connection->wq, &operation->work); } gb_operation_put(operation); @@ -931,12 +928,14 @@ void gb_connection_recv(struct gb_connection *connection, */ void gb_operation_cancel(struct gb_operation *operation, int errno) { + struct gb_connection *connection = operation->connection; + if (WARN_ON(gb_operation_is_incoming(operation))) return; if (gb_operation_result_set(operation, errno)) { gb_message_cancel(operation->request); - queue_work(gb_operation_workqueue, &operation->work); + queue_work(connection->wq, &operation->work); } atomic_inc(&operation->waiters); @@ -1043,15 +1042,8 @@ int __init gb_operation_init(void) if (!gb_operation_cache) goto err_destroy_message_cache; - gb_operation_workqueue = alloc_workqueue("greybus_operation", - WQ_UNBOUND, 1); - if (!gb_operation_workqueue) - goto err_operation; - return 0; -err_operation: - kmem_cache_destroy(gb_operation_cache); - gb_operation_cache = NULL; + err_destroy_message_cache: kmem_cache_destroy(gb_message_cache); gb_message_cache = NULL; @@ -1061,8 +1053,6 @@ err_destroy_message_cache: void gb_operation_exit(void) { - destroy_workqueue(gb_operation_workqueue); - gb_operation_workqueue = NULL; kmem_cache_destroy(gb_operation_cache); gb_operation_cache = NULL; kmem_cache_destroy(gb_message_cache); -- cgit v1.2.3-59-g8ed1b From 701615f82f29d0cce28a397267f847de3ee93b6e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 23 Jul 2015 10:50:03 +0200 Subject: greybus: operation: add completion work queue Add dedicated bound work queue for operation completions and use the connection work queues for incoming requests only. There is no need to keep responses ordered internally or with respect to requests. Instead allow operations to complete as soon as possible when a response arrives (or the operation is cancelled). Note that this also allows synchronous requests to be submitted from request handlers as responses will no longer be blocked on the same single-threaded work queue. Similarly, operations can now also be cancelled from a request handler. Tested-by: Rui Miguel Silva Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 69218aa4b7f1..4e9c4a896365 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -19,6 +19,9 @@ static struct kmem_cache *gb_operation_cache; static struct kmem_cache *gb_message_cache; +/* Workqueue to handle Greybus operation completions. */ +static struct workqueue_struct *gb_operation_completion_wq; + /* Wait queue for synchronous cancellations. */ static DECLARE_WAIT_QUEUE_HEAD(gb_operation_cancellation_queue); @@ -796,8 +799,10 @@ void greybus_message_sent(struct greybus_host_device *hd, gb_operation_put_active(operation); gb_operation_put(operation); } else if (status) { - if (gb_operation_result_set(operation, status)) - queue_work(connection->wq, &operation->work); + if (gb_operation_result_set(operation, status)) { + queue_work(gb_operation_completion_wq, + &operation->work); + } } } EXPORT_SYMBOL_GPL(greybus_message_sent); @@ -874,7 +879,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, /* The rest will be handled in work queue context */ if (gb_operation_result_set(operation, errno)) { memcpy(message->header, data, size); - queue_work(connection->wq, &operation->work); + queue_work(gb_operation_completion_wq, &operation->work); } gb_operation_put(operation); @@ -928,14 +933,12 @@ void gb_connection_recv(struct gb_connection *connection, */ void gb_operation_cancel(struct gb_operation *operation, int errno) { - struct gb_connection *connection = operation->connection; - if (WARN_ON(gb_operation_is_incoming(operation))) return; if (gb_operation_result_set(operation, errno)) { gb_message_cancel(operation->request); - queue_work(connection->wq, &operation->work); + queue_work(gb_operation_completion_wq, &operation->work); } atomic_inc(&operation->waiters); @@ -1042,8 +1045,16 @@ int __init gb_operation_init(void) if (!gb_operation_cache) goto err_destroy_message_cache; + gb_operation_completion_wq = alloc_workqueue("greybus_completion", + 0, 0); + if (!gb_operation_completion_wq) + goto err_destroy_operation_cache; + return 0; +err_destroy_operation_cache: + kmem_cache_destroy(gb_operation_cache); + gb_operation_cache = NULL; err_destroy_message_cache: kmem_cache_destroy(gb_message_cache); gb_message_cache = NULL; @@ -1053,6 +1064,8 @@ err_destroy_message_cache: void gb_operation_exit(void) { + destroy_workqueue(gb_operation_completion_wq); + gb_operation_completion_wq = NULL; kmem_cache_destroy(gb_operation_cache); gb_operation_cache = NULL; kmem_cache_destroy(gb_message_cache); -- cgit v1.2.3-59-g8ed1b From c2939f41bca86d30388e6fa48e469a4e748b71e2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 22 Jul 2015 11:09:23 -0700 Subject: greybus: loopback: add poll support to the iteration_count sysfs file This adds the ability to poll on "iteration_count" in sysfs and be woken up when it changes, saving some cycles constantly hammering on the file waiting for it to change. Signed-off-by: Greg Kroah-Hartman Tested-by: Bryan O'Donoghue --- drivers/staging/greybus/loopback.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 50eacd1e11a3..08f0bee198c2 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -141,7 +141,6 @@ gb_loopback_stats_attrs(throughput); gb_loopback_ro_attr(error, d); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ gb_loopback_ro_attr(iteration_count, u); -/* TODO iteration_count might be better with the KOBJ_CHANGE event */ /* * Type of loopback message to send based on protocol type definitions @@ -406,6 +405,8 @@ static int gb_loopback_fn(void *data) if (gb->iteration_max) { if (gb->iteration_count < gb->iteration_max) { gb->iteration_count++; + sysfs_notify(&gb->connection->dev.kobj, NULL, + "iteration_count"); } else { gb->type = 0; continue; -- cgit v1.2.3-59-g8ed1b From 3dfe8aaaeede22b0601c95cf1a4c2eadcb3851ba Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 24 Jul 2015 10:02:56 +0100 Subject: greybus: loopback: convert loopback wake/sleep to a waitqueue Current code will incrementally poll for gb->type == 0 and sleep. This type of polling strategy wastes cycles. This patch changes the sleep strategy by introducing a wait-queue which waits for gb->type != 0 or kthread_should_stop() to wake-up and work or to wake-up and terminate. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 08f0bee198c2..229494833a16 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -31,6 +31,7 @@ struct gb_loopback { u8 version_minor; struct task_struct *task; + wait_queue_head_t wq; int type; u32 size; @@ -113,22 +114,24 @@ static DEVICE_ATTR_RW(field) static void gb_loopback_reset_stats(struct gb_loopback *gb); static void gb_loopback_check_attr(struct gb_loopback *gb) { + if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) + gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; + if (gb->size > gb->size_max) + gb->size = gb->size_max; + gb->error = 0; + gb->iteration_count = 0; + gb_loopback_reset_stats(gb); + switch (gb->type) { case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: case GB_LOOPBACK_TYPE_SINK: + wake_up(&gb->wq); break; default: gb->type = 0; break; } - if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) - gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; - if (gb->size > gb->size_max) - gb->size = gb->size_max; - gb->error = 0; - gb->iteration_count = 0; - gb_loopback_reset_stats(gb); } /* Time to send and receive one message */ @@ -397,11 +400,12 @@ static int gb_loopback_fn(void *data) struct timeval tlat = {0, 0}; struct gb_loopback *gb = (struct gb_loopback *)data; - while (!kthread_should_stop()) { - if (!gb->type) { - msleep(1000); - continue; - } + while (1) { + if (!gb->type) + wait_event_interruptible(gb->wq, gb->type || + kthread_should_stop()); + if (kthread_should_stop()) + break; if (gb->iteration_max) { if (gb->iteration_count < gb->iteration_max) { gb->iteration_count++; @@ -429,7 +433,6 @@ static int gb_loopback_fn(void *data) gb->ts = gb->te; if (gb->ms_wait) msleep(gb->ms_wait); - } return 0; } @@ -463,6 +466,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb->size_max -= sizeof(struct gb_loopback_transfer_request); gb_loopback_reset_stats(gb); + init_waitqueue_head(&gb->wq); gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { retval = PTR_ERR(gb->task); -- cgit v1.2.3-59-g8ed1b From 67c93ae6f544a0953d1159ffcc0dd14bea77fc53 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:19 +0530 Subject: greybus: svc: fully initialize the partially initialized connection SVC hello message is received now and we should fully initialize the partially initialized connection. This can be done by removing and re-adding the device corresponding to the connection. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 24 ++++++++++++++++++++++++ drivers/staging/greybus/connection.h | 2 ++ drivers/staging/greybus/svc.c | 16 +--------------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b88abed2e1ad..0cd716d58a3a 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -111,6 +111,30 @@ struct device_type greybus_connection_type = { }; +int svc_update_connection(struct gb_interface *intf, + struct gb_connection *connection) +{ + struct gb_bundle *bundle; + + bundle = gb_bundle_create(intf, GB_SVC_BUNDLE_ID, GREYBUS_CLASS_SVC); + if (!bundle) + return -EINVAL; + + device_del(&connection->dev); + connection->bundle = bundle; + connection->dev.parent = &bundle->dev; + dev_set_name(&connection->dev, "%s:%d", dev_name(&bundle->dev), + GB_SVC_CPORT_ID); + + WARN_ON(device_add(&connection->dev)); + + spin_lock_irq(&gb_connections_lock); + list_add(&connection->bundle_links, &bundle->connections); + spin_unlock_irq(&gb_connections_lock); + + return 0; +} + void gb_connection_bind_protocol(struct gb_connection *connection) { struct gb_protocol *protocol; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 6b40c4aeeeac..f02b9d9fb084 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -47,6 +47,8 @@ struct gb_connection { }; #define to_gb_connection(d) container_of(d, struct gb_connection, dev) +int svc_update_connection(struct gb_interface *intf, + struct gb_connection *connection); struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id); struct gb_connection *gb_connection_create_range(struct greybus_host_device *hd, diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ebd282d33e66..28a4ca9f8aca 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -43,7 +43,6 @@ gb_ap_interface_create(struct greybus_host_device *hd, { struct gb_interface *intf; struct device *dev = &hd->endo->dev; - int ret; intf = gb_interface_create(hd, interface_id); if (!intf) { @@ -53,20 +52,7 @@ gb_ap_interface_create(struct greybus_host_device *hd, } intf->device_id = GB_DEVICE_ID_AP; - - /* - * XXX: Disable the initial svc connection here, but don't destroy it - * yet. We do need to send a response of 'svc-hello message' on that. - */ - - /* Establish new control CPort connection */ - ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_SVC); - if (ret) { - dev_err(&intf->dev, "%s: Failed to create svc connection (%d %d)\n", - __func__, interface_id, ret); - gb_interface_destroy(intf); - intf = NULL; - } + svc_update_connection(intf, connection); return intf; } -- cgit v1.2.3-59-g8ed1b From dcd05008e491252280c178d9d5300a885b4f4548 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:20 +0530 Subject: greybus: svc: connection is created only once now We no longer create a fresh connection on receiving svc-hello message, but rather update the initial one. Update 'initial_svc_connection' after the connection is fully initialized. Look for the partially initialized connection while removing hd, as hd might be removed before getting svc-hello requests from svc. Also update gb_svc_connection_init() to initialize id_map on the first (and the only) call to connection-init function. We also can't update connection->bundle->intf->svc, as its a bundle-less connection. Lets stop updating intf->svc as its not really used. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 6 ++++++ drivers/staging/greybus/svc.c | 28 +++++----------------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 3b1be2dd2359..bbfae4a01ce3 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -236,6 +236,12 @@ void greybus_remove_hd(struct greybus_host_device *hd) gb_interfaces_remove(hd); gb_endo_remove(hd->endo); + /* Is the SVC still using the partially uninitialized connection ? */ + if (hd->initial_svc_connection) { + gb_connection_exit(hd->initial_svc_connection); + gb_connection_destroy(hd->initial_svc_connection); + } + /* * Make sure there are no leftovers that can potentially corrupt sysfs. */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 28a4ca9f8aca..969c3899e954 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -54,6 +54,9 @@ gb_ap_interface_create(struct greybus_host_device *hd, intf->device_id = GB_DEVICE_ID_AP; svc_update_connection(intf, connection); + /* Its no longer a partially initialized connection */ + hd->initial_svc_connection = NULL; + return intf; } @@ -384,24 +387,11 @@ static int gb_svc_connection_init(struct gb_connection *connection) svc->connection = connection; connection->private = svc; - /* - * SVC connection is created twice: - * - before the interface-id of the AP and the endo type is known. - * - after receiving endo type and interface-id of the AP from the SVC. - * - * We should do light-weight initialization for the first case. - */ - if (!connection->bundle) { - WARN_ON(connection->hd->initial_svc_connection); - connection->hd->initial_svc_connection = connection; - return 0; - } + WARN_ON(connection->hd->initial_svc_connection); + connection->hd->initial_svc_connection = connection; ida_init(&greybus_svc_device_id_map); - /* Set interface's svc connection */ - connection->bundle->intf->svc = svc; - return 0; } @@ -409,14 +399,6 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; - if (connection->hd->initial_svc_connection == connection) { - connection->hd->initial_svc_connection = NULL; - } else { - if (WARN_ON(connection->bundle->intf->svc != svc)) - return; - connection->bundle->intf->svc = NULL; - } - connection->private = NULL; kfree(svc); } -- cgit v1.2.3-59-g8ed1b From b45864d40ca6f453a708d1e74c2108424ac15bdb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:21 +0530 Subject: greybus: svc: remove interface->svc pointer Its not updated/used anymore, remove it. Also move back the struct gb_svc to svc.c as its not referenced by external users anymore. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.h | 1 - drivers/staging/greybus/svc.c | 6 ++++++ drivers/staging/greybus/svc.h | 7 +------ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 9a9260c43be4..fc2d29fc7ddb 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -14,7 +14,6 @@ struct gb_interface { struct device dev; struct gb_control *control; - struct gb_svc *svc; struct list_head bundles; struct list_head links; /* greybus_host_device->interfaces */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 969c3899e954..5f2b2b43b939 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -9,6 +9,12 @@ #include "greybus.h" +struct gb_svc { + struct gb_connection *connection; + u8 version_major; + u8 version_minor; +}; + static struct ida greybus_svc_device_id_map; /* diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 66497808ee62..ee39479cf9b2 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -10,12 +10,7 @@ #ifndef __SVC_H #define __SVC_H -struct gb_svc { - struct gb_connection *connection; - u8 version_major; - u8 version_minor; -}; - +struct gb_svc; int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); -- cgit v1.2.3-59-g8ed1b From 23119de4e69c8a96f10de4bcc62d3751e7d77eb4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:22 +0530 Subject: greybus: interface: localize gb_interface_destroy() Its not referenced by svc or any other code anymore, lets stop exposing it to rest of the files. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index fc2d29fc7ddb..558e6dbaa26b 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -52,7 +52,6 @@ struct gb_interface *gb_interface_find(struct greybus_host_device *hd, struct gb_interface *gb_interface_create(struct greybus_host_device *hd, u8 interface_id); int gb_interface_init(struct gb_interface *intf, u8 device_id); -void gb_interface_destroy(struct gb_interface *intf); void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id); void gb_interfaces_remove(struct greybus_host_device *hd); -- cgit v1.2.3-59-g8ed1b From e619e8505b66229fdf2735ac00891d340111b0a8 Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:29 -0400 Subject: greybus: battery: fix panic on operation error If an operation times out or otherwise returns an error, val->intval should not be set and an error-code should be returned. Fixes a panic on unload while receiving -ENOTCONN. Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index aad174975258..e66623966dc9 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -281,7 +281,7 @@ static int get_property(struct power_supply *b, return -EINVAL; } - return 0; + return (val->intval < 0) ? val->intval : 0; } // FIXME - verify this list, odds are some can be removed and others added. -- cgit v1.2.3-59-g8ed1b From 52fa38327b39f1d307447023a8eea1f7b7ca0039 Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:30 -0400 Subject: greybus: connection: silence warning on unbound protocols If a protocol was not successfully created, we can't drop the refcount on it. This might happen for example if the connection fails to bind a protocol. Silences a warning on cleanup. Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 3 ++- drivers/staging/greybus/protocol.c | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 0cd716d58a3a..c57deb2053ac 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -319,7 +319,8 @@ void gb_connection_destroy(struct gb_connection *connection) list_del(&connection->hd_links); spin_unlock_irq(&gb_connections_lock); - gb_protocol_put(connection->protocol); + if (connection->protocol) + gb_protocol_put(connection->protocol); connection->protocol = NULL; id_map = &connection->hd->cport_id_map; diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 6aebbbd585e1..06b4841173ce 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -196,9 +196,6 @@ void gb_protocol_put(struct gb_protocol *protocol) u8 minor; u8 protocol_count; - if (WARN_ON(!protocol)) - return; - id = protocol->id; major = protocol->major; minor = protocol->minor; -- cgit v1.2.3-59-g8ed1b From 77c461fa861ac813143510caaa712bfb93d15b63 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 18:42:03 +0530 Subject: greybus: connection: compare hd_cport_id for matching SVC protocol SVC protocol isn't per interface, but per entire entire endo. And so svc cport id must be matched against hd_cport_id, not per-interface cport id. Fixes: 82edfac17e5f ("connection: don't send connected/disconnected events for SVC connection") Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c57deb2053ac..b32da8af68f4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -344,7 +344,8 @@ int gb_connection_init(struct gb_connection *connection) * Inform Interface about Active CPorts. We don't need to do this * operation for control cport. */ - if (cport_id != GB_CONTROL_CPORT_ID && cport_id != GB_SVC_CPORT_ID) { + if (cport_id != GB_CONTROL_CPORT_ID && + connection->hd_cport_id != GB_SVC_CPORT_ID) { struct gb_control *control = connection->bundle->intf->control; ret = gb_control_connected_operation(control, cport_id); @@ -396,7 +397,8 @@ void gb_connection_exit(struct gb_connection *connection) * Inform Interface about In-active CPorts. We don't need to do this * operation for control cport. */ - if (cport_id != GB_CONTROL_CPORT_ID && cport_id != GB_SVC_CPORT_ID) { + if (cport_id != GB_CONTROL_CPORT_ID && + connection->hd_cport_id != GB_SVC_CPORT_ID) { struct gb_control *control = connection->bundle->intf->control; int ret; -- cgit v1.2.3-59-g8ed1b From 8aa013526e62639e4e1dd1bec75a84fe27ab7432 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:23 +0530 Subject: greybus: es1/2: Lets start using svc protocol All bits and pieces are in place now. Lets start using svc protocol instead of stuff present in ap.c. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 15 ++++----------- drivers/staging/greybus/es2.c | 15 ++++----------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 0cb7a3c7ef72..7d764a5afeb3 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -615,8 +615,6 @@ static int ap_probe(struct usb_interface *interface, bool bulk_out_found = false; int retval = -ENOMEM; int i; - u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC - u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ @@ -718,16 +716,11 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* - * XXX Soon this will be initiated later, with a combination - * XXX of a Control protocol probe operation and a - * XXX subsequent Control protocol connected operation for - * XXX the SVC connection. At that point we know we're - * XXX properly connected to an Endo. - */ - retval = greybus_endo_setup(hd, endo_id, ap_intf_id); - if (retval) + /* Initialize AP's greybus interface */ + if (!gb_ap_svc_connection_create(hd)) { + retval = -EINVAL; goto error; + } /* Start up our svc urb, which allows events to start flowing */ retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 323721a2e2aa..7621e95071ed 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -715,8 +715,6 @@ static int ap_probe(struct usb_interface *interface, int bulk_out = 0; int retval = -ENOMEM; int i; - u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC - u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ @@ -822,16 +820,11 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* - * XXX Soon this will be initiated later, with a combination - * XXX of a Control protocol probe operation and a - * XXX subsequent Control protocol connected operation for - * XXX the SVC connection. At that point we know we're - * XXX properly connected to an Endo. - */ - retval = greybus_endo_setup(hd, endo_id, ap_intf_id); - if (retval) + /* Initialize AP's greybus interface */ + if (!gb_ap_svc_connection_create(hd)) { + retval = -EINVAL; goto error; + } /* Start up our svc urb, which allows events to start flowing */ retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); -- cgit v1.2.3-59-g8ed1b From 8c116e8d8e99ba6220f1a2cf8bc40b8d34d583d3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:24 +0530 Subject: greybus: Switch to Cport 0 for svc and control protocol Initially we fixed it to Cport 2, but its changed to Cport 0 now. Lets switch that in code as well. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 61fe9dce6ec0..9708e934242f 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -64,9 +64,9 @@ * CONTROL and SVC protocols for communication between AP and SVC. */ #define GB_SVC_BUNDLE_ID 0 -#define GB_SVC_CPORT_ID 2 +#define GB_SVC_CPORT_ID 0 #define GB_CONTROL_BUNDLE_ID 0 -#define GB_CONTROL_CPORT_ID 2 +#define GB_CONTROL_CPORT_ID 0 /* Control Protocol */ -- cgit v1.2.3-59-g8ed1b From 972e84a8dc3c1fdcff5e423866559c87354cec68 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 16:47:07 -0700 Subject: greybus: es1: remove svc endpoint message handling We have switched over to use the "new" svc messages, no more need to have a special USB endpoint to handle them, they come through the normal CPort messages. Based on a patch from Viresh. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 71 ++----------------------------------------- 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 7d764a5afeb3..82a7c67c1037 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -18,7 +18,6 @@ #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { @@ -58,11 +57,8 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure * @control_endpoint: endpoint to send data to SVC - * @svc_endpoint: endpoint for SVC data in * @cport_in_endpoint: bulk in endpoint for CPort data * @cport-out_endpoint: bulk out endpoint for CPort data - * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint - * @svc_urb: urb for SVC messages coming in on @svc_endpoint * @cport_in_urb: array of urbs for the CPort in messages * @cport_in_buffer: array of buffers for the @cport_in_urb urbs * @cport_out_urb: array of urbs for the CPort out messages @@ -78,13 +74,9 @@ struct es1_ap_dev { struct greybus_host_device *hd; __u8 control_endpoint; - __u8 svc_endpoint; __u8 cport_in_endpoint; __u8 cport_out_endpoint; - u8 *svc_buffer; - struct urb *svc_urb; - struct urb *cport_in_urb[NUM_CPORT_IN_URB]; u8 *cport_in_buffer[NUM_CPORT_IN_URB]; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; @@ -367,12 +359,6 @@ static void ap_disconnect(struct usb_interface *interface) es1->cport_in_buffer[i] = NULL; } - usb_kill_urb(es1->svc_urb); - usb_free_urb(es1->svc_urb); - es1->svc_urb = NULL; - kfree(es1->svc_buffer); - es1->svc_buffer = NULL; - usb_set_intfdata(interface, NULL); udev = es1->usb_dev; greybus_remove_hd(es1->hd); @@ -380,33 +366,6 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(udev); } -/* Callback for when we get a SVC message */ -static void svc_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb svc in error %d (dropped)\n", status); - return; - } - - /* We have a message, create a new message structure, add it to the - * list, and wake up our thread that will process the messages. - */ - greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); - -exit: - /* resubmit the urb to get more messages */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "Can not submit urb for AP data: %d\n", retval); -} - static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; @@ -610,12 +569,10 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - bool int_in_found = false; bool bulk_in_found = false; bool bulk_out_found = false; int retval = -ENOMEM; int i; - u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); @@ -644,11 +601,7 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_int_in(endpoint)) { - es1->svc_endpoint = endpoint->bEndpointAddress; - svc_interval = endpoint->bInterval; - int_in_found = true; - } else if (usb_endpoint_is_bulk_in(endpoint)) { + if (usb_endpoint_is_bulk_in(endpoint)) { es1->cport_in_endpoint = endpoint->bEndpointAddress; bulk_in_found = true; } else if (usb_endpoint_is_bulk_out(endpoint)) { @@ -660,27 +613,12 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if ((int_in_found == false) || - (bulk_in_found == false) || + if ((bulk_in_found == false) || (bulk_out_found == false)) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } - /* Create our buffer and URB to get SVC messages, and start it up */ - es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); - if (!es1->svc_buffer) - goto error; - - es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!es1->svc_urb) - goto error; - - usb_fill_int_urb(es1->svc_urb, udev, - usb_rcvintpipe(udev, es1->svc_endpoint), - es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, - hd, svc_interval); - /* Allocate buffers for our cport in messages and start them up */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { struct urb *urb; @@ -722,11 +660,6 @@ static int ap_probe(struct usb_interface *interface, goto error; } - /* Start up our svc urb, which allows events to start flowing */ - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, -- cgit v1.2.3-59-g8ed1b From 939799057e69f9691919e987b145d5e009358905 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 17:09:48 -0700 Subject: greybus: es2: remove svc endpoint message handling We have switched over to use the "new" svc messages, no more need to have a special USB endpoint to handle them, they come through the normal CPort messages. Based on a patch from Viresh. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 71 ++----------------------------------------- 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 7621e95071ed..c353ca56ce96 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -18,7 +18,6 @@ #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { @@ -85,10 +84,7 @@ struct es1_cport_out { * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure * @control_endpoint: endpoint to send data to SVC - * @svc_endpoint: endpoint for SVC data in - * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint - * @svc_urb: urb for SVC messages coming in on @svc_endpoint * @cport_in: endpoint, urbs and buffer for cport in messages * @cport_out: endpoint for for cport out messages * @cport_out_urb: array of urbs for the CPort out messages @@ -104,10 +100,6 @@ struct es1_ap_dev { struct greybus_host_device *hd; __u8 control_endpoint; - __u8 svc_endpoint; - - u8 *svc_buffer; - struct urb *svc_urb; struct es1_cport_in cport_in[NUM_BULKS]; struct es1_cport_out cport_out[NUM_BULKS]; @@ -467,12 +459,6 @@ static void ap_disconnect(struct usb_interface *interface) } } - usb_kill_urb(es1->svc_urb); - usb_free_urb(es1->svc_urb); - es1->svc_urb = NULL; - kfree(es1->svc_buffer); - es1->svc_buffer = NULL; - usb_set_intfdata(interface, NULL); udev = es1->usb_dev; greybus_remove_hd(es1->hd); @@ -480,33 +466,6 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(udev); } -/* Callback for when we get a SVC message */ -static void svc_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb svc in error %d (dropped)\n", status); - return; - } - - /* We have a message, create a new message structure, add it to the - * list, and wake up our thread that will process the messages. - */ - greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); - -exit: - /* resubmit the urb to get more messages */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "Can not submit urb for AP data: %d\n", retval); -} - static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; @@ -710,12 +669,10 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - bool int_in_found = false; int bulk_in = 0; int bulk_out = 0; int retval = -ENOMEM; int i; - u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); @@ -744,11 +701,7 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_int_in(endpoint)) { - es1->svc_endpoint = endpoint->bEndpointAddress; - svc_interval = endpoint->bInterval; - int_in_found = true; - } else if (usb_endpoint_is_bulk_in(endpoint)) { + if (usb_endpoint_is_bulk_in(endpoint)) { es1->cport_in[bulk_in++].endpoint = endpoint->bEndpointAddress; } else if (usb_endpoint_is_bulk_out(endpoint)) { @@ -760,27 +713,12 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if ((int_in_found == false) || - (bulk_in == 0) || + if ((bulk_in == 0) || (bulk_out == 0)) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } - /* Create our buffer and URB to get SVC messages, and start it up */ - es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); - if (!es1->svc_buffer) - goto error; - - es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!es1->svc_urb) - goto error; - - usb_fill_int_urb(es1->svc_urb, udev, - usb_rcvintpipe(udev, es1->svc_endpoint), - es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, - hd, svc_interval); - /* Allocate buffers for our cport in messages and start them up */ for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; @@ -826,11 +764,6 @@ static int ap_probe(struct usb_interface *interface, goto error; } - /* Start up our svc urb, which allows events to start flowing */ - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, -- cgit v1.2.3-59-g8ed1b From 0af7268882755ae8c3807129dfbbdbe836fd0228 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:26 +0530 Subject: greybus: get rid of old svc-protocol Its not used anymore as we have more sophisticated svc protocol in place, lets get rid of earlier code. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 - drivers/staging/greybus/ap.c | 351 -------------------------------------- drivers/staging/greybus/core.c | 9 - drivers/staging/greybus/greybus.h | 2 - 4 files changed, 363 deletions(-) delete mode 100644 drivers/staging/greybus/ap.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1467c5b3fcd8..d14bf4d21983 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,6 +1,5 @@ greybus-y := core.o \ debugfs.o \ - ap.o \ manifest.o \ endo.o \ module.o \ diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c deleted file mode 100644 index fc238170ad12..000000000000 --- a/drivers/staging/greybus/ap.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Greybus "AP" message loop handling - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include "svc_msg.h" -#include "greybus_manifest.h" -#include "greybus.h" - -struct ap_msg { - u8 *data; - size_t size; - struct greybus_host_device *hd; - struct work_struct event; -}; - -static struct workqueue_struct *ap_workqueue; - -static struct svc_msg *svc_msg_alloc(enum svc_function_id id) -{ - struct svc_msg *svc_msg; - - svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL); - if (!svc_msg) - return NULL; - - // FIXME - verify we are only sending function IDs we should be - svc_msg->header.function_id = id; - return svc_msg; -} - -static void svc_msg_free(struct svc_msg *svc_msg) -{ - kfree(svc_msg); -} - -static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - int retval; - - // FIXME - Do we need to do more than just pass it to the hd and then - // free it? - retval = hd->driver->submit_svc(svc_msg, hd); - - svc_msg_free(svc_msg); - return retval; -} - -static void svc_handshake(struct svc_function_handshake *handshake, - int payload_length, struct greybus_host_device *hd) -{ - struct svc_msg *svc_msg; - - if (payload_length != sizeof(*handshake)) { - dev_err(hd->parent, - "Illegal size of svc handshake message %d\n", - payload_length); - return; - } - - /* A new SVC communication channel, let's verify a supported version */ - if ((handshake->version_major != GREYBUS_VERSION_MAJOR) || - (handshake->version_minor != GREYBUS_VERSION_MINOR)) { - dev_warn(hd->parent, - "received invalid greybus version %u.%u\n", - handshake->version_major, handshake->version_minor); - return; - } - - /* Validate that the handshake came from the SVC */ - if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) { - /* we don't know what to do with this, log it and return */ - dev_dbg(hd->parent, "received invalid handshake type %d\n", - handshake->handshake_type); - return; - } - - /* Send back a AP_HELLO message */ - svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE); - if (!svc_msg) - return; - - svc_msg->header.message_type = SVC_MSG_DATA; - svc_msg->header.payload_length = - cpu_to_le16(sizeof(*handshake)); - svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR; - svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR; - svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; - - (void)svc_msg_send(svc_msg, hd); -} - -static void svc_management(struct svc_function_unipro_management *management, - int payload_length, struct greybus_host_device *hd) -{ - struct gb_interface *intf; - int ret; - - if (payload_length != sizeof(*management)) { - dev_err(hd->parent, - "Illegal size of svc management message %d\n", - payload_length); - return; - } - - switch (management->management_packet_type) { - case SVC_MANAGEMENT_AP_ID: - hd->device_id = management->ap_id.device_id; - break; - case SVC_MANAGEMENT_LINK_UP: - intf = gb_interface_find(hd, management->link_up.interface_id); - if (!intf) { - dev_err(hd->parent, "Interface ID %d not found\n", - management->link_up.interface_id); - return; - } - ret = gb_interface_init(intf, management->link_up.device_id); - if (ret) { - dev_err(hd->parent, - "error %d initializing interface %hhu\n", - ret, management->link_up.interface_id); - return; - } - break; - default: - dev_err(hd->parent, "Unhandled UniPro management message\n"); - } -} - -static void svc_hotplug(struct svc_function_hotplug *hotplug, - int payload_length, struct greybus_host_device *hd) -{ - u8 interface_id = hotplug->interface_id; - - switch (hotplug->hotplug_event) { - case SVC_HOTPLUG_EVENT: - /* Add a new interface to the system */ - if (payload_length != sizeof(*hotplug)) { - dev_err(hd->parent, - "Illegal size of svc hotplug message %d\n", - payload_length); - return; - } - dev_dbg(hd->parent, "interface id %d added\n", interface_id); - gb_interface_create(hd, interface_id); - break; - - case SVC_HOTUNPLUG_EVENT: - /* Remove a interface from the system */ - if (payload_length != sizeof(*hotplug)) { - dev_err(hd->parent, - "Illegal size of svc hotunplug message %d\n", - payload_length); - return; - } - dev_dbg(hd->parent, "interface id %d removed\n", interface_id); - gb_interface_remove(hd, interface_id); - break; - - default: - dev_err(hd->parent, - "Received invalid hotplug message type %d\n", - hotplug->hotplug_event); - break; - } -} - -static void svc_power(struct svc_function_power *power, - int payload_length, struct greybus_host_device *hd) -{ - u8 interface_id = power->interface_id; - - /* - * The AP is only allowed to get a Battery Status message, not a Battery - * Status Request - */ - if (power->power_type != SVC_POWER_BATTERY_STATUS) { - dev_err(hd->parent, "Received invalid power type %d\n", - power->power_type); - return; - } - - /* - * As struct struct svc_function_power_battery_status_request is 0 bytes - * big, we can just check the union of the whole structure to validate - * the size of this message. - */ - if (payload_length != sizeof(*power)) { - dev_err(hd->parent, - "Illegal size of svc power message %d\n", - payload_length); - return; - } - - dev_dbg(hd->parent, "power status for interface id %d is %d\n", - interface_id, power->status.status); - - // FIXME - do something with the power information, like update our - // battery information... -} - -static void svc_epm(struct svc_function_epm *epm, - int payload_length, struct greybus_host_device *hd) -{ - /* What? An AP should not get this message */ - dev_err(hd->parent, "Got an EPM message???\n"); -} - -static void svc_suspend(struct svc_function_suspend *suspend, - int payload_length, struct greybus_host_device *hd) -{ - /* What? An AP should not get this message */ - dev_err(hd->parent, "Got an suspend message???\n"); -} - -static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) -{ - struct svc_msg *svc_msg; - struct svc_msg_header *header; - struct greybus_host_device *hd = ap_msg->hd; - - svc_msg = (struct svc_msg *)ap_msg->data; - header = &svc_msg->header; - - /* Validate the message type */ - if (header->message_type != SVC_MSG_DATA) { - dev_err(hd->parent, "message type %d received?\n", - header->message_type); - return NULL; - } - - /* - * The validation of the size of the message buffer happens in each - * svc_* function, due to the different types of messages, keeping the - * logic for each message only in one place. - */ - - return svc_msg; -} - -static void ap_process_event(struct work_struct *work) -{ - struct svc_msg *svc_msg; - struct greybus_host_device *hd; - struct ap_msg *ap_msg; - int payload_length; - - ap_msg = container_of(work, struct ap_msg, event); - hd = ap_msg->hd; - - /* Turn the "raw" data into a real message */ - svc_msg = convert_ap_message(ap_msg); - if (!svc_msg) - return; - - payload_length = le16_to_cpu(svc_msg->header.payload_length); - - /* Look at the message to figure out what to do with it */ - switch (svc_msg->header.function_id) { - case SVC_FUNCTION_HANDSHAKE: - svc_handshake(&svc_msg->handshake, payload_length, hd); - break; - case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT: - svc_management(&svc_msg->management, payload_length, hd); - break; - case SVC_FUNCTION_HOTPLUG: - svc_hotplug(&svc_msg->hotplug, payload_length, hd); - break; - case SVC_FUNCTION_POWER: - svc_power(&svc_msg->power, payload_length, hd); - break; - case SVC_FUNCTION_EPM: - svc_epm(&svc_msg->epm, payload_length, hd); - break; - case SVC_FUNCTION_SUSPEND: - svc_suspend(&svc_msg->suspend, payload_length, hd); - break; - default: - dev_err(hd->parent, "received invalid SVC function ID %d\n", - svc_msg->header.function_id); - } - - /* clean the message up */ - kfree(ap_msg->data); - kfree(ap_msg); -} - -int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size) -{ - struct ap_msg *ap_msg; - - /* - * Totally naive copy the message into a new structure that we slowly - * create and add it to the list. Let's get this working, the odds of - * this being any "slow path" for AP messages is really low at this - * point in time, but you never know, so this comment is here to point - * out that maybe we should use a slab allocator, or even just not copy - * the data, but use it directly and force the urbs to be "new" each - * time. - */ - - /* Note - this can, and will, be called in interrupt context. */ - ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC); - if (!ap_msg) - return -ENOMEM; - ap_msg->data = kmalloc(size, GFP_ATOMIC); - if (!ap_msg->data) { - kfree(ap_msg); - return -ENOMEM; - } - memcpy(ap_msg->data, data, size); - ap_msg->size = size; - ap_msg->hd = hd; - - INIT_WORK(&ap_msg->event, ap_process_event); - queue_work(ap_workqueue, &ap_msg->event); - - return 0; -} -EXPORT_SYMBOL_GPL(greybus_svc_in); - -int __init gb_ap_init(void) -{ - ap_workqueue = alloc_ordered_workqueue("greybus_ap", 0); - if (!ap_workqueue) - return -ENOMEM; - - return 0; -} - -void gb_ap_exit(void) -{ - destroy_workqueue(ap_workqueue); - ap_workqueue = NULL; -} - - diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index bbfae4a01ce3..18075f7ec038 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -269,12 +269,6 @@ static int __init gb_init(void) goto error_bus; } - retval = gb_ap_init(); - if (retval) { - pr_err("gb_ap_init failed (%d)\n", retval); - goto error_ap; - } - retval = gb_operation_init(); if (retval) { pr_err("gb_operation_init failed (%d)\n", retval); @@ -308,8 +302,6 @@ error_control: error_endo: gb_operation_exit(); error_operation: - gb_ap_exit(); -error_ap: bus_unregister(&greybus_bus_type); error_bus: gb_debugfs_cleanup(); @@ -324,7 +316,6 @@ static void __exit gb_exit(void) gb_control_protocol_exit(); gb_endo_exit(); gb_operation_exit(); - gb_ap_exit(); bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2214f447df2b..90f996276ca4 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -156,8 +156,6 @@ void greybus_deregister_driver(struct greybus_driver *driver); int greybus_disabled(void); int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); -int gb_ap_init(void); -void gb_ap_exit(void); void gb_debugfs_init(void); void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); -- cgit v1.2.3-59-g8ed1b From 4de9b5679a13e4154a201996115ceb4a9f95cadb Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:31 -0400 Subject: greybus: svc: add route create operation Implement the SVC Protocol Route Create Operation. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 8 ++++++++ drivers/staging/greybus/svc.c | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9708e934242f..5b97073f4100 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -563,6 +563,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_RESET 0x06 #define GB_SVC_TYPE_CONN_CREATE 0x07 #define GB_SVC_TYPE_CONN_DESTROY 0x08 +#define GB_SVC_TYPE_ROUTE_CREATE 0x0b /* SVC version request/response have same payload as gb_protocol_version_response */ @@ -616,6 +617,13 @@ struct gb_svc_conn_destroy_request { }; /* connection destroy response has no payload */ +struct gb_svc_route_create_request { + __u8 intf1_id; + __u8 dev1_id; + __u8 intf2_id; + __u8 dev2_id; +}; + /* UART */ /* Version of the Greybus UART protocol we support */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5f2b2b43b939..f3321296db8a 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -118,6 +118,20 @@ static int connection_destroy_operation(struct gb_svc *svc, &request, sizeof(request), NULL, 0); } +static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, + u8 intf2_id, u8 dev2_id) +{ + struct gb_svc_route_create_request request; + + request.intf1_id = intf1_id; + request.dev1_id = dev1_id; + request.intf2_id = intf2_id; + request.dev2_id = dev2_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE, + &request, sizeof(request), NULL, 0); +} + int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) { return intf_device_id_operation(svc, intf_id, device_id); @@ -148,6 +162,13 @@ int gb_svc_connection_destroy(struct gb_svc *svc, } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); +int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, + u8 intf2_id, u8 dev2_id) { + return route_create_operation(svc, intf1_id, dev1_id, + intf2_id, dev2_id); +} +EXPORT_SYMBOL_GPL(gb_svc_route_create); + static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; -- cgit v1.2.3-59-g8ed1b From 5d6896d24a7bf557816677abdac225074576f8a3 Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:32 -0400 Subject: greybus: svc: create bidirectional routes on hotplug request Upon receiving a hotplug request, we need to prepare the routing table to allow packets to flow between the AP interface and the newly detected interface. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index f3321296db8a..3d1808712598 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -309,6 +309,25 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) goto ida_put; } + /* + * Create a two-way route between the AP and the new interface + */ + ret = route_create_operation(svc, hd->endo->ap_intf_id, + GB_DEVICE_ID_AP, intf_id, device_id); + if (ret) { + dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", + __func__, intf_id, device_id, ret); + goto ida_put; + } + + ret = route_create_operation(svc, intf_id, device_id, + hd->endo->ap_intf_id, GB_DEVICE_ID_AP); + if (ret) { + dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", + __func__, intf_id, device_id, ret); + goto ida_put; + } + ret = gb_interface_init(intf, device_id); if (ret) { dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", -- cgit v1.2.3-59-g8ed1b From 9c1552d7c4a97acb759ab41635197d213d677c9e Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:33 -0400 Subject: greybus: svc: connection: ask SVC to create connections Ask the SVC to do all the necessary bits for creating a new connection. This is skipped for the initial SVC connection. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 7 ++++++- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/svc.c | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b32da8af68f4..1a657f706b93 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -251,7 +251,12 @@ gb_connection_create_range(struct greybus_host_device *hd, spin_unlock_irq(&gb_connections_lock); - /* XXX Will have to establish connections to get version */ + if (hd_cport_id != GB_SVC_CPORT_ID) { + gb_svc_connection_create(hd->svc, + hd->endo->ap_intf_id, hd_cport_id, + bundle->intf->interface_id, cport_id); + } + gb_connection_bind_protocol(connection); if (!connection->protocol) dev_warn(&connection->dev, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 90f996276ca4..ef11b960d1b7 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -103,6 +103,7 @@ struct greybus_host_device { struct gb_endo *endo; struct gb_connection *initial_svc_connection; + struct gb_svc *svc; /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 3d1808712598..b718a881a7e3 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -430,6 +430,7 @@ static int gb_svc_connection_init(struct gb_connection *connection) if (!svc) return -ENOMEM; + connection->hd->svc = svc; svc->connection = connection; connection->private = svc; @@ -445,6 +446,7 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; + connection->hd->svc = NULL; connection->private = NULL; kfree(svc); } -- cgit v1.2.3-59-g8ed1b From e50b7be565a888ff0b1ac38247cc3c34e1f9c2f7 Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:34 -0400 Subject: greybus: svc: add flags and traffic class parameter to connection create op The AP needs to be able to specify L4 CPort flags and traffic class parameters on a connection-by-connection basis. Extend the connection create operation to accept these. Since there's no policy to decide these, fix them at TC0 with end-to-end-flow control, controlled segment dropping, and CPort safety valve enabled. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 ++ drivers/staging/greybus/svc.c | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 5b97073f4100..e2d38dfe06b6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -606,6 +606,8 @@ struct gb_svc_conn_create_request { __u16 cport1_id; __u8 intf2_id; __u16 cport2_id; + __u8 tc; + __u8 flags; }; /* connection create response has no payload */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b718a881a7e3..b94a84aca0d8 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -9,6 +9,10 @@ #include "greybus.h" +#define CPORT_FLAGS_E2EFC (1) +#define CPORT_FLAGS_CSD_N (2) +#define CPORT_FLAGS_CSV_N (4) + struct gb_svc { struct gb_connection *connection; u8 version_major; @@ -98,6 +102,12 @@ static int connection_create_operation(struct gb_svc *svc, request.cport1_id = cport1_id; request.intf2_id = intf2_id; request.cport2_id = cport2_id; + /* + * XXX: fix connections paramaters to TC0 and all CPort flags + * for now. + */ + request.tc = 0; + request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC; return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); -- cgit v1.2.3-59-g8ed1b From 90c807e4fb19dba5db3ec7b6a86b0038aa574f84 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 17:15:59 -0700 Subject: greybus: greybus.h: remove greybus_svc_in() The function is gone, remove it from the header file as well. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index ef11b960d1b7..a0114953d3e7 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -156,7 +156,6 @@ void greybus_deregister_driver(struct greybus_driver *driver); int greybus_disabled(void); -int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); void gb_debugfs_init(void); void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); -- cgit v1.2.3-59-g8ed1b From d187576fbe66995a97eebddaa27474b50407d18f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 17:19:21 -0700 Subject: greybus: remove submit_svc from the host driver The callback is never used anymore, so remove it from struct greybus_host_driver as well as from the es1 and es2 drivers. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 +-- drivers/staging/greybus/es1.c | 22 ---------------------- drivers/staging/greybus/es2.c | 22 ---------------------- drivers/staging/greybus/greybus.h | 2 -- 4 files changed, 1 insertion(+), 48 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 18075f7ec038..49a28ba532dc 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -175,8 +175,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * Validate that the driver implements all of the callbacks * so that we don't have to every time we make them. */ - if ((!driver->message_send) || (!driver->message_cancel) || - (!driver->submit_svc)) { + if ((!driver->message_send) || (!driver->message_cancel)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); return ERR_PTR(-EINVAL); } diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 82a7c67c1037..e203180e1e5c 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -14,7 +14,6 @@ #include #include "greybus.h" -#include "svc_msg.h" #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ @@ -95,26 +94,6 @@ static void usb_log_enable(struct es1_ap_dev *es1); static void usb_log_disable(struct es1_ap_dev *es1); #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ -static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - int retval; - - /* SVC messages go down our control pipe */ - retval = usb_control_msg(es1->usb_dev, - usb_sndctrlpipe(es1->usb_dev, - es1->control_endpoint), - REQUEST_SVC, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, 0x00, - (char *)svc_msg, - sizeof(*svc_msg), - ES1_TIMEOUT); - if (retval != sizeof(*svc_msg)) - return retval; - - return 0; -} static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) { @@ -295,7 +274,6 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, - .submit_svc = submit_svc, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index c353ca56ce96..aba1927fc34f 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -14,7 +14,6 @@ #include #include "greybus.h" -#include "svc_msg.h" #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ @@ -134,26 +133,6 @@ static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ -static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - int retval; - - /* SVC messages go down our control pipe */ - retval = usb_control_msg(es1->usb_dev, - usb_sndctrlpipe(es1->usb_dev, - es1->control_endpoint), - REQUEST_SVC, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, 0x00, - (char *)svc_msg, - sizeof(*svc_msg), - ES1_TIMEOUT); - if (retval != sizeof(*svc_msg)) - return retval; - - return 0; -} static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set) { @@ -391,7 +370,6 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, - .submit_svc = submit_svc, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index a0114953d3e7..30ea4a4c17ea 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -84,8 +84,6 @@ struct greybus_host_driver { int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); - int (*submit_svc)(struct svc_msg *svc_msg, - struct greybus_host_device *hd); }; struct greybus_host_device { -- cgit v1.2.3-59-g8ed1b From 4bc88276ebc4ec329fea0e7f811a5cc55ca869ae Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 17:21:25 -0700 Subject: greybus: interface: make gb_interface_destroy() static The function is only called locally, so mark it static to make sparse happy. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index f1e2956b25a7..4a26bf6f714c 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -183,7 +183,7 @@ put_module: /* * Tear down a previously set up module. */ -void gb_interface_destroy(struct gb_interface *intf) +static void interface_destroy(struct gb_interface *intf) { struct gb_module *module; struct gb_bundle *bundle; @@ -279,7 +279,7 @@ void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id) struct gb_interface *intf = gb_interface_find(hd, interface_id); if (intf) - gb_interface_destroy(intf); + interface_destroy(intf); else dev_err(hd->parent, "interface id %d not found\n", interface_id); @@ -290,5 +290,5 @@ void gb_interfaces_remove(struct greybus_host_device *hd) struct gb_interface *intf, *temp; list_for_each_entry_safe(intf, temp, &hd->interfaces, links) - gb_interface_destroy(intf); + interface_destroy(intf); } -- cgit v1.2.3-59-g8ed1b From 5a202efe63dde70885ba91c294690dbbf054a184 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 25 Jul 2015 10:10:05 +0530 Subject: greybus: es1: create svc connection early enough The svc connection needs to be ready before creating the URBs, otherwise the svc version request might come in before the AP was ready to parse them. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 12 ++++++------ drivers/staging/greybus/es2.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index e203180e1e5c..5418f4675ec1 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -563,6 +563,12 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } + /* Initialize AP's greybus interface */ + if (!gb_ap_svc_connection_create(hd)) { + retval = -EINVAL; + goto error; + } + es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; @@ -632,12 +638,6 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* Initialize AP's greybus interface */ - if (!gb_ap_svc_connection_create(hd)) { - retval = -EINVAL; - goto error; - } - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index aba1927fc34f..d2c054a2ec0e 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -663,6 +663,12 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } + /* Initialize AP's greybus interface */ + if (!gb_ap_svc_connection_create(hd)) { + retval = -EINVAL; + goto error; + } + es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; @@ -736,12 +742,6 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* Initialize AP's greybus interface */ - if (!gb_ap_svc_connection_create(hd)) { - retval = -EINVAL; - goto error; - } - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, -- cgit v1.2.3-59-g8ed1b From d8742156c1c0125b709c262c5a564bb828e126dc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 25 Jul 2015 10:10:06 +0530 Subject: greybus: remove svc_msg.h Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/svc_msg.h | 157 -------------------------------------- 2 files changed, 158 deletions(-) delete mode 100644 drivers/staging/greybus/svc_msg.h diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 30ea4a4c17ea..cdade1e9b4c7 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -73,7 +73,6 @@ */ struct greybus_host_device; -struct svc_msg; /* Greybus "Host driver" structure, needed by a host controller driver to be * able to handle both SVC control as well as "real" greybus messages diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h deleted file mode 100644 index 3c628c5d6e38..000000000000 --- a/drivers/staging/greybus/svc_msg.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Greybus AP <-> SVC message structure format. - * - * See "Greybus Application Protocol" document (version 0.1) for - * details on these values and structures. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 and BSD license. - */ - -#ifndef __SVC_MSG_H -#define __SVC_MSG_H - -enum svc_function_id { - SVC_FUNCTION_HANDSHAKE = 0x00, - SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, - SVC_FUNCTION_HOTPLUG = 0x02, - SVC_FUNCTION_POWER = 0x03, - SVC_FUNCTION_EPM = 0x04, - SVC_FUNCTION_SUSPEND = 0x05, -}; - -enum svc_msg_type { - SVC_MSG_DATA = 0x00, - SVC_MSG_ERROR = 0xff, -}; - -struct svc_msg_header { - __u8 function_id; /* enum svc_function_id */ - __u8 message_type; - __le16 payload_length; -} __packed; - -enum svc_function_handshake_type { - SVC_HANDSHAKE_SVC_HELLO = 0x00, - SVC_HANDSHAKE_AP_HELLO = 0x01, - SVC_HANDSHAKE_MODULE_HELLO = 0x02, -}; - -struct svc_function_handshake { - __u8 version_major; - __u8 version_minor; - __u8 handshake_type; /* enum svc_function_handshake_type */ -} __packed; - -struct svc_function_unipro_set_route { - __u8 device_id; -} __packed; - -struct svc_function_unipro_link_up { - __u8 interface_id; /* Interface id within the Endo */ - __u8 device_id; -} __packed; - -struct svc_function_ap_id { - __u8 interface_id; - __u8 device_id; -} __packed; - -enum svc_function_management_event { - SVC_MANAGEMENT_AP_ID = 0x00, - SVC_MANAGEMENT_LINK_UP = 0x01, - SVC_MANAGEMENT_SET_ROUTE = 0x02, -}; - -struct svc_function_unipro_management { - __u8 management_packet_type; /* enum svc_function_management_event */ - union { - struct svc_function_ap_id ap_id; - struct svc_function_unipro_link_up link_up; - struct svc_function_unipro_set_route set_route; - }; -} __packed; - -enum svc_function_hotplug_event { - SVC_HOTPLUG_EVENT = 0x00, - SVC_HOTUNPLUG_EVENT = 0x01, -}; - -struct svc_function_hotplug { - __u8 hotplug_event; /* enum svc_function_hotplug_event */ - __u8 interface_id; /* Interface id within the Endo */ -} __packed; - -enum svc_function_power_type { - SVC_POWER_BATTERY_STATUS = 0x00, - SVC_POWER_BATTERY_STATUS_REQUEST = 0x01, -}; - -enum svc_function_battery_status { - SVC_BATTERY_UNKNOWN = 0x00, - SVC_BATTERY_CHARGING = 0x01, - SVC_BATTERY_DISCHARGING = 0x02, - SVC_BATTERY_NOT_CHARGING = 0x03, - SVC_BATTERY_FULL = 0x04, -}; - -struct svc_function_power_battery_status { - __le16 charge_full; - __le16 charge_now; - __u8 status; /* enum svc_function_battery_status */ -} __packed; - -struct svc_function_power_battery_status_request { -} __packed; - -/* XXX - * Each interface carries power, so it's possible these things - * are associated with each UniPro device and not just the module. - * For now it's safe to assume it's per-module. - */ -struct svc_function_power { - __u8 power_type; /* enum svc_function_power_type */ - __u8 interface_id; - union { - struct svc_function_power_battery_status status; - struct svc_function_power_battery_status_request request; - }; -} __packed; - -enum svc_function_epm_command_type { - SVC_EPM_ENABLE = 0x00, - SVC_EPM_DISABLE = 0x01, -}; - -/* EPM's are associated with the module */ -struct svc_function_epm { - __u8 epm_command_type; /* enum svc_function_epm_command_type */ - __u8 module_id; -} __packed; - -enum svc_function_suspend_command_type { - SVC_SUSPEND_FIXME_1 = 0x00, // FIXME - SVC_SUSPEND_FIXME_2 = 0x01, -}; - -/* We'll want independent control for multi-interface modules */ -struct svc_function_suspend { - __u8 suspend_command_type; /* enum function_suspend_command_type */ - __u8 device_id; -} __packed; - -struct svc_msg { - struct svc_msg_header header; - union { - struct svc_function_handshake handshake; - struct svc_function_unipro_management management; - struct svc_function_hotplug hotplug; - struct svc_function_power power; - struct svc_function_epm epm; - struct svc_function_suspend suspend; - }; -} __packed; - -#endif /* __SVC_MSG_H */ -- cgit v1.2.3-59-g8ed1b From fe166c6a2b9225d71b1b4f1035603e189f6c3640 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Jul 2015 14:23:44 -0700 Subject: greybus: core: clean up ida memory for host controller We forgot to free any ida internal structures that were used by this host controller structure when we free the memory for the controller. So fix that up by doing so in the release function. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 49a28ba532dc..7d5cd99157f5 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -161,6 +161,7 @@ static void free_hd(struct kref *kref) hd = container_of(kref, struct greybus_host_device, kref); + ida_destroy(&hd->cport_id_map); kfree(hd); mutex_unlock(&hd_mutex); } -- cgit v1.2.3-59-g8ed1b From 9612f2bcd466364a244b8c2a012de69abd8fb258 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Jul 2015 07:28:42 +0530 Subject: greybus: initialize svc connection while creating hd Its really part of initializing the host device and is required for every 'hd' that is created. Lets move the call to do basic initialization of svc connection to greybus_create_hd(). Also add a comment to specify why we need to do it that early. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 15 +++++++++++++++ drivers/staging/greybus/es1.c | 6 ------ drivers/staging/greybus/es2.c | 6 ------ drivers/staging/greybus/svc.c | 1 - 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 7d5cd99157f5..9f105fb12ede 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -208,6 +208,21 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; + /* + * Initialize AP's SVC protocol connection: + * + * This is required as part of early initialization of the host device + * as we need this connection in order to start any kind of message + * exchange between the AP and the SVC. SVC will start with a + * 'get-version' request followed by a 'svc-hello' message and at that + * time we will create a fully initialized svc-connection, as we need + * endo-id and AP's interface id for that. + */ + if (!gb_ap_svc_connection_create(hd)) { + kref_put_mutex(&hd->kref, free_hd, &hd_mutex); + return ERR_PTR(-ENOMEM); + } + return hd; } EXPORT_SYMBOL_GPL(greybus_create_hd); diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 5418f4675ec1..c1fab375bb0b 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -563,12 +563,6 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } - /* Initialize AP's greybus interface */ - if (!gb_ap_svc_connection_create(hd)) { - retval = -EINVAL; - goto error; - } - es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index d2c054a2ec0e..558345cd80af 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -663,12 +663,6 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } - /* Initialize AP's greybus interface */ - if (!gb_ap_svc_connection_create(hd)) { - retval = -EINVAL; - goto error; - } - es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b94a84aca0d8..784b7709b432 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -40,7 +40,6 @@ gb_ap_svc_connection_create(struct greybus_host_device *hd) return connection; } -EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create); /* * We know endo-type and AP's interface id now, lets create a proper svc -- cgit v1.2.3-59-g8ed1b From a7e60062b022d1ce1104610d0b0c37322aeb66b6 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 28 Jul 2015 18:34:35 +0100 Subject: greybus: loopback: remove redundant timestamping It is of more interest to graphing system performance to base our timestamps on the time it takes a greybus_operation_sync() to complete. Higher level timestamping code is less accurate and not relevant to throughput and latency characterization. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 41 ++++++++++---------------------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 229494833a16..5f7c1a6256cc 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -43,8 +43,6 @@ struct gb_loopback { struct gb_loopback_stats latency; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; - struct timeval ts; - struct timeval te; u64 elapsed_nsecs; u32 error; }; @@ -180,11 +178,9 @@ static struct attribute *loopback_attrs[] = { }; ATTRIBUTE_GROUPS(loopback); -static int gb_loopback_sink(struct gb_loopback *gb, - struct timeval *tping, u32 len) +static int gb_loopback_sink(struct gb_loopback *gb, u32 len) { struct timeval ts, te; - u64 elapsed_nsecs; struct gb_loopback_transfer_request *request; int retval; @@ -199,18 +195,15 @@ static int gb_loopback_sink(struct gb_loopback *gb, request, len + sizeof(*request), NULL, 0); do_gettimeofday(&te); - elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); - *tping = ns_to_timeval(elapsed_nsecs); + gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); kfree(request); return retval; } -static int gb_loopback_transfer(struct gb_loopback *gb, - struct timeval *tping, u32 len) +static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) { struct timeval ts, te; - u64 elapsed_nsecs; struct gb_loopback_transfer_request *request; struct gb_loopback_transfer_response *response; int retval; @@ -231,8 +224,7 @@ static int gb_loopback_transfer(struct gb_loopback *gb, request, len + sizeof(*request), response, len + sizeof(*response)); do_gettimeofday(&te); - elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); - *tping = ns_to_timeval(elapsed_nsecs); + gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); if (retval) goto gb_error; @@ -247,18 +239,16 @@ gb_error: return retval; } -static int gb_loopback_ping(struct gb_loopback *gb, struct timeval *tping) +static int gb_loopback_ping(struct gb_loopback *gb) { struct timeval ts, te; - u64 elapsed_nsecs; int retval; do_gettimeofday(&ts); retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_PING, NULL, 0, NULL, 0); do_gettimeofday(&te); - elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); - *tping = ns_to_timeval(elapsed_nsecs); + gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); return retval; } @@ -324,7 +314,6 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb->requests_per_second, &reset, sizeof(struct gb_loopback_stats)); - memset(&gb->ts, 0, sizeof(struct timeval)); } static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u64 val) @@ -375,8 +364,7 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) gb_loopback_update_stats(&gb->throughput, throughput); } -static void gb_loopback_calculate_stats(struct gb_loopback *gb, - struct timeval *tlat) +static void gb_loopback_calculate_stats(struct gb_loopback *gb) { u32 lat; u64 tmp; @@ -397,7 +385,6 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb, static int gb_loopback_fn(void *data) { int error = 0; - struct timeval tlat = {0, 0}; struct gb_loopback *gb = (struct gb_loopback *)data; while (1) { @@ -416,21 +403,15 @@ static int gb_loopback_fn(void *data) continue; } } - if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) - do_gettimeofday(&gb->ts); if (gb->type == GB_LOOPBACK_TYPE_PING) - error = gb_loopback_ping(gb, &tlat); + error = gb_loopback_ping(gb); else if (gb->type == GB_LOOPBACK_TYPE_TRANSFER) - error = gb_loopback_transfer(gb, &tlat, gb->size); + error = gb_loopback_transfer(gb, gb->size); else if (gb->type == GB_LOOPBACK_TYPE_SINK) - error = gb_loopback_sink(gb, &tlat, gb->size); + error = gb_loopback_sink(gb, gb->size); if (error) gb->error++; - do_gettimeofday(&gb->te); - gb->elapsed_nsecs = timeval_to_ns(&gb->te) - - timeval_to_ns(&gb->ts); - gb_loopback_calculate_stats(gb, &tlat); - gb->ts = gb->te; + gb_loopback_calculate_stats(gb); if (gb->ms_wait) msleep(gb->ms_wait); } -- cgit v1.2.3-59-g8ed1b From 85d678c0bee883c63b760b35a4104feac9018a0f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 28 Jul 2015 18:34:36 +0100 Subject: greybus: loopback: make loopback code thread safe Current code allows a sysfs callback and a kernel worker thread to write all over and act upon data that could be in the process of being updated by the other. This patch adds a reasonably coarse mutex to enscure sync between the two. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 5f7c1a6256cc..afba4225defd 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -8,6 +8,7 @@ */ #include #include +#include #include #include #include @@ -30,6 +31,7 @@ struct gb_loopback { u8 version_major; u8 version_minor; + struct mutex mutex; struct task_struct *task; wait_queue_head_t wq; @@ -101,10 +103,13 @@ static ssize_t field##_store(struct device *dev, \ struct gb_connection *connection = to_gb_connection(dev); \ struct gb_loopback *gb = \ (struct gb_loopback *)connection->private; \ + mutex_lock(&gb->mutex); \ ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ - return -EINVAL; \ - gb_loopback_check_attr(gb); \ + len = -EINVAL; \ + else \ + gb_loopback_check_attr(gb); \ + mutex_unlock(&gb->mutex); \ return len; \ } \ static DEVICE_ATTR_RW(field) @@ -385,6 +390,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) static int gb_loopback_fn(void *data) { int error = 0; + int ms_wait; struct gb_loopback *gb = (struct gb_loopback *)data; while (1) { @@ -393,6 +399,8 @@ static int gb_loopback_fn(void *data) kthread_should_stop()); if (kthread_should_stop()) break; + + mutex_lock(&gb->mutex); if (gb->iteration_max) { if (gb->iteration_count < gb->iteration_max) { gb->iteration_count++; @@ -400,6 +408,7 @@ static int gb_loopback_fn(void *data) "iteration_count"); } else { gb->type = 0; + mutex_unlock(&gb->mutex); continue; } } @@ -412,8 +421,10 @@ static int gb_loopback_fn(void *data) if (error) gb->error++; gb_loopback_calculate_stats(gb); - if (gb->ms_wait) - msleep(gb->ms_wait); + ms_wait = gb->ms_wait; + mutex_unlock(&gb->mutex); + if (ms_wait) + msleep(ms_wait); } return 0; } @@ -448,6 +459,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb_loopback_reset_stats(gb); init_waitqueue_head(&gb->wq); + mutex_init(&gb->mutex); gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { retval = PTR_ERR(gb->task); -- cgit v1.2.3-59-g8ed1b From cbd204b48dc445a194d3892e2f46644f9074f308 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 28 Jul 2015 18:34:38 +0100 Subject: greybus: loopback: provide interface to read all latency data-points The current loopback code provides the minimum, maximum and average latency values for a given test set. It would be highly useful for user-space to have access to each one of the latency metrics in order to graph outliers. This patch adds a simple character device interface implmenting a read() interface that allows user-space to read out the saved latency metrics which have been stored in a kfifo for this purpose. A module parameter is provided to allow varying the depth of the kfifo in order to allow a user to capture potentially large data sets. This version sets the default depth for the kfifo at 8192 dwords. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 146 ++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index afba4225defd..7b3ce13032c5 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -14,6 +14,10 @@ #include #include #include +#include +#include +#include + #include #include "greybus.h" @@ -31,9 +35,13 @@ struct gb_loopback { u8 version_major; u8 version_minor; + struct kfifo kfifo; struct mutex mutex; struct task_struct *task; wait_queue_head_t wq; + dev_t dev; + struct cdev cdev; + struct device *device; int type; u32 size; @@ -49,6 +57,21 @@ struct gb_loopback { u32 error; }; +#define GB_LOOPBACK_FIFO_DEFAULT 8192 + +static struct class *loopback_class; +static int loopback_major; +static const struct file_operations loopback_fops; +static DEFINE_IDA(minors); +static unsigned kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT; +module_param(kfifo_depth, uint, 0444); + +/* Number of minor devices this driver supports */ +#define NUM_MINORS 256 + +/* Maximum size of any one send data buffer we support */ +#define MAX_PACKET_SIZE (PAGE_SIZE * 2) + #define GB_LOOPBACK_MS_WAIT_MAX 1000 /* Define get_version() routine */ @@ -129,6 +152,7 @@ static void gb_loopback_check_attr(struct gb_loopback *gb) case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: case GB_LOOPBACK_TYPE_SINK: + kfifo_reset_out(&gb->kfifo); wake_up(&gb->wq); break; default: @@ -381,6 +405,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) /* Log latency stastic */ gb_loopback_update_stats(&gb->latency, lat); + kfifo_in(&gb->kfifo, (unsigned char *)&lat, sizeof(lat)); /* Log throughput and requests using latency as benchmark */ gb_loopback_throughput_update(gb, lat); @@ -433,10 +458,12 @@ static int gb_loopback_connection_init(struct gb_connection *connection) { struct gb_loopback *gb; int retval; + int minor; gb = kzalloc(sizeof(*gb), GFP_KERNEL); if (!gb) return -ENOMEM; + gb_loopback_reset_stats(gb); gb->connection = connection; connection->private = gb; @@ -444,6 +471,13 @@ static int gb_loopback_connection_init(struct gb_connection *connection) if (retval) goto out_free; + /* Get a minor number */ + minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); + if (minor < 0) { + retval = minor; + goto out_free; + } + /* Check the version */ retval = get_version(gb); if (retval) @@ -457,17 +491,44 @@ static int gb_loopback_connection_init(struct gb_connection *connection) } gb->size_max -= sizeof(struct gb_loopback_transfer_request); - gb_loopback_reset_stats(gb); + /* Allocate kfifo */ + if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32), + GFP_KERNEL)) { + retval = -ENOMEM; + goto out_get_ver; + } + + /* Create device entry */ + gb->dev = MKDEV(loopback_major, minor); + cdev_init(&gb->cdev, &loopback_fops); + retval = cdev_add(&gb->cdev, gb->dev, 1); + if (retval) + goto out_cdev; + + gb->device = device_create(loopback_class, &connection->dev, gb->dev, + gb, "gb!loopback%d", minor); + if (IS_ERR(gb->device)) { + retval = PTR_ERR(gb->device); + goto out_device; + } + + /* Fork worker thread */ init_waitqueue_head(&gb->wq); mutex_init(&gb->mutex); gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { retval = PTR_ERR(gb->task); - goto out_get_ver; + goto out_kfifo; } return 0; +out_device: + cdev_del(&gb->cdev); +out_cdev: + ida_simple_remove(&minors, minor); +out_kfifo: + kfifo_free(&gb->kfifo); out_get_ver: sysfs_remove_groups(&connection->dev.kobj, loopback_groups); out_free: @@ -481,6 +542,11 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); + + cdev_del(&gb->cdev); + ida_simple_remove(&minors, MINOR(gb->dev)); + device_del(gb->device); + kfifo_free(&gb->kfifo); sysfs_remove_groups(&connection->dev.kobj, loopback_groups); kfree(gb); } @@ -495,6 +561,80 @@ static struct gb_protocol loopback_protocol = { .request_recv = gb_loopback_request_recv, }; -gb_protocol_driver(&loopback_protocol); +static int loopback_open(struct inode *inode, struct file *file) +{ + struct cdev *cdev = inode->i_cdev; + struct gb_loopback *gb = container_of(cdev, struct gb_loopback, cdev); + + file->private_data = gb; + return 0; +} + +static ssize_t loopback_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct gb_loopback *gb = file->private_data; + size_t fifo_len; + int retval; + + if (!count || count%sizeof(u32)) + return -EINVAL; + + mutex_lock(&gb->mutex); + fifo_len = kfifo_len(&gb->kfifo); + if (kfifo_to_user(&gb->kfifo, buf, min(fifo_len, count), &retval) != 0) + retval = -EIO; + mutex_unlock(&gb->mutex); + + return retval; +} + +static const struct file_operations loopback_fops = { + .owner = THIS_MODULE, + .read = loopback_read, + .open = loopback_open, + .llseek = noop_llseek, +}; + +static int loopback_init(void) +{ + dev_t dev; + int retval; + + loopback_class = class_create(THIS_MODULE, "gb_loopback"); + if (IS_ERR(loopback_class)) { + retval = PTR_ERR(loopback_class); + goto error_class; + } + + retval = alloc_chrdev_region(&dev, 0, NUM_MINORS, "gb_loopback"); + if (retval < 0) + goto error_chrdev; + + loopback_major = MAJOR(dev); + + retval = gb_protocol_register(&loopback_protocol); + if (retval) + goto error_gb; + + return 0; + +error_gb: + unregister_chrdev_region(dev, NUM_MINORS); +error_chrdev: + class_destroy(loopback_class); +error_class: + return retval; +} +module_init(loopback_init); + +static void __exit loopback_exit(void) +{ + gb_protocol_deregister(&loopback_protocol); + unregister_chrdev_region(MKDEV(loopback_major, 0), NUM_MINORS); + class_destroy(loopback_class); + ida_destroy(&minors); +} +module_exit(loopback_exit); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From cb60f4960ea03d373899c2c21b0611404025c31f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 28 Jul 2015 18:34:39 +0100 Subject: greybus: loopback: warn user if kfifo cannot log all data The depth of the kfifo used to log the latency data for user-space can be moved upwards or downward by way of a module parameter. The user may still specify a test set that's larger than the number of kfifo elements we have available. If the user specifies more iterations than can be logged give a warning as feedback and continue with the test. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 7b3ce13032c5..08a77fee385a 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -131,14 +131,15 @@ static ssize_t field##_store(struct device *dev, \ if (ret != 1) \ len = -EINVAL; \ else \ - gb_loopback_check_attr(gb); \ + gb_loopback_check_attr(connection, gb); \ mutex_unlock(&gb->mutex); \ return len; \ } \ static DEVICE_ATTR_RW(field) static void gb_loopback_reset_stats(struct gb_loopback *gb); -static void gb_loopback_check_attr(struct gb_loopback *gb) +static void gb_loopback_check_attr(struct gb_connection *connection, + struct gb_loopback *gb) { if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; @@ -148,6 +149,12 @@ static void gb_loopback_check_attr(struct gb_loopback *gb) gb->iteration_count = 0; gb_loopback_reset_stats(gb); + if (kfifo_depth < gb->iteration_max) { + dev_warn(&connection->dev, + "iteration_max %u kfifo_depth %u cannot log all data\n", + gb->iteration_max, kfifo_depth); + } + switch (gb->type) { case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: -- cgit v1.2.3-59-g8ed1b From 3eac885de205ef57c3d434a8a4e2eadfd4cedde7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 29 Jul 2015 10:05:09 -0700 Subject: greybus: svc: revert svc changes to keep things working for a while. The firmware for the svc changes isn't quite ready, so revert the whole set of patches in one hunk to get things back to a working state for the other firmware developers. The svc patches will be added back in a separate branch. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/ap.c | 351 ++++++++++++++++++++++++++++ drivers/staging/greybus/connection.c | 7 +- drivers/staging/greybus/core.c | 27 +-- drivers/staging/greybus/es1.c | 106 ++++++++- drivers/staging/greybus/es2.c | 106 ++++++++- drivers/staging/greybus/greybus.h | 7 +- drivers/staging/greybus/greybus_protocols.h | 14 +- drivers/staging/greybus/interface.c | 6 +- drivers/staging/greybus/svc.c | 53 +---- drivers/staging/greybus/svc_msg.h | 157 +++++++++++++ 11 files changed, 741 insertions(+), 94 deletions(-) create mode 100644 drivers/staging/greybus/ap.c create mode 100644 drivers/staging/greybus/svc_msg.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index d14bf4d21983..1467c5b3fcd8 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,5 +1,6 @@ greybus-y := core.o \ debugfs.o \ + ap.o \ manifest.o \ endo.o \ module.o \ diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c new file mode 100644 index 000000000000..fc238170ad12 --- /dev/null +++ b/drivers/staging/greybus/ap.c @@ -0,0 +1,351 @@ +/* + * Greybus "AP" message loop handling + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "svc_msg.h" +#include "greybus_manifest.h" +#include "greybus.h" + +struct ap_msg { + u8 *data; + size_t size; + struct greybus_host_device *hd; + struct work_struct event; +}; + +static struct workqueue_struct *ap_workqueue; + +static struct svc_msg *svc_msg_alloc(enum svc_function_id id) +{ + struct svc_msg *svc_msg; + + svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL); + if (!svc_msg) + return NULL; + + // FIXME - verify we are only sending function IDs we should be + svc_msg->header.function_id = id; + return svc_msg; +} + +static void svc_msg_free(struct svc_msg *svc_msg) +{ + kfree(svc_msg); +} + +static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + int retval; + + // FIXME - Do we need to do more than just pass it to the hd and then + // free it? + retval = hd->driver->submit_svc(svc_msg, hd); + + svc_msg_free(svc_msg); + return retval; +} + +static void svc_handshake(struct svc_function_handshake *handshake, + int payload_length, struct greybus_host_device *hd) +{ + struct svc_msg *svc_msg; + + if (payload_length != sizeof(*handshake)) { + dev_err(hd->parent, + "Illegal size of svc handshake message %d\n", + payload_length); + return; + } + + /* A new SVC communication channel, let's verify a supported version */ + if ((handshake->version_major != GREYBUS_VERSION_MAJOR) || + (handshake->version_minor != GREYBUS_VERSION_MINOR)) { + dev_warn(hd->parent, + "received invalid greybus version %u.%u\n", + handshake->version_major, handshake->version_minor); + return; + } + + /* Validate that the handshake came from the SVC */ + if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) { + /* we don't know what to do with this, log it and return */ + dev_dbg(hd->parent, "received invalid handshake type %d\n", + handshake->handshake_type); + return; + } + + /* Send back a AP_HELLO message */ + svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE); + if (!svc_msg) + return; + + svc_msg->header.message_type = SVC_MSG_DATA; + svc_msg->header.payload_length = + cpu_to_le16(sizeof(*handshake)); + svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR; + svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR; + svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; + + (void)svc_msg_send(svc_msg, hd); +} + +static void svc_management(struct svc_function_unipro_management *management, + int payload_length, struct greybus_host_device *hd) +{ + struct gb_interface *intf; + int ret; + + if (payload_length != sizeof(*management)) { + dev_err(hd->parent, + "Illegal size of svc management message %d\n", + payload_length); + return; + } + + switch (management->management_packet_type) { + case SVC_MANAGEMENT_AP_ID: + hd->device_id = management->ap_id.device_id; + break; + case SVC_MANAGEMENT_LINK_UP: + intf = gb_interface_find(hd, management->link_up.interface_id); + if (!intf) { + dev_err(hd->parent, "Interface ID %d not found\n", + management->link_up.interface_id); + return; + } + ret = gb_interface_init(intf, management->link_up.device_id); + if (ret) { + dev_err(hd->parent, + "error %d initializing interface %hhu\n", + ret, management->link_up.interface_id); + return; + } + break; + default: + dev_err(hd->parent, "Unhandled UniPro management message\n"); + } +} + +static void svc_hotplug(struct svc_function_hotplug *hotplug, + int payload_length, struct greybus_host_device *hd) +{ + u8 interface_id = hotplug->interface_id; + + switch (hotplug->hotplug_event) { + case SVC_HOTPLUG_EVENT: + /* Add a new interface to the system */ + if (payload_length != sizeof(*hotplug)) { + dev_err(hd->parent, + "Illegal size of svc hotplug message %d\n", + payload_length); + return; + } + dev_dbg(hd->parent, "interface id %d added\n", interface_id); + gb_interface_create(hd, interface_id); + break; + + case SVC_HOTUNPLUG_EVENT: + /* Remove a interface from the system */ + if (payload_length != sizeof(*hotplug)) { + dev_err(hd->parent, + "Illegal size of svc hotunplug message %d\n", + payload_length); + return; + } + dev_dbg(hd->parent, "interface id %d removed\n", interface_id); + gb_interface_remove(hd, interface_id); + break; + + default: + dev_err(hd->parent, + "Received invalid hotplug message type %d\n", + hotplug->hotplug_event); + break; + } +} + +static void svc_power(struct svc_function_power *power, + int payload_length, struct greybus_host_device *hd) +{ + u8 interface_id = power->interface_id; + + /* + * The AP is only allowed to get a Battery Status message, not a Battery + * Status Request + */ + if (power->power_type != SVC_POWER_BATTERY_STATUS) { + dev_err(hd->parent, "Received invalid power type %d\n", + power->power_type); + return; + } + + /* + * As struct struct svc_function_power_battery_status_request is 0 bytes + * big, we can just check the union of the whole structure to validate + * the size of this message. + */ + if (payload_length != sizeof(*power)) { + dev_err(hd->parent, + "Illegal size of svc power message %d\n", + payload_length); + return; + } + + dev_dbg(hd->parent, "power status for interface id %d is %d\n", + interface_id, power->status.status); + + // FIXME - do something with the power information, like update our + // battery information... +} + +static void svc_epm(struct svc_function_epm *epm, + int payload_length, struct greybus_host_device *hd) +{ + /* What? An AP should not get this message */ + dev_err(hd->parent, "Got an EPM message???\n"); +} + +static void svc_suspend(struct svc_function_suspend *suspend, + int payload_length, struct greybus_host_device *hd) +{ + /* What? An AP should not get this message */ + dev_err(hd->parent, "Got an suspend message???\n"); +} + +static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) +{ + struct svc_msg *svc_msg; + struct svc_msg_header *header; + struct greybus_host_device *hd = ap_msg->hd; + + svc_msg = (struct svc_msg *)ap_msg->data; + header = &svc_msg->header; + + /* Validate the message type */ + if (header->message_type != SVC_MSG_DATA) { + dev_err(hd->parent, "message type %d received?\n", + header->message_type); + return NULL; + } + + /* + * The validation of the size of the message buffer happens in each + * svc_* function, due to the different types of messages, keeping the + * logic for each message only in one place. + */ + + return svc_msg; +} + +static void ap_process_event(struct work_struct *work) +{ + struct svc_msg *svc_msg; + struct greybus_host_device *hd; + struct ap_msg *ap_msg; + int payload_length; + + ap_msg = container_of(work, struct ap_msg, event); + hd = ap_msg->hd; + + /* Turn the "raw" data into a real message */ + svc_msg = convert_ap_message(ap_msg); + if (!svc_msg) + return; + + payload_length = le16_to_cpu(svc_msg->header.payload_length); + + /* Look at the message to figure out what to do with it */ + switch (svc_msg->header.function_id) { + case SVC_FUNCTION_HANDSHAKE: + svc_handshake(&svc_msg->handshake, payload_length, hd); + break; + case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT: + svc_management(&svc_msg->management, payload_length, hd); + break; + case SVC_FUNCTION_HOTPLUG: + svc_hotplug(&svc_msg->hotplug, payload_length, hd); + break; + case SVC_FUNCTION_POWER: + svc_power(&svc_msg->power, payload_length, hd); + break; + case SVC_FUNCTION_EPM: + svc_epm(&svc_msg->epm, payload_length, hd); + break; + case SVC_FUNCTION_SUSPEND: + svc_suspend(&svc_msg->suspend, payload_length, hd); + break; + default: + dev_err(hd->parent, "received invalid SVC function ID %d\n", + svc_msg->header.function_id); + } + + /* clean the message up */ + kfree(ap_msg->data); + kfree(ap_msg); +} + +int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size) +{ + struct ap_msg *ap_msg; + + /* + * Totally naive copy the message into a new structure that we slowly + * create and add it to the list. Let's get this working, the odds of + * this being any "slow path" for AP messages is really low at this + * point in time, but you never know, so this comment is here to point + * out that maybe we should use a slab allocator, or even just not copy + * the data, but use it directly and force the urbs to be "new" each + * time. + */ + + /* Note - this can, and will, be called in interrupt context. */ + ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC); + if (!ap_msg) + return -ENOMEM; + ap_msg->data = kmalloc(size, GFP_ATOMIC); + if (!ap_msg->data) { + kfree(ap_msg); + return -ENOMEM; + } + memcpy(ap_msg->data, data, size); + ap_msg->size = size; + ap_msg->hd = hd; + + INIT_WORK(&ap_msg->event, ap_process_event); + queue_work(ap_workqueue, &ap_msg->event); + + return 0; +} +EXPORT_SYMBOL_GPL(greybus_svc_in); + +int __init gb_ap_init(void) +{ + ap_workqueue = alloc_ordered_workqueue("greybus_ap", 0); + if (!ap_workqueue) + return -ENOMEM; + + return 0; +} + +void gb_ap_exit(void) +{ + destroy_workqueue(ap_workqueue); + ap_workqueue = NULL; +} + + diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1a657f706b93..b32da8af68f4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -251,12 +251,7 @@ gb_connection_create_range(struct greybus_host_device *hd, spin_unlock_irq(&gb_connections_lock); - if (hd_cport_id != GB_SVC_CPORT_ID) { - gb_svc_connection_create(hd->svc, - hd->endo->ap_intf_id, hd_cport_id, - bundle->intf->interface_id, cport_id); - } - + /* XXX Will have to establish connections to get version */ gb_connection_bind_protocol(connection); if (!connection->protocol) dev_warn(&connection->dev, diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9f105fb12ede..225fa4fb5268 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -176,7 +176,8 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * Validate that the driver implements all of the callbacks * so that we don't have to every time we make them. */ - if ((!driver->message_send) || (!driver->message_cancel)) { + if ((!driver->message_send) || (!driver->message_cancel) || + (!driver->submit_svc)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); return ERR_PTR(-EINVAL); } @@ -208,21 +209,6 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; - /* - * Initialize AP's SVC protocol connection: - * - * This is required as part of early initialization of the host device - * as we need this connection in order to start any kind of message - * exchange between the AP and the SVC. SVC will start with a - * 'get-version' request followed by a 'svc-hello' message and at that - * time we will create a fully initialized svc-connection, as we need - * endo-id and AP's interface id for that. - */ - if (!gb_ap_svc_connection_create(hd)) { - kref_put_mutex(&hd->kref, free_hd, &hd_mutex); - return ERR_PTR(-ENOMEM); - } - return hd; } EXPORT_SYMBOL_GPL(greybus_create_hd); @@ -284,6 +270,12 @@ static int __init gb_init(void) goto error_bus; } + retval = gb_ap_init(); + if (retval) { + pr_err("gb_ap_init failed (%d)\n", retval); + goto error_ap; + } + retval = gb_operation_init(); if (retval) { pr_err("gb_operation_init failed (%d)\n", retval); @@ -317,6 +309,8 @@ error_control: error_endo: gb_operation_exit(); error_operation: + gb_ap_exit(); +error_ap: bus_unregister(&greybus_bus_type); error_bus: gb_debugfs_cleanup(); @@ -331,6 +325,7 @@ static void __exit gb_exit(void) gb_control_protocol_exit(); gb_endo_exit(); gb_operation_exit(); + gb_ap_exit(); bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); } diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index c1fab375bb0b..0cb7a3c7ef72 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -14,9 +14,11 @@ #include #include "greybus.h" +#include "svc_msg.h" #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ +#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { @@ -56,8 +58,11 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure * @control_endpoint: endpoint to send data to SVC + * @svc_endpoint: endpoint for SVC data in * @cport_in_endpoint: bulk in endpoint for CPort data * @cport-out_endpoint: bulk out endpoint for CPort data + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint + * @svc_urb: urb for SVC messages coming in on @svc_endpoint * @cport_in_urb: array of urbs for the CPort in messages * @cport_in_buffer: array of buffers for the @cport_in_urb urbs * @cport_out_urb: array of urbs for the CPort out messages @@ -73,9 +78,13 @@ struct es1_ap_dev { struct greybus_host_device *hd; __u8 control_endpoint; + __u8 svc_endpoint; __u8 cport_in_endpoint; __u8 cport_out_endpoint; + u8 *svc_buffer; + struct urb *svc_urb; + struct urb *cport_in_urb[NUM_CPORT_IN_URB]; u8 *cport_in_buffer[NUM_CPORT_IN_URB]; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; @@ -94,6 +103,26 @@ static void usb_log_enable(struct es1_ap_dev *es1); static void usb_log_disable(struct es1_ap_dev *es1); #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + int retval; + + /* SVC messages go down our control pipe */ + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + REQUEST_SVC, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + (char *)svc_msg, + sizeof(*svc_msg), + ES1_TIMEOUT); + if (retval != sizeof(*svc_msg)) + return retval; + + return 0; +} static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) { @@ -274,6 +303,7 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, + .submit_svc = submit_svc, }; /* Common function to report consistent warnings based on URB status */ @@ -337,6 +367,12 @@ static void ap_disconnect(struct usb_interface *interface) es1->cport_in_buffer[i] = NULL; } + usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); + es1->svc_urb = NULL; + kfree(es1->svc_buffer); + es1->svc_buffer = NULL; + usb_set_intfdata(interface, NULL); udev = es1->usb_dev; greybus_remove_hd(es1->hd); @@ -344,6 +380,33 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(udev); } +/* Callback for when we get a SVC message */ +static void svc_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb svc in error %d (dropped)\n", status); + return; + } + + /* We have a message, create a new message structure, add it to the + * list, and wake up our thread that will process the messages. + */ + greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); + +exit: + /* resubmit the urb to get more messages */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "Can not submit urb for AP data: %d\n", retval); +} + static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; @@ -547,10 +610,14 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; + bool int_in_found = false; bool bulk_in_found = false; bool bulk_out_found = false; int retval = -ENOMEM; int i; + u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC + u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC + u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); @@ -579,7 +646,11 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { + if (usb_endpoint_is_int_in(endpoint)) { + es1->svc_endpoint = endpoint->bEndpointAddress; + svc_interval = endpoint->bInterval; + int_in_found = true; + } else if (usb_endpoint_is_bulk_in(endpoint)) { es1->cport_in_endpoint = endpoint->bEndpointAddress; bulk_in_found = true; } else if (usb_endpoint_is_bulk_out(endpoint)) { @@ -591,12 +662,27 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if ((bulk_in_found == false) || + if ((int_in_found == false) || + (bulk_in_found == false) || (bulk_out_found == false)) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } + /* Create our buffer and URB to get SVC messages, and start it up */ + es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); + if (!es1->svc_buffer) + goto error; + + es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!es1->svc_urb) + goto error; + + usb_fill_int_urb(es1->svc_urb, udev, + usb_rcvintpipe(udev, es1->svc_endpoint), + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, + hd, svc_interval); + /* Allocate buffers for our cport in messages and start them up */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { struct urb *urb; @@ -632,6 +718,22 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } + /* + * XXX Soon this will be initiated later, with a combination + * XXX of a Control protocol probe operation and a + * XXX subsequent Control protocol connected operation for + * XXX the SVC connection. At that point we know we're + * XXX properly connected to an Endo. + */ + retval = greybus_endo_setup(hd, endo_id, ap_intf_id); + if (retval) + goto error; + + /* Start up our svc urb, which allows events to start flowing */ + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 558345cd80af..323721a2e2aa 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -14,9 +14,11 @@ #include #include "greybus.h" +#include "svc_msg.h" #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ +#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { @@ -83,7 +85,10 @@ struct es1_cport_out { * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure * @control_endpoint: endpoint to send data to SVC + * @svc_endpoint: endpoint for SVC data in + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint + * @svc_urb: urb for SVC messages coming in on @svc_endpoint * @cport_in: endpoint, urbs and buffer for cport in messages * @cport_out: endpoint for for cport out messages * @cport_out_urb: array of urbs for the CPort out messages @@ -99,6 +104,10 @@ struct es1_ap_dev { struct greybus_host_device *hd; __u8 control_endpoint; + __u8 svc_endpoint; + + u8 *svc_buffer; + struct urb *svc_urb; struct es1_cport_in cport_in[NUM_BULKS]; struct es1_cport_out cport_out[NUM_BULKS]; @@ -133,6 +142,26 @@ static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + int retval; + + /* SVC messages go down our control pipe */ + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + REQUEST_SVC, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + (char *)svc_msg, + sizeof(*svc_msg), + ES1_TIMEOUT); + if (retval != sizeof(*svc_msg)) + return retval; + + return 0; +} static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set) { @@ -370,6 +399,7 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, + .submit_svc = submit_svc, }; /* Common function to report consistent warnings based on URB status */ @@ -437,6 +467,12 @@ static void ap_disconnect(struct usb_interface *interface) } } + usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); + es1->svc_urb = NULL; + kfree(es1->svc_buffer); + es1->svc_buffer = NULL; + usb_set_intfdata(interface, NULL); udev = es1->usb_dev; greybus_remove_hd(es1->hd); @@ -444,6 +480,33 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(udev); } +/* Callback for when we get a SVC message */ +static void svc_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb svc in error %d (dropped)\n", status); + return; + } + + /* We have a message, create a new message structure, add it to the + * list, and wake up our thread that will process the messages. + */ + greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); + +exit: + /* resubmit the urb to get more messages */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "Can not submit urb for AP data: %d\n", retval); +} + static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; @@ -647,10 +710,14 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; + bool int_in_found = false; int bulk_in = 0; int bulk_out = 0; int retval = -ENOMEM; int i; + u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC + u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC + u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); @@ -679,7 +746,11 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { + if (usb_endpoint_is_int_in(endpoint)) { + es1->svc_endpoint = endpoint->bEndpointAddress; + svc_interval = endpoint->bInterval; + int_in_found = true; + } else if (usb_endpoint_is_bulk_in(endpoint)) { es1->cport_in[bulk_in++].endpoint = endpoint->bEndpointAddress; } else if (usb_endpoint_is_bulk_out(endpoint)) { @@ -691,12 +762,27 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if ((bulk_in == 0) || + if ((int_in_found == false) || + (bulk_in == 0) || (bulk_out == 0)) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } + /* Create our buffer and URB to get SVC messages, and start it up */ + es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); + if (!es1->svc_buffer) + goto error; + + es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!es1->svc_urb) + goto error; + + usb_fill_int_urb(es1->svc_urb, udev, + usb_rcvintpipe(udev, es1->svc_endpoint), + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, + hd, svc_interval); + /* Allocate buffers for our cport in messages and start them up */ for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; @@ -736,6 +822,22 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } + /* + * XXX Soon this will be initiated later, with a combination + * XXX of a Control protocol probe operation and a + * XXX subsequent Control protocol connected operation for + * XXX the SVC connection. At that point we know we're + * XXX properly connected to an Endo. + */ + retval = greybus_endo_setup(hd, endo_id, ap_intf_id); + if (retval) + goto error; + + /* Start up our svc urb, which allows events to start flowing */ + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index cdade1e9b4c7..2214f447df2b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -73,6 +73,7 @@ */ struct greybus_host_device; +struct svc_msg; /* Greybus "Host driver" structure, needed by a host controller driver to be * able to handle both SVC control as well as "real" greybus messages @@ -83,6 +84,8 @@ struct greybus_host_driver { int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); + int (*submit_svc)(struct svc_msg *svc_msg, + struct greybus_host_device *hd); }; struct greybus_host_device { @@ -100,7 +103,6 @@ struct greybus_host_device { struct gb_endo *endo; struct gb_connection *initial_svc_connection; - struct gb_svc *svc; /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); @@ -153,6 +155,9 @@ void greybus_deregister_driver(struct greybus_driver *driver); int greybus_disabled(void); +int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); +int gb_ap_init(void); +void gb_ap_exit(void); void gb_debugfs_init(void); void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e2d38dfe06b6..61fe9dce6ec0 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -64,9 +64,9 @@ * CONTROL and SVC protocols for communication between AP and SVC. */ #define GB_SVC_BUNDLE_ID 0 -#define GB_SVC_CPORT_ID 0 +#define GB_SVC_CPORT_ID 2 #define GB_CONTROL_BUNDLE_ID 0 -#define GB_CONTROL_CPORT_ID 0 +#define GB_CONTROL_CPORT_ID 2 /* Control Protocol */ @@ -563,7 +563,6 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_RESET 0x06 #define GB_SVC_TYPE_CONN_CREATE 0x07 #define GB_SVC_TYPE_CONN_DESTROY 0x08 -#define GB_SVC_TYPE_ROUTE_CREATE 0x0b /* SVC version request/response have same payload as gb_protocol_version_response */ @@ -606,8 +605,6 @@ struct gb_svc_conn_create_request { __u16 cport1_id; __u8 intf2_id; __u16 cport2_id; - __u8 tc; - __u8 flags; }; /* connection create response has no payload */ @@ -619,13 +616,6 @@ struct gb_svc_conn_destroy_request { }; /* connection destroy response has no payload */ -struct gb_svc_route_create_request { - __u8 intf1_id; - __u8 dev1_id; - __u8 intf2_id; - __u8 dev2_id; -}; - /* UART */ /* Version of the Greybus UART protocol we support */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 4a26bf6f714c..f1e2956b25a7 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -183,7 +183,7 @@ put_module: /* * Tear down a previously set up module. */ -static void interface_destroy(struct gb_interface *intf) +void gb_interface_destroy(struct gb_interface *intf) { struct gb_module *module; struct gb_bundle *bundle; @@ -279,7 +279,7 @@ void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id) struct gb_interface *intf = gb_interface_find(hd, interface_id); if (intf) - interface_destroy(intf); + gb_interface_destroy(intf); else dev_err(hd->parent, "interface id %d not found\n", interface_id); @@ -290,5 +290,5 @@ void gb_interfaces_remove(struct greybus_host_device *hd) struct gb_interface *intf, *temp; list_for_each_entry_safe(intf, temp, &hd->interfaces, links) - interface_destroy(intf); + gb_interface_destroy(intf); } diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 784b7709b432..5f2b2b43b939 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -9,10 +9,6 @@ #include "greybus.h" -#define CPORT_FLAGS_E2EFC (1) -#define CPORT_FLAGS_CSD_N (2) -#define CPORT_FLAGS_CSV_N (4) - struct gb_svc { struct gb_connection *connection; u8 version_major; @@ -40,6 +36,7 @@ gb_ap_svc_connection_create(struct greybus_host_device *hd) return connection; } +EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create); /* * We know endo-type and AP's interface id now, lets create a proper svc @@ -101,12 +98,6 @@ static int connection_create_operation(struct gb_svc *svc, request.cport1_id = cport1_id; request.intf2_id = intf2_id; request.cport2_id = cport2_id; - /* - * XXX: fix connections paramaters to TC0 and all CPort flags - * for now. - */ - request.tc = 0; - request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC; return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); @@ -127,20 +118,6 @@ static int connection_destroy_operation(struct gb_svc *svc, &request, sizeof(request), NULL, 0); } -static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, - u8 intf2_id, u8 dev2_id) -{ - struct gb_svc_route_create_request request; - - request.intf1_id = intf1_id; - request.dev1_id = dev1_id; - request.intf2_id = intf2_id; - request.dev2_id = dev2_id; - - return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE, - &request, sizeof(request), NULL, 0); -} - int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) { return intf_device_id_operation(svc, intf_id, device_id); @@ -171,13 +148,6 @@ int gb_svc_connection_destroy(struct gb_svc *svc, } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); -int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, - u8 intf2_id, u8 dev2_id) { - return route_create_operation(svc, intf1_id, dev1_id, - intf2_id, dev2_id); -} -EXPORT_SYMBOL_GPL(gb_svc_route_create); - static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -318,25 +288,6 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) goto ida_put; } - /* - * Create a two-way route between the AP and the new interface - */ - ret = route_create_operation(svc, hd->endo->ap_intf_id, - GB_DEVICE_ID_AP, intf_id, device_id); - if (ret) { - dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", - __func__, intf_id, device_id, ret); - goto ida_put; - } - - ret = route_create_operation(svc, intf_id, device_id, - hd->endo->ap_intf_id, GB_DEVICE_ID_AP); - if (ret) { - dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", - __func__, intf_id, device_id, ret); - goto ida_put; - } - ret = gb_interface_init(intf, device_id); if (ret) { dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", @@ -439,7 +390,6 @@ static int gb_svc_connection_init(struct gb_connection *connection) if (!svc) return -ENOMEM; - connection->hd->svc = svc; svc->connection = connection; connection->private = svc; @@ -455,7 +405,6 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; - connection->hd->svc = NULL; connection->private = NULL; kfree(svc); } diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h new file mode 100644 index 000000000000..3c628c5d6e38 --- /dev/null +++ b/drivers/staging/greybus/svc_msg.h @@ -0,0 +1,157 @@ +/* + * Greybus AP <-> SVC message structure format. + * + * See "Greybus Application Protocol" document (version 0.1) for + * details on these values and structures. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 and BSD license. + */ + +#ifndef __SVC_MSG_H +#define __SVC_MSG_H + +enum svc_function_id { + SVC_FUNCTION_HANDSHAKE = 0x00, + SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, + SVC_FUNCTION_HOTPLUG = 0x02, + SVC_FUNCTION_POWER = 0x03, + SVC_FUNCTION_EPM = 0x04, + SVC_FUNCTION_SUSPEND = 0x05, +}; + +enum svc_msg_type { + SVC_MSG_DATA = 0x00, + SVC_MSG_ERROR = 0xff, +}; + +struct svc_msg_header { + __u8 function_id; /* enum svc_function_id */ + __u8 message_type; + __le16 payload_length; +} __packed; + +enum svc_function_handshake_type { + SVC_HANDSHAKE_SVC_HELLO = 0x00, + SVC_HANDSHAKE_AP_HELLO = 0x01, + SVC_HANDSHAKE_MODULE_HELLO = 0x02, +}; + +struct svc_function_handshake { + __u8 version_major; + __u8 version_minor; + __u8 handshake_type; /* enum svc_function_handshake_type */ +} __packed; + +struct svc_function_unipro_set_route { + __u8 device_id; +} __packed; + +struct svc_function_unipro_link_up { + __u8 interface_id; /* Interface id within the Endo */ + __u8 device_id; +} __packed; + +struct svc_function_ap_id { + __u8 interface_id; + __u8 device_id; +} __packed; + +enum svc_function_management_event { + SVC_MANAGEMENT_AP_ID = 0x00, + SVC_MANAGEMENT_LINK_UP = 0x01, + SVC_MANAGEMENT_SET_ROUTE = 0x02, +}; + +struct svc_function_unipro_management { + __u8 management_packet_type; /* enum svc_function_management_event */ + union { + struct svc_function_ap_id ap_id; + struct svc_function_unipro_link_up link_up; + struct svc_function_unipro_set_route set_route; + }; +} __packed; + +enum svc_function_hotplug_event { + SVC_HOTPLUG_EVENT = 0x00, + SVC_HOTUNPLUG_EVENT = 0x01, +}; + +struct svc_function_hotplug { + __u8 hotplug_event; /* enum svc_function_hotplug_event */ + __u8 interface_id; /* Interface id within the Endo */ +} __packed; + +enum svc_function_power_type { + SVC_POWER_BATTERY_STATUS = 0x00, + SVC_POWER_BATTERY_STATUS_REQUEST = 0x01, +}; + +enum svc_function_battery_status { + SVC_BATTERY_UNKNOWN = 0x00, + SVC_BATTERY_CHARGING = 0x01, + SVC_BATTERY_DISCHARGING = 0x02, + SVC_BATTERY_NOT_CHARGING = 0x03, + SVC_BATTERY_FULL = 0x04, +}; + +struct svc_function_power_battery_status { + __le16 charge_full; + __le16 charge_now; + __u8 status; /* enum svc_function_battery_status */ +} __packed; + +struct svc_function_power_battery_status_request { +} __packed; + +/* XXX + * Each interface carries power, so it's possible these things + * are associated with each UniPro device and not just the module. + * For now it's safe to assume it's per-module. + */ +struct svc_function_power { + __u8 power_type; /* enum svc_function_power_type */ + __u8 interface_id; + union { + struct svc_function_power_battery_status status; + struct svc_function_power_battery_status_request request; + }; +} __packed; + +enum svc_function_epm_command_type { + SVC_EPM_ENABLE = 0x00, + SVC_EPM_DISABLE = 0x01, +}; + +/* EPM's are associated with the module */ +struct svc_function_epm { + __u8 epm_command_type; /* enum svc_function_epm_command_type */ + __u8 module_id; +} __packed; + +enum svc_function_suspend_command_type { + SVC_SUSPEND_FIXME_1 = 0x00, // FIXME + SVC_SUSPEND_FIXME_2 = 0x01, +}; + +/* We'll want independent control for multi-interface modules */ +struct svc_function_suspend { + __u8 suspend_command_type; /* enum function_suspend_command_type */ + __u8 device_id; +} __packed; + +struct svc_msg { + struct svc_msg_header header; + union { + struct svc_function_handshake handshake; + struct svc_function_unipro_management management; + struct svc_function_hotplug hotplug; + struct svc_function_power power; + struct svc_function_epm epm; + struct svc_function_suspend suspend; + }; +} __packed; + +#endif /* __SVC_MSG_H */ -- cgit v1.2.3-59-g8ed1b From 619dccd27b4a9747a42d8dc3e2ed32e32cf29f8a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:23 +0530 Subject: greybus: es1/2: Lets start using svc protocol All bits and pieces are in place now. Lets start using svc protocol instead of stuff present in ap.c. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 15 ++++----------- drivers/staging/greybus/es2.c | 15 ++++----------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 0cb7a3c7ef72..7d764a5afeb3 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -615,8 +615,6 @@ static int ap_probe(struct usb_interface *interface, bool bulk_out_found = false; int retval = -ENOMEM; int i; - u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC - u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ @@ -718,16 +716,11 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* - * XXX Soon this will be initiated later, with a combination - * XXX of a Control protocol probe operation and a - * XXX subsequent Control protocol connected operation for - * XXX the SVC connection. At that point we know we're - * XXX properly connected to an Endo. - */ - retval = greybus_endo_setup(hd, endo_id, ap_intf_id); - if (retval) + /* Initialize AP's greybus interface */ + if (!gb_ap_svc_connection_create(hd)) { + retval = -EINVAL; goto error; + } /* Start up our svc urb, which allows events to start flowing */ retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 323721a2e2aa..7621e95071ed 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -715,8 +715,6 @@ static int ap_probe(struct usb_interface *interface, int bulk_out = 0; int retval = -ENOMEM; int i; - u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC - u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ @@ -822,16 +820,11 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* - * XXX Soon this will be initiated later, with a combination - * XXX of a Control protocol probe operation and a - * XXX subsequent Control protocol connected operation for - * XXX the SVC connection. At that point we know we're - * XXX properly connected to an Endo. - */ - retval = greybus_endo_setup(hd, endo_id, ap_intf_id); - if (retval) + /* Initialize AP's greybus interface */ + if (!gb_ap_svc_connection_create(hd)) { + retval = -EINVAL; goto error; + } /* Start up our svc urb, which allows events to start flowing */ retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); -- cgit v1.2.3-59-g8ed1b From ef5d949a8b4eb311d60f38afb83c2c135d785b8a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:24 +0530 Subject: greybus: Switch to Cport 0 for svc and control protocol Initially we fixed it to Cport 2, but its changed to Cport 0 now. Lets switch that in code as well. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 61fe9dce6ec0..9708e934242f 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -64,9 +64,9 @@ * CONTROL and SVC protocols for communication between AP and SVC. */ #define GB_SVC_BUNDLE_ID 0 -#define GB_SVC_CPORT_ID 2 +#define GB_SVC_CPORT_ID 0 #define GB_CONTROL_BUNDLE_ID 0 -#define GB_CONTROL_CPORT_ID 2 +#define GB_CONTROL_CPORT_ID 0 /* Control Protocol */ -- cgit v1.2.3-59-g8ed1b From 1a95812d4656817ded72bba50c74020b5b21589f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 16:47:07 -0700 Subject: greybus: es1: remove svc endpoint message handling We have switched over to use the "new" svc messages, no more need to have a special USB endpoint to handle them, they come through the normal CPort messages. Based on a patch from Viresh. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 71 ++----------------------------------------- 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 7d764a5afeb3..82a7c67c1037 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -18,7 +18,6 @@ #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { @@ -58,11 +57,8 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure * @control_endpoint: endpoint to send data to SVC - * @svc_endpoint: endpoint for SVC data in * @cport_in_endpoint: bulk in endpoint for CPort data * @cport-out_endpoint: bulk out endpoint for CPort data - * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint - * @svc_urb: urb for SVC messages coming in on @svc_endpoint * @cport_in_urb: array of urbs for the CPort in messages * @cport_in_buffer: array of buffers for the @cport_in_urb urbs * @cport_out_urb: array of urbs for the CPort out messages @@ -78,13 +74,9 @@ struct es1_ap_dev { struct greybus_host_device *hd; __u8 control_endpoint; - __u8 svc_endpoint; __u8 cport_in_endpoint; __u8 cport_out_endpoint; - u8 *svc_buffer; - struct urb *svc_urb; - struct urb *cport_in_urb[NUM_CPORT_IN_URB]; u8 *cport_in_buffer[NUM_CPORT_IN_URB]; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; @@ -367,12 +359,6 @@ static void ap_disconnect(struct usb_interface *interface) es1->cport_in_buffer[i] = NULL; } - usb_kill_urb(es1->svc_urb); - usb_free_urb(es1->svc_urb); - es1->svc_urb = NULL; - kfree(es1->svc_buffer); - es1->svc_buffer = NULL; - usb_set_intfdata(interface, NULL); udev = es1->usb_dev; greybus_remove_hd(es1->hd); @@ -380,33 +366,6 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(udev); } -/* Callback for when we get a SVC message */ -static void svc_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb svc in error %d (dropped)\n", status); - return; - } - - /* We have a message, create a new message structure, add it to the - * list, and wake up our thread that will process the messages. - */ - greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); - -exit: - /* resubmit the urb to get more messages */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "Can not submit urb for AP data: %d\n", retval); -} - static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; @@ -610,12 +569,10 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - bool int_in_found = false; bool bulk_in_found = false; bool bulk_out_found = false; int retval = -ENOMEM; int i; - u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); @@ -644,11 +601,7 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_int_in(endpoint)) { - es1->svc_endpoint = endpoint->bEndpointAddress; - svc_interval = endpoint->bInterval; - int_in_found = true; - } else if (usb_endpoint_is_bulk_in(endpoint)) { + if (usb_endpoint_is_bulk_in(endpoint)) { es1->cport_in_endpoint = endpoint->bEndpointAddress; bulk_in_found = true; } else if (usb_endpoint_is_bulk_out(endpoint)) { @@ -660,27 +613,12 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if ((int_in_found == false) || - (bulk_in_found == false) || + if ((bulk_in_found == false) || (bulk_out_found == false)) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } - /* Create our buffer and URB to get SVC messages, and start it up */ - es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); - if (!es1->svc_buffer) - goto error; - - es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!es1->svc_urb) - goto error; - - usb_fill_int_urb(es1->svc_urb, udev, - usb_rcvintpipe(udev, es1->svc_endpoint), - es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, - hd, svc_interval); - /* Allocate buffers for our cport in messages and start them up */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { struct urb *urb; @@ -722,11 +660,6 @@ static int ap_probe(struct usb_interface *interface, goto error; } - /* Start up our svc urb, which allows events to start flowing */ - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, -- cgit v1.2.3-59-g8ed1b From b767ee402070ce467a2b8470706740b55dd3af21 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 17:09:48 -0700 Subject: greybus: es2: remove svc endpoint message handling We have switched over to use the "new" svc messages, no more need to have a special USB endpoint to handle them, they come through the normal CPort messages. Based on a patch from Viresh. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 71 ++----------------------------------------- 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 7621e95071ed..c353ca56ce96 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -18,7 +18,6 @@ #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { @@ -85,10 +84,7 @@ struct es1_cport_out { * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure * @control_endpoint: endpoint to send data to SVC - * @svc_endpoint: endpoint for SVC data in - * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint - * @svc_urb: urb for SVC messages coming in on @svc_endpoint * @cport_in: endpoint, urbs and buffer for cport in messages * @cport_out: endpoint for for cport out messages * @cport_out_urb: array of urbs for the CPort out messages @@ -104,10 +100,6 @@ struct es1_ap_dev { struct greybus_host_device *hd; __u8 control_endpoint; - __u8 svc_endpoint; - - u8 *svc_buffer; - struct urb *svc_urb; struct es1_cport_in cport_in[NUM_BULKS]; struct es1_cport_out cport_out[NUM_BULKS]; @@ -467,12 +459,6 @@ static void ap_disconnect(struct usb_interface *interface) } } - usb_kill_urb(es1->svc_urb); - usb_free_urb(es1->svc_urb); - es1->svc_urb = NULL; - kfree(es1->svc_buffer); - es1->svc_buffer = NULL; - usb_set_intfdata(interface, NULL); udev = es1->usb_dev; greybus_remove_hd(es1->hd); @@ -480,33 +466,6 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(udev); } -/* Callback for when we get a SVC message */ -static void svc_in_callback(struct urb *urb) -{ - struct greybus_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - int status = check_urb_status(urb); - int retval; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb svc in error %d (dropped)\n", status); - return; - } - - /* We have a message, create a new message structure, add it to the - * list, and wake up our thread that will process the messages. - */ - greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); - -exit: - /* resubmit the urb to get more messages */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "Can not submit urb for AP data: %d\n", retval); -} - static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; @@ -710,12 +669,10 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - bool int_in_found = false; int bulk_in = 0; int bulk_out = 0; int retval = -ENOMEM; int i; - u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); @@ -744,11 +701,7 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_int_in(endpoint)) { - es1->svc_endpoint = endpoint->bEndpointAddress; - svc_interval = endpoint->bInterval; - int_in_found = true; - } else if (usb_endpoint_is_bulk_in(endpoint)) { + if (usb_endpoint_is_bulk_in(endpoint)) { es1->cport_in[bulk_in++].endpoint = endpoint->bEndpointAddress; } else if (usb_endpoint_is_bulk_out(endpoint)) { @@ -760,27 +713,12 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if ((int_in_found == false) || - (bulk_in == 0) || + if ((bulk_in == 0) || (bulk_out == 0)) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } - /* Create our buffer and URB to get SVC messages, and start it up */ - es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); - if (!es1->svc_buffer) - goto error; - - es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!es1->svc_urb) - goto error; - - usb_fill_int_urb(es1->svc_urb, udev, - usb_rcvintpipe(udev, es1->svc_endpoint), - es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, - hd, svc_interval); - /* Allocate buffers for our cport in messages and start them up */ for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; @@ -826,11 +764,6 @@ static int ap_probe(struct usb_interface *interface, goto error; } - /* Start up our svc urb, which allows events to start flowing */ - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, -- cgit v1.2.3-59-g8ed1b From 38e7b48b0c2e9c3fe2831d824466dc52d1393e7c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 24 Jul 2015 15:32:26 +0530 Subject: greybus: get rid of old svc-protocol Its not used anymore as we have more sophisticated svc protocol in place, lets get rid of earlier code. Signed-off-by: Viresh Kumar Tested-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 - drivers/staging/greybus/ap.c | 351 -------------------------------------- drivers/staging/greybus/core.c | 9 - drivers/staging/greybus/greybus.h | 2 - 4 files changed, 363 deletions(-) delete mode 100644 drivers/staging/greybus/ap.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1467c5b3fcd8..d14bf4d21983 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,6 +1,5 @@ greybus-y := core.o \ debugfs.o \ - ap.o \ manifest.o \ endo.o \ module.o \ diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c deleted file mode 100644 index fc238170ad12..000000000000 --- a/drivers/staging/greybus/ap.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Greybus "AP" message loop handling - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include "svc_msg.h" -#include "greybus_manifest.h" -#include "greybus.h" - -struct ap_msg { - u8 *data; - size_t size; - struct greybus_host_device *hd; - struct work_struct event; -}; - -static struct workqueue_struct *ap_workqueue; - -static struct svc_msg *svc_msg_alloc(enum svc_function_id id) -{ - struct svc_msg *svc_msg; - - svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL); - if (!svc_msg) - return NULL; - - // FIXME - verify we are only sending function IDs we should be - svc_msg->header.function_id = id; - return svc_msg; -} - -static void svc_msg_free(struct svc_msg *svc_msg) -{ - kfree(svc_msg); -} - -static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - int retval; - - // FIXME - Do we need to do more than just pass it to the hd and then - // free it? - retval = hd->driver->submit_svc(svc_msg, hd); - - svc_msg_free(svc_msg); - return retval; -} - -static void svc_handshake(struct svc_function_handshake *handshake, - int payload_length, struct greybus_host_device *hd) -{ - struct svc_msg *svc_msg; - - if (payload_length != sizeof(*handshake)) { - dev_err(hd->parent, - "Illegal size of svc handshake message %d\n", - payload_length); - return; - } - - /* A new SVC communication channel, let's verify a supported version */ - if ((handshake->version_major != GREYBUS_VERSION_MAJOR) || - (handshake->version_minor != GREYBUS_VERSION_MINOR)) { - dev_warn(hd->parent, - "received invalid greybus version %u.%u\n", - handshake->version_major, handshake->version_minor); - return; - } - - /* Validate that the handshake came from the SVC */ - if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) { - /* we don't know what to do with this, log it and return */ - dev_dbg(hd->parent, "received invalid handshake type %d\n", - handshake->handshake_type); - return; - } - - /* Send back a AP_HELLO message */ - svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE); - if (!svc_msg) - return; - - svc_msg->header.message_type = SVC_MSG_DATA; - svc_msg->header.payload_length = - cpu_to_le16(sizeof(*handshake)); - svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR; - svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR; - svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; - - (void)svc_msg_send(svc_msg, hd); -} - -static void svc_management(struct svc_function_unipro_management *management, - int payload_length, struct greybus_host_device *hd) -{ - struct gb_interface *intf; - int ret; - - if (payload_length != sizeof(*management)) { - dev_err(hd->parent, - "Illegal size of svc management message %d\n", - payload_length); - return; - } - - switch (management->management_packet_type) { - case SVC_MANAGEMENT_AP_ID: - hd->device_id = management->ap_id.device_id; - break; - case SVC_MANAGEMENT_LINK_UP: - intf = gb_interface_find(hd, management->link_up.interface_id); - if (!intf) { - dev_err(hd->parent, "Interface ID %d not found\n", - management->link_up.interface_id); - return; - } - ret = gb_interface_init(intf, management->link_up.device_id); - if (ret) { - dev_err(hd->parent, - "error %d initializing interface %hhu\n", - ret, management->link_up.interface_id); - return; - } - break; - default: - dev_err(hd->parent, "Unhandled UniPro management message\n"); - } -} - -static void svc_hotplug(struct svc_function_hotplug *hotplug, - int payload_length, struct greybus_host_device *hd) -{ - u8 interface_id = hotplug->interface_id; - - switch (hotplug->hotplug_event) { - case SVC_HOTPLUG_EVENT: - /* Add a new interface to the system */ - if (payload_length != sizeof(*hotplug)) { - dev_err(hd->parent, - "Illegal size of svc hotplug message %d\n", - payload_length); - return; - } - dev_dbg(hd->parent, "interface id %d added\n", interface_id); - gb_interface_create(hd, interface_id); - break; - - case SVC_HOTUNPLUG_EVENT: - /* Remove a interface from the system */ - if (payload_length != sizeof(*hotplug)) { - dev_err(hd->parent, - "Illegal size of svc hotunplug message %d\n", - payload_length); - return; - } - dev_dbg(hd->parent, "interface id %d removed\n", interface_id); - gb_interface_remove(hd, interface_id); - break; - - default: - dev_err(hd->parent, - "Received invalid hotplug message type %d\n", - hotplug->hotplug_event); - break; - } -} - -static void svc_power(struct svc_function_power *power, - int payload_length, struct greybus_host_device *hd) -{ - u8 interface_id = power->interface_id; - - /* - * The AP is only allowed to get a Battery Status message, not a Battery - * Status Request - */ - if (power->power_type != SVC_POWER_BATTERY_STATUS) { - dev_err(hd->parent, "Received invalid power type %d\n", - power->power_type); - return; - } - - /* - * As struct struct svc_function_power_battery_status_request is 0 bytes - * big, we can just check the union of the whole structure to validate - * the size of this message. - */ - if (payload_length != sizeof(*power)) { - dev_err(hd->parent, - "Illegal size of svc power message %d\n", - payload_length); - return; - } - - dev_dbg(hd->parent, "power status for interface id %d is %d\n", - interface_id, power->status.status); - - // FIXME - do something with the power information, like update our - // battery information... -} - -static void svc_epm(struct svc_function_epm *epm, - int payload_length, struct greybus_host_device *hd) -{ - /* What? An AP should not get this message */ - dev_err(hd->parent, "Got an EPM message???\n"); -} - -static void svc_suspend(struct svc_function_suspend *suspend, - int payload_length, struct greybus_host_device *hd) -{ - /* What? An AP should not get this message */ - dev_err(hd->parent, "Got an suspend message???\n"); -} - -static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) -{ - struct svc_msg *svc_msg; - struct svc_msg_header *header; - struct greybus_host_device *hd = ap_msg->hd; - - svc_msg = (struct svc_msg *)ap_msg->data; - header = &svc_msg->header; - - /* Validate the message type */ - if (header->message_type != SVC_MSG_DATA) { - dev_err(hd->parent, "message type %d received?\n", - header->message_type); - return NULL; - } - - /* - * The validation of the size of the message buffer happens in each - * svc_* function, due to the different types of messages, keeping the - * logic for each message only in one place. - */ - - return svc_msg; -} - -static void ap_process_event(struct work_struct *work) -{ - struct svc_msg *svc_msg; - struct greybus_host_device *hd; - struct ap_msg *ap_msg; - int payload_length; - - ap_msg = container_of(work, struct ap_msg, event); - hd = ap_msg->hd; - - /* Turn the "raw" data into a real message */ - svc_msg = convert_ap_message(ap_msg); - if (!svc_msg) - return; - - payload_length = le16_to_cpu(svc_msg->header.payload_length); - - /* Look at the message to figure out what to do with it */ - switch (svc_msg->header.function_id) { - case SVC_FUNCTION_HANDSHAKE: - svc_handshake(&svc_msg->handshake, payload_length, hd); - break; - case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT: - svc_management(&svc_msg->management, payload_length, hd); - break; - case SVC_FUNCTION_HOTPLUG: - svc_hotplug(&svc_msg->hotplug, payload_length, hd); - break; - case SVC_FUNCTION_POWER: - svc_power(&svc_msg->power, payload_length, hd); - break; - case SVC_FUNCTION_EPM: - svc_epm(&svc_msg->epm, payload_length, hd); - break; - case SVC_FUNCTION_SUSPEND: - svc_suspend(&svc_msg->suspend, payload_length, hd); - break; - default: - dev_err(hd->parent, "received invalid SVC function ID %d\n", - svc_msg->header.function_id); - } - - /* clean the message up */ - kfree(ap_msg->data); - kfree(ap_msg); -} - -int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size) -{ - struct ap_msg *ap_msg; - - /* - * Totally naive copy the message into a new structure that we slowly - * create and add it to the list. Let's get this working, the odds of - * this being any "slow path" for AP messages is really low at this - * point in time, but you never know, so this comment is here to point - * out that maybe we should use a slab allocator, or even just not copy - * the data, but use it directly and force the urbs to be "new" each - * time. - */ - - /* Note - this can, and will, be called in interrupt context. */ - ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC); - if (!ap_msg) - return -ENOMEM; - ap_msg->data = kmalloc(size, GFP_ATOMIC); - if (!ap_msg->data) { - kfree(ap_msg); - return -ENOMEM; - } - memcpy(ap_msg->data, data, size); - ap_msg->size = size; - ap_msg->hd = hd; - - INIT_WORK(&ap_msg->event, ap_process_event); - queue_work(ap_workqueue, &ap_msg->event); - - return 0; -} -EXPORT_SYMBOL_GPL(greybus_svc_in); - -int __init gb_ap_init(void) -{ - ap_workqueue = alloc_ordered_workqueue("greybus_ap", 0); - if (!ap_workqueue) - return -ENOMEM; - - return 0; -} - -void gb_ap_exit(void) -{ - destroy_workqueue(ap_workqueue); - ap_workqueue = NULL; -} - - diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 225fa4fb5268..cd15a94a67de 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -270,12 +270,6 @@ static int __init gb_init(void) goto error_bus; } - retval = gb_ap_init(); - if (retval) { - pr_err("gb_ap_init failed (%d)\n", retval); - goto error_ap; - } - retval = gb_operation_init(); if (retval) { pr_err("gb_operation_init failed (%d)\n", retval); @@ -309,8 +303,6 @@ error_control: error_endo: gb_operation_exit(); error_operation: - gb_ap_exit(); -error_ap: bus_unregister(&greybus_bus_type); error_bus: gb_debugfs_cleanup(); @@ -325,7 +317,6 @@ static void __exit gb_exit(void) gb_control_protocol_exit(); gb_endo_exit(); gb_operation_exit(); - gb_ap_exit(); bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2214f447df2b..90f996276ca4 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -156,8 +156,6 @@ void greybus_deregister_driver(struct greybus_driver *driver); int greybus_disabled(void); int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); -int gb_ap_init(void); -void gb_ap_exit(void); void gb_debugfs_init(void); void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); -- cgit v1.2.3-59-g8ed1b From e08aaa49df7866c1a786dc01ce4f8a47c6aaa954 Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:31 -0400 Subject: greybus: svc: add route create operation Implement the SVC Protocol Route Create Operation. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 8 ++++++++ drivers/staging/greybus/svc.c | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9708e934242f..5b97073f4100 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -563,6 +563,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_RESET 0x06 #define GB_SVC_TYPE_CONN_CREATE 0x07 #define GB_SVC_TYPE_CONN_DESTROY 0x08 +#define GB_SVC_TYPE_ROUTE_CREATE 0x0b /* SVC version request/response have same payload as gb_protocol_version_response */ @@ -616,6 +617,13 @@ struct gb_svc_conn_destroy_request { }; /* connection destroy response has no payload */ +struct gb_svc_route_create_request { + __u8 intf1_id; + __u8 dev1_id; + __u8 intf2_id; + __u8 dev2_id; +}; + /* UART */ /* Version of the Greybus UART protocol we support */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5f2b2b43b939..f3321296db8a 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -118,6 +118,20 @@ static int connection_destroy_operation(struct gb_svc *svc, &request, sizeof(request), NULL, 0); } +static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, + u8 intf2_id, u8 dev2_id) +{ + struct gb_svc_route_create_request request; + + request.intf1_id = intf1_id; + request.dev1_id = dev1_id; + request.intf2_id = intf2_id; + request.dev2_id = dev2_id; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE, + &request, sizeof(request), NULL, 0); +} + int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) { return intf_device_id_operation(svc, intf_id, device_id); @@ -148,6 +162,13 @@ int gb_svc_connection_destroy(struct gb_svc *svc, } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); +int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, + u8 intf2_id, u8 dev2_id) { + return route_create_operation(svc, intf1_id, dev1_id, + intf2_id, dev2_id); +} +EXPORT_SYMBOL_GPL(gb_svc_route_create); + static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; -- cgit v1.2.3-59-g8ed1b From 7e275465fabd58065ec49c770bd4446f41a13e87 Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:32 -0400 Subject: greybus: svc: create bidirectional routes on hotplug request Upon receiving a hotplug request, we need to prepare the routing table to allow packets to flow between the AP interface and the newly detected interface. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index f3321296db8a..3d1808712598 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -309,6 +309,25 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) goto ida_put; } + /* + * Create a two-way route between the AP and the new interface + */ + ret = route_create_operation(svc, hd->endo->ap_intf_id, + GB_DEVICE_ID_AP, intf_id, device_id); + if (ret) { + dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", + __func__, intf_id, device_id, ret); + goto ida_put; + } + + ret = route_create_operation(svc, intf_id, device_id, + hd->endo->ap_intf_id, GB_DEVICE_ID_AP); + if (ret) { + dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", + __func__, intf_id, device_id, ret); + goto ida_put; + } + ret = gb_interface_init(intf, device_id); if (ret) { dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", -- cgit v1.2.3-59-g8ed1b From 75a60ed20d1ad58983e04dee27faae454e81d6a5 Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:33 -0400 Subject: greybus: svc: connection: ask SVC to create connections Ask the SVC to do all the necessary bits for creating a new connection. This is skipped for the initial SVC connection. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 7 ++++++- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/svc.c | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b32da8af68f4..1a657f706b93 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -251,7 +251,12 @@ gb_connection_create_range(struct greybus_host_device *hd, spin_unlock_irq(&gb_connections_lock); - /* XXX Will have to establish connections to get version */ + if (hd_cport_id != GB_SVC_CPORT_ID) { + gb_svc_connection_create(hd->svc, + hd->endo->ap_intf_id, hd_cport_id, + bundle->intf->interface_id, cport_id); + } + gb_connection_bind_protocol(connection); if (!connection->protocol) dev_warn(&connection->dev, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 90f996276ca4..ef11b960d1b7 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -103,6 +103,7 @@ struct greybus_host_device { struct gb_endo *endo; struct gb_connection *initial_svc_connection; + struct gb_svc *svc; /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 3d1808712598..b718a881a7e3 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -430,6 +430,7 @@ static int gb_svc_connection_init(struct gb_connection *connection) if (!svc) return -ENOMEM; + connection->hd->svc = svc; svc->connection = connection; connection->private = svc; @@ -445,6 +446,7 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; + connection->hd->svc = NULL; connection->private = NULL; kfree(svc); } -- cgit v1.2.3-59-g8ed1b From 0b22649755df615d4b82b625f938a31435ff1726 Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Fri, 24 Jul 2015 19:02:34 -0400 Subject: greybus: svc: add flags and traffic class parameter to connection create op The AP needs to be able to specify L4 CPort flags and traffic class parameters on a connection-by-connection basis. Extend the connection create operation to accept these. Since there's no policy to decide these, fix them at TC0 with end-to-end-flow control, controlled segment dropping, and CPort safety valve enabled. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Perry Hung Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 ++ drivers/staging/greybus/svc.c | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 5b97073f4100..e2d38dfe06b6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -606,6 +606,8 @@ struct gb_svc_conn_create_request { __u16 cport1_id; __u8 intf2_id; __u16 cport2_id; + __u8 tc; + __u8 flags; }; /* connection create response has no payload */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b718a881a7e3..b94a84aca0d8 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -9,6 +9,10 @@ #include "greybus.h" +#define CPORT_FLAGS_E2EFC (1) +#define CPORT_FLAGS_CSD_N (2) +#define CPORT_FLAGS_CSV_N (4) + struct gb_svc { struct gb_connection *connection; u8 version_major; @@ -98,6 +102,12 @@ static int connection_create_operation(struct gb_svc *svc, request.cport1_id = cport1_id; request.intf2_id = intf2_id; request.cport2_id = cport2_id; + /* + * XXX: fix connections paramaters to TC0 and all CPort flags + * for now. + */ + request.tc = 0; + request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC; return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); -- cgit v1.2.3-59-g8ed1b From 8476ec60e1111839469779bb0bba1eade95e53c5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 17:15:59 -0700 Subject: greybus: greybus.h: remove greybus_svc_in() The function is gone, remove it from the header file as well. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index ef11b960d1b7..a0114953d3e7 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -156,7 +156,6 @@ void greybus_deregister_driver(struct greybus_driver *driver); int greybus_disabled(void); -int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); void gb_debugfs_init(void); void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); -- cgit v1.2.3-59-g8ed1b From 260998ebc6c5e4a2518347eb4f99629fac4fe6cb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 17:19:21 -0700 Subject: greybus: remove submit_svc from the host driver The callback is never used anymore, so remove it from struct greybus_host_driver as well as from the es1 and es2 drivers. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 +-- drivers/staging/greybus/es1.c | 22 ---------------------- drivers/staging/greybus/es2.c | 22 ---------------------- drivers/staging/greybus/greybus.h | 2 -- 4 files changed, 1 insertion(+), 48 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index cd15a94a67de..7d5cd99157f5 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -176,8 +176,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * Validate that the driver implements all of the callbacks * so that we don't have to every time we make them. */ - if ((!driver->message_send) || (!driver->message_cancel) || - (!driver->submit_svc)) { + if ((!driver->message_send) || (!driver->message_cancel)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); return ERR_PTR(-EINVAL); } diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 82a7c67c1037..e203180e1e5c 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -14,7 +14,6 @@ #include #include "greybus.h" -#include "svc_msg.h" #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ @@ -95,26 +94,6 @@ static void usb_log_enable(struct es1_ap_dev *es1); static void usb_log_disable(struct es1_ap_dev *es1); #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ -static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - int retval; - - /* SVC messages go down our control pipe */ - retval = usb_control_msg(es1->usb_dev, - usb_sndctrlpipe(es1->usb_dev, - es1->control_endpoint), - REQUEST_SVC, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, 0x00, - (char *)svc_msg, - sizeof(*svc_msg), - ES1_TIMEOUT); - if (retval != sizeof(*svc_msg)) - return retval; - - return 0; -} static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) { @@ -295,7 +274,6 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, - .submit_svc = submit_svc, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index c353ca56ce96..aba1927fc34f 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -14,7 +14,6 @@ #include #include "greybus.h" -#include "svc_msg.h" #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ @@ -134,26 +133,6 @@ static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ -static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - int retval; - - /* SVC messages go down our control pipe */ - retval = usb_control_msg(es1->usb_dev, - usb_sndctrlpipe(es1->usb_dev, - es1->control_endpoint), - REQUEST_SVC, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, 0x00, - (char *)svc_msg, - sizeof(*svc_msg), - ES1_TIMEOUT); - if (retval != sizeof(*svc_msg)) - return retval; - - return 0; -} static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set) { @@ -391,7 +370,6 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, - .submit_svc = submit_svc, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index a0114953d3e7..30ea4a4c17ea 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -84,8 +84,6 @@ struct greybus_host_driver { int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); - int (*submit_svc)(struct svc_msg *svc_msg, - struct greybus_host_device *hd); }; struct greybus_host_device { -- cgit v1.2.3-59-g8ed1b From d45b1b8674cbefe1358a042d800f84766e534ba5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Jul 2015 17:21:25 -0700 Subject: greybus: interface: make gb_interface_destroy() static The function is only called locally, so mark it static to make sparse happy. Tested-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index f1e2956b25a7..4a26bf6f714c 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -183,7 +183,7 @@ put_module: /* * Tear down a previously set up module. */ -void gb_interface_destroy(struct gb_interface *intf) +static void interface_destroy(struct gb_interface *intf) { struct gb_module *module; struct gb_bundle *bundle; @@ -279,7 +279,7 @@ void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id) struct gb_interface *intf = gb_interface_find(hd, interface_id); if (intf) - gb_interface_destroy(intf); + interface_destroy(intf); else dev_err(hd->parent, "interface id %d not found\n", interface_id); @@ -290,5 +290,5 @@ void gb_interfaces_remove(struct greybus_host_device *hd) struct gb_interface *intf, *temp; list_for_each_entry_safe(intf, temp, &hd->interfaces, links) - gb_interface_destroy(intf); + interface_destroy(intf); } -- cgit v1.2.3-59-g8ed1b From 0b161d0a92717d57fd1598e326a68e859bf92a48 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 25 Jul 2015 10:10:05 +0530 Subject: greybus: es1: create svc connection early enough The svc connection needs to be ready before creating the URBs, otherwise the svc version request might come in before the AP was ready to parse them. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 12 ++++++------ drivers/staging/greybus/es2.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index e203180e1e5c..5418f4675ec1 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -563,6 +563,12 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } + /* Initialize AP's greybus interface */ + if (!gb_ap_svc_connection_create(hd)) { + retval = -EINVAL; + goto error; + } + es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; @@ -632,12 +638,6 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* Initialize AP's greybus interface */ - if (!gb_ap_svc_connection_create(hd)) { - retval = -EINVAL; - goto error; - } - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index aba1927fc34f..d2c054a2ec0e 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -663,6 +663,12 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } + /* Initialize AP's greybus interface */ + if (!gb_ap_svc_connection_create(hd)) { + retval = -EINVAL; + goto error; + } + es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; @@ -736,12 +742,6 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } - /* Initialize AP's greybus interface */ - if (!gb_ap_svc_connection_create(hd)) { - retval = -EINVAL; - goto error; - } - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, -- cgit v1.2.3-59-g8ed1b From ca539a67c6a25e47233fdab67bdf1d4db00f883f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 25 Jul 2015 10:10:06 +0530 Subject: greybus: remove svc_msg.h Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/svc_msg.h | 157 -------------------------------------- 2 files changed, 158 deletions(-) delete mode 100644 drivers/staging/greybus/svc_msg.h diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 30ea4a4c17ea..cdade1e9b4c7 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -73,7 +73,6 @@ */ struct greybus_host_device; -struct svc_msg; /* Greybus "Host driver" structure, needed by a host controller driver to be * able to handle both SVC control as well as "real" greybus messages diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h deleted file mode 100644 index 3c628c5d6e38..000000000000 --- a/drivers/staging/greybus/svc_msg.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Greybus AP <-> SVC message structure format. - * - * See "Greybus Application Protocol" document (version 0.1) for - * details on these values and structures. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 and BSD license. - */ - -#ifndef __SVC_MSG_H -#define __SVC_MSG_H - -enum svc_function_id { - SVC_FUNCTION_HANDSHAKE = 0x00, - SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, - SVC_FUNCTION_HOTPLUG = 0x02, - SVC_FUNCTION_POWER = 0x03, - SVC_FUNCTION_EPM = 0x04, - SVC_FUNCTION_SUSPEND = 0x05, -}; - -enum svc_msg_type { - SVC_MSG_DATA = 0x00, - SVC_MSG_ERROR = 0xff, -}; - -struct svc_msg_header { - __u8 function_id; /* enum svc_function_id */ - __u8 message_type; - __le16 payload_length; -} __packed; - -enum svc_function_handshake_type { - SVC_HANDSHAKE_SVC_HELLO = 0x00, - SVC_HANDSHAKE_AP_HELLO = 0x01, - SVC_HANDSHAKE_MODULE_HELLO = 0x02, -}; - -struct svc_function_handshake { - __u8 version_major; - __u8 version_minor; - __u8 handshake_type; /* enum svc_function_handshake_type */ -} __packed; - -struct svc_function_unipro_set_route { - __u8 device_id; -} __packed; - -struct svc_function_unipro_link_up { - __u8 interface_id; /* Interface id within the Endo */ - __u8 device_id; -} __packed; - -struct svc_function_ap_id { - __u8 interface_id; - __u8 device_id; -} __packed; - -enum svc_function_management_event { - SVC_MANAGEMENT_AP_ID = 0x00, - SVC_MANAGEMENT_LINK_UP = 0x01, - SVC_MANAGEMENT_SET_ROUTE = 0x02, -}; - -struct svc_function_unipro_management { - __u8 management_packet_type; /* enum svc_function_management_event */ - union { - struct svc_function_ap_id ap_id; - struct svc_function_unipro_link_up link_up; - struct svc_function_unipro_set_route set_route; - }; -} __packed; - -enum svc_function_hotplug_event { - SVC_HOTPLUG_EVENT = 0x00, - SVC_HOTUNPLUG_EVENT = 0x01, -}; - -struct svc_function_hotplug { - __u8 hotplug_event; /* enum svc_function_hotplug_event */ - __u8 interface_id; /* Interface id within the Endo */ -} __packed; - -enum svc_function_power_type { - SVC_POWER_BATTERY_STATUS = 0x00, - SVC_POWER_BATTERY_STATUS_REQUEST = 0x01, -}; - -enum svc_function_battery_status { - SVC_BATTERY_UNKNOWN = 0x00, - SVC_BATTERY_CHARGING = 0x01, - SVC_BATTERY_DISCHARGING = 0x02, - SVC_BATTERY_NOT_CHARGING = 0x03, - SVC_BATTERY_FULL = 0x04, -}; - -struct svc_function_power_battery_status { - __le16 charge_full; - __le16 charge_now; - __u8 status; /* enum svc_function_battery_status */ -} __packed; - -struct svc_function_power_battery_status_request { -} __packed; - -/* XXX - * Each interface carries power, so it's possible these things - * are associated with each UniPro device and not just the module. - * For now it's safe to assume it's per-module. - */ -struct svc_function_power { - __u8 power_type; /* enum svc_function_power_type */ - __u8 interface_id; - union { - struct svc_function_power_battery_status status; - struct svc_function_power_battery_status_request request; - }; -} __packed; - -enum svc_function_epm_command_type { - SVC_EPM_ENABLE = 0x00, - SVC_EPM_DISABLE = 0x01, -}; - -/* EPM's are associated with the module */ -struct svc_function_epm { - __u8 epm_command_type; /* enum svc_function_epm_command_type */ - __u8 module_id; -} __packed; - -enum svc_function_suspend_command_type { - SVC_SUSPEND_FIXME_1 = 0x00, // FIXME - SVC_SUSPEND_FIXME_2 = 0x01, -}; - -/* We'll want independent control for multi-interface modules */ -struct svc_function_suspend { - __u8 suspend_command_type; /* enum function_suspend_command_type */ - __u8 device_id; -} __packed; - -struct svc_msg { - struct svc_msg_header header; - union { - struct svc_function_handshake handshake; - struct svc_function_unipro_management management; - struct svc_function_hotplug hotplug; - struct svc_function_power power; - struct svc_function_epm epm; - struct svc_function_suspend suspend; - }; -} __packed; - -#endif /* __SVC_MSG_H */ -- cgit v1.2.3-59-g8ed1b From e75b82bfc72533be25ce42241ca2d07b607ee705 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 28 Jul 2015 07:28:42 +0530 Subject: greybus: initialize svc connection while creating hd Its really part of initializing the host device and is required for every 'hd' that is created. Lets move the call to do basic initialization of svc connection to greybus_create_hd(). Also add a comment to specify why we need to do it that early. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 15 +++++++++++++++ drivers/staging/greybus/es1.c | 6 ------ drivers/staging/greybus/es2.c | 6 ------ drivers/staging/greybus/svc.c | 1 - 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 7d5cd99157f5..9f105fb12ede 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -208,6 +208,21 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; + /* + * Initialize AP's SVC protocol connection: + * + * This is required as part of early initialization of the host device + * as we need this connection in order to start any kind of message + * exchange between the AP and the SVC. SVC will start with a + * 'get-version' request followed by a 'svc-hello' message and at that + * time we will create a fully initialized svc-connection, as we need + * endo-id and AP's interface id for that. + */ + if (!gb_ap_svc_connection_create(hd)) { + kref_put_mutex(&hd->kref, free_hd, &hd_mutex); + return ERR_PTR(-ENOMEM); + } + return hd; } EXPORT_SYMBOL_GPL(greybus_create_hd); diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 5418f4675ec1..c1fab375bb0b 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -563,12 +563,6 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } - /* Initialize AP's greybus interface */ - if (!gb_ap_svc_connection_create(hd)) { - retval = -EINVAL; - goto error; - } - es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index d2c054a2ec0e..558345cd80af 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -663,12 +663,6 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } - /* Initialize AP's greybus interface */ - if (!gb_ap_svc_connection_create(hd)) { - retval = -EINVAL; - goto error; - } - es1 = hd_to_es1(hd); es1->hd = hd; es1->usb_intf = interface; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b94a84aca0d8..784b7709b432 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -40,7 +40,6 @@ gb_ap_svc_connection_create(struct greybus_host_device *hd) return connection; } -EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create); /* * We know endo-type and AP's interface id now, lets create a proper svc -- cgit v1.2.3-59-g8ed1b From 48ed3f6e4202c9ddadac057108c273b8be85a78d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 30 Jul 2015 22:13:27 +0530 Subject: greybus: connection: remove special check for svc cport id This is required to get things working for now, after the latest revert of svc protocol is done. Currently svc's cport id is set to 2 and that hd cport id will be used for the third connection we make. And that protocol (which is i2c in one of the cases), may not work as the (dis)connected event isn't sent for it. Fix this by getting rid of svc protocol check from (dis)connected events for now. This must be reverted later, once svc protocol is included again. Signed-off-by: Viresh Kumar Tested-by: Mark Greer Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b32da8af68f4..3b553e682359 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -344,8 +344,7 @@ int gb_connection_init(struct gb_connection *connection) * Inform Interface about Active CPorts. We don't need to do this * operation for control cport. */ - if (cport_id != GB_CONTROL_CPORT_ID && - connection->hd_cport_id != GB_SVC_CPORT_ID) { + if (cport_id != GB_CONTROL_CPORT_ID) { struct gb_control *control = connection->bundle->intf->control; ret = gb_control_connected_operation(control, cport_id); @@ -397,8 +396,7 @@ void gb_connection_exit(struct gb_connection *connection) * Inform Interface about In-active CPorts. We don't need to do this * operation for control cport. */ - if (cport_id != GB_CONTROL_CPORT_ID && - connection->hd_cport_id != GB_SVC_CPORT_ID) { + if (cport_id != GB_CONTROL_CPORT_ID) { struct gb_control *control = connection->bundle->intf->control; int ret; -- cgit v1.2.3-59-g8ed1b From 32574ff3e4360e7c98fe5b2d54afb652aca45a8d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Jul 2015 12:22:09 -0700 Subject: greybus: Revert "connection: remove special check for svc cport id" This reverts commit 60b1d71fdfc2c5266051a1297df01ea0b1f3ba99 as we want the port number to be correct in the svc branch. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index e7f9c2f82982..1a657f706b93 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -349,7 +349,8 @@ int gb_connection_init(struct gb_connection *connection) * Inform Interface about Active CPorts. We don't need to do this * operation for control cport. */ - if (cport_id != GB_CONTROL_CPORT_ID) { + if (cport_id != GB_CONTROL_CPORT_ID && + connection->hd_cport_id != GB_SVC_CPORT_ID) { struct gb_control *control = connection->bundle->intf->control; ret = gb_control_connected_operation(control, cport_id); @@ -401,7 +402,8 @@ void gb_connection_exit(struct gb_connection *connection) * Inform Interface about In-active CPorts. We don't need to do this * operation for control cport. */ - if (cport_id != GB_CONTROL_CPORT_ID) { + if (cport_id != GB_CONTROL_CPORT_ID && + connection->hd_cport_id != GB_SVC_CPORT_ID) { struct gb_control *control = connection->bundle->intf->control; int ret; -- cgit v1.2.3-59-g8ed1b From 3a6b5aeec771205d35f28616a610d9ed5d2c49b2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Jul 2015 20:30:36 +0200 Subject: greybus: usb: clean up driver Remove unused, broken or unneeded code and constructs. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/usb.c | 181 ++---------------------------------------- 1 file changed, 6 insertions(+), 175 deletions(-) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index e49fffdca53b..04c8783eb061 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -24,32 +24,7 @@ #define GB_USB_TYPE_PROTOCOL_VERSION 0x01 #define GB_USB_TYPE_HCD_STOP 0x02 #define GB_USB_TYPE_HCD_START 0x03 -#define GB_USB_TYPE_URB_ENQUEUE 0x04 -#define GB_USB_TYPE_URB_DEQUEUE 0x05 -#define GB_USB_TYPE_ENDPOINT_DISABLE 0x06 #define GB_USB_TYPE_HUB_CONTROL 0x07 -#define GB_USB_TYPE_GET_FRAME_NUMBER 0x08 -#define GB_USB_TYPE_HUB_STATUS_DATA 0x09 - -struct gb_usb_urb_enqueue_request { - __le32 pipe; - __le32 transfer_flags; - __le32 transfer_buffer_length; - __le32 maxpacket; - __le32 interval; - __le64 hcpriv_ep; - __le32 number_of_packets; - u8 setup_packet[8]; - u8 payload[0]; -}; - -struct gb_usb_urb_dequeue_request { - __le64 hcpriv_ep; -}; - -struct gb_usb_endpoint_disable_request { - __le64 hcpriv; -}; struct gb_usb_hub_control_request { __le16 typeReq; @@ -62,22 +37,6 @@ struct gb_usb_hub_control_response { u8 buf[0]; }; -struct gb_usb_header { - __le16 size; - __le16 id; - __u8 type; -}; - -struct gb_usb_hub_status { - __le32 status; - __le16 buf_size; - u8 buf[0]; -}; - -static struct gb_usb_hub_status *hub_status; // FIXME!!! -static DEFINE_SPINLOCK(hub_status_lock); -static atomic_t frame_number; // FIXME!!! - struct gb_usb_device { struct gb_connection *connection; @@ -123,83 +82,22 @@ static int hcd_start(struct usb_hcd *hcd) static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { - struct gb_usb_device *dev = to_gb_usb_device(hcd); - struct gb_usb_urb_enqueue_request *request; - struct gb_operation *operation; - int ret; - - operation = gb_operation_create(dev->connection, - GB_USB_TYPE_URB_ENQUEUE, - sizeof(*request) + - urb->transfer_buffer_length, 0, - GFP_KERNEL); - if (!operation) - return -ENODEV; - - request = operation->request->payload; - request->pipe = cpu_to_le32(urb->pipe); - request->transfer_flags = cpu_to_le32(urb->transfer_flags); - request->transfer_buffer_length = cpu_to_le32(urb->transfer_buffer_length); - request->interval = cpu_to_le32(urb->interval); - request->hcpriv_ep = cpu_to_le64((unsigned long)urb->ep->hcpriv); - request->number_of_packets = cpu_to_le32(urb->number_of_packets); - - memcpy(request->setup_packet, urb->setup_packet, 8); - memcpy(&request->payload, urb->transfer_buffer, - urb->transfer_buffer_length); - - ret = gb_operation_request_send_sync(operation); - gb_operation_destroy(operation); - - return ret; + return -ENXIO; } static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { - struct gb_usb_device *dev = to_gb_usb_device(hcd); - struct gb_usb_urb_dequeue_request request; - int ret; - - urb->ep->hcpriv = NULL; - request.hcpriv_ep = cpu_to_le64((unsigned long)urb->hcpriv); - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_URB_DEQUEUE, - &request, sizeof(request), NULL, 0); - urb->hcpriv = NULL; - return ret; -} - -static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ - struct gb_usb_device *dev = to_gb_usb_device(hcd); - struct gb_usb_endpoint_disable_request request; - int ret; - - request.hcpriv = cpu_to_le64((unsigned long)ep->hcpriv); - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_ENDPOINT_DISABLE, - &request, sizeof(request), NULL, 0); - ep->hcpriv = NULL; -} - -static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ + return -ENXIO; } static int get_frame_number(struct usb_hcd *hcd) { - return atomic_read(&frame_number); + return 0; } static int hub_status_data(struct usb_hcd *hcd, char *buf) { - int retval; - unsigned long flags; - - spin_lock_irqsave(&hub_status_lock, flags); - memcpy(buf, hub_status->buf, le16_to_cpu(hub_status->buf_size)); - retval = le32_to_cpu(hub_status->status); - spin_unlock_irqrestore(&hub_status_lock, flags); - - return retval; + return 0; } static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, @@ -232,82 +130,15 @@ static struct hc_driver usb_gb_hc_driver = { .start = hcd_start, .stop = hcd_stop, + .urb_enqueue = urb_enqueue, .urb_dequeue = urb_dequeue, - .endpoint_disable = endpoint_disable, - .endpoint_reset = endpoint_reset, + .get_frame_number = get_frame_number, .hub_status_data = hub_status_data, .hub_control = hub_control, }; -#if 0 -static inline void gb_usb_handle_get_frame_number(struct gbuf *gbuf) -{ - __le32 frame_num; - const size_t packet_size = sizeof(struct gb_usb_header) + - sizeof(frame_num); - struct gb_usb_header* hdr = gbuf->transfer_buffer; - - if (le16_to_cpu(hdr->size) != packet_size) { - pr_err("%s(): dropping packet too small\n", __func__); - return; - } - - frame_num = (__le32) ((char*) gbuf->transfer_buffer + - sizeof(struct gb_usb_header)); - atomic_set(&frame_number, le32_to_cpu(frame_num)); -} - -static inline void gb_usb_handle_hubs_status_data(struct gbuf *gbuf) -{ - struct gb_usb_hub_status *new_hubstatus, *hubstatus; - struct gb_usb_header* hdr = gbuf->transfer_buffer; - const size_t min_packet_size = sizeof(struct gb_usb_header) + - sizeof(struct gb_usb_hub_status); - unsigned long flags; - - if (le16_to_cpu(hdr->size) < min_packet_size) { - pr_err("%s(): dropping packet too small\n", __func__); - return; - } - - hubstatus = (struct gb_usb_hub_status*) ((char*) gbuf->transfer_buffer - + sizeof(struct gb_usb_header)); - - if (le16_to_cpu(hdr->size) != min_packet_size + hubstatus->buf_size) { - pr_err("%s(): invalid packet size, dropping packet\n", - __func__); - return; - } - - new_hubstatus = kmalloc(hubstatus->buf_size, GFP_KERNEL); - memcpy(&new_hubstatus, hubstatus, hubstatus->buf_size); - - spin_lock_irqsave(&hub_status_lock, flags); - hubstatus = hub_status; - hub_status = new_hubstatus; - spin_unlock_irqrestore(&hub_status_lock, flags); - - kfree(hubstatus); -} - -static void gb_usb_in_handler(struct gbuf *gbuf) -{ - struct gb_usb_header* hdr = gbuf->transfer_buffer; - - switch (hdr->type) { - case GB_USB_TYPE_GET_FRAME_NUMBER: - gb_usb_handle_get_frame_number(gbuf); - break; - - case GB_USB_TYPE_HUB_STATUS_DATA: - gb_usb_handle_hubs_status_data(gbuf); - break; - } -} -#endif - static int gb_usb_connection_init(struct gb_connection *connection) { struct device *dev = &connection->dev; -- cgit v1.2.3-59-g8ed1b From 583804f7137737e07e69eaed2904cf043f226a83 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Jul 2015 20:30:37 +0200 Subject: greybus: usb: renumber operation types Renumber the current operation types. NOTE: Protocol change. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/usb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 04c8783eb061..2a146d77ecda 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -22,9 +22,9 @@ /* Greybus USB request types */ #define GB_USB_TYPE_INVALID 0x00 #define GB_USB_TYPE_PROTOCOL_VERSION 0x01 -#define GB_USB_TYPE_HCD_STOP 0x02 -#define GB_USB_TYPE_HCD_START 0x03 -#define GB_USB_TYPE_HUB_CONTROL 0x07 +#define GB_USB_TYPE_HCD_START 0x02 +#define GB_USB_TYPE_HCD_STOP 0x03 +#define GB_USB_TYPE_HUB_CONTROL 0x04 struct gb_usb_hub_control_request { __le16 typeReq; -- cgit v1.2.3-59-g8ed1b From cc9bd53eb1bf50e265a8f8741a624bf67851f5c0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Jul 2015 20:30:38 +0200 Subject: greybus: usb: fix hcd allocation, deregistration and deallocation Fix allocation, deregistration and deallocation of USB HCD, and update the hcd_priv helper functions. The HCD private data was not allocated correctly, something which would lead to a crash when accessed in hcd_start. The HCD was neither reregistered or deallocated on connection tear down. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/usb.c | 46 ++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 2a146d77ecda..1ba731e6bfad 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -40,12 +40,19 @@ struct gb_usb_hub_control_response { struct gb_usb_device { struct gb_connection *connection; - struct usb_hcd *hcd; u8 version_major; u8 version_minor; }; -#define to_gb_usb_device(d) ((struct gb_usb_device*) d->hcd_priv) +static inline struct gb_usb_device *to_gb_usb_device(struct usb_hcd *hcd) +{ + return (struct gb_usb_device *)hcd->hcd_priv; +} + +static inline struct usb_hcd *gb_usb_device_to_hcd(struct gb_usb_device *dev) +{ + return container_of((void *)dev, struct usb_hcd, hcd_priv); +} /* Define get_version() routine */ define_get_version(gb_usb_device, USB); @@ -143,45 +150,44 @@ static int gb_usb_connection_init(struct gb_connection *connection) { struct device *dev = &connection->dev; struct gb_usb_device *gb_usb_dev; + struct usb_hcd *hcd; int retval; - gb_usb_dev = kzalloc(sizeof(*gb_usb_dev), GFP_KERNEL); - if (!gb_usb_dev) + hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); + if (!hcd) return -ENOMEM; + gb_usb_dev = to_gb_usb_device(hcd); gb_usb_dev->connection = connection; connection->private = gb_usb_dev; /* Check for compatible protocol version */ retval = get_version(gb_usb_dev); if (retval) - goto error_create_hcd; - - gb_usb_dev->hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); - if (!gb_usb_dev->hcd) { - retval = -ENODEV; - goto error_create_hcd; - } + goto err_put_hcd; - gb_usb_dev->hcd->has_tt = 1; - gb_usb_dev->hcd->hcd_priv[0] = (unsigned long) gb_usb_dev; + hcd->has_tt = 1; - retval = usb_add_hcd(gb_usb_dev->hcd, 0, 0); + retval = usb_add_hcd(hcd, 0, 0); if (retval) - goto error_add_hcd; + goto err_put_hcd; return 0; -error_add_hcd: - usb_put_hcd(gb_usb_dev->hcd); -error_create_hcd: - kfree(gb_usb_dev); + +err_put_hcd: + usb_put_hcd(hcd); + return retval; } static void gb_usb_connection_exit(struct gb_connection *connection) { - // FIXME - tear everything down! + struct gb_usb_device *gb_usb_dev = connection->private; + struct usb_hcd *hcd = gb_usb_device_to_hcd(gb_usb_dev); + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); } static struct gb_protocol usb_protocol = { -- cgit v1.2.3-59-g8ed1b From b1e4a1f8519aebfbf99bee8b3839498f6645e2d7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Jul 2015 20:30:39 +0200 Subject: greybus: usb: fix hc_driver fields Fix hc_driver description, product_desc and flags fields. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/usb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 1ba731e6bfad..151e49f8e4ab 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -130,11 +130,12 @@ static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, } static struct hc_driver usb_gb_hc_driver = { - .description = "greybus_usb", - .product_desc = "GB-Bridge USB Controller", /* TODO: Get this from GPB ?*/ - .flags = HCD_MEMORY | HCD_USB2, /* FIXME: Get this from GPB */ + .description = "greybus-hcd", + .product_desc = "Greybus USB Host Controller", .hcd_priv_size = sizeof(struct gb_usb_device), + .flags = HCD_USB2, + .start = hcd_start, .stop = hcd_stop, -- cgit v1.2.3-59-g8ed1b From a422ecd28eb03eca86d2445d8d690402e183838b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Jul 2015 20:30:40 +0200 Subject: greybus: usb: implement hub_control callback Implement the hub_control callback. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/usb.c | 44 +++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 151e49f8e4ab..17a5d7a5a824 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -110,21 +110,41 @@ static int hub_status_data(struct usb_hcd *hcd, char *buf) static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { - struct gb_usb_hub_control_request request; struct gb_usb_device *dev = to_gb_usb_device(hcd); + struct gb_operation *operation; + struct gb_usb_hub_control_request *request; + struct gb_usb_hub_control_response *response; + size_t response_size; int ret; - request.typeReq = cpu_to_le16(typeReq); - request.wValue = cpu_to_le16(wValue); - request.wIndex = cpu_to_le16(wIndex); - request.wLength = cpu_to_le16(wLength); - - // FIXME - buf needs to come back in struct gb_usb_hub_control_response - // for some types of requests, depending on typeReq. Do we do this in a - // "generic" way, or only ask for a response for the ones we "know" need - // a response (a small subset of all valid typeReq, thankfully.) - ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HUB_CONTROL, - &request, sizeof(request), NULL, 0); + /* FIXME: handle unspecified lengths */ + response_size = sizeof(*response) + wLength; + + operation = gb_operation_create(dev->connection, + GB_USB_TYPE_HUB_CONTROL, + sizeof(*request), + response_size, + GFP_KERNEL); + if (!operation) + return -ENOMEM; + + request = operation->request->payload; + request->typeReq = cpu_to_le16(typeReq); + request->wValue = cpu_to_le16(wValue); + request->wIndex = cpu_to_le16(wIndex); + request->wLength = cpu_to_le16(wLength); + + ret = gb_operation_request_send_sync(operation); + if (ret) + goto out; + + if (wLength) { + /* Greybus core has verified response size */ + response = operation->response->payload; + memcpy(buf, response->buf, wLength); + } +out: + gb_operation_put(operation); return ret; } -- cgit v1.2.3-59-g8ed1b From a96493560cd16095fd00a936bfa9de8614bab842 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Jul 2015 20:30:41 +0200 Subject: greybus: usb: disable protocol driver The USB bridged-PHY protocol driver currently depends on changes to USB core that are not yet upstream. Disable for now. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/usb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 17a5d7a5a824..50173b96f712 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -190,6 +190,18 @@ static int gb_usb_connection_init(struct gb_connection *connection) hcd->has_tt = 1; + /* + * FIXME: The USB bridged-PHY protocol driver depends on changes to + * USB core which are not yet upstream. + * + * Disable for now. + */ + if (1) { + dev_warn(&connection->dev, "USB protocol disabled\n"); + retval = -EPROTONOSUPPORT; + goto err_put_hcd; + } + retval = usb_add_hcd(hcd, 0, 0); if (retval) goto err_put_hcd; -- cgit v1.2.3-59-g8ed1b From f47c1b023ee55737ab5f6b6f479cab06a5a03ba2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 11:42:53 +0530 Subject: greybus: operation: Drop alignment attribute from operation message header The buffers allocated for message header is already 64 bit aligned and we have explicit pad bytes in the header structure, to 64 bit align the operation specific data. And so there is no need to add the aligned attribute to the operation message header. Drop it. Suggested-by: Johan Hovold Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 00189e963a01..1b7cc6a8cc26 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -62,7 +62,7 @@ enum gb_operation_result { * * The wire format for all numeric fields in the header is little * endian. Any operation-specific data begins immediately after the - * header, and is 64-bit aligned. + * header. */ struct gb_operation_msg_hdr { __le16 size; /* Size in bytes of header + payload */ @@ -70,7 +70,7 @@ struct gb_operation_msg_hdr { __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ __u8 result; /* Result of request (in responses only) */ __u8 pad[2]; /* must be zero (ignore when read) */ -} __aligned(sizeof(u64)); +}; #define GB_OPERATION_MESSAGE_SIZE_MIN sizeof(struct gb_operation_msg_hdr) #define GB_OPERATION_MESSAGE_SIZE_MAX U16_MAX -- cgit v1.2.3-59-g8ed1b From e34fae58f6e004baf26ddac937ac13bd53b7c5f2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 11:42:54 +0530 Subject: greybus: operation: Move operation header to greybus_protocols.h This should be exposed to external users (like gbsim). Move it to greybus_protocols.h. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 31 ++++++++++++++++++++++++++++- drivers/staging/greybus/operation.h | 29 --------------------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 61fe9dce6ec0..9192ee72c6b8 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -69,9 +69,38 @@ #define GB_CONTROL_CPORT_ID 2 -/* Control Protocol */ +/* + * All operation messages (both requests and responses) begin with + * a header that encodes the size of the message (header included). + * This header also contains a unique identifier, that associates a + * response message with its operation. The header contains an + * operation type field, whose interpretation is dependent on what + * type of protocol is used over the connection. The high bit + * (0x80) of the operation type field is used to indicate whether + * the message is a request (clear) or a response (set). + * + * Response messages include an additional result byte, which + * communicates the result of the corresponding request. A zero + * result value means the operation completed successfully. Any + * other value indicates an error; in this case, the payload of the + * response message (if any) is ignored. The result byte must be + * zero in the header for a request message. + * + * The wire format for all numeric fields in the header is little + * endian. Any operation-specific data begins immediately after the + * header. + */ +struct gb_operation_msg_hdr { + __le16 size; /* Size in bytes of header + payload */ + __le16 operation_id; /* Operation unique id */ + __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ + __u8 result; /* Result of request (in responses only) */ + __u8 pad[2]; /* must be zero (ignore when read) */ +}; +/* Control Protocol */ + /* version request has no payload */ struct gb_protocol_version_response { __u8 major; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 1b7cc6a8cc26..16488d016943 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -43,35 +43,6 @@ enum gb_operation_result { GB_OP_MALFUNCTION = 0xff, }; -/* - * All operation messages (both requests and responses) begin with - * a header that encodes the size of the message (header included). - * This header also contains a unique identifier, that associates a - * response message with its operation. The header contains an - * operation type field, whose interpretation is dependent on what - * type of protocol is used over the connection. The high bit - * (0x80) of the operation type field is used to indicate whether - * the message is a request (clear) or a response (set). - * - * Response messages include an additional result byte, which - * communicates the result of the corresponding request. A zero - * result value means the operation completed successfully. Any - * other value indicates an error; in this case, the payload of the - * response message (if any) is ignored. The result byte must be - * zero in the header for a request message. - * - * The wire format for all numeric fields in the header is little - * endian. Any operation-specific data begins immediately after the - * header. - */ -struct gb_operation_msg_hdr { - __le16 size; /* Size in bytes of header + payload */ - __le16 operation_id; /* Operation unique id */ - __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ - __u8 result; /* Result of request (in responses only) */ - __u8 pad[2]; /* must be zero (ignore when read) */ -}; - #define GB_OPERATION_MESSAGE_SIZE_MIN sizeof(struct gb_operation_msg_hdr) #define GB_OPERATION_MESSAGE_SIZE_MAX U16_MAX -- cgit v1.2.3-59-g8ed1b From 46d26c5d57daaa39a5f396b13544cdb4ec15d284 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 1 Aug 2015 11:50:37 +0200 Subject: greybus: greybus_protocols: fix typo in comment Fix misspelled variable name in comment. Reviewed-by: Alex Elder Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9192ee72c6b8..0441de5becd6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -571,7 +571,7 @@ struct gb_spi_transfer_request { __u8 chip_select; /* of the spi device */ __u8 mode; /* of the spi device */ __le16 count; - struct gb_spi_transfer transfers[0]; /* trnasfer_count of these */ + struct gb_spi_transfer transfers[0]; /* count of these */ }; struct gb_spi_transfer_response { -- cgit v1.2.3-59-g8ed1b From 2130f09871a70ce425b8d328a641b7a1935ca831 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 1 Aug 2015 11:50:38 +0200 Subject: greybus: svc: fix message packing Add missing packed attributes to prevent implicit structure padding. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 0441de5becd6..121300a84e1c 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -599,7 +599,7 @@ struct gb_spi_transfer_response { struct gb_svc_hello_request { __le16 endo_id; __u8 interface_id; -}; +} __packed; /* hello response has no payload */ struct gb_svc_intf_device_id_request { @@ -616,7 +616,7 @@ struct gb_svc_intf_hotplug_request { __le32 ara_vend_id; __le32 ara_prod_id; } data; -}; +} __packed; /* hotplug response has no payload */ struct gb_svc_intf_hot_unplug_request { @@ -634,7 +634,7 @@ struct gb_svc_conn_create_request { __u16 cport1_id; __u8 intf2_id; __u16 cport2_id; -}; +} __packed; /* connection create response has no payload */ struct gb_svc_conn_destroy_request { @@ -642,7 +642,7 @@ struct gb_svc_conn_destroy_request { __u16 cport1_id; __u8 intf2_id; __u16 cport2_id; -}; +} __packed; /* connection destroy response has no payload */ /* UART */ -- cgit v1.2.3-59-g8ed1b From cfc5f2c6deb5493d5854036f3d0f396ea4a3e99d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 1 Aug 2015 11:50:39 +0200 Subject: greybus: uart: fix message packing Add missing packed attributes to prevent implicit structure padding. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 121300a84e1c..cdd5b54874bd 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -678,7 +678,7 @@ struct gb_uart_recv_data_request { __le16 size; __u8 flags; __u8 data[0]; -}; +} __packed; struct gb_uart_set_line_coding_request { __le32 rate; @@ -695,7 +695,7 @@ struct gb_uart_set_line_coding_request { #define GB_SERIAL_SPACE_PARITY 4 __u8 data_bits; -}; +} __packed; /* output control lines */ #define GB_UART_CTRL_DTR 0x01 -- cgit v1.2.3-59-g8ed1b From 8bbd9edcb71cee96fa7a835f3e445841b8c48fd7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 1 Aug 2015 11:50:40 +0200 Subject: greybus: sdio: fix message packing Add missing packed attributes to prevent implicit structure padding. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index cdd5b54874bd..e49376d283f0 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -845,7 +845,7 @@ struct gb_sdio_set_ios_request { #define GB_SDIO_SET_DRIVER_TYPE_A 0x01 #define GB_SDIO_SET_DRIVER_TYPE_C 0x02 #define GB_SDIO_SET_DRIVER_TYPE_D 0x03 -}; +} __packed; /* command request */ struct gb_sdio_command_request { @@ -865,7 +865,7 @@ struct gb_sdio_command_request { #define GB_SDIO_CMD_BC 0x03 __le32 cmd_arg; -}; +} __packed; struct gb_sdio_command_response { __le32 resp[4]; @@ -881,7 +881,7 @@ struct gb_sdio_transfer_request { __le16 data_blocks; __le16 data_blksz; __u8 data[0]; -}; +} __packed; struct gb_sdio_transfer_response { __le16 data_blocks; -- cgit v1.2.3-59-g8ed1b From 5b559e64578783b944691f6f9e7c19812d2fced6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 1 Aug 2015 11:50:41 +0200 Subject: greybus: greybus_protocols: use only type attributes for message packing For consistency reasons, use only type attributes for message packing. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e49376d283f0..290b85f6ff68 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -277,8 +277,8 @@ struct gb_gpio_set_value_request { struct gb_gpio_set_debounce_request { __u8 which; - __le16 usec __packed; -}; + __le16 usec; +} __packed; /* debounce response has no payload */ struct gb_gpio_irq_type_request { @@ -336,9 +336,9 @@ struct gb_pwm_deactivate_request { struct gb_pwm_config_request { __u8 which; - __le32 duty __packed; - __le32 period __packed; -}; + __le32 duty; + __le32 period; +} __packed; struct gb_pwm_polarity_request { __u8 which; -- cgit v1.2.3-59-g8ed1b From 03cb4fa92ae76cebdb519c92fd3839ac291eac0f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Jul 2015 11:44:08 +0530 Subject: greybus: svc: Move '{' to a new line in function definitions That's the style we follow for kernel code. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 784b7709b432..b89e88952a30 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -172,7 +172,8 @@ int gb_svc_connection_destroy(struct gb_svc *svc, EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, - u8 intf2_id, u8 dev2_id) { + u8 intf2_id, u8 dev2_id) +{ return route_create_operation(svc, intf1_id, dev1_id, intf2_id, dev2_id); } -- cgit v1.2.3-59-g8ed1b From bfb287a17e79f8d9bd6a99cf79634c4493ad7028 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:09 -0500 Subject: greybus: interface: declare gb_interface_destroy() Add a public declaration for gb_interface_destroy(), matching gb_interface_create(). It's not yet used outside "interface.c" but I suppose it could be, and its scope is currently public. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 558e6dbaa26b..e60a3705494e 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -51,6 +51,7 @@ struct gb_interface *gb_interface_find(struct greybus_host_device *hd, struct gb_interface *gb_interface_create(struct greybus_host_device *hd, u8 interface_id); +void gb_interface_destroy(struct gb_interface *intf); int gb_interface_init(struct gb_interface *intf, u8 device_id); void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id); void gb_interfaces_remove(struct greybus_host_device *hd); -- cgit v1.2.3-59-g8ed1b From 006335a02677ed20dbff44f398a9cbf823db0293 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:10 -0500 Subject: greybus: looback: fix two typos Fix two misspellings. And add spaces around a '%' operator. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 08a77fee385a..08f77808e99d 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -183,7 +183,7 @@ gb_loopback_ro_attr(iteration_count, u); * Type of loopback message to send based on protocol type definitions * 0 => Don't send message * 2 => Send ping message continuously (message without payload) - * 3 => Send transer message continuously (message with payload, + * 3 => Send transfer message continuously (message with payload, * payload returned in response) * 4 => Send a sink message (message with payload, no payload in response) */ @@ -410,7 +410,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) do_div(tmp, NSEC_PER_USEC); lat = tmp; - /* Log latency stastic */ + /* Log latency statistic */ gb_loopback_update_stats(&gb->latency, lat); kfifo_in(&gb->kfifo, (unsigned char *)&lat, sizeof(lat)); @@ -584,7 +584,7 @@ static ssize_t loopback_read(struct file *file, char __user *buf, size_t count, size_t fifo_len; int retval; - if (!count || count%sizeof(u32)) + if (!count || count % sizeof(u32)) return -EINVAL; mutex_lock(&gb->mutex); -- cgit v1.2.3-59-g8ed1b From 3f12c3ed21d9e28ae2dc50a6c567ee5d5c6054d1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:11 -0500 Subject: greybus: loopback: drop unneeded casts for void pointers There is no need to cast a void pointer to a particular type. Drop the casts used in this way, mainly in the attribute definition macros. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 08f77808e99d..66059a96bb17 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -84,8 +84,7 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct gb_connection *connection = to_gb_connection(dev); \ - struct gb_loopback *gb = \ - (struct gb_loopback *)connection->private; \ + struct gb_loopback *gb = connection->private; \ return sprintf(buf, "%"#type"\n", gb->field); \ } \ static DEVICE_ATTR_RO(field) @@ -96,8 +95,7 @@ static ssize_t name##_##field##_show(struct device *dev, \ char *buf) \ { \ struct gb_connection *connection = to_gb_connection(dev); \ - struct gb_loopback *gb = \ - (struct gb_loopback *)connection->private; \ + struct gb_loopback *gb = connection->private; \ return sprintf(buf, "%"#type"\n", gb->name.field); \ } \ static DEVICE_ATTR_RO(name##_##field) @@ -113,8 +111,7 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct gb_connection *connection = to_gb_connection(dev); \ - struct gb_loopback *gb = \ - (struct gb_loopback *)connection->private; \ + struct gb_loopback *gb = connection->private; \ return sprintf(buf, "%"#type"\n", gb->field); \ } \ static ssize_t field##_store(struct device *dev, \ @@ -124,8 +121,7 @@ static ssize_t field##_store(struct device *dev, \ { \ int ret; \ struct gb_connection *connection = to_gb_connection(dev); \ - struct gb_loopback *gb = \ - (struct gb_loopback *)connection->private; \ + struct gb_loopback *gb = connection->private; \ mutex_lock(&gb->mutex); \ ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ @@ -423,7 +419,7 @@ static int gb_loopback_fn(void *data) { int error = 0; int ms_wait; - struct gb_loopback *gb = (struct gb_loopback *)data; + struct gb_loopback *gb = data; while (1) { if (!gb->type) -- cgit v1.2.3-59-g8ed1b From 3320e784550551ae8ae5c22d58e6b90addafe8a4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:12 -0500 Subject: greybus: loopback: use U64_MAX for initialization Use the largest representable value when initializing the "min" field when resetting loopback statistics. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 66059a96bb17..cdefaffc2184 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -340,7 +340,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) static void gb_loopback_reset_stats(struct gb_loopback *gb) { struct gb_loopback_stats reset = { - .min = 0xffffffff, + .min = U64_MAX, }; memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); -- cgit v1.2.3-59-g8ed1b From a6e7e53548b0d54aab0a5c5449e7093a495555d8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:13 -0500 Subject: greybus: loopback: use u32 for stats update The only values supplied to gb_loopback_update_stats() are 32-bits, so change the type of the second argument to reflect that. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index cdefaffc2184..2042bed075d4 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -348,7 +348,7 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) sizeof(struct gb_loopback_stats)); } -static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u64 val) +static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) { if (stats->min > val) stats->min = val; -- cgit v1.2.3-59-g8ed1b From e051c82b2ba7fb3e237595c4f6d106e04a86baa1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:14 -0500 Subject: greybus: loopback: record 32-bit min and max The minimum and maximum values for stats values are always 32 bits. Change the type for these fields to reflect this. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 2042bed075d4..2ec6c8071754 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -23,8 +23,8 @@ #include "greybus.h" struct gb_loopback_stats { - u64 min; - u64 max; + u32 min; + u32 max; u64 avg; u64 sum; u64 count; @@ -101,8 +101,8 @@ static ssize_t name##_##field##_show(struct device *dev, \ static DEVICE_ATTR_RO(name##_##field) #define gb_loopback_stats_attrs(field) \ - gb_loopback_ro_stats_attr(field, min, llu); \ - gb_loopback_ro_stats_attr(field, max, llu); \ + gb_loopback_ro_stats_attr(field, min, u); \ + gb_loopback_ro_stats_attr(field, max, u); \ gb_loopback_ro_stats_attr(field, avg, llu); #define gb_loopback_attr(field, type) \ @@ -340,7 +340,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) static void gb_loopback_reset_stats(struct gb_loopback *gb) { struct gb_loopback_stats reset = { - .min = U64_MAX, + .min = U32_MAX, }; memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); -- cgit v1.2.3-59-g8ed1b From e13fc28f728ae6aa5e72c90a35d4fb0298205c6c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:15 -0500 Subject: greybus: loopback: use a 32-bit count The count of statistical samples recorded is currently a 64-bit value. 32 bits is sufficient, and in fact anything more than that won't work for the do_div() call it's pass to anyway. So make the count field be 32 bits. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 2ec6c8071754..580e00247a78 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -27,7 +27,7 @@ struct gb_loopback_stats { u32 max; u64 avg; u64 sum; - u64 count; + u32 count; }; struct gb_loopback { -- cgit v1.2.3-59-g8ed1b From b937aa9e2a5e69cea4a0cc59b2d4dc7a0323ef60 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:16 -0500 Subject: greybus: loopback: error is an unsigned attribute The error count is unsigned, so fix the format specifier used in its attribute definition. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 580e00247a78..283a68448232 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -171,7 +171,7 @@ gb_loopback_stats_attrs(requests_per_second); /* Quantity of data sent and received on this cport */ gb_loopback_stats_attrs(throughput); /* Number of errors encountered during loop */ -gb_loopback_ro_attr(error, d); +gb_loopback_ro_attr(error, u); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ gb_loopback_ro_attr(iteration_count, u); -- cgit v1.2.3-59-g8ed1b From 19c2a443c9efc87ae19d0ef7a5d212614a92479e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:17 -0500 Subject: greybus: loopback: all read-only attributes are unsigned The only values passed to the gb_loopback_ro_attr() macro are unsigned 32-bit values. So there's no need to pass a "type" format specifier. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 283a68448232..6afacdba619f 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -78,14 +78,14 @@ module_param(kfifo_depth, uint, 0444); define_get_version(gb_loopback, LOOPBACK); /* interface sysfs attributes */ -#define gb_loopback_ro_attr(field, type) \ +#define gb_loopback_ro_attr(field) \ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_connection *connection = to_gb_connection(dev); \ struct gb_loopback *gb = connection->private; \ - return sprintf(buf, "%"#type"\n", gb->field); \ + return sprintf(buf, "%u\n", gb->field); \ } \ static DEVICE_ATTR_RO(field) @@ -171,9 +171,9 @@ gb_loopback_stats_attrs(requests_per_second); /* Quantity of data sent and received on this cport */ gb_loopback_stats_attrs(throughput); /* Number of errors encountered during loop */ -gb_loopback_ro_attr(error, u); +gb_loopback_ro_attr(error); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ -gb_loopback_ro_attr(iteration_count, u); +gb_loopback_ro_attr(iteration_count); /* * Type of loopback message to send based on protocol type definitions -- cgit v1.2.3-59-g8ed1b From 7a135a965c62f7abdf8f3acc5ca45a03d27b272c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:18 -0500 Subject: greybus: loopback: use separate attribute macro for average Define a separate macro for displaying the average of the samples collected. This will be used so we can calculate the average only when requested, rather than every time a new value gets recorded. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 6afacdba619f..432eeabe963b 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -100,10 +100,21 @@ static ssize_t name##_##field##_show(struct device *dev, \ } \ static DEVICE_ATTR_RO(name##_##field) +#define gb_loopback_ro_avg_attr(name) \ +static ssize_t name##_avg_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct gb_connection *connection = to_gb_connection(dev); \ + struct gb_loopback *gb = connection->private; \ + return sprintf(buf, "%llu\n", gb->name.avg); \ +} \ +static DEVICE_ATTR_RO(name##_avg) + #define gb_loopback_stats_attrs(field) \ gb_loopback_ro_stats_attr(field, min, u); \ gb_loopback_ro_stats_attr(field, max, u); \ - gb_loopback_ro_stats_attr(field, avg, llu); + gb_loopback_ro_avg_attr(field); #define gb_loopback_attr(field, type) \ static ssize_t field##_show(struct device *dev, \ -- cgit v1.2.3-59-g8ed1b From ff71d395f3cd5e5efe4b9b3c94c2d41581fb9d8c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:19 -0500 Subject: greybus: loopback: compute average stats on demand only Stop recording and updating the average every time a sample is recorded. Instead, compute it from the sum and count only when it's required. Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 432eeabe963b..e76f8a70d113 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -25,7 +25,6 @@ struct gb_loopback_stats { u32 min; u32 max; - u64 avg; u64 sum; u32 count; }; @@ -107,7 +106,11 @@ static ssize_t name##_avg_show(struct device *dev, \ { \ struct gb_connection *connection = to_gb_connection(dev); \ struct gb_loopback *gb = connection->private; \ - return sprintf(buf, "%llu\n", gb->name.avg); \ + struct gb_loopback_stats *stats = &gb->name; \ + u32 count = stats->count ? stats->count : 1; \ + u64 avg = stats->sum + count / 2; /* round closest */ \ + u32 rem = do_div(avg, count); \ + return sprintf(buf, "%llu.%06u\n", avg, 1000000 * rem / count); \ } \ static DEVICE_ATTR_RO(name##_avg) @@ -367,8 +370,6 @@ static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) stats->max = val; stats->sum += val; stats->count++; - stats->avg = stats->sum; - do_div(stats->avg, stats->count); } static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency) -- cgit v1.2.3-59-g8ed1b From 1fb807cf6da0b65af4ba6ab100d38ebef17ff25e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 3 Aug 2015 12:57:20 -0500 Subject: greybus: loopback: fix connection cleanup paths The error paths in gb_loopback_connection_init() are kind of screwed up--not in proper order, and the label naming convention seems a little inconsistent. Fix this, ensuring each error cleans up the setup that's been done up to that point. Use the convention that the label indicates the first thing that needs to be cleaned up. Reorder the statements in gb_loopback_connection_exit() to match the order of cleanup in gb_loopback_connection_init(). Signed-off-by: Alex Elder Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index e76f8a70d113..564276d90da2 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -490,19 +490,19 @@ static int gb_loopback_connection_init(struct gb_connection *connection) minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); if (minor < 0) { retval = minor; - goto out_free; + goto out_sysfs; } /* Check the version */ retval = get_version(gb); if (retval) - goto out_get_ver; + goto out_minor; /* Calculate maximum payload */ gb->size_max = gb_operation_get_payload_size_max(connection); if (gb->size_max <= sizeof(struct gb_loopback_transfer_request)) { retval = -EINVAL; - goto out_get_ver; + goto out_minor; } gb->size_max -= sizeof(struct gb_loopback_transfer_request); @@ -510,7 +510,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32), GFP_KERNEL)) { retval = -ENOMEM; - goto out_get_ver; + goto out_minor; } /* Create device entry */ @@ -518,13 +518,13 @@ static int gb_loopback_connection_init(struct gb_connection *connection) cdev_init(&gb->cdev, &loopback_fops); retval = cdev_add(&gb->cdev, gb->dev, 1); if (retval) - goto out_cdev; + goto out_kfifo; gb->device = device_create(loopback_class, &connection->dev, gb->dev, gb, "gb!loopback%d", minor); if (IS_ERR(gb->device)) { retval = PTR_ERR(gb->device); - goto out_device; + goto out_cdev; } /* Fork worker thread */ @@ -533,21 +533,25 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { retval = PTR_ERR(gb->task); - goto out_kfifo; + goto out_device; } return 0; out_device: - cdev_del(&gb->cdev); + device_del(gb->device); out_cdev: - ida_simple_remove(&minors, minor); + cdev_del(&gb->cdev); out_kfifo: kfifo_free(&gb->kfifo); -out_get_ver: +out_minor: + ida_simple_remove(&minors, minor); +out_sysfs: sysfs_remove_groups(&connection->dev.kobj, loopback_groups); out_free: + connection->private = NULL; kfree(gb); + return retval; } @@ -555,13 +559,14 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) { struct gb_loopback *gb = connection->private; + connection->private = NULL; if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); - cdev_del(&gb->cdev); - ida_simple_remove(&minors, MINOR(gb->dev)); device_del(gb->device); + cdev_del(&gb->cdev); kfifo_free(&gb->kfifo); + ida_simple_remove(&minors, MINOR(gb->dev)); sysfs_remove_groups(&connection->dev.kobj, loopback_groups); kfree(gb); } -- cgit v1.2.3-59-g8ed1b From 47a96858b526e14882438e455ec078fc7e51155b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 4 Aug 2015 13:44:10 -0500 Subject: greybus: kernel_ver.h: define U32_MAX and U64_MAX These were not defined, and I just posted patches that use them. Signed-off-by: Alex Elder Tested-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index c2e92df9f417..d0e05e656475 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -57,6 +57,14 @@ #define U16_MAX ((u16)(~0U)) #endif /* !U16_MAX */ +#ifndef U32_MAX +#define U32_MAX ((u32)(~0U)) +#endif /* !U32_MAX */ + +#ifndef U64_MAX +#define U64_MAX ((u64)(~0U)) +#endif /* !U64_MAX */ + /* * The GPIO api sucks rocks in places, like removal, so work around their * explicit requirements of catching the return value for kernels older than -- cgit v1.2.3-59-g8ed1b From 7fea641c94d645bf30712cb6e6c498597c7e5634 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 6 Aug 2015 12:44:52 +0530 Subject: greybus: raw: Print expected/actual payload size on mismatch Print (expected-payload-size actual-payload-size), when the size doesn't match for requests received by the module. This gives more details required for debugging the issue. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/raw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index 3be96db2588b..a17a9868a08e 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -121,7 +121,8 @@ static int gb_raw_receive(u8 type, struct gb_operation *op) /* Verify size of payload */ if (op->request->payload_size < sizeof(*receive)) { - dev_err(raw->device, "raw receive request too small\n"); + dev_err(raw->device, "raw receive request too small (%zu < %zu)\n", + op->request->payload_size, sizeof(*receive)); return -EINVAL; } receive = op->request->payload; -- cgit v1.2.3-59-g8ed1b From 782c3b732889b5fbce796a4b548d47a43e4d8c42 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 6 Aug 2015 12:44:51 +0530 Subject: greybus: gpio: Print expected/actual payload size on mismatch Print (expected-payload-size actual-payload-size), when the size doesn't match for requests received by the module. This gives more details required for debugging the issue. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 6539530a178c..caee9d19d723 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -366,7 +366,8 @@ static int gb_gpio_request_recv(u8 type, struct gb_operation *op) request = op->request; if (request->payload_size < sizeof(*event)) { - dev_err(ggc->chip.dev, "short event received\n"); + dev_err(ggc->chip.dev, "short event received (%zu < %zu)\n", + request->payload_size, sizeof(*event)); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 6d05ad3c4226024ad78dfbd2ab683b760ab56389 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 6 Aug 2015 12:44:54 +0530 Subject: greybus: svc: Print expected/actual payload size on mismatch Print (expected-payload-size actual-payload-size), when the size doesn't match for requests received by the module. This gives more details required for debugging the issue. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b89e88952a30..4d59cbb0b337 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -269,7 +269,8 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) int ret; if (request->payload_size < sizeof(*hotplug)) { - dev_err(dev, "%s: short hotplug request received\n", __func__); + dev_err(dev, "%s: short hotplug request received (%zu < %zu)\n", + __func__, request->payload_size, sizeof(*hotplug)); return -EINVAL; } @@ -372,7 +373,8 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) if (request->payload_size < sizeof(*hot_unplug)) { dev_err(&op->connection->dev, - "short hot unplug request received\n"); + "short hot unplug request received (%zu < %zu)\n", + request->payload_size, sizeof(*hot_unplug)); return -EINVAL; } @@ -400,7 +402,8 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) if (request->payload_size < sizeof(*reset)) { dev_err(&op->connection->dev, - "short reset request received\n"); + "short reset request received (%zu < %zu)\n", + request->payload_size, sizeof(*reset)); return -EINVAL; } reset = request->payload; -- cgit v1.2.3-59-g8ed1b From 067906f6906922ad784452218b09bfb2b9519643 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 6 Aug 2015 12:44:55 +0530 Subject: greybus: svc: Handle hotplug request from a workqueue Bringing up a module can be time consuming, as that may require lots of initialization on the module side. Over that, we may also need to download the firmware first and flash that on the module. In order to make other hotplug events to not wait for all this to finish, handle most of module hotplug stuff outside of the hotplug callback, with help of a workqueue. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 77 ++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 4d59cbb0b337..4bf55e0e4f34 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -8,6 +8,7 @@ */ #include "greybus.h" +#include #define CPORT_FLAGS_E2EFC (1) #define CPORT_FLAGS_CSD_N (2) @@ -19,6 +20,12 @@ struct gb_svc { u8 version_minor; }; +struct svc_hotplug { + struct work_struct work; + struct gb_connection *connection; + struct gb_svc_intf_hotplug_request data; +}; + static struct ida greybus_svc_device_id_map; /* @@ -253,13 +260,19 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } -static int gb_svc_intf_hotplug_recv(struct gb_operation *op) +/* + * 'struct svc_hotplug' should be freed by svc_process_hotplug() before it + * returns, irrespective of success or Failure in bringing up the module. + */ +static void svc_process_hotplug(struct work_struct *work) { - struct gb_message *request = op->request; - struct gb_svc_intf_hotplug_request *hotplug = request->payload; - struct gb_svc *svc = op->connection->private; - struct greybus_host_device *hd = op->connection->bundle->intf->hd; - struct device *dev = &op->connection->dev; + struct svc_hotplug *svc_hotplug = container_of(work, struct svc_hotplug, + work); + struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data; + struct gb_connection *connection = svc_hotplug->connection; + struct gb_svc *svc = connection->private; + struct greybus_host_device *hd = connection->bundle->intf->hd; + struct device *dev = &connection->dev; struct gb_interface *intf; u8 intf_id, device_id; u32 unipro_mfg_id; @@ -268,19 +281,8 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) u32 ara_prod_id; int ret; - if (request->payload_size < sizeof(*hotplug)) { - dev_err(dev, "%s: short hotplug request received (%zu < %zu)\n", - __func__, request->payload_size, sizeof(*hotplug)); - return -EINVAL; - } - /* * Grab the information we need. - * - * XXX I'd really like to acknowledge receipt, and then - * XXX continue processing the request. There's no need - * XXX for the SVC to wait. In fact, it might be best to - * XXX have the SVC get acknowledgement before we proceed. */ intf_id = hotplug->intf_id; unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); @@ -293,7 +295,7 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) if (!intf) { dev_err(dev, "%s: Failed to create interface with id %hhu\n", __func__, intf_id); - return -EINVAL; + goto free_svc_hotplug; } /* @@ -346,7 +348,7 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) goto svc_id_free; } - return 0; + goto free_svc_hotplug; svc_id_free: /* @@ -357,8 +359,43 @@ ida_put: ida_simple_remove(&greybus_svc_device_id_map, device_id); destroy_interface: gb_interface_remove(hd, intf_id); +free_svc_hotplug: + kfree(svc_hotplug); +} - return ret; +/* + * Bringing up a module can be time consuming, as that may require lots of + * initialization on the module side. Over that, we may also need to download + * the firmware first and flash that on the module. + * + * In order to make other hotplug events to not wait for all this to finish, + * handle most of module hotplug stuff outside of the hotplug callback, with + * help of a workqueue. + */ +static int gb_svc_intf_hotplug_recv(struct gb_operation *op) +{ + struct gb_message *request = op->request; + struct svc_hotplug *svc_hotplug; + + if (request->payload_size < sizeof(svc_hotplug->data)) { + dev_err(&op->connection->dev, + "%s: short hotplug request received (%zu < %zu)\n", + __func__, request->payload_size, + sizeof(svc_hotplug->data)); + return -EINVAL; + } + + svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_ATOMIC); + if (!svc_hotplug) + return -ENOMEM; + + svc_hotplug->connection = op->connection; + memcpy(&svc_hotplug->data, op->request->payload, sizeof(svc_hotplug->data)); + + INIT_WORK(&svc_hotplug->work, svc_process_hotplug); + queue_work(system_unbound_wq, &svc_hotplug->work); + + return 0; } static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) -- cgit v1.2.3-59-g8ed1b From f1f6fa44ae49e91c0a5fe9e0c6e5614e6aa22283 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 08:09:32 +0530 Subject: greybus: sdio: error out only for smaller payloads received != was used in place of <, while comparing expected and actual payload size. The module may be running a higher version of the protocol and might have some extra fields (towards the end) in the structure, and the AP needs to ignore them. This also updates the print (expected-payload-size < actual-payload-size), when the size doesn't match for requests received by the module. This gives more details required for debugging. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index a709bd64f824..345cffff7db0 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -192,8 +192,9 @@ static int gb_sdio_event_recv(u8 type, struct gb_operation *op) request = op->request; - if (request->payload_size != sizeof(*payload)) { - dev_err(mmc_dev(host->mmc), "wrong event size received\n"); + if (request->payload_size < sizeof(*payload)) { + dev_err(mmc_dev(host->mmc), "wrong event size received (%zu < %zu)\n", + request->payload_size, sizeof(*payload)); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From d79ae495e2ddb2b6f39376c4eed080dbc1b074d5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:32 +0530 Subject: greybus: hid: spell fix (s/infomation/information) Minor spell fix. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index a367fd5fad70..d423f8eb145e 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -84,7 +84,7 @@ struct gb_hid { static DEFINE_MUTEX(gb_hid_open_mutex); -/* Routines to get controller's infomation over greybus */ +/* Routines to get controller's information over greybus */ /* Define get_version() routine */ define_get_version(gb_hid, HID); -- cgit v1.2.3-59-g8ed1b From 8cbd20aa0de89eb344e05d51ae3bedfabeae360a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:33 +0530 Subject: greybus: battery: Use (already defined) major/minor macros We already have macros for these, use them instead of writing fixed values. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index e66623966dc9..43799a357add 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -372,8 +372,8 @@ static void gb_battery_connection_exit(struct gb_connection *connection) static struct gb_protocol battery_protocol = { .name = "battery", .id = GREYBUS_PROTOCOL_BATTERY, - .major = 0, - .minor = 1, + .major = GB_BATTERY_VERSION_MAJOR, + .minor = GB_BATTERY_VERSION_MINOR, .connection_init = gb_battery_connection_init, .connection_exit = gb_battery_connection_exit, .request_recv = NULL, /* no incoming requests */ -- cgit v1.2.3-59-g8ed1b From 8590ed30be0d3164073d90d1bcf728dd776487c4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:34 +0530 Subject: greybus: gpio: Use (already defined) major/minor macros We already have macros for these, use them instead of writing fixed values. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index caee9d19d723..a5db014eaaf2 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -719,8 +719,8 @@ static void gb_gpio_connection_exit(struct gb_connection *connection) static struct gb_protocol gpio_protocol = { .name = "gpio", .id = GREYBUS_PROTOCOL_GPIO, - .major = 0, - .minor = 1, + .major = GB_GPIO_VERSION_MAJOR, + .minor = GB_GPIO_VERSION_MINOR, .connection_init = gb_gpio_connection_init, .connection_exit = gb_gpio_connection_exit, .request_recv = gb_gpio_request_recv, -- cgit v1.2.3-59-g8ed1b From 34ec36457f360fd2930fbf6e4cdfd6f7bc4d98c1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:35 +0530 Subject: greybus: hid: Use (already defined) major/minor macros We already have macros for these, use them instead of writing fixed values. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index d423f8eb145e..0baed865b760 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -523,8 +523,8 @@ static void gb_hid_connection_exit(struct gb_connection *connection) static struct gb_protocol hid_protocol = { .name = "hid", .id = GREYBUS_PROTOCOL_HID, - .major = 0, - .minor = 1, + .major = GB_HID_VERSION_MAJOR, + .minor = GB_HID_VERSION_MINOR, .connection_init = gb_hid_connection_init, .connection_exit = gb_hid_connection_exit, .request_recv = gb_hid_irq_handler, -- cgit v1.2.3-59-g8ed1b From bbd80cc166dc841f6bab7fe721da157c6371e84b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:36 +0530 Subject: greybus: i2c: Use (already defined) major/minor macros We already have macros for these, use them instead of writing fixed values. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 9514e69d0d4b..edb675384a71 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -349,8 +349,8 @@ static void gb_i2c_connection_exit(struct gb_connection *connection) static struct gb_protocol i2c_protocol = { .name = "i2c", .id = GREYBUS_PROTOCOL_I2C, - .major = 0, - .minor = 1, + .major = GB_I2C_VERSION_MAJOR, + .minor = GB_I2C_VERSION_MINOR, .connection_init = gb_i2c_connection_init, .connection_exit = gb_i2c_connection_exit, .request_recv = NULL, /* no incoming requests */ -- cgit v1.2.3-59-g8ed1b From 3fb97e43ba4ab094a72d9b9fe165e476e756c4ed Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:37 +0530 Subject: greybus: pwm: Use (already defined) major/minor macros We already have macros for these, use them instead of writing fixed values. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/pwm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index be7131a41a97..c7f8c6338801 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -239,8 +239,8 @@ static void gb_pwm_connection_exit(struct gb_connection *connection) static struct gb_protocol pwm_protocol = { .name = "pwm", .id = GREYBUS_PROTOCOL_PWM, - .major = 0, - .minor = 1, + .major = GB_PWM_VERSION_MAJOR, + .minor = GB_PWM_VERSION_MINOR, .connection_init = gb_pwm_connection_init, .connection_exit = gb_pwm_connection_exit, .request_recv = NULL, /* no incoming requests */ -- cgit v1.2.3-59-g8ed1b From 7549219d4ea0a9d8b78c3819dff8dcdb9db14eb1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:38 +0530 Subject: greybus: spi: Use (already defined) major/minor macros We already have macros for these, use them instead of writing fixed values. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 306fb074c183..77d6bf0b7f13 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -340,8 +340,8 @@ static void gb_spi_connection_exit(struct gb_connection *connection) static struct gb_protocol spi_protocol = { .name = "spi", .id = GREYBUS_PROTOCOL_SPI, - .major = 0, - .minor = 1, + .major = GB_SPI_VERSION_MAJOR, + .minor = GB_SPI_VERSION_MINOR, .connection_init = gb_spi_connection_init, .connection_exit = gb_spi_connection_exit, .request_recv = NULL, -- cgit v1.2.3-59-g8ed1b From 9475fb5c5ad1d61a37c2bd47fb94dcf55b59aaf2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:39 +0530 Subject: greybus: uart: Use (already defined) major/minor macros We already have macros for these, use them instead of writing fixed values. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 0166c4cdb451..7a51c7c792c9 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -767,8 +767,8 @@ static void gb_tty_exit(void) static struct gb_protocol uart_protocol = { .name = "uart", .id = GREYBUS_PROTOCOL_UART, - .major = 0, - .minor = 1, + .major = GB_UART_VERSION_MAJOR, + .minor = GB_UART_VERSION_MINOR, .connection_init = gb_uart_connection_init, .connection_exit = gb_uart_connection_exit, .request_recv = gb_uart_request_recv, -- cgit v1.2.3-59-g8ed1b From f514b5c31435909960fe32e309d7417c52629cc1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:40 +0530 Subject: greybus: usb: Use (already defined) major/minor macros We already have macros for these, use them instead of writing fixed values. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 50173b96f712..80c42b20adda 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -226,8 +226,8 @@ static void gb_usb_connection_exit(struct gb_connection *connection) static struct gb_protocol usb_protocol = { .name = "usb", .id = GREYBUS_PROTOCOL_USB, - .major = 0, - .minor = 1, + .major = GB_USB_VERSION_MAJOR, + .minor = GB_USB_VERSION_MINOR, .connection_init = gb_usb_connection_init, .connection_exit = gb_usb_connection_exit, .request_recv = NULL, /* FIXME we have requests!!! */ -- cgit v1.2.3-59-g8ed1b From f0a1698f18722fccb920448285b8fc87f793870a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 8 Aug 2015 10:25:41 +0530 Subject: greybus: vibrator: Use (already defined) major/minor macros We already have macros for these, use them instead of writing fixed values. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/vibrator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 62b3552006fc..df9c4b14c707 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -167,8 +167,8 @@ static void gb_vibrator_connection_exit(struct gb_connection *connection) static struct gb_protocol vibrator_protocol = { .name = "vibrator", .id = GREYBUS_PROTOCOL_VIBRATOR, - .major = 0, - .minor = 1, + .major = GB_VIBRATOR_VERSION_MAJOR, + .minor = GB_VIBRATOR_VERSION_MINOR, .connection_init = gb_vibrator_connection_init, .connection_exit = gb_vibrator_connection_exit, .request_recv = NULL, /* no incoming requests */ -- cgit v1.2.3-59-g8ed1b From 33f06a75eecaa34b43c83f57280c6ef1ffedd101 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 7 Aug 2015 18:06:43 +0530 Subject: greybus: svc: Remove FIXME for firmware download Its handled by the firmware driver now, drop the FIXME comment from svc code. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5f2b2b43b939..7ac45a9d95c4 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -256,7 +256,6 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id); ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id); - // FIXME May require firmware download intf = gb_interface_create(hd, intf_id); if (!intf) { dev_err(dev, "%s: Failed to create interface with id %hhu\n", -- cgit v1.2.3-59-g8ed1b From 10c7ae04e36f05a82dd23eb314092c77df2c2049 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:29:18 +0530 Subject: greybus: audio: Add '<' to the print message for short messages This increases readability a bit more, as it tells which value is actual size and which one is expected size. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 9f5f95913e8f..0e9bec5089b2 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -357,7 +357,7 @@ static int gb_i2s_mgmt_report_event_recv(u8 type, struct gb_operation *op) } if (op->request->payload_size < sizeof(*req)) { - dev_err(&connection->dev, "Short request received: %zu, %zu\n", + dev_err(&connection->dev, "Short request received (%zu < %zu)\n", op->request->payload_size, sizeof(*req)); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 0c32d2a5b2a6f284efd196d568e1df3db5999c5d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:29:19 +0530 Subject: greybus: svc: error out only for smaller payloads received != was used in place of <, while comparing expected and actual payload size. The module may be running a higher version of the protocol and might have some extra fields (towards the end) in the structure, and the AP needs to ignore them. This also updates the print (expected-payload-size < actual-payload-size), when the size doesn't match for requests received by the module. This gives more details required for debugging. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 7ac45a9d95c4..17ffb1353fb4 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -193,8 +193,8 @@ static int gb_svc_hello(struct gb_operation *op) * SVC sends information about the endo and interface-id on the hello * request, use that to create an endo. */ - if (op->request->payload_size != sizeof(*hello_request)) { - dev_err(dev, "%s: Illegal size of hello request (%zu %zu)\n", + if (op->request->payload_size < sizeof(*hello_request)) { + dev_err(dev, "%s: Illegal size of hello request (%zu < %zu)\n", __func__, op->request->payload_size, sizeof(*hello_request)); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 186906590280c907ee497ed5e48bb94926d7b960 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:35:56 +0530 Subject: greybus: connection: disconnect connection on failures during initialization Its possible for connection_init() to fail, in such cases the disconnected event must be sent to the module. It is missing currently, fix it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 44 ++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3b553e682359..7a5840bb8473 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -330,6 +330,27 @@ void gb_connection_destroy(struct gb_connection *connection) device_unregister(&connection->dev); } +static void gb_connection_disconnected(struct gb_connection *connection) +{ + struct gb_control *control; + int cport_id = connection->intf_cport_id; + int ret; + + /* + * Inform Interface about In-active CPorts. We don't need to do this + * operation for control cport. + */ + if (cport_id == GB_CONTROL_CPORT_ID) + return; + + control = connection->bundle->intf->control; + + ret = gb_control_disconnected_operation(control, cport_id); + if (ret) + dev_warn(&connection->dev, + "Failed to disconnect CPort-%d (%d)\n", cport_id, ret); +} + int gb_connection_init(struct gb_connection *connection) { int cport_id = connection->intf_cport_id; @@ -366,15 +387,18 @@ int gb_connection_init(struct gb_connection *connection) spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_ERROR; spin_unlock_irq(&connection->lock); + goto disconnect; } + return 0; + +disconnect: + gb_connection_disconnected(connection); return ret; } void gb_connection_exit(struct gb_connection *connection) { - int cport_id = connection->intf_cport_id; - if (!connection->protocol) { dev_warn(&connection->dev, "exit without protocol.\n"); return; @@ -391,21 +415,7 @@ void gb_connection_exit(struct gb_connection *connection) gb_connection_cancel_operations(connection, -ESHUTDOWN); connection->protocol->connection_exit(connection); - - /* - * Inform Interface about In-active CPorts. We don't need to do this - * operation for control cport. - */ - if (cport_id != GB_CONTROL_CPORT_ID) { - struct gb_control *control = connection->bundle->intf->control; - int ret; - - ret = gb_control_disconnected_operation(control, cport_id); - if (ret) - dev_warn(&connection->dev, - "Failed to disconnect CPort-%d (%d)\n", - cport_id, ret); - } + gb_connection_disconnected(connection); } void gb_hd_connections_exit(struct greybus_host_device *hd) -- cgit v1.2.3-59-g8ed1b From 2b11a45d29f52c94fdea1d8f1c7baa25cb7368bb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:35:57 +0530 Subject: greybus: define greybus wide protocol request numbers Some request numbers (like invalid and get_version) are same across all protocols. Create common macros for them. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 290b85f6ff68..b95d24bd8e62 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -99,6 +99,10 @@ struct gb_operation_msg_hdr { }; +/* Generic request numbers supported by all modules */ +#define GB_REQUEST_TYPE_INVALID 0x00 +#define GB_REQUEST_TYPE_PROTOCOL_VERSION 0x01 + /* Control Protocol */ /* version request has no payload */ -- cgit v1.2.3-59-g8ed1b From d653f4b19163aa9f3a8046bd6e895b8615856d11 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:35:58 +0530 Subject: greybus: connection: Save major/minor supported by module Save major/minor number supported by the module inside connection structure, as this can be used later. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.h | 2 ++ drivers/staging/greybus/protocol.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index f02b9d9fb084..0dbbc202e953 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -34,6 +34,8 @@ struct gb_connection { u8 protocol_id; u8 major; u8 minor; + u8 module_major; + u8 module_minor; spinlock_t lock; enum gb_connection_state state; diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 06b4841173ce..ba80f552fa31 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -182,6 +182,9 @@ int gb_protocol_get_version(struct gb_connection *connection, int type, return -ENOTSUPP; } + connection->module_major = response->major; + connection->module_minor = response->minor; + dev_dbg(&connection->dev, "version_major = %u version_minor = %u\n", response->major, response->minor); -- cgit v1.2.3-59-g8ed1b From 7ba26a8ced4a391133aa899a61647154d8d4d24c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:35:59 +0530 Subject: greybus: connection: request protocol version before initializing connection This can (should) be done from a common place as its required for most of the protocols. Do it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7a5840bb8473..b1f1df81be50 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -382,17 +382,34 @@ int gb_connection_init(struct gb_connection *connection) connection->state = GB_CONNECTION_STATE_ENABLED; spin_unlock_irq(&connection->lock); - ret = connection->protocol->connection_init(connection); - if (ret) { - spin_lock_irq(&connection->lock); - connection->state = GB_CONNECTION_STATE_ERROR; - spin_unlock_irq(&connection->lock); - goto disconnect; + /* + * Request protocol version supported by the module. We don't need to do + * this for SVC as that is initiated by the SVC. + */ + if (connection->hd_cport_id != GB_SVC_CPORT_ID) { + struct gb_protocol_version_response response; + + ret = gb_protocol_get_version(connection, + GB_REQUEST_TYPE_PROTOCOL_VERSION, + NULL, 0, &response, + connection->protocol->major); + if (ret) { + dev_err(&connection->dev, + "Failed to get version CPort-%d (%d)\n", + cport_id, ret); + goto disconnect; + } } - return 0; + ret = connection->protocol->connection_init(connection); + if (!ret) + return 0; disconnect: + spin_lock_irq(&connection->lock); + connection->state = GB_CONNECTION_STATE_ERROR; + spin_unlock_irq(&connection->lock); + gb_connection_disconnected(connection); return ret; } -- cgit v1.2.3-59-g8ed1b From 9937a60a06ea21acc6680615258109a417ed25a8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:00 +0530 Subject: greybus: audio: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-gb-cmds.c | 28 ---------------------------- drivers/staging/greybus/audio.c | 14 ++------------ drivers/staging/greybus/audio.h | 2 -- 3 files changed, 2 insertions(+), 42 deletions(-) diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c index 10de34d6fa93..112aed994fd2 100644 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ b/drivers/staging/greybus/audio-gb-cmds.c @@ -12,37 +12,9 @@ #include "greybus.h" #include "audio.h" -#define GB_I2S_MGMT_VERSION_MAJOR 0x00 -#define GB_I2S_MGMT_VERSION_MINOR 0x01 - -#define GB_I2S_DATA_VERSION_MAJOR 0x00 -#define GB_I2S_DATA_VERSION_MINOR 0x01 - /*********************************** * GB I2S helper functions ***********************************/ -int gb_i2s_mgmt_get_version(struct gb_connection *connection) -{ - struct gb_protocol_version_response response; - - memset(&response, 0, sizeof(response)); - return gb_protocol_get_version(connection, - GB_I2S_MGMT_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, - GB_I2S_MGMT_VERSION_MAJOR); -} - -int gb_i2s_data_get_version(struct gb_connection *connection) -{ - struct gb_protocol_version_response response; - - memset(&response, 0, sizeof(response)); - return gb_protocol_get_version(connection, - GB_I2S_DATA_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, - GB_I2S_DATA_VERSION_MAJOR); -} - int gb_i2s_mgmt_activate_cport(struct gb_connection *connection, uint16_t cport) { diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 0e9bec5089b2..6754c907cd6e 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -225,12 +225,6 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) goto out_card; } - ret = gb_i2s_data_get_version(connection); - if (ret) { - pr_err("i2s data get_version() failed: %d\n", ret); - goto out_get_ver; - } - #if USE_RT5645 rt5647_info.addr = RT5647_I2C_ADDR; strlcpy(rt5647_info.type, "rt5647", I2C_NAME_SIZE); @@ -251,8 +245,10 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) return 0; +#if USE_RT5645 out_get_ver: platform_device_unregister(&snd_dev->card); +#endif out_card: platform_device_unregister(&snd_dev->cpu_dai); out_dai: @@ -296,12 +292,6 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection) connection->private = snd_dev; spin_unlock_irqrestore(&snd_dev->lock, flags); - ret = gb_i2s_mgmt_get_version(connection); - if (ret) { - pr_err("i2s mgmt get_version() failed: %d\n", ret); - goto err_free_snd_dev; - } - ret = gb_i2s_mgmt_get_cfgs(snd_dev, connection); if (ret) { pr_err("can't get i2s configurations: %d\n", ret); diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index da95c1b3cb1e..e4975930f29a 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -75,8 +75,6 @@ struct gb_snd { /* * GB I2S cmd functions */ -int gb_i2s_mgmt_get_version(struct gb_connection *connection); -int gb_i2s_data_get_version(struct gb_connection *connection); int gb_i2s_mgmt_activate_cport(struct gb_connection *connection, uint16_t cport); int gb_i2s_mgmt_deactivate_cport(struct gb_connection *connection, -- cgit v1.2.3-59-g8ed1b From f8cbc30de009fdf11aff0c5d9d5dfb3f8ceb4990 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:01 +0530 Subject: greybus: battery: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index 43799a357add..a56eb81b2386 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -32,8 +32,6 @@ struct gb_battery { // updates from the SVC "on the fly" so we don't have to always go ask // the battery for some information. Hopefully... struct gb_connection *connection; - u8 version_major; - u8 version_minor; }; @@ -42,8 +40,6 @@ struct gb_battery { #define GB_BATTERY_VERSION_MINOR 0x01 /* Greybus battery request types */ -#define GB_BATTERY_TYPE_INVALID 0x00 -#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01 #define GB_BATTERY_TYPE_TECHNOLOGY 0x02 #define GB_BATTERY_TYPE_STATUS 0x03 #define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 @@ -94,9 +90,6 @@ struct gb_battery_voltage_response { __le32 voltage; }; -/* Define get_version() routine */ -define_get_version(gb_battery, BATTERY); - static int get_tech(struct gb_battery *gb) { struct gb_battery_technology_response tech_response; @@ -345,12 +338,7 @@ static int gb_battery_connection_init(struct gb_connection *connection) gb->connection = connection; connection->private = gb; - /* Check the version */ - retval = get_version(gb); - if (retval) - goto out; retval = init_and_register(connection, gb); -out: if (retval) kfree(gb); -- cgit v1.2.3-59-g8ed1b From 507d75c4f4502cae84b50dcb0c246d67913e9ab5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:02 +0530 Subject: greybus: control: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 10 +--------- drivers/staging/greybus/control.h | 2 -- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index a69a703a1848..605e6134d564 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -12,9 +12,6 @@ #include #include "greybus.h" -/* Define get_version() routine */ -define_get_version(gb_control, CONTROL); - /* Get Manifest's size from the interface */ int gb_control_get_manifest_size_operation(struct gb_interface *intf) { @@ -100,7 +97,6 @@ static int gb_control_request_recv(u8 type, struct gb_operation *op) static int gb_control_connection_init(struct gb_connection *connection) { struct gb_control *control; - int ret; control = kzalloc(sizeof(*control), GFP_KERNEL); if (!control) @@ -109,14 +105,10 @@ static int gb_control_connection_init(struct gb_connection *connection) control->connection = connection; connection->private = control; - ret = get_version(control); - if (ret) - kfree(control); - /* Set interface's control connection */ connection->bundle->intf->control = control; - return ret; + return 0; } static void gb_control_connection_exit(struct gb_connection *connection) diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 6e41a2b4c70d..3248d965e593 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -12,8 +12,6 @@ struct gb_control { struct gb_connection *connection; - u8 version_major; - u8 version_minor; }; int gb_control_connected_operation(struct gb_control *control, u16 cport_id); -- cgit v1.2.3-59-g8ed1b From 03490fdbe745a6af23527df32e6ceab4b5749a8f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:03 +0530 Subject: greybus: gpio: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index a5db014eaaf2..6a04a1be573a 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -33,8 +33,6 @@ struct gb_gpio_line { struct gb_gpio_controller { struct gb_connection *connection; - u8 version_major; - u8 version_minor; u8 line_max; /* max line number */ struct gb_gpio_line *lines; @@ -51,9 +49,6 @@ struct gb_gpio_controller { container_of(chip, struct gb_gpio_controller, chip) #define irq_data_to_gpio_chip(d) (d->domain->host_data) -/* Define get_version() routine */ -define_get_version(gb_gpio_controller, GPIO); - static int gb_gpio_line_count_operation(struct gb_gpio_controller *ggc) { struct gb_gpio_line_count_response response; @@ -476,11 +471,6 @@ static int gb_gpio_controller_setup(struct gb_gpio_controller *ggc) { int ret; - /* First thing we need to do is check the version */ - ret = get_version(ggc); - if (ret) - return ret; - /* Now find out how many lines there are */ ret = gb_gpio_line_count_operation(ggc); if (ret) -- cgit v1.2.3-59-g8ed1b From 2dad338c9c28f464c9717e9d0391997b649e3acd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:04 +0530 Subject: greybus: hid: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 0baed865b760..887dbac8c117 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -68,8 +68,6 @@ struct gb_hid_input_report_request { /* Greybus HID device's structure */ struct gb_hid { struct gb_connection *connection; - u8 version_major; - u8 version_minor; struct hid_device *hid; struct gb_hid_desc_response hdesc; @@ -86,9 +84,6 @@ static DEFINE_MUTEX(gb_hid_open_mutex); /* Routines to get controller's information over greybus */ -/* Define get_version() routine */ -define_get_version(gb_hid, HID); - /* Operations performed on greybus */ static int gb_hid_get_desc(struct gb_hid *ghid) { @@ -445,10 +440,6 @@ static int gb_hid_init(struct gb_hid *ghid) struct hid_device *hid = ghid->hid; int ret; - ret = get_version(ghid); - if (ret) - return ret; - ret = gb_hid_get_desc(ghid); if (ret) return ret; -- cgit v1.2.3-59-g8ed1b From d8886f4a06593fd1849a5076e08488c0ebaf74a3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:05 +0530 Subject: greybus: i2c: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index edb675384a71..75b92d683036 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -16,8 +16,6 @@ struct gb_i2c_device { struct gb_connection *connection; - u8 version_major; - u8 version_minor; u32 functionality; u16 timeout_msec; @@ -26,9 +24,6 @@ struct gb_i2c_device { struct i2c_adapter adapter; }; -/* Define get_version() routine */ -define_get_version(gb_i2c_device, I2C); - /* * Map Greybus i2c functionality bits into Linux ones */ @@ -277,11 +272,6 @@ static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) { int ret; - /* First thing we need to do is check the version */ - ret = get_version(gb_i2c_dev); - if (ret) - return ret; - /* Assume the functionality never changes, just get it once */ ret = gb_i2c_functionality_operation(gb_i2c_dev); if (ret) -- cgit v1.2.3-59-g8ed1b From 47d3cfbbadfb761aaf3967df314f860f8a324f8d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:06 +0530 Subject: greybus: loopback: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 564276d90da2..88c329afd3ea 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -31,8 +31,6 @@ struct gb_loopback_stats { struct gb_loopback { struct gb_connection *connection; - u8 version_major; - u8 version_minor; struct kfifo kfifo; struct mutex mutex; @@ -73,9 +71,6 @@ module_param(kfifo_depth, uint, 0444); #define GB_LOOPBACK_MS_WAIT_MAX 1000 -/* Define get_version() routine */ -define_get_version(gb_loopback, LOOPBACK); - /* interface sysfs attributes */ #define gb_loopback_ro_attr(field) \ static ssize_t field##_show(struct device *dev, \ @@ -493,11 +488,6 @@ static int gb_loopback_connection_init(struct gb_connection *connection) goto out_sysfs; } - /* Check the version */ - retval = get_version(gb); - if (retval) - goto out_minor; - /* Calculate maximum payload */ gb->size_max = gb_operation_get_payload_size_max(connection); if (gb->size_max <= sizeof(struct gb_loopback_transfer_request)) { -- cgit v1.2.3-59-g8ed1b From 2e93d02c18ce39430a0cf0c591f067067bad2181 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:07 +0530 Subject: greybus: pwm: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/pwm.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index c7f8c6338801..5f335895d230 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -16,8 +16,6 @@ struct gb_pwm_chip { struct gb_connection *connection; - u8 version_major; - u8 version_minor; u8 pwm_max; /* max pwm number */ struct pwm_chip chip; @@ -27,9 +25,6 @@ struct gb_pwm_chip { container_of(chip, struct gb_pwm_chip, chip) -/* Define get_version() routine */ -define_get_version(gb_pwm_chip, PWM); - static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) { struct gb_pwm_count_response response; @@ -194,11 +189,6 @@ static int gb_pwm_connection_init(struct gb_connection *connection) pwmc->connection = connection; connection->private = pwmc; - /* Check for compatible protocol version */ - ret = get_version(pwmc); - if (ret) - goto out_err; - /* Query number of pwms present */ ret = gb_pwm_count_operation(pwmc); if (ret) -- cgit v1.2.3-59-g8ed1b From f06eda1b1744fcde6769eee22bceb7d9fee23bf5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:08 +0530 Subject: greybus: raw: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/raw.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index a17a9868a08e..215d42165282 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -19,8 +19,6 @@ struct gb_raw { struct gb_connection *connection; - u8 version_major; - u8 version_minor; struct list_head list; int list_data; @@ -35,13 +33,8 @@ struct gb_raw { #define GB_RAW_VERSION_MINOR 0x01 /* Greybus raw request types */ -#define GB_RAW_TYPE_INVALID 0x00 -#define GB_RAW_TYPE_PROTOCOL_VERSION 0x01 #define GB_RAW_TYPE_SEND 0x02 -/* Define get_version() routine */ -define_get_version(gb_raw, RAW); - struct gb_raw_send_request { __le32 len; __u8 data[0]; @@ -180,11 +173,6 @@ static int gb_raw_connection_init(struct gb_connection *connection) raw->connection = connection; connection->private = raw; - /* Check the protocol version */ - retval = get_version(raw); - if (retval) - goto error_free; - INIT_LIST_HEAD(&raw->list); mutex_init(&raw->list_lock); -- cgit v1.2.3-59-g8ed1b From 7071ca1df5e26949e6a24684f52d45ca18f07077 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:09 +0530 Subject: greybus: sdio: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 345cffff7db0..24b2e3152fa1 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -18,8 +18,6 @@ struct gb_sdio_host { struct gb_connection *connection; - u8 version_major; - u8 version_minor; struct mmc_host *mmc; struct mmc_request *mrq; struct mutex lock; /* lock for this host */ @@ -36,9 +34,6 @@ struct gb_sdio_host { static struct workqueue_struct *gb_sdio_mrq_workqueue; -/* Define get_version() routine */ -define_get_version(gb_sdio_host, SDIO); - #define GB_SDIO_RSP_R1_R5_R6_R7 (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ GB_SDIO_RSP_OPCODE) #define GB_SDIO_RSP_R3_R4 (GB_SDIO_RSP_PRESENT) @@ -695,10 +690,6 @@ static int gb_sdio_connection_init(struct gb_connection *connection) host->connection = connection; connection->private = host; - ret = get_version(host); - if (ret < 0) - goto free_mmc; - ret = gb_sdio_get_caps(host); if (ret < 0) goto free_mmc; -- cgit v1.2.3-59-g8ed1b From 3fb0c8f516e06cb3bc39610aeb1311b8376bdc61 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:10 +0530 Subject: greybus: spi: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/spi.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 77d6bf0b7f13..ef3cc33772f8 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -17,8 +17,6 @@ struct gb_spi { struct gb_connection *connection; - u8 version_major; - u8 version_minor; /* Modes supported by spi controller */ u16 mode; @@ -181,9 +179,6 @@ static void gb_spi_cleanup(struct spi_device *spi) /* Routines to get controller infomation */ -/* Define get_version() routine */ -define_get_version(gb_spi, SPI); - /* * Map Greybus spi mode bits/flags/bpw into Linux ones. * All bits are same for now and so these macro's return same values. @@ -264,11 +259,6 @@ static int gb_spi_init(struct gb_spi *spi) { int ret; - /* First thing we need to do is check the version */ - ret = get_version(spi); - if (ret) - return ret; - /* mode never changes, just get it once */ ret = gb_spi_mode_operation(spi); if (ret) -- cgit v1.2.3-59-g8ed1b From a94e14486477b5738061ac09ab69025985c1eda5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:11 +0530 Subject: greybus: uart: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 7a51c7c792c9..9e8bf6f4b8d7 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -55,8 +55,6 @@ struct gb_tty { struct async_icount oldcount; wait_queue_head_t wioctl; struct mutex mutex; - u8 version_major; - u8 version_minor; u8 ctrlin; /* input control lines */ u8 ctrlout; /* output control lines */ struct gb_tty_line_coding line_coding; @@ -67,9 +65,6 @@ static DEFINE_IDR(tty_minors); static DEFINE_MUTEX(table_lock); static atomic_t reference_count = ATOMIC_INIT(0); -/* Define get_version() routine */ -define_get_version(gb_tty, UART); - static int gb_uart_receive_data(struct gb_tty *gb_tty, struct gb_connection *connection, struct gb_uart_recv_data_request *receive_data) @@ -628,21 +623,16 @@ static int gb_uart_connection_init(struct gb_connection *connection) gb_tty->connection = connection; connection->private = gb_tty; - /* Check for compatible protocol version */ - retval = get_version(gb_tty); - if (retval) - goto error_version; - minor = alloc_minor(gb_tty); if (minor < 0) { if (minor == -ENOSPC) { dev_err(&connection->dev, "no more free minor numbers\n"); retval = -ENODEV; - goto error_version; + goto error_minor; } retval = minor; - goto error_version; + goto error_minor; } gb_tty->minor = minor; @@ -674,7 +664,7 @@ static int gb_uart_connection_init(struct gb_connection *connection) error: tty_port_destroy(&gb_tty->port); release_minor(gb_tty); -error_version: +error_minor: connection->private = NULL; kfree(gb_tty->buffer); error_payload: -- cgit v1.2.3-59-g8ed1b From 0a12a187fdaa90108a681423a7f1e8ef135a1544 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:12 +0530 Subject: greybus: usb: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/usb.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 80c42b20adda..2133d0d25f25 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -20,8 +20,6 @@ #define GB_USB_VERSION_MINOR 0x01 /* Greybus USB request types */ -#define GB_USB_TYPE_INVALID 0x00 -#define GB_USB_TYPE_PROTOCOL_VERSION 0x01 #define GB_USB_TYPE_HCD_START 0x02 #define GB_USB_TYPE_HCD_STOP 0x03 #define GB_USB_TYPE_HUB_CONTROL 0x04 @@ -39,9 +37,6 @@ struct gb_usb_hub_control_response { struct gb_usb_device { struct gb_connection *connection; - - u8 version_major; - u8 version_minor; }; static inline struct gb_usb_device *to_gb_usb_device(struct usb_hcd *hcd) @@ -54,9 +49,6 @@ static inline struct usb_hcd *gb_usb_device_to_hcd(struct gb_usb_device *dev) return container_of((void *)dev, struct usb_hcd, hcd_priv); } -/* Define get_version() routine */ -define_get_version(gb_usb_device, USB); - static void hcd_stop(struct usb_hcd *hcd) { struct gb_usb_device *dev = to_gb_usb_device(hcd); @@ -183,11 +175,6 @@ static int gb_usb_connection_init(struct gb_connection *connection) gb_usb_dev->connection = connection; connection->private = gb_usb_dev; - /* Check for compatible protocol version */ - retval = get_version(gb_usb_dev); - if (retval) - goto err_put_hcd; - hcd->has_tt = 1; /* -- cgit v1.2.3-59-g8ed1b From a404504a8651250b2632cf62356de6c77a923430 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:13 +0530 Subject: greybus: vibrator: Drop get_version support This is done from a common place now, no need to replicate it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/vibrator.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index df9c4b14c707..96d649acf122 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -19,8 +19,6 @@ struct gb_vibrator_device { struct gb_connection *connection; struct device *dev; int minor; /* vibrator minor number */ - u8 version_major; - u8 version_minor; }; /* Version of the Greybus vibrator protocol we support */ @@ -28,8 +26,6 @@ struct gb_vibrator_device { #define GB_VIBRATOR_VERSION_MINOR 0x01 /* Greybus Vibrator operation types */ -#define GB_VIBRATOR_TYPE_INVALID 0x00 -#define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01 #define GB_VIBRATOR_TYPE_ON 0x02 #define GB_VIBRATOR_TYPE_OFF 0x03 @@ -37,9 +33,6 @@ struct gb_vibrator_on_request { __le16 timeout_ms; }; -/* Define get_version() routine */ -define_get_version(gb_vibrator_device, VIBRATOR); - static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) { struct gb_vibrator_on_request request; @@ -108,10 +101,6 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) vib->connection = connection; connection->private = vib; - retval = get_version(vib); - if (retval) - goto error; - /* * For now we create a device in sysfs for the vibrator, but odds are * there is a "real" device somewhere in the kernel for this, but I -- cgit v1.2.3-59-g8ed1b From 3ea959e3911e0c9ae49eb855d7b4f744349eb977 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:14 +0530 Subject: greybus: svc: preserve major/minor of protocol supported by SVC These weren't preserved earlier, save them in the connection structure instead of creating its own fields.. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 17ffb1353fb4..025b2bad9428 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -11,8 +11,6 @@ struct gb_svc { struct gb_connection *connection; - u8 version_major; - u8 version_minor; }; static struct ida greybus_svc_device_id_map; @@ -163,6 +161,9 @@ static int gb_svc_version_request(struct gb_operation *op) return -ENOTSUPP; } + connection->module_major = version->major; + connection->module_minor = version->minor; + if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); -- cgit v1.2.3-59-g8ed1b From b9938c49131f1d2c65e8783a5a17ff6a96d9ce89 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:15 +0530 Subject: greybus: protocol: Drop define_get_version support No more users now, drop it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.h | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 758b36ef1f55..45606adacf35 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -107,27 +107,4 @@ void gb_##__protocol##_exit(void) \ gb_protocol_deregister(&__protocol); \ } \ -/* - * Macro to create get_version() routine for protocols - * @__device: name of the device struct - * @__protocol: name of protocol in CAPITALS - */ -#define define_get_version(__device, __protocol) \ -static int get_version(struct __device *dev) \ -{ \ - struct gb_protocol_version_response response; \ - int retval; \ - \ - retval = gb_protocol_get_version(dev->connection, \ - GB_##__protocol##_TYPE_PROTOCOL_VERSION,\ - NULL, 0, &response, \ - GB_##__protocol##_VERSION_MAJOR); \ - if (retval) \ - return retval; \ - \ - dev->version_major = response.major; \ - dev->version_minor = response.minor; \ - return 0; \ -} - #endif /* __PROTOCOL_H */ -- cgit v1.2.3-59-g8ed1b From bf81454738990e7acd089e1b8aac8bab6a54637f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 11 Aug 2015 07:36:16 +0530 Subject: greybus: protocol: Remove unnecessary params of gb_protocol_get_version() Some of the parameters are not really required, drop them. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 7 +------ drivers/staging/greybus/protocol.c | 22 +++++++++++----------- drivers/staging/greybus/protocol.h | 6 ++---- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b1f1df81be50..88383b6e603f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -387,12 +387,7 @@ int gb_connection_init(struct gb_connection *connection) * this for SVC as that is initiated by the SVC. */ if (connection->hd_cport_id != GB_SVC_CPORT_ID) { - struct gb_protocol_version_response response; - - ret = gb_protocol_get_version(connection, - GB_REQUEST_TYPE_PROTOCOL_VERSION, - NULL, 0, &response, - connection->protocol->major); + ret = gb_protocol_get_version(connection, NULL, 0); if (ret) { dev_err(&connection->dev, "Failed to get version CPort-%d (%d)\n", diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index ba80f552fa31..b63e28c1b950 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -163,30 +163,30 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) return protocol; } -int gb_protocol_get_version(struct gb_connection *connection, int type, - void *request, int request_size, - struct gb_protocol_version_response *response, - __u8 major) +int gb_protocol_get_version(struct gb_connection *connection, void *request, + int request_size) { + struct gb_protocol_version_response response; int retval; - retval = gb_operation_sync(connection, type, request, request_size, - response, sizeof(*response)); + retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, + request, request_size, &response, + sizeof(response)); if (retval) return retval; - if (response->major > major) { + if (response.major > connection->protocol->major) { dev_err(&connection->dev, "unsupported major version (%hhu > %hhu)\n", - response->major, major); + response.major, connection->protocol->major); return -ENOTSUPP; } - connection->module_major = response->major; - connection->module_minor = response->minor; + connection->module_major = response.major; + connection->module_minor = response.minor; dev_dbg(&connection->dev, "version_major = %u version_minor = %u\n", - response->major, response->minor); + response.major, response.minor); return 0; } diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 45606adacf35..34a7f185a638 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -44,10 +44,8 @@ int gb_protocol_deregister(struct gb_protocol *protocol); __gb_protocol_register(protocol, THIS_MODULE) struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); -int gb_protocol_get_version(struct gb_connection *connection, int type, - void *request, int request_size, - struct gb_protocol_version_response *response, - __u8 major); +int gb_protocol_get_version(struct gb_connection *connection, void *request, + int request_size); void gb_protocol_put(struct gb_protocol *protocol); -- cgit v1.2.3-59-g8ed1b From 8ebc998f5fb146b7304fb7ac4e4d80059b6197fe Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 11 Aug 2015 13:50:50 +0100 Subject: greybus: connection: fix jump label on device_add failure On device_add() failure in gb_connection_create_range() we jump to err_remove_ida. Instead we should be jumping to err_free_connection, so change the flow to accomodate. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 88383b6e603f..3765aa87ef2d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -238,7 +238,7 @@ gb_connection_create_range(struct greybus_host_device *hd, pr_err("failed to add connection device for cport 0x%04hx\n", cport_id); - goto err_remove_ida; + goto err_free_connection; } spin_lock_irq(&gb_connections_lock); -- cgit v1.2.3-59-g8ed1b From a1a4a29cb9e9593a1f47d549af212f35f131e6cc Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 11 Aug 2015 13:50:51 +0100 Subject: greybus: connection: add a timestamp kfifo to track connection handoff For the ES2 test activity it may be beneficial to have a performance metric that doesn't include any of the greybus stack malloc, workqueues etc. In order to faciltate, this patch adds a simple kfifo structure to hold two timestamp values. One timestamp will represent the last reasonable point a greybus outbound timestamp can be taken, the other timestamp will represent the first reasonable point an inbound timestamp can be taken. In order to facilitate this model, tracking the timestamps in the connection structure appears to be the best place to keep store of this data. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 36 +++++++++++++++++++++++++++++++++++- drivers/staging/greybus/connection.h | 5 +++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3765aa87ef2d..0ec5b0dcc145 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -11,6 +11,10 @@ #include "greybus.h" +#define GB_CONNECTION_TS_KFIFO_ELEMENTS 2 +#define GB_CONNECTION_TS_KFIFO_LEN \ + (GB_CONNECTION_TS_KFIFO_ELEMENTS * sizeof(struct timeval)) + static DEFINE_SPINLOCK(gb_connections_lock); /* This is only used at initialization time; no locking is required. */ @@ -63,6 +67,29 @@ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, } EXPORT_SYMBOL_GPL(greybus_data_rcvd); +void gb_connection_push_timestamp(struct gb_connection *connection) +{ + struct timeval tv; + + do_gettimeofday(&tv); + kfifo_in_locked(&connection->ts_kfifo, (void *)&tv, + sizeof(struct timeval), &connection->lock); +} +EXPORT_SYMBOL_GPL(gb_connection_push_timestamp); + +int gb_connection_pop_timestamp(struct gb_connection *connection, + struct timeval *tv) +{ + int retval; + + if (!kfifo_len(&connection->ts_kfifo)) + return -ENOMEM; + retval = kfifo_out_locked(&connection->ts_kfifo, (void *)tv, + sizeof(*tv), &connection->lock); + return retval; +} +EXPORT_SYMBOL_GPL(gb_connection_pop_timestamp); + static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -102,6 +129,7 @@ static void gb_connection_release(struct device *dev) struct gb_connection *connection = to_gb_connection(dev); destroy_workqueue(connection->wq); + kfifo_free(&connection->ts_kfifo); kfree(connection); } @@ -222,6 +250,10 @@ gb_connection_create_range(struct greybus_host_device *hd, if (!connection->wq) goto err_free_connection; + if (kfifo_alloc(&connection->ts_kfifo, GB_CONNECTION_TS_KFIFO_LEN, + GFP_KERNEL)) + goto err_free_connection; + connection->dev.parent = parent; connection->dev.bus = &greybus_bus_type; connection->dev.type = &greybus_connection_type; @@ -238,7 +270,7 @@ gb_connection_create_range(struct greybus_host_device *hd, pr_err("failed to add connection device for cport 0x%04hx\n", cport_id); - goto err_free_connection; + goto err_free_kfifo; } spin_lock_irq(&gb_connections_lock); @@ -259,6 +291,8 @@ gb_connection_create_range(struct greybus_host_device *hd, return connection; +err_free_kfifo: + kfifo_free(&connection->ts_kfifo); err_free_connection: kfree(connection); err_remove_ida: diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 0dbbc202e953..a26a48033fc6 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -11,6 +11,7 @@ #define __CONNECTION_H #include +#include enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, @@ -42,6 +43,7 @@ struct gb_connection { struct list_head operations; struct workqueue_struct *wq; + struct kfifo ts_kfifo; atomic_t op_cycle; @@ -65,6 +67,9 @@ void gb_hd_connections_exit(struct greybus_host_device *hd); void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); +void gb_connection_push_timestamp(struct gb_connection *connection); +int gb_connection_pop_timestamp(struct gb_connection *connection, + struct timeval *tv); void gb_connection_bind_protocol(struct gb_connection *connection); -- cgit v1.2.3-59-g8ed1b From 3f2a809e8b4c69f61de17c3efe144b9dba23924b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 11 Aug 2015 13:50:52 +0100 Subject: greybus: es-drivers: add outbound timestamp to connection In order to facilitate grabbing a timestamp that doesn't include greybus overhead, this patch adds a timestamp right before usb_submit_urb() for both es1.c and es2.c. Long term the timestmaping of messages like this probably wants to go away but, for the moment it may have some use to the firmware people instrumenting the performance of the system. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 2 ++ drivers/staging/greybus/es2.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 0cb7a3c7ef72..7fe1eaa17024 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -16,6 +16,7 @@ #include "greybus.h" #include "svc_msg.h" #include "kernel_ver.h" +#include "connection.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) @@ -244,6 +245,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, usb_sndbulkpipe(udev, es1->cport_out_endpoint), message->buffer, buffer_size, cport_out_callback, message); + gb_connection_push_timestamp(message->operation->connection); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 323721a2e2aa..43cbc4d06e5f 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -16,6 +16,7 @@ #include "greybus.h" #include "svc_msg.h" #include "kernel_ver.h" +#include "connection.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) @@ -340,6 +341,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, es1->cport_out[bulk_ep_set].endpoint), message->buffer, buffer_size, cport_out_callback, message); + gb_connection_push_timestamp(message->operation->connection); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); -- cgit v1.2.3-59-g8ed1b From 4c192665f0183150cff38b6954687752f3461e13 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 11 Aug 2015 13:50:53 +0100 Subject: greybus: loopback: functionally decompose calculation of turn-around times We have a pattern similar to this over and over again gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); good software practice dictates we functionally decompose this. This patch decomposes into gb_loopback_calc_latency(). Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 88c329afd3ea..ac38644c4a48 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -219,6 +219,16 @@ static struct attribute *loopback_attrs[] = { }; ATTRIBUTE_GROUPS(loopback); +static void gb_loopback_calc_latency(struct gb_loopback *gb, + struct timeval *ts, struct timeval *te) +{ + u64 t1, t2; + + t1 = timeval_to_ns(ts); + t2 = timeval_to_ns(te); + gb->elapsed_nsecs = t2 - t1; +} + static int gb_loopback_sink(struct gb_loopback *gb, u32 len) { struct timeval ts, te; @@ -236,7 +246,7 @@ static int gb_loopback_sink(struct gb_loopback *gb, u32 len) request, len + sizeof(*request), NULL, 0); do_gettimeofday(&te); - gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); + gb_loopback_calc_latency(gb, &ts, &te); kfree(request); return retval; @@ -265,7 +275,7 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) request, len + sizeof(*request), response, len + sizeof(*response)); do_gettimeofday(&te); - gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); + gb_loopback_calc_latency(gb, &ts, &te); if (retval) goto gb_error; @@ -289,7 +299,7 @@ static int gb_loopback_ping(struct gb_loopback *gb) retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_PING, NULL, 0, NULL, 0); do_gettimeofday(&te); - gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); + gb_loopback_calc_latency(gb, &ts, &te); return retval; } -- cgit v1.2.3-59-g8ed1b From fd489e1ac617d662e248557afd8aa06ee731440b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 11 Aug 2015 13:50:54 +0100 Subject: greybus: loopback: handle timestamp roll-over This patch ensures we account for roll-over in the loopback driver. do_gettimeofday() is used to grab a timestamp. Two timestamps are derived one before and one after a gb_operation_sync(), however since do_gettimeofday() returns the number of seconds and mircoseconds that have elapsed today - we need to account for a situation where the timestamp starts at say 23:59:999us rolls over and the end time is now earlier than the start time. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ac38644c4a48..852b6beaecac 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -22,6 +22,8 @@ #include "greybus.h" +#define NSEC_PER_DAY 86400000000000ULL + struct gb_loopback_stats { u32 min; u32 max; @@ -226,7 +228,10 @@ static void gb_loopback_calc_latency(struct gb_loopback *gb, t1 = timeval_to_ns(ts); t2 = timeval_to_ns(te); - gb->elapsed_nsecs = t2 - t1; + if (t2 > t1) + gb->elapsed_nsecs = t2 - t1; + else + gb->elapsed_nsecs = NSEC_PER_DAY - t2 + t1; } static int gb_loopback_sink(struct gb_loopback *gb, u32 len) -- cgit v1.2.3-59-g8ed1b From 3944a454f1d5634cdcd8b8844199d67a1110dccb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 12 Aug 2015 09:19:31 +0530 Subject: greybus: interface: Preserve data received during hotplug event This shall be used later to find a firmware blob for the interface, lets save it in the interface structure. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.h | 6 ++++++ drivers/staging/greybus/svc.c | 13 +++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index e60a3705494e..38210ad4e631 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -28,6 +28,12 @@ struct gb_interface { char *product_string; u64 unique_id; + /* Information taken from the hotplug event */ + u32 unipro_mfg_id; + u32 unipro_prod_id; + u32 ara_vend_id; + u32 ara_prod_id; + struct gb_module *module; struct greybus_host_device *hd; }; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 025b2bad9428..73e7947fadba 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -232,10 +232,6 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) struct device *dev = &op->connection->dev; struct gb_interface *intf; u8 intf_id, device_id; - u32 unipro_mfg_id; - u32 unipro_prod_id; - u32 ara_vend_id; - u32 ara_prod_id; int ret; if (request->payload_size < sizeof(*hotplug)) { @@ -252,10 +248,6 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) * XXX have the SVC get acknowledgement before we proceed. */ intf_id = hotplug->intf_id; - unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); - unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); - ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id); - ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id); intf = gb_interface_create(hd, intf_id); if (!intf) { @@ -264,6 +256,11 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) return -EINVAL; } + intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); + intf->unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); + intf->ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id); + intf->ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id); + /* * Create a device id for the interface: * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC -- cgit v1.2.3-59-g8ed1b From 738599c0dd7fef4d28f416ff9b0b3bc1b07468d2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 12 Aug 2015 09:19:32 +0530 Subject: greybus: protocol: Create request structure from within gb_protocol_get_version() The version request can only send the version of protocol for which it is initiated and gb_protocol_get_version() has all the information to create the request structure. Replace the 'request' and 'request_size' arguments to gb_protocol_get_version() with a bool to know if the version information of the protocol should be sent or not. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/protocol.c | 12 ++++++++++-- drivers/staging/greybus/protocol.h | 3 +-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 0ec5b0dcc145..2b2be3fd1638 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -421,7 +421,7 @@ int gb_connection_init(struct gb_connection *connection) * this for SVC as that is initiated by the SVC. */ if (connection->hd_cport_id != GB_SVC_CPORT_ID) { - ret = gb_protocol_get_version(connection, NULL, 0); + ret = gb_protocol_get_version(connection, false); if (ret) { dev_err(&connection->dev, "Failed to get version CPort-%d (%d)\n", diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index b63e28c1b950..5bdc2c026efd 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -163,12 +163,20 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) return protocol; } -int gb_protocol_get_version(struct gb_connection *connection, void *request, - int request_size) +int gb_protocol_get_version(struct gb_connection *connection, bool send_request) { struct gb_protocol_version_response response; + struct gb_protocol_version_response *request = NULL; + int request_size = 0; int retval; + if (send_request) { + response.major = connection->protocol->major; + response.minor = connection->protocol->minor; + request = &response; + request_size = sizeof(*request); + } + retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, request, request_size, &response, sizeof(response)); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 34a7f185a638..87b5a1010de0 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -44,8 +44,7 @@ int gb_protocol_deregister(struct gb_protocol *protocol); __gb_protocol_register(protocol, THIS_MODULE) struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); -int gb_protocol_get_version(struct gb_connection *connection, void *request, - int request_size); +int gb_protocol_get_version(struct gb_connection *connection, bool send_request); void gb_protocol_put(struct gb_protocol *protocol); -- cgit v1.2.3-59-g8ed1b From 90f1b617d88f145506e9061436069583cb82d101 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 12 Aug 2015 09:19:33 +0530 Subject: greybus: Add firmware protocol driver This adds firmware protocol driver based on the latest specs available on mailing lists. This uses the firmware framework present in kernel. Refer Documentation/firmware_class/README on how it works. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/core.c | 9 ++ drivers/staging/greybus/firmware.c | 199 ++++++++++++++++++++++++++++ drivers/staging/greybus/firmware.h | 16 +++ drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/greybus_manifest.h | 2 + drivers/staging/greybus/greybus_protocols.h | 54 ++++++++ 7 files changed, 282 insertions(+) create mode 100644 drivers/staging/greybus/firmware.c create mode 100644 drivers/staging/greybus/firmware.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1467c5b3fcd8..3c32d1427dc0 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -10,6 +10,7 @@ greybus-y := core.o \ protocol.o \ control.o \ svc.o \ + firmware.o \ operation.o gb-phy-y := gpbridge.o \ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 225fa4fb5268..6edeec9c1fdf 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -300,8 +300,16 @@ static int __init gb_init(void) goto error_svc; } + retval = gb_firmware_protocol_init(); + if (retval) { + pr_err("gb_firmware_protocol_init failed\n"); + goto error_firmware; + } + return 0; /* Success */ +error_firmware: + gb_svc_protocol_exit(); error_svc: gb_control_protocol_exit(); error_control: @@ -321,6 +329,7 @@ module_init(gb_init); static void __exit gb_exit(void) { + gb_firmware_protocol_exit(); gb_svc_protocol_exit(); gb_control_protocol_exit(); gb_endo_exit(); diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c new file mode 100644 index 000000000000..13efaabb891b --- /dev/null +++ b/drivers/staging/greybus/firmware.c @@ -0,0 +1,199 @@ +/* + * FIRMWARE Greybus driver. + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include + +#include "greybus.h" + +struct gb_firmware { + struct gb_connection *connection; + const struct firmware *fw; +}; + +static void free_firmware(struct gb_firmware *firmware) +{ + release_firmware(firmware->fw); + firmware->fw = NULL; +} + +/* This returns path of the firmware blob on the disk */ +static int download_firmware(struct gb_firmware *firmware, u8 stage) +{ + struct gb_connection *connection = firmware->connection; + struct gb_interface *intf = connection->bundle->intf; + char firmware_name[28]; + + /* Already have a firmware, free it */ + if (firmware->fw) + free_firmware(firmware); + + /* + * Create firmware name + * + * XXX Name it properly.. + */ + sprintf(firmware_name, "ara:%04x:%04x:%04x:%04x:%04x.fw", intf->unipro_mfg_id, + intf->unipro_prod_id, intf->ara_vend_id, intf->ara_prod_id, + stage); + + return request_firmware(&firmware->fw, firmware_name, &connection->dev); +} + +static int gb_firmware_size_request(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_firmware *firmware = connection->private; + struct gb_firmware_size_request *size_request = op->request->payload; + struct gb_firmware_size_response *size_response; + struct device *dev = &connection->dev; + int ret; + + if (op->request->payload_size != sizeof(*size_request)) { + dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", + __func__, op->request->payload_size, + sizeof(*size_request)); + return -EINVAL; + } + + ret = download_firmware(firmware, size_request->stage); + if (ret) { + dev_err(dev, "%s: failed to download firmware (%d)\n", __func__, + ret); + return ret; + } + + if (!gb_operation_response_alloc(op, sizeof(*size_response), + GFP_KERNEL)) { + dev_err(dev, "%s: error allocating response\n", __func__); + free_firmware(firmware); + return -ENOMEM; + } + + size_response = op->response->payload; + size_response->size = cpu_to_le32(firmware->fw->size); + + return 0; +} + +static int gb_firmware_get_firmware(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_firmware *firmware = connection->private; + struct gb_firmware_get_firmware_request *firmware_request = op->request->payload; + struct gb_firmware_get_firmware_response *firmware_response; + struct device *dev = &connection->dev; + unsigned int offset, size; + + if (op->request->payload_size != sizeof(*firmware_request)) { + dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", + __func__, op->request->payload_size, + sizeof(*firmware_request)); + return -EINVAL; + } + + if (!firmware->fw) { + dev_err(dev, "%s: firmware not available\n", __func__); + return -EINVAL; + } + + offset = le32_to_cpu(firmware_request->offset); + size = le32_to_cpu(firmware_request->size); + + if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, + GFP_KERNEL)) { + dev_err(dev, "%s: error allocating response\n", __func__); + return -ENOMEM; + } + + firmware_response = op->response->payload; + memcpy(firmware_response->data, firmware->fw->data + offset, size); + + return 0; +} + +static int gb_firmware_ready_to_boot(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_firmware_ready_to_boot_request *rtb_request = op->request->payload; + struct device *dev = &connection->dev; + u8 stage, status; + + if (op->request->payload_size != sizeof(*rtb_request)) { + dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", + __func__, op->request->payload_size, + sizeof(*rtb_request)); + return -EINVAL; + } + + stage = rtb_request->stage; + status = rtb_request->status; + + /* Return error if the blob was invalid */ + if (status == GB_FIRMWARE_BOOT_STATUS_INVALID) + return -EINVAL; + + /* + * XXX Should we return error for insecure firmware? + */ + + return 0; +} + +static int gb_firmware_request_recv(u8 type, struct gb_operation *op) +{ + switch (type) { + case GB_FIRMWARE_TYPE_FIRMWARE_SIZE: + return gb_firmware_size_request(op); + case GB_FIRMWARE_TYPE_GET_FIRMWARE: + return gb_firmware_get_firmware(op); + case GB_FIRMWARE_TYPE_READY_TO_BOOT: + return gb_firmware_ready_to_boot(op); + default: + dev_err(&op->connection->dev, + "unsupported request: %hhu\n", type); + return -EINVAL; + } +} + +static int gb_firmware_connection_init(struct gb_connection *connection) +{ + struct gb_firmware *firmware; + + firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); + if (!firmware) + return -ENOMEM; + + firmware->connection = connection; + connection->private = firmware; + + return 0; +} + +static void gb_firmware_connection_exit(struct gb_connection *connection) +{ + struct gb_firmware *firmware = connection->private; + + /* Release firmware */ + if (firmware->fw) + free_firmware(firmware); + + connection->private = NULL; + kfree(firmware); +} + +static struct gb_protocol firmware_protocol = { + .name = "firmware", + .id = GREYBUS_PROTOCOL_FIRMWARE, + .major = GB_FIRMWARE_VERSION_MAJOR, + .minor = GB_FIRMWARE_VERSION_MINOR, + .connection_init = gb_firmware_connection_init, + .connection_exit = gb_firmware_connection_exit, + .request_recv = gb_firmware_request_recv, +}; +gb_builtin_protocol_driver(firmware_protocol); diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h new file mode 100644 index 000000000000..548d297eec63 --- /dev/null +++ b/drivers/staging/greybus/firmware.h @@ -0,0 +1,16 @@ +/* + * Greybus firmware code + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __FIRMWARE_H +#define __FIRMWARE_H + +int gb_firmware_protocol_init(void); +void gb_firmware_protocol_exit(void); + +#endif /* __FIRMWARE_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2214f447df2b..0d4ca700a711 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -27,6 +27,7 @@ #include "manifest.h" #include "endo.h" #include "svc.h" +#include "firmware.h" #include "module.h" #include "control.h" #include "interface.h" diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 9c4d7cae9bbb..687adf2cb645 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -43,6 +43,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12, GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13, GREYBUS_PROTOCOL_SVC = 0x14, + GREYBUS_PROTOCOL_FIRMWARE = 0x15, /* ... */ GREYBUS_PROTOCOL_RAW = 0xfe, GREYBUS_PROTOCOL_VENDOR = 0xff, @@ -70,6 +71,7 @@ enum greybus_class_type { GREYBUS_CLASS_I2S_RECEIVER = 0x12, GREYBUS_CLASS_I2S_TRANSMITTER = 0x13, GREYBUS_CLASS_SVC = 0x14, + GREYBUS_CLASS_FIRMWARE = 0x15, /* ... */ GREYBUS_CLASS_RAW = 0xfe, GREYBUS_CLASS_VENDOR = 0xff, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b95d24bd8e62..357ecd371adb 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -146,6 +146,60 @@ struct gb_control_disconnected_request { }; /* Control protocol [dis]connected response has no payload */ + +/* Firmware Protocol */ + +/* Version of the Greybus firmware protocol we support */ +#define GB_FIRMWARE_VERSION_MAJOR 0x00 +#define GB_FIRMWARE_VERSION_MINOR 0x01 + +/* Greybus firmware request types */ +#define GB_FIRMWARE_TYPE_INVALID 0x00 +#define GB_FIRMWARE_TYPE_PROTOCOL_VERSION 0x01 +#define GB_FIRMWARE_TYPE_FIRMWARE_SIZE 0x02 +#define GB_FIRMWARE_TYPE_GET_FIRMWARE 0x03 +#define GB_FIRMWARE_TYPE_READY_TO_BOOT 0x04 + +/* Greybus firmware boot stages */ +#define GB_FIRMWARE_BOOT_STAGE_ONE 0x01 /* Reserved for the boot ROM */ +#define GB_FIRMWARE_BOOT_STAGE_TWO 0x02 /* Firmware package to be loaded by the boot ROM */ +#define GB_FIRMWARE_BOOT_STAGE_THREE 0x03 /* Module personality package loaded by Stage 2 firmware */ + +/* Greybus firmware ready to boot status */ +#define GB_FIRMWARE_BOOT_STATUS_INVALID 0x00 /* Firmware blob could not be validated */ +#define GB_FIRMWARE_BOOT_STATUS_INSECURE 0x01 /* Firmware blob is valid but insecure */ +#define GB_FIRMWARE_BOOT_STATUS_SECURE 0x02 /* Firmware blob is valid and secure */ + +/* Max firmware data fetch size in bytes */ +#define GB_FIRMWARE_FETCH_MAX 2000 + +/* Firmware protocol firmware size request/response */ +struct gb_firmware_size_request { + __u8 stage; +}; + +struct gb_firmware_size_response { + __le32 size; +}; + +/* Firmware protocol get firmware request/response */ +struct gb_firmware_get_firmware_request { + __le32 offset; + __le32 size; +}; + +struct gb_firmware_get_firmware_response { + __u8 data[0]; +}; + +/* Firmware protocol Ready to boot request */ +struct gb_firmware_ready_to_boot_request { + __u8 stage; + __u8 status; +}; +/* Firmware protocol Ready to boot response has no payload */ + + /* I2C */ /* Version of the Greybus i2c protocol we support */ -- cgit v1.2.3-59-g8ed1b From 58dab0f2a872be5dc2bdb15f3dc487b4a1b41aaf Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 12 Aug 2015 09:19:34 +0530 Subject: greybus: connection: Send protocol version for firmware protocol As per greybus specs, we need to send the protocol version for firmware protocol and so this special case Hack. Probably we should always send the protocol version AP supports and kill this hack completely. But then it requires updates to specs as well, and that should be done after some discussion. For now, add a FIXME for that and a special case for firmware protocol. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2b2be3fd1638..6078443a7dca 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -421,7 +421,19 @@ int gb_connection_init(struct gb_connection *connection) * this for SVC as that is initiated by the SVC. */ if (connection->hd_cport_id != GB_SVC_CPORT_ID) { - ret = gb_protocol_get_version(connection, false); + bool send_request = false; + + /* + * We need to send the protocol version of the firmware protocol + * supported by AP and so this special case. + */ + if (connection->protocol->id == GREYBUS_PROTOCOL_FIRMWARE) + send_request = true; + + // FIXME: Should we always send the protocol version AP can + // support ? + + ret = gb_protocol_get_version(connection, send_request); if (ret) { dev_err(&connection->dev, "Failed to get version CPort-%d (%d)\n", -- cgit v1.2.3-59-g8ed1b From 1cb5fa47c54ba045593c4dfede72338107ba2133 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 12 Aug 2015 11:04:06 +0530 Subject: greybus: protocol: send own protocol version while requesting it The greybus specifications clearly say (for all protocols) that the sender is responsible for sending the highest version of protocol it supports, while it requests the same from the receiver. But the greybus code never followed that. Fix, this by always sending AP's version of the protocol, while requesting the same from svc/module. This also renames 'response' to 'version' in gb_protocol_get_version() as it is used for both request/response. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 14 +------------- drivers/staging/greybus/protocol.c | 28 +++++++++++----------------- drivers/staging/greybus/protocol.h | 2 +- 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index eae6ad143d0b..27b64d6c8f2e 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -428,19 +428,7 @@ int gb_connection_init(struct gb_connection *connection) * this for SVC as that is initiated by the SVC. */ if (connection->hd_cport_id != GB_SVC_CPORT_ID) { - bool send_request = false; - - /* - * We need to send the protocol version of the firmware protocol - * supported by AP and so this special case. - */ - if (connection->protocol->id == GREYBUS_PROTOCOL_FIRMWARE) - send_request = true; - - // FIXME: Should we always send the protocol version AP can - // support ? - - ret = gb_protocol_get_version(connection, send_request); + ret = gb_protocol_get_version(connection); if (ret) { dev_err(&connection->dev, "Failed to get version CPort-%d (%d)\n", diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 5bdc2c026efd..1c746597f016 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -163,38 +163,32 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) return protocol; } -int gb_protocol_get_version(struct gb_connection *connection, bool send_request) +int gb_protocol_get_version(struct gb_connection *connection) { - struct gb_protocol_version_response response; - struct gb_protocol_version_response *request = NULL; - int request_size = 0; + struct gb_protocol_version_response version; int retval; - if (send_request) { - response.major = connection->protocol->major; - response.minor = connection->protocol->minor; - request = &response; - request_size = sizeof(*request); - } + version.major = connection->protocol->major; + version.minor = connection->protocol->minor; retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, - request, request_size, &response, - sizeof(response)); + &version, sizeof(version), &version, + sizeof(version)); if (retval) return retval; - if (response.major > connection->protocol->major) { + if (version.major > connection->protocol->major) { dev_err(&connection->dev, "unsupported major version (%hhu > %hhu)\n", - response.major, connection->protocol->major); + version.major, connection->protocol->major); return -ENOTSUPP; } - connection->module_major = response.major; - connection->module_minor = response.minor; + connection->module_major = version.major; + connection->module_minor = version.minor; dev_dbg(&connection->dev, "version_major = %u version_minor = %u\n", - response.major, response.minor); + version.major, version.minor); return 0; } diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 87b5a1010de0..8d55a4a2f06e 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -44,7 +44,7 @@ int gb_protocol_deregister(struct gb_protocol *protocol); __gb_protocol_register(protocol, THIS_MODULE) struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); -int gb_protocol_get_version(struct gb_connection *connection, bool send_request); +int gb_protocol_get_version(struct gb_connection *connection); void gb_protocol_put(struct gb_protocol *protocol); -- cgit v1.2.3-59-g8ed1b From ce83294348fee40d4173f5e5b41c2999405149b4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 12 Aug 2015 11:51:10 +0530 Subject: greybus: battery: Move request/response structure/definitions to greybus_protocols.h These must be exposed to external modules, like gbsim. Move them to greybus_protocols.h file. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery.c | 55 --------------------------- drivers/staging/greybus/greybus_protocols.h | 58 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index a56eb81b2386..6b354de146d1 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -35,61 +35,6 @@ struct gb_battery { }; -/* Version of the Greybus battery protocol we support */ -#define GB_BATTERY_VERSION_MAJOR 0x00 -#define GB_BATTERY_VERSION_MINOR 0x01 - -/* Greybus battery request types */ -#define GB_BATTERY_TYPE_TECHNOLOGY 0x02 -#define GB_BATTERY_TYPE_STATUS 0x03 -#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 -#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05 -#define GB_BATTERY_TYPE_TEMPERATURE 0x06 -#define GB_BATTERY_TYPE_VOLTAGE 0x07 -#define GB_BATTERY_TYPE_CURRENT 0x08 -#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX -#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX - -/* Should match up with battery types in linux/power_supply.h */ -#define GB_BATTERY_TECH_UNKNOWN 0x0000 -#define GB_BATTERY_TECH_NiMH 0x0001 -#define GB_BATTERY_TECH_LION 0x0002 -#define GB_BATTERY_TECH_LIPO 0x0003 -#define GB_BATTERY_TECH_LiFe 0x0004 -#define GB_BATTERY_TECH_NiCd 0x0005 -#define GB_BATTERY_TECH_LiMn 0x0006 - -struct gb_battery_technology_response { - __le32 technology; -}; - -/* Should match up with battery status in linux/power_supply.h */ -#define GB_BATTERY_STATUS_UNKNOWN 0x0000 -#define GB_BATTERY_STATUS_CHARGING 0x0001 -#define GB_BATTERY_STATUS_DISCHARGING 0x0002 -#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003 -#define GB_BATTERY_STATUS_FULL 0x0004 - -struct gb_battery_status_response { - __le16 battery_status; -}; - -struct gb_battery_max_voltage_response { - __le32 max_voltage; -}; - -struct gb_battery_capacity_response { - __le32 capacity; -}; - -struct gb_battery_temperature_response { - __le32 temperature; -}; - -struct gb_battery_voltage_response { - __le32 voltage; -}; - static int get_tech(struct gb_battery *gb) { struct gb_battery_technology_response tech_response; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 43a072881715..e0aed3be21d0 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -200,6 +200,64 @@ struct gb_firmware_ready_to_boot_request { /* Firmware protocol Ready to boot response has no payload */ +/* BATTERY */ + +/* Version of the Greybus battery protocol we support */ +#define GB_BATTERY_VERSION_MAJOR 0x00 +#define GB_BATTERY_VERSION_MINOR 0x01 + +/* Greybus battery request types */ +#define GB_BATTERY_TYPE_TECHNOLOGY 0x02 +#define GB_BATTERY_TYPE_STATUS 0x03 +#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 +#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05 +#define GB_BATTERY_TYPE_TEMPERATURE 0x06 +#define GB_BATTERY_TYPE_VOLTAGE 0x07 +#define GB_BATTERY_TYPE_CURRENT 0x08 +#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX +#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX + +/* Should match up with battery types in linux/power_supply.h */ +#define GB_BATTERY_TECH_UNKNOWN 0x0000 +#define GB_BATTERY_TECH_NiMH 0x0001 +#define GB_BATTERY_TECH_LION 0x0002 +#define GB_BATTERY_TECH_LIPO 0x0003 +#define GB_BATTERY_TECH_LiFe 0x0004 +#define GB_BATTERY_TECH_NiCd 0x0005 +#define GB_BATTERY_TECH_LiMn 0x0006 + +struct gb_battery_technology_response { + __le32 technology; +}; + +/* Should match up with battery status in linux/power_supply.h */ +#define GB_BATTERY_STATUS_UNKNOWN 0x0000 +#define GB_BATTERY_STATUS_CHARGING 0x0001 +#define GB_BATTERY_STATUS_DISCHARGING 0x0002 +#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003 +#define GB_BATTERY_STATUS_FULL 0x0004 + +struct gb_battery_status_response { + __le16 battery_status; +}; + +struct gb_battery_max_voltage_response { + __le32 max_voltage; +}; + +struct gb_battery_capacity_response { + __le32 capacity; +}; + +struct gb_battery_temperature_response { + __le32 temperature; +}; + +struct gb_battery_voltage_response { + __le32 voltage; +}; + + /* I2C */ /* Version of the Greybus i2c protocol we support */ -- cgit v1.2.3-59-g8ed1b From 51aee043ecdb890bbfd33e4cb3b73269ee8371e3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 12 Aug 2015 11:51:11 +0530 Subject: greybus: hid: Move request/response structure/definitions to greybus_protocols.h These must be exposed to external modules, like gbsim. Move them to greybus_protocols.h file. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 52 +++++++++++++++++++++++++++++ drivers/staging/greybus/hid.c | 49 --------------------------- 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e0aed3be21d0..0ba11f8629d6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -258,6 +258,58 @@ struct gb_battery_voltage_response { }; +/* HID */ + +/* Version of the Greybus hid protocol we support */ +#define GB_HID_VERSION_MAJOR 0x00 +#define GB_HID_VERSION_MINOR 0x01 + +/* Greybus HID operation types */ +#define GB_HID_TYPE_INVALID 0x00 +#define GB_HID_TYPE_PROTOCOL_VERSION 0x01 +#define GB_HID_TYPE_GET_DESC 0x02 +#define GB_HID_TYPE_GET_REPORT_DESC 0x03 +#define GB_HID_TYPE_PWR_ON 0x04 +#define GB_HID_TYPE_PWR_OFF 0x05 +#define GB_HID_TYPE_GET_REPORT 0x06 +#define GB_HID_TYPE_SET_REPORT 0x07 +#define GB_HID_TYPE_IRQ_EVENT 0x08 + +/* Report type */ +#define GB_HID_INPUT_REPORT 0 +#define GB_HID_OUTPUT_REPORT 1 +#define GB_HID_FEATURE_REPORT 2 + +/* Different request/response structures */ +/* HID get descriptor response */ +struct gb_hid_desc_response { + __u8 bLength; + __le16 wReportDescLength; + __le16 bcdHID; + __le16 wProductID; + __le16 wVendorID; + __u8 bCountryCode; +} __packed; + +/* HID get report request/response */ +struct gb_hid_get_report_request { + __u8 report_type; + __u8 report_id; +}; + +/* HID set report request */ +struct gb_hid_set_report_request { + __u8 report_type; + __u8 report_id; + __u8 report[0]; +}; + +/* HID input report request, via interrupt pipe */ +struct gb_hid_input_report_request { + __u8 report[0]; +}; + + /* I2C */ /* Version of the Greybus i2c protocol we support */ diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 887dbac8c117..0044b844c3e9 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -16,55 +16,6 @@ #include "greybus.h" -/* Version of the Greybus hid protocol we support */ -#define GB_HID_VERSION_MAJOR 0x00 -#define GB_HID_VERSION_MINOR 0x01 - -/* Greybus HID operation types */ -#define GB_HID_TYPE_INVALID 0x00 -#define GB_HID_TYPE_PROTOCOL_VERSION 0x01 -#define GB_HID_TYPE_GET_DESC 0x02 -#define GB_HID_TYPE_GET_REPORT_DESC 0x03 -#define GB_HID_TYPE_PWR_ON 0x04 -#define GB_HID_TYPE_PWR_OFF 0x05 -#define GB_HID_TYPE_GET_REPORT 0x06 -#define GB_HID_TYPE_SET_REPORT 0x07 -#define GB_HID_TYPE_IRQ_EVENT 0x08 - -/* Report type */ -#define GB_HID_INPUT_REPORT 0 -#define GB_HID_OUTPUT_REPORT 1 -#define GB_HID_FEATURE_REPORT 2 - -/* Different request/response structures */ -/* HID get descriptor response */ -struct gb_hid_desc_response { - __u8 bLength; - __le16 wReportDescLength; - __le16 bcdHID; - __le16 wProductID; - __le16 wVendorID; - __u8 bCountryCode; -} __packed; - -/* HID get report request/response */ -struct gb_hid_get_report_request { - __u8 report_type; - __u8 report_id; -}; - -/* HID set report request */ -struct gb_hid_set_report_request { - __u8 report_type; - __u8 report_id; - __u8 report[0]; -}; - -/* HID input report request, via interrupt pipe */ -struct gb_hid_input_report_request { - __u8 report[0]; -}; - /* Greybus HID device's structure */ struct gb_hid { struct gb_connection *connection; -- cgit v1.2.3-59-g8ed1b From d65e3a22aa3ef28f068cdc38ac82f3e1914cde9a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Aug 2015 10:04:45 +0530 Subject: greybus: raw: Move request/response structure/definitions to greybus_protocols.h These must be exposed to external modules, like gbsim. Move them to greybus_protocols.h file. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 16 ++++++++++++++++ drivers/staging/greybus/raw.c | 12 ------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 0ba11f8629d6..09a4466f1a29 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -823,6 +823,22 @@ struct gb_svc_route_create_request { __u8 dev2_id; }; + +/* RAW */ + +/* Version of the Greybus raw protocol we support */ +#define GB_RAW_VERSION_MAJOR 0x00 +#define GB_RAW_VERSION_MINOR 0x01 + +/* Greybus raw request types */ +#define GB_RAW_TYPE_SEND 0x02 + +struct gb_raw_send_request { + __le32 len; + __u8 data[0]; +}; + + /* UART */ /* Version of the Greybus UART protocol we support */ diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index 215d42165282..ce0f59d08e0d 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -28,18 +28,6 @@ struct gb_raw { struct device *device; }; -/* Version of the Greybus raw protocol we support */ -#define GB_RAW_VERSION_MAJOR 0x00 -#define GB_RAW_VERSION_MINOR 0x01 - -/* Greybus raw request types */ -#define GB_RAW_TYPE_SEND 0x02 - -struct gb_raw_send_request { - __le32 len; - __u8 data[0]; -}; - struct raw_data { struct list_head entry; u32 len; -- cgit v1.2.3-59-g8ed1b From 22322c474551c208625d3b95ffba39b1882fa645 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Aug 2015 10:35:29 +0530 Subject: greybus: connection: Destroy wq on failure Need to destroy the wq created earlier, do it. Fixes: d0f1778a6b67 ("greybus/connection: add a timestamp kfifo to track connection handoff") Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 27b64d6c8f2e..d0f499d509ba 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -252,7 +252,7 @@ gb_connection_create_range(struct greybus_host_device *hd, if (kfifo_alloc(&connection->ts_kfifo, GB_CONNECTION_TS_KFIFO_LEN, GFP_KERNEL)) - goto err_free_connection; + goto err_destroy_wq; connection->dev.parent = parent; connection->dev.bus = &greybus_bus_type; @@ -298,6 +298,8 @@ gb_connection_create_range(struct greybus_host_device *hd, err_free_kfifo: kfifo_free(&connection->ts_kfifo); +err_destroy_wq: + destroy_workqueue(connection->wq); err_free_connection: kfree(connection); err_remove_ida: -- cgit v1.2.3-59-g8ed1b From 435ea76218c9f3257990c6da8b37bf7708598f49 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Aug 2015 10:40:28 +0530 Subject: greybus: connection: Don't free resources freed by gb_connection_release() We are already doing put_device() here and so don't need to free resources directly, except ida. Fixes: afde17fe0b61 ("greybus/connection: fix jump label on device_add failure") Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index d0f499d509ba..7eb28680e498 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -270,7 +270,7 @@ gb_connection_create_range(struct greybus_host_device *hd, pr_err("failed to add connection device for cport 0x%04hx\n", cport_id); - goto err_free_kfifo; + goto err_remove_ida; } spin_lock_irq(&gb_connections_lock); @@ -296,8 +296,6 @@ gb_connection_create_range(struct greybus_host_device *hd, return connection; -err_free_kfifo: - kfifo_free(&connection->ts_kfifo); err_destroy_wq: destroy_workqueue(connection->wq); err_free_connection: -- cgit v1.2.3-59-g8ed1b From 0e2462d1b71b15790d37f9f66b01cdf1003a8e72 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Aug 2015 07:57:38 +0530 Subject: greybus: Drop protocol specific _PROTOCOL_VERSION and _INVALID macros Greybus core supports protocol independent macros for this now, use them. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 2 +- drivers/staging/greybus/greybus_protocols.h | 24 ------------------------ drivers/staging/greybus/loopback.c | 2 +- drivers/staging/greybus/svc.c | 2 +- 4 files changed, 3 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 605e6134d564..e675c5c89123 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -71,7 +71,7 @@ static int gb_control_request_recv(u8 type, struct gb_operation *op) // Send authenticated block of data, confirming this module is // an AP. break; - case GB_CONTROL_TYPE_PROTOCOL_VERSION: + case GB_REQUEST_TYPE_PROTOCOL_VERSION: if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) { dev_err(&connection->dev, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 09a4466f1a29..a80c2f253533 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -118,8 +118,6 @@ struct gb_protocol_version_response { #define GB_CONTROL_VERSION_MINOR 0x01 /* Greybus control request types */ -#define GB_CONTROL_TYPE_INVALID 0x00 -#define GB_CONTROL_TYPE_PROTOCOL_VERSION 0x01 #define GB_CONTROL_TYPE_PROBE_AP 0x02 #define GB_CONTROL_TYPE_GET_MANIFEST_SIZE 0x03 #define GB_CONTROL_TYPE_GET_MANIFEST 0x04 @@ -154,8 +152,6 @@ struct gb_control_disconnected_request { #define GB_FIRMWARE_VERSION_MINOR 0x01 /* Greybus firmware request types */ -#define GB_FIRMWARE_TYPE_INVALID 0x00 -#define GB_FIRMWARE_TYPE_PROTOCOL_VERSION 0x01 #define GB_FIRMWARE_TYPE_FIRMWARE_SIZE 0x02 #define GB_FIRMWARE_TYPE_GET_FIRMWARE 0x03 #define GB_FIRMWARE_TYPE_READY_TO_BOOT 0x04 @@ -265,8 +261,6 @@ struct gb_battery_voltage_response { #define GB_HID_VERSION_MINOR 0x01 /* Greybus HID operation types */ -#define GB_HID_TYPE_INVALID 0x00 -#define GB_HID_TYPE_PROTOCOL_VERSION 0x01 #define GB_HID_TYPE_GET_DESC 0x02 #define GB_HID_TYPE_GET_REPORT_DESC 0x03 #define GB_HID_TYPE_PWR_ON 0x04 @@ -317,8 +311,6 @@ struct gb_hid_input_report_request { #define GB_I2C_VERSION_MINOR 0x01 /* Greybus i2c request types */ -#define GB_I2C_TYPE_INVALID 0x00 -#define GB_I2C_TYPE_PROTOCOL_VERSION 0x01 #define GB_I2C_TYPE_FUNCTIONALITY 0x02 #define GB_I2C_TYPE_TIMEOUT 0x03 #define GB_I2C_TYPE_RETRIES 0x04 @@ -374,8 +366,6 @@ struct gb_i2c_transfer_response { #define GB_GPIO_VERSION_MINOR 0x01 /* Greybus GPIO request types */ -#define GB_GPIO_TYPE_INVALID 0x00 -#define GB_GPIO_TYPE_PROTOCOL_VERSION 0x01 #define GB_GPIO_TYPE_LINE_COUNT 0x02 #define GB_GPIO_TYPE_ACTIVATE 0x03 #define GB_GPIO_TYPE_DEACTIVATE 0x04 @@ -479,8 +469,6 @@ struct gb_gpio_irq_event_request { #define GB_PWM_VERSION_MINOR 0x01 /* Greybus PWM operation types */ -#define GB_PWM_TYPE_INVALID 0x00 -#define GB_PWM_TYPE_PROTOCOL_VERSION 0x01 #define GB_PWM_TYPE_PWM_COUNT 0x02 #define GB_PWM_TYPE_ACTIVATE 0x03 #define GB_PWM_TYPE_DEACTIVATE 0x04 @@ -523,7 +511,6 @@ struct gb_pwm_disable_request { /* I2S */ -#define GB_I2S_MGMT_TYPE_PROTOCOL_VERSION 0x01 #define GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS 0x02 #define GB_I2S_MGMT_TYPE_SET_CONFIGURATION 0x03 #define GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE 0x04 @@ -650,7 +637,6 @@ struct gb_i2s_mgmt_report_event_request { }; /* report event response has no payload */ -#define GB_I2S_DATA_TYPE_PROTOCOL_VERSION 0x01 #define GB_I2S_DATA_TYPE_SEND_DATA 0x02 struct gb_i2s_send_data_request { @@ -687,8 +673,6 @@ struct gb_i2s_send_data_request { #define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */ /* Greybus spi operation types */ -#define GB_SPI_TYPE_INVALID 0x00 -#define GB_SPI_TYPE_PROTOCOL_VERSION 0x01 #define GB_SPI_TYPE_MODE 0x02 #define GB_SPI_TYPE_FLAGS 0x03 #define GB_SPI_TYPE_BITS_PER_WORD_MASK 0x04 @@ -751,8 +735,6 @@ struct gb_spi_transfer_response { #define GB_SVC_VERSION_MINOR 0x01 /* Greybus SVC request types */ -#define GB_SVC_TYPE_INVALID 0x00 -#define GB_SVC_TYPE_PROTOCOL_VERSION 0x01 #define GB_SVC_TYPE_SVC_HELLO 0x02 #define GB_SVC_TYPE_INTF_DEVICE_ID 0x03 #define GB_SVC_TYPE_INTF_HOTPLUG 0x04 @@ -846,8 +828,6 @@ struct gb_raw_send_request { #define GB_UART_VERSION_MINOR 0x01 /* Greybus UART operation types */ -#define GB_UART_TYPE_INVALID 0x00 -#define GB_UART_TYPE_PROTOCOL_VERSION 0x01 #define GB_UART_TYPE_SEND_DATA 0x02 #define GB_UART_TYPE_RECEIVE_DATA 0x03 /* Unsolicited data */ #define GB_UART_TYPE_SET_LINE_CODING 0x04 @@ -919,8 +899,6 @@ struct gb_uart_serial_state_request { #define GB_LOOPBACK_VERSION_MINOR 0x01 /* Greybus loopback request types */ -#define GB_LOOPBACK_TYPE_INVALID 0x00 -#define GB_LOOPBACK_TYPE_PROTOCOL_VERSION 0x01 #define GB_LOOPBACK_TYPE_PING 0x02 #define GB_LOOPBACK_TYPE_TRANSFER 0x03 #define GB_LOOPBACK_TYPE_SINK 0x04 @@ -940,8 +918,6 @@ struct gb_loopback_transfer_response { #define GB_SDIO_VERSION_MINOR 0x01 /* Greybus SDIO operation types */ -#define GB_SDIO_TYPE_INVALID 0x00 -#define GB_SDIO_TYPE_PROTOCOL_VERSION 0x01 #define GB_SDIO_TYPE_GET_CAPABILITIES 0x02 #define GB_SDIO_TYPE_SET_IOS 0x03 #define GB_SDIO_TYPE_COMMAND 0x04 diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 852b6beaecac..cf525f8f942a 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -319,7 +319,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) /* By convention, the AP initiates the version operation */ switch (type) { - case GB_LOOPBACK_TYPE_PROTOCOL_VERSION: + case GB_REQUEST_TYPE_PROTOCOL_VERSION: dev_err(&connection->dev, "module-initiated version operation\n"); return -EINVAL; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5ff7cf3b2f0b..452f81b61203 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -452,7 +452,7 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) static int gb_svc_request_recv(u8 type, struct gb_operation *op) { switch (type) { - case GB_SVC_TYPE_PROTOCOL_VERSION: + case GB_REQUEST_TYPE_PROTOCOL_VERSION: return gb_svc_version_request(op); case GB_SVC_TYPE_SVC_HELLO: return gb_svc_hello(op); -- cgit v1.2.3-59-g8ed1b From 21e3a3ed9b8257e8e885458eb7573385c7e4ce26 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Aug 2015 08:12:30 +0530 Subject: greybus: operation: print message type on errors This can be very useful debug information, print it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 4e9c4a896365..0bb5b8d9cb3b 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -235,8 +235,8 @@ static void gb_operation_request_handle(struct gb_operation *operation) ret = gb_operation_response_send(operation, status); if (ret) { dev_err(&operation->connection->dev, - "failed to send response %d: %d\n", - status, ret); + "failed to send response %d for type 0x%02hhx: %d\n", + status, operation->type, ret); return; } } @@ -794,7 +794,8 @@ void greybus_message_sent(struct greybus_host_device *hd, if (message == operation->response) { if (status) { dev_err(&connection->dev, - "error sending response: %d\n", status); + "error sending response type 0x%02hhx: %d\n", + operation->type, status); } gb_operation_put_active(operation); gb_operation_put(operation); @@ -867,8 +868,8 @@ static void gb_connection_recv_response(struct gb_connection *connection, message = operation->response; message_size = sizeof(*message->header) + message->payload_size; if (!errno && size != message_size) { - dev_err(&connection->dev, "bad message size (%zu != %zu)\n", - size, message_size); + dev_err(&connection->dev, "bad message (0x%02hhx) size (%zu != %zu)\n", + size, message_size, message->header->type); errno = -EMSGSIZE; } @@ -913,8 +914,9 @@ void gb_connection_recv(struct gb_connection *connection, msg_size = le16_to_cpu(header.size); if (size < msg_size) { dev_err(&connection->dev, - "incomplete message received: 0x%04x (%zu < %zu)\n", - le16_to_cpu(header.operation_id), size, msg_size); + "incomplete message received for type 0x%02hhx: 0x%04x (%zu < %zu)\n", + header.type, le16_to_cpu(header.operation_id), size, + msg_size); return; /* XXX Should still complete operation */ } @@ -1019,8 +1021,8 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { - dev_err(&connection->dev, "synchronous operation failed: %d\n", - ret); + dev_err(&connection->dev, "synchronous operation failed: 0x%02hhx (%d)\n", + type, ret); } else { if (response_size) { memcpy(response, operation->response->payload, -- cgit v1.2.3-59-g8ed1b From dc4a10693f3f0e6658b06dd1947643637c0476a4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Aug 2015 17:07:10 +0530 Subject: greybus: loopback: Send some sensible data Signed-off-by: Viresh Kumar Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index cf525f8f942a..39ad4141ba8c 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -273,6 +273,8 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) return -ENOMEM; } + memset(request->data, 0x5A, len); + request->len = cpu_to_le32(len); do_gettimeofday(&ts); -- cgit v1.2.3-59-g8ed1b From dc366f8e397c41ba582821e24abdc2bc4ae80a47 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 14 Aug 2015 17:07:11 +0530 Subject: greybus: loopback: Print error on data mismatch Signed-off-by: Viresh Kumar Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 39ad4141ba8c..3263d8351aa3 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -287,8 +287,10 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) if (retval) goto gb_error; - if (memcmp(request->data, response->data, len)) + if (memcmp(request->data, response->data, len)) { + pr_info("%s: Loopback Data doesn't match\n", __func__); retval = -EREMOTEIO; + } gb_error: kfree(request); -- cgit v1.2.3-59-g8ed1b From 13fcfbb6c5bbd7aee7e9980313dc29469357c5cd Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Fri, 14 Aug 2015 13:58:18 +0100 Subject: greybus: makefile: add needed config options for lights Add a function to check kernel versions and append the necessary options to support LEDS_CLASS, LEDS_CLASS_FLASH and V4L2_FLASH_LED_CLASS depending of the kernel version. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 11627035222c..3006edef7e7b 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -49,13 +49,26 @@ INSTALL_MOD_PATH ?= /.. PWD := $(shell pwd) # kernel config option that shall be enable -CONFIG_OPTIONS_ENABLE := SYSFS SPI USB SND_SOC MMC +CONFIG_OPTIONS_ENABLE := SYSFS SPI USB SND_SOC MMC LEDS_CLASS # kernel config option that shall be disable CONFIG_OPTIONS_DISABLE := # this only run in kbuild part of the makefile ifneq ($(KERNELRELEASE),) +# This function returns the argument version if current kernel version is minor +# than the passed version, return 1 if equal or the current kernel version if it +# is greater than argument version. +kvers_cmp=$(shell [[ "$(KERNELVERSION)" == "$(1)" ]] && echo 1 || echo -e "$(1)\n$(KERNELVERSION)" | sort -V | tail -1) + +ifneq ($(call kvers_cmp,"3.19.0"),3.19.0) + CONFIG_OPTIONS_ENABLE += LEDS_CLASS_FLASH +endif + +ifneq ($(call kvers_cmp,"4.2.0"),4.2.0) + CONFIG_OPTIONS_ENABLE += V4L2_FLASH_LED_CLASS +endif + $(foreach opt,$(CONFIG_OPTIONS_ENABLE),$(if $(CONFIG_$(opt)),, \ $(error CONFIG_$(opt) is disabled in the kernel configuration and must be enable \ to continue compilation))) -- cgit v1.2.3-59-g8ed1b From 2870b52bae4c81823ffcb3ed2b0626fb39d64f48 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Fri, 14 Aug 2015 13:58:19 +0100 Subject: greybus: lights: add lights implementation This patch adds lights implementation for Greybus Lights class, it allows multiplexing of lights devices using the same connection. Also adds two sysfs entries to led class (color, fade) which are commonly used in several existing LED devices. It support 2 major class of devices (normal LED and flash type), for the first it registers to led_classdev, for the latest it registers in the led_classdev_flash and v4l2_flash, depending on the support of the kernel version. Each Module can have N light devices attach and each light can have multiple channel associated: glights |->light0 | |->channel0 | |->channel1 | | .... | |->channeln |->... |->lightn |->channel0 |->channel1 | .... |->channeln Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/greybus_protocols.h | 176 ++++ drivers/staging/greybus/kernel_ver.h | 41 + drivers/staging/greybus/light.c | 1201 +++++++++++++++++++++++++++ 4 files changed, 1420 insertions(+) create mode 100644 drivers/staging/greybus/light.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 3006edef7e7b..5c29b63c6c28 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -30,6 +30,7 @@ gb-phy-y := gpbridge.o \ gb-vibrator-y := vibrator.o gb-battery-y := battery.o gb-loopback-y := loopback.o +gb-light-y := light.o gb-raw-y := raw.o gb-es1-y := es1.o gb-es2-y := es2.o @@ -39,6 +40,7 @@ obj-m += gb-phy.o obj-m += gb-vibrator.o obj-m += gb-battery.o obj-m += gb-loopback.o +obj-m += gb-light.o obj-m += gb-raw.o obj-m += gb-es1.o obj-m += gb-es2.o diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index a80c2f253533..32b927f901f7 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1067,5 +1067,181 @@ struct gb_sdio_event_request { #define GB_SDIO_WP 0x04 }; +/* Lights */ + +#define GB_LIGHTS_VERSION_MAJOR 0x00 +#define GB_LIGHTS_VERSION_MINOR 0x01 + +/* Greybus Lights request types */ +#define GB_LIGHTS_TYPE_INVALID 0x00 +#define GB_LIGHTS_TYPE_PROTOCOL_VERSION 0x01 +#define GB_LIGHTS_TYPE_GET_LIGHTS 0x02 +#define GB_LIGHTS_TYPE_GET_LIGHT_CONFIG 0x03 +#define GB_LIGHTS_TYPE_GET_CHANNEL_CONFIG 0x04 +#define GB_LIGHTS_TYPE_GET_CHANNEL_FLASH_CONFIG 0x05 +#define GB_LIGHTS_TYPE_SET_BRIGHTNESS 0x06 +#define GB_LIGHTS_TYPE_SET_BLINK 0x07 +#define GB_LIGHTS_TYPE_SET_COLOR 0x08 +#define GB_LIGHTS_TYPE_SET_FADE 0x09 +#define GB_LIGHTS_TYPE_EVENT 0x0A +#define GB_LIGHTS_TYPE_SET_FLASH_INTENSITY 0x0B +#define GB_LIGHTS_TYPE_SET_FLASH_STROBE 0x0C +#define GB_LIGHTS_TYPE_SET_FLASH_TIMEOUT 0x0D +#define GB_LIGHTS_TYPE_GET_FLASH_FAULT 0x0E + +/* Greybus Light modes */ + +/* + * if you add any specific mode below, update also the + * GB_CHANNEL_MODE_DEFINED_RANGE value accordingly + */ +#define GB_CHANNEL_MODE_NONE 0x00000000 +#define GB_CHANNEL_MODE_BATTERY 0x00000001 +#define GB_CHANNEL_MODE_POWER 0x00000002 +#define GB_CHANNEL_MODE_WIRELESS 0x00000004 +#define GB_CHANNEL_MODE_BLUETOOTH 0x00000008 +#define GB_CHANNEL_MODE_KEYBOARD 0x00000010 +#define GB_CHANNEL_MODE_BUTTONS 0x00000020 +#define GB_CHANNEL_MODE_NOTIFICATION 0x00000040 +#define GB_CHANNEL_MODE_ATTENTION 0x00000080 +#define GB_CHANNEL_MODE_FLASH 0x00000100 +#define GB_CHANNEL_MODE_TORCH 0x00000200 +#define GB_CHANNEL_MODE_INDICATOR 0x00000400 + +/* Lights Mode valid bit values */ +#define GB_CHANNEL_MODE_DEFINED_RANGE 0x000004FF +#define GB_CHANNEL_MODE_VENDOR_RANGE 0x00F00000 + +/* Greybus Light Channels Flags */ +#define GB_LIGHT_CHANNEL_MULTICOLOR 0x00000001 +#define GB_LIGHT_CHANNEL_FADER 0x00000002 +#define GB_LIGHT_CHANNEL_BLINK 0x00000004 + +/* get count of lights in module */ +struct gb_lights_get_lights_response { + __u8 lights_count; +}; + +/* light config request payload */ +struct gb_lights_get_light_config_request { + __u8 id; +}; + +/* light config response payload */ +struct gb_lights_get_light_config_response { + __u8 channel_count; + __u8 name[32]; +}; + +/* channel config request payload */ +struct gb_lights_get_channel_config_request { + __u8 light_id; + __u8 channel_id; +}; + +/* channel flash config request payload */ +struct gb_lights_get_channel_flash_config_request { + __u8 light_id; + __u8 channel_id; +}; + +/* channel config response payload */ +struct gb_lights_get_channel_config_response { + __u8 max_brightness; + __le32 flags; + __le32 color; + __u8 color_name[32]; + __le32 mode; + __u8 mode_name[32]; +} __packed; + +/* channel flash config response payload */ +struct gb_lights_get_channel_flash_config_response { + __le32 intensity_min_uA; + __le32 intensity_max_uA; + __le32 intensity_step_uA; + __le32 timeout_min_us; + __le32 timeout_max_us; + __le32 timeout_step_us; +}; + +/* blink request payload: response have no payload */ +struct gb_lights_blink_request { + __u8 light_id; + __u8 channel_id; + __le16 time_on_ms; + __le16 time_off_ms; +}; + +/* set brightness request payload: response have no payload */ +struct gb_lights_set_brightness_request { + __u8 light_id; + __u8 channel_id; + __u8 brightness; +}; + +/* set color request payload: response have no payload */ +struct gb_lights_set_color_request { + __u8 light_id; + __u8 channel_id; + __le32 color; +} __packed; + +/* set fade request payload: response have no payload */ +struct gb_lights_set_fade_request { + __u8 light_id; + __u8 channel_id; + __u8 fade_in; + __u8 fade_out; +}; + +/* event request: generated by module */ +struct gb_lights_event_request { + __u8 light_id; + __u8 event; +#define GB_LIGHTS_LIGHT_CONFIG 0x01 +}; + +/* set flash intensity request payload: response have no payload */ +struct gb_lights_set_flash_intensity_request { + __u8 light_id; + __u8 channel_id; + __le32 intensity_uA; +} __packed; + +/* set flash strobe state request payload: response have no payload */ +struct gb_lights_set_flash_strobe_request { + __u8 light_id; + __u8 channel_id; + __u8 state; +}; + +/* set flash timeout request payload: response have no payload */ +struct gb_lights_set_flash_timeout_request { + __u8 light_id; + __u8 channel_id; + __le32 timeout_us; +} __packed; + +/* get flash fault request payload */ +struct gb_lights_get_flash_fault_request { + __u8 light_id; + __u8 channel_id; +}; + +/* get flash fault response payload */ +struct gb_lights_get_flash_fault_response { + __le32 fault; +#define GB_LIGHTS_FLASH_FAULT_OVER_VOLTAGE 0x00000000 +#define GB_LIGHTS_FLASH_FAULT_TIMEOUT 0x00000001 +#define GB_LIGHTS_FLASH_FAULT_OVER_TEMPERATURE 0x00000002 +#define GB_LIGHTS_FLASH_FAULT_SHORT_CIRCUIT 0x00000004 +#define GB_LIGHTS_FLASH_FAULT_OVER_CURRENT 0x00000008 +#define GB_LIGHTS_FLASH_FAULT_INDICATOR 0x00000010 +#define GB_LIGHTS_FLASH_FAULT_UNDER_VOLTAGE 0x00000020 +#define GB_LIGHTS_FLASH_FAULT_INPUT_VOLTAGE 0x00000040 +#define GB_LIGHTS_FLASH_FAULT_LED_OVER_TEMPERATURE 0x00000080 +}; + #endif /* __GREYBUS_PROTOCOLS_H */ diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index d0e05e656475..f3cb1895c092 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -248,4 +248,45 @@ static inline size_t sg_pcopy_from_buffer(struct scatterlist *sgl, list_entry((ptr)->prev, type, member) #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) +/* + * Before this version the led classdev did not support groups + */ +#define LED_HAVE_GROUPS +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +/* + * At this time the internal API for the set brightness was changed to the async + * version, and one sync API was added to handle cases that need immediate + * effect. Also, the led class flash and lock for sysfs access was introduced. + */ +#define LED_HAVE_SET_SYNC +#define LED_HAVE_FLASH +#define LED_HAVE_LOCK +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) +/* + * From this version upper it was introduced the possibility to disable led + * sysfs entries to handle control of the led device to v4l2, which was + * implemented later. So, before that this should return false. + */ +#include +static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev) +{ + return false; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) +/* + * New helper functions for registering/unregistering flash led devices as v4l2 + * subdevices were added. + */ +#define V4L2_HAVE_FLASH +#include +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c new file mode 100644 index 000000000000..e8ef7f5c37a0 --- /dev/null +++ b/drivers/staging/greybus/light.c @@ -0,0 +1,1201 @@ +/* + * Greybus Lights protocol driver. + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include + +#include "greybus.h" +#include "greybus_protocols.h" + +#define NAMES_MAX 32 + +struct gb_channel { + u8 id; + u32 flags; + u32 color; + char *color_name; + u8 fade_in; + u8 fade_out; + u32 mode; + char *mode_name; + struct attribute **attrs; + struct attribute_group *attr_group; + const struct attribute_group **attr_groups; + struct work_struct work_brightness_set; + struct led_classdev *led; +#ifdef LED_HAVE_FLASH + struct led_classdev_flash fled; + struct led_flash_setting intensity_uA; + struct led_flash_setting timeout_us; +#else + struct led_classdev cled; +#endif + struct gb_light *light; + bool is_registered; + bool releasing; + bool strobe_state; +}; + +struct gb_light { + u8 id; + char *name; + struct gb_lights *glights; + u32 flags; + u8 channels_count; + struct gb_channel *channels; + bool has_flash; +#ifdef V4L2_HAVE_FLASH + struct v4l2_flash *v4l2_flash; +#endif +}; + +struct gb_lights { + struct gb_connection *connection; + u8 lights_count; + struct gb_light *lights; + struct mutex lights_lock; +}; + +static void gb_lights_channel_free(struct gb_channel *channel); + +static struct gb_connection *get_conn_from_channel(struct gb_channel *channel) +{ + return channel->light->glights->connection; +} + +static struct gb_connection *get_conn_from_light(struct gb_light *light) +{ + return light->glights->connection; +} + +static bool is_channel_flash(struct gb_channel *channel) +{ + return !!(channel->mode & (GB_CHANNEL_MODE_FLASH | GB_CHANNEL_MODE_TORCH + | GB_CHANNEL_MODE_INDICATOR)); +} + +#ifdef LED_HAVE_FLASH +static struct gb_channel *get_channel_from_cdev(struct led_classdev *cdev) +{ + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev); + + return container_of(fled_cdev, struct gb_channel, fled); +} + +static struct led_classdev *get_channel_cdev(struct gb_channel *channel) +{ + return &channel->fled.led_cdev; +} + +static struct gb_channel *get_channel_from_mode(struct gb_light *light, + u32 mode) +{ + struct gb_channel *channel = NULL; + int i; + + for (i = 0; i < light->channels_count; i++) { + channel = &light->channels[i]; + if (channel && channel->mode == mode) + break; + } + return channel; +} + +static int __gb_lights_flash_intensity_set(struct gb_channel *channel, + u32 intensity) +{ + struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_lights_set_flash_intensity_request req; + + if (channel->releasing) + return -ESHUTDOWN; + + req.light_id = channel->light->id; + req.channel_id = channel->id; + req.intensity_uA = cpu_to_le32(intensity); + + return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_INTENSITY, + &req, sizeof(req), NULL, 0); +} + +static int __gb_lights_flash_brightness_set(struct gb_channel *channel) +{ + u32 intensity; + + /* If the channel is flash we need to get the attached torch channel */ + if (channel->mode & GB_CHANNEL_MODE_FLASH) + channel = get_channel_from_mode(channel->light, + GB_CHANNEL_MODE_TORCH); + + /* For not flash we need to convert brightness to intensity */ + intensity = channel->intensity_uA.min + + (channel->intensity_uA.step * channel->led->brightness); + + return __gb_lights_flash_intensity_set(channel, intensity); +} +#else /* LED_HAVE_FLASH */ +static struct gb_channel *get_channel_from_cdev(struct led_classdev *cdev) +{ + return container_of(cdev, struct gb_channel, cled); +} + +static struct led_classdev *get_channel_cdev(struct gb_channel *channel) +{ + return &channel->cled; +} + +static int __gb_lights_flash_brightness_set(struct gb_channel *channel) +{ + return 0; +} +#endif /* !LED_HAVE_FLASH */ + +#ifdef LED_HAVE_GROUPS +static int gb_lights_color_set(struct gb_channel *channel, u32 color); +static int gb_lights_fade_set(struct gb_channel *channel); + +#ifdef LED_HAVE_LOCK +static void led_lock(struct led_classdev *cdev) +{ + mutex_lock(&cdev->led_access); +} + +static void led_unlock(struct led_classdev *cdev) +{ + mutex_unlock(&cdev->led_access); +} +#else +static void led_lock(struct led_classdev *cdev) +{ +} + +static void led_unlock(struct led_classdev *cdev) +{ +} +#endif /* !LED_HAVE_LOCK */ + +#define gb_lights_fade_attr(__dir) \ +static ssize_t fade_##__dir##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct led_classdev *cdev = dev_get_drvdata(dev); \ + struct gb_channel *channel = get_channel_from_cdev(cdev); \ + \ + return sprintf(buf, "%u\n", channel->fade_##__dir); \ +} \ + \ +static ssize_t fade_##__dir##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + struct led_classdev *cdev = dev_get_drvdata(dev); \ + struct gb_channel *channel = get_channel_from_cdev(cdev); \ + u8 fade; \ + int ret; \ + \ + led_lock(cdev); \ + if (led_sysfs_is_disabled(cdev)) { \ + ret = -EBUSY; \ + goto unlock; \ + } \ + \ + ret = kstrtou8(buf, 0, &fade); \ + if (ret < 0) { \ + dev_err(dev, "could not parse fade value %d\n", ret); \ + goto unlock; \ + } \ + if (channel->fade_##__dir == fade) \ + goto unlock; \ + channel->fade_##__dir = fade; \ + \ + ret = gb_lights_fade_set(channel); \ + if (ret < 0) \ + goto unlock; \ + \ + ret = size; \ +unlock: \ + led_unlock(cdev); \ + return ret; \ +} \ +static DEVICE_ATTR_RW(fade_##__dir) + +gb_lights_fade_attr(in); +gb_lights_fade_attr(out); + +static ssize_t color_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct gb_channel *channel = get_channel_from_cdev(cdev); + + return sprintf(buf, "0x%08x\n", channel->color); +} + +static ssize_t color_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct gb_channel *channel = get_channel_from_cdev(cdev); + u32 color; + int ret; + + led_lock(cdev); + if (led_sysfs_is_disabled(cdev)) { + ret = -EBUSY; + goto unlock; + } + ret = kstrtou32(buf, 0, &color); + if (ret < 0) { + dev_err(dev, "could not parse color value %d\n", ret); + goto unlock; + } + + ret = gb_lights_color_set(channel, color); + if (ret < 0) + goto unlock; + + channel->color = color; + ret = size; +unlock: + led_unlock(cdev); + return ret; +} +static DEVICE_ATTR_RW(color); + +static int channel_attr_groups_set(struct gb_channel *channel, + struct led_classdev *cdev) +{ + int attr = 0; + int size = 0; + + if (channel->flags & GB_LIGHT_CHANNEL_MULTICOLOR) + size++; + if (channel->flags & GB_LIGHT_CHANNEL_FADER) + size++; + + if (!size) + return 0; + + /* Set attributes based in the channel flags */ + channel->attrs = kcalloc(size, sizeof(**channel->attrs), GFP_KERNEL); + if (!channel->attrs) + return -ENOMEM; + channel->attr_group = kcalloc(1, sizeof(*channel->attr_group), + GFP_KERNEL); + if (!channel->attr_group) + return -ENOMEM; + channel->attr_groups = kcalloc(2, sizeof(*channel->attr_groups), + GFP_KERNEL); + if (!channel->attr_groups) + return -ENOMEM; + + if (channel->flags & GB_LIGHT_CHANNEL_MULTICOLOR) + channel->attrs[attr++] = &dev_attr_color.attr; + if (channel->flags & GB_LIGHT_CHANNEL_FADER) { + channel->attrs[attr++] = &dev_attr_fade_in.attr; + channel->attrs[attr++] = &dev_attr_fade_out.attr; + } + + channel->attr_group->attrs = channel->attrs; + + channel->attr_groups[0] = channel->attr_group; + + cdev->groups = channel->attr_groups; + + return 0; +} + +static int gb_lights_fade_set(struct gb_channel *channel) +{ + struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_lights_set_fade_request req; + + if (channel->releasing) + return -ESHUTDOWN; + + req.light_id = channel->light->id; + req.channel_id = channel->id; + req.fade_in = channel->fade_in; + req.fade_out = channel->fade_out; + return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FADE, + &req, sizeof(req), NULL, 0); +} + +static int gb_lights_color_set(struct gb_channel *channel, u32 color) +{ + struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_lights_set_color_request req; + + if (channel->releasing) + return -ESHUTDOWN; + + req.light_id = channel->light->id; + req.channel_id = channel->id; + req.color = cpu_to_le32(color); + return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_COLOR, + &req, sizeof(req), NULL, 0); +} +#else /* LED_HAVE_GROUPS */ +static int channel_attr_groups_set(struct gb_channel *channel, + struct led_classdev *cdev) +{ + return 0; +} +#endif /* !LED_HAVE_GROUPS */ + +static int __gb_lights_led_brightness_set(struct gb_channel *channel) +{ + struct gb_lights_set_brightness_request req; + struct gb_connection *connection = get_conn_from_channel(channel); + + req.light_id = channel->light->id; + req.channel_id = channel->id; + req.brightness = (u8)channel->led->brightness; + + return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BRIGHTNESS, + &req, sizeof(req), NULL, 0); +} + +static int __gb_lights_brightness_set(struct gb_channel *channel) +{ + int ret; + + if (channel->releasing) + return 0; + + if (is_channel_flash(channel)) + ret = __gb_lights_flash_brightness_set(channel); + else + ret = __gb_lights_led_brightness_set(channel); + + return ret; +} + +static void gb_brightness_set_work(struct work_struct *work) +{ + struct gb_channel *channel = container_of(work, struct gb_channel, + work_brightness_set); + + __gb_lights_brightness_set(channel); +} + +#ifdef LED_HAVE_SET_SYNC +static int gb_brightness_set_sync(struct led_classdev *cdev, + enum led_brightness value) +{ + struct gb_channel *channel = get_channel_from_cdev(cdev); + + channel->led->brightness = value; + + return __gb_lights_brightness_set(channel); +} +#endif + +static void gb_brightness_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct gb_channel *channel = get_channel_from_cdev(cdev); + + if (channel->releasing) + return; + + cdev->brightness = value; + schedule_work(&channel->work_brightness_set); +} + +static enum led_brightness gb_brightness_get(struct led_classdev *cdev) + +{ + struct gb_channel *channel = get_channel_from_cdev(cdev); + + return channel->led->brightness; +} + +static int gb_blink_set(struct led_classdev *cdev, unsigned long *delay_on, + unsigned long *delay_off) +{ + struct gb_channel *channel = get_channel_from_cdev(cdev); + struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_lights_blink_request req; + + if (channel->releasing) + return -ESHUTDOWN; + + req.light_id = channel->light->id; + req.channel_id = channel->id; + req.time_on_ms = cpu_to_le16(*delay_on); + req.time_off_ms = cpu_to_le16(*delay_off); + + return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BLINK, &req, + sizeof(req), NULL, 0); +} + +static void gb_lights_led_operations_set(struct gb_channel *channel, + struct led_classdev *cdev) +{ + cdev->brightness_set = gb_brightness_set; + cdev->brightness_get = gb_brightness_get; +#ifdef LED_HAVE_SET_SYNC + cdev->brightness_set_sync = gb_brightness_set_sync; +#endif + INIT_WORK(&channel->work_brightness_set, gb_brightness_set_work); + + if (channel->flags & GB_LIGHT_CHANNEL_BLINK) + cdev->blink_set = gb_blink_set; +} + +#ifdef V4L2_HAVE_FLASH +/* V4L2 specific helpers */ +static void __gb_lights_channel_v4l2_config(struct led_flash_setting *channel_s, + struct led_flash_setting *v4l2_s) +{ + v4l2_s->min = channel_s->min; + v4l2_s->max = channel_s->max; + v4l2_s->step = channel_s->step; + /* For v4l2 val is the default value */ + v4l2_s->val = channel_s->max; +} + +static int gb_lights_light_v4l2_register(struct gb_light *light) +{ + struct gb_connection *connection = get_conn_from_light(light); + struct device *dev = &connection->dev; + struct v4l2_flash_config *sd_cfg; + struct led_classdev_flash *fled; + struct led_classdev_flash *iled = NULL; + struct gb_channel *channel_torch, *channel_ind, *channel_flash; + int ret = 0; + + sd_cfg = kcalloc(1, sizeof(*sd_cfg), GFP_KERNEL); + if (!sd_cfg) + return -ENOMEM; + + channel_torch = get_channel_from_mode(light, GB_CHANNEL_MODE_TORCH); + if (channel_torch) + __gb_lights_channel_v4l2_config(&channel_torch->intensity_uA, + &sd_cfg->torch_intensity); + + channel_ind = get_channel_from_mode(light, GB_CHANNEL_MODE_INDICATOR); + if (channel_ind) { + __gb_lights_channel_v4l2_config(&channel_ind->intensity_uA, + &sd_cfg->indicator_intensity); + iled = &channel_ind->fled; + } + + channel_flash = get_channel_from_mode(light, GB_CHANNEL_MODE_FLASH); + WARN_ON(!channel_flash); + + fled = &channel_flash->fled; + + snprintf(sd_cfg->dev_name, sizeof(sd_cfg->dev_name), "%s", light->name); + + /* Set the possible values to faults, in our case all faults */ + sd_cfg->flash_faults = LED_FAULT_OVER_VOLTAGE | LED_FAULT_TIMEOUT | + LED_FAULT_OVER_TEMPERATURE | LED_FAULT_SHORT_CIRCUIT | + LED_FAULT_OVER_CURRENT | LED_FAULT_INDICATOR | + LED_FAULT_UNDER_VOLTAGE | LED_FAULT_INPUT_VOLTAGE | + LED_FAULT_LED_OVER_TEMPERATURE; + + light->v4l2_flash = v4l2_flash_init(dev, NULL, fled, iled, + NULL, sd_cfg); + if (IS_ERR_OR_NULL(light->v4l2_flash)) { + ret = PTR_ERR(light->v4l2_flash); + goto out_free; + } + + return ret; + +out_free: + kfree(sd_cfg); + return ret; +} + +static void gb_lights_light_v4l2_unregister(struct gb_light *light) +{ + v4l2_flash_release(light->v4l2_flash); +} +#else +static int gb_lights_light_v4l2_register(struct gb_light *light) +{ + struct gb_connection *connection = get_conn_from_light(light); + + dev_err(&connection->dev, "no support for v4l2 subdevices\n"); + return 0; +} + +static void gb_lights_light_v4l2_unregister(struct gb_light *light) +{ +} +#endif + +#ifdef LED_HAVE_FLASH +/* Flash specific operations */ +static int gb_lights_flash_intensity_set(struct led_classdev_flash *fcdev, + u32 brightness) +{ + struct gb_channel *channel = container_of(fcdev, struct gb_channel, + fled); + int ret; + + ret = __gb_lights_flash_intensity_set(channel, brightness); + if (ret < 0) + return ret; + + fcdev->brightness.val = brightness; + + return 0; +} + +static int gb_lights_flash_intensity_get(struct led_classdev_flash *fcdev, + u32 *brightness) +{ + *brightness = fcdev->brightness.val; + + return 0; +} + +static int gb_lights_flash_strobe_set(struct led_classdev_flash *fcdev, + bool state) +{ + struct gb_channel *channel = container_of(fcdev, struct gb_channel, + fled); + struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_lights_set_flash_strobe_request req; + int ret; + + if (channel->releasing) + return -ESHUTDOWN; + + req.light_id = channel->light->id; + req.channel_id = channel->id; + req.state = state ? 1 : 0; + + ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_STROBE, + &req, sizeof(req), NULL, 0); + if (ret < 0) + return ret; + channel->strobe_state = state; + + return 0; +} + +static int gb_lights_flash_strobe_get(struct led_classdev_flash *fcdev, + bool *state) +{ + struct gb_channel *channel = container_of(fcdev, struct gb_channel, + fled); + + *state = channel->strobe_state; + return 0; +} + +static int gb_lights_flash_timeout_set(struct led_classdev_flash *fcdev, + u32 timeout) +{ + struct gb_channel *channel = container_of(fcdev, struct gb_channel, + fled); + struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_lights_set_flash_timeout_request req; + int ret; + + if (channel->releasing) + return -ESHUTDOWN; + + req.light_id = channel->light->id; + req.channel_id = channel->id; + req.timeout_us = cpu_to_le32(timeout); + + ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_TIMEOUT, + &req, sizeof(req), NULL, 0); + if (ret < 0) + return ret; + fcdev->timeout.val = timeout; + + return 0; +} + +static int gb_lights_flash_fault_get(struct led_classdev_flash *fcdev, + u32 *fault) +{ + struct gb_channel *channel = container_of(fcdev, struct gb_channel, + fled); + struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_lights_get_flash_fault_request req; + struct gb_lights_get_flash_fault_response resp; + int ret; + + if (channel->releasing) + return -ESHUTDOWN; + + req.light_id = channel->light->id; + req.channel_id = channel->id; + + ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_GET_FLASH_FAULT, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret < 0) + return ret; + + *fault = le32_to_cpu(resp.fault); + + return 0; +} + +static const struct led_flash_ops gb_lights_flash_ops = { + .flash_brightness_set = gb_lights_flash_intensity_set, + .flash_brightness_get = gb_lights_flash_intensity_get, + .strobe_set = gb_lights_flash_strobe_set, + .strobe_get = gb_lights_flash_strobe_get, + .timeout_set = gb_lights_flash_timeout_set, + .fault_get = gb_lights_flash_fault_get, +}; + +static int __gb_lights_channel_torch_attach(struct gb_channel *channel, + struct gb_channel *channel_torch) +{ + char *name; + + /* we can only attach torch to a flash channel */ + if (!(channel->mode & GB_CHANNEL_MODE_FLASH)) + return 0; + + /* Move torch brightness to the destination */ + channel->led->max_brightness = channel_torch->led->max_brightness; + + /* append mode name to flash name */ + name = kasprintf(GFP_KERNEL, "%s_%s", channel->led->name, + channel_torch->mode_name); + if (!name) + return -ENOMEM; + kfree(channel->led->name); + channel->led->name = name; + + /* free original torch channel resources */ + gb_lights_channel_free(channel_torch); + + channel_torch->led = channel->led; + + return 0; +} + +static int __gb_lights_flash_led_register(struct gb_channel *channel) +{ + struct gb_connection *connection = get_conn_from_channel(channel); + struct led_classdev_flash *fled = &channel->fled; + struct led_flash_setting *fset; + struct gb_channel *channel_torch; + int ret; + + fled->ops = &gb_lights_flash_ops; + + fled->led_cdev.flags |= LED_DEV_CAP_FLASH; + + fset = &fled->brightness; + fset->min = channel->intensity_uA.min; + fset->max = channel->intensity_uA.max; + fset->step = channel->intensity_uA.step; + + /* Only the flash mode have the timeout constraints settings */ + if (channel->mode & GB_CHANNEL_MODE_FLASH) { + fset = &fled->timeout; + fset->min = channel->timeout_us.min; + fset->max = channel->timeout_us.max; + fset->step = channel->timeout_us.step; + } + + /* + * If light have torch mode channel, this channel will be the led + * classdev of the registered above flash classdev + */ + channel_torch = get_channel_from_mode(channel->light, + GB_CHANNEL_MODE_TORCH); + if (channel_torch) { + ret = __gb_lights_channel_torch_attach(channel, channel_torch); + if (ret < 0) + goto fail; + } + + ret = led_classdev_flash_register(&connection->bundle->intf->dev, + fled); + if (ret < 0) + goto fail; + + channel->is_registered = true; + return 0; +fail: + channel->led = NULL; + return ret; +} + +static void __gb_lights_flash_led_unregister(struct gb_channel *channel) +{ + if (!channel->is_registered) + return; + + led_classdev_flash_unregister(&channel->fled); +} + +static int gb_lights_channel_flash_config(struct gb_channel *channel) +{ + struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_lights_get_channel_flash_config_request req; + struct gb_lights_get_channel_flash_config_response conf; + struct led_flash_setting *fset; + int ret; + + req.light_id = channel->light->id; + req.channel_id = channel->id; + + ret = gb_operation_sync(connection, + GB_LIGHTS_TYPE_GET_CHANNEL_FLASH_CONFIG, + &req, sizeof(req), &conf, sizeof(conf)); + if (ret < 0) + return ret; + + /* + * Intensity constraints for flash related modes: flash, torch, + * indicator. They will be needed for v4l2 registration. + */ + fset = &channel->intensity_uA; + fset->min = le32_to_cpu(conf.intensity_min_uA); + fset->max = le32_to_cpu(conf.intensity_max_uA); + fset->step = le32_to_cpu(conf.intensity_step_uA); + + /* + * On flash type, max brightness is set as the number of intensity steps + * available. + */ + channel->led->max_brightness = (fset->max - fset->min) / fset->step; + + /* Only the flash mode have the timeout constraints settings */ + if (channel->mode & GB_CHANNEL_MODE_FLASH) { + fset = &channel->timeout_us; + fset->min = le32_to_cpu(conf.timeout_min_us); + fset->max = le32_to_cpu(conf.timeout_max_us); + fset->step = le32_to_cpu(conf.timeout_step_us); + } + + return 0; +} +#else +static int gb_lights_channel_flash_config(struct gb_channel *channel) +{ + struct gb_connection *connection = get_conn_from_channel(channel); + + dev_err(&connection->dev, "no support for flash devices\n"); + return 0; +} + +static int __gb_lights_flash_led_register(struct gb_channel *channel) +{ + return 0; +} + +static void __gb_lights_flash_led_unregister(struct gb_channel *channel) +{ +} + +#endif /* LED_HAVE_FLASH */ + +static int __gb_lights_led_register(struct gb_channel *channel) +{ + struct gb_connection *connection = get_conn_from_channel(channel); + struct led_classdev *cdev = get_channel_cdev(channel); + int ret; + + ret = led_classdev_register(&connection->bundle->intf->dev, + cdev); + if (ret < 0) + channel->led = NULL; + else + channel->is_registered = true; + return ret; +} + +static int gb_lights_channel_register(struct gb_channel *channel) +{ + /* Normal LED channel, just register in led classdev and we are done */ + if (!is_channel_flash(channel)) + return __gb_lights_led_register(channel); + + /* + * Flash Type need more work, register flash classdev, indicator as + * flash classdev, torch will be led classdev of the flash classdev. + */ + if (!(channel->mode & GB_CHANNEL_MODE_TORCH)) + return __gb_lights_flash_led_register(channel); + + return 0; +} + +static void __gb_lights_led_unregister(struct gb_channel *channel) +{ + struct led_classdev *cdev = get_channel_cdev(channel); + + if (!channel->is_registered) + return; + + led_classdev_unregister(cdev); + channel->led = NULL; +} + +static void gb_lights_channel_unregister(struct gb_channel *channel) +{ + /* The same as register, handle channels differently */ + if (!is_channel_flash(channel)) { + __gb_lights_led_unregister(channel); + return; + } + + if (channel->mode & GB_CHANNEL_MODE_TORCH) + __gb_lights_led_unregister(channel); + else + __gb_lights_flash_led_unregister(channel); +} + +static int gb_lights_channel_config(struct gb_light *light, + struct gb_channel *channel) +{ + struct gb_lights_get_channel_config_response conf; + struct gb_lights_get_channel_config_request req; + struct gb_connection *connection = get_conn_from_light(light); + struct led_classdev *cdev = get_channel_cdev(channel); + char *name; + int ret; + + req.light_id = light->id; + req.channel_id = channel->id; + + ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_GET_CHANNEL_CONFIG, + &req, sizeof(req), &conf, sizeof(conf)); + if (ret < 0) + return ret; + + channel->light = light; + channel->mode = le32_to_cpu(conf.mode); + channel->flags = le32_to_cpu(conf.flags); + channel->color = le32_to_cpu(conf.color); + channel->color_name = kstrndup(conf.color_name, NAMES_MAX, GFP_KERNEL); + if (!channel->color_name) + return -ENOMEM; + channel->mode_name = kstrndup(conf.mode_name, NAMES_MAX, GFP_KERNEL); + if (!channel->mode_name) + return -ENOMEM; + + channel->led = cdev; + + name = kasprintf(GFP_KERNEL, "%s:%s:%s", light->name, + channel->color_name, channel->mode_name); + if (!name) + return -ENOMEM; + + cdev->name = name; + + cdev->max_brightness = conf.max_brightness; + + ret = channel_attr_groups_set(channel, cdev); + if (ret < 0) + return ret; + + gb_lights_led_operations_set(channel, cdev); + + /* + * If it is not a flash related channel (flash, torch or indicator) we + * are done here. If not, continue and fetch flash related + * configurations. + */ + if (!is_channel_flash(channel)) + return ret; + + light->has_flash = true; + + ret = gb_lights_channel_flash_config(channel); + if (ret < 0) + return ret; + + return ret; +} + +static int gb_lights_light_config(struct gb_lights *glights, u8 id) +{ + struct gb_light *light = &glights->lights[id]; + struct gb_lights_get_light_config_request req; + struct gb_lights_get_light_config_response conf; + int ret; + int i; + + light->glights = glights; + light->id = id; + + req.id = id; + + ret = gb_operation_sync(glights->connection, + GB_LIGHTS_TYPE_GET_LIGHT_CONFIG, + &req, sizeof(req), &conf, sizeof(conf)); + if (ret < 0) + return ret; + + if (!conf.channel_count) + return -EINVAL; + if (!strlen(conf.name)) + return -EINVAL; + + light->channels_count = conf.channel_count; + light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); + + light->channels = kzalloc(light->channels_count * + sizeof(struct gb_channel), GFP_KERNEL); + if (!light->channels) + return -ENOMEM; + + /* First we collect all the configurations for all channels */ + for (i = 0; i < light->channels_count; i++) { + light->channels[i].id = i; + ret = gb_lights_channel_config(light, &light->channels[i]); + if (ret < 0) + return ret; + } + + /* + * Then, if everything went ok in getting configurations, we register + * the classdev, flash classdev and v4l2 subsystem, if a flash device is + * found. + */ + for (i = 0; i < light->channels_count; i++) { + ret = gb_lights_channel_register(&light->channels[i]); + if (ret < 0) + return ret; + } + + if (light->has_flash) { + ret = gb_lights_light_v4l2_register(light); + if (ret < 0) + return ret; + } + + return 0; +} + +static void gb_lights_channel_free(struct gb_channel *channel) +{ + if (&channel->work_brightness_set) + flush_work(&channel->work_brightness_set); + kfree(channel->attrs); + kfree(channel->attr_group); + kfree(channel->attr_groups); + kfree(channel->color_name); + kfree(channel->mode_name); +} + +static void gb_lights_channel_release(struct gb_channel *channel) +{ + if (!channel) + return; + + channel->releasing = true; + + gb_lights_channel_unregister(channel); + + gb_lights_channel_free(channel); +} + +static void gb_lights_light_release(struct gb_light *light) +{ + int i; + int count; + + if (!light) + return; + + count = light->channels_count; + + if (light->has_flash) + gb_lights_light_v4l2_unregister(light); + + for (i = 0; i < count; i++) { + gb_lights_channel_release(&light->channels[i]); + light->channels_count--; + } + kfree(light->channels); + kfree(light->name); +} + +static void gb_lights_release(struct gb_lights *glights) +{ + int i; + + if (!glights) + return; + + mutex_lock(&glights->lights_lock); + if (!glights->lights) + goto free_glights; + + for (i = 0; i < glights->lights_count; i++) + gb_lights_light_release(&glights->lights[i]); + + kfree(glights->lights); + +free_glights: + mutex_unlock(&glights->lights_lock); + mutex_destroy(&glights->lights_lock); + kfree(glights); +} + +static int gb_lights_get_count(struct gb_lights *glights) +{ + struct gb_lights_get_lights_response resp; + int ret; + + ret = gb_operation_sync(glights->connection, GB_LIGHTS_TYPE_GET_LIGHTS, + NULL, 0, &resp, sizeof(resp)); + if (ret < 0) + return ret; + + if (!resp.lights_count) + return -EINVAL; + + glights->lights_count = resp.lights_count; + + return 0; +} + +static int gb_lights_setup(struct gb_lights *glights) +{ + struct gb_connection *connection = glights->connection; + int ret; + int i; + + mutex_lock(&glights->lights_lock); + ret = gb_lights_get_count(glights); + if (ret < 0) + goto out; + + glights->lights = kzalloc(glights->lights_count * + sizeof(struct gb_light), GFP_KERNEL); + if (!glights->lights) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < glights->lights_count; i++) { + ret = gb_lights_light_config(glights, i); + if (ret < 0) { + dev_err(&connection->dev, + "Fail to configure lights device\n"); + goto out; + } + } + +out: + mutex_unlock(&glights->lights_lock); + return ret; +} + +static int gb_lights_event_recv(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_lights *glights = connection->private; + struct gb_message *request; + struct gb_lights_event_request *payload; + int ret = 0; + u8 light_id; + u8 event; + + if (type != GB_LIGHTS_TYPE_EVENT) { + dev_err(&connection->dev, + "Unsupported unsolicited event: %u\n", type); + return -EINVAL; + } + + request = op->request; + + if (request->payload_size < sizeof(*payload)) { + dev_err(&connection->dev, + "Wrong event size received (%zu < %zu)\n", + request->payload_size, sizeof(*payload)); + return -EINVAL; + } + + payload = request->payload; + light_id = payload->light_id; + + if (light_id >= glights->lights_count || !&glights->lights[light_id]) { + dev_err(&connection->dev, + "Event received for unconfigured light id: %d\n", + light_id); + return -EINVAL; + } + + event = payload->event; + + if (event & GB_LIGHTS_LIGHT_CONFIG) { + mutex_lock(&glights->lights_lock); + gb_lights_light_release(&glights->lights[light_id]); + ret = gb_lights_light_config(glights, light_id); + if (ret < 0) + gb_lights_light_release(&glights->lights[light_id]); + mutex_unlock(&glights->lights_lock); + } + + return ret; +} + +static int gb_lights_connection_init(struct gb_connection *connection) +{ + struct gb_lights *glights; + int ret; + + glights = kzalloc(sizeof(*glights), GFP_KERNEL); + if (!glights) + return -ENOMEM; + + glights->connection = connection; + connection->private = glights; + + mutex_init(&glights->lights_lock); + + /* + * Setup all the lights devices over this connection, if anything goes + * wrong tear down all lights + */ + ret = gb_lights_setup(glights); + if (ret < 0) + goto out; + + return 0; + +out: + gb_lights_release(glights); + return ret; +} + +static void gb_lights_connection_exit(struct gb_connection *connection) +{ + struct gb_lights *glights = connection->private; + + gb_lights_release(glights); +} + +static struct gb_protocol lights_protocol = { + .name = "lights", + .id = GREYBUS_PROTOCOL_LIGHTS, + .major = GB_LIGHTS_VERSION_MAJOR, + .minor = GB_LIGHTS_VERSION_MINOR, + .connection_init = gb_lights_connection_init, + .connection_exit = gb_lights_connection_exit, + .request_recv = gb_lights_event_recv, +}; + +gb_protocol_driver(&lights_protocol); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 1ab2e8918de583f7f98289448af6d8784a4c4ced Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Sun, 16 Aug 2015 00:48:03 +0100 Subject: greybus: makefile: use POSIX functions for kernel cmp [[, == and echo -e are bash/zsh-ism and not POSIX, so when using a POSIX shell the kernel_cmp can issue some warnings and not work properly. Use only POSIX operators for kernel version compare. Signed-off-by: Rui Miguel Silva Tested-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 5c29b63c6c28..31b025da5e98 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -61,7 +61,7 @@ ifneq ($(KERNELRELEASE),) # This function returns the argument version if current kernel version is minor # than the passed version, return 1 if equal or the current kernel version if it # is greater than argument version. -kvers_cmp=$(shell [[ "$(KERNELVERSION)" == "$(1)" ]] && echo 1 || echo -e "$(1)\n$(KERNELVERSION)" | sort -V | tail -1) +kvers_cmp=$(shell [ "$(KERNELVERSION)" = "$(1)" ] && echo 1 || printf "$(1)\n$(KERNELVERSION)" | sort -V | tail -1) ifneq ($(call kvers_cmp,"3.19.0"),3.19.0) CONFIG_OPTIONS_ENABLE += LEDS_CLASS_FLASH -- cgit v1.2.3-59-g8ed1b From 4be6ea54d46166dcd8f7673b87b578c611577c29 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sat, 15 Aug 2015 08:54:18 +0530 Subject: greybus: operation: Fix wrong order of arguments The order of arguments is wrong and that shows up as a warning only on 64 bit machines. Fixes: cb0ef0c019ab ("operation: print message type on errors") Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 0bb5b8d9cb3b..f9b71e79b4c4 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -869,7 +869,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, message_size = sizeof(*message->header) + message->payload_size; if (!errno && size != message_size) { dev_err(&connection->dev, "bad message (0x%02hhx) size (%zu != %zu)\n", - size, message_size, message->header->type); + message->header->type, size, message_size); errno = -EMSGSIZE; } -- cgit v1.2.3-59-g8ed1b From 7ee582680230f56ad43afc9cbb46e050e1027afb Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Mon, 17 Aug 2015 17:03:43 +0100 Subject: greybus: greybus_protocol: remove unused lights macros Greybus core now handle the _INVALID and PROTOCOL_VERSION types, so no need to have specific protocol macros for this. Just drop them. Signed-off-by: Rui Miguel Silva Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 32b927f901f7..0c11bec39065 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1073,8 +1073,6 @@ struct gb_sdio_event_request { #define GB_LIGHTS_VERSION_MINOR 0x01 /* Greybus Lights request types */ -#define GB_LIGHTS_TYPE_INVALID 0x00 -#define GB_LIGHTS_TYPE_PROTOCOL_VERSION 0x01 #define GB_LIGHTS_TYPE_GET_LIGHTS 0x02 #define GB_LIGHTS_TYPE_GET_LIGHT_CONFIG 0x03 #define GB_LIGHTS_TYPE_GET_CHANNEL_CONFIG 0x04 -- cgit v1.2.3-59-g8ed1b From 464dc8cbdeb4a58791b9b6dc90e17e4b4f7d0d90 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:57:16 +0100 Subject: greybus: interface: change typo replicable => replaceable 'user-replicable' means something that a user can replicate. 'user-replaceable' means something that a user can replace. We defintely mean to say replaceable not replicable here. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 4a26bf6f714c..c5211a313d34 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -109,7 +109,7 @@ int gb_create_bundle_connection(struct gb_interface *intf, u8 class) } /* - * A Greybus module represents a user-replicable component on an Ara + * A Greybus module represents a user-replaceable component on an Ara * phone. An interface is the physical connection on that module. A * module may have more than one interface. * -- cgit v1.2.3-59-g8ed1b From 2f8423046f21093818ffc4fae917f8e9dd8e9931 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:02 +0100 Subject: greybus: loopback: add ability to graph greybus latency This patch adds the ability to graph the latency of the overhead introduced by the greybus stack in the roundtrip time AP->Module->AP. Since this is a small number it is reported in nanoseconds, not mircoseconds. This data can also be derived with tracepoints but it's being provided in this format to export to CSV file more easily than with tracepoints. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 46 ++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 3263d8351aa3..74bc2b4eec63 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -50,9 +50,11 @@ struct gb_loopback { int ms_wait; struct gb_loopback_stats latency; + struct gb_loopback_stats latency_gb; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; u64 elapsed_nsecs; + u64 elapsed_nsecs_gb; u32 error; }; @@ -177,6 +179,8 @@ static void gb_loopback_check_attr(struct gb_connection *connection, /* Time to send and receive one message */ gb_loopback_stats_attrs(latency); +/* Time to send and receive one message not including greybus */ +gb_loopback_stats_attrs(latency_gb); /* Number of requests sent per second on this cport */ gb_loopback_stats_attrs(requests_per_second); /* Quantity of data sent and received on this cport */ @@ -209,6 +213,7 @@ gb_loopback_attr(iteration_max, u); static struct attribute *loopback_attrs[] = { dev_stats_attrs(latency), + dev_stats_attrs(latency_gb), dev_stats_attrs(requests_per_second), dev_stats_attrs(throughput), &dev_attr_type.attr, @@ -221,17 +226,16 @@ static struct attribute *loopback_attrs[] = { }; ATTRIBUTE_GROUPS(loopback); -static void gb_loopback_calc_latency(struct gb_loopback *gb, - struct timeval *ts, struct timeval *te) +static u64 gb_loopback_calc_latency(struct timeval *ts, struct timeval *te) { u64 t1, t2; t1 = timeval_to_ns(ts); t2 = timeval_to_ns(te); if (t2 > t1) - gb->elapsed_nsecs = t2 - t1; + return t2 - t1; else - gb->elapsed_nsecs = NSEC_PER_DAY - t2 + t1; + return NSEC_PER_DAY - t2 + t1; } static int gb_loopback_sink(struct gb_loopback *gb, u32 len) @@ -251,7 +255,15 @@ static int gb_loopback_sink(struct gb_loopback *gb, u32 len) request, len + sizeof(*request), NULL, 0); do_gettimeofday(&te); - gb_loopback_calc_latency(gb, &ts, &te); + + /* Calculate the total time the message took */ + gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); + + /* Calculate non-greybus related component of the latency */ + gb_connection_pop_timestamp(gb->connection, &ts); + gb_connection_pop_timestamp(gb->connection, &te); + gb->elapsed_nsecs_gb = gb_loopback_calc_latency(&ts, &te); + kfree(request); return retval; @@ -282,7 +294,14 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) request, len + sizeof(*request), response, len + sizeof(*response)); do_gettimeofday(&te); - gb_loopback_calc_latency(gb, &ts, &te); + + /* Calculate the total time the message took */ + gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); + + /* Calculate non-greybus related component of the latency */ + gb_connection_pop_timestamp(gb->connection, &ts); + gb_connection_pop_timestamp(gb->connection, &te); + gb->elapsed_nsecs_gb = gb_loopback_calc_latency(&ts, &te); if (retval) goto gb_error; @@ -308,7 +327,14 @@ static int gb_loopback_ping(struct gb_loopback *gb) retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_PING, NULL, 0, NULL, 0); do_gettimeofday(&te); - gb_loopback_calc_latency(gb, &ts, &te); + + /* Calculate the total time the message took */ + gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); + + /* Calculate non-greybus related component of the latency */ + gb_connection_pop_timestamp(gb->connection, &ts); + gb_connection_pop_timestamp(gb->connection, &te); + gb->elapsed_nsecs_gb = gb_loopback_calc_latency(&ts, &te); return retval; } @@ -371,6 +397,7 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) .min = U32_MAX, }; memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb->latency_gb, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb->requests_per_second, &reset, sizeof(struct gb_loopback_stats)); @@ -439,6 +466,11 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) /* Log throughput and requests using latency as benchmark */ gb_loopback_throughput_update(gb, lat); gb_loopback_requests_update(gb, lat); + + /* Calculate the greybus related latency number in nanoseconds */ + tmp = gb->elapsed_nsecs - gb->elapsed_nsecs_gb; + lat = tmp; + gb_loopback_update_stats(&gb->latency_gb, lat); } static int gb_loopback_fn(void *data) -- cgit v1.2.3-59-g8ed1b From aa27bf82694de73f850581d5c928d99b16032be7 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:03 +0100 Subject: greybus: loopback: convert sample report interface to debugfs Greg previously suggested switching over to debugfs instead of a char interface to report raw samples to user-space. At the time we agreed not to go for that change. However later patches in this series are made simpler if debugfs is used instead of /dev, so it makes sense to make the conversion now for that reason. This patch removes the char interface and replaces it with a debugfs interface. Raw samples will be acquired from /sys/kernel/debug/gb_loopback/raw_latency_endo0:x:y:z:w where x = y = z = w = Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 177 ++++++++++++++----------------------- 1 file changed, 66 insertions(+), 111 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 74bc2b4eec63..5f66048927a7 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -31,16 +32,21 @@ struct gb_loopback_stats { u32 count; }; +struct gb_loopback_device { + struct dentry *root; + u32 count; +}; + +static struct gb_loopback_device gb_dev; + struct gb_loopback { struct gb_connection *connection; + struct dentry *file; struct kfifo kfifo; struct mutex mutex; struct task_struct *task; wait_queue_head_t wq; - dev_t dev; - struct cdev cdev; - struct device *device; int type; u32 size; @@ -60,16 +66,9 @@ struct gb_loopback { #define GB_LOOPBACK_FIFO_DEFAULT 8192 -static struct class *loopback_class; -static int loopback_major; -static const struct file_operations loopback_fops; -static DEFINE_IDA(minors); static unsigned kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT; module_param(kfifo_depth, uint, 0444); -/* Number of minor devices this driver supports */ -#define NUM_MINORS 256 - /* Maximum size of any one send data buffer we support */ #define MAX_PACKET_SIZE (PAGE_SIZE * 2) @@ -515,35 +514,72 @@ static int gb_loopback_fn(void *data) return 0; } +static int gb_loopback_dbgfs_latency_show(struct seq_file *s, void *unused) +{ + struct gb_loopback *gb = s->private; + u32 latency; + int retval; + + if (kfifo_len(&gb->kfifo) == 0) { + retval = -EAGAIN; + goto done; + } + + mutex_lock(&gb->mutex); + retval = kfifo_out(&gb->kfifo, &latency, sizeof(latency)); + if (retval > 0) { + seq_printf(s, "%u", latency); + retval = 0; + } + mutex_unlock(&gb->mutex); +done: + return retval; +} + +static int gb_loopback_latency_open(struct inode *inode, struct file *file) +{ + return single_open(file, gb_loopback_dbgfs_latency_show, + inode->i_private); +} + +static const struct file_operations gb_loopback_debugfs_latency_ops = { + .open = gb_loopback_latency_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define DEBUGFS_NAMELEN 24 + static int gb_loopback_connection_init(struct gb_connection *connection) { struct gb_loopback *gb; int retval; - int minor; + char name[DEBUGFS_NAMELEN]; gb = kzalloc(sizeof(*gb), GFP_KERNEL); if (!gb) return -ENOMEM; gb_loopback_reset_stats(gb); + snprintf(name, sizeof(name), "raw_latency_endo0:%d:%d:%d:%d", + connection->bundle->intf->module->module_id, + connection->bundle->intf->interface_id, + connection->bundle->id, + connection->intf_cport_id); + gb->file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, gb, + &gb_loopback_debugfs_latency_ops); gb->connection = connection; connection->private = gb; retval = sysfs_create_groups(&connection->dev.kobj, loopback_groups); if (retval) - goto out_free; - - /* Get a minor number */ - minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); - if (minor < 0) { - retval = minor; - goto out_sysfs; - } + goto out_debugfs; /* Calculate maximum payload */ gb->size_max = gb_operation_get_payload_size_max(connection); if (gb->size_max <= sizeof(struct gb_loopback_transfer_request)) { retval = -EINVAL; - goto out_minor; + goto out_sysfs; } gb->size_max -= sizeof(struct gb_loopback_transfer_request); @@ -551,21 +587,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32), GFP_KERNEL)) { retval = -ENOMEM; - goto out_minor; - } - - /* Create device entry */ - gb->dev = MKDEV(loopback_major, minor); - cdev_init(&gb->cdev, &loopback_fops); - retval = cdev_add(&gb->cdev, gb->dev, 1); - if (retval) - goto out_kfifo; - - gb->device = device_create(loopback_class, &connection->dev, gb->dev, - gb, "gb!loopback%d", minor); - if (IS_ERR(gb->device)) { - retval = PTR_ERR(gb->device); - goto out_cdev; + goto out_sysfs; } /* Fork worker thread */ @@ -574,22 +596,18 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { retval = PTR_ERR(gb->task); - goto out_device; + goto out_kfifo; } + gb_dev.count++; return 0; -out_device: - device_del(gb->device); -out_cdev: - cdev_del(&gb->cdev); out_kfifo: kfifo_free(&gb->kfifo); -out_minor: - ida_simple_remove(&minors, minor); out_sysfs: sysfs_remove_groups(&connection->dev.kobj, loopback_groups); -out_free: +out_debugfs: + debugfs_remove(gb->file); connection->private = NULL; kfree(gb); @@ -600,15 +618,14 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) { struct gb_loopback *gb = connection->private; + gb_dev.count--; connection->private = NULL; if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); - device_del(gb->device); - cdev_del(&gb->cdev); kfifo_free(&gb->kfifo); - ida_simple_remove(&minors, MINOR(gb->dev)); sysfs_remove_groups(&connection->dev.kobj, loopback_groups); + debugfs_remove(gb->file); kfree(gb); } @@ -622,79 +639,17 @@ static struct gb_protocol loopback_protocol = { .request_recv = gb_loopback_request_recv, }; -static int loopback_open(struct inode *inode, struct file *file) -{ - struct cdev *cdev = inode->i_cdev; - struct gb_loopback *gb = container_of(cdev, struct gb_loopback, cdev); - - file->private_data = gb; - return 0; -} - -static ssize_t loopback_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - struct gb_loopback *gb = file->private_data; - size_t fifo_len; - int retval; - - if (!count || count % sizeof(u32)) - return -EINVAL; - - mutex_lock(&gb->mutex); - fifo_len = kfifo_len(&gb->kfifo); - if (kfifo_to_user(&gb->kfifo, buf, min(fifo_len, count), &retval) != 0) - retval = -EIO; - mutex_unlock(&gb->mutex); - - return retval; -} - -static const struct file_operations loopback_fops = { - .owner = THIS_MODULE, - .read = loopback_read, - .open = loopback_open, - .llseek = noop_llseek, -}; - static int loopback_init(void) { - dev_t dev; - int retval; - - loopback_class = class_create(THIS_MODULE, "gb_loopback"); - if (IS_ERR(loopback_class)) { - retval = PTR_ERR(loopback_class); - goto error_class; - } - - retval = alloc_chrdev_region(&dev, 0, NUM_MINORS, "gb_loopback"); - if (retval < 0) - goto error_chrdev; - - loopback_major = MAJOR(dev); - - retval = gb_protocol_register(&loopback_protocol); - if (retval) - goto error_gb; - - return 0; - -error_gb: - unregister_chrdev_region(dev, NUM_MINORS); -error_chrdev: - class_destroy(loopback_class); -error_class: - return retval; + gb_dev.root = debugfs_create_dir("gb_loopback", NULL); + return gb_protocol_register(&loopback_protocol); } module_init(loopback_init); static void __exit loopback_exit(void) { + debugfs_remove_recursive(gb_dev.root); gb_protocol_deregister(&loopback_protocol); - unregister_chrdev_region(MKDEV(loopback_major, 0), NUM_MINORS); - class_destroy(loopback_class); - ida_destroy(&minors); } module_exit(loopback_exit); -- cgit v1.2.3-59-g8ed1b From 67d1eeceb1aab4a192a0f132cd230d41932ba91e Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:04 +0100 Subject: greybus: loopback: support synchronized tests over multiple cports The loopback code as currently implemented allows free running threads to blast data to cports in isolation, however no overall stastics are gathered for the aggregate throughput to a given module. This patch moves derivation of stastics for all cports to one structure so that 1 to n cports will report their overall stastics in one place. Later patches update the sysfs/debugfs accessor functions to provide the per-module and per-connection data separately. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 266 +++++++++++++++++++++++++++---------- 1 file changed, 196 insertions(+), 70 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 5f66048927a7..b46ded22b533 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -35,6 +35,27 @@ struct gb_loopback_stats { struct gb_loopback_device { struct dentry *root; u32 count; + + struct mutex mutex; + struct list_head list; + wait_queue_head_t wq; + + int type; + u32 size; + u32 iteration_max; + u32 iteration_count; + size_t size_max; + int ms_wait; + u32 error; + + struct timeval start; + struct timeval end; + + /* Overall stats */ + struct gb_loopback_stats latency; + struct gb_loopback_stats latency_gb; + struct gb_loopback_stats throughput; + struct gb_loopback_stats requests_per_second; }; static struct gb_loopback_device gb_dev; @@ -46,19 +67,15 @@ struct gb_loopback { struct kfifo kfifo; struct mutex mutex; struct task_struct *task; - wait_queue_head_t wq; - - int type; - u32 size; - u32 iteration_max; - u32 iteration_count; - size_t size_max; - int ms_wait; + struct list_head entry; + /* Per connection stats */ struct gb_loopback_stats latency; struct gb_loopback_stats latency_gb; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; + + u32 iteration_count; u64 elapsed_nsecs; u64 elapsed_nsecs_gb; u32 error; @@ -133,45 +150,86 @@ static ssize_t field##_store(struct device *dev, \ { \ int ret; \ struct gb_connection *connection = to_gb_connection(dev); \ - struct gb_loopback *gb = connection->private; \ - mutex_lock(&gb->mutex); \ + mutex_lock(&gb_dev.mutex); \ ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ len = -EINVAL; \ else \ - gb_loopback_check_attr(connection, gb); \ - mutex_unlock(&gb->mutex); \ + gb_loopback_check_attr(connection); \ + mutex_unlock(&gb_dev.mutex); \ return len; \ } \ static DEVICE_ATTR_RW(field) -static void gb_loopback_reset_stats(struct gb_loopback *gb); -static void gb_loopback_check_attr(struct gb_connection *connection, - struct gb_loopback *gb) +#define gb_dev_loopback_ro_attr(field) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return sprintf(buf, "%u\n", gb_dev.field); \ +} \ +static DEVICE_ATTR_RO(field) + +#define gb_dev_loopback_rw_attr(field, type) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return sprintf(buf, "%"#type"\n", gb_dev.field); \ +} \ +static ssize_t field##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, \ + size_t len) \ +{ \ + int ret; \ + struct gb_connection *connection = to_gb_connection(dev); \ + mutex_lock(&gb_dev.mutex); \ + ret = sscanf(buf, "%"#type, &gb_dev.field); \ + if (ret != 1) \ + len = -EINVAL; \ + else \ + gb_loopback_check_attr(&gb_dev, connection); \ + mutex_unlock(&gb_dev.mutex); \ + return len; \ +} \ +static DEVICE_ATTR_RW(field) + +static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev); +static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, + struct gb_connection *connection) { - if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) - gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; - if (gb->size > gb->size_max) - gb->size = gb->size_max; - gb->error = 0; - gb->iteration_count = 0; - gb_loopback_reset_stats(gb); - - if (kfifo_depth < gb->iteration_max) { - dev_warn(&connection->dev, - "iteration_max %u kfifo_depth %u cannot log all data\n", - gb->iteration_max, kfifo_depth); + struct gb_loopback *gb; + + if (gb_dev->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) + gb_dev->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; + if (gb_dev->size > gb_dev->size_max) + gb_dev->size = gb_dev->size_max; + gb_dev->iteration_count = 0; + gb_dev->error = 0; + + list_for_each_entry(gb, &gb_dev->list, entry) { + mutex_lock(&gb->mutex); + gb->iteration_count = 0; + gb->error = 0; + if (kfifo_depth < gb_dev->iteration_max) { + dev_warn(&connection->dev, + "cannot log bytes %u kfifo_depth %u\n", + gb_dev->iteration_max, kfifo_depth); + } + kfifo_reset_out(&gb->kfifo); + mutex_unlock(&gb->mutex); } - switch (gb->type) { + switch (gb_dev->type) { case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: case GB_LOOPBACK_TYPE_SINK: - kfifo_reset_out(&gb->kfifo); - wake_up(&gb->wq); + gb_loopback_reset_stats(gb_dev); + wake_up(&gb_dev->wq); break; default: - gb->type = 0; + gb_dev->type = 0; break; } } @@ -186,8 +244,6 @@ gb_loopback_stats_attrs(requests_per_second); gb_loopback_stats_attrs(throughput); /* Number of errors encountered during loop */ gb_loopback_ro_attr(error); -/* The current index of the for (i = 0; i < iteration_max; i++) loop */ -gb_loopback_ro_attr(iteration_count); /* * Type of loopback message to send based on protocol type definitions @@ -197,13 +253,15 @@ gb_loopback_ro_attr(iteration_count); * payload returned in response) * 4 => Send a sink message (message with payload, no payload in response) */ -gb_loopback_attr(type, d); +gb_dev_loopback_rw_attr(type, d); /* Size of transfer message payload: 0-4096 bytes */ -gb_loopback_attr(size, u); +gb_dev_loopback_rw_attr(size, u); /* Time to wait between two messages: 0-1000 ms */ -gb_loopback_attr(ms_wait, d); +gb_dev_loopback_rw_attr(ms_wait, d); /* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */ -gb_loopback_attr(iteration_max, u); +gb_dev_loopback_rw_attr(iteration_max, u); +/* The current index of the for (i = 0; i < iteration_max; i++) loop */ +gb_dev_loopback_ro_attr(iteration_count); #define dev_stats_attrs(name) \ &dev_attr_##name##_min.attr, \ @@ -341,7 +399,6 @@ static int gb_loopback_ping(struct gb_loopback *gb) static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) { struct gb_connection *connection = operation->connection; - struct gb_loopback *gb = connection->private; struct gb_loopback_transfer_request *request; struct gb_loopback_transfer_response *response; size_t len; @@ -365,10 +422,10 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) } request = operation->request->payload; len = le32_to_cpu(request->len); - if (len > gb->size_max) { + if (len > gb_dev.size_max) { dev_err(&connection->dev, "transfer request too large (%zu > %zu)\n", - len, gb->size_max); + len, gb_dev.size_max); return -EINVAL; } @@ -390,15 +447,34 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) } } -static void gb_loopback_reset_stats(struct gb_loopback *gb) +static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev) { struct gb_loopback_stats reset = { .min = U32_MAX, }; - memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb->latency_gb, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb->requests_per_second, &reset, + struct gb_loopback *gb; + + /* Reset per-connection stats */ + list_for_each_entry(gb, &gb_dev->list, entry) { + mutex_lock(&gb->mutex); + memcpy(&gb->latency, &reset, + sizeof(struct gb_loopback_stats)); + memcpy(&gb->latency_gb, &reset, + sizeof(struct gb_loopback_stats)); + memcpy(&gb->throughput, &reset, + sizeof(struct gb_loopback_stats)); + memcpy(&gb->requests_per_second, &reset, + sizeof(struct gb_loopback_stats)); + mutex_unlock(&gb->mutex); + } + + /* Reset aggregate stats */ + memset(&gb_dev->start, 0, sizeof(struct timeval)); + memset(&gb_dev->end, 0, sizeof(struct timeval)); + memcpy(&gb_dev->latency, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb_dev->latency_gb, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb_dev->throughput, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb_dev->requests_per_second, &reset, sizeof(struct gb_loopback_stats)); } @@ -417,6 +493,7 @@ static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency) u32 req = USEC_PER_SEC; do_div(req, latency); + gb_loopback_update_stats(&gb_dev.requests_per_second, req); gb_loopback_update_stats(&gb->requests_per_second, req); } @@ -425,17 +502,17 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) u32 throughput; u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2; - switch (gb->type) { + switch (gb_dev.type) { case GB_LOOPBACK_TYPE_PING: break; case GB_LOOPBACK_TYPE_SINK: aggregate_size += sizeof(struct gb_loopback_transfer_request) + - gb->size; + gb_dev.size; break; case GB_LOOPBACK_TYPE_TRANSFER: aggregate_size += sizeof(struct gb_loopback_transfer_request) + sizeof(struct gb_loopback_transfer_response) + - gb->size * 2; + gb_dev.size * 2; break; default: return; @@ -445,6 +522,7 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) throughput = USEC_PER_SEC; do_div(throughput, latency); throughput *= aggregate_size; + gb_loopback_update_stats(&gb_dev.throughput, throughput); gb_loopback_update_stats(&gb->throughput, throughput); } @@ -459,7 +537,10 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) lat = tmp; /* Log latency statistic */ + gb_loopback_update_stats(&gb_dev.latency, lat); gb_loopback_update_stats(&gb->latency, lat); + + /* Raw latency log on a per thread basis */ kfifo_in(&gb->kfifo, (unsigned char *)&lat, sizeof(lat)); /* Log throughput and requests using latency as benchmark */ @@ -469,6 +550,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) /* Calculate the greybus related latency number in nanoseconds */ tmp = gb->elapsed_nsecs - gb->elapsed_nsecs_gb; lat = tmp; + gb_loopback_update_stats(&gb_dev.latency_gb, lat); gb_loopback_update_stats(&gb->latency_gb, lat); } @@ -476,38 +558,74 @@ static int gb_loopback_fn(void *data) { int error = 0; int ms_wait; + int type; + u32 size; + u32 low_count; struct gb_loopback *gb = data; + struct gb_loopback *gb_list; while (1) { - if (!gb->type) - wait_event_interruptible(gb->wq, gb->type || + if (!gb_dev.type) + wait_event_interruptible(gb_dev.wq, gb_dev.type || kthread_should_stop()); if (kthread_should_stop()) break; - mutex_lock(&gb->mutex); - if (gb->iteration_max) { - if (gb->iteration_count < gb->iteration_max) { - gb->iteration_count++; + mutex_lock(&gb_dev.mutex); + if (gb_dev.iteration_max) { + /* Determine overall lowest count */ + low_count = gb->iteration_count; + list_for_each_entry(gb_list, &gb_dev.list, entry) { + if (gb_list->iteration_count < low_count) + low_count = gb_list->iteration_count; + } + /* All threads achieved at least low_count iterations */ + if (gb_dev.iteration_count < low_count) { + gb_dev.iteration_count = low_count; sysfs_notify(&gb->connection->dev.kobj, NULL, "iteration_count"); - } else { - gb->type = 0; - mutex_unlock(&gb->mutex); + } + /* Optionally terminate */ + if (gb_dev.iteration_count == gb_dev.iteration_max) { + gb_dev.type = 0; + mutex_unlock(&gb_dev.mutex); continue; } } - if (gb->type == GB_LOOPBACK_TYPE_PING) + size = gb_dev.size; + ms_wait = gb_dev.ms_wait; + type = gb_dev.type; + mutex_unlock(&gb_dev.mutex); + + mutex_lock(&gb->mutex); + if (gb->iteration_count >= gb_dev.iteration_max) { + /* If this thread finished before siblings then sleep */ + ms_wait = 1; + mutex_unlock(&gb->mutex); + goto sleep; + } + /* Else operations to perform */ + if (type == GB_LOOPBACK_TYPE_PING) error = gb_loopback_ping(gb); - else if (gb->type == GB_LOOPBACK_TYPE_TRANSFER) - error = gb_loopback_transfer(gb, gb->size); - else if (gb->type == GB_LOOPBACK_TYPE_SINK) - error = gb_loopback_sink(gb, gb->size); - if (error) + else if (type == GB_LOOPBACK_TYPE_TRANSFER) + error = gb_loopback_transfer(gb, size); + else if (type == GB_LOOPBACK_TYPE_SINK) + error = gb_loopback_sink(gb, size); + mutex_unlock(&gb->mutex); + + mutex_lock(&gb_dev.mutex); + mutex_lock(&gb->mutex); + + if (error) { + gb_dev.error++; gb->error++; + } gb_loopback_calculate_stats(gb); - ms_wait = gb->ms_wait; + gb->iteration_count++; + mutex_unlock(&gb->mutex); + mutex_unlock(&gb_dev.mutex); +sleep: if (ms_wait) msleep(ms_wait); } @@ -549,7 +667,7 @@ static const struct file_operations gb_loopback_debugfs_latency_ops = { .release = single_release, }; -#define DEBUGFS_NAMELEN 24 +#define DEBUGFS_NAMELEN 32 static int gb_loopback_connection_init(struct gb_connection *connection) { @@ -560,7 +678,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb = kzalloc(sizeof(*gb), GFP_KERNEL); if (!gb) return -ENOMEM; - gb_loopback_reset_stats(gb); + gb_loopback_reset_stats(&gb_dev); snprintf(name, sizeof(name), "raw_latency_endo0:%d:%d:%d:%d", connection->bundle->intf->module->module_id, @@ -576,12 +694,14 @@ static int gb_loopback_connection_init(struct gb_connection *connection) goto out_debugfs; /* Calculate maximum payload */ - gb->size_max = gb_operation_get_payload_size_max(connection); - if (gb->size_max <= sizeof(struct gb_loopback_transfer_request)) { + mutex_lock(&gb_dev.mutex); + gb_dev.size_max = gb_operation_get_payload_size_max(connection); + if (gb_dev.size_max <= sizeof(struct gb_loopback_transfer_request)) { retval = -EINVAL; goto out_sysfs; } - gb->size_max -= sizeof(struct gb_loopback_transfer_request); + gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request); + mutex_unlock(&gb_dev.mutex); /* Allocate kfifo */ if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32), @@ -591,7 +711,6 @@ static int gb_loopback_connection_init(struct gb_connection *connection) } /* Fork worker thread */ - init_waitqueue_head(&gb->wq); mutex_init(&gb->mutex); gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { @@ -599,6 +718,9 @@ static int gb_loopback_connection_init(struct gb_connection *connection) goto out_kfifo; } + mutex_lock(&gb_dev.mutex); + list_add_tail(&gb->entry, &gb_dev.list); + mutex_unlock(&gb_dev.mutex); gb_dev.count++; return 0; @@ -641,7 +763,11 @@ static struct gb_protocol loopback_protocol = { static int loopback_init(void) { + init_waitqueue_head(&gb_dev.wq); + INIT_LIST_HEAD(&gb_dev.list); + mutex_init(&gb_dev.mutex); gb_dev.root = debugfs_create_dir("gb_loopback", NULL); + return gb_protocol_register(&loopback_protocol); } module_init(loopback_init); -- cgit v1.2.3-59-g8ed1b From bd416103b204494e29b095e28c5a7b6a77e8f2b9 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:05 +0100 Subject: greybus: loopback: add gb_loopback_nsec_to_usec_latency A helper function to convert from a nanosecond value to a latency value expressed in mircoseconds. This will be used again in subsequent patches. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index b46ded22b533..4879edda982a 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -283,6 +283,15 @@ static struct attribute *loopback_attrs[] = { }; ATTRIBUTE_GROUPS(loopback); +static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs) +{ + u32 lat; + + do_div(elapsed_nsecs, NSEC_PER_USEC); + lat = elapsed_nsecs; + return lat; +} + static u64 gb_loopback_calc_latency(struct timeval *ts, struct timeval *te) { u64 t1, t2; @@ -532,9 +541,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) u64 tmp; /* Express latency in terms of microseconds */ - tmp = gb->elapsed_nsecs; - do_div(tmp, NSEC_PER_USEC); - lat = tmp; + lat = gb_loopback_nsec_to_usec_latency(gb->elapsed_nsecs); /* Log latency statistic */ gb_loopback_update_stats(&gb_dev.latency, lat); -- cgit v1.2.3-59-g8ed1b From 7c985351d40475ab752b230d5761e646973cb59b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:06 +0100 Subject: greybus: loopback: functionally decompose gb_loopback_calc_latency The __gb_loopback_calc_latency will be useful in later patches. Provide it here and use as intended. Later on we just want to use the timestamp rollover detection, so split it out now. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 4879edda982a..39375f1e0d8e 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -292,16 +292,22 @@ static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs) return lat; } +static u64 __gb_loopback_calc_latency(u64 t1, u64 t2) +{ + if (t2 > t1) + return t2 - t1; + else + return NSEC_PER_DAY - t2 + t1; +} + static u64 gb_loopback_calc_latency(struct timeval *ts, struct timeval *te) { u64 t1, t2; t1 = timeval_to_ns(ts); t2 = timeval_to_ns(te); - if (t2 > t1) - return t2 - t1; - else - return NSEC_PER_DAY - t2 + t1; + + return __gb_loopback_calc_latency(t1, t2); } static int gb_loopback_sink(struct gb_loopback *gb, u32 len) -- cgit v1.2.3-59-g8ed1b From 4b0ea00caf837160b25482d48d1f7a95ba47d318 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:07 +0100 Subject: greybus: loopback: graph round-trip time for all threads This patch adds the ability to time the delta between all threads like this t1 = timestmap(); thread1:gb_operation_sync(); thread2:gb_operation_sync(); t2 = timestamp(); In order to enable that behaviour without forcing an undesirable checkpointing scheme this patch introduces a kfifo for each thread to store the raw timestamps and calculate a time difference. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 155 +++++++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 39375f1e0d8e..0d2de708ec0b 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -36,6 +36,7 @@ struct gb_loopback_device { struct dentry *root; u32 count; + struct kfifo kfifo; struct mutex mutex; struct list_head list; wait_queue_head_t wq; @@ -64,7 +65,8 @@ struct gb_loopback { struct gb_connection *connection; struct dentry *file; - struct kfifo kfifo; + struct kfifo kfifo_lat; + struct kfifo kfifo_ts; struct mutex mutex; struct task_struct *task; struct list_head entry; @@ -75,6 +77,7 @@ struct gb_loopback { struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; + u32 lbid; u32 iteration_count; u64 elapsed_nsecs; u64 elapsed_nsecs_gb; @@ -217,7 +220,8 @@ static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, "cannot log bytes %u kfifo_depth %u\n", gb_dev->iteration_max, kfifo_depth); } - kfifo_reset_out(&gb->kfifo); + kfifo_reset_out(&gb->kfifo_lat); + kfifo_reset_out(&gb->kfifo_ts); mutex_unlock(&gb->mutex); } @@ -225,6 +229,7 @@ static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: case GB_LOOPBACK_TYPE_SINK: + kfifo_reset_out(&gb_dev->kfifo); gb_loopback_reset_stats(gb_dev); wake_up(&gb_dev->wq); break; @@ -310,6 +315,13 @@ static u64 gb_loopback_calc_latency(struct timeval *ts, struct timeval *te) return __gb_loopback_calc_latency(t1, t2); } +static void gb_loopback_push_latency_ts(struct gb_loopback *gb, + struct timeval *ts, struct timeval *te) +{ + kfifo_in(&gb->kfifo_ts, (unsigned char *)ts, sizeof(*ts)); + kfifo_in(&gb->kfifo_ts, (unsigned char *)te, sizeof(*te)); +} + static int gb_loopback_sink(struct gb_loopback *gb, u32 len) { struct timeval ts, te; @@ -329,6 +341,7 @@ static int gb_loopback_sink(struct gb_loopback *gb, u32 len) do_gettimeofday(&te); /* Calculate the total time the message took */ + gb_loopback_push_latency_ts(gb, &ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); /* Calculate non-greybus related component of the latency */ @@ -368,6 +381,7 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) do_gettimeofday(&te); /* Calculate the total time the message took */ + gb_loopback_push_latency_ts(gb, &ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); /* Calculate non-greybus related component of the latency */ @@ -401,6 +415,7 @@ static int gb_loopback_ping(struct gb_loopback *gb) do_gettimeofday(&te); /* Calculate the total time the message took */ + gb_loopback_push_latency_ts(gb, &ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); /* Calculate non-greybus related component of the latency */ @@ -541,6 +556,59 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) gb_loopback_update_stats(&gb->throughput, throughput); } +static int gb_loopback_calculate_aggregate_stats(void) +{ + struct gb_loopback *gb; + struct timeval ts; + struct timeval te; + u64 t1, t2; + u64 ts_min; + u64 te_max; + u64 elapsed_nsecs; + u32 lat; + int i, latched; + int rollover = 0; + + for (i = 0; i < gb_dev.iteration_max; i++) { + latched = 0; + ts_min = 0; + te_max = 0; + list_for_each_entry(gb, &gb_dev.list, entry) { + if (kfifo_out(&gb->kfifo_ts, &ts, sizeof(ts)) < sizeof(ts)) + goto error; + if (kfifo_out(&gb->kfifo_ts, &te, sizeof(te)) < sizeof(te)) + goto error; + t1 = timeval_to_ns(&ts); + t2 = timeval_to_ns(&te); + + /* minimum timestamp is always what we want */ + if (latched == 0 || t1 < ts_min) + ts_min = t1; + + /* maximum timestamp needs to handle rollover */ + if (t2 > t1) { + if (latched == 0 || t2 > te_max) + te_max = t2; + } else { + if (latched == 0 || rollover == 0) + te_max = t2; + if (rollover == 1 && t2 > te_max) + te_max = t2; + rollover = 1; + } + latched = 1; + } + /* Calculate the aggregate timestamp */ + elapsed_nsecs = __gb_loopback_calc_latency(ts_min, te_max); + lat = gb_loopback_nsec_to_usec_latency(elapsed_nsecs); + kfifo_in(&gb_dev.kfifo, (unsigned char *)&lat, sizeof(lat)); + } + return 0; +error: + kfifo_reset_out(&gb_dev.kfifo); + return -ENOMEM; +} + static void gb_loopback_calculate_stats(struct gb_loopback *gb) { u32 lat; @@ -554,7 +622,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) gb_loopback_update_stats(&gb->latency, lat); /* Raw latency log on a per thread basis */ - kfifo_in(&gb->kfifo, (unsigned char *)&lat, sizeof(lat)); + kfifo_in(&gb->kfifo_lat, (unsigned char *)&lat, sizeof(lat)); /* Log throughput and requests using latency as benchmark */ gb_loopback_throughput_update(gb, lat); @@ -600,6 +668,7 @@ static int gb_loopback_fn(void *data) } /* Optionally terminate */ if (gb_dev.iteration_count == gb_dev.iteration_max) { + gb_loopback_calculate_aggregate_stats(); gb_dev.type = 0; mutex_unlock(&gb_dev.mutex); continue; @@ -645,28 +714,37 @@ sleep: return 0; } -static int gb_loopback_dbgfs_latency_show(struct seq_file *s, void *unused) +static int gb_loopback_dbgfs_latency_show_common(struct seq_file *s, + struct kfifo *kfifo, + struct mutex *mutex) { - struct gb_loopback *gb = s->private; u32 latency; int retval; - if (kfifo_len(&gb->kfifo) == 0) { + if (kfifo_len(kfifo) == 0) { retval = -EAGAIN; goto done; } - mutex_lock(&gb->mutex); - retval = kfifo_out(&gb->kfifo, &latency, sizeof(latency)); + mutex_lock(mutex); + retval = kfifo_out(kfifo, &latency, sizeof(latency)); if (retval > 0) { seq_printf(s, "%u", latency); retval = 0; } - mutex_unlock(&gb->mutex); + mutex_unlock(mutex); done: return retval; } +static int gb_loopback_dbgfs_latency_show(struct seq_file *s, void *unused) +{ + struct gb_loopback *gb = s->private; + + return gb_loopback_dbgfs_latency_show_common(s, &gb->kfifo_lat, + &gb->mutex); +} + static int gb_loopback_latency_open(struct inode *inode, struct file *file) { return single_open(file, gb_loopback_dbgfs_latency_show, @@ -717,18 +795,24 @@ static int gb_loopback_connection_init(struct gb_connection *connection) mutex_unlock(&gb_dev.mutex); /* Allocate kfifo */ - if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32), + if (kfifo_alloc(&gb->kfifo_lat, kfifo_depth * sizeof(u32), GFP_KERNEL)) { retval = -ENOMEM; goto out_sysfs; } + if (kfifo_alloc(&gb->kfifo_ts, kfifo_depth * sizeof(struct timeval) * 2, + GFP_KERNEL)) { + retval = -ENOMEM; + goto out_kfifo0; + } /* Fork worker thread */ mutex_init(&gb->mutex); + gb->lbid = 1 << gb_dev.count; gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { retval = PTR_ERR(gb->task); - goto out_kfifo; + goto out_kfifo1; } mutex_lock(&gb_dev.mutex); @@ -737,8 +821,10 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb_dev.count++; return 0; -out_kfifo: - kfifo_free(&gb->kfifo); +out_kfifo1: + kfifo_free(&gb->kfifo_ts); +out_kfifo0: + kfifo_free(&gb->kfifo_lat); out_sysfs: sysfs_remove_groups(&connection->dev.kobj, loopback_groups); out_debugfs: @@ -758,7 +844,8 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); - kfifo_free(&gb->kfifo); + kfifo_free(&gb->kfifo_lat); + kfifo_free(&gb->kfifo_ts); sysfs_remove_groups(&connection->dev.kobj, loopback_groups); debugfs_remove(gb->file); kfree(gb); @@ -774,20 +861,58 @@ static struct gb_protocol loopback_protocol = { .request_recv = gb_loopback_request_recv, }; +static int gb_loopback_dbgfs_dev_latency_show(struct seq_file *s, void *unused) +{ + struct gb_loopback_device *gb_dev = s->private; + + return gb_loopback_dbgfs_latency_show_common(s, &gb_dev->kfifo, + &gb_dev->mutex); +} + +static int gb_loopback_dev_latency_open(struct inode *inode, struct file *file) +{ + return single_open(file, gb_loopback_dbgfs_dev_latency_show, + inode->i_private); +} + +static const struct file_operations gb_loopback_debugfs_dev_latency_ops = { + .open = gb_loopback_dev_latency_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int loopback_init(void) { + int retval; + init_waitqueue_head(&gb_dev.wq); INIT_LIST_HEAD(&gb_dev.list); mutex_init(&gb_dev.mutex); gb_dev.root = debugfs_create_dir("gb_loopback", NULL); - return gb_protocol_register(&loopback_protocol); + if (kfifo_alloc(&gb_dev.kfifo, kfifo_depth * sizeof(u32), GFP_KERNEL)) { + retval = -ENOMEM; + goto error_debugfs; + } + + debugfs_create_file("aggregate_latency", S_IFREG | S_IRUGO, + gb_dev.root, &gb_dev, + &gb_loopback_debugfs_dev_latency_ops); + retval = gb_protocol_register(&loopback_protocol); + if (!retval) + return retval; + +error_debugfs: + debugfs_remove_recursive(gb_dev.root); + return retval; } module_init(loopback_init); static void __exit loopback_exit(void) { debugfs_remove_recursive(gb_dev.root); + kfifo_free(&gb_dev.kfifo); gb_protocol_deregister(&loopback_protocol); } module_exit(loopback_exit); -- cgit v1.2.3-59-g8ed1b From 84cfad02b7d9ee0ec910a40aa96850c337a0d745 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:08 +0100 Subject: greybus: loopback: add bitmask of connections to include in test Feature add which enables the ability to select a bit-mask of connections to run when executing a loopback test set. This is a feature add to facilitate testing on the firmware side minus the necessity to recompile firmware to support unicast (v) multicast (v) bitmask. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 0d2de708ec0b..b362635d8a54 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -42,6 +42,7 @@ struct gb_loopback_device { wait_queue_head_t wq; int type; + u32 mask; u32 size; u32 iteration_max; u32 iteration_count; @@ -267,6 +268,8 @@ gb_dev_loopback_rw_attr(ms_wait, d); gb_dev_loopback_rw_attr(iteration_max, u); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ gb_dev_loopback_ro_attr(iteration_count); +/* A bit-mask of destination connecitons to include in the test run */ +gb_dev_loopback_rw_attr(mask, u); #define dev_stats_attrs(name) \ &dev_attr_##name##_min.attr, \ @@ -283,6 +286,7 @@ static struct attribute *loopback_attrs[] = { &dev_attr_ms_wait.attr, &dev_attr_iteration_count.attr, &dev_attr_iteration_max.attr, + &dev_attr_mask.attr, &dev_attr_error.attr, NULL, }; @@ -322,6 +326,11 @@ static void gb_loopback_push_latency_ts(struct gb_loopback *gb, kfifo_in(&gb->kfifo_ts, (unsigned char *)te, sizeof(*te)); } +static int gb_loopback_active(struct gb_loopback *gb) +{ + return (gb_dev.mask == 0 || (gb_dev.mask & gb->lbid)); +} + static int gb_loopback_sink(struct gb_loopback *gb, u32 len) { struct timeval ts, te; @@ -574,6 +583,8 @@ static int gb_loopback_calculate_aggregate_stats(void) ts_min = 0; te_max = 0; list_for_each_entry(gb, &gb_dev.list, entry) { + if (!gb_loopback_active(gb)) + continue; if (kfifo_out(&gb->kfifo_ts, &ts, sizeof(ts)) < sizeof(ts)) goto error; if (kfifo_out(&gb->kfifo_ts, &te, sizeof(te)) < sizeof(te)) @@ -653,10 +664,14 @@ static int gb_loopback_fn(void *data) break; mutex_lock(&gb_dev.mutex); + if (!gb_loopback_active(gb)) + goto unlock_continue; if (gb_dev.iteration_max) { /* Determine overall lowest count */ low_count = gb->iteration_count; list_for_each_entry(gb_list, &gb_dev.list, entry) { + if (!gb_loopback_active(gb_list)) + continue; if (gb_list->iteration_count < low_count) low_count = gb_list->iteration_count; } @@ -670,8 +685,7 @@ static int gb_loopback_fn(void *data) if (gb_dev.iteration_count == gb_dev.iteration_max) { gb_loopback_calculate_aggregate_stats(); gb_dev.type = 0; - mutex_unlock(&gb_dev.mutex); - continue; + goto unlock_continue; } } size = gb_dev.size; @@ -706,6 +720,7 @@ static int gb_loopback_fn(void *data) gb->iteration_count++; mutex_unlock(&gb->mutex); +unlock_continue: mutex_unlock(&gb_dev.mutex); sleep: if (ms_wait) -- cgit v1.2.3-59-g8ed1b From 42b9da5efe63e42298094a23edf00d2e0830812c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:09 +0100 Subject: greybus: loopback: initialized ms_wait negate warning ms_wait = 0; caught by a compiler warning. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index b362635d8a54..ff2e79212877 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -649,7 +649,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) static int gb_loopback_fn(void *data) { int error = 0; - int ms_wait; + int ms_wait = 0; int type; u32 size; u32 low_count; -- cgit v1.2.3-59-g8ed1b From a5a0ba4318aafaa0686893ba62b2ab34598d88c7 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:10 +0100 Subject: greybus: loopback: remove checkpatch error causing macro checkpatch.pl is choking on a later change to dev_stats_attrs, where checkpatch expects to see the values encapsulated in curly brackets. Encapsulating in curly brackets will cause a compiler error. To resolve the dichotomy this patch drops the macros and adds the arrary declarations directly. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ff2e79212877..0a9bc9139109 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -271,16 +271,19 @@ gb_dev_loopback_ro_attr(iteration_count); /* A bit-mask of destination connecitons to include in the test run */ gb_dev_loopback_rw_attr(mask, u); -#define dev_stats_attrs(name) \ - &dev_attr_##name##_min.attr, \ - &dev_attr_##name##_max.attr, \ - &dev_attr_##name##_avg.attr - static struct attribute *loopback_attrs[] = { - dev_stats_attrs(latency), - dev_stats_attrs(latency_gb), - dev_stats_attrs(requests_per_second), - dev_stats_attrs(throughput), + &dev_attr_latency_min.attr, + &dev_attr_latency_max.attr, + &dev_attr_latency_avg.attr, + &dev_attr_latency_gb_min.attr, + &dev_attr_latency_gb_max.attr, + &dev_attr_latency_gb_avg.attr, + &dev_attr_requests_per_second_min.attr, + &dev_attr_requests_per_second_max.attr, + &dev_attr_requests_per_second_avg.attr, + &dev_attr_throughput_min.attr, + &dev_attr_throughput_max.attr, + &dev_attr_throughput_avg.attr, &dev_attr_type.attr, &dev_attr_size.attr, &dev_attr_ms_wait.attr, -- cgit v1.2.3-59-g8ed1b From f06272b283e15951bae9b9af24cff74b7fcabaef Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:11 +0100 Subject: greybus: loopback: add module level sys/debug fs data points Code this far has used the first connection's sysfs entry to present variables intended to control the entire test - across multiple connections. This patch changes that so that the module level variables only appear at the end0:x level in sysfs. Example: Total counts for errors over the entire set of connections will be here /sys/bus/greybus/devices/endo0:x/error_dev In contrast an error for each connection will be presented like this /sys/bus/greybus/devices/endo0:x:y:z:w/error_con x = y = z = w = Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 243 +++++++++++++++++++++++-------------- 1 file changed, 154 insertions(+), 89 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 0a9bc9139109..01bed4398826 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -96,47 +96,68 @@ module_param(kfifo_depth, uint, 0444); #define GB_LOOPBACK_MS_WAIT_MAX 1000 /* interface sysfs attributes */ -#define gb_loopback_ro_attr(field) \ -static ssize_t field##_show(struct device *dev, \ +#define gb_loopback_ro_attr(field, pfx, conn) \ +static ssize_t field##_##pfx##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_connection *connection = to_gb_connection(dev); \ - struct gb_loopback *gb = connection->private; \ - return sprintf(buf, "%u\n", gb->field); \ + struct gb_connection *connection; \ + struct gb_loopback *gb; \ + if (conn) { \ + connection = to_gb_connection(dev); \ + gb = connection->private; \ + return sprintf(buf, "%u\n", gb->field); \ + } else { \ + return sprintf(buf, "%u\n", gb_dev.field); \ + } \ } \ -static DEVICE_ATTR_RO(field) +static DEVICE_ATTR_RO(field##_##pfx) -#define gb_loopback_ro_stats_attr(name, field, type) \ -static ssize_t name##_##field##_show(struct device *dev, \ +#define gb_loopback_ro_stats_attr(name, field, type, pfx, conn) \ +static ssize_t name##_##field##_##pfx##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_connection *connection = to_gb_connection(dev); \ - struct gb_loopback *gb = connection->private; \ - return sprintf(buf, "%"#type"\n", gb->name.field); \ + struct gb_connection *connection; \ + struct gb_loopback *gb; \ + if (conn) { \ + connection = to_gb_connection(dev); \ + gb = connection->private; \ + return sprintf(buf, "%"#type"\n", gb->name.field); \ + } else { \ + return sprintf(buf, "%"#type"\n", gb_dev.name.field); \ + } \ } \ -static DEVICE_ATTR_RO(name##_##field) +static DEVICE_ATTR_RO(name##_##field##_##pfx) -#define gb_loopback_ro_avg_attr(name) \ -static ssize_t name##_avg_show(struct device *dev, \ +#define gb_loopback_ro_avg_attr(name, pfx, conn) \ +static ssize_t name##_avg_##pfx##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_connection *connection = to_gb_connection(dev); \ - struct gb_loopback *gb = connection->private; \ - struct gb_loopback_stats *stats = &gb->name; \ - u32 count = stats->count ? stats->count : 1; \ - u64 avg = stats->sum + count / 2; /* round closest */ \ - u32 rem = do_div(avg, count); \ + struct gb_loopback_stats *stats; \ + struct gb_connection *connection; \ + struct gb_loopback *gb; \ + u64 avg; \ + u32 count, rem; \ + if (conn) { \ + connection = to_gb_connection(dev); \ + gb = connection->private; \ + stats = &gb->name; \ + } else { \ + stats = &gb_dev.name; \ + } \ + count = stats->count ? stats->count : 1; \ + avg = stats->sum + count / 2; /* round closest */ \ + rem = do_div(avg, count); \ return sprintf(buf, "%llu.%06u\n", avg, 1000000 * rem / count); \ } \ -static DEVICE_ATTR_RO(name##_avg) +static DEVICE_ATTR_RO(name##_avg_##pfx) -#define gb_loopback_stats_attrs(field) \ - gb_loopback_ro_stats_attr(field, min, u); \ - gb_loopback_ro_stats_attr(field, max, u); \ - gb_loopback_ro_avg_attr(field); +#define gb_loopback_stats_attrs(field, pfx, conn) \ + gb_loopback_ro_stats_attr(field, min, u, pfx, conn); \ + gb_loopback_ro_stats_attr(field, max, u, pfx, conn); \ + gb_loopback_ro_avg_attr(field, pfx, conn) #define gb_loopback_attr(field, type) \ static ssize_t field##_show(struct device *dev, \ @@ -165,8 +186,8 @@ static ssize_t field##_store(struct device *dev, \ } \ static DEVICE_ATTR_RW(field) -#define gb_dev_loopback_ro_attr(field) \ -static ssize_t field##_show(struct device *dev, \ +#define gb_dev_loopback_ro_attr(field, conn) \ +static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ @@ -241,15 +262,20 @@ static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, } /* Time to send and receive one message */ -gb_loopback_stats_attrs(latency); +gb_loopback_stats_attrs(latency, dev, false); +gb_loopback_stats_attrs(latency, con, true); /* Time to send and receive one message not including greybus */ -gb_loopback_stats_attrs(latency_gb); +gb_loopback_stats_attrs(latency_gb, dev, false); +gb_loopback_stats_attrs(latency_gb, con, true); /* Number of requests sent per second on this cport */ -gb_loopback_stats_attrs(requests_per_second); +gb_loopback_stats_attrs(requests_per_second, dev, false); +gb_loopback_stats_attrs(requests_per_second, con, true); /* Quantity of data sent and received on this cport */ -gb_loopback_stats_attrs(throughput); +gb_loopback_stats_attrs(throughput, dev, false); +gb_loopback_stats_attrs(throughput, con, true); /* Number of errors encountered during loop */ -gb_loopback_ro_attr(error); +gb_loopback_ro_attr(error, dev, false); +gb_loopback_ro_attr(error, con, true); /* * Type of loopback message to send based on protocol type definitions @@ -267,33 +293,51 @@ gb_dev_loopback_rw_attr(ms_wait, d); /* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */ gb_dev_loopback_rw_attr(iteration_max, u); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ -gb_dev_loopback_ro_attr(iteration_count); +gb_dev_loopback_ro_attr(iteration_count, false); /* A bit-mask of destination connecitons to include in the test run */ gb_dev_loopback_rw_attr(mask, u); -static struct attribute *loopback_attrs[] = { - &dev_attr_latency_min.attr, - &dev_attr_latency_max.attr, - &dev_attr_latency_avg.attr, - &dev_attr_latency_gb_min.attr, - &dev_attr_latency_gb_max.attr, - &dev_attr_latency_gb_avg.attr, - &dev_attr_requests_per_second_min.attr, - &dev_attr_requests_per_second_max.attr, - &dev_attr_requests_per_second_avg.attr, - &dev_attr_throughput_min.attr, - &dev_attr_throughput_max.attr, - &dev_attr_throughput_avg.attr, +static struct attribute *loopback_dev_attrs[] = { + &dev_attr_latency_min_dev.attr, + &dev_attr_latency_max_dev.attr, + &dev_attr_latency_avg_dev.attr, + &dev_attr_latency_gb_min_dev.attr, + &dev_attr_latency_gb_max_dev.attr, + &dev_attr_latency_gb_avg_dev.attr, + &dev_attr_requests_per_second_min_dev.attr, + &dev_attr_requests_per_second_max_dev.attr, + &dev_attr_requests_per_second_avg_dev.attr, + &dev_attr_throughput_min_dev.attr, + &dev_attr_throughput_max_dev.attr, + &dev_attr_throughput_avg_dev.attr, &dev_attr_type.attr, &dev_attr_size.attr, &dev_attr_ms_wait.attr, &dev_attr_iteration_count.attr, &dev_attr_iteration_max.attr, &dev_attr_mask.attr, - &dev_attr_error.attr, + &dev_attr_error_dev.attr, NULL, }; -ATTRIBUTE_GROUPS(loopback); +ATTRIBUTE_GROUPS(loopback_dev); + +static struct attribute *loopback_con_attrs[] = { + &dev_attr_latency_min_con.attr, + &dev_attr_latency_max_con.attr, + &dev_attr_latency_avg_con.attr, + &dev_attr_latency_gb_min_con.attr, + &dev_attr_latency_gb_max_con.attr, + &dev_attr_latency_gb_avg_con.attr, + &dev_attr_requests_per_second_min_con.attr, + &dev_attr_requests_per_second_max_con.attr, + &dev_attr_requests_per_second_avg_con.attr, + &dev_attr_throughput_min_con.attr, + &dev_attr_throughput_max_con.attr, + &dev_attr_throughput_avg_con.attr, + &dev_attr_error_con.attr, + NULL, +}; +ATTRIBUTE_GROUPS(loopback_con); static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs) { @@ -776,6 +820,27 @@ static const struct file_operations gb_loopback_debugfs_latency_ops = { .release = single_release, }; +static int gb_loopback_dbgfs_dev_latency_show(struct seq_file *s, void *unused) +{ + struct gb_loopback_device *gb_dev = s->private; + + return gb_loopback_dbgfs_latency_show_common(s, &gb_dev->kfifo, + &gb_dev->mutex); +} + +static int gb_loopback_dev_latency_open(struct inode *inode, struct file *file) +{ + return single_open(file, gb_loopback_dbgfs_dev_latency_show, + inode->i_private); +} + +static const struct file_operations gb_loopback_debugfs_dev_latency_ops = { + .open = gb_loopback_dev_latency_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + #define DEBUGFS_NAMELEN 32 static int gb_loopback_connection_init(struct gb_connection *connection) @@ -783,12 +848,39 @@ static int gb_loopback_connection_init(struct gb_connection *connection) struct gb_loopback *gb; int retval; char name[DEBUGFS_NAMELEN]; + struct kobject *kobj = &connection->bundle->intf->module->dev.kobj; gb = kzalloc(sizeof(*gb), GFP_KERNEL); if (!gb) return -ENOMEM; gb_loopback_reset_stats(&gb_dev); + /* If this is the first connection - create a module endo0:x entries */ + mutex_lock(&gb_dev.mutex); + if (!gb_dev.count) { + snprintf(name, sizeof(name), "raw_latency_endo0:%d", + connection->bundle->intf->module->module_id); + debugfs_create_file(name, S_IFREG | S_IRUGO, + gb_dev.root, &gb_dev, + &gb_loopback_debugfs_dev_latency_ops); + retval = sysfs_create_groups(kobj, loopback_dev_groups); + if (retval) { + mutex_unlock(&gb_dev.mutex); + goto out_sysfs; + } + /* Calculate maximum payload */ + gb_dev.size_max = gb_operation_get_payload_size_max(connection); + if (gb_dev.size_max <= + sizeof(struct gb_loopback_transfer_request)) { + retval = -EINVAL; + mutex_unlock(&gb_dev.mutex); + goto out_sysfs; + } + gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request); + } + mutex_unlock(&gb_dev.mutex); + + /* Create per-connection sysfs and debugfs data-points */ snprintf(name, sizeof(name), "raw_latency_endo0:%d:%d:%d:%d", connection->bundle->intf->module->module_id, connection->bundle->intf->interface_id, @@ -798,25 +890,16 @@ static int gb_loopback_connection_init(struct gb_connection *connection) &gb_loopback_debugfs_latency_ops); gb->connection = connection; connection->private = gb; - retval = sysfs_create_groups(&connection->dev.kobj, loopback_groups); + retval = sysfs_create_groups(&connection->dev.kobj, + loopback_con_groups); if (retval) - goto out_debugfs; - - /* Calculate maximum payload */ - mutex_lock(&gb_dev.mutex); - gb_dev.size_max = gb_operation_get_payload_size_max(connection); - if (gb_dev.size_max <= sizeof(struct gb_loopback_transfer_request)) { - retval = -EINVAL; - goto out_sysfs; - } - gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request); - mutex_unlock(&gb_dev.mutex); + goto out_sysfs_dev; /* Allocate kfifo */ if (kfifo_alloc(&gb->kfifo_lat, kfifo_depth * sizeof(u32), GFP_KERNEL)) { retval = -ENOMEM; - goto out_sysfs; + goto out_sysfs_conn; } if (kfifo_alloc(&gb->kfifo_ts, kfifo_depth * sizeof(struct timeval) * 2, GFP_KERNEL)) { @@ -843,11 +926,14 @@ out_kfifo1: kfifo_free(&gb->kfifo_ts); out_kfifo0: kfifo_free(&gb->kfifo_lat); -out_sysfs: - sysfs_remove_groups(&connection->dev.kobj, loopback_groups); -out_debugfs: +out_sysfs_conn: + sysfs_remove_groups(&connection->dev.kobj, loopback_con_groups); +out_sysfs_dev: + if (!gb_dev.count) + sysfs_remove_groups(kobj, loopback_dev_groups); debugfs_remove(gb->file); connection->private = NULL; +out_sysfs: kfree(gb); return retval; @@ -856,6 +942,7 @@ out_debugfs: static void gb_loopback_connection_exit(struct gb_connection *connection) { struct gb_loopback *gb = connection->private; + struct kobject *kobj = &connection->bundle->intf->module->dev.kobj; gb_dev.count--; connection->private = NULL; @@ -864,7 +951,9 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); - sysfs_remove_groups(&connection->dev.kobj, loopback_groups); + if (!gb_dev.count) + sysfs_remove_groups(kobj, loopback_dev_groups); + sysfs_remove_groups(&connection->dev.kobj, loopback_con_groups); debugfs_remove(gb->file); kfree(gb); } @@ -879,27 +968,6 @@ static struct gb_protocol loopback_protocol = { .request_recv = gb_loopback_request_recv, }; -static int gb_loopback_dbgfs_dev_latency_show(struct seq_file *s, void *unused) -{ - struct gb_loopback_device *gb_dev = s->private; - - return gb_loopback_dbgfs_latency_show_common(s, &gb_dev->kfifo, - &gb_dev->mutex); -} - -static int gb_loopback_dev_latency_open(struct inode *inode, struct file *file) -{ - return single_open(file, gb_loopback_dbgfs_dev_latency_show, - inode->i_private); -} - -static const struct file_operations gb_loopback_debugfs_dev_latency_ops = { - .open = gb_loopback_dev_latency_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static int loopback_init(void) { int retval; @@ -914,9 +982,6 @@ static int loopback_init(void) goto error_debugfs; } - debugfs_create_file("aggregate_latency", S_IFREG | S_IRUGO, - gb_dev.root, &gb_dev, - &gb_loopback_debugfs_dev_latency_ops); retval = gb_protocol_register(&loopback_protocol); if (!retval) return retval; -- cgit v1.2.3-59-g8ed1b From 98676ca83680a2ca6afb38e900b3d956f6917185 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 17 Aug 2015 00:55:12 +0100 Subject: greybus: loopback: fix typo in comment Alex previously post a patch to fix this typo. Somehow it fell through the cracks in the meantime. Do it again 'stastic' is a word 'statistic' is not. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 01bed4398826..8dd648cc0796 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -675,7 +675,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) /* Express latency in terms of microseconds */ lat = gb_loopback_nsec_to_usec_latency(gb->elapsed_nsecs); - /* Log latency statistic */ + /* Log latency stastic */ gb_loopback_update_stats(&gb_dev.latency, lat); gb_loopback_update_stats(&gb->latency, lat); -- cgit v1.2.3-59-g8ed1b From 977e209ab41073def3cf0e1034c429a832ba54df Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 31 Aug 2015 09:00:16 +0200 Subject: greybus: es1/es2: set transfer flag to send a zero-length packet Greybus messages with a multiple size of 512B generate timeouts (any other message size doesn't). 512B is exactly the packet size of a bulk out endpoint. Hence USB device is expecting a short (< 512B) or zero-length packet to finish the transfer, which is never generated and causes the timeout. Set the transfer flag to send a zero-length packet in this situation. Signed-off-by: Alexandre Bailon Reviewed-by: Patrick Titiano Reviewed-by: Johan Hovold Signed-off-by: Johan Hovold --- drivers/staging/greybus/es1.c | 1 + drivers/staging/greybus/es2.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 2aa271751ac1..ddc26dec3150 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -216,6 +216,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, usb_sndbulkpipe(udev, es1->cport_out_endpoint), message->buffer, buffer_size, cport_out_callback, message); + urb->transfer_flags |= URB_ZERO_PACKET; gb_connection_push_timestamp(message->operation->connection); retval = usb_submit_urb(urb, gfp_mask); if (retval) { diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 008685b2b15b..295cc6839551 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -312,6 +312,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, es1->cport_out[bulk_ep_set].endpoint), message->buffer, buffer_size, cport_out_callback, message); + urb->transfer_flags |= URB_ZERO_PACKET; gb_connection_push_timestamp(message->operation->connection); retval = usb_submit_urb(urb, gfp_mask); if (retval) { -- cgit v1.2.3-59-g8ed1b From af0b4d5a19e3d49059afe91307fce980291f43f9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 28 Aug 2015 11:58:24 +0200 Subject: greybus: firmware: fix potential stack corruption Use snprintf when generating the firmware name to avoid stack corruption if the fixed-size buffer overflows. Note that the current buffer size appears to expect 16-bit ids while the they are actually 32-bit, something which could trigger the corruption. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar --- drivers/staging/greybus/firmware.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 13efaabb891b..e888b7ae4c59 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -38,9 +38,10 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) * * XXX Name it properly.. */ - sprintf(firmware_name, "ara:%04x:%04x:%04x:%04x:%04x.fw", intf->unipro_mfg_id, - intf->unipro_prod_id, intf->ara_vend_id, intf->ara_prod_id, - stage); + snprintf(firmware_name, sizeof(firmware_name), + "ara:%04x:%04x:%04x:%04x:%04x.fw", + intf->unipro_mfg_id, intf->unipro_prod_id, + intf->ara_vend_id, intf->ara_prod_id, stage); return request_firmware(&firmware->fw, firmware_name, &connection->dev); } -- cgit v1.2.3-59-g8ed1b From 2d465d57c8bbb0da96f352447e283e06bfa8513c Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 20 Aug 2015 15:20:17 +0100 Subject: greybus: sdio: fix command type defines Broadcast command with response and without response where swapped related to what is defined in greybus specification. Make it coherent with the document. Signed-off-by: Rui Miguel Silva Signed-off-by: Johan Hovold --- drivers/staging/greybus/greybus_protocols.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 0c11bec39065..b84c710da4a9 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1031,8 +1031,8 @@ struct gb_sdio_command_request { __u8 cmd_type; #define GB_SDIO_CMD_AC 0x00 #define GB_SDIO_CMD_ADTC 0x01 -#define GB_SDIO_CMD_BCR 0x02 -#define GB_SDIO_CMD_BC 0x03 +#define GB_SDIO_CMD_BC 0x02 +#define GB_SDIO_CMD_BCR 0x03 __le32 cmd_arg; } __packed; -- cgit v1.2.3-59-g8ed1b From c01c77ce4b2a7a39134d90f90ab964926ef88123 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 1 Sep 2015 12:25:24 +0200 Subject: greybus: endo: fix endo-id allocation flag Use GFP_KERNEL for endo ida allocation in gb_endo_register, which is not called from atomic context. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar --- drivers/staging/greybus/endo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index baa4aa581096..84d695df2d62 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -439,7 +439,7 @@ static int gb_endo_register(struct greybus_host_device *hd, { int retval; - retval = ida_simple_get(&greybus_endo_id_map, 0, 0, GFP_ATOMIC); + retval = ida_simple_get(&greybus_endo_id_map, 0, 0, GFP_KERNEL); if (retval < 0) return retval; -- cgit v1.2.3-59-g8ed1b From 89f637f716a90ec9da7a26d53ad883ea2b806e66 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 1 Sep 2015 12:25:25 +0200 Subject: greybus: svc: fix device-id allocation flag Use GFP_KERNEL for device-id allocation in svc_process_hotplug, which is called from a work queue. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 452f81b61203..029b5a7d1560 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -304,7 +304,7 @@ static void svc_process_hotplug(struct work_struct *work) * XXX about an AP with multiple interface blocks? */ device_id = ida_simple_get(&greybus_svc_device_id_map, - GB_DEVICE_ID_MODULES_START, 0, GFP_ATOMIC); + GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); if (device_id < 0) { ret = device_id; dev_err(dev, "%s: Failed to allocate device id for interface with id %hhu (%d)\n", -- cgit v1.2.3-59-g8ed1b From 287bba82fe2f74dd89f7f24f2da438a7974e57af Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 1 Sep 2015 12:25:26 +0200 Subject: greybus: svc: fix hot-plug-state allocation flag Use GFP_KERNEL for hot-plug state allocation in gb_svc_intf_hotplug_recv, which is called from a request handler (i.e. a work queue). Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 029b5a7d1560..db009223d759 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -382,7 +382,7 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) return -EINVAL; } - svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_ATOMIC); + svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_KERNEL); if (!svc_hotplug) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 505c9d27b804f2b1e1e6432a89876df197432bbe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 1 Sep 2015 12:25:27 +0200 Subject: greybus: interface: fix potential attribute-buffer overflow Use scnprintf in the generic attribute helper, which does not currently check for buffer overflow. The attribute helper is used to print generic strings, which could potentially overflow the buffer. Note that the only strings currently exported are taken from greybus string descriptors and should therefore be limited to 255 chars. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Reviewed-by: Bryan O'Donoghue --- drivers/staging/greybus/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c5211a313d34..0c3613e16d57 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -16,7 +16,7 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct gb_interface *intf = to_gb_interface(dev); \ - return sprintf(buf, "%"#type"\n", intf->field); \ + return scnprintf(buf, PAGE_SIZE, "%"#type"\n", intf->field); \ } \ static DEVICE_ATTR_RO(field) -- cgit v1.2.3-59-g8ed1b From 2630fbf828ed921bd52cfe1fbacc9ad16aa2f47a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 1 Sep 2015 12:25:28 +0200 Subject: greybus: pwm: replace pr_err with dev_err Replace pr_err with the more descriptive dev_err. Also include the error code on failure to register the PWM chip. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar --- drivers/staging/greybus/pwm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 5f335895d230..d91905f0f7b3 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -204,7 +204,7 @@ static int gb_pwm_connection_init(struct gb_connection *connection) ret = pwmchip_add(pwm); if (ret) { - pr_err("Failed to register PWM\n"); + dev_err(&connection->dev, "failed to register PWM: %d\n", ret); goto out_err; } -- cgit v1.2.3-59-g8ed1b From b9fb704afd6e2e3c749fc076f1d8fa36ae695d4d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 1 Sep 2015 17:16:16 +0530 Subject: greybus: svc: get hd directly from connection There is no need to perform connection->bundle->intf->hd as the same can be done with connection->hd. Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/svc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index db009223d759..ac8ff44f3a89 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -272,7 +272,7 @@ static void svc_process_hotplug(struct work_struct *work) struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data; struct gb_connection *connection = svc_hotplug->connection; struct gb_svc *svc = connection->private; - struct greybus_host_device *hd = connection->bundle->intf->hd; + struct greybus_host_device *hd = connection->hd; struct device *dev = &connection->dev; struct gb_interface *intf; u8 intf_id, device_id; @@ -399,7 +399,7 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) { struct gb_message *request = op->request; struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload; - struct greybus_host_device *hd = op->connection->bundle->intf->hd; + struct greybus_host_device *hd = op->connection->hd; struct device *dev = &op->connection->dev; u8 device_id; struct gb_interface *intf; -- cgit v1.2.3-59-g8ed1b From e074a2e2874aaa96da3e54d98ecb886201d96435 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Wed, 2 Sep 2015 15:50:34 +0200 Subject: greybus: es2: rename misnamed CPORT_MAX into CPORT_COUNT Rename the misnamed macro CPORT_MAX into CPORT_COUNT. CPORT_MAX could let people think that the macro is holding the value of the last CPort ID usable. Signed-off-by: Fabien Parent Reviewed-by: Johan Hovold Signed-off-by: Johan Hovold --- drivers/staging/greybus/es2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 295cc6839551..680808959848 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -34,7 +34,7 @@ static struct task_struct *apb1_log_task; static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* Number of cport present on USB bridge */ -#define CPORT_MAX 44 +#define CPORT_COUNT 44 /* Number of bulk in and bulk out couple */ #define NUM_BULKS 7 @@ -108,7 +108,7 @@ struct es1_ap_dev { bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; spinlock_t cport_out_urb_lock; - int cport_to_ep[CPORT_MAX]; + int cport_to_ep[CPORT_COUNT]; }; struct cport_to_ep { @@ -128,7 +128,7 @@ static void usb_log_disable(struct es1_ap_dev *es1); static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) { - if (cport_id >= CPORT_MAX) + if (cport_id >= CPORT_COUNT) return 0; return es1->cport_to_ep[cport_id]; } @@ -139,7 +139,7 @@ static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set) { int i; - for (i = 0; i < CPORT_MAX; i++) { + for (i = 0; i < CPORT_COUNT; i++) { if (es1->cport_to_ep[i] == bulk_ep_set) return 1; } @@ -154,7 +154,7 @@ int map_cport_to_ep(struct es1_ap_dev *es1, if (bulk_ep_set == 0 || bulk_ep_set >= NUM_BULKS) return -EINVAL; - if (cport_id >= CPORT_MAX) + if (cport_id >= CPORT_COUNT) return -EINVAL; if (bulk_ep_set && ep_in_use(es1, bulk_ep_set)) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 144670c2ae3f9b452e021a9032b25730d296ba53 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Wed, 2 Sep 2015 15:50:35 +0200 Subject: greybus: add num_cports field to greybus hd This commit is doing the preparation work in order to get the number of cports supported from the UniPro IP instead of using a constant defined in a Kconfig file. Greybus host device is now holding the cport count, and all the code will now use this value instead of the constant CPORT_ID_MAX when referring to an AP's CPort ID. Signed-off-by: Fabien Parent [johan: es1 supports 256 cports, minor style changes ] Signed-off-by: Johan Hovold --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/core.c | 9 ++++++++- drivers/staging/greybus/es1.c | 10 +++++++--- drivers/staging/greybus/es2.c | 13 +++++++------ drivers/staging/greybus/greybus.h | 10 +++++++--- drivers/staging/greybus/interface.c | 2 +- 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7eb28680e498..286e7da3f275 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -311,7 +311,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, { return gb_connection_create_range(bundle->intf->hd, bundle, &bundle->dev, cport_id, protocol_id, - 0, CPORT_ID_MAX); + 0, bundle->intf->hd->num_cports - 1); } /* diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index af71d027490c..3ba744b0e998 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -168,7 +168,8 @@ static void free_hd(struct kref *kref) struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver, struct device *parent, - size_t buffer_size_max) + size_t buffer_size_max, + size_t num_cports) { struct greybus_host_device *hd; @@ -186,6 +187,11 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver return NULL; } + if (num_cports == 0 || num_cports > CPORT_ID_MAX) { + dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports); + return ERR_PTR(-EINVAL); + } + /* * Make sure to never allocate messages larger than what the Greybus * protocol supports. @@ -207,6 +213,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver INIT_LIST_HEAD(&hd->connections); ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; + hd->num_cports = num_cports; /* * Initialize AP's SVC protocol connection: diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index ddc26dec3150..fc4297a8bdb2 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -33,6 +33,9 @@ static struct dentry *apb1_log_enable_dentry; static struct task_struct *apb1_log_task; static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); +/* Number of CPorts supported by es1 */ +#define CPORT_COUNT 256 + /* * Number of CPort IN urbs in flight at any point in time. * Adjust if we are having stalls in the USB buffer due to not enough urbs in @@ -193,7 +196,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, * of where the data should be sent. Do one last check of * the target CPort id before filling it in. */ - if (!cport_id_valid(cport_id)) { + if (!cport_id_valid(hd, cport_id)) { pr_err("invalid destination cport 0x%02x\n", cport_id); return -EINVAL; } @@ -372,7 +375,7 @@ static void cport_in_callback(struct urb *urb) header = urb->transfer_buffer; cport_id = gb_message_cport_unpack(header); - if (cport_id_valid(cport_id)) + if (cport_id_valid(hd, cport_id)) greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); else @@ -560,7 +563,8 @@ static int ap_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); - hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); + hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX, + CPORT_COUNT); if (IS_ERR(hd)) { usb_put_dev(udev); return PTR_ERR(hd); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 680808959848..4b1f34fe1a60 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -128,7 +128,7 @@ static void usb_log_disable(struct es1_ap_dev *es1); static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) { - if (cport_id >= CPORT_COUNT) + if (cport_id >= es1->hd->num_cports) return 0; return es1->cport_to_ep[cport_id]; } @@ -139,7 +139,7 @@ static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set) { int i; - for (i = 0; i < CPORT_COUNT; i++) { + for (i = 0; i < es1->hd->num_cports; i++) { if (es1->cport_to_ep[i] == bulk_ep_set) return 1; } @@ -154,7 +154,7 @@ int map_cport_to_ep(struct es1_ap_dev *es1, if (bulk_ep_set == 0 || bulk_ep_set >= NUM_BULKS) return -EINVAL; - if (cport_id >= CPORT_COUNT) + if (cport_id >= es1->hd->num_cports) return -EINVAL; if (bulk_ep_set && ep_in_use(es1, bulk_ep_set)) return -EINVAL; @@ -287,7 +287,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, * of where the data should be sent. Do one last check of * the target CPort id before filling it in. */ - if (!cport_id_valid(cport_id)) { + if (!cport_id_valid(hd, cport_id)) { pr_err("invalid destination cport 0x%02x\n", cport_id); return -EINVAL; } @@ -472,7 +472,7 @@ static void cport_in_callback(struct urb *urb) header = urb->transfer_buffer; cport_id = gb_message_cport_unpack(header); - if (cport_id_valid(cport_id)) + if (cport_id_valid(hd, cport_id)) greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); else @@ -660,7 +660,8 @@ static int ap_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); - hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); + hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX, + CPORT_COUNT); if (IS_ERR(hd)) { usb_put_dev(udev); return PTR_ERR(hd); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 57265558c682..27a772421529 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -96,6 +96,9 @@ struct greybus_host_device { struct ida cport_id_map; u8 device_id; + /* Number of CPorts supported by the UniPro IP */ + size_t num_cports; + /* Host device buffer constraints */ size_t buffer_size_max; @@ -109,7 +112,8 @@ struct greybus_host_device { struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, struct device *parent, - size_t buffer_size_max); + size_t buffer_size_max, + size_t num_cports); int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id, u8 ap_intf_id); void greybus_remove_hd(struct greybus_host_device *hd); @@ -191,9 +195,9 @@ static inline int is_gb_connection(const struct device *dev) return dev->type == &greybus_connection_type; } -static inline bool cport_id_valid(u16 cport_id) +static inline bool cport_id_valid(struct greybus_host_device *hd, u16 cport_id) { - return cport_id != CPORT_ID_BAD && cport_id <= CPORT_ID_MAX; + return cport_id != CPORT_ID_BAD && cport_id < hd->num_cports; } #endif /* __KERNEL__ */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 0c3613e16d57..c38fb8b31c2b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -84,7 +84,7 @@ int gb_create_bundle_connection(struct gb_interface *intf, u8 class) bundle_id = GB_CONTROL_BUNDLE_ID; cport_id = GB_CONTROL_CPORT_ID; ida_start = 0; - ida_end = CPORT_ID_MAX; + ida_end = intf->hd->num_cports - 1; } else if (class == GREYBUS_CLASS_SVC) { protocol_id = GREYBUS_PROTOCOL_SVC; bundle_id = GB_SVC_BUNDLE_ID; -- cgit v1.2.3-59-g8ed1b From c011d558e19aa9b6bb739426f5553bf3e1ad5a69 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Wed, 2 Sep 2015 15:50:36 +0200 Subject: greybus: es2: dynamically allocate array for cport <-> ep mapping In order to be able to dynamically determine the number of CPorts supported by the UniPro IP instead of hardcoding the value we need to dynamically allocate the array that is doing the cport-ep mapping. Signed-off-by: Fabien Parent Reviewed-by: Johan Hovold Signed-off-by: Johan Hovold --- drivers/staging/greybus/es2.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 4b1f34fe1a60..909ca6acae1c 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -108,7 +108,7 @@ struct es1_ap_dev { bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; spinlock_t cport_out_urb_lock; - int cport_to_ep[CPORT_COUNT]; + int *cport_to_ep; }; struct cport_to_ep { @@ -443,6 +443,7 @@ static void ap_disconnect(struct usb_interface *interface) usb_set_intfdata(interface, NULL); udev = es1->usb_dev; greybus_remove_hd(es1->hd); + kfree(es1->cport_to_ep); usb_put_dev(udev); } @@ -678,6 +679,13 @@ static int ap_probe(struct usb_interface *interface, endpoint = &udev->ep0.desc; es1->control_endpoint = endpoint->bEndpointAddress; + es1->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es1->cport_to_ep), + GFP_KERNEL); + if (!es1->cport_to_ep) { + retval = -ENOMEM; + goto error; + } + /* find all 3 of our endpoints */ iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { -- cgit v1.2.3-59-g8ed1b From 24a6112fa53e8a08de21b063abb22d10c2595048 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Wed, 2 Sep 2015 15:50:37 +0200 Subject: greybus: es2: get cport count from apb1 usb device Use the control request REQUEST_CPORT_COUNT in order to get the number of CPorts supported by the UniPro IP. Signed-off-by: Fabien Parent Signed-off-by: Johan Hovold --- drivers/staging/greybus/es2.c | 53 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 909ca6acae1c..13605b8f873b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -33,9 +33,6 @@ static struct dentry *apb1_log_enable_dentry; static struct task_struct *apb1_log_task; static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); -/* Number of cport present on USB bridge */ -#define CPORT_COUNT 44 - /* Number of bulk in and bulk out couple */ #define NUM_BULKS 7 @@ -60,6 +57,9 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* vendor request to map a cport to bulk in and bulk out endpoints */ #define REQUEST_EP_MAPPING 0x03 +/* vendor request to get the number of cports available */ +#define REQUEST_CPORT_COUNT 0x04 + /* * @endpoint: bulk in endpoint for CPort data * @urb: array of urbs for the CPort in messages @@ -636,6 +636,39 @@ static const struct file_operations apb1_log_enable_fops = { .write = apb1_log_enable_write, }; +static int apb1_get_cport_count(struct usb_device *udev) +{ + int retval; + __le16 *cport_count; + + cport_count = kmalloc(sizeof(*cport_count), GFP_KERNEL); + if (!cport_count) + return -ENOMEM; + + retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + REQUEST_CPORT_COUNT, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, cport_count, + sizeof(*cport_count), ES1_TIMEOUT); + if (retval < 0) { + dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", + retval); + goto out; + } + + retval = le16_to_cpu(*cport_count); + + /* We need to fit a CPort ID in one byte of a message header */ + if (retval > U8_MAX) { + retval = U8_MAX; + dev_warn(&udev->dev, "Limiting number of CPorts to U8_MAX\n"); + } + +out: + kfree(cport_count); + return retval; +} + /* * The ES1 USB Bridge device contains 4 endpoints * 1 Control - usual USB stuff + AP -> SVC messages @@ -655,14 +688,20 @@ static int ap_probe(struct usb_interface *interface, int bulk_out = 0; int retval = -ENOMEM; int i; - - /* We need to fit a CPort ID in one byte of a message header */ - BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); + int num_cports; udev = usb_get_dev(interface_to_usbdev(interface)); + num_cports = apb1_get_cport_count(udev); + if (num_cports < 0) { + usb_put_dev(udev); + dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", + num_cports); + return num_cports; + } + hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX, - CPORT_COUNT); + num_cports); if (IS_ERR(hd)) { usb_put_dev(udev); return PTR_ERR(hd); -- cgit v1.2.3-59-g8ed1b From f470ead894e3d33e70f62d43b8e255beb37f183b Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Wed, 2 Sep 2015 15:50:38 +0200 Subject: greybus: es{1,2}: remove control endpoint field There is no need to store the endpoint number of the control requests since the default control endpoint is used and the USB standard defines for it a fixed endpoint number of 0. Remove every instance of the field control_endpoint and replace it with a hardcoded 0 value. Signed-off-by: Fabien Parent Reviewed-by: Johan Hovold Signed-off-by: Johan Hovold --- drivers/staging/greybus/es1.c | 9 +-------- drivers/staging/greybus/es2.c | 13 ++----------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index fc4297a8bdb2..f049d655b82e 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -59,7 +59,6 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); * @usb_dev: pointer to the USB device we are. * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure - * @control_endpoint: endpoint to send data to SVC * @cport_in_endpoint: bulk in endpoint for CPort data * @cport-out_endpoint: bulk out endpoint for CPort data * @cport_in_urb: array of urbs for the CPort in messages @@ -76,7 +75,6 @@ struct es1_ap_dev { struct usb_interface *usb_intf; struct greybus_host_device *hd; - __u8 control_endpoint; __u8 cport_in_endpoint; __u8 cport_out_endpoint; @@ -420,8 +418,7 @@ static void apb1_log_get(struct es1_ap_dev *es1, char *buf) /* SVC messages go down our control pipe */ do { retval = usb_control_msg(es1->usb_dev, - usb_rcvctrlpipe(es1->usb_dev, - es1->control_endpoint), + usb_rcvctrlpipe(es1->usb_dev, 0), REQUEST_LOG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, @@ -577,10 +574,6 @@ static int ap_probe(struct usb_interface *interface, spin_lock_init(&es1->cport_out_urb_lock); usb_set_intfdata(interface, es1); - /* Control endpoint is the pipe to talk to this AP, so save it off */ - endpoint = &udev->ep0.desc; - es1->control_endpoint = endpoint->bEndpointAddress; - /* find all 3 of our endpoints */ iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 13605b8f873b..d6bd210a6337 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -83,7 +83,6 @@ struct es1_cport_out { * @usb_dev: pointer to the USB device we are. * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure - * @control_endpoint: endpoint to send data to SVC * @cport_in: endpoint, urbs and buffer for cport in messages * @cport_out: endpoint for for cport out messages @@ -99,8 +98,6 @@ struct es1_ap_dev { struct usb_interface *usb_intf; struct greybus_host_device *hd; - __u8 control_endpoint; - struct es1_cport_in cport_in[NUM_BULKS]; struct es1_cport_out cport_out[NUM_BULKS]; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; @@ -169,8 +166,7 @@ int map_cport_to_ep(struct es1_ap_dev *es1, cport_to_ep->endpoint_out = es1->cport_out[bulk_ep_set].endpoint; retval = usb_control_msg(es1->usb_dev, - usb_sndctrlpipe(es1->usb_dev, - es1->control_endpoint), + usb_sndctrlpipe(es1->usb_dev, 0), REQUEST_EP_MAPPING, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, @@ -518,8 +514,7 @@ static void apb1_log_get(struct es1_ap_dev *es1, char *buf) /* SVC messages go down our control pipe */ do { retval = usb_control_msg(es1->usb_dev, - usb_rcvctrlpipe(es1->usb_dev, - es1->control_endpoint), + usb_rcvctrlpipe(es1->usb_dev, 0), REQUEST_LOG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, @@ -714,10 +709,6 @@ static int ap_probe(struct usb_interface *interface, spin_lock_init(&es1->cport_out_urb_lock); usb_set_intfdata(interface, es1); - /* Control endpoint is the pipe to talk to this AP, so save it off */ - endpoint = &udev->ep0.desc; - es1->control_endpoint = endpoint->bEndpointAddress; - es1->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es1->cport_to_ep), GFP_KERNEL); if (!es1->cport_to_ep) { -- cgit v1.2.3-59-g8ed1b From 1a58a3befaccdf84f5a31e3cecd26c8d57c10890 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Sep 2015 17:37:38 +0200 Subject: greybus: core: fix hd-creation error path Make sure to return an errno when a host-device buffer-size check fails. Fixes: 1f92f6404614 ("core: return error code when creating host device") Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar --- drivers/staging/greybus/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 3ba744b0e998..605a0887dc61 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -184,7 +184,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) { dev_err(parent, "greybus host-device buffers too small\n"); - return NULL; + return ERR_PTR(-EINVAL); } if (num_cports == 0 || num_cports > CPORT_ID_MAX) { -- cgit v1.2.3-59-g8ed1b From 239adcf6bd68dd7b1910f236e7f2854de535e7aa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Sep 2015 18:03:20 +0200 Subject: greybus: es1: fix build-time cport constraint The CPort count of es1 is now defined by CPORT_COUNT. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar --- drivers/staging/greybus/es1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index f049d655b82e..2b4bb075b9e6 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -556,7 +556,7 @@ static int ap_probe(struct usb_interface *interface, int i; /* We need to fit a CPort ID in one byte of a message header */ - BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); + BUILD_BUG_ON(CPORT_COUNT > U8_MAX + 1); udev = usb_get_dev(interface_to_usbdev(interface)); -- cgit v1.2.3-59-g8ed1b From 1dc53922655a1f2537488a9d52aa99d111f38f66 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Sep 2015 18:03:21 +0200 Subject: greybus: fix cport-id defines The CPORT_ID_MAX define has been used by host drivers as a device limit, but also for sanity checks when parsing manifests. Now that it's only used for sanity checks we can increase it to the specification maximum (4095) and get rid of the config-option that could be used to override the previous limit (128). Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar --- drivers/staging/greybus/greybus.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 27a772421529..8e215f870c1d 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -53,15 +53,9 @@ .match_flags = GREYBUS_DEVICE_ID_MATCH_SERIAL, \ .serial_number = (s), -/* XXX I couldn't get my Kconfig file to be noticed for out-of-tree build */ -#ifndef CONFIG_CPORT_ID_MAX -#define CONFIG_CPORT_ID_MAX 128 -#endif /* !CONFIG_CPORT_ID_MAX */ - -/* Maximum number of CPorts usable by a host device */ -/* XXX This should really be determined by the AP module manifest */ -#define CPORT_ID_MAX CONFIG_CPORT_ID_MAX -#define CPORT_ID_BAD U16_MAX /* UniPro max id is 4095 */ +/* Maximum number of CPorts */ +#define CPORT_ID_MAX 4095 /* UniPro max id is 4095 */ +#define CPORT_ID_BAD U16_MAX /* For SP1 hardware, we are going to "hardcode" each device to have all logical * blocks in order to be able to address them as one unified "unit". Then -- cgit v1.2.3-59-g8ed1b From 3a17dd413a1857fc92b22e54c03cae155f106056 Mon Sep 17 00:00:00 2001 From: Vishal Bhoj Date: Wed, 2 Sep 2015 22:35:19 -0700 Subject: greybus: build: android: avoid building for targets that don't have kernel binary For sdk related targets, the greybus build will error out due to missing kernel dependency. Let's avoid those targets by checking TARGET_NO_KERNEL. Signed-off-by: Vishal Bhoj Signed-off-by: Michael Scott Signed-off-by: Johan Hovold --- drivers/staging/greybus/Android.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/Android.mk b/drivers/staging/greybus/Android.mk index 112f4dedb0d8..3357499ae0fd 100644 --- a/drivers/staging/greybus/Android.mk +++ b/drivers/staging/greybus/Android.mk @@ -1,6 +1,8 @@ .PHONY: build-greybus +ifneq ($(TARGET_NO_KERNEL), true) $(PRODUCT_OUT)/ramdisk.img: build-greybus +endif GREYBUS_MODULE_OUT_PATH := $(PRODUCT_OUT)/root/lib/modules -- cgit v1.2.3-59-g8ed1b From f66427adfd407ea08236ab541df192e15671b6cf Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 2 Sep 2015 21:27:13 +0530 Subject: greybus: svc: Include system headers at the top System headers should get included before greybus.h. Its followed everywhere except svc.c. Fix it. Reported-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/svc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ac8ff44f3a89..3b37dfeb8ef4 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -7,9 +7,10 @@ * Released under the GPLv2 only. */ -#include "greybus.h" #include +#include "greybus.h" + #define CPORT_FLAGS_E2EFC (1) #define CPORT_FLAGS_CSD_N (2) #define CPORT_FLAGS_CSV_N (4) -- cgit v1.2.3-59-g8ed1b From 3ccb1600babfda0c71cba376e21f4f95a61080a2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 3 Sep 2015 15:42:22 +0530 Subject: greybus: svc: reject invalid state requests The request sequence for SVC protocol is fixed at least upto SVC_HELLO request. The first request has to be Protocol Version, followed by SVC_HELLO. Any other request can follow them, but these two. Add another field in 'struct gb_svc' that keeps track of current state of the protocol driver. It tracks only upto SVC_HELLO, as we don't need to track later ones. Also add a comment, about the order in which the requests are allowed and why a race can't happen while accessing 'state'. This removes the WARN_ON() in gb_svc_hello() as we track state transition with 'state' field. This also fixes a crash, when the hotplug request is received before fully initializing the svc connection. The crash mostly happens while accessing svc->connection->bundle, which is NULL, but can happen at other places too, as svc connection isn't fully initialized. Reported-by: Johan Hovold Signed-off-by: Viresh Kumar [johan: add 0x-prefix to warning message ] Signed-off-by: Johan Hovold --- drivers/staging/greybus/svc.c | 57 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 3b37dfeb8ef4..42b89ee9e094 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -15,8 +15,15 @@ #define CPORT_FLAGS_CSD_N (2) #define CPORT_FLAGS_CSV_N (4) +enum gb_svc_state { + GB_SVC_STATE_RESET, + GB_SVC_STATE_PROTOCOL_VERSION, + GB_SVC_STATE_SVC_HELLO, +}; + struct gb_svc { struct gb_connection *connection; + enum gb_svc_state state; }; struct svc_hotplug { @@ -226,9 +233,6 @@ static int gb_svc_hello(struct gb_operation *op) u8 interface_id; int ret; - /* Hello message should be received only during early bootup */ - WARN_ON(hd->initial_svc_connection != connection); - /* * SVC sends information about the endo and interface-id on the hello * request, use that to create an endo. @@ -452,11 +456,53 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) static int gb_svc_request_recv(u8 type, struct gb_operation *op) { + struct gb_connection *connection = op->connection; + struct gb_svc *svc = connection->private; + int ret = 0; + + /* + * SVC requests need to follow a specific order (at least initially) and + * below code takes care of enforcing that. The expected order is: + * - PROTOCOL_VERSION + * - SVC_HELLO + * - Any other request, but the earlier two. + * + * Incoming requests are guaranteed to be serialized and so we don't + * need to protect 'state' for any races. + */ switch (type) { case GB_REQUEST_TYPE_PROTOCOL_VERSION: - return gb_svc_version_request(op); + if (svc->state != GB_SVC_STATE_RESET) + ret = -EINVAL; + break; case GB_SVC_TYPE_SVC_HELLO: - return gb_svc_hello(op); + if (svc->state != GB_SVC_STATE_PROTOCOL_VERSION) + ret = -EINVAL; + break; + default: + if (svc->state != GB_SVC_STATE_SVC_HELLO) + ret = -EINVAL; + break; + } + + if (ret) { + dev_warn(&connection->dev, + "unexpected SVC request 0x%02x received (state %u)\n", + type, svc->state); + return ret; + } + + switch (type) { + case GB_REQUEST_TYPE_PROTOCOL_VERSION: + ret = gb_svc_version_request(op); + if (!ret) + svc->state = GB_SVC_STATE_PROTOCOL_VERSION; + return ret; + case GB_SVC_TYPE_SVC_HELLO: + ret = gb_svc_hello(op); + if (!ret) + svc->state = GB_SVC_STATE_SVC_HELLO; + return ret; case GB_SVC_TYPE_INTF_HOTPLUG: return gb_svc_intf_hotplug_recv(op); case GB_SVC_TYPE_INTF_HOT_UNPLUG: @@ -479,6 +525,7 @@ static int gb_svc_connection_init(struct gb_connection *connection) return -ENOMEM; connection->hd->svc = svc; + svc->state = GB_SVC_STATE_RESET; svc->connection = connection; connection->private = svc; -- cgit v1.2.3-59-g8ed1b From d6ec787299c242775ef1e49c6f558121effa248c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:02 +0530 Subject: greybus: greybus_protocols: svc: Add missing comment All request/responses either have a structure representing them or a comment saying the request/response payload doesn't exist. The comment was missing for route create response message, add it. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/greybus_protocols.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b84c710da4a9..846f3e327269 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -804,6 +804,7 @@ struct gb_svc_route_create_request { __u8 intf2_id; __u8 dev2_id; }; +/* route create response has no payload */ /* RAW */ -- cgit v1.2.3-59-g8ed1b From 829a91e7155e4cb5c90e99ebd6048c7618b29e9b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:03 +0530 Subject: greybus: greybus_protocols: Pack structure as required by the module firmware These structures are expected to be packed by the module firmware code, but the kernel wasn't following it until now. Its all working currently because compiler doesn't add any pad bytes for these structures, as their elements are already aligned to their size. But these structures can change in future and we better mark them packed. Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/greybus_protocols.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 846f3e327269..1984e5e94332 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -172,27 +172,27 @@ struct gb_control_disconnected_request { /* Firmware protocol firmware size request/response */ struct gb_firmware_size_request { __u8 stage; -}; +} __packed; struct gb_firmware_size_response { __le32 size; -}; +} __packed; /* Firmware protocol get firmware request/response */ struct gb_firmware_get_firmware_request { __le32 offset; __le32 size; -}; +} __packed; struct gb_firmware_get_firmware_response { __u8 data[0]; -}; +} __packed; /* Firmware protocol Ready to boot request */ struct gb_firmware_ready_to_boot_request { __u8 stage; __u8 status; -}; +} __packed; /* Firmware protocol Ready to boot response has no payload */ @@ -756,7 +756,7 @@ struct gb_svc_hello_request { struct gb_svc_intf_device_id_request { __u8 intf_id; __u8 device_id; -}; +} __packed; /* device id response has no payload */ struct gb_svc_intf_hotplug_request { @@ -772,12 +772,12 @@ struct gb_svc_intf_hotplug_request { struct gb_svc_intf_hot_unplug_request { __u8 intf_id; -}; +} __packed; /* hot unplug response has no payload */ struct gb_svc_intf_reset_request { __u8 intf_id; -}; +} __packed; /* interface reset response has no payload */ struct gb_svc_conn_create_request { @@ -803,7 +803,7 @@ struct gb_svc_route_create_request { __u8 dev1_id; __u8 intf2_id; __u8 dev2_id; -}; +} __packed; /* route create response has no payload */ -- cgit v1.2.3-59-g8ed1b From b701686a3e6739bc8bd94fba13809e25a4ce1bd3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:04 +0530 Subject: greybus: greybus_protocols: Pack all request/response structure These structures are exchanged between the AP and the module and must be packed to avoid any unwanted holes. Its all working currently because compiler doesn't add any pad bytes for these structures, as their elements are already aligned to their size. But these structures can change in future and we better mark them packed. Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/greybus_protocols.h | 164 ++++++++++++++-------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 1984e5e94332..c8bfced0ccf7 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -96,7 +96,7 @@ struct gb_operation_msg_hdr { __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ __u8 result; /* Result of request (in responses only) */ __u8 pad[2]; /* must be zero (ignore when read) */ -}; +} __packed; /* Generic request numbers supported by all modules */ @@ -109,7 +109,7 @@ struct gb_operation_msg_hdr { struct gb_protocol_version_response { __u8 major; __u8 minor; -}; +} __packed; /* Control Protocol */ @@ -127,21 +127,21 @@ struct gb_protocol_version_response { /* Control protocol manifest get size request has no payload*/ struct gb_control_get_manifest_size_response { __le16 size; -}; +} __packed; /* Control protocol manifest get request has no payload */ struct gb_control_get_manifest_response { __u8 data[0]; -}; +} __packed; /* Control protocol [dis]connected request */ struct gb_control_connected_request { __le16 cport_id; -}; +} __packed; struct gb_control_disconnected_request { __le16 cport_id; -}; +} __packed; /* Control protocol [dis]connected response has no payload */ @@ -224,7 +224,7 @@ struct gb_firmware_ready_to_boot_request { struct gb_battery_technology_response { __le32 technology; -}; +} __packed; /* Should match up with battery status in linux/power_supply.h */ #define GB_BATTERY_STATUS_UNKNOWN 0x0000 @@ -235,23 +235,23 @@ struct gb_battery_technology_response { struct gb_battery_status_response { __le16 battery_status; -}; +} __packed; struct gb_battery_max_voltage_response { __le32 max_voltage; -}; +} __packed; struct gb_battery_capacity_response { __le32 capacity; -}; +} __packed; struct gb_battery_temperature_response { __le32 temperature; -}; +} __packed; struct gb_battery_voltage_response { __le32 voltage; -}; +} __packed; /* HID */ @@ -289,19 +289,19 @@ struct gb_hid_desc_response { struct gb_hid_get_report_request { __u8 report_type; __u8 report_id; -}; +} __packed; /* HID set report request */ struct gb_hid_set_report_request { __u8 report_type; __u8 report_id; __u8 report[0]; -}; +} __packed; /* HID input report request, via interrupt pipe */ struct gb_hid_input_report_request { __u8 report[0]; -}; +} __packed; /* I2C */ @@ -322,16 +322,16 @@ struct gb_hid_input_report_request { /* functionality request has no payload */ struct gb_i2c_functionality_response { __le32 functionality; -}; +} __packed; struct gb_i2c_timeout_request { __le16 msec; -}; +} __packed; /* timeout response has no payload */ struct gb_i2c_retries_request { __u8 retries; -}; +} __packed; /* retries response has no payload */ /* @@ -348,15 +348,15 @@ struct gb_i2c_transfer_op { __le16 addr; __le16 flags; __le16 size; -}; +} __packed; struct gb_i2c_transfer_request { __le16 op_count; struct gb_i2c_transfer_op ops[0]; /* op_count of these */ -}; +} __packed; struct gb_i2c_transfer_response { __u8 data[0]; /* inbound data */ -}; +} __packed; /* GPIO */ @@ -390,47 +390,47 @@ struct gb_i2c_transfer_response { /* line count request has no payload */ struct gb_gpio_line_count_response { __u8 count; -}; +} __packed; struct gb_gpio_activate_request { __u8 which; -}; +} __packed; /* activate response has no payload */ struct gb_gpio_deactivate_request { __u8 which; -}; +} __packed; /* deactivate response has no payload */ struct gb_gpio_get_direction_request { __u8 which; -}; +} __packed; struct gb_gpio_get_direction_response { __u8 direction; -}; +} __packed; struct gb_gpio_direction_in_request { __u8 which; -}; +} __packed; /* direction in response has no payload */ struct gb_gpio_direction_out_request { __u8 which; __u8 value; -}; +} __packed; /* direction out response has no payload */ struct gb_gpio_get_value_request { __u8 which; -}; +} __packed; struct gb_gpio_get_value_response { __u8 value; -}; +} __packed; struct gb_gpio_set_value_request { __u8 which; __u8 value; -}; +} __packed; /* set value response has no payload */ struct gb_gpio_set_debounce_request { @@ -442,23 +442,23 @@ struct gb_gpio_set_debounce_request { struct gb_gpio_irq_type_request { __u8 which; __u8 type; -}; +} __packed; /* irq type response has no payload */ struct gb_gpio_irq_mask_request { __u8 which; -}; +} __packed; /* irq mask response has no payload */ struct gb_gpio_irq_unmask_request { __u8 which; -}; +} __packed; /* irq unmask response has no payload */ /* irq event requests originate on another module and are handled on the AP */ struct gb_gpio_irq_event_request { __u8 which; -}; +} __packed; /* irq event has no response */ @@ -480,15 +480,15 @@ struct gb_gpio_irq_event_request { /* pwm count request has no payload */ struct gb_pwm_count_response { __u8 count; -}; +} __packed; struct gb_pwm_activate_request { __u8 which; -}; +} __packed; struct gb_pwm_deactivate_request { __u8 which; -}; +} __packed; struct gb_pwm_config_request { __u8 which; @@ -499,15 +499,15 @@ struct gb_pwm_config_request { struct gb_pwm_polarity_request { __u8 which; __u8 polarity; -}; +} __packed; struct gb_pwm_enable_request { __u8 which; -}; +} __packed; struct gb_pwm_disable_request { __u8 which; -}; +} __packed; /* I2S */ @@ -593,48 +593,48 @@ struct gb_i2s_mgmt_configuration { __u8 ll_wclk_tx_edge; __u8 ll_wclk_rx_edge; __u8 ll_data_offset; -}; +} __packed; /* get supported configurations request has no payload */ struct gb_i2s_mgmt_get_supported_configurations_response { __u8 config_count; __u8 pad[3]; struct gb_i2s_mgmt_configuration config[0]; -}; +} __packed; struct gb_i2s_mgmt_set_configuration_request { struct gb_i2s_mgmt_configuration config; -}; +} __packed; /* set configuration response has no payload */ struct gb_i2s_mgmt_set_samples_per_message_request { __le16 samples_per_message; -}; +} __packed; /* set samples per message response has no payload */ /* get processing request delay has no payload */ struct gb_i2s_mgmt_get_processing_delay_response { __le32 microseconds; -}; +} __packed; struct gb_i2s_mgmt_set_start_delay_request { __le32 microseconds; -}; +} __packed; /* set start delay response has no payload */ struct gb_i2s_mgmt_activate_cport_request { __le16 cport; -}; +} __packed; /* activate cport response has no payload */ struct gb_i2s_mgmt_deactivate_cport_request { __le16 cport; -}; +} __packed; /* deactivate cport response has no payload */ struct gb_i2s_mgmt_report_event_request { __u8 event; -}; +} __packed; /* report event response has no payload */ #define GB_I2S_DATA_TYPE_SEND_DATA 0x02 @@ -643,7 +643,7 @@ struct gb_i2s_send_data_request { __le32 sample_number; __le32 size; __u8 data[0]; -}; +} __packed; /* send data has no response at all */ @@ -682,22 +682,22 @@ struct gb_i2s_send_data_request { /* mode request has no payload */ struct gb_spi_mode_response { __le16 mode; -}; +} __packed; /* flags request has no payload */ struct gb_spi_flags_response { __le16 flags; -}; +} __packed; /* bits-per-word request has no payload */ struct gb_spi_bpw_response { __le32 bits_per_word_mask; -}; +} __packed; /* num-chipselects request has no payload */ struct gb_spi_chipselect_response { __le16 num_chipselect; -}; +} __packed; /** * struct gb_spi_transfer - a read/write buffer pair @@ -717,18 +717,18 @@ struct gb_spi_transfer { __le16 delay_usecs; __u8 cs_change; __u8 bits_per_word; -}; +} __packed; struct gb_spi_transfer_request { __u8 chip_select; /* of the spi device */ __u8 mode; /* of the spi device */ __le16 count; struct gb_spi_transfer transfers[0]; /* count of these */ -}; +} __packed; struct gb_spi_transfer_response { __u8 data[0]; /* inbound data */ -}; +} __packed; /* Version of the Greybus SVC protocol we support */ #define GB_SVC_VERSION_MAJOR 0x00 @@ -819,7 +819,7 @@ struct gb_svc_route_create_request { struct gb_raw_send_request { __le32 len; __u8 data[0]; -}; +} __packed; /* UART */ @@ -840,7 +840,7 @@ struct gb_raw_send_request { struct gb_uart_send_data_request { __le16 size; __u8 data[0]; -}; +} __packed; /* recv-data-request flags */ #define GB_UART_RECV_FLAG_FRAMING 0x01 /* Framing error */ @@ -878,11 +878,11 @@ struct gb_uart_set_line_coding_request { struct gb_uart_set_control_line_state_request { __u8 control; -}; +} __packed; struct gb_uart_set_break_request { __u8 state; -}; +} __packed; /* input control lines and line errors */ #define GB_UART_CTRL_DCD 0x01 @@ -891,7 +891,7 @@ struct gb_uart_set_break_request { struct gb_uart_serial_state_request { __u8 control; -}; +} __packed; /* Loopback */ @@ -907,11 +907,11 @@ struct gb_uart_serial_state_request { struct gb_loopback_transfer_request { __le32 len; __u8 data[0]; -}; +} __packed; struct gb_loopback_transfer_response { __u8 data[0]; -}; +} __packed; /* SDIO */ /* Version of the Greybus sdio protocol we support */ @@ -954,7 +954,7 @@ struct gb_sdio_get_caps_response { __le32 ocr; __le16 max_blk_count; __le16 max_blk_size; -}; +} __packed; /* set ios request: response has no payload */ struct gb_sdio_set_ios_request { @@ -1040,7 +1040,7 @@ struct gb_sdio_command_request { struct gb_sdio_command_response { __le32 resp[4]; -}; +} __packed; /* transfer request */ struct gb_sdio_transfer_request { @@ -1058,7 +1058,7 @@ struct gb_sdio_transfer_response { __le16 data_blocks; __le16 data_blksz; __u8 data[0]; -}; +} __packed; /* event request: generated by module and is defined as unidirectional */ struct gb_sdio_event_request { @@ -1066,7 +1066,7 @@ struct gb_sdio_event_request { #define GB_SDIO_CARD_INSERTED 0x01 #define GB_SDIO_CARD_REMOVED 0x02 #define GB_SDIO_WP 0x04 -}; +} __packed; /* Lights */ @@ -1119,30 +1119,30 @@ struct gb_sdio_event_request { /* get count of lights in module */ struct gb_lights_get_lights_response { __u8 lights_count; -}; +} __packed; /* light config request payload */ struct gb_lights_get_light_config_request { __u8 id; -}; +} __packed; /* light config response payload */ struct gb_lights_get_light_config_response { __u8 channel_count; __u8 name[32]; -}; +} __packed; /* channel config request payload */ struct gb_lights_get_channel_config_request { __u8 light_id; __u8 channel_id; -}; +} __packed; /* channel flash config request payload */ struct gb_lights_get_channel_flash_config_request { __u8 light_id; __u8 channel_id; -}; +} __packed; /* channel config response payload */ struct gb_lights_get_channel_config_response { @@ -1162,7 +1162,7 @@ struct gb_lights_get_channel_flash_config_response { __le32 timeout_min_us; __le32 timeout_max_us; __le32 timeout_step_us; -}; +} __packed; /* blink request payload: response have no payload */ struct gb_lights_blink_request { @@ -1170,14 +1170,14 @@ struct gb_lights_blink_request { __u8 channel_id; __le16 time_on_ms; __le16 time_off_ms; -}; +} __packed; /* set brightness request payload: response have no payload */ struct gb_lights_set_brightness_request { __u8 light_id; __u8 channel_id; __u8 brightness; -}; +} __packed; /* set color request payload: response have no payload */ struct gb_lights_set_color_request { @@ -1192,14 +1192,14 @@ struct gb_lights_set_fade_request { __u8 channel_id; __u8 fade_in; __u8 fade_out; -}; +} __packed; /* event request: generated by module */ struct gb_lights_event_request { __u8 light_id; __u8 event; #define GB_LIGHTS_LIGHT_CONFIG 0x01 -}; +} __packed; /* set flash intensity request payload: response have no payload */ struct gb_lights_set_flash_intensity_request { @@ -1213,7 +1213,7 @@ struct gb_lights_set_flash_strobe_request { __u8 light_id; __u8 channel_id; __u8 state; -}; +} __packed; /* set flash timeout request payload: response have no payload */ struct gb_lights_set_flash_timeout_request { @@ -1226,7 +1226,7 @@ struct gb_lights_set_flash_timeout_request { struct gb_lights_get_flash_fault_request { __u8 light_id; __u8 channel_id; -}; +} __packed; /* get flash fault response payload */ struct gb_lights_get_flash_fault_response { @@ -1240,7 +1240,7 @@ struct gb_lights_get_flash_fault_response { #define GB_LIGHTS_FLASH_FAULT_UNDER_VOLTAGE 0x00000020 #define GB_LIGHTS_FLASH_FAULT_INPUT_VOLTAGE 0x00000040 #define GB_LIGHTS_FLASH_FAULT_LED_OVER_TEMPERATURE 0x00000080 -}; +} __packed; #endif /* __GREYBUS_PROTOCOLS_H */ -- cgit v1.2.3-59-g8ed1b From d9fcffff2c5442225cec21f55e74fb752e67e57f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:05 +0530 Subject: greybus: svc: No need to return errors from [gb_]svc_connection_destroy() These routines are responsible to destroy a connection that is going away, the return value is of no use. At best, print an error message to show that we got an error. Make their return type void. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/svc.c | 22 ++++++++++++++-------- drivers/staging/greybus/svc.h | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 42b89ee9e094..e56cb187786f 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -125,19 +125,26 @@ static int connection_create_operation(struct gb_svc *svc, &request, sizeof(request), NULL, 0); } -static int connection_destroy_operation(struct gb_svc *svc, +static void connection_destroy_operation(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) { struct gb_svc_conn_destroy_request request; + struct gb_connection *connection = svc->connection; + int ret; request.intf1_id = intf1_id; request.cport1_id = cport1_id; request.intf2_id = intf2_id; request.cport2_id = cport2_id; - return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_DESTROY, - &request, sizeof(request), NULL, 0); + ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY, + &request, sizeof(request), NULL, 0); + if (ret) { + dev_err(&connection->dev, + "failed to destroy connection (%hhx:%hx %hhx:%hx) %d\n", + intf1_id, cport1_id, intf2_id, cport2_id, ret); + } } static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, @@ -175,12 +182,11 @@ int gb_svc_connection_create(struct gb_svc *svc, } EXPORT_SYMBOL_GPL(gb_svc_connection_create); -int gb_svc_connection_destroy(struct gb_svc *svc, - u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id) +void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) { - return connection_destroy_operation(svc, intf1_id, cport1_id, - intf2_id, cport2_id); + connection_destroy_operation(svc, intf1_id, cport1_id, intf2_id, + cport2_id); } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index ee39479cf9b2..dae4a08bd463 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -16,8 +16,8 @@ int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); -int gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id); +void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From 3f0e9183b443ca7cae058372906a652f97301c11 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:06 +0530 Subject: greybus: svc: Kill unnecessary wrapper functions Not sure why they were created, but there is no need for them. Kill them. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/svc.c | 66 +++++++++++-------------------------------- 1 file changed, 16 insertions(+), 50 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index e56cb187786f..005c28a20279 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -82,8 +82,7 @@ gb_ap_interface_create(struct greybus_host_device *hd, return intf; } -static int intf_device_id_operation(struct gb_svc *svc, - u8 intf_id, u8 device_id) +int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) { struct gb_svc_intf_device_id_request request; @@ -93,8 +92,9 @@ static int intf_device_id_operation(struct gb_svc *svc, return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID, &request, sizeof(request), NULL, 0); } +EXPORT_SYMBOL_GPL(gb_svc_intf_device_id); -static int intf_reset_operation(struct gb_svc *svc, u8 intf_id) +int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id) { struct gb_svc_intf_reset_request request; @@ -103,8 +103,9 @@ static int intf_reset_operation(struct gb_svc *svc, u8 intf_id) return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET, &request, sizeof(request), NULL, 0); } +EXPORT_SYMBOL_GPL(gb_svc_intf_reset); -static int connection_create_operation(struct gb_svc *svc, +int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) { @@ -124,10 +125,10 @@ static int connection_create_operation(struct gb_svc *svc, return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); } +EXPORT_SYMBOL_GPL(gb_svc_connection_create); -static void connection_destroy_operation(struct gb_svc *svc, - u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id) +void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) { struct gb_svc_conn_destroy_request request; struct gb_connection *connection = svc->connection; @@ -146,9 +147,10 @@ static void connection_destroy_operation(struct gb_svc *svc, intf1_id, cport1_id, intf2_id, cport2_id, ret); } } +EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); -static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, - u8 intf2_id, u8 dev2_id) +int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, + u8 intf2_id, u8 dev2_id) { struct gb_svc_route_create_request request; @@ -160,42 +162,6 @@ static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE, &request, sizeof(request), NULL, 0); } - -int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) -{ - return intf_device_id_operation(svc, intf_id, device_id); -} -EXPORT_SYMBOL_GPL(gb_svc_intf_device_id); - -int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id) -{ - return intf_reset_operation(svc, intf_id); -} -EXPORT_SYMBOL_GPL(gb_svc_intf_reset); - -int gb_svc_connection_create(struct gb_svc *svc, - u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id) -{ - return connection_create_operation(svc, intf1_id, cport1_id, - intf2_id, cport2_id); -} -EXPORT_SYMBOL_GPL(gb_svc_connection_create); - -void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id) -{ - connection_destroy_operation(svc, intf1_id, cport1_id, intf2_id, - cport2_id); -} -EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); - -int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, - u8 intf2_id, u8 dev2_id) -{ - return route_create_operation(svc, intf1_id, dev1_id, - intf2_id, dev2_id); -} EXPORT_SYMBOL_GPL(gb_svc_route_create); static int gb_svc_version_request(struct gb_operation *op) @@ -323,7 +289,7 @@ static void svc_process_hotplug(struct work_struct *work) goto destroy_interface; } - ret = intf_device_id_operation(svc, intf_id, device_id); + ret = gb_svc_intf_device_id(svc, intf_id, device_id); if (ret) { dev_err(dev, "%s: Device id operation failed, interface %hhu device_id %hhu (%d)\n", __func__, intf_id, device_id, ret); @@ -333,16 +299,16 @@ static void svc_process_hotplug(struct work_struct *work) /* * Create a two-way route between the AP and the new interface */ - ret = route_create_operation(svc, hd->endo->ap_intf_id, - GB_DEVICE_ID_AP, intf_id, device_id); + ret = gb_svc_route_create(svc, hd->endo->ap_intf_id, GB_DEVICE_ID_AP, + intf_id, device_id); if (ret) { dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", __func__, intf_id, device_id, ret); goto ida_put; } - ret = route_create_operation(svc, intf_id, device_id, - hd->endo->ap_intf_id, GB_DEVICE_ID_AP); + ret = gb_svc_route_create(svc, intf_id, device_id, hd->endo->ap_intf_id, + GB_DEVICE_ID_AP); if (ret) { dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", __func__, intf_id, device_id, ret); -- cgit v1.2.3-59-g8ed1b From 505f16cc05a4ddeafe5efe33645123fed194d811 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:07 +0530 Subject: greybus: svc: unexport few internal functions There are no external users of these, and probably would never be. Make them static. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/svc.c | 8 +++----- drivers/staging/greybus/svc.h | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 005c28a20279..b32519ac26fb 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -82,7 +82,7 @@ gb_ap_interface_create(struct greybus_host_device *hd, return intf; } -int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) +static int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) { struct gb_svc_intf_device_id_request request; @@ -92,7 +92,6 @@ int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID, &request, sizeof(request), NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_intf_device_id); int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id) { @@ -149,8 +148,8 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); -int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, - u8 intf2_id, u8 dev2_id) +static int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, + u8 intf2_id, u8 dev2_id) { struct gb_svc_route_create_request request; @@ -162,7 +161,6 @@ int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE, &request, sizeof(request), NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_route_create); static int gb_svc_version_request(struct gb_operation *op) { diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index dae4a08bd463..f1acb82523d1 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -12,7 +12,6 @@ struct gb_svc; -int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); -- cgit v1.2.3-59-g8ed1b From ad14b9e9ec4973416a911a5d3c59caedc048a279 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:08 +0530 Subject: greybus: bundle: kill unnecessary forward declaration of routine Move the function to an earlier place, to kill the unnecessary forward declaration. Reviewed-by: Bryan O'Donoghue Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/bundle.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 694bcce67bf2..459c62a3efb9 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -9,8 +9,6 @@ #include "greybus.h" -static void gb_bundle_connections_exit(struct gb_bundle *bundle); - static ssize_t class_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -197,6 +195,18 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, return bundle; } +static void gb_bundle_connections_exit(struct gb_bundle *bundle) +{ + struct gb_connection *connection; + struct gb_connection *next; + + list_for_each_entry_safe(connection, next, &bundle->connections, + bundle_links) { + gb_connection_exit(connection); + gb_connection_destroy(connection); + } +} + /* * Tear down a previously set up bundle. */ @@ -224,15 +234,3 @@ found: return bundle; } - -static void gb_bundle_connections_exit(struct gb_bundle *bundle) -{ - struct gb_connection *connection; - struct gb_connection *next; - - list_for_each_entry_safe(connection, next, &bundle->connections, - bundle_links) { - gb_connection_exit(connection); - gb_connection_destroy(connection); - } -} -- cgit v1.2.3-59-g8ed1b From 630096899120321791f0ca6fea7ad7f9bd572e20 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:09 +0530 Subject: greybus: connection: staticize gb_connection_init() Its not used by external users, mark it static. This required some shuffling of the code. Reviewed-by: Bryan O'Donoghue Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/connection.c | 52 ++++++++++++++++++------------------ drivers/staging/greybus/connection.h | 1 - 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 286e7da3f275..62cbeb304616 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -163,31 +163,6 @@ int svc_update_connection(struct gb_interface *intf, return 0; } -void gb_connection_bind_protocol(struct gb_connection *connection) -{ - struct gb_protocol *protocol; - - /* If we already have a protocol bound here, just return */ - if (connection->protocol) - return; - - protocol = gb_protocol_get(connection->protocol_id, - connection->major, - connection->minor); - if (!protocol) - return; - connection->protocol = protocol; - - /* - * If we have a valid device_id for the interface block, then we have an - * active device, so bring up the connection at the same time. - */ - if ((!connection->bundle && - connection->hd_cport_id == GB_SVC_CPORT_ID) || - connection->bundle->intf->device_id != GB_DEVICE_ID_BAD) - gb_connection_init(connection); -} - /* * Set up a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -391,7 +366,7 @@ static void gb_connection_disconnected(struct gb_connection *connection) "Failed to disconnect CPort-%d (%d)\n", cport_id, ret); } -int gb_connection_init(struct gb_connection *connection) +static int gb_connection_init(struct gb_connection *connection) { int cport_id = connection->intf_cport_id; int ret; @@ -480,3 +455,28 @@ void gb_hd_connections_exit(struct greybus_host_device *hd) gb_connection_destroy(connection); } } + +void gb_connection_bind_protocol(struct gb_connection *connection) +{ + struct gb_protocol *protocol; + + /* If we already have a protocol bound here, just return */ + if (connection->protocol) + return; + + protocol = gb_protocol_get(connection->protocol_id, + connection->major, + connection->minor); + if (!protocol) + return; + connection->protocol = protocol; + + /* + * If we have a valid device_id for the interface block, then we have an + * active device, so bring up the connection at the same time. + */ + if ((!connection->bundle && + connection->hd_cport_id == GB_SVC_CPORT_ID) || + connection->bundle->intf->device_id != GB_DEVICE_ID_BAD) + gb_connection_init(connection); +} diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index a26a48033fc6..f1b5863820c5 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -61,7 +61,6 @@ struct gb_connection *gb_connection_create_range(struct greybus_host_device *hd, u32 ida_end); void gb_connection_destroy(struct gb_connection *connection); -int gb_connection_init(struct gb_connection *connection); void gb_connection_exit(struct gb_connection *connection); void gb_hd_connections_exit(struct greybus_host_device *hd); -- cgit v1.2.3-59-g8ed1b From 3dca03de9db7c467811bdfcb0b3b90cb9a184a8f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:10 +0530 Subject: greybus: connection: convert connected dev_warn() to dev_err() Failures from control-connected operations are fatal errors and must be reported with dev_err() instead of dev_warn(). Fix it. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar [ johan: do not promote disconnected warnings, update summary ] Signed-off-by: Johan Hovold --- drivers/staging/greybus/connection.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 62cbeb304616..f89b199230dc 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -386,9 +386,9 @@ static int gb_connection_init(struct gb_connection *connection) ret = gb_control_connected_operation(control, cport_id); if (ret) { - dev_warn(&connection->dev, - "Failed to connect CPort-%d (%d)\n", - cport_id, ret); + dev_err(&connection->dev, + "Failed to connect CPort-%d (%d)\n", + cport_id, ret); return 0; } } -- cgit v1.2.3-59-g8ed1b From 58f469a7da3c0b2e126851f850780de20182708b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:11 +0530 Subject: greybus: connection: no need to verify connection->protocol connection->protocol will always be valid in gb_connection_init() as it is called only from a single routine, after initializing the 'protocol' field. No need to check it again. Reviewed-by: Bryan O'Donoghue Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/connection.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f89b199230dc..557fe6d6b7af 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -371,11 +371,6 @@ static int gb_connection_init(struct gb_connection *connection) int cport_id = connection->intf_cport_id; int ret; - if (!connection->protocol) { - dev_warn(&connection->dev, "init without protocol.\n"); - return 0; - } - /* * Inform Interface about Active CPorts. We don't need to do this * operation for control cport. -- cgit v1.2.3-59-g8ed1b From 4c583f42c3cc0b6ebb678f5e7ecd5374479bfc22 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:12 +0530 Subject: greybus: connection: Propagate error properly We just got an error, propagate the exact return value instead of 0. Reviewed-by: Bryan O'Donoghue Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 557fe6d6b7af..8fe056d57493 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -384,7 +384,7 @@ static int gb_connection_init(struct gb_connection *connection) dev_err(&connection->dev, "Failed to connect CPort-%d (%d)\n", cport_id, ret); - return 0; + return ret; } } -- cgit v1.2.3-59-g8ed1b From fda2381bd2d96b4f45806e4796d558eec678e3c6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:13 +0530 Subject: greybus: connection: call gb_connection_exit() from gb_connection_destroy() Both the routines are always called together and in the same sequence. Rather than duplicating this at different places, make gb_connection_destroy() call gb_connection_exit(). This also makes it more sensible, as gb_connection_init() is never called directly by the users and so its its counterpart shouldn't be called directly as well. Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/bundle.c | 4 +-- drivers/staging/greybus/connection.c | 60 ++++++++++++++++++------------------ drivers/staging/greybus/connection.h | 2 -- drivers/staging/greybus/core.c | 4 +-- 4 files changed, 32 insertions(+), 38 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 459c62a3efb9..5c09bccf55b8 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -201,10 +201,8 @@ static void gb_bundle_connections_exit(struct gb_bundle *bundle) struct gb_connection *next; list_for_each_entry_safe(connection, next, &bundle->connections, - bundle_links) { - gb_connection_exit(connection); + bundle_links) gb_connection_destroy(connection); - } } /* diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 8fe056d57493..29870536aa2f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -318,32 +318,6 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, spin_unlock_irq(&connection->lock); } -/* - * Tear down a previously set up connection. - */ -void gb_connection_destroy(struct gb_connection *connection) -{ - struct ida *id_map; - - if (WARN_ON(!connection)) - return; - - spin_lock_irq(&gb_connections_lock); - list_del(&connection->bundle_links); - list_del(&connection->hd_links); - spin_unlock_irq(&gb_connections_lock); - - if (connection->protocol) - gb_protocol_put(connection->protocol); - connection->protocol = NULL; - - id_map = &connection->hd->cport_id_map; - ida_simple_remove(id_map, connection->hd_cport_id); - connection->hd_cport_id = CPORT_ID_BAD; - - device_unregister(&connection->dev); -} - static void gb_connection_disconnected(struct gb_connection *connection) { struct gb_control *control; @@ -420,7 +394,7 @@ disconnect: return ret; } -void gb_connection_exit(struct gb_connection *connection) +static void gb_connection_exit(struct gb_connection *connection) { if (!connection->protocol) { dev_warn(&connection->dev, "exit without protocol.\n"); @@ -441,14 +415,40 @@ void gb_connection_exit(struct gb_connection *connection) gb_connection_disconnected(connection); } +/* + * Tear down a previously set up connection. + */ +void gb_connection_destroy(struct gb_connection *connection) +{ + struct ida *id_map; + + if (WARN_ON(!connection)) + return; + + gb_connection_exit(connection); + + spin_lock_irq(&gb_connections_lock); + list_del(&connection->bundle_links); + list_del(&connection->hd_links); + spin_unlock_irq(&gb_connections_lock); + + if (connection->protocol) + gb_protocol_put(connection->protocol); + connection->protocol = NULL; + + id_map = &connection->hd->cport_id_map; + ida_simple_remove(id_map, connection->hd_cport_id); + connection->hd_cport_id = CPORT_ID_BAD; + + device_unregister(&connection->dev); +} + void gb_hd_connections_exit(struct greybus_host_device *hd) { struct gb_connection *connection; - list_for_each_entry(connection, &hd->connections, hd_links) { - gb_connection_exit(connection); + list_for_each_entry(connection, &hd->connections, hd_links) gb_connection_destroy(connection); - } } void gb_connection_bind_protocol(struct gb_connection *connection) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index f1b5863820c5..0b442fe61b87 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -60,8 +60,6 @@ struct gb_connection *gb_connection_create_range(struct greybus_host_device *hd, u16 cport_id, u8 protocol_id, u32 ida_start, u32 ida_end); void gb_connection_destroy(struct gb_connection *connection); - -void gb_connection_exit(struct gb_connection *connection); void gb_hd_connections_exit(struct greybus_host_device *hd); void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 605a0887dc61..3c89cb368f6d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -259,10 +259,8 @@ void greybus_remove_hd(struct greybus_host_device *hd) gb_endo_remove(hd->endo); /* Is the SVC still using the partially uninitialized connection ? */ - if (hd->initial_svc_connection) { - gb_connection_exit(hd->initial_svc_connection); + if (hd->initial_svc_connection) gb_connection_destroy(hd->initial_svc_connection); - } /* * Make sure there are no leftovers that can potentially corrupt sysfs. -- cgit v1.2.3-59-g8ed1b From 50bb9ccaa0e12823e7f225ce1571d4ae3d1f83e0 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Mon, 31 Aug 2015 17:21:14 +0530 Subject: greybus: connection: fail to bind if connection init fails gb_connection_init() can fail and will return proper error code in that case, but the caller is ignoring it currently. Fix that by properly handling errors returned from gb_connection_init() and propagating them to callers of gb_connection_bind_protocol(). Signed-off-by: Fabien Parent Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/connection.c | 19 ++++++++++++++----- drivers/staging/greybus/connection.h | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 29870536aa2f..d0642bcd7600 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -451,19 +451,20 @@ void gb_hd_connections_exit(struct greybus_host_device *hd) gb_connection_destroy(connection); } -void gb_connection_bind_protocol(struct gb_connection *connection) +int gb_connection_bind_protocol(struct gb_connection *connection) { struct gb_protocol *protocol; + int ret; /* If we already have a protocol bound here, just return */ if (connection->protocol) - return; + return 0; protocol = gb_protocol_get(connection->protocol_id, connection->major, connection->minor); if (!protocol) - return; + return 0; connection->protocol = protocol; /* @@ -472,6 +473,14 @@ void gb_connection_bind_protocol(struct gb_connection *connection) */ if ((!connection->bundle && connection->hd_cport_id == GB_SVC_CPORT_ID) || - connection->bundle->intf->device_id != GB_DEVICE_ID_BAD) - gb_connection_init(connection); + connection->bundle->intf->device_id != GB_DEVICE_ID_BAD) { + ret = gb_connection_init(connection); + if (ret) { + gb_protocol_put(protocol); + connection->protocol = NULL; + return ret; + } + } + + return 0; } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 0b442fe61b87..e3ae01dcfd82 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -68,6 +68,6 @@ void gb_connection_push_timestamp(struct gb_connection *connection); int gb_connection_pop_timestamp(struct gb_connection *connection, struct timeval *tv); -void gb_connection_bind_protocol(struct gb_connection *connection); +int gb_connection_bind_protocol(struct gb_connection *connection); #endif /* __CONNECTION_H */ -- cgit v1.2.3-59-g8ed1b From 6c0d57b4e612c00b14852c2f8b179d518462ea6c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 31 Aug 2015 17:21:15 +0530 Subject: greybus: connection: protocol can be NULL in gb_connection_exit() gb_connection_exit() is getting called from gb_connection_destroy() now, which will get called from failure path of gb_connection_create_range() (in a later commit). And at that point connection->protocol will be NULL. Don't print an error message if this happens in gb_connection_exit(). Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/connection.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index d0642bcd7600..466ea36da0aa 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -396,10 +396,8 @@ disconnect: static void gb_connection_exit(struct gb_connection *connection) { - if (!connection->protocol) { - dev_warn(&connection->dev, "exit without protocol.\n"); + if (!connection->protocol) return; - } spin_lock_irq(&connection->lock); if (connection->state != GB_CONNECTION_STATE_ENABLED) { -- cgit v1.2.3-59-g8ed1b From 2111134fe413311ea2e89c7ffc66abb68ada5982 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Sep 2015 15:10:09 +0530 Subject: greybus: spi: Allow spi-core to allocate bus numbers dynamically And that fixes these warnings generated with BDB: WARNING: at /home/viresh/ara/jetson-kernel-build/tegra/fs/sysfs/dir.c:530 sysfs_add_one+0xa4/0xb4() sysfs: cannot create duplicate filename '/class/spi_master/spi0' Modules linked in: gb_es2(O) gb_phy(O) greybus(O) CPU: 2 PID: 111 Comm: kworker/u8:3 Tainted: G W O 3.10.40-gf32f9c5ca7e8 #2 Workqueue: events_unbound svc_process_hotplug [greybus] [] (unwind_backtrace+0x0/0x13c) from [] (show_stack+0x18/0x1c) [] (show_stack+0x18/0x1c) from [] (warn_slowpath_common+0x5c/0x74) [] (warn_slowpath_common+0x5c/0x74) from [] (warn_slowpath_fmt+0x38/0x48) [] (warn_slowpath_fmt+0x38/0x48) from [] (sysfs_add_one+0xa4/0xb4) [] (sysfs_add_one+0xa4/0xb4) from [] (sysfs_do_create_link_sd+0xc0/0x20c) [] (sysfs_do_create_link_sd+0xc0/0x20c) from [] (device_add+0x2e8/0x5f4) [] (device_add+0x2e8/0x5f4) from [] (spi_register_master+0x15c/0x654) [] (spi_register_master+0x15c/0x654) from [] (gb_spi_connection_init+0x164/0x19c [gb_phy]) [] (gb_spi_connection_init+0x164/0x19c [gb_phy]) from [] (gb_connection_bind_protocol+0x160/0x1b4 [greybus]) [] (gb_connection_bind_protocol+0x160/0x1b4 [greybus]) from [] (gb_connection_create_range+0x228/0x2fc [greybus]) [] (gb_connection_create_range+0x228/0x2fc [greybus]) from [] (gb_connection_create+0x40/0x48 [greybus]) [] (gb_connection_create+0x40/0x48 [greybus]) from [] (gb_manifest_parse+0x61c/0x628 [greybus]) [] (gb_manifest_parse+0x61c/0x628 [greybus]) from [] (gb_interface_init+0x130/0x170 [greybus]) [] (gb_interface_init+0x130/0x170 [greybus]) from [] (svc_process_hotplug+0x214/0x258 [greybus]) [] (svc_process_hotplug+0x214/0x258 [greybus]) from [] (process_one_work+0x13c/0x454) [] (process_one_work+0x13c/0x454) from [] (worker_thread+0x140/0x3dc) [] (worker_thread+0x140/0x3dc) from [] (kthread+0xe0/0xe4) [] (kthread+0xe0/0xe4) from [] (ret_from_fork+0x14/0x20) Reported-by: Mitchell Tasman Suggested-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Johan Hovold --- drivers/staging/greybus/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index ef3cc33772f8..e5d216646b9c 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -299,7 +299,7 @@ static int gb_spi_connection_init(struct gb_connection *connection) if (ret) goto out_err; - master->bus_num = 0; /* How do we get controller id here? */ + master->bus_num = -1; /* Allow spi-core to allocate it dynamically */ master->num_chipselect = spi->num_chipselect; master->mode_bits = spi->mode; master->flags = spi->flags; -- cgit v1.2.3-59-g8ed1b From 8afd831ea00822fdc811ff640ef62352619d64c0 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Fri, 4 Sep 2015 16:55:40 +0200 Subject: greybus: Greybus driver: add a new callbacks to driver Add connection_create and connection_destroy callbacks. ES2 can map a cport to a pair of endpoints. Because ES2 have only a few pair of endpoints, ES2 need to have access to some high level connection information such as protocol id to effectively map the cports. These callback will provide these information and help ES2 to map cports. Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 5 +++++ drivers/staging/greybus/greybus.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 466ea36da0aa..c25e5705f865 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -262,6 +262,8 @@ gb_connection_create_range(struct greybus_host_device *hd, gb_svc_connection_create(hd->svc, hd->endo->ap_intf_id, hd_cport_id, bundle->intf->interface_id, cport_id); + if (hd->driver->connection_create) + hd->driver->connection_create(connection); } gb_connection_bind_protocol(connection); @@ -430,6 +432,9 @@ void gb_connection_destroy(struct gb_connection *connection) list_del(&connection->hd_links); spin_unlock_irq(&gb_connections_lock); + if (connection->hd->driver->connection_destroy) + connection->hd->driver->connection_destroy(connection); + if (connection->protocol) gb_protocol_put(connection->protocol); connection->protocol = NULL; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 8e215f870c1d..9f2eb1f2e8a9 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -75,6 +75,8 @@ struct greybus_host_device; struct greybus_host_driver { size_t hd_priv_size; + void (*connection_create)(struct gb_connection *connection); + void (*connection_destroy)(struct gb_connection *connection); int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); -- cgit v1.2.3-59-g8ed1b From 566830fdb1b666b5159709a9e66ed5f2c85f58b7 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Fri, 4 Sep 2015 16:55:42 +0200 Subject: greybus: es2: update the bulk_ep_set value accepted by map_to_cpor_ep() The endpoint set 0 is currently considered as invalid. But 0 mean muxed cports on ep1 and ep2, then it must not return -EINVAL. Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index d6bd210a6337..96090aaa4181 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -149,7 +149,7 @@ int map_cport_to_ep(struct es1_ap_dev *es1, int retval; struct cport_to_ep *cport_to_ep; - if (bulk_ep_set == 0 || bulk_ep_set >= NUM_BULKS) + if (bulk_ep_set < 0 || bulk_ep_set >= NUM_BULKS) return -EINVAL; if (cport_id >= es1->hd->num_cports) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From ff477d073f2e71a1fa59f86cb44bd9d48674a71b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 4 Sep 2015 16:53:31 +0100 Subject: greybus: loopback: make sure to list_del on connection_exit gb_loopback_connection_exit does a kfree on a data structure associated with a loopback connection but fails to do a corresponding list_del(). On subsequent enumerations this can lead to a NULL pointer dereference. Each list_add in gb_loopback_connection_init() must have a corresponding list_del in gb_loopback_connection_exit(), this patch adds the relevant list_del() and ensures that an appropriate mutex protecting gb_dev.list is held while doing so. Signed-off-by: Bryan O'Donoghue Reported-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 8dd648cc0796..231d1d4c1104 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -944,18 +944,23 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) struct gb_loopback *gb = connection->private; struct kobject *kobj = &connection->bundle->intf->module->dev.kobj; - gb_dev.count--; - connection->private = NULL; if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); + mutex_lock(&gb_dev.mutex); + + connection->private = NULL; kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); if (!gb_dev.count) sysfs_remove_groups(kobj, loopback_dev_groups); sysfs_remove_groups(&connection->dev.kobj, loopback_con_groups); debugfs_remove(gb->file); + list_del(&gb->entry); kfree(gb); + gb_dev.count--; + + mutex_unlock(&gb_dev.mutex); } static struct gb_protocol loopback_protocol = { -- cgit v1.2.3-59-g8ed1b From 98d7fbcad90b43f4e4fe03ef3261dbdcb6fe220f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 7 Sep 2015 16:01:19 +0530 Subject: greybus: manifest: don't reject the interface on failing to initialize a cport A 'bundle' represents a device in greybus. It may require multiple cports for its functioning. If we fail to setup any cport of a bundle, we better reject the complete bundle as the device may not be able to function properly then. But, failing to setup a cport of bundle X doesn't mean that the device corresponding to bundle Y will not work properly. Bundles should be treated as separate independent devices. While parsing manifest for an interface, treat bundles as separate entities and don't reject entire interface and its bundles on failing to initialize a cport. But make sure the bundle which needs the cport, gets destroyed properly. We now release the bundle descriptor before parsing the cports, but that shouldn't make any difference. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 43 +++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 559b2ee6e434..eda0f55580e3 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -273,6 +273,7 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) struct gb_bundle *bundle; struct gb_bundle *bundle_next; u32 count = 0; + u8 bundle_id; list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { struct greybus_descriptor_bundle *desc_bundle; @@ -282,9 +283,10 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) /* Found one. Set up its bundle structure*/ desc_bundle = desc->data; + bundle_id = desc_bundle->id; /* Don't recreate bundle for control cport */ - if (desc_bundle->id == GB_CONTROL_BUNDLE_ID) { + if (bundle_id == GB_CONTROL_BUNDLE_ID) { /* This should have class set to control class */ if (desc_bundle->class != GREYBUS_CLASS_CONTROL) { dev_err(&intf->dev, @@ -301,22 +303,47 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) if (desc_bundle->class == GREYBUS_CLASS_CONTROL) { dev_err(&intf->dev, "bundle 0x%02x cannot use control class\n", - desc_bundle->id); + bundle_id); goto cleanup; } - bundle = gb_bundle_create(intf, desc_bundle->id, - desc_bundle->class); + bundle = gb_bundle_create(intf, bundle_id, desc_bundle->class); if (!bundle) goto cleanup; parse_cports: - /* Now go set up this bundle's functions and cports */ - if (!gb_manifest_parse_cports(bundle)) - goto cleanup; - /* Done with this bundle descriptor */ release_manifest_descriptor(desc); + + /* + * Now go set up this bundle's functions and cports. + * + * A 'bundle' represents a device in greybus. It may require + * multiple cports for its functioning. If we fail to setup any + * cport of a bundle, we better reject the complete bundle as + * the device may not be able to function properly then. + * + * But, failing to setup a cport of bundle X doesn't mean that + * the device corresponding to bundle Y will not work properly. + * Bundles should be treated as separate independent devices. + * + * While parsing manifest for an interface, treat bundles as + * separate entities and don't reject entire interface and its + * bundles on failing to initialize a cport. But make sure the + * bundle which needs the cport, gets destroyed properly. + * + * The control bundle and its connections are special. The + * entire manifest should be rejected if we failed to initialize + * the control bundle/connections. + */ + if (!gb_manifest_parse_cports(bundle)) { + if (bundle_id == GB_CONTROL_BUNDLE_ID) + goto cleanup; + + gb_bundle_destroy(bundle); + continue; + } + count++; } -- cgit v1.2.3-59-g8ed1b From 4317f874f48d14fbd5b1d408336843dbe129ab3e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 7 Sep 2015 16:01:20 +0530 Subject: greybus: manifest: release cport descriptors to avoid 'excess descriptors' warning If we fail to initialize a cport of a bundle, we abort the entire bundle. But that leads to following (unnecessary) warnings as few of the cport descriptors, belonging to the aborted bundle were never parsed: "greybus: excess descriptors in interface manifest" Fix that by releasing all cport descriptors for the aborted bundle. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index eda0f55580e3..2264ec5914f9 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -258,6 +258,19 @@ print_error_exit: GREYBUS_PROTOCOL_CONTROL); exit: + /* + * Free all cports for this bundle to avoid 'excess descriptors' + * warnings. + */ + list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { + struct greybus_descriptor_cport *desc_cport = desc->data; + + if (desc->type != GREYBUS_TYPE_CPORT) + continue; + if (desc_cport->bundle == bundle_id) + release_manifest_descriptor(desc); + } + return 0; /* Error; count should also be 0 */ } -- cgit v1.2.3-59-g8ed1b From a1163fae63079f01c24d9c062b1e22e5ada89f91 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 7 Sep 2015 16:01:21 +0530 Subject: greybus: connection: call gb_svc_connection_create() from gb_connection_init() There are two operations which very much work together: - AP asks the SVC to create a connection between a cport of AP and a cport of module. - AP tells the module that the connection is created. Its better (logically) to do these two operations together and so call gb_svc_connection_create() from gb_connection_init() instead. Also check its return value properly. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c25e5705f865..973d841d8e91 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -258,14 +258,6 @@ gb_connection_create_range(struct greybus_host_device *hd, spin_unlock_irq(&gb_connections_lock); - if (hd_cport_id != GB_SVC_CPORT_ID) { - gb_svc_connection_create(hd->svc, - hd->endo->ap_intf_id, hd_cport_id, - bundle->intf->interface_id, cport_id); - if (hd->driver->connection_create) - hd->driver->connection_create(connection); - } - gb_connection_bind_protocol(connection); if (!connection->protocol) dev_warn(&connection->dev, @@ -345,8 +337,29 @@ static void gb_connection_disconnected(struct gb_connection *connection) static int gb_connection_init(struct gb_connection *connection) { int cport_id = connection->intf_cport_id; + struct greybus_host_device *hd = connection->hd; int ret; + /* + * Request the SVC to create a connection from AP's cport to interface's + * cport. + */ + if (connection->hd_cport_id != GB_SVC_CPORT_ID) { + ret = gb_svc_connection_create(hd->svc, + hd->endo->ap_intf_id, connection->hd_cport_id, + connection->bundle->intf->interface_id, + cport_id); + if (ret) { + dev_err(&connection->dev, + "%s: Failed to create svc connection (%d)\n", + __func__, ret); + return ret; + } + + if (hd->driver->connection_create) + hd->driver->connection_create(connection); + } + /* * Inform Interface about Active CPorts. We don't need to do this * operation for control cport. -- cgit v1.2.3-59-g8ed1b From 1b7a9cd5a54536af5a97738097780369779c62d9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 7 Sep 2015 16:01:22 +0530 Subject: greybus: connection: destroy svc connection on connection exit or errors While initializing a connection, the AP requests the SVC to create a connection between a cport on AP and a cport on the Module. The opposite of that is missing, when connection is destroyed or if errors occur after creating the connection. Fix it. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 973d841d8e91..de3962ab07bc 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -312,6 +312,19 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, spin_unlock_irq(&connection->lock); } +static void +gb_connection_svc_connection_destroy(struct gb_connection *connection) +{ + if (connection->hd_cport_id == GB_SVC_CPORT_ID) + return; + + gb_svc_connection_destroy(connection->hd->svc, + connection->hd->endo->ap_intf_id, + connection->hd_cport_id, + connection->bundle->intf->interface_id, + connection->intf_cport_id); +} + static void gb_connection_disconnected(struct gb_connection *connection) { struct gb_control *control; @@ -373,7 +386,7 @@ static int gb_connection_init(struct gb_connection *connection) dev_err(&connection->dev, "Failed to connect CPort-%d (%d)\n", cport_id, ret); - return ret; + goto svc_destroy; } } @@ -406,6 +419,9 @@ disconnect: spin_unlock_irq(&connection->lock); gb_connection_disconnected(connection); +svc_destroy: + gb_connection_svc_connection_destroy(connection); + return ret; } @@ -426,6 +442,7 @@ static void gb_connection_exit(struct gb_connection *connection) connection->protocol->connection_exit(connection); gb_connection_disconnected(connection); + gb_connection_svc_connection_destroy(connection); } /* -- cgit v1.2.3-59-g8ed1b From fb198317fd89ec02582ffd4ca318ec088d06c74e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 7 Sep 2015 16:01:23 +0530 Subject: greybus: connection: Call connection_destroy() from gb_connection_svc_connection_destroy() connection_create() is called right after svc is requested to create the connection and so connection_destroy() must be called just before we request the SVC to destroy the connection. Over that, this fixes the inconsistency where connection_create() is called for all connections except SVC connection, but connection_destroy() is called always. Acked-by: Alexandre Bailon Fixes: 5313ca607afb ("Greybus driver: add a new callbacks to driver") Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index de3962ab07bc..e1a7705fd227 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -318,6 +318,9 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection) if (connection->hd_cport_id == GB_SVC_CPORT_ID) return; + if (connection->hd->driver->connection_destroy) + connection->hd->driver->connection_destroy(connection); + gb_svc_connection_destroy(connection->hd->svc, connection->hd->endo->ap_intf_id, connection->hd_cport_id, @@ -462,9 +465,6 @@ void gb_connection_destroy(struct gb_connection *connection) list_del(&connection->hd_links); spin_unlock_irq(&gb_connections_lock); - if (connection->hd->driver->connection_destroy) - connection->hd->driver->connection_destroy(connection); - if (connection->protocol) gb_protocol_put(connection->protocol); connection->protocol = NULL; -- cgit v1.2.3-59-g8ed1b From 5a5296bb8d67e90866d352b93ecce95d08b77fc0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 7 Sep 2015 16:01:24 +0530 Subject: greybus: Add flags to struct gb_protocol This helps in removing special per-protocol code, with the help of generic flags passed by protocol drivers. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 28 ++++++++++------------------ drivers/staging/greybus/control.c | 2 ++ drivers/staging/greybus/protocol.h | 8 ++++++++ drivers/staging/greybus/svc.c | 5 +++++ 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index e1a7705fd227..3dcbb7832a58 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -315,7 +315,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, static void gb_connection_svc_connection_destroy(struct gb_connection *connection) { - if (connection->hd_cport_id == GB_SVC_CPORT_ID) + if (connection->protocol->flags & GB_PROTOCOL_SKIP_SVC_CONNECTION) return; if (connection->hd->driver->connection_destroy) @@ -334,12 +334,8 @@ static void gb_connection_disconnected(struct gb_connection *connection) int cport_id = connection->intf_cport_id; int ret; - /* - * Inform Interface about In-active CPorts. We don't need to do this - * operation for control cport. - */ - if ((cport_id == GB_CONTROL_CPORT_ID) || - (connection->hd_cport_id == GB_SVC_CPORT_ID)) + /* Inform Interface about inactive CPorts */ + if (connection->protocol->flags & GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED) return; control = connection->bundle->intf->control; @@ -354,13 +350,14 @@ static int gb_connection_init(struct gb_connection *connection) { int cport_id = connection->intf_cport_id; struct greybus_host_device *hd = connection->hd; + struct gb_protocol *protocol = connection->protocol; int ret; /* * Request the SVC to create a connection from AP's cport to interface's * cport. */ - if (connection->hd_cport_id != GB_SVC_CPORT_ID) { + if (!(protocol->flags & GB_PROTOCOL_SKIP_SVC_CONNECTION)) { ret = gb_svc_connection_create(hd->svc, hd->endo->ap_intf_id, connection->hd_cport_id, connection->bundle->intf->interface_id, @@ -375,13 +372,8 @@ static int gb_connection_init(struct gb_connection *connection) if (hd->driver->connection_create) hd->driver->connection_create(connection); } - - /* - * Inform Interface about Active CPorts. We don't need to do this - * operation for control cport. - */ - if (cport_id != GB_CONTROL_CPORT_ID && - connection->hd_cport_id != GB_SVC_CPORT_ID) { + /* Inform Interface about active CPorts */ + if (!(protocol->flags & GB_PROTOCOL_SKIP_CONTROL_CONNECTED)) { struct gb_control *control = connection->bundle->intf->control; ret = gb_control_connected_operation(control, cport_id); @@ -402,7 +394,7 @@ static int gb_connection_init(struct gb_connection *connection) * Request protocol version supported by the module. We don't need to do * this for SVC as that is initiated by the SVC. */ - if (connection->hd_cport_id != GB_SVC_CPORT_ID) { + if (!(protocol->flags & GB_PROTOCOL_SKIP_VERSION)) { ret = gb_protocol_get_version(connection); if (ret) { dev_err(&connection->dev, @@ -412,7 +404,7 @@ static int gb_connection_init(struct gb_connection *connection) } } - ret = connection->protocol->connection_init(connection); + ret = protocol->connection_init(connection); if (!ret) return 0; @@ -505,7 +497,7 @@ int gb_connection_bind_protocol(struct gb_connection *connection) * active device, so bring up the connection at the same time. */ if ((!connection->bundle && - connection->hd_cport_id == GB_SVC_CPORT_ID) || + protocol->flags & GB_PROTOCOL_NO_BUNDLE) || connection->bundle->intf->device_id != GB_DEVICE_ID_BAD) { ret = gb_connection_init(connection); if (ret) { diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index e675c5c89123..c092bebba77c 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -130,5 +130,7 @@ static struct gb_protocol control_protocol = { .connection_init = gb_control_connection_init, .connection_exit = gb_control_connection_exit, .request_recv = gb_control_request_recv, + .flags = GB_PROTOCOL_SKIP_CONTROL_CONNECTED | + GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED, }; gb_builtin_protocol_driver(control_protocol); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 8d55a4a2f06e..d856885a89e1 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -13,6 +13,13 @@ struct gb_connection; struct gb_operation; +/* Possible flags for protocol drivers */ +#define GB_PROTOCOL_SKIP_CONTROL_CONNECTED BIT(0) /* Don't sent connected requests */ +#define GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED BIT(1) /* Don't sent disconnected requests */ +#define GB_PROTOCOL_NO_BUNDLE BIT(2) /* Protocol May have a bundle-less connection */ +#define GB_PROTOCOL_SKIP_VERSION BIT(3) /* Don't send get_version() requests */ +#define GB_PROTOCOL_SKIP_SVC_CONNECTION BIT(4) /* Don't send SVC connection requests */ + typedef int (*gb_connection_init_t)(struct gb_connection *); typedef void (*gb_connection_exit_t)(struct gb_connection *); typedef int (*gb_request_recv_t)(u8, struct gb_operation *); @@ -27,6 +34,7 @@ struct gb_protocol { u8 major; u8 minor; u8 count; + unsigned long flags; struct list_head links; /* global list */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b32519ac26fb..21dafcc3409a 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -524,5 +524,10 @@ static struct gb_protocol svc_protocol = { .connection_init = gb_svc_connection_init, .connection_exit = gb_svc_connection_exit, .request_recv = gb_svc_request_recv, + .flags = GB_PROTOCOL_SKIP_CONTROL_CONNECTED | + GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED | + GB_PROTOCOL_NO_BUNDLE | + GB_PROTOCOL_SKIP_VERSION | + GB_PROTOCOL_SKIP_SVC_CONNECTION, }; gb_builtin_protocol_driver(svc_protocol); -- cgit v1.2.3-59-g8ed1b From bb10685246b4a4885e569306eca0bb64ded1b4f0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 7 Sep 2015 16:01:25 +0530 Subject: greybus: svc: No need of per-direction route-create requests The route-create request creates bi-directional routes and there is no need to make separate calls for setting up routes on both the directions. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 21dafcc3409a..71a4869e10b9 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -148,6 +148,7 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); +/* Creates bi-directional routes between the devices */ static int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, u8 intf2_id, u8 dev2_id) { @@ -305,14 +306,6 @@ static void svc_process_hotplug(struct work_struct *work) goto ida_put; } - ret = gb_svc_route_create(svc, intf_id, device_id, hd->endo->ap_intf_id, - GB_DEVICE_ID_AP); - if (ret) { - dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", - __func__, intf_id, device_id, ret); - goto ida_put; - } - ret = gb_interface_init(intf, device_id); if (ret) { dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", -- cgit v1.2.3-59-g8ed1b From 0a020570ed2f7817998870a9451526ffe492c44a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 7 Sep 2015 18:05:26 +0530 Subject: greybus: svc: destroy the route on module hot-unplug We created two-way routes between the AP and module's interface on hotplug, and forgot to remove them on hot-unplug. The same is also required while handling errors in hotplug case. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 7 +++++++ drivers/staging/greybus/svc.c | 31 +++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c8bfced0ccf7..77a7c4956929 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -743,6 +743,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_CONN_CREATE 0x07 #define GB_SVC_TYPE_CONN_DESTROY 0x08 #define GB_SVC_TYPE_ROUTE_CREATE 0x0b +#define GB_SVC_TYPE_ROUTE_DESTROY 0x0c /* SVC version request/response have same payload as gb_protocol_version_response */ @@ -806,6 +807,12 @@ struct gb_svc_route_create_request { } __packed; /* route create response has no payload */ +struct gb_svc_route_destroy_request { + __u8 intf1_id; + __u8 intf2_id; +} __packed; +/* route destroy response has no payload */ + /* RAW */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 71a4869e10b9..73fad4a735f2 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -163,6 +163,24 @@ static int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, &request, sizeof(request), NULL, 0); } +/* Destroys bi-directional routes between the devices */ +static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) +{ + struct gb_svc_route_destroy_request request; + int ret; + + request.intf1_id = intf1_id; + request.intf2_id = intf2_id; + + ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_DESTROY, + &request, sizeof(request), NULL, 0); + if (ret) { + dev_err(&svc->connection->dev, + "failed to destroy route (%hhx %hhx) %d\n", + intf1_id, intf2_id, ret); + } +} + static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -303,18 +321,20 @@ static void svc_process_hotplug(struct work_struct *work) if (ret) { dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", __func__, intf_id, device_id, ret); - goto ida_put; + goto svc_id_free; } ret = gb_interface_init(intf, device_id); if (ret) { dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", __func__, intf_id, device_id, ret); - goto svc_id_free; + goto destroy_route; } goto free_svc_hotplug; +destroy_route: + gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id); svc_id_free: /* * XXX Should we tell SVC that this id doesn't belong to interface @@ -369,6 +389,7 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload; struct greybus_host_device *hd = op->connection->hd; struct device *dev = &op->connection->dev; + struct gb_svc *svc = op->connection->private; u8 device_id; struct gb_interface *intf; u8 intf_id; @@ -391,6 +412,12 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) device_id = intf->device_id; gb_interface_remove(hd, intf_id); + + /* + * Destroy the two-way route between the AP and the interface. + */ + gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id); + ida_simple_remove(&greybus_svc_device_id_map, device_id); return 0; -- cgit v1.2.3-59-g8ed1b From 8552ca0f3634bf6aaf8f752fb9a73652326117eb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 7 Sep 2015 17:56:58 +0530 Subject: greybus: connection: Add sysfs 'ap_cport_id' file for connections Its a very useful piece of information, i.e. the cport id of the AP to which the cport of the module is connected, and is required lots of times. It isn't known in advance as it is allocated at runtime. This patch creates another file 'ap_cport_id', only for the connection directories, which will give the cport id of the AP. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 8 ++++++++ drivers/staging/greybus/connection.c | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 808fde96f56b..acc405456198 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -126,6 +126,14 @@ Description: 3 - error 4 - destroying +What: /sys/bus/greybus/device/.../ap_cport_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The cport ID of the AP, to which cport of the module is + connected. + What: /sys/bus/greybus/device/.../protocol_id Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3dcbb7832a58..dc45298ad099 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -116,9 +116,18 @@ protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(protocol_id); +static ssize_t +ap_cport_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct gb_connection *connection = to_gb_connection(dev); + return sprintf(buf, "%hu\n", connection->hd_cport_id); +} +static DEVICE_ATTR_RO(ap_cport_id); + static struct attribute *connection_attrs[] = { &dev_attr_state.attr, &dev_attr_protocol_id.attr, + &dev_attr_ap_cport_id.attr, NULL, }; -- cgit v1.2.3-59-g8ed1b From 6de00a5f32e09b58879175f2942ebeac699d3534 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:39 +0100 Subject: greybus: loopback: ensure count decrement happens before sysfs_remove_groups This patches fixes a case where gb_dev.count is decremented too late in the exit() routine. Signed-off-by: Bryan O'Donoghue Reported-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 231d1d4c1104..33c21cf22687 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -952,14 +952,13 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) connection->private = NULL; kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); + gb_dev.count--; if (!gb_dev.count) sysfs_remove_groups(kobj, loopback_dev_groups); sysfs_remove_groups(&connection->dev.kobj, loopback_con_groups); debugfs_remove(gb->file); list_del(&gb->entry); kfree(gb); - gb_dev.count--; - mutex_unlock(&gb_dev.mutex); } -- cgit v1.2.3-59-g8ed1b From 29f020290d8897e3ed6ed8a8f8938c0b8545bbcd Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:40 +0100 Subject: greybus: loopback: hold a coarse lock while init/exit run This patch holds gb_dev.mutex for the duration of init and exit to reduce complexity while ensuring that init and exit run atomically with respect to slave threads @ gb_loopback_fn(). Signed-off-by: Bryan O'Donoghue Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 33c21cf22687..7e1f527e3aef 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -864,21 +864,18 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb_dev.root, &gb_dev, &gb_loopback_debugfs_dev_latency_ops); retval = sysfs_create_groups(kobj, loopback_dev_groups); - if (retval) { - mutex_unlock(&gb_dev.mutex); + if (retval) goto out_sysfs; - } + /* Calculate maximum payload */ gb_dev.size_max = gb_operation_get_payload_size_max(connection); if (gb_dev.size_max <= sizeof(struct gb_loopback_transfer_request)) { retval = -EINVAL; - mutex_unlock(&gb_dev.mutex); goto out_sysfs; } gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request); } - mutex_unlock(&gb_dev.mutex); /* Create per-connection sysfs and debugfs data-points */ snprintf(name, sizeof(name), "raw_latency_endo0:%d:%d:%d:%d", @@ -916,10 +913,9 @@ static int gb_loopback_connection_init(struct gb_connection *connection) goto out_kfifo1; } - mutex_lock(&gb_dev.mutex); list_add_tail(&gb->entry, &gb_dev.list); - mutex_unlock(&gb_dev.mutex); gb_dev.count++; + mutex_unlock(&gb_dev.mutex); return 0; out_kfifo1: @@ -934,6 +930,7 @@ out_sysfs_dev: debugfs_remove(gb->file); connection->private = NULL; out_sysfs: + mutex_unlock(&gb_dev.mutex); kfree(gb); return retval; -- cgit v1.2.3-59-g8ed1b From 5f3e0d17a3938ee253b6e6b7d16033121645b3a4 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:41 +0100 Subject: greybus: loopback: exit kfree after mutex release init doesn't have a lock for kzalloc so exit shouldn't have lock with the corresponding kfree. Signed-off-by: Bryan O'Donoghue Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 7e1f527e3aef..745490af7e47 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -955,8 +955,8 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) sysfs_remove_groups(&connection->dev.kobj, loopback_con_groups); debugfs_remove(gb->file); list_del(&gb->entry); - kfree(gb); mutex_unlock(&gb_dev.mutex); + kfree(gb); } static struct gb_protocol loopback_protocol = { -- cgit v1.2.3-59-g8ed1b From c7ea5ed6f2790240a8ddaf98905a9465a37e839e Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:42 +0100 Subject: greybus: loopback: ensure debugfs entires are cleaned up on exit bdd4bba4 ('greybus/loopback: add module level sys/debug fs data points') added a debugfs entry attached to gb_dev but omitted the cleanup on gb_init error and gb_exit. This patchs fixes the missing debugfs_remove(). Signed-off-by: Bryan O'Donoghue Reported-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 745490af7e47..8bfeec8f71a0 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -34,6 +34,7 @@ struct gb_loopback_stats { struct gb_loopback_device { struct dentry *root; + struct dentry *file; u32 count; struct kfifo kfifo; @@ -860,9 +861,9 @@ static int gb_loopback_connection_init(struct gb_connection *connection) if (!gb_dev.count) { snprintf(name, sizeof(name), "raw_latency_endo0:%d", connection->bundle->intf->module->module_id); - debugfs_create_file(name, S_IFREG | S_IRUGO, - gb_dev.root, &gb_dev, - &gb_loopback_debugfs_dev_latency_ops); + gb_dev.file = debugfs_create_file(name, S_IFREG | S_IRUGO, + gb_dev.root, &gb_dev, + &gb_loopback_debugfs_dev_latency_ops); retval = sysfs_create_groups(kobj, loopback_dev_groups); if (retval) goto out_sysfs; @@ -925,8 +926,10 @@ out_kfifo0: out_sysfs_conn: sysfs_remove_groups(&connection->dev.kobj, loopback_con_groups); out_sysfs_dev: - if (!gb_dev.count) + if (!gb_dev.count) { sysfs_remove_groups(kobj, loopback_dev_groups); + debugfs_remove(gb_dev.file); + } debugfs_remove(gb->file); connection->private = NULL; out_sysfs: @@ -950,8 +953,10 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); gb_dev.count--; - if (!gb_dev.count) + if (!gb_dev.count) { sysfs_remove_groups(kobj, loopback_dev_groups); + debugfs_remove(gb_dev.file); + } sysfs_remove_groups(&connection->dev.kobj, loopback_con_groups); debugfs_remove(gb->file); list_del(&gb->entry); -- cgit v1.2.3-59-g8ed1b From 909cdeb5d81926bb294394919ba84bf9b98d5744 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:43 +0100 Subject: greybus: loopback: ensure sysfs entries are cleaned up on exit bdd4bba4 ('greybus/loopback: add module level sys/debug fs data points') added a sysfs entry attached to gb_dev but missed the jump to out_sysfs_dev This patchs fixes the missing jump to out_sysfs_dev. Signed-off-by: Bryan O'Donoghue Reported-by: Viresh Kumar Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 8bfeec8f71a0..338522a49f9d 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -873,7 +873,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) if (gb_dev.size_max <= sizeof(struct gb_loopback_transfer_request)) { retval = -EINVAL; - goto out_sysfs; + goto out_sysfs_dev; } gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request); } -- cgit v1.2.3-59-g8ed1b From f2cfa7043940cdda5c9e945b1cf5c66da172bc33 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:44 +0100 Subject: greybus: loopback: convert pr_info to dev_err This patch fixes and invalid use of pr_info() in favour of dev_err(); Signed-off-by: Bryan O'Donoghue Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 338522a49f9d..a9b901fbeb5c 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -450,7 +450,7 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) goto gb_error; if (memcmp(request->data, response->data, len)) { - pr_info("%s: Loopback Data doesn't match\n", __func__); + dev_err(&gb->connection->dev, "Loopback Data doesn't match\n"); retval = -EREMOTEIO; } -- cgit v1.2.3-59-g8ed1b From 4a655ad4c2b83815ccab5057e29bf0f6c981a94e Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:45 +0100 Subject: greybus: loopback: add response len to loopback protocol This patch adds a len field to the loopback protocol. This field is validated in gb_loopback_transfer() and stuffed in gb_loopback_request_recv(). Signed-off-by: Bryan O'Donoghue Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 1 + drivers/staging/greybus/loopback.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 77a7c4956929..76fea9a8ef27 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -917,6 +917,7 @@ struct gb_loopback_transfer_request { } __packed; struct gb_loopback_transfer_response { + __le32 len; __u8 data[0]; } __packed; diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index a9b901fbeb5c..ba65457f9f70 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -524,6 +524,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) return -ENOMEM; } response = operation->response->payload; + response->len = cpu_to_le32(len); memcpy(response->data, request->data, len); } return 0; -- cgit v1.2.3-59-g8ed1b From 5015115def3ed9907fb018180be969cf2f2f116d Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:46 +0100 Subject: greybus: loopback: sort list of connections for masking purposes In user-space we specify a list of connections as a bit-mask with the assumption that a list such is indexed as indicated below. end0:3:3:1:1 = 1 end0:3:3:2:3 = 2 end0:3:3:3:4 = 4 Current code assigns bitmask ids based on the order of discovery, however user-space has no idea what the order of discovery is. This patch sorts the linked list of connections associated with the loopback driver and assigns a bit-id based on the sorted list - not the order of discovery. This change therefore enforces the end-users idea that end0:3:3:1:1 above is always denoted by bit 1 - even if from the AP's perspective it was the last entry discovered. Signed-off-by: Bryan O'Donoghue Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 48 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ba65457f9f70..a62e122c2bcc 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -843,6 +844,50 @@ static const struct file_operations gb_loopback_debugfs_dev_latency_ops = { .release = single_release, }; +static int gb_loopback_bus_id_compare(void *priv, struct list_head *lha, + struct list_head *lhb) +{ + struct gb_loopback *a = list_entry(lha, struct gb_loopback, entry); + struct gb_loopback *b = list_entry(lhb, struct gb_loopback, entry); + struct gb_connection *ca = a->connection; + struct gb_connection *cb = b->connection; + + if (ca->bundle->intf->module->module_id < + cb->bundle->intf->module->module_id) + return -1; + if (cb->bundle->intf->module->module_id < + ca->bundle->intf->module->module_id) + return 1; + if (ca->bundle->intf->interface_id < cb->bundle->intf->interface_id) + return -1; + if (cb->bundle->intf->interface_id < ca->bundle->intf->interface_id) + return 1; + if (ca->bundle->id < cb->bundle->id) + return -1; + if (cb->bundle->id < ca->bundle->id) + return 1; + if (ca->intf_cport_id < cb->intf_cport_id) + return -1; + else if (cb->intf_cport_id < ca->intf_cport_id) + return 1; + + return 0; +} + +static void gb_loopback_insert_id(struct gb_loopback *gb) +{ + struct gb_loopback *gb_list; + u32 new_lbid = 0; + + /* perform an insertion sort */ + list_add_tail(&gb->entry, &gb_dev.list); + list_sort(NULL, &gb_dev.list, gb_loopback_bus_id_compare); + list_for_each_entry(gb_list, &gb_dev.list, entry) { + gb_list->lbid = 1 << new_lbid; + new_lbid++; + } +} + #define DEBUGFS_NAMELEN 32 static int gb_loopback_connection_init(struct gb_connection *connection) @@ -908,14 +953,13 @@ static int gb_loopback_connection_init(struct gb_connection *connection) /* Fork worker thread */ mutex_init(&gb->mutex); - gb->lbid = 1 << gb_dev.count; gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); if (IS_ERR(gb->task)) { retval = PTR_ERR(gb->task); goto out_kfifo1; } - list_add_tail(&gb->entry, &gb_dev.list); + gb_loopback_insert_id(gb); gb_dev.count++; mutex_unlock(&gb_dev.mutex); return 0; -- cgit v1.2.3-59-g8ed1b From fbb8edbafe1525c4168108a46c1134f5a7cd27c4 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:47 +0100 Subject: greybus: loopback: add gb_loopback_operation_sync In order to extract timestamps from gb_message instead of gb_connection we will need access to the gb_operation structure. A first step to that is to create our own gb_loopback_operation_sync which will call gb_operation_request_send_sync() directly. Once loopback is using this function internally it will be possible to convert to gb_message based timestamps and drop gb_connection based timestamps in two seperate patches. Signed-off-by: Bryan O'Donoghue Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 98 ++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index a62e122c2bcc..6155b50b6702 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -380,22 +380,43 @@ static int gb_loopback_active(struct gb_loopback *gb) return (gb_dev.mask == 0 || (gb_dev.mask & gb->lbid)); } -static int gb_loopback_sink(struct gb_loopback *gb, u32 len) +static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, + void *request, int request_size, + void *response, int response_size) { + struct gb_operation *operation; struct timeval ts, te; - struct gb_loopback_transfer_request *request; - int retval; - - request = kmalloc(len + sizeof(*request), GFP_KERNEL); - if (!request) - return -ENOMEM; - - request->len = cpu_to_le32(len); + int ret; do_gettimeofday(&ts); - retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_SINK, - request, len + sizeof(*request), NULL, 0); + operation = gb_operation_create(gb->connection, type, request_size, + response_size, GFP_KERNEL); + if (!operation) { + ret = -ENOMEM; + goto error; + } + if (request_size) + memcpy(operation->request->payload, request, request_size); + + ret = gb_operation_request_send_sync(operation); + if (ret) { + dev_err(&gb->connection->dev, + "synchronous operation failed: %d\n", ret); + } else { + if (response_size == operation->response->payload_size) { + memcpy(response, operation->response->payload, + response_size); + } else { + dev_err(&gb->connection->dev, + "response size %zu expected %d\n", + operation->response->payload_size, + response_size); + } + } + gb_operation_destroy(operation); + +error: do_gettimeofday(&te); /* Calculate the total time the message took */ @@ -407,14 +428,28 @@ static int gb_loopback_sink(struct gb_loopback *gb, u32 len) gb_connection_pop_timestamp(gb->connection, &te); gb->elapsed_nsecs_gb = gb_loopback_calc_latency(&ts, &te); + return ret; +} + +static int gb_loopback_sink(struct gb_loopback *gb, u32 len) +{ + struct gb_loopback_transfer_request *request; + int retval; + + request = kmalloc(len + sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + request->len = cpu_to_le32(len); + retval = gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_SINK, + request, len + sizeof(*request), + NULL, 0); kfree(request); return retval; } static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) { - struct timeval ts, te; struct gb_loopback_transfer_request *request; struct gb_loopback_transfer_response *response; int retval; @@ -431,22 +466,9 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) memset(request->data, 0x5A, len); request->len = cpu_to_le32(len); - - do_gettimeofday(&ts); - retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_TRANSFER, - request, len + sizeof(*request), - response, len + sizeof(*response)); - do_gettimeofday(&te); - - /* Calculate the total time the message took */ - gb_loopback_push_latency_ts(gb, &ts, &te); - gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); - - /* Calculate non-greybus related component of the latency */ - gb_connection_pop_timestamp(gb->connection, &ts); - gb_connection_pop_timestamp(gb->connection, &te); - gb->elapsed_nsecs_gb = gb_loopback_calc_latency(&ts, &te); - + retval = gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_TRANSFER, + request, len + sizeof(*request), + response, len + sizeof(*response)); if (retval) goto gb_error; @@ -464,24 +486,8 @@ gb_error: static int gb_loopback_ping(struct gb_loopback *gb) { - struct timeval ts, te; - int retval; - - do_gettimeofday(&ts); - retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_PING, - NULL, 0, NULL, 0); - do_gettimeofday(&te); - - /* Calculate the total time the message took */ - gb_loopback_push_latency_ts(gb, &ts, &te); - gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); - - /* Calculate non-greybus related component of the latency */ - gb_connection_pop_timestamp(gb->connection, &ts); - gb_connection_pop_timestamp(gb->connection, &te); - gb->elapsed_nsecs_gb = gb_loopback_calc_latency(&ts, &te); - - return retval; + return gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_PING, + NULL, 0, NULL, 0); } static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) -- cgit v1.2.3-59-g8ed1b From 26717ed328b5d238b5c6268912d45703b99b79d0 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 14 Sep 2015 10:48:55 +0100 Subject: greybus: loopback: use dev_name to populate sysfsname dev_name() will give a nice string representing the end0:X:Y:Z:W name mitigating the need to pick apart the various nested data structures and print out their various identifiers. Signed-off-by: Bryan O'Donoghue Suggested-by: Viresh Kumar Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 6155b50b6702..512453621a9b 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -931,11 +931,8 @@ static int gb_loopback_connection_init(struct gb_connection *connection) } /* Create per-connection sysfs and debugfs data-points */ - snprintf(name, sizeof(name), "raw_latency_endo0:%d:%d:%d:%d", - connection->bundle->intf->module->module_id, - connection->bundle->intf->interface_id, - connection->bundle->id, - connection->intf_cport_id); + snprintf(name, sizeof(name), "raw_latency_endo0:%s", + dev_name(&connection->dev)); gb->file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, gb, &gb_loopback_debugfs_latency_ops); gb->connection = connection; -- cgit v1.2.3-59-g8ed1b From 19151c3dd4cfcced6f173a9b1e5aba07d7d1d9b9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 9 Sep 2015 21:08:29 +0530 Subject: greybus: svc: Implement DME peer get/set attributes helpers These are required to get/set DME attributes of the modules. This is implemented based on the greybus specifications. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 24 ++++++++++ drivers/staging/greybus/svc.c | 72 +++++++++++++++++++++++++++++ drivers/staging/greybus/svc.h | 4 ++ 3 files changed, 100 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 76fea9a8ef27..6fd20bdc94af 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -742,6 +742,8 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_RESET 0x06 #define GB_SVC_TYPE_CONN_CREATE 0x07 #define GB_SVC_TYPE_CONN_DESTROY 0x08 +#define GB_SVC_TYPE_DME_PEER_GET 0x09 +#define GB_SVC_TYPE_DME_PEER_SET 0x0a #define GB_SVC_TYPE_ROUTE_CREATE 0x0b #define GB_SVC_TYPE_ROUTE_DESTROY 0x0c @@ -799,6 +801,28 @@ struct gb_svc_conn_destroy_request { } __packed; /* connection destroy response has no payload */ +struct gb_svc_dme_peer_get_request { + __u8 intf_id; + __u16 attr; + __u16 selector; +} __packed; + +struct gb_svc_dme_peer_get_response { + __u16 result_code; + __u32 attr_value; +} __packed; + +struct gb_svc_dme_peer_set_request { + __u8 intf_id; + __u16 attr; + __u16 selector; + __u32 value; +} __packed; + +struct gb_svc_dme_peer_set_response { + __u16 result_code; +} __packed; + struct gb_svc_route_create_request { __u8 intf1_id; __u8 dev1_id; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 73fad4a735f2..241b9da2f823 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -104,6 +104,78 @@ int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id) } EXPORT_SYMBOL_GPL(gb_svc_intf_reset); +int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, + u32 *value) +{ + struct gb_svc_dme_peer_get_request request; + struct gb_svc_dme_peer_get_response response; + u16 result; + int ret; + + request.intf_id = intf_id; + request.attr = cpu_to_le16(attr); + request.selector = cpu_to_le16(selector); + + ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_GET, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) { + dev_err(&svc->connection->dev, + "failed to get DME attribute (%hhu %hx %hu) %d\n", + intf_id, attr, selector, ret); + return ret; + } + + result = le16_to_cpu(response.result_code); + if (result) { + dev_err(&svc->connection->dev, + "Unipro error %hu while getting DME attribute (%hhu %hx %hu)\n", + result, intf_id, attr, selector); + return -EINVAL; + } + + if (value) + *value = le32_to_cpu(response.attr_value); + + return 0; +} +EXPORT_SYMBOL_GPL(gb_svc_dme_peer_get); + +int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, + u32 value) +{ + struct gb_svc_dme_peer_set_request request; + struct gb_svc_dme_peer_set_response response; + u16 result; + int ret; + + request.intf_id = intf_id; + request.attr = cpu_to_le16(attr); + request.selector = cpu_to_le16(selector); + request.value = cpu_to_le32(value); + + ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_SET, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) { + dev_err(&svc->connection->dev, + "failed to set DME attribute (%hhu %hx %hu %u) %d\n", + intf_id, attr, selector, value, ret); + return ret; + } + + result = le16_to_cpu(response.result_code); + if (result) { + dev_err(&svc->connection->dev, + "Unipro error %hu while setting DME attribute (%hhu %hx %hu %u)\n", + result, intf_id, attr, selector, value); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set); + int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index f1acb82523d1..75518f8a199e 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -17,6 +17,10 @@ int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); +int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, + u32 *value); +int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, + u32 value); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From 8bcc2d80dd6aaed009bd0212cf844ef39131547a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 9 Sep 2015 21:08:30 +0530 Subject: greybus: firmware: firmware's file name is 32 characters long 28 is the wrong value and should be 32 instead. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index e888b7ae4c59..fbc6048644a0 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -27,7 +27,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) { struct gb_connection *connection = firmware->connection; struct gb_interface *intf = connection->bundle->intf; - char firmware_name[28]; + char firmware_name[32]; /* Already have a firmware, free it */ if (firmware->fw) -- cgit v1.2.3-59-g8ed1b From c9733b78ca4734a10e1c9b7a23b44fc1736d9daa Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 9 Sep 2015 21:08:31 +0530 Subject: greybus: firmware: Fix incorrect firmware file's name All the id-fields are 32 bit long instead of 16 bits and so we will need 8 characters per field instead of four. Also the stage field is only one byte long and so needs just two characters to represent it. Reported-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index fbc6048644a0..b8f33e7bce55 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -27,7 +27,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) { struct gb_connection *connection = firmware->connection; struct gb_interface *intf = connection->bundle->intf; - char firmware_name[32]; + char firmware_name[46]; /* Already have a firmware, free it */ if (firmware->fw) @@ -39,7 +39,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) * XXX Name it properly.. */ snprintf(firmware_name, sizeof(firmware_name), - "ara:%04x:%04x:%04x:%04x:%04x.fw", + "ara:%08x:%08x:%08x:%08x:%02x.fw", intf->unipro_mfg_id, intf->unipro_prod_id, intf->ara_vend_id, intf->ara_prod_id, stage); -- cgit v1.2.3-59-g8ed1b From 1a8862863a7171e8b97fe8833bb3a78770a8aaee Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 9 Sep 2015 21:08:32 +0530 Subject: greybus: firmware: firmware image name has .tftf instead of .fw That's how the bootrom-tool names it, and that's how the kernel should expect it. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index b8f33e7bce55..fdd5e0cc9ebc 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -27,7 +27,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) { struct gb_connection *connection = firmware->connection; struct gb_interface *intf = connection->bundle->intf; - char firmware_name[46]; + char firmware_name[48]; /* Already have a firmware, free it */ if (firmware->fw) @@ -39,7 +39,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) * XXX Name it properly.. */ snprintf(firmware_name, sizeof(firmware_name), - "ara:%08x:%08x:%08x:%08x:%02x.fw", + "ara:%08x:%08x:%08x:%08x:%02x.tftf", intf->unipro_mfg_id, intf->unipro_prod_id, intf->ara_vend_id, intf->ara_prod_id, stage); -- cgit v1.2.3-59-g8ed1b From 4c9e228407a25a99876b979479ff76208ab7de47 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 9 Sep 2015 21:08:33 +0530 Subject: greybus: firmware: Send AP-ready operation Module's Bootrom needs a way to know (currently), when to start sending requests to the AP. The version request is sent before connection_init() routine is called, and if the module sends the request right after receiving version request, the connection->private field will be NULL. Fix this TEMPORARILY by sending an AP_READY request. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 15 +++++++++++++++ drivers/staging/greybus/greybus_protocols.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index fdd5e0cc9ebc..5642bc33c1ce 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -165,6 +165,7 @@ static int gb_firmware_request_recv(u8 type, struct gb_operation *op) static int gb_firmware_connection_init(struct gb_connection *connection) { struct gb_firmware *firmware; + int ret; firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) @@ -173,6 +174,20 @@ static int gb_firmware_connection_init(struct gb_connection *connection) firmware->connection = connection; connection->private = firmware; + /* + * Module's Bootrom needs a way to know (currently), when to start + * sending requests to the AP. The version request is sent before this + * routine is called, and if the module sends the request right after + * receiving version request, the connection->private field will be + * NULL. + * + * Fix this TEMPORARILY by sending an AP_READY request. + */ + ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_AP_READY, NULL, 0, + NULL, 0); + if (ret) + dev_err(&connection->dev, "Failed to send AP READY (%d)\n", ret); + return 0; } diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 6fd20bdc94af..d77e03915428 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -155,6 +155,7 @@ struct gb_control_disconnected_request { #define GB_FIRMWARE_TYPE_FIRMWARE_SIZE 0x02 #define GB_FIRMWARE_TYPE_GET_FIRMWARE 0x03 #define GB_FIRMWARE_TYPE_READY_TO_BOOT 0x04 +#define GB_FIRMWARE_TYPE_AP_READY 0x05 /* Request with no-payload */ /* Greybus firmware boot stages */ #define GB_FIRMWARE_BOOT_STAGE_ONE 0x01 /* Reserved for the boot ROM */ -- cgit v1.2.3-59-g8ed1b From d7849bffc622b3405ef226364d67bb81af48c152 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 14 Sep 2015 20:19:02 +0200 Subject: greybus: vibrator: fix tear-down race Do not release the minor number until after the device has been deregistered. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/vibrator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 96d649acf122..fd40cda565e4 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -148,8 +148,8 @@ static void gb_vibrator_connection_exit(struct gb_connection *connection) #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); #endif - ida_simple_remove(&minors, vib->minor); device_unregister(vib->dev); + ida_simple_remove(&minors, vib->minor); kfree(vib); } -- cgit v1.2.3-59-g8ed1b From e5265266ec6360702feb6ff69d1aec9e318ae1f6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 14 Sep 2015 20:19:03 +0200 Subject: greybus: sdio: fix tear-down use-after-free The mmc-driver private data must not be accessed after mmc_free_host() has released it. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 24b2e3152fa1..14617e31782e 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -750,8 +750,8 @@ static void gb_sdio_connection_exit(struct gb_connection *connection) flush_workqueue(gb_sdio_mrq_workqueue); destroy_workqueue(gb_sdio_mrq_workqueue); mmc_remove_host(mmc); - mmc_free_host(mmc); kfree(host->xfer_buffer); + mmc_free_host(mmc); } static struct gb_protocol sdio_protocol = { -- cgit v1.2.3-59-g8ed1b From b9154df5cfd2d4cc180f425f0b26e095ce1ffdbc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 14 Sep 2015 20:19:04 +0200 Subject: greybus: sdio: fix work-queue leak and use-after-free A single global work-queue pointer was used for the per-connection workqueue, something which would lead to memory leaks and all sorts of bad things if there are ever more than one SDIO connection in a system. Also add the missing error handling when allocating the queue. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 14617e31782e..bfa1181074d6 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -25,6 +25,7 @@ struct gb_sdio_host { void *xfer_buffer; spinlock_t xfer; /* lock to cancel ongoing transfer */ bool xfer_stop; + struct workqueue_struct *mrq_workqueue; struct work_struct mrqwork; u8 queued_events; bool removed; @@ -32,7 +33,6 @@ struct gb_sdio_host { bool read_only; }; -static struct workqueue_struct *gb_sdio_mrq_workqueue; #define GB_SDIO_RSP_R1_R5_R6_R7 (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ GB_SDIO_RSP_OPCODE) @@ -497,7 +497,7 @@ static void gb_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) goto out; } - queue_work(gb_sdio_mrq_workqueue, &host->mrqwork); + queue_work(host->mrq_workqueue, &host->mrqwork); mutex_unlock(&host->lock); return; @@ -710,7 +710,12 @@ static int gb_sdio_connection_init(struct gb_connection *connection) } mutex_init(&host->lock); spin_lock_init(&host->xfer); - gb_sdio_mrq_workqueue = alloc_workqueue("gb_sdio_mrq", 0, 1); + host->mrq_workqueue = alloc_workqueue("mmc-%s", 0, 1, + dev_name(&connection->dev)); + if (!host->mrq_workqueue) { + ret = -ENOMEM; + goto free_buffer; + } INIT_WORK(&host->mrqwork, gb_sdio_mrq_work); ret = mmc_add_host(mmc); @@ -723,9 +728,9 @@ static int gb_sdio_connection_init(struct gb_connection *connection) return ret; free_work: - destroy_workqueue(gb_sdio_mrq_workqueue); + destroy_workqueue(host->mrq_workqueue); +free_buffer: kfree(host->xfer_buffer); - free_mmc: connection->private = NULL; mmc_free_host(mmc); @@ -747,8 +752,8 @@ static void gb_sdio_connection_exit(struct gb_connection *connection) connection->private = NULL; mutex_unlock(&host->lock); - flush_workqueue(gb_sdio_mrq_workqueue); - destroy_workqueue(gb_sdio_mrq_workqueue); + flush_workqueue(host->mrq_workqueue); + destroy_workqueue(host->mrq_workqueue); mmc_remove_host(mmc); kfree(host->xfer_buffer); mmc_free_host(mmc); -- cgit v1.2.3-59-g8ed1b From 1ff3dc920500c2f1cdfaa08c6812f19e05a36da1 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 14 Sep 2015 18:20:42 +0200 Subject: greybus: es2: rename misnamed variables and methods Some methods and variables name were a lot confusing. Replace it or add ep_pair in methods or varaibles name to make sources less confusing. Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 96090aaa4181..2ac2c1b433a5 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -123,7 +123,7 @@ static void cport_out_callback(struct urb *urb); static void usb_log_enable(struct es1_ap_dev *es1); static void usb_log_disable(struct es1_ap_dev *es1); -static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) +static int cport_to_ep_pair(struct es1_ap_dev *es1, u16 cport_id) { if (cport_id >= es1->hd->num_cports) return 0; @@ -132,38 +132,38 @@ static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ -static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set) +static int ep_pair_in_use(struct es1_ap_dev *es1, int ep_pair) { int i; for (i = 0; i < es1->hd->num_cports; i++) { - if (es1->cport_to_ep[i] == bulk_ep_set) + if (es1->cport_to_ep[i] == ep_pair) return 1; } return 0; } int map_cport_to_ep(struct es1_ap_dev *es1, - u16 cport_id, int bulk_ep_set) + u16 cport_id, int ep_pair) { int retval; struct cport_to_ep *cport_to_ep; - if (bulk_ep_set < 0 || bulk_ep_set >= NUM_BULKS) + if (ep_pair < 0 || ep_pair >= NUM_BULKS) return -EINVAL; if (cport_id >= es1->hd->num_cports) return -EINVAL; - if (bulk_ep_set && ep_in_use(es1, bulk_ep_set)) + if (ep_pair && ep_pair_in_use(es1, ep_pair)) return -EINVAL; cport_to_ep = kmalloc(sizeof(*cport_to_ep), GFP_KERNEL); if (!cport_to_ep) return -ENOMEM; - es1->cport_to_ep[cport_id] = bulk_ep_set; + es1->cport_to_ep[cport_id] = ep_pair; cport_to_ep->cport_id = cpu_to_le16(cport_id); - cport_to_ep->endpoint_in = es1->cport_in[bulk_ep_set].endpoint; - cport_to_ep->endpoint_out = es1->cport_out[bulk_ep_set].endpoint; + cport_to_ep->endpoint_in = es1->cport_in[ep_pair].endpoint; + cport_to_ep->endpoint_out = es1->cport_out[ep_pair].endpoint; retval = usb_control_msg(es1->usb_dev, usb_sndctrlpipe(es1->usb_dev, 0), @@ -275,7 +275,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, size_t buffer_size; int retval; struct urb *urb; - int bulk_ep_set; + int ep_pair; unsigned long flags; /* @@ -302,10 +302,10 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, buffer_size = sizeof(*message->header) + message->payload_size; - bulk_ep_set = cport_to_ep(es1, cport_id); + ep_pair = cport_to_ep_pair(es1, cport_id); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, - es1->cport_out[bulk_ep_set].endpoint), + es1->cport_out[ep_pair].endpoint), message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; -- cgit v1.2.3-59-g8ed1b From 64c9cabcac57904b8a6d6ae9e8a90c258762c86b Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 14 Sep 2015 18:20:43 +0200 Subject: greybus: es2: change (un)map methods to static Endpoints pair will only be managed by es2 driver. map_cport_to_ep() and unmap_cport() should be static. Signed-off-by: Alexandre Bailon Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 2ac2c1b433a5..ec06dc65f98d 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -143,7 +143,7 @@ static int ep_pair_in_use(struct es1_ap_dev *es1, int ep_pair) return 0; } -int map_cport_to_ep(struct es1_ap_dev *es1, +static int map_cport_to_ep(struct es1_ap_dev *es1, u16 cport_id, int ep_pair) { int retval; @@ -180,7 +180,7 @@ int map_cport_to_ep(struct es1_ap_dev *es1, return retval; } -int unmap_cport(struct es1_ap_dev *es1, u16 cport_id) +static int unmap_cport(struct es1_ap_dev *es1, u16 cport_id) { return map_cport_to_ep(es1, cport_id, 0); } -- cgit v1.2.3-59-g8ed1b From e5acf736caac014de39f618f7725164b75176fea Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Mon, 14 Sep 2015 18:20:44 +0200 Subject: greybus: es2: add some documentation about endpoints mapping Signed-off-by: Alexandre Bailon Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index ec06dc65f98d..834ccd4e23b8 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -108,6 +108,12 @@ struct es1_ap_dev { int *cport_to_ep; }; +/** + * cport_to_ep - information about cport to endpoints mapping + * @cport_id: the id of cport to map to endpoints + * @endpoint_in: the endpoint number to use for in transfer + * @endpoint_out: he endpoint number to use for out transfer + */ struct cport_to_ep { __le16 cport_id; __u8 endpoint_in; @@ -123,6 +129,7 @@ static void cport_out_callback(struct urb *urb); static void usb_log_enable(struct es1_ap_dev *es1); static void usb_log_disable(struct es1_ap_dev *es1); +/* Get the endpoints pair mapped to the cport */ static int cport_to_ep_pair(struct es1_ap_dev *es1, u16 cport_id) { if (cport_id >= es1->hd->num_cports) @@ -132,6 +139,7 @@ static int cport_to_ep_pair(struct es1_ap_dev *es1, u16 cport_id) #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +/* Test if the endpoints pair is already mapped to a cport */ static int ep_pair_in_use(struct es1_ap_dev *es1, int ep_pair) { int i; @@ -143,6 +151,7 @@ static int ep_pair_in_use(struct es1_ap_dev *es1, int ep_pair) return 0; } +/* Configure the endpoint mapping and send the request to APBridge */ static int map_cport_to_ep(struct es1_ap_dev *es1, u16 cport_id, int ep_pair) { @@ -180,6 +189,7 @@ static int map_cport_to_ep(struct es1_ap_dev *es1, return retval; } +/* Unmap a cport: use the muxed endpoints pair */ static int unmap_cport(struct es1_ap_dev *es1, u16 cport_id) { return map_cport_to_ep(es1, cport_id, 0); -- cgit v1.2.3-59-g8ed1b From c09db1820f7ae71d0b1aad7cd1efafab0a880917 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 15 Sep 2015 09:18:08 +0200 Subject: greybus: svc: fix ida memory leak The device-id map was never deallocated on SVC-connection tear down. Also make the map per-SVC-connection (there should still be only one) rather than use a global pointer. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 241b9da2f823..5a130e59f075 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -24,6 +24,7 @@ enum gb_svc_state { struct gb_svc { struct gb_connection *connection; enum gb_svc_state state; + struct ida device_id_map; }; struct svc_hotplug { @@ -32,7 +33,6 @@ struct svc_hotplug { struct gb_svc_intf_hotplug_request data; }; -static struct ida greybus_svc_device_id_map; /* * AP's SVC cport is required early to get messages from the SVC. This happens @@ -369,7 +369,7 @@ static void svc_process_hotplug(struct work_struct *work) * XXX Do we need to allocate device ID for SVC or the AP here? And what * XXX about an AP with multiple interface blocks? */ - device_id = ida_simple_get(&greybus_svc_device_id_map, + device_id = ida_simple_get(&svc->device_id_map, GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); if (device_id < 0) { ret = device_id; @@ -413,7 +413,7 @@ svc_id_free: * XXX anymore. */ ida_put: - ida_simple_remove(&greybus_svc_device_id_map, device_id); + ida_simple_remove(&svc->device_id_map, device_id); destroy_interface: gb_interface_remove(hd, intf_id); free_svc_hotplug: @@ -490,7 +490,7 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) */ gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id); - ida_simple_remove(&greybus_svc_device_id_map, device_id); + ida_simple_remove(&svc->device_id_map, device_id); return 0; } @@ -594,7 +594,7 @@ static int gb_svc_connection_init(struct gb_connection *connection) WARN_ON(connection->hd->initial_svc_connection); connection->hd->initial_svc_connection = connection; - ida_init(&greybus_svc_device_id_map); + ida_init(&svc->device_id_map); return 0; } @@ -603,6 +603,7 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; + ida_destroy(&svc->device_id_map); connection->hd->svc = NULL; connection->private = NULL; kfree(svc); -- cgit v1.2.3-59-g8ed1b From 59832931cbafa9211ec9ff8434b6d914f82f0f18 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 15 Sep 2015 10:48:00 +0200 Subject: greybus: svc: fix version response The SVC-protocol driver currently accepts the version offered by the SVC, but still responded with a hard-coded version. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5a130e59f075..075d81d20cb8 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -278,8 +278,9 @@ static int gb_svc_version_request(struct gb_operation *op) } version = op->response->payload; - version->major = GB_SVC_VERSION_MAJOR; - version->minor = GB_SVC_VERSION_MINOR; + version->major = connection->module_major; + version->minor = connection->module_minor; + return 0; } -- cgit v1.2.3-59-g8ed1b From cfb16906860d76b5afe192d32fb79cf9af853b92 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 15 Sep 2015 10:48:01 +0200 Subject: greybus: greybus_protocols: add missing version-request definition Add the missing version-request definition that was falsely claimed to be empty. Update the generic version-request helper and SVC version-request handler to use the request definition rather than rely on the response happening to have the same layout, something which also improves readability. Remove a misplaced "Control Protocol" header while at it. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 11 ++++++++--- drivers/staging/greybus/protocol.c | 21 +++++++++++---------- drivers/staging/greybus/svc.c | 21 +++++++++++---------- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index d77e03915428..9b2d18916403 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -103,9 +103,11 @@ struct gb_operation_msg_hdr { #define GB_REQUEST_TYPE_INVALID 0x00 #define GB_REQUEST_TYPE_PROTOCOL_VERSION 0x01 -/* Control Protocol */ +struct gb_protocol_version_request { + __u8 major; + __u8 minor; +} __packed; -/* version request has no payload */ struct gb_protocol_version_response { __u8 major; __u8 minor; @@ -748,7 +750,10 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_ROUTE_CREATE 0x0b #define GB_SVC_TYPE_ROUTE_DESTROY 0x0c -/* SVC version request/response have same payload as gb_protocol_version_response */ +/* + * SVC version request/response has the same payload as + * gb_protocol_version_request/response. + */ /* SVC protocol hello request */ struct gb_svc_hello_request { diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 1c746597f016..c93f96365667 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -165,30 +165,31 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) int gb_protocol_get_version(struct gb_connection *connection) { - struct gb_protocol_version_response version; + struct gb_protocol_version_request request; + struct gb_protocol_version_response response; int retval; - version.major = connection->protocol->major; - version.minor = connection->protocol->minor; + request.major = connection->protocol->major; + request.minor = connection->protocol->minor; retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, - &version, sizeof(version), &version, - sizeof(version)); + &request, sizeof(request), &response, + sizeof(response)); if (retval) return retval; - if (version.major > connection->protocol->major) { + if (response.major > connection->protocol->major) { dev_err(&connection->dev, "unsupported major version (%hhu > %hhu)\n", - version.major, connection->protocol->major); + response.major, connection->protocol->major); return -ENOTSUPP; } - connection->module_major = version.major; - connection->module_minor = version.minor; + connection->module_major = response.major; + connection->module_minor = response.minor; dev_dbg(&connection->dev, "version_major = %u version_minor = %u\n", - version.major, version.minor); + response.major, response.minor); return 0; } diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 075d81d20cb8..28f03dc463f3 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -256,30 +256,31 @@ static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_protocol_version_response *version; + struct gb_protocol_version_request *request; + struct gb_protocol_version_response *response; struct device *dev = &connection->dev; - version = op->request->payload; + request = op->request->payload; - if (version->major > GB_SVC_VERSION_MAJOR) { + if (request->major > GB_SVC_VERSION_MAJOR) { dev_err(&connection->dev, "unsupported major version (%hhu > %hhu)\n", - version->major, GB_SVC_VERSION_MAJOR); + request->major, GB_SVC_VERSION_MAJOR); return -ENOTSUPP; } - connection->module_major = version->major; - connection->module_minor = version->minor; + connection->module_major = request->major; + connection->module_minor = request->minor; - if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) { + if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); return -ENOMEM; } - version = op->response->payload; - version->major = connection->module_major; - version->minor = connection->module_minor; + response = op->response->payload; + response->major = connection->module_major; + response->minor = connection->module_minor; return 0; } -- cgit v1.2.3-59-g8ed1b From 2498050b03399cc0df32863f9a4c2a63b1f30d79 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 15 Sep 2015 15:33:51 +0100 Subject: greybus: svc: fix endianness for svc message Some fields in svc request were not being set with the correct endianness, which will trigger the following sparse issues as example: greybus/svc.c:116:22: warning: incorrect type in assignment (different base types) greybus/svc.c:116:22: expected unsigned short [unsigned] [assigned] [usertype] attr greybus/svc.c:116:22: got restricted __le16 [usertype] Signed-off-by: Rui Miguel Silva Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 24 ++++++++++++------------ drivers/staging/greybus/svc.c | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9b2d18916403..b3d0c57ea073 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -791,9 +791,9 @@ struct gb_svc_intf_reset_request { struct gb_svc_conn_create_request { __u8 intf1_id; - __u16 cport1_id; + __le16 cport1_id; __u8 intf2_id; - __u16 cport2_id; + __le16 cport2_id; __u8 tc; __u8 flags; } __packed; @@ -801,32 +801,32 @@ struct gb_svc_conn_create_request { struct gb_svc_conn_destroy_request { __u8 intf1_id; - __u16 cport1_id; + __le16 cport1_id; __u8 intf2_id; - __u16 cport2_id; + __le16 cport2_id; } __packed; /* connection destroy response has no payload */ struct gb_svc_dme_peer_get_request { __u8 intf_id; - __u16 attr; - __u16 selector; + __le16 attr; + __le16 selector; } __packed; struct gb_svc_dme_peer_get_response { - __u16 result_code; - __u32 attr_value; + __le16 result_code; + __le32 attr_value; } __packed; struct gb_svc_dme_peer_set_request { __u8 intf_id; - __u16 attr; - __u16 selector; - __u32 value; + __le16 attr; + __le16 selector; + __le32 value; } __packed; struct gb_svc_dme_peer_set_response { - __u16 result_code; + __le16 result_code; } __packed; struct gb_svc_route_create_request { diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 28f03dc463f3..35e2fb525dfa 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -183,9 +183,9 @@ int gb_svc_connection_create(struct gb_svc *svc, struct gb_svc_conn_create_request request; request.intf1_id = intf1_id; - request.cport1_id = cport1_id; + request.cport1_id = cpu_to_le16(cport1_id); request.intf2_id = intf2_id; - request.cport2_id = cport2_id; + request.cport2_id = cpu_to_le16(cport2_id); /* * XXX: fix connections paramaters to TC0 and all CPort flags * for now. @@ -206,9 +206,9 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, int ret; request.intf1_id = intf1_id; - request.cport1_id = cport1_id; + request.cport1_id = cpu_to_le16(cport1_id); request.intf2_id = intf2_id; - request.cport2_id = cport2_id; + request.cport2_id = cpu_to_le16(cport2_id); ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY, &request, sizeof(request), NULL, 0); -- cgit v1.2.3-59-g8ed1b From ced6007a6d2d48efb32d2ca48a13304a04470659 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Wed, 16 Sep 2015 03:29:35 +0200 Subject: greybus: es{1,2}: remove obselete define The SVC Control request is obsolete and not used anymore. Remove the related define. Signed-off-by: Fabien Parent Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 3 --- drivers/staging/greybus/es2.c | 3 --- 2 files changed, 6 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 2b4bb075b9e6..e44bf6cf895c 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -48,9 +48,6 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); */ #define NUM_CPORT_OUT_URB 8 -/* vendor request AP message */ -#define REQUEST_SVC 0x01 - /* vendor request APB1 log */ #define REQUEST_LOG 0x02 diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 834ccd4e23b8..cd33aa7e6d42 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -48,9 +48,6 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); */ #define NUM_CPORT_OUT_URB (8 * NUM_BULKS) -/* vendor request AP message */ -#define REQUEST_SVC 0x01 - /* vendor request APB1 log */ #define REQUEST_LOG 0x02 -- cgit v1.2.3-59-g8ed1b From a95c258c6fe6b4929f52398d1b6cd5432efe2c86 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 17 Sep 2015 13:17:21 +0200 Subject: greybus: connection: clean up svc-connection creation Move SVC-connection creation to its own helper. Note that the connection_create host-driver callback is really unrelated to the SVC connection, and will be removed by a later patch. It is is included for now as the connection_destroy callback is currently made from the SVC-connection-destroy helper. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 53 +++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index dc45298ad099..8abac19f0c43 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -321,6 +321,37 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, spin_unlock_irq(&connection->lock); } +/* + * Request the SVC to create a connection from AP's cport to interface's + * cport. + */ +static int +gb_connection_svc_connection_create(struct gb_connection *connection) +{ + struct greybus_host_device *hd = connection->hd; + struct gb_protocol *protocol = connection->protocol; + int ret; + + if (protocol->flags & GB_PROTOCOL_SKIP_SVC_CONNECTION) + return 0; + + ret = gb_svc_connection_create(hd->svc, + hd->endo->ap_intf_id, + connection->hd_cport_id, + connection->bundle->intf->interface_id, + connection->intf_cport_id); + if (ret) { + dev_err(&connection->dev, + "failed to create svc connection: %d\n", ret); + return ret; + } + + if (hd->driver->connection_create) + hd->driver->connection_create(connection); + + return 0; +} + static void gb_connection_svc_connection_destroy(struct gb_connection *connection) { @@ -358,29 +389,13 @@ static void gb_connection_disconnected(struct gb_connection *connection) static int gb_connection_init(struct gb_connection *connection) { int cport_id = connection->intf_cport_id; - struct greybus_host_device *hd = connection->hd; struct gb_protocol *protocol = connection->protocol; int ret; - /* - * Request the SVC to create a connection from AP's cport to interface's - * cport. - */ - if (!(protocol->flags & GB_PROTOCOL_SKIP_SVC_CONNECTION)) { - ret = gb_svc_connection_create(hd->svc, - hd->endo->ap_intf_id, connection->hd_cport_id, - connection->bundle->intf->interface_id, - cport_id); - if (ret) { - dev_err(&connection->dev, - "%s: Failed to create svc connection (%d)\n", - __func__, ret); - return ret; - } + ret = gb_connection_svc_connection_create(connection); + if (ret) + return ret; - if (hd->driver->connection_create) - hd->driver->connection_create(connection); - } /* Inform Interface about active CPorts */ if (!(protocol->flags & GB_PROTOCOL_SKIP_CONTROL_CONNECTED)) { struct gb_control *control = connection->bundle->intf->control; -- cgit v1.2.3-59-g8ed1b From 8d7a712ca86b4da7220b8c3da37f118fed3e0bb2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 17 Sep 2015 13:17:22 +0200 Subject: greybus: connection: clean up init error paths Clearly mark error-path labels as such and clean up control flow. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 8abac19f0c43..5b8aa04a9f15 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -405,7 +405,7 @@ static int gb_connection_init(struct gb_connection *connection) dev_err(&connection->dev, "Failed to connect CPort-%d (%d)\n", cport_id, ret); - goto svc_destroy; + goto err_svc_destroy; } } @@ -424,21 +424,23 @@ static int gb_connection_init(struct gb_connection *connection) dev_err(&connection->dev, "Failed to get version CPort-%d (%d)\n", cport_id, ret); - goto disconnect; + goto err_disconnect; } } ret = protocol->connection_init(connection); - if (!ret) - return 0; + if (ret) + goto err_disconnect; + + return 0; -disconnect: +err_disconnect: spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_ERROR; spin_unlock_irq(&connection->lock); gb_connection_disconnected(connection); -svc_destroy: +err_svc_destroy: gb_connection_svc_connection_destroy(connection); return ret; -- cgit v1.2.3-59-g8ed1b From 72d748226379979e4a2e3e1ebf5379fa57f6f3fe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 17 Sep 2015 13:17:23 +0200 Subject: greybus: connection: clean up control-disconnected helper Rename helper to the more descriptive gb_connection_control_disconnected(). Use u16 for cport number, remove redundant cport number from warning message, and shorten a long line. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 5b8aa04a9f15..ac787923b2d7 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -368,22 +368,25 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection) connection->intf_cport_id); } -static void gb_connection_disconnected(struct gb_connection *connection) +/* Inform Interface about inactive CPorts */ +static void +gb_connection_control_disconnected(struct gb_connection *connection) { + struct gb_protocol *protocol = connection->protocol; struct gb_control *control; - int cport_id = connection->intf_cport_id; + u16 cport_id = connection->intf_cport_id; int ret; - /* Inform Interface about inactive CPorts */ - if (connection->protocol->flags & GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED) + if (protocol->flags & GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED) return; control = connection->bundle->intf->control; ret = gb_control_disconnected_operation(control, cport_id); - if (ret) + if (ret) { dev_warn(&connection->dev, - "Failed to disconnect CPort-%d (%d)\n", cport_id, ret); + "failed to disconnect cport: %d\n", ret); + } } static int gb_connection_init(struct gb_connection *connection) @@ -439,7 +442,7 @@ err_disconnect: connection->state = GB_CONNECTION_STATE_ERROR; spin_unlock_irq(&connection->lock); - gb_connection_disconnected(connection); + gb_connection_control_disconnected(connection); err_svc_destroy: gb_connection_svc_connection_destroy(connection); @@ -462,7 +465,7 @@ static void gb_connection_exit(struct gb_connection *connection) gb_connection_cancel_operations(connection, -ESHUTDOWN); connection->protocol->connection_exit(connection); - gb_connection_disconnected(connection); + gb_connection_control_disconnected(connection); gb_connection_svc_connection_destroy(connection); } -- cgit v1.2.3-59-g8ed1b From 9d7fc25b3c05a3330002560c19f0dfbfe83ea8a0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 17 Sep 2015 13:17:24 +0200 Subject: greybus: connection: add control-connected helper Add control-connected helper to improve readability. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 38 ++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ac787923b2d7..de5581267fce 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -368,6 +368,29 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection) connection->intf_cport_id); } +/* Inform Interface about active CPorts */ +static int gb_connection_control_connected(struct gb_connection *connection) +{ + struct gb_protocol *protocol = connection->protocol; + struct gb_control *control; + u16 cport_id = connection->intf_cport_id; + int ret; + + if (protocol->flags & GB_PROTOCOL_SKIP_CONTROL_CONNECTED) + return 0; + + control = connection->bundle->intf->control; + + ret = gb_control_connected_operation(control, cport_id); + if (ret) { + dev_err(&connection->dev, + "failed to connect cport: %d\n", ret); + return ret; + } + + return 0; +} + /* Inform Interface about inactive CPorts */ static void gb_connection_control_disconnected(struct gb_connection *connection) @@ -399,18 +422,9 @@ static int gb_connection_init(struct gb_connection *connection) if (ret) return ret; - /* Inform Interface about active CPorts */ - if (!(protocol->flags & GB_PROTOCOL_SKIP_CONTROL_CONNECTED)) { - struct gb_control *control = connection->bundle->intf->control; - - ret = gb_control_connected_operation(control, cport_id); - if (ret) { - dev_err(&connection->dev, - "Failed to connect CPort-%d (%d)\n", - cport_id, ret); - goto err_svc_destroy; - } - } + ret = gb_connection_control_connected(connection); + if (ret) + goto err_svc_destroy; /* Need to enable the connection to initialize it */ spin_lock_irq(&connection->lock); -- cgit v1.2.3-59-g8ed1b From 2846d3ed2e5608bcdb773cf308af7fabbfcb663e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 17 Sep 2015 13:17:25 +0200 Subject: greybus: connection: add protocol-version helper Add protocol-version to improve readability. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 39 +++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index de5581267fce..91d1e477a6b6 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -412,9 +412,30 @@ gb_connection_control_disconnected(struct gb_connection *connection) } } +/* + * Request protocol version supported by the module. We don't need to do + * this for SVC as that is initiated by the SVC. + */ +static int gb_connection_protocol_get_version(struct gb_connection *connection) +{ + struct gb_protocol *protocol = connection->protocol; + int ret; + + if (protocol->flags & GB_PROTOCOL_SKIP_VERSION) + return 0; + + ret = gb_protocol_get_version(connection); + if (ret) { + dev_err(&connection->dev, + "failed to get protocol version: %d\n", ret); + return ret; + } + + return 0; +} + static int gb_connection_init(struct gb_connection *connection) { - int cport_id = connection->intf_cport_id; struct gb_protocol *protocol = connection->protocol; int ret; @@ -431,19 +452,9 @@ static int gb_connection_init(struct gb_connection *connection) connection->state = GB_CONNECTION_STATE_ENABLED; spin_unlock_irq(&connection->lock); - /* - * Request protocol version supported by the module. We don't need to do - * this for SVC as that is initiated by the SVC. - */ - if (!(protocol->flags & GB_PROTOCOL_SKIP_VERSION)) { - ret = gb_protocol_get_version(connection); - if (ret) { - dev_err(&connection->dev, - "Failed to get version CPort-%d (%d)\n", - cport_id, ret); - goto err_disconnect; - } - } + ret = gb_connection_protocol_get_version(connection); + if (ret) + goto err_disconnect; ret = protocol->connection_init(connection); if (ret) -- cgit v1.2.3-59-g8ed1b From d7ea30a57145d13a6e74c90e4f7277cb8705bcc1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 17 Sep 2015 13:17:26 +0200 Subject: greybus: hd: add optional cport enable and disable callbacks Add optional cport enable and disable callbacks to the greybus host drivers, that can be used to initialise and allocate/release resources associated with a cport during connection setup/teardown (e.g. software queues and hardware state). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 37 +++++++++++++++++++++++++++++++++++- drivers/staging/greybus/greybus.h | 2 ++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 91d1e477a6b6..b4947f01d22b 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -284,6 +284,34 @@ err_remove_ida: return NULL; } +static int gb_connection_hd_cport_enable(struct gb_connection *connection) +{ + struct greybus_host_device *hd = connection->hd; + int ret; + + if (!hd->driver->cport_enable) + return 0; + + ret = hd->driver->cport_enable(hd, connection->hd_cport_id); + if (ret) { + dev_err(&connection->dev, + "failed to enable host cport: %d\n", ret); + return ret; + } + + return 0; +} + +static void gb_connection_hd_cport_disable(struct gb_connection *connection) +{ + struct greybus_host_device *hd = connection->hd; + + if (!hd->driver->cport_disable) + return; + + hd->driver->cport_disable(hd, connection->hd_cport_id); +} + struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id) { @@ -439,10 +467,14 @@ static int gb_connection_init(struct gb_connection *connection) struct gb_protocol *protocol = connection->protocol; int ret; - ret = gb_connection_svc_connection_create(connection); + ret = gb_connection_hd_cport_enable(connection); if (ret) return ret; + ret = gb_connection_svc_connection_create(connection); + if (ret) + goto err_hd_cport_disable; + ret = gb_connection_control_connected(connection); if (ret) goto err_svc_destroy; @@ -470,6 +502,8 @@ err_disconnect: gb_connection_control_disconnected(connection); err_svc_destroy: gb_connection_svc_connection_destroy(connection); +err_hd_cport_disable: + gb_connection_hd_cport_disable(connection); return ret; } @@ -492,6 +526,7 @@ static void gb_connection_exit(struct gb_connection *connection) connection->protocol->connection_exit(connection); gb_connection_control_disconnected(connection); gb_connection_svc_connection_destroy(connection); + gb_connection_hd_cport_disable(connection); } /* diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 9f2eb1f2e8a9..a320d58a219a 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -75,6 +75,8 @@ struct greybus_host_device; struct greybus_host_driver { size_t hd_priv_size; + int (*cport_enable)(struct greybus_host_device *hd, u16 cport_id); + int (*cport_disable)(struct greybus_host_device *hd, u16 cport_id); void (*connection_create)(struct gb_connection *connection); void (*connection_destroy)(struct gb_connection *connection); int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, -- cgit v1.2.3-59-g8ed1b From 3afe952127e6379b32ba0d845231dbe952d6117b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 17 Sep 2015 13:17:27 +0200 Subject: greybus: hd: remove connection-create/destroy callbacks These host-driver callbacks were intended to allow host drivers to prepare a cport, something which can now be handled by the cport enable/disable callbacks instead. The current create/destroy are somewhat confusingly named as they were not supposed to create or destroy connections. They were however called from the unrelated helper functions that do create and destroy SVC connections. Furthermore, no errors were returned should the create callback fail, which should have caused the connection initialisation to fail. Remove these unused callbacks for now, and let us use the cport enable/disable callbacks that should be able handle all host cport initialisation (possibly after also adding an interface to provide information for endpoint-cport mapping). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ------ drivers/staging/greybus/greybus.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b4947f01d22b..f1541312fcc5 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -374,9 +374,6 @@ gb_connection_svc_connection_create(struct gb_connection *connection) return ret; } - if (hd->driver->connection_create) - hd->driver->connection_create(connection); - return 0; } @@ -386,9 +383,6 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection) if (connection->protocol->flags & GB_PROTOCOL_SKIP_SVC_CONNECTION) return; - if (connection->hd->driver->connection_destroy) - connection->hd->driver->connection_destroy(connection); - gb_svc_connection_destroy(connection->hd->svc, connection->hd->endo->ap_intf_id, connection->hd_cport_id, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index a320d58a219a..e4e53c139ac4 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -77,8 +77,6 @@ struct greybus_host_driver { int (*cport_enable)(struct greybus_host_device *hd, u16 cport_id); int (*cport_disable)(struct greybus_host_device *hd, u16 cport_id); - void (*connection_create)(struct gb_connection *connection); - void (*connection_destroy)(struct gb_connection *connection); int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); -- cgit v1.2.3-59-g8ed1b From 2e49b15763cc460cabc0713e05c7d24fb892d9ad Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 15 Sep 2015 11:04:30 +0530 Subject: greybus: Documentation: Update sysfs-bus-greybus The file names here weren't in sync with what we have today and the updates give a better picture of the same. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs-bus-greybus | 117 ++++++++++++++------- 1 file changed, 78 insertions(+), 39 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index acc405456198..b2e699ccdcd5 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -1,46 +1,54 @@ -What: /sys/bus/greybus/device/endo +What: /sys/bus/greybus/device/endoE Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The "root" endo for the Greybus device tree. XXX is - replaced with the numeric value of the endo layout - scheme as documented in the ARA Module Developer Kit. + The "root" endo devices for the Greybus device tree. E + is replaced with a 2 byte number representing the endo, + mostly 0. -What: /sys/bus/greybus/device/endo/id +What: /sys/bus/greybus/device/endoE/id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The Endo ID, which is a 2-byte hexadecimal value - defined by the the Endo layout scheme, documented in + defined by the Endo layout scheme, documented in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo/ap_intf_id +What: /sys/bus/greybus/device/endoE/ap_intf_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The AP interface ID, a small non-zero integer which + The AP interface ID, a 1-byte non-zero integer which defines the position of the AP module on the Endo. The interface positions are defined in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endo/svc/serial_number +What: /sys/bus/greybus/device/endoE/svc/serial_number Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The serial number of the SVC device -What: /sys/bus/greybus/device/endo/svc/version +What: /sys/bus/greybus/device/endoE/svc/version Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The version number of the firmware in the SVC device. -What: /sys/bus/greybus/device/endo/../epm +What: /sys/bus/greybus/device/endoE:M +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + A module slot on the endoE, M is replaced by a 1-byte + number representing the module slot. + +What: /sys/bus/greybus/device/endoE:M/epm Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -50,7 +58,7 @@ Description: Writing 1 to it turns it on, writing 0 to it turns it off. Reading the value returns if it is on or off. -What: /sys/bus/greybus/device/endo/.../power_control +What: /sys/bus/greybus/device/endoE:M/power_control Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -60,7 +68,7 @@ Description: to the module, writing 0 to it turns power off to the module. Reading the value returns if it is on or off. -What: /sys/bus/greybus/device/endo/.../present +What: /sys/bus/greybus/device/endoE:M/present Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -70,63 +78,93 @@ Description: This is read-only, 1 means a module is present, 0 means no module is present. -What: /sys/bus/greybus/device/.../product +What: /sys/bus/greybus/device/endoE:M:I +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + An Interface I on the module slot M on the endoE, I is + replaced by a 1-byte number representing the interface. + +What: /sys/bus/greybus/device/endoE:M:I/device_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The device ID of a Greybus interface block. + +What: /sys/bus/greybus/device/endoE:M:I/product Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Product ID of a Greybus interface block. -What: /sys/bus/greybus/device/.../product_string +What: /sys/bus/greybus/device/endoE:M:I/product_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Product ID string of a Greybus interface block. -What: /sys/bus/greybus/device/.../unique_id +What: /sys/bus/greybus/device/endoE:M:I/unique_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Unique ID of a Greybus interface block. -What: /sys/bus/greybus/device/.../vendor +What: /sys/bus/greybus/device/endoE:M:I/vendor Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Vendor ID of a Greybus interface block. -What: /sys/bus/greybus/device/.../vendor_string +What: /sys/bus/greybus/device/endoE:M:I/vendor_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Vendor ID string of a Greybus interface block. -What: /sys/bus/greybus/device/.../device_id +What: /sys/bus/greybus/device/endoE:M:I:B Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The device ID of a Greybus interface block. + A bundle B on the Interface I, B is replaced by a 1-byte + number representing the bundle. -What: /sys/bus/greybus/device/.../state +What: /sys/bus/greybus/device/endoE:M:I:B/class Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The current state of a Greybus connection. + The greybus class of the bundle B. - It will be one of the following values: - 0 - invalid - 1 - disabled - 2 - enabled - 3 - error - 4 - destroying +What: /sys/bus/greybus/device/endoE:M:I:B/state +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + A bundle has a state that is managed by the userspace + Endo process. This file allows that Endo to signal + other Android HALs that the state of the bundle has + changed to a specific value. When written to, any + process watching the file will be woken up, and the new + value can be read. It's a "poor-man's IPC", yes, but + simplifies the Android userspace code immensely. -What: /sys/bus/greybus/device/.../ap_cport_id +What: /sys/bus/greybus/device/endoE:M:I:B:C +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + A cport C within bundle B, C is replaced by a 2-byte + number representing the cport. + +What: /sys/bus/greybus/device/endoE:M:I:B:C/ap_cport_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -134,22 +172,23 @@ Description: The cport ID of the AP, to which cport of the module is connected. -What: /sys/bus/greybus/device/.../protocol_id +What: /sys/bus/greybus/device/endoE:M:I:B:C/protocol_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The protocol ID of a Greybus connection. + The protocol ID of a Greybus cport. -What: /sys/bus/greybus/device/.../state +What: /sys/bus/greybus/device/endoE:M:I:B:C/state Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - A bundle has a state that is managed by the userspace - Endo process. This file allows that Endo to signal - other Android HALs that the state of the bundle has - changed to a specific value. When written to, any - process watching the file will be woken up, and the new - value can be read. It's a "poor-man's IPC", yes, but - simplifies the Android userspace code immensely. + The current state of a Greybus connection. + + It will be one of the following values: + 0 - invalid + 1 - disabled + 2 - enabled + 3 - error + 4 - destroying -- cgit v1.2.3-59-g8ed1b From bb03ed920166a1fa2df77d5ef96ff1d749fa2967 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 18 Sep 2015 16:38:44 +0100 Subject: greybus: tracepoints: add tracepoint definitions This patch adds greybus_trace.h with the following trace definitions - trace_gb_message_send - trace_gb_message_recv_request - trace_gb_message_recv_response - trace_gb_message_cancel_incoming - trace_gb_message_cancel_outgoing Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_trace.h | 120 ++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 drivers/staging/greybus/greybus_trace.h diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h new file mode 100644 index 000000000000..abceb3ac7a13 --- /dev/null +++ b/drivers/staging/greybus/greybus_trace.h @@ -0,0 +1,120 @@ +/* + * Greybus driver and device API + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM greybus + +#if !defined(_TRACE_GREYBUS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_GREYBUS_H + +#include + +struct gb_message; + +DECLARE_EVENT_CLASS(gb_message, + + TP_PROTO(struct gb_message *message), + + TP_ARGS(message), + + TP_STRUCT__entry( + __string(name, dev_name(&message->operation->connection->dev)) + __field(u16, op_id) + __field(u16, intf_cport_id) + __field(u16, hd_cport_id) + __field(size_t, payload_size) + ), + + TP_fast_assign( + __assign_str(name, dev_name(&message->operation->connection->dev)) + __entry->op_id = message->operation->id; + __entry->intf_cport_id = + message->operation->connection->intf_cport_id; + __entry->hd_cport_id = + message->operation->connection->hd_cport_id; + __entry->payload_size = message->payload_size; + ), + + TP_printk("greybus:%s op=%04x if_id=%04x hd_id=%04x l=%zu", + __get_str(name), __entry->op_id, __entry->intf_cport_id, + __entry->hd_cport_id, __entry->payload_size) +); + +/* + * tracepoint name greybus:gb_message_send + * description send a greybus message + * location operation.c:gb_message_send + */ +DEFINE_EVENT(gb_message, gb_message_send, + + TP_PROTO(struct gb_message *message), + + TP_ARGS(message) +); + +/* + * tracepoint name greybus:gb_message_recv_request + * description receive a greybus request + * location operation.c:gb_connection_recv_request + */ +DEFINE_EVENT(gb_message, gb_message_recv_request, + + TP_PROTO(struct gb_message *message), + + TP_ARGS(message) +); + +/* + * tracepoint name greybus:gb_message_recv_response + * description receive a greybus response + * location operation.c:gb_connection_recv_response + */ +DEFINE_EVENT(gb_message, gb_message_recv_response, + + TP_PROTO(struct gb_message *message), + + TP_ARGS(message) +); + +/* + * tracepoint name greybus:gb_message_cancel_outgoing + * description cancel outgoing greybus request + * location operation.c:gb_message_cancel + */ +DEFINE_EVENT(gb_message, gb_message_cancel_outgoing, + + TP_PROTO(struct gb_message *message), + + TP_ARGS(message) +); + +/* + * tracepoint name greybus:gb_message_cancel_incoming + * description cancel incoming greybus request + * location operation.c:gb_message_cancel_incoming + */ +DEFINE_EVENT(gb_message, gb_message_cancel_incoming, + + TP_PROTO(struct gb_message *message), + + TP_ARGS(message) +); + +#endif /* _TRACE_GREYBUS_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +/* + * TRACE_INCLUDE_FILE is not needed if the filename and TRACE_SYSTEM are equal + */ +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE greybus_trace +#include + -- cgit v1.2.3-59-g8ed1b From 5c8ad599b942a9a027045e69fc76c84f23c0acf0 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 18 Sep 2015 16:38:45 +0100 Subject: greybus: operation, core: hook tracepoints into message opertions This patch hooks tracepoints for greybus messages - trace_gb_message_send - trace_gb_message_recv_request - trace_gb_message_recv_response - trace_gb_message_cancel_outgoing - trace_gb_message_cancel_incoming It provides standard tracepoints at /sys/kernel/debug/tracing/events/greybus/gb_message_send /sys/kernel/debug/tracing/events/greybus/gb_message_recv_response /sys/kernel/debug/tracing/events/greybus/gb_message_recv_request /sys/kernel/debug/tracing/events/greybus/gb_message_cancel_outgoing /sys/kernel/debug/tracing/events/greybus/gb_message_cancel_incoming Giving outputs like gb_message_recv_request: greybus:1-1.1:0 op=0001 if_id=0000 hd_id=0000 l=2 gb_message_send: greybus:1-1.1:0 op=0001 if_id=0000 hd_id=0000 l=2 Similarly perf events can be viewed with standard perf tools e.g. root@beaglebone:~# perf list 'greybus:*' greybus:gb_message_send [Tracepoint event] greybus:gb_message_recv_request [Tracepoint event] greybus:gb_message_recv_response [Tracepoint event] greybus:gb_message_cancel_outgoing [Tracepoint event] greybus:gb_message_cancel_incoming [Tracepoint event] Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 3 +++ drivers/staging/greybus/core.c | 3 +++ drivers/staging/greybus/operation.c | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 31b025da5e98..af280538425e 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -82,6 +82,9 @@ endif # add -Wall to try to catch everything we can. ccflags-y := -Wall +# needed for trace events +ccflags-y += -I$(src) + all: module module: diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 3c89cb368f6d..765e0db844f1 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -9,7 +9,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define CREATE_TRACE_POINTS #include "greybus.h" +#include "greybus_trace.h" /* Allow greybus to be disabled at boot if needed */ static bool nogreybus; @@ -347,6 +349,7 @@ static void __exit gb_exit(void) gb_operation_exit(); bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); + tracepoint_synchronize_unregister(); } module_exit(gb_exit); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index f9b71e79b4c4..d159831f1421 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -15,6 +15,7 @@ #include #include "greybus.h" +#include "greybus_trace.h" static struct kmem_cache *gb_operation_cache; static struct kmem_cache *gb_message_cache; @@ -197,6 +198,7 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp) { struct gb_connection *connection = message->operation->connection; + trace_gb_message_send(message); return connection->hd->driver->message_send(connection->hd, connection->hd_cport_id, message, @@ -834,6 +836,7 @@ static void gb_connection_recv_request(struct gb_connection *connection, gb_operation_put(operation); return; } + trace_gb_message_recv_request(operation->request); /* * The initial reference to the operation will be dropped when the @@ -872,6 +875,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, message->header->type, size, message_size); errno = -EMSGSIZE; } + trace_gb_message_recv_response(operation->response); /* We must ignore the payload if a bad status is returned */ if (errno) @@ -942,6 +946,7 @@ void gb_operation_cancel(struct gb_operation *operation, int errno) gb_message_cancel(operation->request); queue_work(gb_operation_completion_wq, &operation->work); } + trace_gb_message_cancel_outgoing(operation->request); atomic_inc(&operation->waiters); wait_event(gb_operation_cancellation_queue, @@ -968,6 +973,7 @@ void gb_operation_cancel_incoming(struct gb_operation *operation, int errno) if (!gb_operation_result_set(operation, errno)) gb_message_cancel(operation->response); } + trace_gb_message_cancel_incoming(operation->response); atomic_inc(&operation->waiters); wait_event(gb_operation_cancellation_queue, -- cgit v1.2.3-59-g8ed1b From 608f6619a295ce755061a046e967d8e32c2f4f5b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Sep 2015 09:42:52 -0700 Subject: greybus: es2: comment out unused functions The Qualcomm kernel builds with -Werror so the existing es2.c driver breaks the build due to unused static functions. As we are still hashing out exactly how to implement this logic at the moment, just comment out the functions to make the build be clean, no logic changes happen here at all. Reported-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index cd33aa7e6d42..3a5f93f8d661 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -136,6 +136,8 @@ static int cport_to_ep_pair(struct es1_ap_dev *es1, u16 cport_id) #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +/* Disable for now until we work all of this out to keep a warning-free build */ +#if 0 /* Test if the endpoints pair is already mapped to a cport */ static int ep_pair_in_use(struct es1_ap_dev *es1, int ep_pair) { @@ -191,6 +193,7 @@ static int unmap_cport(struct es1_ap_dev *es1, u16 cport_id) { return map_cport_to_ep(es1, cport_id, 0); } +#endif static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) { -- cgit v1.2.3-59-g8ed1b From d090446a915f109e65f3b2643269b134e1ddb4ef Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 22 Sep 2015 18:06:37 -0700 Subject: greybus: loopback: drop dependency on internal timestamps This patch drops tracking of internal latencies, it's possible to derive these times via kernel tracepoints and some user-space scripting. Since there's no other use of the internal timestamp than the loopback driver we remove the connection.c, connection.h, es1.c, es2.c and loopback.c inter-dependency in one go. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 30 ------------------------------ drivers/staging/greybus/connection.h | 4 ---- drivers/staging/greybus/es1.c | 1 - drivers/staging/greybus/es2.c | 1 - drivers/staging/greybus/loopback.c | 27 --------------------------- 5 files changed, 63 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f1541312fcc5..05a9b548b64a 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -67,29 +67,6 @@ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, } EXPORT_SYMBOL_GPL(greybus_data_rcvd); -void gb_connection_push_timestamp(struct gb_connection *connection) -{ - struct timeval tv; - - do_gettimeofday(&tv); - kfifo_in_locked(&connection->ts_kfifo, (void *)&tv, - sizeof(struct timeval), &connection->lock); -} -EXPORT_SYMBOL_GPL(gb_connection_push_timestamp); - -int gb_connection_pop_timestamp(struct gb_connection *connection, - struct timeval *tv) -{ - int retval; - - if (!kfifo_len(&connection->ts_kfifo)) - return -ENOMEM; - retval = kfifo_out_locked(&connection->ts_kfifo, (void *)tv, - sizeof(*tv), &connection->lock); - return retval; -} -EXPORT_SYMBOL_GPL(gb_connection_pop_timestamp); - static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -138,7 +115,6 @@ static void gb_connection_release(struct device *dev) struct gb_connection *connection = to_gb_connection(dev); destroy_workqueue(connection->wq); - kfifo_free(&connection->ts_kfifo); kfree(connection); } @@ -234,10 +210,6 @@ gb_connection_create_range(struct greybus_host_device *hd, if (!connection->wq) goto err_free_connection; - if (kfifo_alloc(&connection->ts_kfifo, GB_CONNECTION_TS_KFIFO_LEN, - GFP_KERNEL)) - goto err_destroy_wq; - connection->dev.parent = parent; connection->dev.bus = &greybus_bus_type; connection->dev.type = &greybus_connection_type; @@ -274,8 +246,6 @@ gb_connection_create_range(struct greybus_host_device *hd, return connection; -err_destroy_wq: - destroy_workqueue(connection->wq); err_free_connection: kfree(connection); err_remove_ida: diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index e3ae01dcfd82..27ec5975bbe0 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -43,7 +43,6 @@ struct gb_connection { struct list_head operations; struct workqueue_struct *wq; - struct kfifo ts_kfifo; atomic_t op_cycle; @@ -64,9 +63,6 @@ void gb_hd_connections_exit(struct greybus_host_device *hd); void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); -void gb_connection_push_timestamp(struct gb_connection *connection); -int gb_connection_pop_timestamp(struct gb_connection *connection, - struct timeval *tv); int gb_connection_bind_protocol(struct gb_connection *connection); diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index e44bf6cf895c..72928152879b 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -215,7 +215,6 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; - gb_connection_push_timestamp(message->operation->connection); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 3a5f93f8d661..754210137781 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -319,7 +319,6 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; - gb_connection_push_timestamp(message->operation->connection); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 512453621a9b..7ccf9a26dab3 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -57,7 +57,6 @@ struct gb_loopback_device { /* Overall stats */ struct gb_loopback_stats latency; - struct gb_loopback_stats latency_gb; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; }; @@ -76,14 +75,12 @@ struct gb_loopback { /* Per connection stats */ struct gb_loopback_stats latency; - struct gb_loopback_stats latency_gb; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; u32 lbid; u32 iteration_count; u64 elapsed_nsecs; - u64 elapsed_nsecs_gb; u32 error; }; @@ -266,9 +263,6 @@ static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, /* Time to send and receive one message */ gb_loopback_stats_attrs(latency, dev, false); gb_loopback_stats_attrs(latency, con, true); -/* Time to send and receive one message not including greybus */ -gb_loopback_stats_attrs(latency_gb, dev, false); -gb_loopback_stats_attrs(latency_gb, con, true); /* Number of requests sent per second on this cport */ gb_loopback_stats_attrs(requests_per_second, dev, false); gb_loopback_stats_attrs(requests_per_second, con, true); @@ -303,9 +297,6 @@ static struct attribute *loopback_dev_attrs[] = { &dev_attr_latency_min_dev.attr, &dev_attr_latency_max_dev.attr, &dev_attr_latency_avg_dev.attr, - &dev_attr_latency_gb_min_dev.attr, - &dev_attr_latency_gb_max_dev.attr, - &dev_attr_latency_gb_avg_dev.attr, &dev_attr_requests_per_second_min_dev.attr, &dev_attr_requests_per_second_max_dev.attr, &dev_attr_requests_per_second_avg_dev.attr, @@ -327,9 +318,6 @@ static struct attribute *loopback_con_attrs[] = { &dev_attr_latency_min_con.attr, &dev_attr_latency_max_con.attr, &dev_attr_latency_avg_con.attr, - &dev_attr_latency_gb_min_con.attr, - &dev_attr_latency_gb_max_con.attr, - &dev_attr_latency_gb_avg_con.attr, &dev_attr_requests_per_second_min_con.attr, &dev_attr_requests_per_second_max_con.attr, &dev_attr_requests_per_second_avg_con.attr, @@ -423,11 +411,6 @@ error: gb_loopback_push_latency_ts(gb, &ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); - /* Calculate non-greybus related component of the latency */ - gb_connection_pop_timestamp(gb->connection, &ts); - gb_connection_pop_timestamp(gb->connection, &te); - gb->elapsed_nsecs_gb = gb_loopback_calc_latency(&ts, &te); - return ret; } @@ -554,8 +537,6 @@ static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev) mutex_lock(&gb->mutex); memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb->latency_gb, &reset, - sizeof(struct gb_loopback_stats)); memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb->requests_per_second, &reset, @@ -567,7 +548,6 @@ static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev) memset(&gb_dev->start, 0, sizeof(struct timeval)); memset(&gb_dev->end, 0, sizeof(struct timeval)); memcpy(&gb_dev->latency, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb_dev->latency_gb, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb_dev->throughput, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb_dev->requests_per_second, &reset, sizeof(struct gb_loopback_stats)); @@ -679,7 +659,6 @@ error: static void gb_loopback_calculate_stats(struct gb_loopback *gb) { u32 lat; - u64 tmp; /* Express latency in terms of microseconds */ lat = gb_loopback_nsec_to_usec_latency(gb->elapsed_nsecs); @@ -694,12 +673,6 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) /* Log throughput and requests using latency as benchmark */ gb_loopback_throughput_update(gb, lat); gb_loopback_requests_update(gb, lat); - - /* Calculate the greybus related latency number in nanoseconds */ - tmp = gb->elapsed_nsecs - gb->elapsed_nsecs_gb; - lat = tmp; - gb_loopback_update_stats(&gb_dev.latency_gb, lat); - gb_loopback_update_stats(&gb->latency_gb, lat); } static int gb_loopback_fn(void *data) -- cgit v1.2.3-59-g8ed1b From 32b2b16737d40d0aea6e7a02740ce47acd4fdd2e Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 22 Sep 2015 18:06:38 -0700 Subject: greybus: tracepoints: add tracepoints for host_device tx/rx This patch adds new tracepoint declarations to greybus_trace.h to allow for capture of greybus host device tx and rx events. These two tracepoints allow an observer to see the point where the hardware interface driver performs the relevant read or write to receive or write the data it's been given from the higher layer greybus driver. The following two new tracepoints are declared: - trace_gb_host_device_send - trace_gb_host_device_recv Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 ++ drivers/staging/greybus/greybus_trace.h | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 765e0db844f1..ea23aaff9465 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -13,6 +13,9 @@ #include "greybus.h" #include "greybus_trace.h" +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_send); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_recv); + /* Allow greybus to be disabled at boot if needed */ static bool nogreybus; #ifdef MODULE diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index abceb3ac7a13..6c88d34d6f5d 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -15,6 +15,7 @@ #include struct gb_message; +struct greybus_host_device; DECLARE_EVENT_CLASS(gb_message, @@ -105,6 +106,55 @@ DEFINE_EVENT(gb_message, gb_message_cancel_incoming, TP_ARGS(message) ); +DECLARE_EVENT_CLASS(gb_host_device, + + TP_PROTO(struct greybus_host_device *hd, u16 intf_cport_id, + size_t payload_size), + + TP_ARGS(hd, intf_cport_id, payload_size), + + TP_STRUCT__entry( + __string(name, dev_name(hd->parent)) + __field(u16, intf_cport_id) + __field(size_t, payload_size) + ), + + TP_fast_assign( + __assign_str(name, dev_name(hd->parent)) + __entry->intf_cport_id = intf_cport_id; + __entry->payload_size = payload_size; + ), + + TP_printk("greybus:%s if_id=%04x l=%zu", __get_str(name), + __entry->intf_cport_id, __entry->payload_size) +); + +/* + * tracepoint name greybus:gb_host_device_send + * description tracepoint representing the point data are transmitted + * location es1.c/es2.c:message_send + */ +DEFINE_EVENT(gb_host_device, gb_host_device_send, + + TP_PROTO(struct greybus_host_device *hd, u16 intf_cport_id, + size_t payload_size), + + TP_ARGS(hd, intf_cport_id, payload_size) +); + +/* + * tracepoint name greybus:gb_host_device_recv + * description tracepoint representing the point data are received + * location es1.c/es2.c:cport_in_callback + */ +DEFINE_EVENT(gb_host_device, gb_host_device_recv, + + TP_PROTO(struct greybus_host_device *hd, u16 intf_cport_id, + size_t payload_size), + + TP_ARGS(hd, intf_cport_id, payload_size) +); + #endif /* _TRACE_GREYBUS_H */ /* This part must be outside protection */ -- cgit v1.2.3-59-g8ed1b From 6872c46129d00d0fcc0b32d73498fc86d6823137 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 22 Sep 2015 18:06:39 -0700 Subject: greybus: es1, es2: hook tracepoints to hardware send/recv operations This patch hooks tracepoints for the handoff point to/from hardware. With these tracepoints in place we can view the time between gb_message_send and usb_submit_urb and similarly we can view the time between cport_in_callback and gb_message_recv_response/gb_message_recv_request - trace_gb_host_device_send - trace_gb_host_device_recv It provides standard tracepoints at /sys/kernel/debug/tracing/events/greybus/gb_host_device_send /sys/kernel/debug/tracing/events/greybus/gb_host_device_recv Giving outputs like gb_host_device_recv: greybus:2-1 if_id=0000 l=10 gb_host_device_send: greybus:2-1 if_id=0000 l=10 Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 8 ++++++-- drivers/staging/greybus/es2.c | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 72928152879b..f2853ff4535d 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -16,6 +16,7 @@ #include "greybus.h" #include "kernel_ver.h" #include "connection.h" +#include "greybus_trace.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_GBUF_MSG_SIZE_MAX 2048 @@ -215,6 +216,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; + trace_gb_host_device_send(hd, cport_id, buffer_size); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); @@ -369,12 +371,14 @@ static void cport_in_callback(struct urb *urb) header = urb->transfer_buffer; cport_id = gb_message_cport_unpack(header); - if (cport_id_valid(hd, cport_id)) + if (cport_id_valid(hd, cport_id)) { + trace_gb_host_device_recv(hd, cport_id, urb->actual_length); greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); - else + } else { dev_err(dev, "%s: invalid cport id 0x%02x received\n", __func__, cport_id); + } exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 754210137781..8fee1162aea1 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -16,6 +16,7 @@ #include "greybus.h" #include "kernel_ver.h" #include "connection.h" +#include "greybus_trace.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_GBUF_MSG_SIZE_MAX 2048 @@ -319,6 +320,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; + trace_gb_host_device_send(hd, cport_id, buffer_size); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); @@ -478,12 +480,14 @@ static void cport_in_callback(struct urb *urb) header = urb->transfer_buffer; cport_id = gb_message_cport_unpack(header); - if (cport_id_valid(hd, cport_id)) + if (cport_id_valid(hd, cport_id)) { + trace_gb_host_device_recv(hd, cport_id, urb->actual_length); greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); - else + } else { dev_err(dev, "%s: invalid cport id 0x%02x received\n", __func__, cport_id); + } exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); -- cgit v1.2.3-59-g8ed1b From f01f7a9851f9c75467373ba7797394dba91825da Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 23 Sep 2015 09:31:31 -0700 Subject: greybus: loopback: move sysfs entries to /sys/bus/greybus/devices/endo0 Currently we have sysfs entries that are created when the first incoming connection is created as sub-nodes of the module associated with that connection e.g. /sys/bus/grebus/devices/endo0:X where X is the module identifier associated with the new connection. This is conceptually incorrect since the sysfs entries we create actually aren't bound to a module. Depending on the order connections are brought up we can also have a situation where /sys/bus/greybus/devices/endo0:X has high-level control sysfs data-points but /sys/bus/greybus/devices/endo0:Y does not. Rather than needlessly replicate data-points across each endo0:X, endo0:Y, endo0:Z sysfs directories it is more sensible to locate the entries in /sys/bus/greybus/devices/endo0. Signed-off-by: Bryan O'Donoghue Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 7ccf9a26dab3..3151876a717c 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -874,7 +874,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) struct gb_loopback *gb; int retval; char name[DEBUGFS_NAMELEN]; - struct kobject *kobj = &connection->bundle->intf->module->dev.kobj; + struct kobject *kobj = &connection->hd->endo->dev.kobj; gb = kzalloc(sizeof(*gb), GFP_KERNEL); if (!gb) @@ -963,7 +963,7 @@ out_sysfs: static void gb_loopback_connection_exit(struct gb_connection *connection) { struct gb_loopback *gb = connection->private; - struct kobject *kobj = &connection->bundle->intf->module->dev.kobj; + struct kobject *kobj = &connection->hd->endo->dev.kobj; if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); -- cgit v1.2.3-59-g8ed1b From 42d7f7e884fcce78301ca88da3434f0fcbf3fee5 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 23 Sep 2015 09:31:32 -0700 Subject: greybus: loopback: masked out threads should sleep If a thread is masked out it should not consume CPU cycles during a test. We set an arbitrary 100 millisecond sleep time for each masked out thread. Reasonably blunt instrument to ensure threads with nothing to do don't end up thrashing the acquisition/release of mutexes. Signed-off-by: Bryan O'Donoghue Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 3151876a717c..764ee83df082 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -693,8 +693,10 @@ static int gb_loopback_fn(void *data) break; mutex_lock(&gb_dev.mutex); - if (!gb_loopback_active(gb)) + if (!gb_loopback_active(gb)) { + ms_wait = 100; goto unlock_continue; + } if (gb_dev.iteration_max) { /* Determine overall lowest count */ low_count = gb->iteration_count; -- cgit v1.2.3-59-g8ed1b From 8d8d36da750ccba4c9ba9a293b3a338afd0b75b0 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 23 Sep 2015 09:31:33 -0700 Subject: greybus: loopback: drop redundant endo0 string from debugfs entry name dev_name() will return the endo0 component of the string on it's own, there's no need to include it in the snprintf() when construting the debugfs name. This fixes 'endo0' appearing more than once in the debugfs name - shamefully slipped through testing cb570c93783f ('greybus/loopback: use dev_name to populate sysfsname'). Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 764ee83df082..e728216a61cb 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -906,7 +906,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) } /* Create per-connection sysfs and debugfs data-points */ - snprintf(name, sizeof(name), "raw_latency_endo0:%s", + snprintf(name, sizeof(name), "raw_latency_%s", dev_name(&connection->dev)); gb->file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, gb, &gb_loopback_debugfs_latency_ops); -- cgit v1.2.3-59-g8ed1b From c83d4abe1dfd6aec45f594ad756fb5256263c5b8 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 23 Sep 2015 09:31:34 -0700 Subject: greybus: loopback: remove module specific identifier from debugfs name The datapoint we are using to return metrics across modules and cports shouldn't have a module identifier in it i.e. /sys/kernel/debug/gb_loopback/raw_latency_endo0 not /sys/kernel/debug/gb_loopback/raw_latency_endo0:X This patch removes the module_id used up to this point. Including module_id actually ends up making life harder in user-space so dropping it. Signed-off-by: Bryan O'Donoghue Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index e728216a61cb..5a6354eb4ea9 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -883,11 +883,10 @@ static int gb_loopback_connection_init(struct gb_connection *connection) return -ENOMEM; gb_loopback_reset_stats(&gb_dev); - /* If this is the first connection - create a module endo0:x entries */ + /* If this is the first connection - create a module endo0 entry */ mutex_lock(&gb_dev.mutex); if (!gb_dev.count) { - snprintf(name, sizeof(name), "raw_latency_endo0:%d", - connection->bundle->intf->module->module_id); + snprintf(name, sizeof(name), "raw_latency_endo0"); gb_dev.file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, &gb_dev, &gb_loopback_debugfs_dev_latency_ops); -- cgit v1.2.3-59-g8ed1b From c06307c3b476a40dfc853af34af76b591115e42c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 23 Sep 2015 16:48:07 -0700 Subject: greybus: svc: Use 'dev' instead '&op->connection->dev' We already have a variable to access '&op->connection->dev' directly, lets reuse it. Signed-off-by: Viresh Kumar Reviewed-by: Jean Pihet Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 35e2fb525dfa..056351fe9449 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -469,8 +469,7 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) u8 intf_id; if (request->payload_size < sizeof(*hot_unplug)) { - dev_err(&op->connection->dev, - "short hot unplug request received (%zu < %zu)\n", + dev_err(dev, "short hot unplug request received (%zu < %zu)\n", request->payload_size, sizeof(*hot_unplug)); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From bbaca7115167088e8177ab8cc7aafdcb8666dea9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 23 Sep 2015 16:48:08 -0700 Subject: greybus: svc: Allow consecutive hotplug events for the same module There are two cases where the AP may receive hotplug event for an existing interface, without first getting a hot-unplug request for it. - bootrom loading the firmware image and booting into that, which only generates a hotplug event. i.e. no hot-unplug event, as the module never went away. - Or the firmware on the module crashed and sent hotplug request again to the SVC, which got propagated to AP. Handle such cases by first removing the interface, with a clear print message shown to the user. And then following the normal hotplug sequence to add the interface. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 52 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 056351fe9449..0e029ce40379 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -329,6 +329,25 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } +static void svc_intf_remove(struct gb_connection *connection, + struct gb_interface *intf) +{ + struct greybus_host_device *hd = connection->hd; + struct gb_svc *svc = connection->private; + u8 intf_id = intf->interface_id; + u8 device_id; + + device_id = intf->device_id; + gb_interface_remove(hd, intf_id); + + /* + * Destroy the two-way route between the AP and the interface. + */ + gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id); + + ida_simple_remove(&svc->device_id_map, device_id); +} + /* * 'struct svc_hotplug' should be freed by svc_process_hotplug() before it * returns, irrespective of success or Failure in bringing up the module. @@ -351,6 +370,27 @@ static void svc_process_hotplug(struct work_struct *work) */ intf_id = hotplug->intf_id; + intf = gb_interface_find(hd, intf_id); + if (intf) { + /* + * We have received a hotplug request for an interface that + * already exists. + * + * This can happen in cases like: + * - bootrom loading the firmware image and booting into that, + * which only generates a hotplug event. i.e. no hot-unplug + * event. + * - Or the firmware on the module crashed and sent hotplug + * request again to the SVC, which got propagated to AP. + * + * Remove the interface and add it again, and let user know + * about this with a print message. + */ + dev_info(dev, "Removed interface (%hhu) to add it again\n", + intf_id); + svc_intf_remove(connection, intf); + } + intf = gb_interface_create(hd, intf_id); if (!intf) { dev_err(dev, "%s: Failed to create interface with id %hhu\n", @@ -463,8 +503,6 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload; struct greybus_host_device *hd = op->connection->hd; struct device *dev = &op->connection->dev; - struct gb_svc *svc = op->connection->private; - u8 device_id; struct gb_interface *intf; u8 intf_id; @@ -483,15 +521,7 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) return -EINVAL; } - device_id = intf->device_id; - gb_interface_remove(hd, intf_id); - - /* - * Destroy the two-way route between the AP and the interface. - */ - gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id); - - ida_simple_remove(&svc->device_id_map, device_id); + svc_intf_remove(op->connection, intf); return 0; } -- cgit v1.2.3-59-g8ed1b From 6bec5c78e59dd847c095f4a868baf14be871c8bb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 24 Sep 2015 14:40:29 -0700 Subject: greybus: svc: Read and clear module's boot status As per the module's boot sequence diagram, the AP needs to read/clear T_TstSrcIncrement attribute on hotplug (svc) events. Implement that. FIXME: This is module-hardware dependent and needs to be extended for every type of module we want to support. [ Based on work by Marti & Eli to clear the attribute with DME set] Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 +++ drivers/staging/greybus/svc.c | 40 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b3d0c57ea073..117f55e4463d 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -829,6 +829,10 @@ struct gb_svc_dme_peer_set_response { __le16 result_code; } __packed; +/* Attributes for peer get/set operations */ +#define DME_ATTR_SELECTOR_INDEX 0 +#define DME_ATTR_T_TST_SRC_INCREMENT 0x4083 + struct gb_svc_route_create_request { __u8 intf1_id; __u8 dev1_id; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 0e029ce40379..b59d76fcc405 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -176,6 +176,42 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, } EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set); +/* + * T_TstSrcIncrement is written by the module on ES2 as a stand-in for boot + * status attribute. AP needs to read and clear it, after reading a non-zero + * value from it. + * + * FIXME: This is module-hardware dependent and needs to be extended for every + * type of module we want to support. + */ +static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf) +{ + struct greybus_host_device *hd = intf->hd; + int ret; + u32 value; + + /* Read and clear boot status in T_TstSrcIncrement */ + ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, + DME_ATTR_T_TST_SRC_INCREMENT, + DME_ATTR_SELECTOR_INDEX, &value); + + if (ret) + return ret; + + /* + * A nonzero boot status indicates the module has finished + * booting. Clear it. + */ + if (!value) { + dev_err(&intf->dev, "Module not ready yet\n"); + return -ENODEV; + } + + return gb_svc_dme_peer_set(hd->svc, intf->interface_id, + DME_ATTR_T_TST_SRC_INCREMENT, + DME_ATTR_SELECTOR_INDEX, 0); +} + int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) @@ -398,6 +434,10 @@ static void svc_process_hotplug(struct work_struct *work) goto free_svc_hotplug; } + ret = gb_svc_read_and_clear_module_boot_status(intf); + if (ret) + goto destroy_interface; + intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); intf->unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); intf->ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id); -- cgit v1.2.3-59-g8ed1b From 80d1ede88ab081bd901bd5b3dc2461083915d1d1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 23 Sep 2015 16:48:10 -0700 Subject: greybus: interface: Pass interface pointer to gb_interface_remove() The callers already have a valid interface pointer and there is no need for gb_interface_remove() to find the interface again. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 29 +++++++++-------------------- drivers/staging/greybus/interface.h | 2 +- drivers/staging/greybus/svc.c | 4 ++-- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c38fb8b31c2b..b0253ce8e864 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -183,7 +183,7 @@ put_module: /* * Tear down a previously set up module. */ -static void interface_destroy(struct gb_interface *intf) +void gb_interface_remove(struct gb_interface *intf) { struct gb_module *module; struct gb_bundle *bundle; @@ -207,6 +207,14 @@ static void interface_destroy(struct gb_interface *intf) put_device(&module->dev); } +void gb_interfaces_remove(struct greybus_host_device *hd) +{ + struct gb_interface *intf, *temp; + + list_for_each_entry_safe(intf, temp, &hd->interfaces, links) + gb_interface_remove(intf); +} + /** * gb_interface_init * @@ -273,22 +281,3 @@ free_manifest: kfree(manifest); return ret; } - -void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id) -{ - struct gb_interface *intf = gb_interface_find(hd, interface_id); - - if (intf) - interface_destroy(intf); - else - dev_err(hd->parent, "interface id %d not found\n", - interface_id); -} - -void gb_interfaces_remove(struct greybus_host_device *hd) -{ - struct gb_interface *intf, *temp; - - list_for_each_entry_safe(intf, temp, &hd->interfaces, links) - interface_destroy(intf); -} diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 38210ad4e631..42b5d0156cdc 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -59,7 +59,7 @@ struct gb_interface *gb_interface_create(struct greybus_host_device *hd, u8 interface_id); void gb_interface_destroy(struct gb_interface *intf); int gb_interface_init(struct gb_interface *intf, u8 device_id); -void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id); +void gb_interface_remove(struct gb_interface *intf); void gb_interfaces_remove(struct greybus_host_device *hd); int gb_create_bundle_connection(struct gb_interface *intf, u8 class); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b59d76fcc405..da2ffd655122 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -374,7 +374,7 @@ static void svc_intf_remove(struct gb_connection *connection, u8 device_id; device_id = intf->device_id; - gb_interface_remove(hd, intf_id); + gb_interface_remove(intf); /* * Destroy the two-way route between the AP and the interface. @@ -497,7 +497,7 @@ svc_id_free: ida_put: ida_simple_remove(&svc->device_id_map, device_id); destroy_interface: - gb_interface_remove(hd, intf_id); + gb_interface_remove(intf); free_svc_hotplug: kfree(svc_hotplug); } -- cgit v1.2.3-59-g8ed1b From 78cd67777b71f38dec710dc26f48b685ef6cf120 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 23 Sep 2015 16:48:11 -0700 Subject: greybus: interface: gb_interface_create() isn't called for existing interfaces The callers are ensuring that another interface doesn't exist with the same interface id and so there is no need to check that from gb_interface_create() anymore. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index b0253ce8e864..64bf91ab0fb0 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -127,13 +127,6 @@ struct gb_interface *gb_interface_create(struct greybus_host_device *hd, struct gb_interface *intf; int retval; - intf = gb_interface_find(hd, interface_id); - if (intf) { - dev_err(hd->parent, "Duplicate interface with interface-id: %d will not be created\n", - interface_id); - return NULL; - } - module = gb_module_find(hd, endo_get_module_id(hd->endo, interface_id)); if (!module) return NULL; -- cgit v1.2.3-59-g8ed1b From 06986a2cbe7a3d20ed8a8651afd80f08948f3ee6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 23 Sep 2015 16:48:12 -0700 Subject: greybus: firmware: drop 'stage' from ready-to-boot request The spec says that it doesn't need it, so dropping it. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 3 +-- drivers/staging/greybus/greybus_protocols.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 5642bc33c1ce..884b7e2ad0a8 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -123,7 +123,7 @@ static int gb_firmware_ready_to_boot(struct gb_operation *op) struct gb_connection *connection = op->connection; struct gb_firmware_ready_to_boot_request *rtb_request = op->request->payload; struct device *dev = &connection->dev; - u8 stage, status; + u8 status; if (op->request->payload_size != sizeof(*rtb_request)) { dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", @@ -132,7 +132,6 @@ static int gb_firmware_ready_to_boot(struct gb_operation *op) return -EINVAL; } - stage = rtb_request->stage; status = rtb_request->status; /* Return error if the blob was invalid */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 117f55e4463d..4819cd0e229e 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -193,7 +193,6 @@ struct gb_firmware_get_firmware_response { /* Firmware protocol Ready to boot request */ struct gb_firmware_ready_to_boot_request { - __u8 stage; __u8 status; } __packed; /* Firmware protocol Ready to boot response has no payload */ -- cgit v1.2.3-59-g8ed1b From 336dfeaba1f6dd3b07571e0010707f716a1c6ee8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 23 Sep 2015 16:48:13 -0700 Subject: greybus: firmware: Don't send control-disconnected event for firmware protocol After downloading the firmware for the next boot stage, module's firmware (for current boot stage) jumps into it and the new firmware and sends hotplug request to SVC. On hotplug request from the SVC, the AP first removes the existing interface. At this time, there is no point sending disconnected event for the firmware protocol, for the firmware used in previous stage, as the new firmware wouldn't be aware about it. Set flags for firmware protocol to skip control-disconnected operations. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 884b7e2ad0a8..e04ed6bffcf5 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -210,5 +210,6 @@ static struct gb_protocol firmware_protocol = { .connection_init = gb_firmware_connection_init, .connection_exit = gb_firmware_connection_exit, .request_recv = gb_firmware_request_recv, + .flags = GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED, }; gb_builtin_protocol_driver(firmware_protocol); -- cgit v1.2.3-59-g8ed1b From 58c85123d9a1801af2558a0c925d90fe1599c27f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 26 Sep 2015 14:37:59 -0700 Subject: greybus: es1/2: fix use-after-free in completion callback Reset the hcpriv field before returning the message to greybus core in the OUT-URB completion callback. This fixes a use-after-free bug when sending responses to incoming requests as the final reference is then dropped when the message is returned. Reported-by: Michael Scott Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 8 ++++---- drivers/staging/greybus/es2.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index f2853ff4535d..2c56aaf55b42 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -397,16 +397,16 @@ static void cport_out_callback(struct urb *urb) gb_message_cport_clear(message->header); + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + message->hcpriv = NULL; + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + /* * Tell the submitter that the message send (attempt) is * complete, and report the status. */ greybus_message_sent(hd, message, status); - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - message->hcpriv = NULL; - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - free_urb(es1, urb); } diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 8fee1162aea1..22b67d2ff9df 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -506,16 +506,16 @@ static void cport_out_callback(struct urb *urb) gb_message_cport_clear(message->header); + spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + message->hcpriv = NULL; + spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + /* * Tell the submitter that the message send (attempt) is * complete, and report the status. */ greybus_message_sent(hd, message, status); - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - message->hcpriv = NULL; - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - free_urb(es1, urb); } -- cgit v1.2.3-59-g8ed1b From 6ab1ce4d542ea1b5ffa40321e021d95de47880ea Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 26 Sep 2015 17:59:15 -0700 Subject: greybus: operation: remove gb_operation_destroy Remove legacy interface to "destroy" operations, which is now just a wrapper for gb_operation_put. The old interface name hides the fact that all operations are refcounted and may live on even after having "destroyed" them. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 3 ++- drivers/staging/greybus/i2c.c | 3 ++- drivers/staging/greybus/loopback.c | 3 ++- drivers/staging/greybus/operation.c | 3 ++- drivers/staging/greybus/operation.h | 4 ---- drivers/staging/greybus/spi.c | 3 ++- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 0044b844c3e9..a2f0612076d5 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -92,7 +92,8 @@ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, ret = len; } - gb_operation_destroy(operation); + gb_operation_put(operation); + return ret; } diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 75b92d683036..bf6d11b36b83 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -218,7 +218,8 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, } else if (!gb_i2c_expected_transfer_error(ret)) { pr_err("transfer operation failed (%d)\n", ret); } - gb_operation_destroy(operation); + + gb_operation_put(operation); return ret; } diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 5a6354eb4ea9..5cbb8cb7fd9b 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -402,7 +402,8 @@ static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, response_size); } } - gb_operation_destroy(operation); + + gb_operation_put(operation); error: do_gettimeofday(&te); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d159831f1421..fae6ee9071df 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -1035,7 +1035,8 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, response_size); } } - gb_operation_destroy(operation); + + gb_operation_put(operation); return ret; } diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 16488d016943..0f44ec844106 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -131,10 +131,6 @@ struct gb_operation *gb_operation_create(struct gb_connection *connection, gfp_t gfp); void gb_operation_get(struct gb_operation *operation); void gb_operation_put(struct gb_operation *operation); -static inline void gb_operation_destroy(struct gb_operation *operation) -{ - gb_operation_put(operation); -} bool gb_operation_response_alloc(struct gb_operation *operation, size_t response_size, gfp_t gfp); diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index e5d216646b9c..393f28ad099b 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -156,7 +156,8 @@ static int gb_spi_transfer_one_message(struct spi_master *master, } else { pr_err("transfer operation failed (%d)\n", ret); } - gb_operation_destroy(operation); + + gb_operation_put(operation); msg->actual_length = len; msg->status = 0; -- cgit v1.2.3-59-g8ed1b From 4ee144170a74c990eea2ccec46ab438bc233d2d8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 29 Sep 2015 20:39:17 +0200 Subject: greybus: gpio: handle api change in generic_handle_irq_desc() generic_handle_irq_desc changed the api in the 4.2 kernel, so fix up the gpio driver to handle this properly to keep it working. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 6a04a1be573a..c41812ad7415 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -384,7 +384,11 @@ static int gb_gpio_request_recv(u8 type, struct gb_operation *op) } local_irq_disable(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) generic_handle_irq_desc(irq, desc); +#else + generic_handle_irq_desc(desc); +#endif local_irq_enable(); return 0; -- cgit v1.2.3-59-g8ed1b From 3c9426ad27840e2d1175e3b090cba98f539c10c6 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 30 Sep 2015 11:11:57 +0100 Subject: greybus: gpio: fix generic_handle_irq_desc api change The generic_handle_irq_desc api only have changed in 4.3.0, so check against the correct version, if not will break builds for 4.2.x. Fixes: e7895cfc476 ("gpio: handle api change in generic_handle_irq_desc()") Signed-off-by: Rui Miguel Silva Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index c41812ad7415..6c653ad2f578 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -384,7 +384,7 @@ static int gb_gpio_request_recv(u8 type, struct gb_operation *op) } local_irq_disable(); -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) generic_handle_irq_desc(irq, desc); #else generic_handle_irq_desc(desc); -- cgit v1.2.3-59-g8ed1b From adc401417eb87cfb6ce93e0f66eea19ee40fe5b8 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Fri, 2 Oct 2015 12:05:33 -0700 Subject: greybus: build: android: replace Linaro build specific logic with AOSP equivalents When using greybus build in a different Android setup, it was noted that several portions of this makefile rely on Linaro specific build items. Replace these with more generic build steps. - ANDROID_64 is only defined by Linaro build tasks, use TARGET_ARCH to establish ARCH= parameter for greybus build - KERNEL_TOOLS_PREFIX is only defined by Linaro build tasks. AOSP has a near equivalent variable: TARGET_TOOLS_PREFIX - build-greybus was dependant on subtask: android_kernel a task defined only by Linaro build tasks. Replace with a generic dependancy to the kernel binary located in $OUT (INSTALLED_KERNEL_TARGET). End result is the same: kernel must be built before greybus modules Signed-off-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Android.mk | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/Android.mk b/drivers/staging/greybus/Android.mk index 3357499ae0fd..47af2d34ce67 100644 --- a/drivers/staging/greybus/Android.mk +++ b/drivers/staging/greybus/Android.mk @@ -5,6 +5,7 @@ $(PRODUCT_OUT)/ramdisk.img: build-greybus endif GREYBUS_MODULE_OUT_PATH := $(PRODUCT_OUT)/root/lib/modules +GREYBUS_CC_PREFIX := $(shell cd `dirname $(TARGET_TOOLS_PREFIX)` && pwd)/$(shell basename $(TARGET_TOOLS_PREFIX)) include $(CLEAR_VARS) GREYBUS_SRC_PATH := $(ANDROID_BUILD_TOP)/external/greybus/ @@ -17,19 +18,14 @@ $(LOCAL_PATH)/$(LOCAL_SRC_FILES): build-greybus include $(BUILD_PREBUILT) KDIRARG := KERNELDIR="${ANDROID_PRODUCT_OUT}/obj/kernel" -ifneq ($(ANDROID_64),) - ARCHARG := ARCH=arm64 - FLAGARG := EXTRA_CFLAGS+=-fno-pic -else - ARCHARG := ARCH=arm - FLAGARG := EXTRA_CFLAGS+=-fno-pic -endif +ARCHARG := ARCH=$(TARGET_ARCH) +FLAGARG := EXTRA_CFLAGS+=-fno-pic ARGS := $(KDIRARG) $(ARCHARG) $(FLAGARG) -build-greybus: android_kernel +build-greybus: $(INSTALLED_KERNEL_TARGET) make clean -C $(GREYBUS_SRC_PATH) cd $(GREYBUS_SRC_PATH) &&\ - $(MAKE) -j$(MAKE_JOBS) CROSS_COMPILE=$(KERNEL_TOOLS_PREFIX) $(ARGS) + $(MAKE) -j$(MAKE_JOBS) CROSS_COMPILE=$(GREYBUS_CC_PREFIX) $(ARGS) mkdir -p $(GREYBUS_MODULE_OUT_PATH) ko=`find $(GREYBUS_SRC_PATH) -type f -name "*.ko"`;\ for i in $$ko; do $(KERNEL_TOOLCHAIN_PATH)strip --strip-unneeded $$i;\ -- cgit v1.2.3-59-g8ed1b From d3247a3fc7123f7001829a6ece731cf23e56a829 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Mon, 5 Oct 2015 12:33:04 -0700 Subject: greybus: build: android: fix strip module build step Currently, the greybus module .ko files are quite large and the following error was observed during Android build for each greybus module: strip: Unable to recognise the format of the input file `` Fix the strip build step by replacing the undefined KERNEL_TOOLCHAIN_PATH variable with the GREYBUS_CC_PREFIX variable. Also used as the CROSS_COMPILER value for the module make. Signed-off-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Android.mk b/drivers/staging/greybus/Android.mk index 47af2d34ce67..74948cd2c00b 100644 --- a/drivers/staging/greybus/Android.mk +++ b/drivers/staging/greybus/Android.mk @@ -28,5 +28,5 @@ build-greybus: $(INSTALLED_KERNEL_TARGET) $(MAKE) -j$(MAKE_JOBS) CROSS_COMPILE=$(GREYBUS_CC_PREFIX) $(ARGS) mkdir -p $(GREYBUS_MODULE_OUT_PATH) ko=`find $(GREYBUS_SRC_PATH) -type f -name "*.ko"`;\ - for i in $$ko; do $(KERNEL_TOOLCHAIN_PATH)strip --strip-unneeded $$i;\ + for i in $$ko; do $(GREYBUS_CC_PREFIX)strip --strip-unneeded $$i;\ mv $$i $(GREYBUS_MODULE_OUT_PATH)/; done; -- cgit v1.2.3-59-g8ed1b From 1575ef18aea40ab1f6915917901a80ca780188a6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 7 Oct 2015 15:40:24 -0400 Subject: greybus: svc: skip setting flags for boot over unipro We need to skip setting E2EFC and other flags to the SVC connection create request, for all cports, on an interface that need to boot over unipro, i.e. interfaces required to download firmware. This also adds a FIXME as we need to do it differently for ES3. Tested-by: Eli Sennesh Signed-off-by: Viresh Kumar Signed-off by: Eli Sennesh Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 7 +++++-- drivers/staging/greybus/greybus_protocols.h | 7 +++++++ drivers/staging/greybus/interface.h | 3 +++ drivers/staging/greybus/svc.c | 26 ++++++++++++++++++++++++-- drivers/staging/greybus/svc.h | 2 +- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 05a9b548b64a..6b56b3069fae 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -328,16 +328,19 @@ gb_connection_svc_connection_create(struct gb_connection *connection) { struct greybus_host_device *hd = connection->hd; struct gb_protocol *protocol = connection->protocol; + struct gb_interface *intf; int ret; if (protocol->flags & GB_PROTOCOL_SKIP_SVC_CONNECTION) return 0; + intf = connection->bundle->intf; ret = gb_svc_connection_create(hd->svc, hd->endo->ap_intf_id, connection->hd_cport_id, - connection->bundle->intf->interface_id, - connection->intf_cport_id); + intf->interface_id, + connection->intf_cport_id, + intf->boot_over_unipro); if (ret) { dev_err(&connection->dev, "failed to create svc connection: %d\n", ret); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 4819cd0e229e..dbf409fa16f3 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -832,6 +832,13 @@ struct gb_svc_dme_peer_set_response { #define DME_ATTR_SELECTOR_INDEX 0 #define DME_ATTR_T_TST_SRC_INCREMENT 0x4083 +/* Return value from TST_SRC_INCREMENT */ +#define DME_TSI_SPI_BOOT_STARTED 0x02 +#define DME_TSI_TRUSTED_SPI_BOOT_FINISHED 0x03 +#define DME_TSI_UNTRUSTED_SPI_BOOT_FINISHED 0x04 +#define DME_TSI_UNIPRO_BOOT_STARTED 0x06 +#define DME_TSI_FALLBACK_UNIPRO_BOOT_STARTED 0x09 + struct gb_svc_route_create_request { __u8 intf1_id; __u8 dev1_id; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 42b5d0156cdc..9bce94f680a4 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -36,6 +36,9 @@ struct gb_interface { struct gb_module *module; struct greybus_host_device *hd; + + /* The interface needs to boot over unipro */ + bool boot_over_unipro; }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index da2ffd655122..4b9eb383652c 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -207,6 +207,18 @@ static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf) return -ENODEV; } + /* + * Check if the module needs to boot from unipro. + * For ES2: We need to check lowest 8 bits of 'value'. + * For ES3: We need to check highest 8 bits out of 32 of 'value'. + * + * FIXME: Add code to find if we are on ES2 or ES3 to have separate + * checks. + */ + if (value == DME_TSI_UNIPRO_BOOT_STARTED || + value == DME_TSI_FALLBACK_UNIPRO_BOOT_STARTED) + intf->boot_over_unipro = true; + return gb_svc_dme_peer_set(hd->svc, intf->interface_id, DME_ATTR_T_TST_SRC_INCREMENT, DME_ATTR_SELECTOR_INDEX, 0); @@ -214,7 +226,8 @@ static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf) int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id) + u8 intf2_id, u16 cport2_id, + bool boot_over_unipro) { struct gb_svc_conn_create_request request; @@ -227,7 +240,16 @@ int gb_svc_connection_create(struct gb_svc *svc, * for now. */ request.tc = 0; - request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC; + + /* + * We need to skip setting E2EFC and other flags to the connection + * create request, for all cports, on an interface that need to boot + * over unipro, i.e. interfaces required to download firmware. + */ + if (boot_over_unipro) + request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_CSD_N; + else + request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC; return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 75518f8a199e..3357d317e9d9 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -14,7 +14,7 @@ struct gb_svc; int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id); + u8 intf2_id, u16 cport2_id, bool boot_over_unipro); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, -- cgit v1.2.3-59-g8ed1b From e0f875c336f792d4c2b0d09436426724ae14713e Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 8 Oct 2015 12:10:51 +0100 Subject: greybus: sdio: add field to get_caps response Frequency maximum and minimum are needed to complete the configuration of the controller. Add them to get_caps response operation. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 ++ drivers/staging/greybus/sdio.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index dbf409fa16f3..44a213377cbe 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1002,6 +1002,8 @@ struct gb_sdio_get_caps_response { __le32 ocr; __le16 max_blk_count; __le16 max_blk_size; + __le32 f_min; + __le32 f_max; } __packed; /* set ios request: response has no payload */ diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index bfa1181074d6..ff68956108bf 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -122,6 +122,10 @@ static int gb_sdio_get_caps(struct gb_sdio_host *host) mmc->ocr_avail_sd = mmc->ocr_avail; mmc->ocr_avail_mmc = mmc->ocr_avail; + /* get frequency range values */ + mmc->f_min = le32_to_cpu(response.f_min); + mmc->f_max = le32_to_cpu(response.f_max); + return 0; } -- cgit v1.2.3-59-g8ed1b From 6cac7dc3aaaef8161ac80c5980cec10427996abf Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 8 Oct 2015 12:10:52 +0100 Subject: greybus: sdio: fix card removable detection In kernel versions bellow 3.15, the mmc_card_is_removable helper function has an extra check used for a suspend/resume hack. This made the gd_sdio_process_event to behave badly handling the module card insert event in that versions. So, just test bit the flag that we need, instead of using the helper function. This way will work in all kernel versions. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index ff68956108bf..e5e3b77a2d15 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -144,7 +144,7 @@ static int _gb_sdio_process_events(struct gb_sdio_host *host, u8 event) u8 state_changed = 0; if (event & GB_SDIO_CARD_INSERTED) { - if (!mmc_card_is_removable(host->mmc)) + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) return 0; if (host->card_present) return 0; @@ -153,7 +153,7 @@ static int _gb_sdio_process_events(struct gb_sdio_host *host, u8 event) } if (event & GB_SDIO_CARD_REMOVED) { - if (!mmc_card_is_removable(host->mmc)) + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) return 0; if (!(host->card_present)) return 0; -- cgit v1.2.3-59-g8ed1b From dcb8d8d3e1f929e155cc262d9fbe060f94c02e35 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 8 Oct 2015 12:10:53 +0100 Subject: greybus: sdio: convert greybus ocr values to mmc ones It was missing the translation between the ocr vdd values of greybus to mmc_core values. This would make the detection of range voltage fail. mmc: host doesn't support card's voltages Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index e5e3b77a2d15..21a7b67e1417 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -87,12 +87,35 @@ static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) host->card_present = true; } +static u32 _gb_sdio_get_host_ocr(u32 ocr) +{ + return (((ocr & GB_SDIO_VDD_165_195) ? MMC_VDD_165_195 : 0) | + ((ocr & GB_SDIO_VDD_20_21) ? MMC_VDD_20_21 : 0) | + ((ocr & GB_SDIO_VDD_21_22) ? MMC_VDD_21_22 : 0) | + ((ocr & GB_SDIO_VDD_22_23) ? MMC_VDD_22_23 : 0) | + ((ocr & GB_SDIO_VDD_23_24) ? MMC_VDD_23_24 : 0) | + ((ocr & GB_SDIO_VDD_24_25) ? MMC_VDD_24_25 : 0) | + ((ocr & GB_SDIO_VDD_25_26) ? MMC_VDD_25_26 : 0) | + ((ocr & GB_SDIO_VDD_26_27) ? MMC_VDD_26_27 : 0) | + ((ocr & GB_SDIO_VDD_27_28) ? MMC_VDD_27_28 : 0) | + ((ocr & GB_SDIO_VDD_28_29) ? MMC_VDD_28_29 : 0) | + ((ocr & GB_SDIO_VDD_29_30) ? MMC_VDD_29_30 : 0) | + ((ocr & GB_SDIO_VDD_30_31) ? MMC_VDD_30_31 : 0) | + ((ocr & GB_SDIO_VDD_31_32) ? MMC_VDD_31_32 : 0) | + ((ocr & GB_SDIO_VDD_32_33) ? MMC_VDD_32_33 : 0) | + ((ocr & GB_SDIO_VDD_33_34) ? MMC_VDD_33_34 : 0) | + ((ocr & GB_SDIO_VDD_34_35) ? MMC_VDD_34_35 : 0) | + ((ocr & GB_SDIO_VDD_35_36) ? MMC_VDD_35_36 : 0) + ); +} + static int gb_sdio_get_caps(struct gb_sdio_host *host) { struct gb_sdio_get_caps_response response; struct mmc_host *mmc = host->mmc; u16 data_max; u32 blksz; + u32 ocr; u32 r; int ret; @@ -117,7 +140,8 @@ static int gb_sdio_get_caps(struct gb_sdio_host *host) host->data_max = data_max; /* get ocr supported values */ - mmc->ocr_avail = le32_to_cpu(response.ocr); + ocr = _gb_sdio_get_host_ocr(le32_to_cpu(response.ocr)); + mmc->ocr_avail = ocr; mmc->ocr_avail_sdio = mmc->ocr_avail; mmc->ocr_avail_sd = mmc->ocr_avail; mmc->ocr_avail_mmc = mmc->ocr_avail; -- cgit v1.2.3-59-g8ed1b From f064b872e7a0ee48d5d33278aeb30ffa7ee1c5f5 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 8 Oct 2015 12:10:54 +0100 Subject: greybus: sdio: clarify operator precedence When translating capabilities from greybus to mmc values add some parentheses to clarify operation precedence and avoid static analyses warnings. [sdio.c:81]: (style) Clarify calculation precedence for '&' and '?' Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 44 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 21a7b67e1417..aaca70e19e4a 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -55,30 +55,30 @@ static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) u32 caps = 0; u32 caps2 = 0; - caps = (r & GB_SDIO_CAP_NONREMOVABLE ? MMC_CAP_NONREMOVABLE : 0) | - (r & GB_SDIO_CAP_4_BIT_DATA ? MMC_CAP_4_BIT_DATA : 0) | - (r & GB_SDIO_CAP_8_BIT_DATA ? MMC_CAP_8_BIT_DATA : 0) | - (r & GB_SDIO_CAP_MMC_HS ? MMC_CAP_MMC_HIGHSPEED : 0) | - (r & GB_SDIO_CAP_SD_HS ? MMC_CAP_SD_HIGHSPEED : 0) | - (r & GB_SDIO_CAP_ERASE ? MMC_CAP_ERASE : 0) | - (r & GB_SDIO_CAP_1_2V_DDR ? MMC_CAP_1_2V_DDR : 0) | - (r & GB_SDIO_CAP_1_8V_DDR ? MMC_CAP_1_8V_DDR : 0) | - (r & GB_SDIO_CAP_POWER_OFF_CARD ? MMC_CAP_POWER_OFF_CARD : 0) | - (r & GB_SDIO_CAP_UHS_SDR12 ? MMC_CAP_UHS_SDR12 : 0) | - (r & GB_SDIO_CAP_UHS_SDR25 ? MMC_CAP_UHS_SDR25 : 0) | - (r & GB_SDIO_CAP_UHS_SDR50 ? MMC_CAP_UHS_SDR50 : 0) | - (r & GB_SDIO_CAP_UHS_SDR104 ? MMC_CAP_UHS_SDR104 : 0) | - (r & GB_SDIO_CAP_UHS_DDR50 ? MMC_CAP_UHS_DDR50 : 0) | - (r & GB_SDIO_CAP_DRIVER_TYPE_A ? MMC_CAP_DRIVER_TYPE_A : 0) | - (r & GB_SDIO_CAP_DRIVER_TYPE_C ? MMC_CAP_DRIVER_TYPE_C : 0) | - (r & GB_SDIO_CAP_DRIVER_TYPE_D ? MMC_CAP_DRIVER_TYPE_D : 0); - - caps2 = (r & GB_SDIO_CAP_HS200_1_2V ? MMC_CAP2_HS200_1_2V_SDR : 0) | + caps = ((r & GB_SDIO_CAP_NONREMOVABLE) ? MMC_CAP_NONREMOVABLE : 0) | + ((r & GB_SDIO_CAP_4_BIT_DATA) ? MMC_CAP_4_BIT_DATA : 0) | + ((r & GB_SDIO_CAP_8_BIT_DATA) ? MMC_CAP_8_BIT_DATA : 0) | + ((r & GB_SDIO_CAP_MMC_HS) ? MMC_CAP_MMC_HIGHSPEED : 0) | + ((r & GB_SDIO_CAP_SD_HS) ? MMC_CAP_SD_HIGHSPEED : 0) | + ((r & GB_SDIO_CAP_ERASE) ? MMC_CAP_ERASE : 0) | + ((r & GB_SDIO_CAP_1_2V_DDR) ? MMC_CAP_1_2V_DDR : 0) | + ((r & GB_SDIO_CAP_1_8V_DDR) ? MMC_CAP_1_8V_DDR : 0) | + ((r & GB_SDIO_CAP_POWER_OFF_CARD) ? MMC_CAP_POWER_OFF_CARD : 0) | + ((r & GB_SDIO_CAP_UHS_SDR12) ? MMC_CAP_UHS_SDR12 : 0) | + ((r & GB_SDIO_CAP_UHS_SDR25) ? MMC_CAP_UHS_SDR25 : 0) | + ((r & GB_SDIO_CAP_UHS_SDR50) ? MMC_CAP_UHS_SDR50 : 0) | + ((r & GB_SDIO_CAP_UHS_SDR104) ? MMC_CAP_UHS_SDR104 : 0) | + ((r & GB_SDIO_CAP_UHS_DDR50) ? MMC_CAP_UHS_DDR50 : 0) | + ((r & GB_SDIO_CAP_DRIVER_TYPE_A) ? MMC_CAP_DRIVER_TYPE_A : 0) | + ((r & GB_SDIO_CAP_DRIVER_TYPE_C) ? MMC_CAP_DRIVER_TYPE_C : 0) | + ((r & GB_SDIO_CAP_DRIVER_TYPE_D) ? MMC_CAP_DRIVER_TYPE_D : 0); + + caps2 = ((r & GB_SDIO_CAP_HS200_1_2V) ? MMC_CAP2_HS200_1_2V_SDR : 0) | #ifdef MMC_HS400_SUPPORTED - (r & GB_SDIO_CAP_HS400_1_2V ? MMC_CAP2_HS400_1_2V : 0) | - (r & GB_SDIO_CAP_HS400_1_8V ? MMC_CAP2_HS400_1_8V : 0) | + ((r & GB_SDIO_CAP_HS400_1_2V) ? MMC_CAP2_HS400_1_2V : 0) | + ((r & GB_SDIO_CAP_HS400_1_8V) ? MMC_CAP2_HS400_1_8V : 0) | #endif - (r & GB_SDIO_CAP_HS200_1_8V ? MMC_CAP2_HS200_1_8V_SDR : 0); + ((r & GB_SDIO_CAP_HS200_1_8V) ? MMC_CAP2_HS200_1_8V_SDR : 0); host->mmc->caps = caps | MMC_CAP_NEEDS_POLL; host->mmc->caps2 = caps2; -- cgit v1.2.3-59-g8ed1b From 82ee1e6cde3d9500f3dfc9cefc259b89e87a8865 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Tue, 13 Oct 2015 17:34:50 +0200 Subject: greybus: es2: implement cport reset control request Toshiba UniPro IP requires to reset the CPort that has been used in a previous connection. This commit implement a new control request in order to reset CPorts on an APBridgeA. Signed-off-by: Fabien Parent Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 22b67d2ff9df..f0770389df71 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -58,6 +58,9 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* vendor request to get the number of cports available */ #define REQUEST_CPORT_COUNT 0x04 +/* vendor request to reset a cport state */ +#define REQUEST_RESET_CPORT 0x05 + /* * @endpoint: bulk in endpoint for CPort data * @urb: array of urbs for the CPort in messages @@ -376,10 +379,44 @@ static void message_cancel(struct gb_message *message) usb_free_urb(urb); } +static int cport_reset(struct greybus_host_device *hd, u16 cport_id) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + int retval; + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_RESET_CPORT, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, cport_id, + NULL, 0, ES1_TIMEOUT); + if (retval < 0) { + dev_err(&udev->dev, "failed to reset cport %hu: %d\n", cport_id, + retval); + return retval; + } + + return 0; +} + +static int cport_enable(struct greybus_host_device *hd, u16 cport_id) +{ + int retval; + + if (cport_id != GB_SVC_CPORT_ID) { + retval = cport_reset(hd, cport_id); + if (retval) + return retval; + } + + return 0; +} + static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, + .cport_enable = cport_enable, }; /* Common function to report consistent warnings based on URB status */ -- cgit v1.2.3-59-g8ed1b From 67b81757dad8979e0cd85512fc0e5dfe80e2e96f Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Tue, 13 Oct 2015 17:34:51 +0200 Subject: greybus: connection: destroy connection on failing to bind it gb_connection_bind_protocol() returns proper error codes now and we should destroy the connection on failures. This change also fixes a NULL deref on hotplug when the control connection fails to initialize. Signed-off-by: Fabien Parent Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 6b56b3069fae..bd3f83b2ccb6 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -239,7 +239,14 @@ gb_connection_create_range(struct greybus_host_device *hd, spin_unlock_irq(&gb_connections_lock); - gb_connection_bind_protocol(connection); + retval = gb_connection_bind_protocol(connection); + if (retval) { + dev_err(&connection->dev, "failed to bind protocol: %d\n", + retval); + gb_connection_destroy(connection); + return NULL; + } + if (!connection->protocol) dev_warn(&connection->dev, "protocol 0x%02hhx handler not found\n", protocol_id); -- cgit v1.2.3-59-g8ed1b From 305a031e1ae006eb3cbac5c4c7fd441596f0fe45 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:20 +0200 Subject: greybus: es1/es2: clean up error messages Replace the remaining pr_err with dev_err, and drop redundant function prefixes. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 13 ++++++------- drivers/staging/greybus/es2.c | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 2c56aaf55b42..4d70e89aedd9 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -193,7 +193,8 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, * the target CPort id before filling it in. */ if (!cport_id_valid(hd, cport_id)) { - pr_err("invalid destination cport 0x%02x\n", cport_id); + dev_err(&udev->dev, "invalid destination cport 0x%02x\n", + cport_id); return -EINVAL; } @@ -219,7 +220,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, trace_gb_host_device_send(hd, cport_id, buffer_size); retval = usb_submit_urb(urb, gfp_mask); if (retval) { - pr_err("error %d submitting URB\n", retval); + dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); spin_lock_irqsave(&es1->cport_out_urb_lock, flags); message->hcpriv = NULL; @@ -363,7 +364,7 @@ static void cport_in_callback(struct urb *urb) } if (urb->actual_length < sizeof(*header)) { - dev_err(dev, "%s: short message received\n", __func__); + dev_err(dev, "short message received\n"); goto exit; } @@ -376,15 +377,13 @@ static void cport_in_callback(struct urb *urb) greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); } else { - dev_err(dev, "%s: invalid cport id 0x%02x received\n", - __func__, cport_id); + dev_err(dev, "invalid cport id 0x%02x received\n", cport_id); } exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - dev_err(dev, "%s: error %d in submitting urb.\n", - __func__, retval); + dev_err(dev, "failed to resubmit in-urb: %d\n", retval); } static void cport_out_callback(struct urb *urb) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index f0770389df71..5faf80a1d5dd 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -298,7 +298,8 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, * the target CPort id before filling it in. */ if (!cport_id_valid(hd, cport_id)) { - pr_err("invalid destination cport 0x%02x\n", cport_id); + dev_err(&udev->dev, "invalid destination cport 0x%02x\n", + cport_id); return -EINVAL; } @@ -326,7 +327,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, trace_gb_host_device_send(hd, cport_id, buffer_size); retval = usb_submit_urb(urb, gfp_mask); if (retval) { - pr_err("error %d submitting URB\n", retval); + dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); spin_lock_irqsave(&es1->cport_out_urb_lock, flags); message->hcpriv = NULL; @@ -509,7 +510,7 @@ static void cport_in_callback(struct urb *urb) } if (urb->actual_length < sizeof(*header)) { - dev_err(dev, "%s: short message received\n", __func__); + dev_err(dev, "short message received\n"); goto exit; } @@ -522,15 +523,13 @@ static void cport_in_callback(struct urb *urb) greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); } else { - dev_err(dev, "%s: invalid cport id 0x%02x received\n", - __func__, cport_id); + dev_err(dev, "invalid cport id 0x%02x received\n", cport_id); } exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - dev_err(dev, "%s: error %d in submitting urb.\n", - __func__, retval); + dev_err(dev, "failed to resubmit in-urb: %d\n", retval); } static void cport_out_callback(struct urb *urb) -- cgit v1.2.3-59-g8ed1b From aae5a44ff5a22c3c3398e68c89a4513c75725fd7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:21 +0200 Subject: greybus: connection: replace pr_err with dev_err Replace a couple of pr_err with more informative dev_err and clean up the messages somewhat. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index bd3f83b2ccb6..1158674f2201 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -178,7 +178,7 @@ gb_connection_create_range(struct greybus_host_device *hd, * about holding the connection lock. */ if (bundle && gb_connection_intf_find(bundle->intf, cport_id)) { - pr_err("duplicate interface cport id 0x%04hx\n", cport_id); + dev_err(parent, "cport 0x%04hx already connected\n", cport_id); return NULL; } @@ -223,8 +223,8 @@ gb_connection_create_range(struct greybus_host_device *hd, connection->hd_cport_id = CPORT_ID_BAD; put_device(&connection->dev); - pr_err("failed to add connection device for cport 0x%04hx\n", - cport_id); + dev_err(parent, "failed to register connection to cport %04hx: %d\n", + cport_id, retval); goto err_remove_ida; } -- cgit v1.2.3-59-g8ed1b From ff65e20ee534a5720bacd0b4058458e234ec51d6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:22 +0200 Subject: greybus: operation: drop OOM-response FIXME Drop FIXME about sending responses in OOM situations. If we fail to allocate an operation for an incoming request, we have bigger problems than to worry about sending a response. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index fae6ee9071df..969309e5d4d1 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -828,7 +828,7 @@ static void gb_connection_recv_request(struct gb_connection *connection, type, data, size); if (!operation) { dev_err(&connection->dev, "can't create operation\n"); - return; /* XXX Respond with pre-allocated ENOMEM */ + return; } ret = gb_operation_get_active(operation); -- cgit v1.2.3-59-g8ed1b From 0b1118a9efa9a00e449158bb7bb74249dc92c888 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:23 +0200 Subject: greybus: operation: clean up error messages Clean up and improve error messages. Demote a warning message to warning level. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 969309e5d4d1..56375c9e4d36 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -827,7 +827,7 @@ static void gb_connection_recv_request(struct gb_connection *connection, operation = gb_operation_create_incoming(connection, operation_id, type, data, size); if (!operation) { - dev_err(&connection->dev, "can't create operation\n"); + dev_err(&connection->dev, "can't create incoming operation\n"); return; } @@ -864,14 +864,16 @@ static void gb_connection_recv_response(struct gb_connection *connection, operation = gb_operation_find_outgoing(connection, operation_id); if (!operation) { - dev_err(&connection->dev, "operation not found\n"); + dev_err(&connection->dev, "unexpected response 0x%04hx received\n", + operation_id); return; } message = operation->response; message_size = sizeof(*message->header) + message->payload_size; if (!errno && size != message_size) { - dev_err(&connection->dev, "bad message (0x%02hhx) size (%zu != %zu)\n", + dev_err(&connection->dev, + "malformed response of type 0x%02hhx received (%zu != %zu)\n", message->header->type, size, message_size); errno = -EMSGSIZE; } @@ -903,13 +905,13 @@ void gb_connection_recv(struct gb_connection *connection, u16 operation_id; if (connection->state != GB_CONNECTION_STATE_ENABLED) { - dev_err(&connection->dev, "dropping %zu received bytes\n", + dev_warn(&connection->dev, "dropping %zu received bytes\n", size); return; } if (size < sizeof(header)) { - dev_err(&connection->dev, "message too small\n"); + dev_err(&connection->dev, "short message received\n"); return; } @@ -918,8 +920,8 @@ void gb_connection_recv(struct gb_connection *connection, msg_size = le16_to_cpu(header.size); if (size < msg_size) { dev_err(&connection->dev, - "incomplete message received for type 0x%02hhx: 0x%04x (%zu < %zu)\n", - header.type, le16_to_cpu(header.operation_id), size, + "incomplete message 0x%04hx of type 0x%02hhx received (%zu < %zu)\n", + le16_to_cpu(header.operation_id), header.type, size, msg_size); return; /* XXX Should still complete operation */ } @@ -1027,7 +1029,8 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { - dev_err(&connection->dev, "synchronous operation failed: 0x%02hhx (%d)\n", + dev_err(&connection->dev, + "synchronous operation of type 0x%02hhx failed: %d\n", type, ret); } else { if (response_size) { -- cgit v1.2.3-59-g8ed1b From 78033844daa64c83d91dca73eb1fbcae56c42fac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:24 +0200 Subject: greybus: protocol: warn on bad deregistration A protocol should be deregistered exactly once when the protocol module is being unloaded. This means that protocol deregister will never be called with active users as we take a module reference when looking up a protocol. Remove comment suggesting that we could one day forcefully stop a user of a protocol, and issue a big warning if a protocol is deregistered more than once or at some other time than during module unload (e.g. with active users). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 28 ++++++++-------------------- drivers/staging/greybus/protocol.h | 2 +- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index c93f96365667..140baa685469 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -102,36 +102,24 @@ EXPORT_SYMBOL_GPL(__gb_protocol_register); /* * De-register a previously registered protocol. - * - * XXX Currently this fails (and reports an error to the caller) if - * XXX the protocol is currently in use. We may want to forcefully - * XXX kill off a protocol and all its active users at some point. - * XXX But I think that's better handled by quiescing modules that - * XXX have users and having those users drop their reference. - * - * Returns true if successful, false otherwise. */ -int gb_protocol_deregister(struct gb_protocol *protocol) +void gb_protocol_deregister(struct gb_protocol *protocol) { - u8 protocol_count = 0; - if (!protocol) - return 0; + return; spin_lock_irq(&gb_protocols_lock); protocol = gb_protocol_find(protocol->id, protocol->major, protocol->minor); - if (protocol) { - protocol_count = protocol->count; - if (!protocol_count) - list_del(&protocol->links); + if (WARN_ON(!protocol || protocol->count)) { + spin_unlock_irq(&gb_protocols_lock); + return; } - spin_unlock_irq(&gb_protocols_lock); - if (protocol) - pr_info("Deregistered %s protocol.\n", protocol->name); + list_del(&protocol->links); + spin_unlock_irq(&gb_protocols_lock); - return protocol && !protocol_count; + pr_info("Deregistered %s protocol.\n", protocol->name); } EXPORT_SYMBOL_GPL(gb_protocol_deregister); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index d856885a89e1..2e0f4d667897 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -46,7 +46,7 @@ struct gb_protocol { }; int __gb_protocol_register(struct gb_protocol *protocol, struct module *module); -int gb_protocol_deregister(struct gb_protocol *protocol); +void gb_protocol_deregister(struct gb_protocol *protocol); #define gb_protocol_register(protocol) \ __gb_protocol_register(protocol, THIS_MODULE) -- cgit v1.2.3-59-g8ed1b From 519bf3c3b9c09619ca90f67b194714bb5f30d491 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:25 +0200 Subject: greybus: protocol: warn on protocol put errors Issue a warning if we fail to look up a protocol when dropping a reference. This should never happen as we hold a reference to the protocol module and protocols should only be deregistered at module unload. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 140baa685469..b9de152bade0 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -188,7 +188,6 @@ void gb_protocol_put(struct gb_protocol *protocol) u8 id; u8 major; u8 minor; - u8 protocol_count; id = protocol->id; major = protocol->major; @@ -196,16 +195,11 @@ void gb_protocol_put(struct gb_protocol *protocol) spin_lock_irq(&gb_protocols_lock); protocol = gb_protocol_find(id, major, minor); - if (protocol) { - protocol_count = protocol->count; - if (protocol_count) - protocol->count--; - module_put(protocol->owner); - } + if (WARN_ON(!protocol || !protocol->count)) + goto out; + + protocol->count--; + module_put(protocol->owner); +out: spin_unlock_irq(&gb_protocols_lock); - if (protocol) - WARN_ON(!protocol_count); - else - pr_err("protocol id %hhu version %hhu.%hhu not found\n", - id, major, minor); } -- cgit v1.2.3-59-g8ed1b From 4505c4d44a660866f2006eacafcb8c7d182ec182 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:26 +0200 Subject: greybus: protocol: make version debug message more informative Include function, protocol name and id when printing the version response debug message. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index b9de152bade0..41190e8dfccb 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -155,10 +155,11 @@ int gb_protocol_get_version(struct gb_connection *connection) { struct gb_protocol_version_request request; struct gb_protocol_version_response response; + struct gb_protocol *protocol = connection->protocol; int retval; - request.major = connection->protocol->major; - request.minor = connection->protocol->minor; + request.major = protocol->major; + request.minor = protocol->minor; retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, &request, sizeof(request), &response, @@ -176,8 +177,10 @@ int gb_protocol_get_version(struct gb_connection *connection) connection->module_major = response.major; connection->module_minor = response.minor; - dev_dbg(&connection->dev, "version_major = %u version_minor = %u\n", - response.major, response.minor); + + dev_dbg(&connection->dev, "%s - %s (0x%02hhx) v%hhu.%hhu\n", __func__, + protocol->name, protocol->id, + response.major, response.minor); return 0; } -- cgit v1.2.3-59-g8ed1b From d4efa68803667ab9d3ff6786462d2702b89e58de Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:27 +0200 Subject: greybus: vibrator: add missing protocol-register error handling Add missing error handling when registering the vibrator protocol during module init. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/vibrator.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index fd40cda565e4..2b8032653f55 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -171,7 +171,16 @@ static __init int protocol_init(void) if (retval) return retval; - return gb_protocol_register(&vibrator_protocol); + retval = gb_protocol_register(&vibrator_protocol); + if (retval) + goto err_class_unregister; + + return 0; + +err_class_unregister: + class_unregister(&vibrator_class); + + return retval; } module_init(protocol_init); -- cgit v1.2.3-59-g8ed1b From 9942fc8b14f21525622b85031b19c7ab5b16b339 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:28 +0200 Subject: greybus: protocol: make protocol-lookup error message more informative Make protocol lookup error more informative, by moving it to gb_connection_bind_protocol and using dev_err. Also make sure to use hex format for the protocol id as that is what is used everywhere else. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 7 ++++++- drivers/staging/greybus/protocol.c | 3 --- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1158674f2201..ca812f8a44f8 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -551,8 +551,13 @@ int gb_connection_bind_protocol(struct gb_connection *connection) protocol = gb_protocol_get(connection->protocol_id, connection->major, connection->minor); - if (!protocol) + if (!protocol) { + dev_warn(&connection->dev, + "protocol 0x%02hhx version %hhu.%hhu not found\n", + connection->protocol_id, + connection->major, connection->minor); return 0; + } connection->protocol = protocol; /* diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 41190e8dfccb..889cff2a10c2 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -144,9 +144,6 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) if (protocol) WARN_ON(protocol_count == U8_MAX); - else - pr_err("protocol id %hhu version %hhu.%hhu not found\n", - id, major, minor); return protocol; } -- cgit v1.2.3-59-g8ed1b From 7f0efa06795fea3f04578cf3016d393f9293cd4f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:29 +0200 Subject: greybus: connection: remove duplicate protocol lookup error message Remove duplicate protocol lookup error message, which has already been logged. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ca812f8a44f8..743ea67d022f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -247,10 +247,6 @@ gb_connection_create_range(struct greybus_host_device *hd, return NULL; } - if (!connection->protocol) - dev_warn(&connection->dev, - "protocol 0x%02hhx handler not found\n", protocol_id); - return connection; err_free_connection: -- cgit v1.2.3-59-g8ed1b From 650f38e3160aeaaa77c9e689850ba4c4f78ecb2e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 19:10:30 +0200 Subject: greybus: build: add pwm to config options to enable Add PWM to the kernel config options that shall be enabled. When PWM is not enabled connection init will fail with greybus endo0:3:4:9:9: failed to register PWM: -22 when using the default manifest. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index af280538425e..a62734093690 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -51,7 +51,7 @@ INSTALL_MOD_PATH ?= /.. PWD := $(shell pwd) # kernel config option that shall be enable -CONFIG_OPTIONS_ENABLE := SYSFS SPI USB SND_SOC MMC LEDS_CLASS +CONFIG_OPTIONS_ENABLE := PWM SYSFS SPI USB SND_SOC MMC LEDS_CLASS # kernel config option that shall be disable CONFIG_OPTIONS_DISABLE := -- cgit v1.2.3-59-g8ed1b From 0010245e2c6e431f24c1d80521866f963e148c03 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 13 Oct 2015 11:27:58 +0200 Subject: greybus: gpio: handle set_irq_flags api change The ARM-specific set_irq_flags helper has been removed in 4.3. Instead of doing conditional compilation on the kernel version to avoid build breakages, simply use the genirq interface directly. Suggested-by: Rob Herring Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 6c653ad2f578..5ac859e1c290 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -506,11 +506,7 @@ static int gb_gpio_irq_map(struct irq_domain *domain, unsigned int irq, irq_set_chip_data(irq, ggc); irq_set_chip_and_handler(irq, ggc->irqchip, ggc->irq_handler); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else irq_set_noprobe(irq); -#endif /* * No set-up of the hardware will happen if IRQ_TYPE_NONE * is passed as default type. @@ -523,9 +519,6 @@ static int gb_gpio_irq_map(struct irq_domain *domain, unsigned int irq, static void gb_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif irq_set_chip_and_handler(irq, NULL, NULL); irq_set_chip_data(irq, NULL); } -- cgit v1.2.3-59-g8ed1b From 04db334667b167b6f7c64e791defcb1bc61cfd3a Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 12 Oct 2015 15:45:05 +0100 Subject: greybus: loopback: add reserved fields to track firmware latencies The greybus specification has been extended to include two new reserved fields, which the implementation is using to track internal firmware latencies. This change adds the appropriate fields to the corresponding kernel header. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 44a213377cbe..fedb1b13d035 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -958,6 +958,8 @@ struct gb_loopback_transfer_request { struct gb_loopback_transfer_response { __le32 len; + __le32 reserved0; + __le32 reserved1; __u8 data[0]; } __packed; -- cgit v1.2.3-59-g8ed1b From 5ae2f55b3571408caa6ee0dc908dc60131da7cf6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:12:06 -0700 Subject: greybus: vibrator: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the vibrator driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/vibrator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 2b8032653f55..04737c80496d 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -111,8 +111,8 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) retval = vib->minor; goto error; } - dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib, - "vibrator%d", vib->minor); + dev = device_create(&vibrator_class, &connection->bundle->dev, + MKDEV(0, 0), vib, "vibrator%d", vib->minor); if (IS_ERR(dev)) { retval = -EINVAL; goto err_ida_remove; -- cgit v1.2.3-59-g8ed1b From 4f30bf3aef416ce6870a6da68e42957aeb4edb76 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:15:12 -0700 Subject: greybus: uart: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the uart driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/uart.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 9e8bf6f4b8d7..ec978a451b17 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -94,7 +94,7 @@ static int gb_uart_receive_data(struct gb_tty *gb_tty, count = tty_insert_flip_string_fixed_flag(port, receive_data->data, tty_flags, recv_data_size); if (count != recv_data_size) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "UART: RX 0x%08x bytes only wrote 0x%08x\n", recv_data_size, count); } @@ -121,7 +121,7 @@ static int gb_uart_request_recv(u8 type, struct gb_operation *op) gb_tty->ctrlin = serial_state->control; break; default: - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "unsupported unsolicited request: %02x\n", type); ret = -EINVAL; } @@ -175,7 +175,7 @@ static int send_break(struct gb_tty *gb_tty, u8 state) struct gb_uart_set_break_request request; if ((state != 0) && (state != 1)) { - dev_err(&gb_tty->connection->dev, + dev_err(&gb_tty->connection->bundle->dev, "invalid break state of %d\n", state); return -EINVAL; } @@ -626,7 +626,7 @@ static int gb_uart_connection_init(struct gb_connection *connection) minor = alloc_minor(gb_tty); if (minor < 0) { if (minor == -ENOSPC) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "no more free minor numbers\n"); retval = -ENODEV; goto error_minor; @@ -654,7 +654,7 @@ static int gb_uart_connection_init(struct gb_connection *connection) send_line_coding(gb_tty); tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, - &connection->dev); + &connection->bundle->dev); if (IS_ERR(tty_dev)) { retval = PTR_ERR(tty_dev); goto error; -- cgit v1.2.3-59-g8ed1b From 1cb9e38c8c17b16b2712aaf2ca4d3509f0fe9366 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:15:38 -0700 Subject: greybus: spi: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the spi driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/spi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 393f28ad099b..33e86f9c3182 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -57,7 +57,7 @@ gb_spi_operation_create(struct gb_connection *connection, /* Find number of transfers queued and tx/rx length in the message */ list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!xfer->tx_buf && !xfer->rx_buf) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "bufferless transfer, length %u\n", xfer->len); return NULL; } @@ -73,8 +73,8 @@ gb_spi_operation_create(struct gb_connection *connection, /* Too many transfers ? */ if (count > (u32)U16_MAX) { - dev_err(&connection->dev, "transfer count (%u) too big\n", - count); + dev_err(&connection->bundle->dev, + "transfer count (%u) too big\n", count); return NULL; } @@ -286,9 +286,9 @@ static int gb_spi_connection_init(struct gb_connection *connection) int ret; /* Allocate master with space for data */ - master = spi_alloc_master(&connection->dev, sizeof(*spi)); + master = spi_alloc_master(&connection->bundle->dev, sizeof(*spi)); if (!master) { - dev_err(&connection->dev, "cannot alloc SPI master\n"); + dev_err(&connection->bundle->dev, "cannot alloc SPI master\n"); return -ENOMEM; } -- cgit v1.2.3-59-g8ed1b From b2a637d72df790dbb8550627903c93ec8df4e9f7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:16:24 -0700 Subject: greybus: hid: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the hid driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/hid.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index a2f0612076d5..3ac9c1049f1d 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -86,7 +86,7 @@ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, ret = gb_operation_request_send_sync(operation); if (ret) { - dev_err(&operation->connection->dev, + dev_err(&operation->connection->bundle->dev, "failed to set report: %d\n", ret); } else { ret = len; @@ -104,7 +104,7 @@ static int gb_hid_irq_handler(u8 type, struct gb_operation *op) struct gb_hid_input_report_request *request = op->request->payload; if (type != GB_HID_TYPE_IRQ_EVENT) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "unsupported unsolicited request\n"); return -EINVAL; } @@ -403,7 +403,7 @@ static int gb_hid_init(struct gb_hid *ghid) hid->driver_data = ghid; hid->ll_driver = &gb_hid_ll_driver; - hid->dev.parent = &ghid->connection->dev; + hid->dev.parent = &ghid->connection->bundle->dev; #if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0) hid->hid_get_raw_report = gb_hid_get_raw_report; hid->hid_output_raw_report = gb_hid_output_raw_report; @@ -412,7 +412,8 @@ static int gb_hid_init(struct gb_hid *ghid) /* Set HID device's name */ snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", - dev_name(&ghid->connection->dev), hid->vendor, hid->product); + dev_name(&ghid->connection->bundle->dev), + hid->vendor, hid->product); return 0; } -- cgit v1.2.3-59-g8ed1b From 283dc01fb0ca32254bd5111c22cd9d9d4c4a0216 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:16:49 -0700 Subject: greybus: audio: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the audio driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/audio.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c index 6754c907cd6e..684229ccab6c 100644 --- a/drivers/staging/greybus/audio.c +++ b/drivers/staging/greybus/audio.c @@ -341,13 +341,14 @@ static int gb_i2s_mgmt_report_event_recv(u8 type, struct gb_operation *op) char *event_name; if (type != GB_I2S_MGMT_TYPE_REPORT_EVENT) { - dev_err(&connection->dev, "Invalid request type: %d\n", + dev_err(&connection->bundle->dev, "Invalid request type: %d\n", type); return -EINVAL; } if (op->request->payload_size < sizeof(*req)) { - dev_err(&connection->dev, "Short request received (%zu < %zu)\n", + dev_err(&connection->bundle->dev, + "Short request received (%zu < %zu)\n", op->request->payload_size, sizeof(*req)); return -EINVAL; } @@ -385,12 +386,12 @@ static int gb_i2s_mgmt_report_event_recv(u8 type, struct gb_operation *op) event_name = "DATA_LEN"; break; default: - dev_warn(&connection->dev, "Unknown I2S Event received: %d\n", - req->event); + dev_warn(&connection->bundle->dev, + "Unknown I2S Event received: %d\n", req->event); return -EINVAL; } - dev_warn(&connection->dev, "I2S Event received: %d - '%s'\n", + dev_warn(&connection->bundle->dev, "I2S Event received: %d - '%s'\n", req->event, event_name); return 0; -- cgit v1.2.3-59-g8ed1b From 0a72bd36df947855d7e546ac2e91972860f6894b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:17:29 -0700 Subject: greybus: firmware: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the firmware driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/firmware.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index e04ed6bffcf5..b16f13318be4 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -43,7 +43,8 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) intf->unipro_mfg_id, intf->unipro_prod_id, intf->ara_vend_id, intf->ara_prod_id, stage); - return request_firmware(&firmware->fw, firmware_name, &connection->dev); + return request_firmware(&firmware->fw, firmware_name, + &connection->bundle->dev); } static int gb_firmware_size_request(struct gb_operation *op) @@ -52,7 +53,7 @@ static int gb_firmware_size_request(struct gb_operation *op) struct gb_firmware *firmware = connection->private; struct gb_firmware_size_request *size_request = op->request->payload; struct gb_firmware_size_response *size_response; - struct device *dev = &connection->dev; + struct device *dev = &connection->bundle->dev; int ret; if (op->request->payload_size != sizeof(*size_request)) { @@ -88,7 +89,7 @@ static int gb_firmware_get_firmware(struct gb_operation *op) struct gb_firmware *firmware = connection->private; struct gb_firmware_get_firmware_request *firmware_request = op->request->payload; struct gb_firmware_get_firmware_response *firmware_response; - struct device *dev = &connection->dev; + struct device *dev = &connection->bundle->dev; unsigned int offset, size; if (op->request->payload_size != sizeof(*firmware_request)) { @@ -122,7 +123,7 @@ static int gb_firmware_ready_to_boot(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_firmware_ready_to_boot_request *rtb_request = op->request->payload; - struct device *dev = &connection->dev; + struct device *dev = &connection->bundle->dev; u8 status; if (op->request->payload_size != sizeof(*rtb_request)) { @@ -155,7 +156,7 @@ static int gb_firmware_request_recv(u8 type, struct gb_operation *op) case GB_FIRMWARE_TYPE_READY_TO_BOOT: return gb_firmware_ready_to_boot(op); default: - dev_err(&op->connection->dev, + dev_err(&op->connection->bundle->dev, "unsupported request: %hhu\n", type); return -EINVAL; } @@ -185,7 +186,7 @@ static int gb_firmware_connection_init(struct gb_connection *connection) ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_AP_READY, NULL, 0, NULL, 0); if (ret) - dev_err(&connection->dev, "Failed to send AP READY (%d)\n", ret); + dev_err(&connection->bundle->dev, "Failed to send AP READY (%d)\n", ret); return 0; } -- cgit v1.2.3-59-g8ed1b From c7eb46e459f2a05fa52656ae9d767aa498482ecf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:17:55 -0700 Subject: greybus: gpio: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the gpio driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/gpio.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 5ac859e1c290..5830dc9b87f3 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -353,7 +353,7 @@ static int gb_gpio_request_recv(u8 type, struct gb_operation *op) struct irq_desc *desc; if (type != GB_GPIO_TYPE_IRQ_EVENT) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "unsupported unsolicited request: %u\n", type); return -EINVAL; } @@ -648,7 +648,7 @@ static int gb_gpio_connection_init(struct gb_connection *connection) gpio = &ggc->chip; gpio->label = "greybus_gpio"; - gpio->dev = &connection->dev; + gpio->dev = &connection->bundle->dev; gpio->owner = THIS_MODULE; gpio->request = gb_gpio_request; @@ -666,15 +666,16 @@ static int gb_gpio_connection_init(struct gb_connection *connection) ret = gpiochip_add(gpio); if (ret) { - dev_err(&connection->dev, "failed to add gpio chip: %d\n", - ret); + dev_err(&connection->bundle->dev, + "failed to add gpio chip: %d\n", ret); goto err_free_lines; } ret = gb_gpio_irqchip_add(gpio, irqc, 0, handle_level_irq, IRQ_TYPE_NONE); if (ret) { - dev_err(&connection->dev, "failed to add irq chip: %d\n", ret); + dev_err(&connection->bundle->dev, + "failed to add irq chip: %d\n", ret); goto irqchip_err; } -- cgit v1.2.3-59-g8ed1b From c01e16e3cb120938db87af91248e91f3552a1b73 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:18:32 -0700 Subject: greybus: i2c: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the i2c driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index bf6d11b36b83..5a5af36570c8 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -118,7 +118,7 @@ gb_i2c_operation_create(struct gb_connection *connection, u32 i; if (msg_count > (u32)U16_MAX) { - dev_err(&connection->dev, "msg_count (%u) too big\n", + dev_err(&connection->bundle->dev, "msg_count (%u) too big\n", msg_count); return NULL; } @@ -312,7 +312,7 @@ static int gb_i2c_connection_init(struct gb_connection *connection) adapter->timeout = gb_i2c_dev->timeout_msec * HZ / 1000; adapter->retries = gb_i2c_dev->retries; - adapter->dev.parent = &connection->dev; + adapter->dev.parent = &connection->bundle->dev; snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); i2c_set_adapdata(adapter, gb_i2c_dev); -- cgit v1.2.3-59-g8ed1b From 8d5732f4be9411ddf9e6dc6b4d761f3b06999d94 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:18:50 -0700 Subject: greybus: pwm: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the pwm driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/pwm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index d91905f0f7b3..c3a3a9dfc6b4 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -196,7 +196,7 @@ static int gb_pwm_connection_init(struct gb_connection *connection) pwm = &pwmc->chip; - pwm->dev = &connection->dev; + pwm->dev = &connection->bundle->dev; pwm->ops = &gb_pwm_ops; pwm->base = -1; /* Allocate base dynamically */ pwm->npwm = pwmc->pwm_max + 1; @@ -204,7 +204,8 @@ static int gb_pwm_connection_init(struct gb_connection *connection) ret = pwmchip_add(pwm); if (ret) { - dev_err(&connection->dev, "failed to register PWM: %d\n", ret); + dev_err(&connection->bundle->dev, + "failed to register PWM: %d\n", ret); goto out_err; } -- cgit v1.2.3-59-g8ed1b From 5fd18b37c6a718d9daf8719ac81e4aa67ded4106 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:19:09 -0700 Subject: greybus: light : use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the light driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/light.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index e8ef7f5c37a0..664be973fa96 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -469,7 +469,7 @@ static void __gb_lights_channel_v4l2_config(struct led_flash_setting *channel_s, static int gb_lights_light_v4l2_register(struct gb_light *light) { struct gb_connection *connection = get_conn_from_light(light); - struct device *dev = &connection->dev; + struct device *dev = &connection->bundle->dev; struct v4l2_flash_config *sd_cfg; struct led_classdev_flash *fled; struct led_classdev_flash *iled = NULL; @@ -529,7 +529,7 @@ static int gb_lights_light_v4l2_register(struct gb_light *light) { struct gb_connection *connection = get_conn_from_light(light); - dev_err(&connection->dev, "no support for v4l2 subdevices\n"); + dev_err(&connection->bundle->dev, "no support for v4l2 subdevices\n"); return 0; } @@ -791,7 +791,7 @@ static int gb_lights_channel_flash_config(struct gb_channel *channel) { struct gb_connection *connection = get_conn_from_channel(channel); - dev_err(&connection->dev, "no support for flash devices\n"); + dev_err(&connection->bundle->dev, "no support for flash devices\n"); return 0; } @@ -1090,7 +1090,7 @@ static int gb_lights_setup(struct gb_lights *glights) for (i = 0; i < glights->lights_count; i++) { ret = gb_lights_light_config(glights, i); if (ret < 0) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "Fail to configure lights device\n"); goto out; } @@ -1104,6 +1104,7 @@ out: static int gb_lights_event_recv(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; + struct device *dev = &connection->bundle->dev; struct gb_lights *glights = connection->private; struct gb_message *request; struct gb_lights_event_request *payload; @@ -1112,16 +1113,14 @@ static int gb_lights_event_recv(u8 type, struct gb_operation *op) u8 event; if (type != GB_LIGHTS_TYPE_EVENT) { - dev_err(&connection->dev, - "Unsupported unsolicited event: %u\n", type); + dev_err(dev, "Unsupported unsolicited event: %u\n", type); return -EINVAL; } request = op->request; if (request->payload_size < sizeof(*payload)) { - dev_err(&connection->dev, - "Wrong event size received (%zu < %zu)\n", + dev_err(dev, "Wrong event size received (%zu < %zu)\n", request->payload_size, sizeof(*payload)); return -EINVAL; } @@ -1130,8 +1129,7 @@ static int gb_lights_event_recv(u8 type, struct gb_operation *op) light_id = payload->light_id; if (light_id >= glights->lights_count || !&glights->lights[light_id]) { - dev_err(&connection->dev, - "Event received for unconfigured light id: %d\n", + dev_err(dev, "Event received for unconfigured light id: %d\n", light_id); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 61c80575b90797bb3e74e2e2510eaa376bc8e4cb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:19:42 -0700 Subject: greybus: sdio: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the sdio driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/sdio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index aaca70e19e4a..b5e4af379e7b 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -208,7 +208,7 @@ static int gb_sdio_event_recv(u8 type, struct gb_operation *op) u8 event; if (type != GB_SDIO_TYPE_EVENT) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "unsupported unsolicited event: %u\n", type); return -EINVAL; } @@ -707,7 +707,7 @@ static int gb_sdio_connection_init(struct gb_connection *connection) size_t max_buffer; int ret = 0; - mmc = mmc_alloc_host(sizeof(*host), &connection->dev); + mmc = mmc_alloc_host(sizeof(*host), &connection->bundle->dev); if (!mmc) return -ENOMEM; @@ -739,7 +739,7 @@ static int gb_sdio_connection_init(struct gb_connection *connection) mutex_init(&host->lock); spin_lock_init(&host->xfer); host->mrq_workqueue = alloc_workqueue("mmc-%s", 0, 1, - dev_name(&connection->dev)); + dev_name(&connection->bundle->dev)); if (!host->mrq_workqueue) { ret = -ENOMEM; goto free_buffer; -- cgit v1.2.3-59-g8ed1b From dfdc6e12c8a3b1bc0c123abeb3679208b31a19f6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:20:00 -0700 Subject: greybus: usb: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the usb driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/usb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 2133d0d25f25..6647868b4960 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -57,7 +57,8 @@ static void hcd_stop(struct usb_hcd *hcd) ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, NULL, 0, NULL, 0); if (ret) - dev_err(&dev->connection->dev, "HCD stop failed '%d'\n", ret); + dev_err(&dev->connection->bundle->dev, + "HCD stop failed '%d'\n", ret); } static int hcd_start(struct usb_hcd *hcd) @@ -69,7 +70,8 @@ static int hcd_start(struct usb_hcd *hcd) ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, NULL, 0, NULL, 0); if (ret) { - dev_err(&dev->connection->dev, "HCD start failed '%d'\n", ret); + dev_err(&dev->connection->bundle->dev, + "HCD start failed '%d'\n", ret); return ret; } @@ -161,7 +163,7 @@ static struct hc_driver usb_gb_hc_driver = { static int gb_usb_connection_init(struct gb_connection *connection) { - struct device *dev = &connection->dev; + struct device *dev = &connection->bundle->dev; struct gb_usb_device *gb_usb_dev; struct usb_hcd *hcd; @@ -184,7 +186,7 @@ static int gb_usb_connection_init(struct gb_connection *connection) * Disable for now. */ if (1) { - dev_warn(&connection->dev, "USB protocol disabled\n"); + dev_warn(dev, "USB protocol disabled\n"); retval = -EPROTONOSUPPORT; goto err_put_hcd; } -- cgit v1.2.3-59-g8ed1b From c3aa6556659fe25b2fb27b86e65c6610b4066950 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:21:06 -0700 Subject: greybus: operation: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the operation code to use to use the bundle pointer instead of the connection pointer when printing out error messages. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/operation.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 56375c9e4d36..3e29d211fbcf 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -227,7 +227,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) if (protocol->request_recv) { status = protocol->request_recv(operation->type, operation); } else { - dev_err(&operation->connection->dev, + dev_err(&operation->connection->bundle->dev, "unexpected incoming request type 0x%02hhx\n", operation->type); @@ -236,7 +236,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) ret = gb_operation_response_send(operation, status); if (ret) { - dev_err(&operation->connection->dev, + dev_err(&operation->connection->bundle->dev, "failed to send response %d for type 0x%02hhx: %d\n", status, operation->type, ret); return; @@ -743,7 +743,7 @@ static int gb_operation_response_send(struct gb_operation *operation, /* Record the result */ if (!gb_operation_result_set(operation, errno)) { - dev_err(&connection->dev, "request result already set\n"); + dev_err(&connection->bundle->dev, "request result already set\n"); return -EIO; /* Shouldn't happen */ } @@ -795,7 +795,7 @@ void greybus_message_sent(struct greybus_host_device *hd, */ if (message == operation->response) { if (status) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "error sending response type 0x%02hhx: %d\n", operation->type, status); } @@ -827,7 +827,8 @@ static void gb_connection_recv_request(struct gb_connection *connection, operation = gb_operation_create_incoming(connection, operation_id, type, data, size); if (!operation) { - dev_err(&connection->dev, "can't create incoming operation\n"); + dev_err(&connection->bundle->dev, + "can't create incoming operation\n"); return; } @@ -864,15 +865,15 @@ static void gb_connection_recv_response(struct gb_connection *connection, operation = gb_operation_find_outgoing(connection, operation_id); if (!operation) { - dev_err(&connection->dev, "unexpected response 0x%04hx received\n", - operation_id); + dev_err(&connection->bundle->dev, + "unexpected response 0x%04hx received\n", operation_id); return; } message = operation->response; message_size = sizeof(*message->header) + message->payload_size; if (!errno && size != message_size) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "malformed response of type 0x%02hhx received (%zu != %zu)\n", message->header->type, size, message_size); errno = -EMSGSIZE; @@ -901,17 +902,17 @@ void gb_connection_recv(struct gb_connection *connection, void *data, size_t size) { struct gb_operation_msg_hdr header; + struct device *dev = &connection->bundle->dev; size_t msg_size; u16 operation_id; if (connection->state != GB_CONNECTION_STATE_ENABLED) { - dev_warn(&connection->dev, "dropping %zu received bytes\n", - size); + dev_warn(dev, "dropping %zu received bytes\n", size); return; } if (size < sizeof(header)) { - dev_err(&connection->dev, "short message received\n"); + dev_err(dev, "short message received\n"); return; } @@ -919,7 +920,7 @@ void gb_connection_recv(struct gb_connection *connection, memcpy(&header, data, sizeof(header)); msg_size = le16_to_cpu(header.size); if (size < msg_size) { - dev_err(&connection->dev, + dev_err(dev, "incomplete message 0x%04hx of type 0x%02hhx received (%zu < %zu)\n", le16_to_cpu(header.operation_id), header.type, size, msg_size); @@ -1029,7 +1030,7 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "synchronous operation of type 0x%02hhx failed: %d\n", type, ret); } else { -- cgit v1.2.3-59-g8ed1b From defa37ea9b9396ddc3ba9f556c1f89fec15c260b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:22:39 -0700 Subject: greybus: control: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the control code to use the bundle pointer instead of the connection pointer for printing out error messages. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index c092bebba77c..630b5b646667 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -22,7 +22,7 @@ int gb_control_get_manifest_size_operation(struct gb_interface *intf) ret = gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST_SIZE, NULL, 0, &response, sizeof(response)); if (ret) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "%s: Manifest size get operation failed (%d)\n", __func__, ret); return ret; @@ -74,7 +74,7 @@ static int gb_control_request_recv(u8 type, struct gb_operation *op) case GB_REQUEST_TYPE_PROTOCOL_VERSION: if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "%s: error allocating response\n", __func__); return -ENOMEM; } -- cgit v1.2.3-59-g8ed1b From 29a167ec87fb971931a033766e9e387d0fcabe7d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 14 Oct 2015 11:31:00 -0700 Subject: greybus: raw: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the raw driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/raw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index ce0f59d08e0d..fa5025dd17a3 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -176,8 +176,8 @@ static int gb_raw_connection_init(struct gb_connection *connection) if (retval) goto error_cdev; - raw->device = device_create(raw_class, &connection->dev, raw->dev, raw, - "gb!raw%d", minor); + raw->device = device_create(raw_class, &connection->bundle->dev, + raw->dev, raw, "gb!raw%d", minor); if (IS_ERR(raw->device)) { retval = PTR_ERR(raw->device); goto error_device; -- cgit v1.2.3-59-g8ed1b From 608ab2fe99396eff941256d7f0ae7b91438e6cc1 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 15 Oct 2015 16:10:41 +0100 Subject: greybus: es1,es2: add USB vendor command to timestamp As part of an effort to get deep inspection of latencies throughout the greybus network including HSIC, UniPro and firmware incurred latencies a new command to the APBridge to tag a known offset with timestamping data has been introduced. This patch adds that code to the es1 and es2 drivers. - latency_tag_enable - latency_tag_disable Respectively send the enable/disable command to APBridge on a per-CPort basis. This allows only specified cports to have timestamping data added by APBridge, leaving any CPort not specifically enabled untouched. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 54 +++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/es2.c | 54 +++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/greybus.h | 3 +++ 3 files changed, 111 insertions(+) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 4d70e89aedd9..ba025e782b0c 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -52,6 +52,10 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* vendor request APB1 log */ #define REQUEST_LOG 0x02 +/* vendor request to time the latency of messages on a given cport */ +#define REQUEST_LATENCY_TAG_EN 0x06 +#define REQUEST_LATENCY_TAG_DIS 0x07 + /** * es1_ap_dev - ES1 USB Bridge to AP structure * @usb_dev: pointer to the USB device we are. @@ -273,10 +277,60 @@ static void message_cancel(struct gb_message *message) usb_free_urb(urb); } +static int latency_tag_enable(struct greybus_host_device *hd, u16 cport_id) +{ + int retval; + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + + if (!cport_id_valid(hd, cport_id)) { + dev_err(&udev->dev, "invalid destination cport 0x%02x\n", + cport_id); + return -EINVAL; + } + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_LATENCY_TAG_EN, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, cport_id, 0, NULL, + 0, ES1_TIMEOUT); + + if (retval < 0) + dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n", + cport_id); + return retval; +} + +static int latency_tag_disable(struct greybus_host_device *hd, u16 cport_id) +{ + int retval; + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + + if (!cport_id_valid(hd, cport_id)) { + dev_err(&udev->dev, "invalid destination cport 0x%02x\n", + cport_id); + return -EINVAL; + } + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_LATENCY_TAG_DIS, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, cport_id, 0, NULL, + 0, ES1_TIMEOUT); + + if (retval < 0) + dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n", + cport_id); + return retval; +} + static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, + .latency_tag_enable = latency_tag_enable, + .latency_tag_disable = latency_tag_disable, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 5faf80a1d5dd..f947983cf56c 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -61,6 +61,10 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* vendor request to reset a cport state */ #define REQUEST_RESET_CPORT 0x05 +/* vendor request to time the latency of messages on a given cport */ +#define REQUEST_LATENCY_TAG_EN 0x06 +#define REQUEST_LATENCY_TAG_DIS 0x07 + /* * @endpoint: bulk in endpoint for CPort data * @urb: array of urbs for the CPort in messages @@ -413,11 +417,61 @@ static int cport_enable(struct greybus_host_device *hd, u16 cport_id) return 0; } +static int latency_tag_enable(struct greybus_host_device *hd, u16 cport_id) +{ + int retval; + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + + if (!cport_id_valid(hd, cport_id)) { + dev_err(&udev->dev, "invalid destination cport 0x%02x\n", + cport_id); + return -EINVAL; + } + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_LATENCY_TAG_EN, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, cport_id, 0, NULL, + 0, ES1_TIMEOUT); + + if (retval < 0) + dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n", + cport_id); + return retval; +} + +static int latency_tag_disable(struct greybus_host_device *hd, u16 cport_id) +{ + int retval; + struct es1_ap_dev *es1 = hd_to_es1(hd); + struct usb_device *udev = es1->usb_dev; + + if (!cport_id_valid(hd, cport_id)) { + dev_err(&udev->dev, "invalid destination cport 0x%02x\n", + cport_id); + return -EINVAL; + } + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_LATENCY_TAG_DIS, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, cport_id, 0, NULL, + 0, ES1_TIMEOUT); + + if (retval < 0) + dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n", + cport_id); + return retval; +} + static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, .cport_enable = cport_enable, + .latency_tag_enable = latency_tag_enable, + .latency_tag_disable = latency_tag_disable, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index e4e53c139ac4..ec4a73884b09 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -80,6 +80,9 @@ struct greybus_host_driver { int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); + int (*latency_tag_enable)(struct greybus_host_device *hd, u16 cport_id); + int (*latency_tag_disable)(struct greybus_host_device *hd, + u16 cport_id); }; struct greybus_host_device { -- cgit v1.2.3-59-g8ed1b From e7e2efc438ba721000ed4b747f159e41571570a7 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 15 Oct 2015 16:10:42 +0100 Subject: greybus: connection: add latency tag enable/disable callbacks This patch adds a layered wrapper around optional latency tag enable/disable commands to APBridge. When set APBridge and GPBridge will insert timing information into reserved fields in the loopback response header. Correspondingly when unset no timing information will be included in the response payload. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 32 ++++++++++++++++++++++++++++++++ drivers/staging/greybus/connection.h | 3 +++ 2 files changed, 35 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 743ea67d022f..3b731dab81f0 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -527,6 +527,38 @@ void gb_connection_destroy(struct gb_connection *connection) device_unregister(&connection->dev); } +void gb_connection_latency_tag_enable(struct gb_connection *connection) +{ + struct greybus_host_device *hd = connection->hd; + int ret; + + if (!hd->driver->latency_tag_enable) + return; + + ret = hd->driver->latency_tag_enable(hd, connection->hd_cport_id); + if (ret) { + dev_err(&connection->dev, + "failed to enable latency tag: %d\n", ret); + } +} +EXPORT_SYMBOL_GPL(gb_connection_latency_tag_enable); + +void gb_connection_latency_tag_disable(struct gb_connection *connection) +{ + struct greybus_host_device *hd = connection->hd; + int ret; + + if (!hd->driver->latency_tag_disable) + return; + + ret = hd->driver->latency_tag_disable(hd, connection->hd_cport_id); + if (ret) { + dev_err(&connection->dev, + "failed to disable latency tag: %d\n", ret); + } +} +EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable); + void gb_hd_connections_exit(struct greybus_host_device *hd) { struct gb_connection *connection; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 27ec5975bbe0..2eaf186ab60e 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -66,4 +66,7 @@ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, int gb_connection_bind_protocol(struct gb_connection *connection); +void gb_connection_latency_tag_enable(struct gb_connection *connection); +void gb_connection_latency_tag_disable(struct gb_connection *connection); + #endif /* __CONNECTION_H */ -- cgit v1.2.3-59-g8ed1b From fd58926ebef4a5afa238f35cc99c1f99653c40a4 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 15 Oct 2015 16:10:43 +0100 Subject: greybus: loopback: send command to APBridge to tag throughput A USB vendor command has been added to APBridge to allow for tagging of specific CPort identifiers with internal timing data, specifically geared towards capturing and understanding latencies in the UniPro fabric. This patch sends a command to APBridge for each known loopback CPort to tag data as appropriate. Subsequent patches will present this data to user-space for ongoing integration analysis. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 5cbb8cb7fd9b..95cba823ff90 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -23,6 +23,7 @@ #include #include "greybus.h" +#include "connection.h" #define NSEC_PER_DAY 86400000000000ULL @@ -938,6 +939,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) } gb_loopback_insert_id(gb); + gb_connection_latency_tag_enable(connection); gb_dev.count++; mutex_unlock(&gb_dev.mutex); return 0; @@ -975,6 +977,7 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) connection->private = NULL; kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); + gb_connection_latency_tag_disable(connection); gb_dev.count--; if (!gb_dev.count) { sysfs_remove_groups(kobj, loopback_dev_groups); -- cgit v1.2.3-59-g8ed1b From e6227ee64782e1cda88a70512ac2b0ae8fda2203 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 15 Oct 2015 16:10:44 +0100 Subject: greybus: loopback: add tracker variables to hold firmware timestamps This patch adds tracker variables to hold the incoming firmware derived timestamps where apbridge_latency_ts will contain the APBridge's view of the UniPro turn-around time and gpbridge_latency_ts will contain the GPBridge's view of it's own internal latency. Both values are reported in microseconds. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 95cba823ff90..d2a88af1f290 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -83,6 +83,8 @@ struct gb_loopback { u32 iteration_count; u64 elapsed_nsecs; u32 error; + u32 apbridge_latency_ts; + u32 gpbridge_latency_ts; }; #define GB_LOOPBACK_FIFO_DEFAULT 8192 -- cgit v1.2.3-59-g8ed1b From 1ec5843ee9889914be80a6763e58a79064716023 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 15 Oct 2015 16:10:45 +0100 Subject: greybus: loopback: capture and present firmware supplied latencies In order to provide deep inspection of the greybus/UniPro system instrumentation of 1. APBridge's view of UniPro latency 2. GPBridge's view of internal firmware-only latency have both been added and reported to the AP in the transfer loopback response header. When this data are present we latch and average it over the number of requested cycles, presenting it to user-space via sysfs. This patch adds the following sysfs entries for each loopback CPort - apbridge_unipro_latency_avg_con - apbridge_unipro_latency_max_con - apbridge_unipro_latency_min_con - gpbridge_firmware_latency_avg_con - gpbridge_firmware_latency_max_con - gpbridge_firmware_latency_min_con and the following sysfs entries representing the average values across all available CPorts - apbridge_unipro_latency_avg_dev - apbridge_unipro_latency_max_dev - apbridge_unipro_latency_min_dev - gpbridge_firmware_latency_avg_dev - gpbridge_firmware_latency_max_dev - gpbridge_firmware_latency_min_dev Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index d2a88af1f290..1ac86c15e974 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -60,6 +60,8 @@ struct gb_loopback_device { struct gb_loopback_stats latency; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; + struct gb_loopback_stats apbridge_unipro_latency; + struct gb_loopback_stats gpbridge_firmware_latency; }; static struct gb_loopback_device gb_dev; @@ -78,6 +80,8 @@ struct gb_loopback { struct gb_loopback_stats latency; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; + struct gb_loopback_stats apbridge_unipro_latency; + struct gb_loopback_stats gpbridge_firmware_latency; u32 lbid; u32 iteration_count; @@ -272,6 +276,12 @@ gb_loopback_stats_attrs(requests_per_second, con, true); /* Quantity of data sent and received on this cport */ gb_loopback_stats_attrs(throughput, dev, false); gb_loopback_stats_attrs(throughput, con, true); +/* Latency across the UniPro link from APBridge's perspective */ +gb_loopback_stats_attrs(apbridge_unipro_latency, dev, false); +gb_loopback_stats_attrs(apbridge_unipro_latency, con, true); +/* Firmware induced overhead in the GPBridge */ +gb_loopback_stats_attrs(gpbridge_firmware_latency, dev, false); +gb_loopback_stats_attrs(gpbridge_firmware_latency, con, true); /* Number of errors encountered during loop */ gb_loopback_ro_attr(error, dev, false); gb_loopback_ro_attr(error, con, true); @@ -306,6 +316,12 @@ static struct attribute *loopback_dev_attrs[] = { &dev_attr_throughput_min_dev.attr, &dev_attr_throughput_max_dev.attr, &dev_attr_throughput_avg_dev.attr, + &dev_attr_apbridge_unipro_latency_min_dev.attr, + &dev_attr_apbridge_unipro_latency_max_dev.attr, + &dev_attr_apbridge_unipro_latency_avg_dev.attr, + &dev_attr_gpbridge_firmware_latency_min_dev.attr, + &dev_attr_gpbridge_firmware_latency_max_dev.attr, + &dev_attr_gpbridge_firmware_latency_avg_dev.attr, &dev_attr_type.attr, &dev_attr_size.attr, &dev_attr_ms_wait.attr, @@ -327,6 +343,12 @@ static struct attribute *loopback_con_attrs[] = { &dev_attr_throughput_min_con.attr, &dev_attr_throughput_max_con.attr, &dev_attr_throughput_avg_con.attr, + &dev_attr_apbridge_unipro_latency_min_con.attr, + &dev_attr_apbridge_unipro_latency_max_con.attr, + &dev_attr_apbridge_unipro_latency_avg_con.attr, + &dev_attr_gpbridge_firmware_latency_min_con.attr, + &dev_attr_gpbridge_firmware_latency_max_con.attr, + &dev_attr_gpbridge_firmware_latency_avg_con.attr, &dev_attr_error_con.attr, NULL, }; @@ -463,6 +485,8 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) dev_err(&gb->connection->dev, "Loopback Data doesn't match\n"); retval = -EREMOTEIO; } + gb->apbridge_latency_ts = (u32)__le32_to_cpu(response->reserved0); + gb->gpbridge_latency_ts = (u32)__le32_to_cpu(response->reserved1); gb_error: kfree(request); @@ -545,6 +569,10 @@ static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev) sizeof(struct gb_loopback_stats)); memcpy(&gb->requests_per_second, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb->apbridge_unipro_latency, &reset, + sizeof(struct gb_loopback_stats)); + memcpy(&gb->gpbridge_firmware_latency, &reset, + sizeof(struct gb_loopback_stats)); mutex_unlock(&gb->mutex); } @@ -555,6 +583,10 @@ static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev) memcpy(&gb_dev->throughput, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb_dev->requests_per_second, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb_dev->apbridge_unipro_latency, &reset, + sizeof(struct gb_loopback_stats)); + memcpy(&gb_dev->gpbridge_firmware_latency, &reset, + sizeof(struct gb_loopback_stats)); } static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) @@ -677,6 +709,16 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) /* Log throughput and requests using latency as benchmark */ gb_loopback_throughput_update(gb, lat); gb_loopback_requests_update(gb, lat); + + /* Log the firmware supplied latency values */ + gb_loopback_update_stats(&gb_dev.apbridge_unipro_latency, + gb->apbridge_latency_ts); + gb_loopback_update_stats(&gb->apbridge_unipro_latency, + gb->apbridge_latency_ts); + gb_loopback_update_stats(&gb_dev.gpbridge_firmware_latency, + gb->gpbridge_latency_ts); + gb_loopback_update_stats(&gb->gpbridge_firmware_latency, + gb->gpbridge_latency_ts); } static int gb_loopback_fn(void *data) @@ -736,6 +778,8 @@ static int gb_loopback_fn(void *data) goto sleep; } /* Else operations to perform */ + gb->apbridge_latency_ts = 0; + gb->gpbridge_latency_ts = 0; if (type == GB_LOOPBACK_TYPE_PING) error = gb_loopback_ping(gb); else if (type == GB_LOOPBACK_TYPE_TRANSFER) -- cgit v1.2.3-59-g8ed1b From 10ed1938767ec90a264b73b337ec17a0a674847a Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 15 Oct 2015 23:56:51 +0100 Subject: greybus: sdio: send data block details at command request If SDIO request include data to be transfer send details (data blocks and block size) in command request, as it seems some controllers need this info prior to set the registers correctly. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 ++ drivers/staging/greybus/sdio.c | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index fedb1b13d035..02ba40f1666c 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1088,6 +1088,8 @@ struct gb_sdio_command_request { #define GB_SDIO_CMD_BCR 0x03 __le32 cmd_arg; + __le16 data_blocks; + __le16 data_blksz; } __packed; struct gb_sdio_command_response { diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index b5e4af379e7b..201cfe534459 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -373,8 +373,9 @@ out: static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd) { - struct gb_sdio_command_request request; + struct gb_sdio_command_request request = {0}; struct gb_sdio_command_response response; + struct mmc_data *data = host->mrq->data; u8 cmd_flags; u8 cmd_type; int i; @@ -427,6 +428,11 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd) request.cmd_flags = cmd_flags; request.cmd_type = cmd_type; request.cmd_arg = cpu_to_le32(cmd->arg); + /* some controllers need to know at command time data details */ + if (data) { + request.data_blocks = cpu_to_le16(data->blocks); + request.data_blksz = cpu_to_le16(data->blksz); + } ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_COMMAND, &request, sizeof(request), &response, -- cgit v1.2.3-59-g8ed1b From b6789ee55b99fbc26aa16e4f485397e86498bf51 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 15 Oct 2015 23:56:52 +0100 Subject: greybus: sdio: some cleanups in command function Some cleanups in gb_sdio_command function, ret does not need to be initialize and mrq is already pointing to request, no need to get it from host. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 201cfe534459..beb35747a4f8 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -379,7 +379,7 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd) u8 cmd_flags; u8 cmd_type; int i; - int ret = 0; + int ret; switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: @@ -488,7 +488,7 @@ static void gb_sdio_mrq_work(struct work_struct *work) goto done; if (mrq->data) { - ret = gb_sdio_transfer(host, host->mrq->data); + ret = gb_sdio_transfer(host, mrq->data); if (ret < 0) goto done; } -- cgit v1.2.3-59-g8ed1b From b750fa3370485356b5cddda7c0f7fc9bea056fa8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Oct 2015 16:56:38 -0700 Subject: greybus: connection: remove 'struct device' from 'struct gb_connection' We don't want this in the driver core, as nothing will be binding to it, that's the job of a bundle. So remove the struct device and use a kref to handle reference counting instead. Note, I don't think we really need a kref, but it keeps the lifetime the same as before, and is the simplest change for now. In the future it might be easier to just attach all connections to the bundle and clean them up when the bundle is removed. Also remove the cport sysfs documentation as it's no longer relevant. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold --- .../greybus/Documentation/sysfs-bus-greybus | 37 ---------- drivers/staging/greybus/connection.c | 83 +++------------------- drivers/staging/greybus/connection.h | 3 +- 3 files changed, 9 insertions(+), 114 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index b2e699ccdcd5..22a0c7f5a9b3 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -155,40 +155,3 @@ Description: process watching the file will be woken up, and the new value can be read. It's a "poor-man's IPC", yes, but simplifies the Android userspace code immensely. - -What: /sys/bus/greybus/device/endoE:M:I:B:C -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - A cport C within bundle B, C is replaced by a 2-byte - number representing the cport. - -What: /sys/bus/greybus/device/endoE:M:I:B:C/ap_cport_id -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The cport ID of the AP, to which cport of the module is - connected. - -What: /sys/bus/greybus/device/endoE:M:I:B:C/protocol_id -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The protocol ID of a Greybus cport. - -What: /sys/bus/greybus/device/endoE:M:I:B:C/state -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The current state of a Greybus connection. - - It will be one of the following values: - 0 - invalid - 1 - disabled - 2 - enabled - 3 - error - 4 - destroying diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3b731dab81f0..abd857703917 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -67,63 +67,18 @@ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, } EXPORT_SYMBOL_GPL(greybus_data_rcvd); -static ssize_t state_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct gb_connection *connection = to_gb_connection(dev); - enum gb_connection_state state; - - spin_lock_irq(&connection->lock); - state = connection->state; - spin_unlock_irq(&connection->lock); - - return sprintf(buf, "%d\n", state); -} -static DEVICE_ATTR_RO(state); - -static ssize_t -protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct gb_connection *connection = to_gb_connection(dev); - - if (connection->protocol) - return sprintf(buf, "%d\n", connection->protocol->id); - else - return -EINVAL; -} -static DEVICE_ATTR_RO(protocol_id); - -static ssize_t -ap_cport_id_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct gb_connection *connection = to_gb_connection(dev); - return sprintf(buf, "%hu\n", connection->hd_cport_id); -} -static DEVICE_ATTR_RO(ap_cport_id); - -static struct attribute *connection_attrs[] = { - &dev_attr_state.attr, - &dev_attr_protocol_id.attr, - &dev_attr_ap_cport_id.attr, - NULL, -}; - -ATTRIBUTE_GROUPS(connection); +static DEFINE_MUTEX(connection_mutex); -static void gb_connection_release(struct device *dev) +static void gb_connection_kref_release(struct kref *kref) { - struct gb_connection *connection = to_gb_connection(dev); + struct gb_connection *connection; + connection = container_of(kref, struct gb_connection, kref); destroy_workqueue(connection->wq); kfree(connection); + mutex_unlock(&connection_mutex); } -struct device_type greybus_connection_type = { - .name = "greybus_connection", - .release = gb_connection_release, -}; - - int svc_update_connection(struct gb_interface *intf, struct gb_connection *connection) { @@ -133,13 +88,7 @@ int svc_update_connection(struct gb_interface *intf, if (!bundle) return -EINVAL; - device_del(&connection->dev); connection->bundle = bundle; - connection->dev.parent = &bundle->dev; - dev_set_name(&connection->dev, "%s:%d", dev_name(&bundle->dev), - GB_SVC_CPORT_ID); - - WARN_ON(device_add(&connection->dev)); spin_lock_irq(&gb_connections_lock); list_add(&connection->bundle_links, &bundle->connections); @@ -210,24 +159,7 @@ gb_connection_create_range(struct greybus_host_device *hd, if (!connection->wq) goto err_free_connection; - connection->dev.parent = parent; - connection->dev.bus = &greybus_bus_type; - connection->dev.type = &greybus_connection_type; - connection->dev.groups = connection_groups; - device_initialize(&connection->dev); - dev_set_name(&connection->dev, "%s:%d", - dev_name(parent), cport_id); - - retval = device_add(&connection->dev); - if (retval) { - connection->hd_cport_id = CPORT_ID_BAD; - put_device(&connection->dev); - - dev_err(parent, "failed to register connection to cport %04hx: %d\n", - cport_id, retval); - - goto err_remove_ida; - } + kref_init(&connection->kref); spin_lock_irq(&gb_connections_lock); list_add(&connection->hd_links, &hd->connections); @@ -524,7 +456,8 @@ void gb_connection_destroy(struct gb_connection *connection) ida_simple_remove(id_map, connection->hd_cport_id); connection->hd_cport_id = CPORT_ID_BAD; - device_unregister(&connection->dev); + kref_put_mutex(&connection->kref, gb_connection_kref_release, + &connection_mutex); } void gb_connection_latency_tag_enable(struct gb_connection *connection) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 2eaf186ab60e..af425a2dc5d2 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -24,7 +24,7 @@ enum gb_connection_state { struct gb_connection { struct greybus_host_device *hd; struct gb_bundle *bundle; - struct device dev; + struct kref kref; u16 hd_cport_id; u16 intf_cport_id; @@ -48,7 +48,6 @@ struct gb_connection { void *private; }; -#define to_gb_connection(d) container_of(d, struct gb_connection, dev) int svc_update_connection(struct gb_interface *intf, struct gb_connection *connection); -- cgit v1.2.3-59-g8ed1b From 30482c1e63f3530f38f9364b90e35f1e9a990a4c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Oct 2015 16:56:23 -0700 Subject: greybus: connection: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the connection code to use the bundle pointer instead of the connection pointer for some error messages. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold --- drivers/staging/greybus/connection.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index abd857703917..0be78fe8f50f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -173,8 +173,8 @@ gb_connection_create_range(struct greybus_host_device *hd, retval = gb_connection_bind_protocol(connection); if (retval) { - dev_err(&connection->dev, "failed to bind protocol: %d\n", - retval); + dev_err(&bundle->dev, "%d: failed to bind protocol: %d\n", + cport_id, retval); gb_connection_destroy(connection); return NULL; } @@ -199,8 +199,8 @@ static int gb_connection_hd_cport_enable(struct gb_connection *connection) ret = hd->driver->cport_enable(hd, connection->hd_cport_id); if (ret) { - dev_err(&connection->dev, - "failed to enable host cport: %d\n", ret); + dev_err(&connection->bundle->dev, + "failed to enable host cport: %d\n", ret); return ret; } @@ -277,8 +277,8 @@ gb_connection_svc_connection_create(struct gb_connection *connection) connection->intf_cport_id, intf->boot_over_unipro); if (ret) { - dev_err(&connection->dev, - "failed to create svc connection: %d\n", ret); + dev_err(&connection->bundle->dev, + "failed to create svc connection: %d\n", ret); return ret; } @@ -313,8 +313,8 @@ static int gb_connection_control_connected(struct gb_connection *connection) ret = gb_control_connected_operation(control, cport_id); if (ret) { - dev_err(&connection->dev, - "failed to connect cport: %d\n", ret); + dev_err(&connection->bundle->dev, + "failed to connect cport: %d\n", ret); return ret; } @@ -337,8 +337,8 @@ gb_connection_control_disconnected(struct gb_connection *connection) ret = gb_control_disconnected_operation(control, cport_id); if (ret) { - dev_warn(&connection->dev, - "failed to disconnect cport: %d\n", ret); + dev_warn(&connection->bundle->dev, + "failed to disconnect cport: %d\n", ret); } } @@ -356,8 +356,8 @@ static int gb_connection_protocol_get_version(struct gb_connection *connection) ret = gb_protocol_get_version(connection); if (ret) { - dev_err(&connection->dev, - "failed to get protocol version: %d\n", ret); + dev_err(&connection->bundle->dev, + "failed to get protocol version: %d\n", ret); return ret; } @@ -470,7 +470,7 @@ void gb_connection_latency_tag_enable(struct gb_connection *connection) ret = hd->driver->latency_tag_enable(hd, connection->hd_cport_id); if (ret) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "failed to enable latency tag: %d\n", ret); } } @@ -486,7 +486,7 @@ void gb_connection_latency_tag_disable(struct gb_connection *connection) ret = hd->driver->latency_tag_disable(hd, connection->hd_cport_id); if (ret) { - dev_err(&connection->dev, + dev_err(&connection->bundle->dev, "failed to disable latency tag: %d\n", ret); } } @@ -513,7 +513,7 @@ int gb_connection_bind_protocol(struct gb_connection *connection) connection->major, connection->minor); if (!protocol) { - dev_warn(&connection->dev, + dev_warn(&connection->bundle->dev, "protocol 0x%02hhx version %hhu.%hhu not found\n", connection->protocol_id, connection->major, connection->minor); -- cgit v1.2.3-59-g8ed1b From d9a9ea1b88190a31957489377fd3fee246888373 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Oct 2015 16:55:46 -0700 Subject: greybus: loopback: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the loopback driver to use the bundle pointer instead of the connection pointer. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold --- drivers/staging/greybus/loopback.c | 78 +++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 1ac86c15e974..e1b1be0687a8 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -107,11 +107,11 @@ static ssize_t field##_##pfx##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_connection *connection; \ + struct gb_bundle *bundle; \ struct gb_loopback *gb; \ if (conn) { \ - connection = to_gb_connection(dev); \ - gb = connection->private; \ + bundle = to_gb_bundle(dev); \ + gb = bundle->private; \ return sprintf(buf, "%u\n", gb->field); \ } else { \ return sprintf(buf, "%u\n", gb_dev.field); \ @@ -124,11 +124,11 @@ static ssize_t name##_##field##_##pfx##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_connection *connection; \ + struct gb_bundle *bundle; \ struct gb_loopback *gb; \ if (conn) { \ - connection = to_gb_connection(dev); \ - gb = connection->private; \ + bundle = to_gb_bundle(dev); \ + gb = bundle->private; \ return sprintf(buf, "%"#type"\n", gb->name.field); \ } else { \ return sprintf(buf, "%"#type"\n", gb_dev.name.field); \ @@ -142,13 +142,13 @@ static ssize_t name##_avg_##pfx##_show(struct device *dev, \ char *buf) \ { \ struct gb_loopback_stats *stats; \ - struct gb_connection *connection; \ + struct gb_bundle *bundle; \ struct gb_loopback *gb; \ u64 avg; \ u32 count, rem; \ if (conn) { \ - connection = to_gb_connection(dev); \ - gb = connection->private; \ + bundle = to_gb_bundle(dev); \ + gb = bundle->private; \ stats = &gb->name; \ } else { \ stats = &gb_dev.name; \ @@ -170,8 +170,8 @@ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_connection *connection = to_gb_connection(dev); \ - struct gb_loopback *gb = connection->private; \ + struct gb_bundle *bundle = to_gb_bundle(dev); \ + struct gb_loopback *gb = bundle->private; \ return sprintf(buf, "%"#type"\n", gb->field); \ } \ static ssize_t field##_store(struct device *dev, \ @@ -180,13 +180,13 @@ static ssize_t field##_store(struct device *dev, \ size_t len) \ { \ int ret; \ - struct gb_connection *connection = to_gb_connection(dev); \ + struct gb_bundle *bundle = to_gb_bundle(dev); \ mutex_lock(&gb_dev.mutex); \ ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ len = -EINVAL; \ else \ - gb_loopback_check_attr(connection); \ + gb_loopback_check_attr(&gb_dev, bundle); \ mutex_unlock(&gb_dev.mutex); \ return len; \ } \ @@ -214,13 +214,13 @@ static ssize_t field##_store(struct device *dev, \ size_t len) \ { \ int ret; \ - struct gb_connection *connection = to_gb_connection(dev); \ + struct gb_bundle *bundle = to_gb_bundle(dev); \ mutex_lock(&gb_dev.mutex); \ ret = sscanf(buf, "%"#type, &gb_dev.field); \ if (ret != 1) \ len = -EINVAL; \ else \ - gb_loopback_check_attr(&gb_dev, connection); \ + gb_loopback_check_attr(&gb_dev, bundle); \ mutex_unlock(&gb_dev.mutex); \ return len; \ } \ @@ -228,7 +228,7 @@ static DEVICE_ATTR_RW(field) static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev); static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, - struct gb_connection *connection) + struct gb_bundle *bundle) { struct gb_loopback *gb; @@ -244,7 +244,7 @@ static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, gb->iteration_count = 0; gb->error = 0; if (kfifo_depth < gb_dev->iteration_max) { - dev_warn(&connection->dev, + dev_warn(&bundle->dev, "cannot log bytes %u kfifo_depth %u\n", gb_dev->iteration_max, kfifo_depth); } @@ -414,14 +414,14 @@ static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, ret = gb_operation_request_send_sync(operation); if (ret) { - dev_err(&gb->connection->dev, + dev_err(&gb->connection->bundle->dev, "synchronous operation failed: %d\n", ret); } else { if (response_size == operation->response->payload_size) { memcpy(response, operation->response->payload, response_size); } else { - dev_err(&gb->connection->dev, + dev_err(&gb->connection->bundle->dev, "response size %zu expected %d\n", operation->response->payload_size, response_size); @@ -482,7 +482,8 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) goto gb_error; if (memcmp(request->data, response->data, len)) { - dev_err(&gb->connection->dev, "Loopback Data doesn't match\n"); + dev_err(&gb->connection->bundle->dev, + "Loopback Data doesn't match\n"); retval = -EREMOTEIO; } gb->apbridge_latency_ts = (u32)__le32_to_cpu(response->reserved0); @@ -506,21 +507,20 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) struct gb_connection *connection = operation->connection; struct gb_loopback_transfer_request *request; struct gb_loopback_transfer_response *response; + struct device *dev = &connection->bundle->dev; size_t len; /* By convention, the AP initiates the version operation */ switch (type) { case GB_REQUEST_TYPE_PROTOCOL_VERSION: - dev_err(&connection->dev, - "module-initiated version operation\n"); + dev_err(dev, "module-initiated version operation\n"); return -EINVAL; case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_SINK: return 0; case GB_LOOPBACK_TYPE_TRANSFER: if (operation->request->payload_size < sizeof(*request)) { - dev_err(&connection->dev, - "transfer request too small (%zu < %zu)\n", + dev_err(dev, "transfer request too small (%zu < %zu)\n", operation->request->payload_size, sizeof(*request)); return -EINVAL; /* -EMSGSIZE */ @@ -528,8 +528,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) request = operation->request->payload; len = le32_to_cpu(request->len); if (len > gb_dev.size_max) { - dev_err(&connection->dev, - "transfer request too large (%zu > %zu)\n", + dev_err(dev, "transfer request too large (%zu > %zu)\n", len, gb_dev.size_max); return -EINVAL; } @@ -537,8 +536,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) if (len) { if (!gb_operation_response_alloc(operation, len, GFP_KERNEL)) { - dev_err(&connection->dev, - "error allocating response\n"); + dev_err(dev, "error allocating response\n"); return -ENOMEM; } response = operation->response->payload; @@ -547,8 +545,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) } return 0; default: - dev_err(&connection->dev, - "unsupported request: %hhu\n", type); + dev_err(dev, "unsupported request: %hhu\n", type); return -EINVAL; } } @@ -755,8 +752,8 @@ static int gb_loopback_fn(void *data) /* All threads achieved at least low_count iterations */ if (gb_dev.iteration_count < low_count) { gb_dev.iteration_count = low_count; - sysfs_notify(&gb->connection->dev.kobj, NULL, - "iteration_count"); + sysfs_notify(&gb->connection->bundle->dev.kobj, + NULL, "iteration_count"); } /* Optionally terminate */ if (gb_dev.iteration_count == gb_dev.iteration_max) { @@ -954,12 +951,12 @@ static int gb_loopback_connection_init(struct gb_connection *connection) /* Create per-connection sysfs and debugfs data-points */ snprintf(name, sizeof(name), "raw_latency_%s", - dev_name(&connection->dev)); + dev_name(&connection->bundle->dev)); gb->file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, gb, &gb_loopback_debugfs_latency_ops); gb->connection = connection; - connection->private = gb; - retval = sysfs_create_groups(&connection->dev.kobj, + connection->bundle->private = gb; + retval = sysfs_create_groups(&connection->bundle->dev.kobj, loopback_con_groups); if (retval) goto out_sysfs_dev; @@ -995,14 +992,14 @@ out_kfifo1: out_kfifo0: kfifo_free(&gb->kfifo_lat); out_sysfs_conn: - sysfs_remove_groups(&connection->dev.kobj, loopback_con_groups); + sysfs_remove_groups(&connection->bundle->dev.kobj, loopback_con_groups); out_sysfs_dev: if (!gb_dev.count) { sysfs_remove_groups(kobj, loopback_dev_groups); debugfs_remove(gb_dev.file); } debugfs_remove(gb->file); - connection->private = NULL; + connection->bundle->private = NULL; out_sysfs: mutex_unlock(&gb_dev.mutex); kfree(gb); @@ -1012,7 +1009,7 @@ out_sysfs: static void gb_loopback_connection_exit(struct gb_connection *connection) { - struct gb_loopback *gb = connection->private; + struct gb_loopback *gb = connection->bundle->private; struct kobject *kobj = &connection->hd->endo->dev.kobj; if (!IS_ERR_OR_NULL(gb->task)) @@ -1020,7 +1017,7 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) mutex_lock(&gb_dev.mutex); - connection->private = NULL; + connection->bundle->private = NULL; kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); gb_connection_latency_tag_disable(connection); @@ -1029,7 +1026,8 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) sysfs_remove_groups(kobj, loopback_dev_groups); debugfs_remove(gb_dev.file); } - sysfs_remove_groups(&connection->dev.kobj, loopback_con_groups); + sysfs_remove_groups(&connection->bundle->dev.kobj, + loopback_con_groups); debugfs_remove(gb->file); list_del(&gb->entry); mutex_unlock(&gb_dev.mutex); -- cgit v1.2.3-59-g8ed1b From 5d9c68da01e80c145bfc5e28ccb27b17d3a016b3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Oct 2015 16:55:29 -0700 Subject: greybus: greybus_trace: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the greybus trace code to use the bundle pointer instead of the connection pointer when printing out tracing messages. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold --- drivers/staging/greybus/greybus_trace.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 6c88d34d6f5d..e5e3cc0ce0a8 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -24,7 +24,7 @@ DECLARE_EVENT_CLASS(gb_message, TP_ARGS(message), TP_STRUCT__entry( - __string(name, dev_name(&message->operation->connection->dev)) + __string(name, dev_name(&message->operation->connection->bundle->dev)) __field(u16, op_id) __field(u16, intf_cport_id) __field(u16, hd_cport_id) @@ -32,7 +32,7 @@ DECLARE_EVENT_CLASS(gb_message, ), TP_fast_assign( - __assign_str(name, dev_name(&message->operation->connection->dev)) + __assign_str(name, dev_name(&message->operation->connection->bundle->dev)) __entry->op_id = message->operation->id; __entry->intf_cport_id = message->operation->connection->intf_cport_id; -- cgit v1.2.3-59-g8ed1b From 7e4c8d713539b608a11a38102a203a1a8acd345a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Oct 2015 16:54:18 -0700 Subject: greybus: protocol: use the bundle struct device instead of the connector We are removing struct device from the gb_connection structure in the near future. The gb_bundle structure's struct device should be used as a replacement. This patch moves the protocol code to use the bundle pointer instead of the connection pointer when printing out error messages. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold --- drivers/staging/greybus/protocol.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 889cff2a10c2..1790d1ac8513 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -165,19 +165,19 @@ int gb_protocol_get_version(struct gb_connection *connection) return retval; if (response.major > connection->protocol->major) { - dev_err(&connection->dev, - "unsupported major version (%hhu > %hhu)\n", - response.major, connection->protocol->major); + dev_err(&connection->bundle->dev, + "%d: unsupported major version (%hhu > %hhu)\n", + connection->intf_cport_id, response.major, + connection->protocol->major); return -ENOTSUPP; } connection->module_major = response.major; connection->module_minor = response.minor; - - dev_dbg(&connection->dev, "%s - %s (0x%02hhx) v%hhu.%hhu\n", __func__, - protocol->name, protocol->id, - response.major, response.minor); + dev_dbg(&connection->bundle->dev, + "%d: %s (0x%02hhx) v%hhu.%hhu\n", connection->intf_cport_id, + protocol->name, protocol->id, response.major, response.minor); return 0; } -- cgit v1.2.3-59-g8ed1b From 6ea462a4fc21b523dc139fdb920524f066633a35 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Oct 2015 16:54:00 -0700 Subject: greybus: bundle.h: add a private field to struct gb_bundle The gb_bundle structure needs a private field if we are going to be able to replace the gb_connection device with this one for all use cases. Ideally we could use the private pointer within the struct device, but for now let's just try to mirror how people were using the gb_connection structure to make the patches simpler, and the logic the same. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold --- drivers/staging/greybus/bundle.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 9134df7930c0..24ddd449034c 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -22,6 +22,7 @@ struct gb_bundle { u8 *state; struct list_head links; /* interface->bundles */ + void *private; }; #define to_gb_bundle(d) container_of(d, struct gb_bundle, dev) -- cgit v1.2.3-59-g8ed1b From c69b98d15fff68e83396d800f43508a1dab4130a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Oct 2015 16:53:46 -0700 Subject: greybus: core: remove uevent handling for gb_connection As we are going to be removing the struct device from gb_connection, there is no need to do anything for uevents for them. So just remove the code. It wasn't doing anything anyway, so no functionality is lost here at all. As is_gb_connection() is no longer used, that is also removed in this patch. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold --- drivers/staging/greybus/core.c | 11 ----------- drivers/staging/greybus/greybus.h | 5 ----- 2 files changed, 16 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index ea23aaff9465..5e1c75533d80 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -47,7 +47,6 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) struct gb_module *module = NULL; struct gb_interface *intf = NULL; struct gb_bundle *bundle = NULL; - struct gb_connection *connection = NULL; if (is_gb_endo(dev)) { /* @@ -64,21 +63,11 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); intf = bundle->intf; - } else if (is_gb_connection(dev)) { - connection = to_gb_connection(dev); - bundle = connection->bundle; - intf = bundle->intf; } else { dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n"); return -EINVAL; } - if (connection) { - // FIXME - // add a uevent that can "load" a connection type - return 0; - } - if (bundle) { // FIXME // add a uevent that can "load" a bundle type diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index ec4a73884b09..ae5975632c5e 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -189,11 +189,6 @@ static inline int is_gb_bundle(const struct device *dev) return dev->type == &greybus_bundle_type; } -static inline int is_gb_connection(const struct device *dev) -{ - return dev->type == &greybus_connection_type; -} - static inline bool cport_id_valid(struct greybus_host_device *hd, u16 cport_id) { return cport_id != CPORT_ID_BAD && cport_id < hd->num_cports; -- cgit v1.2.3-59-g8ed1b From b50a24e9471290fae7f7d16be467b3b0937d3d0e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Oct 2015 16:53:31 -0700 Subject: greybus: svc: don't trust any struct devices As the svc code is "odd" in how things are set up at initial connection time, we can't always "know" that we have a valid bundle to use for error messages. So just punt and always use pr_*() calls to ensure that we never incorrectly dereference a pointer. This will get fixed up soon, but for now, let's just live with a bit messier error messages in the log. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold --- drivers/staging/greybus/svc.c | 110 +++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 61 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 4b9eb383652c..e5cd8971b2cf 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -7,6 +7,7 @@ * Released under the GPLv2 only. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "greybus.h" @@ -120,17 +121,15 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&svc->connection->dev, - "failed to get DME attribute (%hhu %hx %hu) %d\n", - intf_id, attr, selector, ret); + pr_err("failed to get DME attribute (%hhu %hx %hu) %d\n", + intf_id, attr, selector, ret); return ret; } result = le16_to_cpu(response.result_code); if (result) { - dev_err(&svc->connection->dev, - "Unipro error %hu while getting DME attribute (%hhu %hx %hu)\n", - result, intf_id, attr, selector); + pr_err("Unipro error %hu while getting DME attribute (%hhu %hx %hu)\n", + result, intf_id, attr, selector); return -EINVAL; } @@ -158,17 +157,15 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&svc->connection->dev, - "failed to set DME attribute (%hhu %hx %hu %u) %d\n", - intf_id, attr, selector, value, ret); + pr_err("failed to set DME attribute (%hhu %hx %hu %u) %d\n", + intf_id, attr, selector, value, ret); return ret; } result = le16_to_cpu(response.result_code); if (result) { - dev_err(&svc->connection->dev, - "Unipro error %hu while setting DME attribute (%hhu %hx %hu %u)\n", - result, intf_id, attr, selector, value); + pr_err("Unipro error %hu while setting DME attribute (%hhu %hx %hu %u)\n", + result, intf_id, attr, selector, value); return -EINVAL; } @@ -270,11 +267,9 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY, &request, sizeof(request), NULL, 0); - if (ret) { - dev_err(&connection->dev, - "failed to destroy connection (%hhx:%hx %hhx:%hx) %d\n", - intf1_id, cport1_id, intf2_id, cport2_id, ret); - } + if (ret) + pr_err("failed to destroy connection (%hhx:%hx %hhx:%hx) %d\n", + intf1_id, cport1_id, intf2_id, cport2_id, ret); } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); @@ -304,11 +299,9 @@ static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_DESTROY, &request, sizeof(request), NULL, 0); - if (ret) { - dev_err(&svc->connection->dev, - "failed to destroy route (%hhx %hhx) %d\n", + if (ret) + pr_err("failed to destroy route (%hhx %hhx) %d\n", intf1_id, intf2_id, ret); - } } static int gb_svc_version_request(struct gb_operation *op) @@ -316,14 +309,13 @@ static int gb_svc_version_request(struct gb_operation *op) struct gb_connection *connection = op->connection; struct gb_protocol_version_request *request; struct gb_protocol_version_response *response; - struct device *dev = &connection->dev; request = op->request->payload; if (request->major > GB_SVC_VERSION_MAJOR) { - dev_err(&connection->dev, - "unsupported major version (%hhu > %hhu)\n", - request->major, GB_SVC_VERSION_MAJOR); + pr_err("%d: unsupported major version (%hhu > %hhu)\n", + connection->intf_cport_id, request->major, + GB_SVC_VERSION_MAJOR); return -ENOTSUPP; } @@ -331,8 +323,8 @@ static int gb_svc_version_request(struct gb_operation *op) connection->module_minor = request->minor; if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) { - dev_err(dev, "%s: error allocating response\n", - __func__); + pr_err("%d: error allocating response\n", + connection->intf_cport_id); return -ENOMEM; } @@ -348,7 +340,6 @@ static int gb_svc_hello(struct gb_operation *op) struct gb_connection *connection = op->connection; struct greybus_host_device *hd = connection->hd; struct gb_svc_hello_request *hello_request; - struct device *dev = &connection->dev; struct gb_interface *intf; u16 endo_id; u8 interface_id; @@ -359,9 +350,9 @@ static int gb_svc_hello(struct gb_operation *op) * request, use that to create an endo. */ if (op->request->payload_size < sizeof(*hello_request)) { - dev_err(dev, "%s: Illegal size of hello request (%zu < %zu)\n", - __func__, op->request->payload_size, - sizeof(*hello_request)); + pr_err("%d: Illegal size of hello request (%zu < %zu)\n", + connection->intf_cport_id, op->request->payload_size, + sizeof(*hello_request)); return -EINVAL; } @@ -418,7 +409,6 @@ static void svc_process_hotplug(struct work_struct *work) struct gb_connection *connection = svc_hotplug->connection; struct gb_svc *svc = connection->private; struct greybus_host_device *hd = connection->hd; - struct device *dev = &connection->dev; struct gb_interface *intf; u8 intf_id, device_id; int ret; @@ -444,15 +434,15 @@ static void svc_process_hotplug(struct work_struct *work) * Remove the interface and add it again, and let user know * about this with a print message. */ - dev_info(dev, "Removed interface (%hhu) to add it again\n", - intf_id); + pr_info("%d: Removed interface (%hhu) to add it again\n", + connection->intf_cport_id, intf_id); svc_intf_remove(connection, intf); } intf = gb_interface_create(hd, intf_id); if (!intf) { - dev_err(dev, "%s: Failed to create interface with id %hhu\n", - __func__, intf_id); + pr_err("%d: Failed to create interface with id %hhu\n", + connection->intf_cport_id, intf_id); goto free_svc_hotplug; } @@ -477,15 +467,15 @@ static void svc_process_hotplug(struct work_struct *work) GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); if (device_id < 0) { ret = device_id; - dev_err(dev, "%s: Failed to allocate device id for interface with id %hhu (%d)\n", - __func__, intf_id, ret); + pr_err("%d: Failed to allocate device id for interface with id %hhu (%d)\n", + connection->intf_cport_id, intf_id, ret); goto destroy_interface; } ret = gb_svc_intf_device_id(svc, intf_id, device_id); if (ret) { - dev_err(dev, "%s: Device id operation failed, interface %hhu device_id %hhu (%d)\n", - __func__, intf_id, device_id, ret); + pr_err("%d: Device id operation failed, interface %hhu device_id %hhu (%d)\n", + connection->intf_cport_id, intf_id, device_id, ret); goto ida_put; } @@ -495,15 +485,15 @@ static void svc_process_hotplug(struct work_struct *work) ret = gb_svc_route_create(svc, hd->endo->ap_intf_id, GB_DEVICE_ID_AP, intf_id, device_id); if (ret) { - dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", - __func__, intf_id, device_id, ret); + pr_err("%d: Route create operation failed, interface %hhu device_id %hhu (%d)\n", + connection->intf_cport_id, intf_id, device_id, ret); goto svc_id_free; } ret = gb_interface_init(intf, device_id); if (ret) { - dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", - __func__, intf_id, device_id, ret); + pr_err("%d: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", + connection->intf_cport_id, intf_id, device_id, ret); goto destroy_route; } @@ -539,10 +529,9 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) struct svc_hotplug *svc_hotplug; if (request->payload_size < sizeof(svc_hotplug->data)) { - dev_err(&op->connection->dev, - "%s: short hotplug request received (%zu < %zu)\n", - __func__, request->payload_size, - sizeof(svc_hotplug->data)); + pr_err("%d: short hotplug request received (%zu < %zu)\n", + op->connection->intf_cport_id, request->payload_size, + sizeof(svc_hotplug->data)); return -EINVAL; } @@ -564,13 +553,13 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) struct gb_message *request = op->request; struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload; struct greybus_host_device *hd = op->connection->hd; - struct device *dev = &op->connection->dev; struct gb_interface *intf; u8 intf_id; if (request->payload_size < sizeof(*hot_unplug)) { - dev_err(dev, "short hot unplug request received (%zu < %zu)\n", - request->payload_size, sizeof(*hot_unplug)); + pr_err("connection %d: short hot unplug request received (%zu < %zu)\n", + op->connection->intf_cport_id, request->payload_size, + sizeof(*hot_unplug)); return -EINVAL; } @@ -578,8 +567,8 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) intf = gb_interface_find(hd, intf_id); if (!intf) { - dev_err(dev, "%s: Couldn't find interface for id %hhu\n", - __func__, intf_id); + pr_err("connection %d: Couldn't find interface for id %hhu\n", + op->connection->intf_cport_id, intf_id); return -EINVAL; } @@ -595,9 +584,9 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) u8 intf_id; if (request->payload_size < sizeof(*reset)) { - dev_err(&op->connection->dev, - "short reset request received (%zu < %zu)\n", - request->payload_size, sizeof(*reset)); + pr_err("connection %d: short reset request received (%zu < %zu)\n", + op->connection->intf_cport_id, request->payload_size, + sizeof(*reset)); return -EINVAL; } reset = request->payload; @@ -641,9 +630,8 @@ static int gb_svc_request_recv(u8 type, struct gb_operation *op) } if (ret) { - dev_warn(&connection->dev, - "unexpected SVC request 0x%02x received (state %u)\n", - type, svc->state); + pr_warn("connection %d: unexpected SVC request 0x%02x received (state %u)\n", + connection->intf_cport_id, type, svc->state); return ret; } @@ -665,8 +653,8 @@ static int gb_svc_request_recv(u8 type, struct gb_operation *op) case GB_SVC_TYPE_INTF_RESET: return gb_svc_intf_reset_recv(op); default: - dev_err(&op->connection->dev, - "unsupported request: %hhu\n", type); + pr_err("connection %d: unsupported request: %hhu\n", + connection->intf_cport_id, type); return -EINVAL; } } -- cgit v1.2.3-59-g8ed1b From d517f274e9db414ae4d6df5173c3e7f41b9dac66 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Wed, 21 Oct 2015 16:50:20 +0530 Subject: greybus: platform: Add platform driver for DB3 AP bridge With DB3, we now have AP as a master as far as AP bridges are concerned. SVC will talk only to AP and AP will control bridges; unlike other module interfaces. So AP supposed to manage/control bridges in all power states including power on reset. During power on reset AP should follow below sequence - Sequence (treated as a Cold Boot) Stage-1 ======= AP: - Power On (Power up from PMIC to AP) - AP start booting Since power to AP bridges are controlled through gpio, power is gated to APB1 & 2 No ref_clk to APB available (under SVC's control) - AP configures USB hub to enable HSIC interface to APB - As part of platform driver probe, AP follow below sequence - Set the pinctrl in default state - Hold APBs in reset by pulling down reset pin - Enable power to APB by enabling regulator and switches - De-assert (set 'low') 'boot_ret' signal - AP will assert (set 'high') the wake_detect signal, triggering connect/detect event to the SVC - AP waits for wake pulse from SVC SVC: - Power On (power up from PMIC to SVC) - SVC starts booting - SVC will de-assert reset signal to unipro switch - Switch starts booting - SVC confirms switch boot status using SPI (or something) - SVC waits for 300 msec (ES2 known issue) - SVC waits for detect/connect event from AP Stage-2 ======= SVC: - ON connect/detect event, SVC send back wake pulse (cold boot) to AP over wake_detect pin, if SVC boot is completed. AP: - On wake pulse from SVC (for cold boot), AP de-asserts (set high') reset signal to APB 1 and/or 2 - Bridges starts booting - Eventually Unipro linkup occurs Testing: - Build tested against Helium kernel - Due to unavailability of MSM and DB3 platform, only minimal testing has been done. - Code has been modified for validation on Helium + SDB platform. Mostly dts changes for gpio numbers And debug messages to check gpio values - On Helium + SDB platform, with addition of debug messages validated the sequence. TODO list: - Currently _only_ supports power on sequence (cold boot). Both warm and cold boot support. Cold and Warm boot is differentiated based on pulse width of wake_detect signal >=5 msec = Cold boot else Warm boot - No support for Power management So the "power-down", "power-off", "wake_in" and "wake_out" signals are not explored/implemented. - Support for Work thread repetitive wake signal if no response from peer May required for PM support, as we have delays in the sequences - pinctrl states, specially to make sure we enable right pullup or pulldown when we set wake_detect pin to input - Convert gpio list into an array, and associated xxx-gpio-name property Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/db3-platform.c | 378 +++++++++++++++++++++++++++++++++ 2 files changed, 380 insertions(+) create mode 100644 drivers/staging/greybus/db3-platform.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index a62734093690..b8dc36b196aa 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -34,6 +34,7 @@ gb-light-y := light.o gb-raw-y := raw.o gb-es1-y := es1.o gb-es2-y := es2.o +gb-db3-y := db3-platform.o obj-m += greybus.o obj-m += gb-phy.o @@ -44,6 +45,7 @@ obj-m += gb-light.o obj-m += gb-raw.o obj-m += gb-es1.o obj-m += gb-es2.o +obj-m += gb-db3.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/db3-platform.c b/drivers/staging/greybus/db3-platform.c new file mode 100644 index 000000000000..4391348ffc0d --- /dev/null +++ b/drivers/staging/greybus/db3-platform.c @@ -0,0 +1,378 @@ +/* + * DB3 Platform driver for AP bridge control interface. + * + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum apb_state { + APB_STATE_OFF, + APB_STATE_ACTIVE, + APB_STATE_STANDBY, +}; + +/* + * Control GPIO signals to and from AP <=> AP Bridges + */ +struct apb_ctrl_gpios { + int wake_detect; /* bi-dir, maps to WAKE_MOD and WAKE_FRAME signals */ + int reset; + int boot_ret; + int pwroff; + int wake_in; + int wake_out; + int pwrdn; +}; + +struct apb_ctrl_drvdata { + struct apb_ctrl_gpios ctrl; + + unsigned int wake_detect_irq; + enum apb_state state; + + struct regulator *vcore; + struct regulator *vio; + + struct pinctrl *pinctrl; + struct pinctrl_state *pin_default; + + /* To protect concurrent access of GPIO registers, need protection */ + spinlock_t lock; +}; + +static inline void assert_wake(unsigned int wake_detect) +{ + gpio_set_value(wake_detect, 1); +} + +static inline void deassert_wake(unsigned int wake_detect) +{ + gpio_set_value(wake_detect, 0); +} + +static irqreturn_t apb_ctrl_wake_detect_irq(int irq, void *devid) +{ + struct apb_ctrl_drvdata *apb_data = devid; + unsigned long flags; + + /* + * TODO: + * Since currently SoC gpio's are being used we are safe here + * But ideally we should create a workqueue and process the control + * signals, especially when we start using GPIO's over slow + * buses like I2C. + */ + if (!gpio_is_valid(apb_data->ctrl.wake_detect) && + !gpio_is_valid(apb_data->ctrl.reset)) + return IRQ_HANDLED; /* Should it be IRQ_NONE ?? */ + + spin_lock_irqsave(&apb_data->lock, flags); + + if (apb_data->state != APB_STATE_ACTIVE) { + /* Bring bridge out of reset on this event */ + gpio_set_value(apb_data->ctrl.reset, 0); + apb_data->state = APB_STATE_ACTIVE; + } else { + /* + * Assert Wake_OUT signal to APB + * It would resemble WakeDetect module's signal pass-through + */ + /* + * We have to generate the pulse, so we may need to schedule + * workqueue here. + * + * Also, since we are using both rising and falling edge for + * interrupt trigger, we may not need workqueue. Just pass + * through the value to bridge. + * Just read GPIO value and pass it to the bridge + */ + } + + spin_unlock_irqrestore(&apb_data->lock, flags); + + return IRQ_HANDLED; +} + +static void apb_ctrl_cleanup(struct apb_ctrl_drvdata *apb_data) +{ + if (apb_data->vcore && regulator_is_enabled(apb_data->vcore) > 0) + regulator_disable(apb_data->vcore); + + if (apb_data->vio && regulator_is_enabled(apb_data->vio) > 0) + regulator_disable(apb_data->vio); + + /* As part of exit, put APB back in reset state */ + if (gpio_is_valid(apb_data->ctrl.reset)) + gpio_set_value(apb_data->ctrl.reset, 1); + + /* TODO: May have to send an event to SVC about this exit */ +} + +/* + * Note: Please do not modify the below sequence, as it is as per the spec + */ +static int apb_ctrl_init_seq(struct device *dev, + struct apb_ctrl_drvdata *apb_data) +{ + int ret; + + pinctrl_select_state(apb_data->pinctrl, apb_data->pin_default); + + /* Hold APB in reset state */ + ret = devm_gpio_request_one(dev, apb_data->ctrl.reset, + GPIOF_OUT_INIT_LOW, "reset"); + if (ret) { + dev_err(dev, "Failed requesting reset gpio %d\n", + apb_data->ctrl.reset); + return ret; + } + + /* Enable power to APB */ + if (apb_data->vcore) { + ret = regulator_enable(apb_data->vcore); + if (ret) { + dev_err(dev, "failed to enable core regulator\n"); + return ret; + } + } + + if (apb_data->vio) { + ret = regulator_enable(apb_data->vio); + if (ret) { + dev_err(dev, "failed to enable IO regulator\n"); + return ret; + } + } + + /* + * We should be safe here to deassert boot retention signal, as + * we are only supporting cold boot as of now. + */ + ret = devm_gpio_request_one(dev, apb_data->ctrl.boot_ret, + GPIOF_OUT_INIT_LOW, "boot retention"); + if (ret) { + dev_err(dev, "Failed requesting reset gpio %d\n", + apb_data->ctrl.boot_ret); + return ret; + } + + ret = devm_gpio_request(dev, apb_data->ctrl.wake_detect, "wake detect"); + if (ret) + dev_err(dev, "Failed requesting wake_detect gpio %d\n", + apb_data->ctrl.wake_detect); + + return ret; +} + +static int apb_ctrl_get_devtree_data(struct device *dev, + struct apb_ctrl_drvdata *apb_data) +{ + struct device_node *np = dev->of_node; + + /* fetch control signals */ + apb_data->ctrl.wake_detect = of_get_named_gpio(np, "wake-detect-gpios", 0); + if (apb_data->ctrl.wake_detect < 0 || + !gpio_is_valid(apb_data->ctrl.wake_detect)) { + dev_err(dev, "failed to get wake detect gpio\n"); + return apb_data->ctrl.wake_detect; + } + + apb_data->ctrl.reset = of_get_named_gpio(np, "reset-gpios", 0); + if (apb_data->ctrl.wake_detect < 0 || + !gpio_is_valid(apb_data->ctrl.reset)) { + dev_err(dev, "failed to get wake detect gpio\n"); + return apb_data->ctrl.wake_detect; + } + + apb_data->ctrl.boot_ret = of_get_named_gpio(np, "boot-ret-gpios", 0); + if (apb_data->ctrl.boot_ret < 0 || + !gpio_is_valid(apb_data->ctrl.boot_ret)) { + dev_err(dev, "failed to get boot retention gpio\n"); + return apb_data->ctrl.boot_ret; + } + + /* It's not mandetory to support power management interface */ + apb_data->ctrl.pwroff = of_get_named_gpio(np, "pwr-off-gpios", 0); + if (apb_data->ctrl.pwroff < 0 || + !gpio_is_valid(apb_data->ctrl.pwroff)) + dev_info(dev, "failed to get power off gpio\n"); + + apb_data->ctrl.pwrdn = of_get_named_gpio(np, "pwr-down-gpios", 0); + if (apb_data->ctrl.pwrdn < 0 || + !gpio_is_valid(apb_data->ctrl.pwrdn)) + dev_info(dev, "failed to get power down gpio\n"); + + /* Regulators are optional, as we may have fixed supply coming in */ + apb_data->vcore = devm_regulator_get(dev, "vcore"); + if (IS_ERR_OR_NULL(apb_data->vcore)) { + dev_info(dev, "no core regulator found\n"); + apb_data->vcore = NULL; + } + + apb_data->vio = devm_regulator_get(dev, "vio"); + if (IS_ERR_OR_NULL(apb_data->vio)) { + dev_info(dev, "no IO regulator found\n"); + apb_data->vio = NULL; + } + + apb_data->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(apb_data->pinctrl)) { + dev_err(dev, "could not get pinctrl handle\n"); + return PTR_ERR(apb_data->pinctrl); + } + apb_data->pin_default = pinctrl_lookup_state(apb_data->pinctrl, "default"); + if (IS_ERR(apb_data->pin_default)) { + dev_err(dev, "could not get default pin state\n"); + return PTR_ERR(apb_data->pin_default); + } + + return 0; +} + +static int apb_ctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apb_ctrl_drvdata *apb_data; + int ret; + + apb_data = devm_kzalloc(&pdev->dev, sizeof(*apb_data), GFP_KERNEL); + if (!apb_data) + return -ENOMEM; + + ret = apb_ctrl_get_devtree_data(dev, apb_data); + if (ret) { + dev_err(dev, "failed to get devicetree data %d\n", ret); + return ret; + } + + ret = apb_ctrl_init_seq(dev, apb_data); + if (ret) { + dev_err(dev, "failed to set init state of control signal %d\n", + ret); + goto exit; + } + + platform_set_drvdata(pdev, apb_data); + + apb_data->state = APB_STATE_OFF; + /* + * Assert AP module detect signal by pulling wake_detect low + */ + deassert_wake(apb_data->ctrl.wake_detect); + + /* + * In order to receive an interrupt, the GPIO must be set to input mode + * + * As per WDM spec, for the cold boot, the wake pulse must be + * >= 5000 usec, but at this stage it is power up sequence, + * so we always treat it as cold boot. + */ + gpio_direction_input(apb_data->ctrl.wake_detect); + + ret = devm_request_irq(dev, gpio_to_irq(apb_data->ctrl.wake_detect), + apb_ctrl_wake_detect_irq, + IRQF_TRIGGER_RISING, + "wake detect", apb_data); + if (ret) { + dev_err(&pdev->dev, "failed to request wake detect IRQ\n"); + goto exit; + } + + /* + * Interrupt handling for WAKE_IN (from bridge) signal is required + * + * Assumption here is, AP already would have woken up and in the + * WAKE_IN/WAKE_FRAME event from bridge, as AP would pass-through event + * to SVC. + * + * Not sure anything else needs to take care here. + */ + dev_err(dev, "Device registered successfully \n"); + return 0; + +exit: + apb_ctrl_cleanup(apb_data); + return ret; +} + +static int apb_ctrl_remove(struct platform_device *pdev) +{ + struct apb_ctrl_drvdata *apb_data = platform_get_drvdata(pdev); + + if (apb_data) + apb_ctrl_cleanup(apb_data); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int apb_ctrl_suspend(struct device *dev) +{ + /* + * If timing profile premits, we may shutdown bridge + * completely + * + * TODO: sequence ?? + * + * Also, need to make sure we meet precondition for unipro suspend + * Precondition: Definition ??? + */ + return 0; +} + +static int apb_ctrl_resume(struct device *dev) +{ + /* + * Atleast for ES2 we have to meet the delay requirement between + * unipro switch and AP bridge init, depending on whether bridge is in + * OFF state or standby state. + * + * Based on whether bridge is in standby or OFF state we may have to + * assert multiple signals. Please refer to WDM spec, for more info. + * + */ + return 0; +} + +static SIMPLE_DEV_PM_OPS(apb_ctrl_pm_ops, apb_ctrl_suspend, apb_ctrl_resume); + +static struct of_device_id apb_ctrl_of_match[] = { + { .compatible = "usbffff,2", }, + { }, +}; +MODULE_DEVICE_TABLE(of, apb_ctrl_of_match); + +static struct platform_driver apb_ctrl_device_driver = { + .probe = apb_ctrl_probe, + .remove = apb_ctrl_remove, + .driver = { + .name = "unipro-APbridge", + .pm = &apb_ctrl_pm_ops, + .of_match_table = of_match_ptr(apb_ctrl_of_match), + } +}; + +module_platform_driver(apb_ctrl_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vaibhav Hiremath "); +MODULE_DESCRIPTION("AP Bridge Control Driver for DB3 platform"); -- cgit v1.2.3-59-g8ed1b From 16cd787d2d54a47b1d8575bb96bff4b0edc7000b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 21 Oct 2015 11:51:42 +0200 Subject: greybus: endo: fix use-after-free in error path Fix use-after-free in endo-registration error path by moving the id-release to the device release function. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 84d695df2d62..c0dc43ff6a5c 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -113,6 +113,7 @@ static void gb_endo_release(struct device *dev) { struct gb_endo *endo = to_gb_endo(dev); + ida_simple_remove(&greybus_endo_id_map, endo->dev_id); kfree(endo); } @@ -462,7 +463,6 @@ static int gb_endo_register(struct greybus_host_device *hd, dev_err(hd->parent, "failed to add endo device of id 0x%04x\n", endo->id); put_device(&endo->dev); - ida_simple_remove(&greybus_endo_id_map, endo->dev_id); } return retval; @@ -518,7 +518,6 @@ void gb_endo_remove(struct gb_endo *endo) /* remove all modules for this endo */ gb_module_remove_all(endo); - ida_simple_remove(&greybus_endo_id_map, endo->dev_id); device_unregister(&endo->dev); } -- cgit v1.2.3-59-g8ed1b From 3014712fe1dc26640178319e55f3a80ce42a4775 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 21 Oct 2015 11:51:43 +0200 Subject: greybus: endo: fix device-id allocation During endo registration, a unique device id was allocated but then never used. Instead the default dev_id 0 (due to kzalloc) was used for device-generation and later for id-deallocation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index c0dc43ff6a5c..37d2dec17a30 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -438,11 +438,14 @@ static int create_modules(struct gb_endo *endo) static int gb_endo_register(struct greybus_host_device *hd, struct gb_endo *endo) { + int dev_id; int retval; - retval = ida_simple_get(&greybus_endo_id_map, 0, 0, GFP_KERNEL); - if (retval < 0) - return retval; + dev_id = ida_simple_get(&greybus_endo_id_map, 0, 0, GFP_KERNEL); + if (dev_id < 0) + return dev_id; + + endo->dev_id = dev_id; endo->dev.parent = hd->parent; endo->dev.bus = &greybus_bus_type; -- cgit v1.2.3-59-g8ed1b From 8d1043a30fc23a3f8a95b972beb694dd948bbe45 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Oct 2015 15:26:43 +0530 Subject: greybus: bundle: fix double freeing of bundle structure The bundle will be released by gb_bundle_release() once all references for the bundle are dropped. And so there is no need to free it in the error path specially. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 5c09bccf55b8..01745f40f6e6 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -184,7 +184,6 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, pr_err("failed to add bundle device for id 0x%02hhx\n", bundle_id); put_device(&bundle->dev); - kfree(bundle); return NULL; } -- cgit v1.2.3-59-g8ed1b From 63e8a14b1f9107ee2b51205385695be43ca40d5e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Oct 2015 15:26:44 +0530 Subject: greybus: interface: fix double freeing of interface structure The interface will be released by gb_interface_release() once all references for the interface are dropped. And so there is no need to free it in the error path specially. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 64bf91ab0fb0..5d238d3e907b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -167,7 +167,6 @@ struct gb_interface *gb_interface_create(struct greybus_host_device *hd, free_intf: put_device(&intf->dev); - kfree(intf); put_module: put_device(&module->dev); return NULL; -- cgit v1.2.3-59-g8ed1b From f7d3ad9828eba01ed2f5ccc1a12b8b1362ed02b4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Oct 2015 15:26:45 +0530 Subject: greybus: module: fix double freeing of module structure The module will be released by gb_module_release() once all references for the module are dropped. And so there is no need to free it in the error path specially. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/module.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index d7706544f3be..43e8bab91caf 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -151,7 +151,6 @@ struct gb_module *gb_module_create(struct device *parent, u8 module_id) pr_err("failed to add module device for id 0x%02hhx\n", module_id); put_device(&module->dev); - kfree(module); return NULL; } -- cgit v1.2.3-59-g8ed1b From 92cc1d225282592b19a91604cc23a99013d22802 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 21 Oct 2015 15:42:44 +0530 Subject: greybus: endo: move greybus_endo_setup() to endo.c It belongs to the endo layer and should be placed in endo.c instead. Do it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 14 -------------- drivers/staging/greybus/endo.c | 14 ++++++++++++++ drivers/staging/greybus/endo.h | 2 ++ drivers/staging/greybus/greybus.h | 2 -- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 5e1c75533d80..be75456a835d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -228,20 +228,6 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver } EXPORT_SYMBOL_GPL(greybus_create_hd); -int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id, - u8 ap_intf_id) -{ - struct gb_endo *endo; - - endo = gb_endo_create(hd, endo_id, ap_intf_id); - if (IS_ERR(endo)) - return PTR_ERR(endo); - hd->endo = endo; - - return 0; -} -EXPORT_SYMBOL_GPL(greybus_endo_setup); - void greybus_remove_hd(struct greybus_host_device *hd) { /* diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 37d2dec17a30..726b1d309219 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -524,6 +524,20 @@ void gb_endo_remove(struct gb_endo *endo) device_unregister(&endo->dev); } +int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id, + u8 ap_intf_id) +{ + struct gb_endo *endo; + + endo = gb_endo_create(hd, endo_id, ap_intf_id); + if (IS_ERR(endo)) + return PTR_ERR(endo); + hd->endo = endo; + + return 0; +} +EXPORT_SYMBOL_GPL(greybus_endo_setup); + int __init gb_endo_init(void) { ida_init(&greybus_endo_id_map); diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index 3622428552a3..0b5384d89f75 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -55,6 +55,8 @@ void gb_endo_exit(void); struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id, u8 ap_intf_id); void gb_endo_remove(struct gb_endo *endo); +int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id, + u8 ap_intf_id); u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index ae5975632c5e..524a412b2e99 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -113,8 +113,6 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, struct device *parent, size_t buffer_size_max, size_t num_cports); -int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id, - u8 ap_intf_id); void greybus_remove_hd(struct greybus_host_device *hd); struct greybus_driver { -- cgit v1.2.3-59-g8ed1b From d934a88d50f92230e48b944c729021fc39c65d98 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 22 Oct 2015 08:58:16 -0500 Subject: greybus: db3-platform: fix code that fetches reset GPIO The code that fetches the reset GPIO for an AP bridge suffers from an apparent copy/paste error. Fix it. Signed-off-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/db3-platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/db3-platform.c b/drivers/staging/greybus/db3-platform.c index 4391348ffc0d..61048f56b43c 100644 --- a/drivers/staging/greybus/db3-platform.c +++ b/drivers/staging/greybus/db3-platform.c @@ -196,10 +196,10 @@ static int apb_ctrl_get_devtree_data(struct device *dev, } apb_data->ctrl.reset = of_get_named_gpio(np, "reset-gpios", 0); - if (apb_data->ctrl.wake_detect < 0 || + if (apb_data->ctrl.reset < 0 || !gpio_is_valid(apb_data->ctrl.reset)) { - dev_err(dev, "failed to get wake detect gpio\n"); - return apb_data->ctrl.wake_detect; + dev_err(dev, "failed to get reset gpio\n"); + return apb_data->ctrl.reset; } apb_data->ctrl.boot_ret = of_get_named_gpio(np, "boot-ret-gpios", 0); -- cgit v1.2.3-59-g8ed1b From 863e652e3f0e6fc3794a3cb5fbab1a3a652f349e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 22 Oct 2015 08:58:17 -0500 Subject: greybus: db3-platform: fix some typos Fix misspelled "mandatory," and use "GPIOs" for the plural form (no apostrophe and capitalized) in comments. Signed-off-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/db3-platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/db3-platform.c b/drivers/staging/greybus/db3-platform.c index 61048f56b43c..49193377a26c 100644 --- a/drivers/staging/greybus/db3-platform.c +++ b/drivers/staging/greybus/db3-platform.c @@ -75,9 +75,9 @@ static irqreturn_t apb_ctrl_wake_detect_irq(int irq, void *devid) /* * TODO: - * Since currently SoC gpio's are being used we are safe here + * Since currently SoC GPIOs are being used we are safe here * But ideally we should create a workqueue and process the control - * signals, especially when we start using GPIO's over slow + * signals, especially when we start using GPIOs over slow * buses like I2C. */ if (!gpio_is_valid(apb_data->ctrl.wake_detect) && @@ -209,7 +209,7 @@ static int apb_ctrl_get_devtree_data(struct device *dev, return apb_data->ctrl.boot_ret; } - /* It's not mandetory to support power management interface */ + /* It's not mandatory to support power management interface */ apb_data->ctrl.pwroff = of_get_named_gpio(np, "pwr-off-gpios", 0); if (apb_data->ctrl.pwroff < 0 || !gpio_is_valid(apb_data->ctrl.pwroff)) -- cgit v1.2.3-59-g8ed1b From ed8fbc2d5611716413c2e31693256c7156692372 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 22 Oct 2015 20:06:57 +0530 Subject: greybus: db3-platform: Use dev_info for success messages Used dev_err() by mistake, fix it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/db3-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/db3-platform.c b/drivers/staging/greybus/db3-platform.c index 49193377a26c..3adc68dff9ee 100644 --- a/drivers/staging/greybus/db3-platform.c +++ b/drivers/staging/greybus/db3-platform.c @@ -305,7 +305,7 @@ static int apb_ctrl_probe(struct platform_device *pdev) * * Not sure anything else needs to take care here. */ - dev_err(dev, "Device registered successfully \n"); + dev_info(dev, "Device registered successfully \n"); return 0; exit: -- cgit v1.2.3-59-g8ed1b From 76be20a2f39d3b08bdf9b653419fbd367920e856 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 22 Oct 2015 13:03:26 -0500 Subject: greybus: db3-platform: get rid of redundant gpio tests In apb_ctrl_get_devtree_data(), the value returned by of_get_named_gpio() has a redundant test for a negative return value. GPIO numbers are non-negative, so this test is redundant--testing gpio_is_valid() is sufficient. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/db3-platform.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/db3-platform.c b/drivers/staging/greybus/db3-platform.c index 3adc68dff9ee..31f27f0628c4 100644 --- a/drivers/staging/greybus/db3-platform.c +++ b/drivers/staging/greybus/db3-platform.c @@ -189,35 +189,30 @@ static int apb_ctrl_get_devtree_data(struct device *dev, /* fetch control signals */ apb_data->ctrl.wake_detect = of_get_named_gpio(np, "wake-detect-gpios", 0); - if (apb_data->ctrl.wake_detect < 0 || - !gpio_is_valid(apb_data->ctrl.wake_detect)) { + if (!gpio_is_valid(apb_data->ctrl.wake_detect)) { dev_err(dev, "failed to get wake detect gpio\n"); return apb_data->ctrl.wake_detect; } apb_data->ctrl.reset = of_get_named_gpio(np, "reset-gpios", 0); - if (apb_data->ctrl.reset < 0 || - !gpio_is_valid(apb_data->ctrl.reset)) { + if (!gpio_is_valid(apb_data->ctrl.reset)) { dev_err(dev, "failed to get reset gpio\n"); return apb_data->ctrl.reset; } apb_data->ctrl.boot_ret = of_get_named_gpio(np, "boot-ret-gpios", 0); - if (apb_data->ctrl.boot_ret < 0 || - !gpio_is_valid(apb_data->ctrl.boot_ret)) { + if (!gpio_is_valid(apb_data->ctrl.boot_ret)) { dev_err(dev, "failed to get boot retention gpio\n"); return apb_data->ctrl.boot_ret; } /* It's not mandatory to support power management interface */ apb_data->ctrl.pwroff = of_get_named_gpio(np, "pwr-off-gpios", 0); - if (apb_data->ctrl.pwroff < 0 || - !gpio_is_valid(apb_data->ctrl.pwroff)) + if (!gpio_is_valid(apb_data->ctrl.pwroff)) dev_info(dev, "failed to get power off gpio\n"); apb_data->ctrl.pwrdn = of_get_named_gpio(np, "pwr-down-gpios", 0); - if (apb_data->ctrl.pwrdn < 0 || - !gpio_is_valid(apb_data->ctrl.pwrdn)) + if (!gpio_is_valid(apb_data->ctrl.pwrdn)) dev_info(dev, "failed to get power down gpio\n"); /* Regulators are optional, as we may have fixed supply coming in */ -- cgit v1.2.3-59-g8ed1b From 8f0a654f38c8008b7156c6efbb59272ed474930e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 Oct 2015 16:40:41 -0700 Subject: greybus: es2: use official USB vendor/device id We now have an officially assigned Google USB device id for the APBridge, so add in support for it in the es2 driver. The old entry can be removed once the firmware has caught up and uses the new number. We should use the version field to determine ES1/ES2/ES3, but that will come later, for now, grab anything with this device id. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index f947983cf56c..7b4ff463b8e4 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -22,8 +22,8 @@ #define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { - /* Made up numbers for the SVC USB Bridge in ES2 */ - { USB_DEVICE(0xffff, 0x0002) }, + { USB_DEVICE(0xffff, 0x0002) }, /* Made up number, delete once firmware is fixed to use real number */ + { USB_DEVICE(0x18d1, 0x1eaf) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); -- cgit v1.2.3-59-g8ed1b From 4b1d82047ebbb108e9961fffd51f8ccd54459221 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 27 Oct 2015 22:18:37 -0500 Subject: greybus: es2: rename es2 data structures I tried this once before and Greg NAK'd it because at that point the es2 code was nearly identical to the es1 code. This is no longer the case, and we need to diverge further, so I think it's time to go down that path. The ap_dev structure changed significantly for ES2 versus ES1 as of this commit: 667f8d3 es2.c: create dedicated struct for cport_in and cport_out Since the structures are no longer the same, they should not have the same name. This patch renames three data structures so the "1" is replaced with a "2", to reflect the Toshiba AP bridge chip revision we are working with. The structures are: es1_ap_dev -> es2_ap_dev es1_cport_in -> es2_cport_in es1_cport_out -> es2_cport_out It changes names of symbols having this type as well. To finish the job, all references "ES1" (in comments and in symbol names) have been switched to use "ES2" instead. The result is a lot of changes, but they amount to a global search and replace of "es1" with "es2" (and "ES1" with "ES2"), and the result has been compile tested. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 246 +++++++++++++++++++++--------------------- 1 file changed, 124 insertions(+), 122 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 7b4ff463b8e4..e51516fc77e5 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -18,8 +18,8 @@ #include "connection.h" #include "greybus_trace.h" -/* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_GBUF_MSG_SIZE_MAX 2048 +/* Memory sizes for the buffers sent to/from the ES2 controller */ +#define ES2_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { { USB_DEVICE(0xffff, 0x0002) }, /* Made up number, delete once firmware is fixed to use real number */ @@ -70,7 +70,7 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); * @urb: array of urbs for the CPort in messages * @buffer: array of buffers for the @cport_in_urb urbs */ -struct es1_cport_in { +struct es2_cport_in { __u8 endpoint; struct urb *urb[NUM_CPORT_IN_URB]; u8 *buffer[NUM_CPORT_IN_URB]; @@ -79,12 +79,12 @@ struct es1_cport_in { /* * @endpoint: bulk out endpoint for CPort data */ -struct es1_cport_out { +struct es2_cport_out { __u8 endpoint; }; /** - * es1_ap_dev - ES1 USB Bridge to AP structure + * es2_ap_dev - ES2 USB Bridge to AP structure * @usb_dev: pointer to the USB device we are. * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure @@ -98,13 +98,13 @@ struct es1_cport_out { * corresponding @cport_out_urb is being cancelled * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" */ -struct es1_ap_dev { +struct es2_ap_dev { struct usb_device *usb_dev; struct usb_interface *usb_intf; struct greybus_host_device *hd; - struct es1_cport_in cport_in[NUM_BULKS]; - struct es1_cport_out cport_out[NUM_BULKS]; + struct es2_cport_in cport_in[NUM_BULKS]; + struct es2_cport_out cport_out[NUM_BULKS]; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; @@ -125,41 +125,41 @@ struct cport_to_ep { __u8 endpoint_out; }; -static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) +static inline struct es2_ap_dev *hd_to_es2(struct greybus_host_device *hd) { - return (struct es1_ap_dev *)&hd->hd_priv; + return (struct es2_ap_dev *)&hd->hd_priv; } static void cport_out_callback(struct urb *urb); -static void usb_log_enable(struct es1_ap_dev *es1); -static void usb_log_disable(struct es1_ap_dev *es1); +static void usb_log_enable(struct es2_ap_dev *es2); +static void usb_log_disable(struct es2_ap_dev *es2); /* Get the endpoints pair mapped to the cport */ -static int cport_to_ep_pair(struct es1_ap_dev *es1, u16 cport_id) +static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id) { - if (cport_id >= es1->hd->num_cports) + if (cport_id >= es2->hd->num_cports) return 0; - return es1->cport_to_ep[cport_id]; + return es2->cport_to_ep[cport_id]; } -#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +#define ES2_TIMEOUT 500 /* 500 ms for the SVC to do something */ /* Disable for now until we work all of this out to keep a warning-free build */ #if 0 /* Test if the endpoints pair is already mapped to a cport */ -static int ep_pair_in_use(struct es1_ap_dev *es1, int ep_pair) +static int ep_pair_in_use(struct es2_ap_dev *es2, int ep_pair) { int i; - for (i = 0; i < es1->hd->num_cports; i++) { - if (es1->cport_to_ep[i] == ep_pair) + for (i = 0; i < es2->hd->num_cports; i++) { + if (es2->cport_to_ep[i] == ep_pair) return 1; } return 0; } /* Configure the endpoint mapping and send the request to APBridge */ -static int map_cport_to_ep(struct es1_ap_dev *es1, +static int map_cport_to_ep(struct es2_ap_dev *es2, u16 cport_id, int ep_pair) { int retval; @@ -167,28 +167,28 @@ static int map_cport_to_ep(struct es1_ap_dev *es1, if (ep_pair < 0 || ep_pair >= NUM_BULKS) return -EINVAL; - if (cport_id >= es1->hd->num_cports) + if (cport_id >= es2->hd->num_cports) return -EINVAL; - if (ep_pair && ep_pair_in_use(es1, ep_pair)) + if (ep_pair && ep_pair_in_use(es2, ep_pair)) return -EINVAL; cport_to_ep = kmalloc(sizeof(*cport_to_ep), GFP_KERNEL); if (!cport_to_ep) return -ENOMEM; - es1->cport_to_ep[cport_id] = ep_pair; + es2->cport_to_ep[cport_id] = ep_pair; cport_to_ep->cport_id = cpu_to_le16(cport_id); - cport_to_ep->endpoint_in = es1->cport_in[ep_pair].endpoint; - cport_to_ep->endpoint_out = es1->cport_out[ep_pair].endpoint; + cport_to_ep->endpoint_in = es2->cport_in[ep_pair].endpoint; + cport_to_ep->endpoint_out = es2->cport_out[ep_pair].endpoint; - retval = usb_control_msg(es1->usb_dev, - usb_sndctrlpipe(es1->usb_dev, 0), + retval = usb_control_msg(es2->usb_dev, + usb_sndctrlpipe(es2->usb_dev, 0), REQUEST_EP_MAPPING, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, (char *)cport_to_ep, sizeof(*cport_to_ep), - ES1_TIMEOUT); + ES2_TIMEOUT); if (retval == sizeof(*cport_to_ep)) retval = 0; kfree(cport_to_ep); @@ -197,30 +197,30 @@ static int map_cport_to_ep(struct es1_ap_dev *es1, } /* Unmap a cport: use the muxed endpoints pair */ -static int unmap_cport(struct es1_ap_dev *es1, u16 cport_id) +static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id) { - return map_cport_to_ep(es1, cport_id, 0); + return map_cport_to_ep(es2, cport_id, 0); } #endif -static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) +static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask) { struct urb *urb = NULL; unsigned long flags; int i; - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + spin_lock_irqsave(&es2->cport_out_urb_lock, flags); /* Look in our pool of allocated urbs first, as that's the "fastest" */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (es1->cport_out_urb_busy[i] == false && - es1->cport_out_urb_cancelled[i] == false) { - es1->cport_out_urb_busy[i] = true; - urb = es1->cport_out_urb[i]; + if (es2->cport_out_urb_busy[i] == false && + es2->cport_out_urb_cancelled[i] == false) { + es2->cport_out_urb_busy[i] = true; + urb = es2->cport_out_urb[i]; break; } } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); if (urb) return urb; @@ -228,12 +228,12 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) * Crap, pool is empty, complain to the syslog and go allocate one * dynamically as we have to succeed. */ - dev_err(&es1->usb_dev->dev, + dev_err(&es2->usb_dev->dev, "No free CPort OUT urbs, having to dynamically allocate one!\n"); return usb_alloc_urb(0, gfp_mask); } -static void free_urb(struct es1_ap_dev *es1, struct urb *urb) +static void free_urb(struct es2_ap_dev *es2, struct urb *urb) { unsigned long flags; int i; @@ -241,15 +241,15 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb) * See if this was an urb in our pool, if so mark it "free", otherwise * we need to free it ourselves. */ - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + spin_lock_irqsave(&es2->cport_out_urb_lock, flags); for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (urb == es1->cport_out_urb[i]) { - es1->cport_out_urb_busy[i] = false; + if (urb == es2->cport_out_urb[i]) { + es2->cport_out_urb_busy[i] = false; urb = NULL; break; } } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); /* If urb is not NULL, then we need to free this urb */ usb_free_urb(urb); @@ -288,8 +288,8 @@ static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) static int message_send(struct greybus_host_device *hd, u16 cport_id, struct gb_message *message, gfp_t gfp_mask) { - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; size_t buffer_size; int retval; struct urb *urb; @@ -308,23 +308,23 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, } /* Find a free urb */ - urb = next_free_urb(es1, gfp_mask); + urb = next_free_urb(es2, gfp_mask); if (!urb) return -ENOMEM; - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + spin_lock_irqsave(&es2->cport_out_urb_lock, flags); message->hcpriv = urb; - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); /* Pack the cport id into the message header */ gb_message_cport_pack(message->header, cport_id); buffer_size = sizeof(*message->header) + message->payload_size; - ep_pair = cport_to_ep_pair(es1, cport_id); + ep_pair = cport_to_ep_pair(es2, cport_id); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, - es1->cport_out[ep_pair].endpoint), + es2->cport_out[ep_pair].endpoint), message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; @@ -333,11 +333,11 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, if (retval) { dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + spin_lock_irqsave(&es2->cport_out_urb_lock, flags); message->hcpriv = NULL; - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); - free_urb(es1, urb); + free_urb(es2, urb); gb_message_cport_clear(message->header); return retval; @@ -352,13 +352,13 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, static void message_cancel(struct gb_message *message) { struct greybus_host_device *hd = message->operation->connection->hd; - struct es1_ap_dev *es1 = hd_to_es1(hd); + struct es2_ap_dev *es2 = hd_to_es2(hd); struct urb *urb; int i; might_sleep(); - spin_lock_irq(&es1->cport_out_urb_lock); + spin_lock_irq(&es2->cport_out_urb_lock); urb = message->hcpriv; /* Prevent dynamically allocated urb from being deallocated. */ @@ -366,19 +366,19 @@ static void message_cancel(struct gb_message *message) /* Prevent pre-allocated urb from being reused. */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (urb == es1->cport_out_urb[i]) { - es1->cport_out_urb_cancelled[i] = true; + if (urb == es2->cport_out_urb[i]) { + es2->cport_out_urb_cancelled[i] = true; break; } } - spin_unlock_irq(&es1->cport_out_urb_lock); + spin_unlock_irq(&es2->cport_out_urb_lock); usb_kill_urb(urb); if (i < NUM_CPORT_OUT_URB) { - spin_lock_irq(&es1->cport_out_urb_lock); - es1->cport_out_urb_cancelled[i] = false; - spin_unlock_irq(&es1->cport_out_urb_lock); + spin_lock_irq(&es2->cport_out_urb_lock); + es2->cport_out_urb_cancelled[i] = false; + spin_unlock_irq(&es2->cport_out_urb_lock); } usb_free_urb(urb); @@ -386,15 +386,15 @@ static void message_cancel(struct gb_message *message) static int cport_reset(struct greybus_host_device *hd, u16 cport_id) { - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; int retval; retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), REQUEST_RESET_CPORT, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, cport_id, - NULL, 0, ES1_TIMEOUT); + NULL, 0, ES2_TIMEOUT); if (retval < 0) { dev_err(&udev->dev, "failed to reset cport %hu: %d\n", cport_id, retval); @@ -420,8 +420,8 @@ static int cport_enable(struct greybus_host_device *hd, u16 cport_id) static int latency_tag_enable(struct greybus_host_device *hd, u16 cport_id) { int retval; - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; if (!cport_id_valid(hd, cport_id)) { dev_err(&udev->dev, "invalid destination cport 0x%02x\n", @@ -433,7 +433,7 @@ static int latency_tag_enable(struct greybus_host_device *hd, u16 cport_id) REQUEST_LATENCY_TAG_EN, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES1_TIMEOUT); + 0, ES2_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n", @@ -444,8 +444,8 @@ static int latency_tag_enable(struct greybus_host_device *hd, u16 cport_id) static int latency_tag_disable(struct greybus_host_device *hd, u16 cport_id) { int retval; - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; if (!cport_id_valid(hd, cport_id)) { dev_err(&udev->dev, "invalid destination cport 0x%02x\n", @@ -457,7 +457,7 @@ static int latency_tag_disable(struct greybus_host_device *hd, u16 cport_id) REQUEST_LATENCY_TAG_DIS, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES1_TIMEOUT); + 0, ES2_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n", @@ -465,8 +465,8 @@ static int latency_tag_disable(struct greybus_host_device *hd, u16 cport_id) return retval; } -static struct greybus_host_driver es1_driver = { - .hd_priv_size = sizeof(struct es1_ap_dev), +static struct greybus_host_driver es2_driver = { + .hd_priv_size = sizeof(struct es2_ap_dev), .message_send = message_send, .message_cancel = message_cancel, .cport_enable = cport_enable, @@ -502,31 +502,32 @@ static int check_urb_status(struct urb *urb) static void ap_disconnect(struct usb_interface *interface) { - struct es1_ap_dev *es1; + struct es2_ap_dev *es2; struct usb_device *udev; int bulk_in; int i; - es1 = usb_get_intfdata(interface); - if (!es1) + es2 = usb_get_intfdata(interface); + if (!es2) return; - usb_log_disable(es1); + usb_log_disable(es2); /* Tear down everything! */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb = es1->cport_out_urb[i]; + struct urb *urb = es2->cport_out_urb[i]; if (!urb) break; usb_kill_urb(urb); usb_free_urb(urb); - es1->cport_out_urb[i] = NULL; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ + es2->cport_out_urb[i] = NULL; + es2->cport_out_urb_busy[i] = false; /* just to be anal */ } for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { - struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; + struct es2_cport_in *cport_in = &es2->cport_in[bulk_in]; + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { struct urb *urb = cport_in->urb[i]; @@ -540,9 +541,9 @@ static void ap_disconnect(struct usb_interface *interface) } usb_set_intfdata(interface, NULL); - udev = es1->usb_dev; - greybus_remove_hd(es1->hd); - kfree(es1->cport_to_ep); + udev = es2->usb_dev; + greybus_remove_hd(es2->hd); + kfree(es2->cport_to_ep); usb_put_dev(udev); } @@ -590,15 +591,15 @@ static void cport_out_callback(struct urb *urb) { struct gb_message *message = urb->context; struct greybus_host_device *hd = message->operation->connection->hd; - struct es1_ap_dev *es1 = hd_to_es1(hd); + struct es2_ap_dev *es2 = hd_to_es2(hd); int status = check_urb_status(urb); unsigned long flags; gb_message_cport_clear(message->header); - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); + spin_lock_irqsave(&es2->cport_out_urb_lock, flags); message->hcpriv = NULL; - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); + spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); /* * Tell the submitter that the message send (attempt) is @@ -606,24 +607,24 @@ static void cport_out_callback(struct urb *urb) */ greybus_message_sent(hd, message, status); - free_urb(es1, urb); + free_urb(es2, urb); } #define APB1_LOG_MSG_SIZE 64 -static void apb1_log_get(struct es1_ap_dev *es1, char *buf) +static void apb1_log_get(struct es2_ap_dev *es2, char *buf) { int retval; /* SVC messages go down our control pipe */ do { - retval = usb_control_msg(es1->usb_dev, - usb_rcvctrlpipe(es1->usb_dev, 0), + retval = usb_control_msg(es2->usb_dev, + usb_rcvctrlpipe(es2->usb_dev, 0), REQUEST_LOG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, buf, APB1_LOG_MSG_SIZE, - ES1_TIMEOUT); + ES2_TIMEOUT); if (retval > 0) kfifo_in(&apb1_log_fifo, buf, retval); } while (retval > 0); @@ -631,7 +632,7 @@ static void apb1_log_get(struct es1_ap_dev *es1, char *buf) static int apb1_log_poll(void *data) { - struct es1_ap_dev *es1 = data; + struct es2_ap_dev *es2 = data; char *buf; buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL); @@ -640,7 +641,7 @@ static int apb1_log_poll(void *data) while (!kthread_should_stop()) { msleep(1000); - apb1_log_get(es1, buf); + apb1_log_get(es2, buf); } kfree(buf); @@ -674,13 +675,13 @@ static const struct file_operations apb1_log_fops = { .read = apb1_log_read, }; -static void usb_log_enable(struct es1_ap_dev *es1) +static void usb_log_enable(struct es2_ap_dev *es2) { if (!IS_ERR_OR_NULL(apb1_log_task)) return; /* get log from APB1 */ - apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); + apb1_log_task = kthread_run(apb1_log_poll, es2, "apb1_log"); if (IS_ERR(apb1_log_task)) return; apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, @@ -688,7 +689,7 @@ static void usb_log_enable(struct es1_ap_dev *es1) &apb1_log_fops); } -static void usb_log_disable(struct es1_ap_dev *es1) +static void usb_log_disable(struct es2_ap_dev *es2) { if (IS_ERR_OR_NULL(apb1_log_task)) return; @@ -703,8 +704,8 @@ static void usb_log_disable(struct es1_ap_dev *es1) static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, size_t count, loff_t *ppos) { - char tmp_buf[3]; int enable = !IS_ERR_OR_NULL(apb1_log_task); + char tmp_buf[3]; sprintf(tmp_buf, "%d\n", enable); return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); @@ -715,16 +716,16 @@ static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, { int enable; ssize_t retval; - struct es1_ap_dev *es1 = (struct es1_ap_dev *)f->f_inode->i_private; + struct es2_ap_dev *es2 = (struct es2_ap_dev *)f->f_inode->i_private; retval = kstrtoint_from_user(buf, count, 10, &enable); if (retval) return retval; if (enable) - usb_log_enable(es1); + usb_log_enable(es2); else - usb_log_disable(es1); + usb_log_disable(es2); return count; } @@ -747,7 +748,7 @@ static int apb1_get_cport_count(struct usb_device *udev) REQUEST_CPORT_COUNT, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, cport_count, - sizeof(*cport_count), ES1_TIMEOUT); + sizeof(*cport_count), ES2_TIMEOUT); if (retval < 0) { dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", retval); @@ -768,7 +769,7 @@ out: } /* - * The ES1 USB Bridge device contains 4 endpoints + * The ES2 USB Bridge device contains 4 endpoints * 1 Control - usual USB stuff + AP -> SVC messages * 1 Interrupt IN - SVC -> AP messages * 1 Bulk IN - CPort data in @@ -777,7 +778,7 @@ out: static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct es1_ap_dev *es1; + struct es2_ap_dev *es2; struct greybus_host_device *hd; struct usb_device *udev; struct usb_host_interface *iface_desc; @@ -798,23 +799,23 @@ static int ap_probe(struct usb_interface *interface, return num_cports; } - hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX, + hd = greybus_create_hd(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX, num_cports); if (IS_ERR(hd)) { usb_put_dev(udev); return PTR_ERR(hd); } - es1 = hd_to_es1(hd); - es1->hd = hd; - es1->usb_intf = interface; - es1->usb_dev = udev; - spin_lock_init(&es1->cport_out_urb_lock); - usb_set_intfdata(interface, es1); + es2 = hd_to_es2(hd); + es2->hd = hd; + es2->usb_intf = interface; + es2->usb_dev = udev; + spin_lock_init(&es2->cport_out_urb_lock); + usb_set_intfdata(interface, es2); - es1->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es1->cport_to_ep), + es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep), GFP_KERNEL); - if (!es1->cport_to_ep) { + if (!es2->cport_to_ep) { retval = -ENOMEM; goto error; } @@ -825,10 +826,10 @@ static int ap_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[i].desc; if (usb_endpoint_is_bulk_in(endpoint)) { - es1->cport_in[bulk_in++].endpoint = + es2->cport_in[bulk_in++].endpoint = endpoint->bEndpointAddress; } else if (usb_endpoint_is_bulk_out(endpoint)) { - es1->cport_out[bulk_out++].endpoint = + es2->cport_out[bulk_out++].endpoint = endpoint->bEndpointAddress; } else { dev_err(&udev->dev, @@ -844,7 +845,8 @@ static int ap_probe(struct usb_interface *interface, /* Allocate buffers for our cport in messages and start them up */ for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { - struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; + struct es2_cport_in *cport_in = &es2->cport_in[bulk_in]; + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { struct urb *urb; u8 *buffer; @@ -852,14 +854,14 @@ static int ap_probe(struct usb_interface *interface, urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) goto error; - buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); + buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL); if (!buffer) goto error; usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, cport_in->endpoint), - buffer, ES1_GBUF_MSG_SIZE_MAX, + buffer, ES2_GBUF_MSG_SIZE_MAX, cport_in_callback, hd); cport_in->urb[i] = urb; cport_in->buffer[i] = buffer; @@ -877,13 +879,13 @@ static int ap_probe(struct usb_interface *interface, if (!urb) goto error; - es1->cport_out_urb[i] = urb; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ + es2->cport_out_urb[i] = urb; + es2->cport_out_urb_busy[i] = false; /* just to be anal */ } apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), - gb_debugfs_get(), es1, + gb_debugfs_get(), es2, &apb1_log_enable_fops); return 0; error: @@ -892,14 +894,14 @@ error: return retval; } -static struct usb_driver es1_ap_driver = { +static struct usb_driver es2_ap_driver = { .name = "es2_ap_driver", .probe = ap_probe, .disconnect = ap_disconnect, .id_table = id_table, }; -module_usb_driver(es1_ap_driver); +module_usb_driver(es2_ap_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Greg Kroah-Hartman "); -- cgit v1.2.3-59-g8ed1b From 1482b3e1a8ab924cca4dc7e2ac9f40c0e9ff37c9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 27 Oct 2015 22:18:38 -0500 Subject: greybus: es2: move APB log task into the es2_ap_dev struct If an APB has a logging task it is associated with that APB. Move the task pointer into the es2_ap_dev structure rather than having it be a single global. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index e51516fc77e5..a4b54d3593a4 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -31,7 +31,6 @@ MODULE_DEVICE_TABLE(usb, id_table); #define APB1_LOG_SIZE SZ_16K static struct dentry *apb1_log_dentry; static struct dentry *apb1_log_enable_dentry; -static struct task_struct *apb1_log_task; static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* Number of bulk in and bulk out couple */ @@ -97,6 +96,8 @@ struct es2_cport_out { * @cport_out_urb_cancelled: array of flags indicating whether the * corresponding @cport_out_urb is being cancelled * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" + * + * @apb1_log_task: task pointer for logging thread */ struct es2_ap_dev { struct usb_device *usb_dev; @@ -111,6 +112,8 @@ struct es2_ap_dev { spinlock_t cport_out_urb_lock; int *cport_to_ep; + + struct task_struct *apb1_log_task; }; /** @@ -677,12 +680,12 @@ static const struct file_operations apb1_log_fops = { static void usb_log_enable(struct es2_ap_dev *es2) { - if (!IS_ERR_OR_NULL(apb1_log_task)) + if (!IS_ERR_OR_NULL(es2->apb1_log_task)) return; /* get log from APB1 */ - apb1_log_task = kthread_run(apb1_log_poll, es2, "apb1_log"); - if (IS_ERR(apb1_log_task)) + es2->apb1_log_task = kthread_run(apb1_log_poll, es2, "apb1_log"); + if (IS_ERR(es2->apb1_log_task)) return; apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, gb_debugfs_get(), NULL, @@ -691,20 +694,21 @@ static void usb_log_enable(struct es2_ap_dev *es2) static void usb_log_disable(struct es2_ap_dev *es2) { - if (IS_ERR_OR_NULL(apb1_log_task)) + if (IS_ERR_OR_NULL(es2->apb1_log_task)) return; debugfs_remove(apb1_log_dentry); apb1_log_dentry = NULL; - kthread_stop(apb1_log_task); - apb1_log_task = NULL; + kthread_stop(es2->apb1_log_task); + es2->apb1_log_task = NULL; } static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, size_t count, loff_t *ppos) { - int enable = !IS_ERR_OR_NULL(apb1_log_task); + struct es2_ap_dev *es2 = f->f_inode->i_private; + int enable = !IS_ERR_OR_NULL(es2->apb1_log_task); char tmp_buf[3]; sprintf(tmp_buf, "%d\n", enable); @@ -716,7 +720,7 @@ static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, { int enable; ssize_t retval; - struct es2_ap_dev *es2 = (struct es2_ap_dev *)f->f_inode->i_private; + struct es2_ap_dev *es2 = f->f_inode->i_private; retval = kstrtoint_from_user(buf, count, 10, &enable); if (retval) -- cgit v1.2.3-59-g8ed1b From 8995a39d6d030b48633c9a2ed85d3244907acff0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 27 Oct 2015 22:18:39 -0500 Subject: greybus: es2: move logging fifo into es2 struct Continue moving the logging data structures into the es2 AP data structure rather than having it be a single global. This patch moves the fifo. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index a4b54d3593a4..3de5ba192a90 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -31,7 +31,6 @@ MODULE_DEVICE_TABLE(usb, id_table); #define APB1_LOG_SIZE SZ_16K static struct dentry *apb1_log_dentry; static struct dentry *apb1_log_enable_dentry; -static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); /* Number of bulk in and bulk out couple */ #define NUM_BULKS 7 @@ -98,6 +97,7 @@ struct es2_cport_out { * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" * * @apb1_log_task: task pointer for logging thread + * @apb1_log_fifo: kernel FIFO to carry logged data */ struct es2_ap_dev { struct usb_device *usb_dev; @@ -114,6 +114,7 @@ struct es2_ap_dev { int *cport_to_ep; struct task_struct *apb1_log_task; + DECLARE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); }; /** @@ -629,7 +630,7 @@ static void apb1_log_get(struct es2_ap_dev *es2, char *buf) APB1_LOG_MSG_SIZE, ES2_TIMEOUT); if (retval > 0) - kfifo_in(&apb1_log_fifo, buf, retval); + kfifo_in(&es2->apb1_log_fifo, buf, retval); } while (retval > 0); } @@ -655,6 +656,7 @@ static int apb1_log_poll(void *data) static ssize_t apb1_log_read(struct file *f, char __user *buf, size_t count, loff_t *ppos) { + struct es2_ap_dev *es2 = f->f_inode->i_private; ssize_t ret; size_t copied; char *tmp_buf; @@ -666,7 +668,7 @@ static ssize_t apb1_log_read(struct file *f, char __user *buf, if (!tmp_buf) return -ENOMEM; - copied = kfifo_out(&apb1_log_fifo, tmp_buf, count); + copied = kfifo_out(&es2->apb1_log_fifo, tmp_buf, count); ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied); kfree(tmp_buf); @@ -815,6 +817,7 @@ static int ap_probe(struct usb_interface *interface, es2->usb_intf = interface; es2->usb_dev = udev; spin_lock_init(&es2->cport_out_urb_lock); + INIT_KFIFO(es2->apb1_log_fifo); usb_set_intfdata(interface, es2); es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep), -- cgit v1.2.3-59-g8ed1b From cbec7e2919f04d3d1abea1cde93a8052e2e8ef04 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 27 Oct 2015 22:18:40 -0500 Subject: greybus: es2: move logging dentries into es2 struct Finish moving the logging data structures into the es2 AP data structure rather than having it be a single global. This patch moves the two dentries related to logging. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 3de5ba192a90..fc3930f6f3a2 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -29,8 +29,6 @@ static const struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE(usb, id_table); #define APB1_LOG_SIZE SZ_16K -static struct dentry *apb1_log_dentry; -static struct dentry *apb1_log_enable_dentry; /* Number of bulk in and bulk out couple */ #define NUM_BULKS 7 @@ -97,6 +95,8 @@ struct es2_cport_out { * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" * * @apb1_log_task: task pointer for logging thread + * @apb1_log_dentry: file system entry for the log file interface + * @apb1_log_enable_dentry: file system entry for enabling logging * @apb1_log_fifo: kernel FIFO to carry logged data */ struct es2_ap_dev { @@ -114,6 +114,8 @@ struct es2_ap_dev { int *cport_to_ep; struct task_struct *apb1_log_task; + struct dentry *apb1_log_dentry; + struct dentry *apb1_log_enable_dentry; DECLARE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); }; @@ -689,7 +691,7 @@ static void usb_log_enable(struct es2_ap_dev *es2) es2->apb1_log_task = kthread_run(apb1_log_poll, es2, "apb1_log"); if (IS_ERR(es2->apb1_log_task)) return; - apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, + es2->apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, gb_debugfs_get(), NULL, &apb1_log_fops); } @@ -699,8 +701,8 @@ static void usb_log_disable(struct es2_ap_dev *es2) if (IS_ERR_OR_NULL(es2->apb1_log_task)) return; - debugfs_remove(apb1_log_dentry); - apb1_log_dentry = NULL; + debugfs_remove(es2->apb1_log_dentry); + es2->apb1_log_dentry = NULL; kthread_stop(es2->apb1_log_task); es2->apb1_log_task = NULL; @@ -890,7 +892,7 @@ static int ap_probe(struct usb_interface *interface, es2->cport_out_urb_busy[i] = false; /* just to be anal */ } - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", + es2->apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es2, &apb1_log_enable_fops); -- cgit v1.2.3-59-g8ed1b From 3be0e17d62d5a9b14d764935b2b2de9c8d6b379a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 27 Oct 2015 22:18:41 -0500 Subject: greybus: es2: don't assume just one AP bridge Previously we had only one AP bridge connected to the AP. We will now have two. Now that the have moved the logging related symbols into the AP device, we can stop assuming there's only on of them. Specifically, rename symbols like "apb1_*" to be just "apb_*", because the "1" is no longer meaningful. Signed-off-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 76 ++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index fc3930f6f3a2..b43435f19c34 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -94,10 +94,10 @@ struct es2_cport_out { * corresponding @cport_out_urb is being cancelled * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" * - * @apb1_log_task: task pointer for logging thread - * @apb1_log_dentry: file system entry for the log file interface - * @apb1_log_enable_dentry: file system entry for enabling logging - * @apb1_log_fifo: kernel FIFO to carry logged data + * @apb_log_task: task pointer for logging thread + * @apb_log_dentry: file system entry for the log file interface + * @apb_log_enable_dentry: file system entry for enabling logging + * @apb_log_fifo: kernel FIFO to carry logged data */ struct es2_ap_dev { struct usb_device *usb_dev; @@ -113,10 +113,10 @@ struct es2_ap_dev { int *cport_to_ep; - struct task_struct *apb1_log_task; - struct dentry *apb1_log_dentry; - struct dentry *apb1_log_enable_dentry; - DECLARE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); + struct task_struct *apb_log_task; + struct dentry *apb_log_dentry; + struct dentry *apb_log_enable_dentry; + DECLARE_KFIFO(apb_log_fifo, char, APB1_LOG_SIZE); }; /** @@ -617,7 +617,7 @@ static void cport_out_callback(struct urb *urb) } #define APB1_LOG_MSG_SIZE 64 -static void apb1_log_get(struct es2_ap_dev *es2, char *buf) +static void apb_log_get(struct es2_ap_dev *es2, char *buf) { int retval; @@ -632,11 +632,11 @@ static void apb1_log_get(struct es2_ap_dev *es2, char *buf) APB1_LOG_MSG_SIZE, ES2_TIMEOUT); if (retval > 0) - kfifo_in(&es2->apb1_log_fifo, buf, retval); + kfifo_in(&es2->apb_log_fifo, buf, retval); } while (retval > 0); } -static int apb1_log_poll(void *data) +static int apb_log_poll(void *data) { struct es2_ap_dev *es2 = data; char *buf; @@ -647,7 +647,7 @@ static int apb1_log_poll(void *data) while (!kthread_should_stop()) { msleep(1000); - apb1_log_get(es2, buf); + apb_log_get(es2, buf); } kfree(buf); @@ -655,7 +655,7 @@ static int apb1_log_poll(void *data) return 0; } -static ssize_t apb1_log_read(struct file *f, char __user *buf, +static ssize_t apb_log_read(struct file *f, char __user *buf, size_t count, loff_t *ppos) { struct es2_ap_dev *es2 = f->f_inode->i_private; @@ -670,7 +670,7 @@ static ssize_t apb1_log_read(struct file *f, char __user *buf, if (!tmp_buf) return -ENOMEM; - copied = kfifo_out(&es2->apb1_log_fifo, tmp_buf, count); + copied = kfifo_out(&es2->apb_log_fifo, tmp_buf, count); ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied); kfree(tmp_buf); @@ -678,48 +678,49 @@ static ssize_t apb1_log_read(struct file *f, char __user *buf, return ret; } -static const struct file_operations apb1_log_fops = { - .read = apb1_log_read, +static const struct file_operations apb_log_fops = { + .read = apb_log_read, }; static void usb_log_enable(struct es2_ap_dev *es2) { - if (!IS_ERR_OR_NULL(es2->apb1_log_task)) + if (!IS_ERR_OR_NULL(es2->apb_log_task)) return; /* get log from APB1 */ - es2->apb1_log_task = kthread_run(apb1_log_poll, es2, "apb1_log"); - if (IS_ERR(es2->apb1_log_task)) + es2->apb_log_task = kthread_run(apb_log_poll, es2, "apb_log"); + if (IS_ERR(es2->apb_log_task)) return; - es2->apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, + /* XXX We will need to rename this per APB */ + es2->apb_log_dentry = debugfs_create_file("apb_log", S_IRUGO, gb_debugfs_get(), NULL, - &apb1_log_fops); + &apb_log_fops); } static void usb_log_disable(struct es2_ap_dev *es2) { - if (IS_ERR_OR_NULL(es2->apb1_log_task)) + if (IS_ERR_OR_NULL(es2->apb_log_task)) return; - debugfs_remove(es2->apb1_log_dentry); - es2->apb1_log_dentry = NULL; + debugfs_remove(es2->apb_log_dentry); + es2->apb_log_dentry = NULL; - kthread_stop(es2->apb1_log_task); - es2->apb1_log_task = NULL; + kthread_stop(es2->apb_log_task); + es2->apb_log_task = NULL; } -static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, +static ssize_t apb_log_enable_read(struct file *f, char __user *buf, size_t count, loff_t *ppos) { struct es2_ap_dev *es2 = f->f_inode->i_private; - int enable = !IS_ERR_OR_NULL(es2->apb1_log_task); + int enable = !IS_ERR_OR_NULL(es2->apb_log_task); char tmp_buf[3]; sprintf(tmp_buf, "%d\n", enable); return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); } -static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, +static ssize_t apb_log_enable_write(struct file *f, const char __user *buf, size_t count, loff_t *ppos) { int enable; @@ -738,12 +739,12 @@ static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, return count; } -static const struct file_operations apb1_log_enable_fops = { - .read = apb1_log_enable_read, - .write = apb1_log_enable_write, +static const struct file_operations apb_log_enable_fops = { + .read = apb_log_enable_read, + .write = apb_log_enable_write, }; -static int apb1_get_cport_count(struct usb_device *udev) +static int apb_get_cport_count(struct usb_device *udev) { int retval; __le16 *cport_count; @@ -799,7 +800,7 @@ static int ap_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); - num_cports = apb1_get_cport_count(udev); + num_cports = apb_get_cport_count(udev); if (num_cports < 0) { usb_put_dev(udev); dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", @@ -819,7 +820,7 @@ static int ap_probe(struct usb_interface *interface, es2->usb_intf = interface; es2->usb_dev = udev; spin_lock_init(&es2->cport_out_urb_lock); - INIT_KFIFO(es2->apb1_log_fifo); + INIT_KFIFO(es2->apb_log_fifo); usb_set_intfdata(interface, es2); es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep), @@ -892,10 +893,11 @@ static int ap_probe(struct usb_interface *interface, es2->cport_out_urb_busy[i] = false; /* just to be anal */ } - es2->apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", + /* XXX We will need to rename this per APB */ + es2->apb_log_enable_dentry = debugfs_create_file("apb_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es2, - &apb1_log_enable_fops); + &apb_log_enable_fops); return 0; error: ap_disconnect(interface); -- cgit v1.2.3-59-g8ed1b From b350007219f65df8594e62c731e29ff3ad2f4d61 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 29 Oct 2015 16:58:24 +0000 Subject: greybus: manifest: fix the placement of arguments to pr_err We're one character out here in the placement of the inputs to pr_err(). Later patches show this up when pushing through checkpatch.pl. This patch fixes by moving the offending variables one character left. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 2264ec5914f9..3e61b6655a5f 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -467,8 +467,8 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) /* Validate major/minor number */ if (header->version_major > GREYBUS_VERSION_MAJOR) { pr_err("manifest version too new (%hhu.%hhu > %hhu.%hhu)\n", - header->version_major, header->version_minor, - GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); + header->version_major, header->version_minor, + GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); return false; } -- cgit v1.2.3-59-g8ed1b From 8417f5e56c8d67371f1810dfa0d1871f0119e89d Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 29 Oct 2015 16:58:29 +0000 Subject: greybus: audio-pcm: fix use of variable unitialized Compiling with clang shows that period_elapsed will be used as a branch conditional unitialized whenever snd_dev->transfer_done < runtime->period_size. Since stack can grow up/down as we proceed though the call stack this should be fixed. This patch fixes by explicitly initalizing period_elapsed to zero. Signed-off-by: Bryan O'Donoghue Reviewed-by: Alex Elder Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio-pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c index fa5e85dfc83b..e62152a7be9e 100644 --- a/drivers/staging/greybus/audio-pcm.c +++ b/drivers/staging/greybus/audio-pcm.c @@ -91,6 +91,7 @@ static void gb_pcm_work(struct work_struct *work) frames = (len + (oldptr % stride)) / stride; + period_elapsed = 0; snd_dev->transfer_done += frames; if (snd_dev->transfer_done >= runtime->period_size) { snd_dev->transfer_done -= runtime->period_size; -- cgit v1.2.3-59-g8ed1b From 53be091214572ffb55e482fe0cd53f2dc37c246a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 2 Nov 2015 11:56:58 +0100 Subject: greybus: connection: remove unused time-stamp defines Remove unused time-stamp defines, that were left in when the time-stamp fifo was removed. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 0be78fe8f50f..aef450f780f3 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -11,10 +11,6 @@ #include "greybus.h" -#define GB_CONNECTION_TS_KFIFO_ELEMENTS 2 -#define GB_CONNECTION_TS_KFIFO_LEN \ - (GB_CONNECTION_TS_KFIFO_ELEMENTS * sizeof(struct timeval)) - static DEFINE_SPINLOCK(gb_connections_lock); /* This is only used at initialization time; no locking is required. */ -- cgit v1.2.3-59-g8ed1b From 41a7fe27d6ee34f9752648805d596de0948a9873 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 2 Nov 2015 11:56:59 +0100 Subject: greybus: control: remove unused request handler Remove request handler for control protocol that makes no sense as we do not have any incoming control requests defined. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 630b5b646667..ffc07a475845 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -60,40 +60,6 @@ int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id) sizeof(request), NULL, 0); } -static int gb_control_request_recv(u8 type, struct gb_operation *op) -{ - struct gb_connection *connection = op->connection; - struct gb_protocol_version_response *version; - - switch (type) { - case GB_CONTROL_TYPE_PROBE_AP: - // TODO - // Send authenticated block of data, confirming this module is - // an AP. - break; - case GB_REQUEST_TYPE_PROTOCOL_VERSION: - if (!gb_operation_response_alloc(op, sizeof(*version), - GFP_KERNEL)) { - dev_err(&connection->bundle->dev, - "%s: error allocating response\n", __func__); - return -ENOMEM; - } - - version = op->response->payload; - version->major = GB_CONTROL_VERSION_MAJOR; - version->minor = GB_CONTROL_VERSION_MINOR; - break; - case GB_CONTROL_TYPE_CONNECTED: - case GB_CONTROL_TYPE_DISCONNECTED: - break; - default: - WARN_ON(1); - break; - } - - return 0; -} - static int gb_control_connection_init(struct gb_connection *connection) { struct gb_control *control; @@ -129,7 +95,6 @@ static struct gb_protocol control_protocol = { .minor = 1, .connection_init = gb_control_connection_init, .connection_exit = gb_control_connection_exit, - .request_recv = gb_control_request_recv, .flags = GB_PROTOCOL_SKIP_CONTROL_CONNECTED | GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED, }; -- cgit v1.2.3-59-g8ed1b From 4d5c446b53655da886822a01b5d004704b564f8d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 2 Nov 2015 11:56:57 +0100 Subject: greybus: es2: update obsolete bridge-device comments Update obsolete bridge-device comments to better describe our current ES2 endpoint layout. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index b43435f19c34..870c9cb0e11f 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -778,11 +778,10 @@ out: } /* - * The ES2 USB Bridge device contains 4 endpoints - * 1 Control - usual USB stuff + AP -> SVC messages - * 1 Interrupt IN - SVC -> AP messages - * 1 Bulk IN - CPort data in - * 1 Bulk OUT - CPort data out + * The ES2 USB Bridge device has 15 endpoints + * 1 Control - usual USB stuff + AP -> APBridgeA messages + * 7 Bulk IN - CPort data in + * 7 Bulk OUT - CPort data out */ static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) @@ -830,7 +829,7 @@ static int ap_probe(struct usb_interface *interface, goto error; } - /* find all 3 of our endpoints */ + /* find all bulk endpoints */ iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; -- cgit v1.2.3-59-g8ed1b From e96e61686388f92af8283cabe5dad158b5fd1f6e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 12:11:32 +0100 Subject: greybus: remove unused host-device id Remove unused device_id field from host-device structure. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 524a412b2e99..f51465e805fb 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -93,7 +93,6 @@ struct greybus_host_device { struct list_head interfaces; struct list_head connections; struct ida cport_id_map; - u8 device_id; /* Number of CPorts supported by the UniPro IP */ size_t num_cports; -- cgit v1.2.3-59-g8ed1b From 0e30550665cafe61776aa9730f876ade9a02a258 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 12:11:31 +0100 Subject: greybus: remove unused connection device type The connection device type is no longer used. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f51465e805fb..edee11a95a17 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -164,7 +164,6 @@ extern struct device_type greybus_endo_type; extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_bundle_type; -extern struct device_type greybus_connection_type; static inline int is_gb_endo(const struct device *dev) { -- cgit v1.2.3-59-g8ed1b From e6c88bf31e9ef22e3503e0f905cb6fa3c8c53f26 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 12:11:30 +0100 Subject: greybus: connection: use bundle device for bundle error messages It is safe to use the bundle device for error messages when we know we have a bundle. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index aef450f780f3..c70518ed504f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -123,7 +123,8 @@ gb_connection_create_range(struct greybus_host_device *hd, * about holding the connection lock. */ if (bundle && gb_connection_intf_find(bundle->intf, cport_id)) { - dev_err(parent, "cport 0x%04hx already connected\n", cport_id); + dev_err(&bundle->dev, "cport 0x%04hx already connected\n", + cport_id); return NULL; } -- cgit v1.2.3-59-g8ed1b From 21dcc9e50339642dfd241fa081a9b2ab6a34221a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 12:11:29 +0100 Subject: greybus: connection: fix potential null-deref in hd_cport_enable Use parent of host device for error messages as the connections bundle may be NULL. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c70518ed504f..e16ed4eafd03 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -196,7 +196,7 @@ static int gb_connection_hd_cport_enable(struct gb_connection *connection) ret = hd->driver->cport_enable(hd, connection->hd_cport_id); if (ret) { - dev_err(&connection->bundle->dev, + dev_err(hd->parent, "failed to enable host cport: %d\n", ret); return ret; } -- cgit v1.2.3-59-g8ed1b From a6e0363cb06989e9584820d3f5c0faad1cc35eae Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 12:11:28 +0100 Subject: greybus: connection: fix potential null-deref in connection create We allow connections without bundles so we must not use the bundle device for error messages after binding the protocol. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index e16ed4eafd03..c3cd69c0f3ed 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -170,7 +170,7 @@ gb_connection_create_range(struct greybus_host_device *hd, retval = gb_connection_bind_protocol(connection); if (retval) { - dev_err(&bundle->dev, "%d: failed to bind protocol: %d\n", + dev_err(parent, "%d: failed to bind protocol: %d\n", cport_id, retval); gb_connection_destroy(connection); return NULL; -- cgit v1.2.3-59-g8ed1b From 5245a90564b0c3f781e0c60cdbeb3b3b34c970df Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 12:11:27 +0100 Subject: greybus: connection: fix potential null-deref when binding protocol We can have connections without bundles so we must not use the bundle device for error messages when failing to look up a protocol. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c3cd69c0f3ed..294e72e8e1c0 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -510,7 +510,7 @@ int gb_connection_bind_protocol(struct gb_connection *connection) connection->major, connection->minor); if (!protocol) { - dev_warn(&connection->bundle->dev, + dev_warn(connection->hd->parent, "protocol 0x%02hhx version %hhu.%hhu not found\n", connection->protocol_id, connection->major, connection->minor); -- cgit v1.2.3-59-g8ed1b From fcfc762f17616e9c720214d85b88ed672e767499 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 12:11:26 +0100 Subject: greybus: connection: kill gb_hd_connections_exit Connections are destroyed as part of interface tear down. If we fail to do that properly it's a bug that should be fixed rather than papered over by a fall-back clean up function. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 -------- drivers/staging/greybus/connection.h | 1 - drivers/staging/greybus/core.c | 6 ------ 3 files changed, 15 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 294e72e8e1c0..de774b988213 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -489,14 +489,6 @@ void gb_connection_latency_tag_disable(struct gb_connection *connection) } EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable); -void gb_hd_connections_exit(struct greybus_host_device *hd) -{ - struct gb_connection *connection; - - list_for_each_entry(connection, &hd->connections, hd_links) - gb_connection_destroy(connection); -} - int gb_connection_bind_protocol(struct gb_connection *connection) { struct gb_protocol *protocol; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index af425a2dc5d2..06c7711da422 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -58,7 +58,6 @@ struct gb_connection *gb_connection_create_range(struct greybus_host_device *hd, u16 cport_id, u8 protocol_id, u32 ida_start, u32 ida_end); void gb_connection_destroy(struct gb_connection *connection); -void gb_hd_connections_exit(struct greybus_host_device *hd); void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index be75456a835d..726bf6480af3 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -242,12 +242,6 @@ void greybus_remove_hd(struct greybus_host_device *hd) if (hd->initial_svc_connection) gb_connection_destroy(hd->initial_svc_connection); - /* - * Make sure there are no leftovers that can potentially corrupt sysfs. - */ - if (WARN_ON(!list_empty(&hd->connections))) - gb_hd_connections_exit(hd); - kref_put_mutex(&hd->kref, free_hd, &hd_mutex); } EXPORT_SYMBOL_GPL(greybus_remove_hd); -- cgit v1.2.3-59-g8ed1b From 26b7ba66c1d698e7d377f61d310b5cfa1cfcf2a0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 12:11:25 +0100 Subject: greybus: endo: fix ida memory leak Fix yet another ida memory leak due to failure to call the destructor. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 726b1d309219..7f24c79230a5 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -547,4 +547,5 @@ int __init gb_endo_init(void) void gb_endo_exit(void) { + ida_destroy(&greybus_endo_id_map); } -- cgit v1.2.3-59-g8ed1b From 04fdd6a51a5b3b448047b83624dbfac85d3daf9a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 18:03:21 +0100 Subject: greybus: remove obsolete comment Remove obsolete comment about the driver model. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index edee11a95a17..219b2ff76558 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -57,16 +57,6 @@ #define CPORT_ID_MAX 4095 /* UniPro max id is 4095 */ #define CPORT_ID_BAD U16_MAX -/* For SP1 hardware, we are going to "hardcode" each device to have all logical - * blocks in order to be able to address them as one unified "unit". Then - * higher up layers will then be able to talk to them as one logical block and - * properly know how they are hooked together (i.e. which i2c port is on the - * same module as the gpio pins, etc.) - * - * So, put the "private" data structures here in greybus.h and link to them off - * of the "main" gb_module structure. - */ - struct greybus_host_device; /* Greybus "Host driver" structure, needed by a host controller driver to be -- cgit v1.2.3-59-g8ed1b From 7bc6faaca7d829d4e6f5d65909d5068f73b76bda Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 18:03:22 +0100 Subject: greybus: create host-device compilation unit Move everything host-device related to hd.c and hd.h. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/core.c | 100 --------------------------------- drivers/staging/greybus/greybus.h | 48 +--------------- drivers/staging/greybus/hd.c | 115 ++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/hd.h | 61 ++++++++++++++++++++ 5 files changed, 178 insertions(+), 147 deletions(-) create mode 100644 drivers/staging/greybus/hd.c create mode 100644 drivers/staging/greybus/hd.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index b8dc36b196aa..ba60430ef760 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,5 +1,6 @@ greybus-y := core.o \ debugfs.o \ + hd.o \ manifest.o \ endo.o \ module.o \ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 726bf6480af3..4396f90e65d6 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -146,106 +146,6 @@ void greybus_deregister_driver(struct greybus_driver *driver) } EXPORT_SYMBOL_GPL(greybus_deregister_driver); - -static DEFINE_MUTEX(hd_mutex); - -static void free_hd(struct kref *kref) -{ - struct greybus_host_device *hd; - - hd = container_of(kref, struct greybus_host_device, kref); - - ida_destroy(&hd->cport_id_map); - kfree(hd); - mutex_unlock(&hd_mutex); -} - -struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver, - struct device *parent, - size_t buffer_size_max, - size_t num_cports) -{ - struct greybus_host_device *hd; - - /* - * Validate that the driver implements all of the callbacks - * so that we don't have to every time we make them. - */ - if ((!driver->message_send) || (!driver->message_cancel)) { - pr_err("Must implement all greybus_host_driver callbacks!\n"); - return ERR_PTR(-EINVAL); - } - - if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) { - dev_err(parent, "greybus host-device buffers too small\n"); - return ERR_PTR(-EINVAL); - } - - if (num_cports == 0 || num_cports > CPORT_ID_MAX) { - dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports); - return ERR_PTR(-EINVAL); - } - - /* - * Make sure to never allocate messages larger than what the Greybus - * protocol supports. - */ - if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) { - dev_warn(parent, "limiting buffer size to %u\n", - GB_OPERATION_MESSAGE_SIZE_MAX); - buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX; - } - - hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL); - if (!hd) - return ERR_PTR(-ENOMEM); - - kref_init(&hd->kref); - hd->parent = parent; - hd->driver = driver; - INIT_LIST_HEAD(&hd->interfaces); - INIT_LIST_HEAD(&hd->connections); - ida_init(&hd->cport_id_map); - hd->buffer_size_max = buffer_size_max; - hd->num_cports = num_cports; - - /* - * Initialize AP's SVC protocol connection: - * - * This is required as part of early initialization of the host device - * as we need this connection in order to start any kind of message - * exchange between the AP and the SVC. SVC will start with a - * 'get-version' request followed by a 'svc-hello' message and at that - * time we will create a fully initialized svc-connection, as we need - * endo-id and AP's interface id for that. - */ - if (!gb_ap_svc_connection_create(hd)) { - kref_put_mutex(&hd->kref, free_hd, &hd_mutex); - return ERR_PTR(-ENOMEM); - } - - return hd; -} -EXPORT_SYMBOL_GPL(greybus_create_hd); - -void greybus_remove_hd(struct greybus_host_device *hd) -{ - /* - * Tear down all interfaces, modules, and the endo that is associated - * with this host controller before freeing the memory associated with - * the host controller. - */ - gb_interfaces_remove(hd); - gb_endo_remove(hd->endo); - - /* Is the SVC still using the partially uninitialized connection ? */ - if (hd->initial_svc_connection) - gb_connection_destroy(hd->initial_svc_connection); - - kref_put_mutex(&hd->kref, free_hd, &hd_mutex); -} -EXPORT_SYMBOL_GPL(greybus_remove_hd); - static int __init gb_init(void) { int retval; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 219b2ff76558..8eb63e020c2d 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -25,6 +25,7 @@ #include "greybus_manifest.h" #include "greybus_protocols.h" #include "manifest.h" +#include "hd.h" #include "endo.h" #include "svc.h" #include "firmware.h" @@ -57,53 +58,6 @@ #define CPORT_ID_MAX 4095 /* UniPro max id is 4095 */ #define CPORT_ID_BAD U16_MAX -struct greybus_host_device; - -/* Greybus "Host driver" structure, needed by a host controller driver to be - * able to handle both SVC control as well as "real" greybus messages - */ -struct greybus_host_driver { - size_t hd_priv_size; - - int (*cport_enable)(struct greybus_host_device *hd, u16 cport_id); - int (*cport_disable)(struct greybus_host_device *hd, u16 cport_id); - int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, - struct gb_message *message, gfp_t gfp_mask); - void (*message_cancel)(struct gb_message *message); - int (*latency_tag_enable)(struct greybus_host_device *hd, u16 cport_id); - int (*latency_tag_disable)(struct greybus_host_device *hd, - u16 cport_id); -}; - -struct greybus_host_device { - struct kref kref; - struct device *parent; - const struct greybus_host_driver *driver; - - struct list_head interfaces; - struct list_head connections; - struct ida cport_id_map; - - /* Number of CPorts supported by the UniPro IP */ - size_t num_cports; - - /* Host device buffer constraints */ - size_t buffer_size_max; - - struct gb_endo *endo; - struct gb_connection *initial_svc_connection; - struct gb_svc *svc; - - /* Private data for the host driver */ - unsigned long hd_priv[0] __aligned(sizeof(s64)); -}; - -struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, - struct device *parent, - size_t buffer_size_max, - size_t num_cports); -void greybus_remove_hd(struct greybus_host_device *hd); - struct greybus_driver { const char *name; diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c new file mode 100644 index 000000000000..3ac85077b431 --- /dev/null +++ b/drivers/staging/greybus/hd.c @@ -0,0 +1,115 @@ +/* + * Greybus Host Device + * + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +#include "greybus.h" + +static DEFINE_MUTEX(hd_mutex); + + +static void free_hd(struct kref *kref) +{ + struct greybus_host_device *hd; + + hd = container_of(kref, struct greybus_host_device, kref); + + ida_destroy(&hd->cport_id_map); + kfree(hd); + mutex_unlock(&hd_mutex); +} + +struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver, + struct device *parent, + size_t buffer_size_max, + size_t num_cports) +{ + struct greybus_host_device *hd; + + /* + * Validate that the driver implements all of the callbacks + * so that we don't have to every time we make them. + */ + if ((!driver->message_send) || (!driver->message_cancel)) { + pr_err("Must implement all greybus_host_driver callbacks!\n"); + return ERR_PTR(-EINVAL); + } + + if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) { + dev_err(parent, "greybus host-device buffers too small\n"); + return ERR_PTR(-EINVAL); + } + + if (num_cports == 0 || num_cports > CPORT_ID_MAX) { + dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports); + return ERR_PTR(-EINVAL); + } + + /* + * Make sure to never allocate messages larger than what the Greybus + * protocol supports. + */ + if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) { + dev_warn(parent, "limiting buffer size to %u\n", + GB_OPERATION_MESSAGE_SIZE_MAX); + buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX; + } + + hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL); + if (!hd) + return ERR_PTR(-ENOMEM); + + kref_init(&hd->kref); + hd->parent = parent; + hd->driver = driver; + INIT_LIST_HEAD(&hd->interfaces); + INIT_LIST_HEAD(&hd->connections); + ida_init(&hd->cport_id_map); + hd->buffer_size_max = buffer_size_max; + hd->num_cports = num_cports; + + /* + * Initialize AP's SVC protocol connection: + * + * This is required as part of early initialization of the host device + * as we need this connection in order to start any kind of message + * exchange between the AP and the SVC. SVC will start with a + * 'get-version' request followed by a 'svc-hello' message and at that + * time we will create a fully initialized svc-connection, as we need + * endo-id and AP's interface id for that. + */ + if (!gb_ap_svc_connection_create(hd)) { + kref_put_mutex(&hd->kref, free_hd, &hd_mutex); + return ERR_PTR(-ENOMEM); + } + + return hd; +} +EXPORT_SYMBOL_GPL(greybus_create_hd); + +void greybus_remove_hd(struct greybus_host_device *hd) +{ + /* + * Tear down all interfaces, modules, and the endo that is associated + * with this host controller before freeing the memory associated with + * the host controller. + */ + gb_interfaces_remove(hd); + gb_endo_remove(hd->endo); + + /* Is the SVC still using the partially uninitialized connection ? */ + if (hd->initial_svc_connection) + gb_connection_destroy(hd->initial_svc_connection); + + kref_put_mutex(&hd->kref, free_hd, &hd_mutex); +} +EXPORT_SYMBOL_GPL(greybus_remove_hd); diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h new file mode 100644 index 000000000000..df953d43a634 --- /dev/null +++ b/drivers/staging/greybus/hd.h @@ -0,0 +1,61 @@ +/* + * Greybus Host Device + * + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __HD_H +#define __HD_H + +struct greybus_host_device; +struct gb_message; + +/* Greybus "Host driver" structure, needed by a host controller driver to be + * able to handle both SVC control as well as "real" greybus messages + */ +struct greybus_host_driver { + size_t hd_priv_size; + + int (*cport_enable)(struct greybus_host_device *hd, u16 cport_id); + int (*cport_disable)(struct greybus_host_device *hd, u16 cport_id); + int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, + struct gb_message *message, gfp_t gfp_mask); + void (*message_cancel)(struct gb_message *message); + int (*latency_tag_enable)(struct greybus_host_device *hd, u16 cport_id); + int (*latency_tag_disable)(struct greybus_host_device *hd, + u16 cport_id); +}; + +struct greybus_host_device { + struct kref kref; + struct device *parent; + const struct greybus_host_driver *driver; + + struct list_head interfaces; + struct list_head connections; + struct ida cport_id_map; + + /* Number of CPorts supported by the UniPro IP */ + size_t num_cports; + + /* Host device buffer constraints */ + size_t buffer_size_max; + + struct gb_endo *endo; + struct gb_connection *initial_svc_connection; + struct gb_svc *svc; + + /* Private data for the host driver */ + unsigned long hd_priv[0] __aligned(sizeof(s64)); +}; + +struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, + struct device *parent, + size_t buffer_size_max, + size_t num_cports); +void greybus_remove_hd(struct greybus_host_device *hd); + +#endif /* __HD_H */ -- cgit v1.2.3-59-g8ed1b From 2537636abae5b81a1de5ad7511a29306f39b2167 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 18:03:23 +0100 Subject: greybus: hd: rename host-device structure Rename host-device structure gb_host_device to match our other structures. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 18 +++++++++--------- drivers/staging/greybus/connection.h | 6 +++--- drivers/staging/greybus/endo.c | 12 ++++++------ drivers/staging/greybus/endo.h | 6 +++--- drivers/staging/greybus/es1.c | 20 ++++++++++---------- drivers/staging/greybus/es2.c | 24 ++++++++++++------------ drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/greybus_trace.h | 8 ++++---- drivers/staging/greybus/hd.c | 10 +++++----- drivers/staging/greybus/hd.h | 19 +++++++++---------- drivers/staging/greybus/interface.c | 6 +++--- drivers/staging/greybus/interface.h | 10 +++++----- drivers/staging/greybus/module.c | 2 +- drivers/staging/greybus/module.h | 4 ++-- drivers/staging/greybus/operation.c | 14 +++++++------- drivers/staging/greybus/operation.h | 2 +- drivers/staging/greybus/svc.c | 14 +++++++------- drivers/staging/greybus/svc.h | 4 ++-- 18 files changed, 90 insertions(+), 91 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index de774b988213..96dabf7640a4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -17,7 +17,7 @@ static DEFINE_SPINLOCK(gb_connections_lock); static struct gb_connection * gb_connection_intf_find(struct gb_interface *intf, u16 cport_id) { - struct greybus_host_device *hd = intf->hd; + struct gb_host_device *hd = intf->hd; struct gb_connection *connection; list_for_each_entry(connection, &hd->connections, hd_links) @@ -28,7 +28,7 @@ gb_connection_intf_find(struct gb_interface *intf, u16 cport_id) } static struct gb_connection * -gb_connection_hd_find(struct greybus_host_device *hd, u16 cport_id) +gb_connection_hd_find(struct gb_host_device *hd, u16 cport_id) { struct gb_connection *connection; unsigned long flags; @@ -48,7 +48,7 @@ found: * Callback from the host driver to let us know that data has been * received on the bundle. */ -void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, +void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length) { struct gb_connection *connection; @@ -105,7 +105,7 @@ int svc_update_connection(struct gb_interface *intf, * pointer otherwise. */ struct gb_connection * -gb_connection_create_range(struct greybus_host_device *hd, +gb_connection_create_range(struct gb_host_device *hd, struct gb_bundle *bundle, struct device *parent, u16 cport_id, u8 protocol_id, u32 ida_start, u32 ida_end) @@ -188,7 +188,7 @@ err_remove_ida: static int gb_connection_hd_cport_enable(struct gb_connection *connection) { - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; int ret; if (!hd->driver->cport_enable) @@ -206,7 +206,7 @@ static int gb_connection_hd_cport_enable(struct gb_connection *connection) static void gb_connection_hd_cport_disable(struct gb_connection *connection) { - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; if (!hd->driver->cport_disable) return; @@ -258,7 +258,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, static int gb_connection_svc_connection_create(struct gb_connection *connection) { - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; struct gb_protocol *protocol = connection->protocol; struct gb_interface *intf; int ret; @@ -459,7 +459,7 @@ void gb_connection_destroy(struct gb_connection *connection) void gb_connection_latency_tag_enable(struct gb_connection *connection) { - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; int ret; if (!hd->driver->latency_tag_enable) @@ -475,7 +475,7 @@ EXPORT_SYMBOL_GPL(gb_connection_latency_tag_enable); void gb_connection_latency_tag_disable(struct gb_connection *connection) { - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; int ret; if (!hd->driver->latency_tag_disable) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 06c7711da422..6163082309c6 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -22,7 +22,7 @@ enum gb_connection_state { }; struct gb_connection { - struct greybus_host_device *hd; + struct gb_host_device *hd; struct gb_bundle *bundle; struct kref kref; u16 hd_cport_id; @@ -53,13 +53,13 @@ int svc_update_connection(struct gb_interface *intf, struct gb_connection *connection); struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id); -struct gb_connection *gb_connection_create_range(struct greybus_host_device *hd, +struct gb_connection *gb_connection_create_range(struct gb_host_device *hd, struct gb_bundle *bundle, struct device *parent, u16 cport_id, u8 protocol_id, u32 ida_start, u32 ida_end); void gb_connection_destroy(struct gb_connection *connection); -void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, +void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length); int gb_connection_bind_protocol(struct gb_connection *connection); diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 7f24c79230a5..df4afb232c5e 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -191,7 +191,7 @@ static bool single_cross_rib(u8 left_ribs, u8 right_ribs) * - Bit 1: 2nd rib location from top, i.e. between interface 2 and 3. * - Bit 2: 3rd rib location from top, i.e. between interface 3 and 4. */ -static bool validate_front_ribs(struct greybus_host_device *hd, +static bool validate_front_ribs(struct gb_host_device *hd, struct endo_layout *layout, bool mini, u16 endo_id) { @@ -254,7 +254,7 @@ static bool validate_front_ribs(struct greybus_host_device *hd, * its rotational orientation. We define one canonical id to represent a * particular endo configuration. */ -static bool validate_back_ribs(struct greybus_host_device *hd, +static bool validate_back_ribs(struct gb_host_device *hd, struct endo_layout *layout, u16 endo_id) { u8 max_ribs = layout->max_ribs; @@ -320,7 +320,7 @@ static bool validate_back_ribs(struct greybus_host_device *hd, * Validate the endo-id passed from SVC. Error out if its not a valid Endo, * else return structure representing ribs positions on front and back of Endo. */ -static int gb_endo_validate_id(struct greybus_host_device *hd, +static int gb_endo_validate_id(struct gb_host_device *hd, struct endo_layout *layout, u16 endo_id) { /* Validate Endo Size */ @@ -435,7 +435,7 @@ static int create_modules(struct gb_endo *endo) return 0; } -static int gb_endo_register(struct greybus_host_device *hd, +static int gb_endo_register(struct gb_host_device *hd, struct gb_endo *endo) { int dev_id; @@ -471,7 +471,7 @@ static int gb_endo_register(struct greybus_host_device *hd, return retval; } -struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id, +struct gb_endo *gb_endo_create(struct gb_host_device *hd, u16 endo_id, u8 ap_intf_id) { struct gb_endo *endo; @@ -524,7 +524,7 @@ void gb_endo_remove(struct gb_endo *endo) device_unregister(&endo->dev); } -int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id, +int greybus_endo_setup(struct gb_host_device *hd, u16 endo_id, u8 ap_intf_id) { struct gb_endo *endo; diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h index 0b5384d89f75..20a8649561d5 100644 --- a/drivers/staging/greybus/endo.h +++ b/drivers/staging/greybus/endo.h @@ -47,15 +47,15 @@ struct gb_endo { #define to_gb_endo(d) container_of(d, struct gb_endo, dev) /* Greybus "private" definitions */ -struct greybus_host_device; +struct gb_host_device; int gb_endo_init(void); void gb_endo_exit(void); -struct gb_endo *gb_endo_create(struct greybus_host_device *hd, +struct gb_endo *gb_endo_create(struct gb_host_device *hd, u16 endo_id, u8 ap_intf_id); void gb_endo_remove(struct gb_endo *endo); -int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id, +int greybus_endo_setup(struct gb_host_device *hd, u16 endo_id, u8 ap_intf_id); u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id); diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index ba025e782b0c..07e1a14e6726 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -60,7 +60,7 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); * es1_ap_dev - ES1 USB Bridge to AP structure * @usb_dev: pointer to the USB device we are. * @usb_intf: pointer to the USB interface we are bound to. - * @hd: pointer to our greybus_host_device structure + * @hd: pointer to our gb_host_device structure * @cport_in_endpoint: bulk in endpoint for CPort data * @cport-out_endpoint: bulk out endpoint for CPort data * @cport_in_urb: array of urbs for the CPort in messages @@ -75,7 +75,7 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); struct es1_ap_dev { struct usb_device *usb_dev; struct usb_interface *usb_intf; - struct greybus_host_device *hd; + struct gb_host_device *hd; __u8 cport_in_endpoint; __u8 cport_out_endpoint; @@ -88,7 +88,7 @@ struct es1_ap_dev { spinlock_t cport_out_urb_lock; }; -static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) +static inline struct es1_ap_dev *hd_to_es1(struct gb_host_device *hd) { return (struct es1_ap_dev *)&hd->hd_priv; } @@ -181,7 +181,7 @@ static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) * Returns zero if the message was successfully queued, or a negative errno * otherwise. */ -static int message_send(struct greybus_host_device *hd, u16 cport_id, +static int message_send(struct gb_host_device *hd, u16 cport_id, struct gb_message *message, gfp_t gfp_mask) { struct es1_ap_dev *es1 = hd_to_es1(hd); @@ -244,7 +244,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, */ static void message_cancel(struct gb_message *message) { - struct greybus_host_device *hd = message->operation->connection->hd; + struct gb_host_device *hd = message->operation->connection->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); struct urb *urb; int i; @@ -277,7 +277,7 @@ static void message_cancel(struct gb_message *message) usb_free_urb(urb); } -static int latency_tag_enable(struct greybus_host_device *hd, u16 cport_id) +static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) { int retval; struct es1_ap_dev *es1 = hd_to_es1(hd); @@ -301,7 +301,7 @@ static int latency_tag_enable(struct greybus_host_device *hd, u16 cport_id) return retval; } -static int latency_tag_disable(struct greybus_host_device *hd, u16 cport_id) +static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) { int retval; struct es1_ap_dev *es1 = hd_to_es1(hd); @@ -403,7 +403,7 @@ static void ap_disconnect(struct usb_interface *interface) static void cport_in_callback(struct urb *urb) { - struct greybus_host_device *hd = urb->context; + struct gb_host_device *hd = urb->context; struct device *dev = &urb->dev->dev; struct gb_operation_msg_hdr *header; int status = check_urb_status(urb); @@ -443,7 +443,7 @@ exit: static void cport_out_callback(struct urb *urb) { struct gb_message *message = urb->context; - struct greybus_host_device *hd = message->operation->connection->hd; + struct gb_host_device *hd = message->operation->connection->hd; struct es1_ap_dev *es1 = hd_to_es1(hd); int status = check_urb_status(urb); unsigned long flags; @@ -599,7 +599,7 @@ static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct es1_ap_dev *es1; - struct greybus_host_device *hd; + struct gb_host_device *hd; struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 870c9cb0e11f..8860f60f6fa4 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -83,7 +83,7 @@ struct es2_cport_out { * es2_ap_dev - ES2 USB Bridge to AP structure * @usb_dev: pointer to the USB device we are. * @usb_intf: pointer to the USB interface we are bound to. - * @hd: pointer to our greybus_host_device structure + * @hd: pointer to our gb_host_device structure * @cport_in: endpoint, urbs and buffer for cport in messages * @cport_out: endpoint for for cport out messages @@ -102,7 +102,7 @@ struct es2_cport_out { struct es2_ap_dev { struct usb_device *usb_dev; struct usb_interface *usb_intf; - struct greybus_host_device *hd; + struct gb_host_device *hd; struct es2_cport_in cport_in[NUM_BULKS]; struct es2_cport_out cport_out[NUM_BULKS]; @@ -131,7 +131,7 @@ struct cport_to_ep { __u8 endpoint_out; }; -static inline struct es2_ap_dev *hd_to_es2(struct greybus_host_device *hd) +static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) { return (struct es2_ap_dev *)&hd->hd_priv; } @@ -291,7 +291,7 @@ static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) * Returns zero if the message was successfully queued, or a negative errno * otherwise. */ -static int message_send(struct greybus_host_device *hd, u16 cport_id, +static int message_send(struct gb_host_device *hd, u16 cport_id, struct gb_message *message, gfp_t gfp_mask) { struct es2_ap_dev *es2 = hd_to_es2(hd); @@ -357,7 +357,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, */ static void message_cancel(struct gb_message *message) { - struct greybus_host_device *hd = message->operation->connection->hd; + struct gb_host_device *hd = message->operation->connection->hd; struct es2_ap_dev *es2 = hd_to_es2(hd); struct urb *urb; int i; @@ -390,7 +390,7 @@ static void message_cancel(struct gb_message *message) usb_free_urb(urb); } -static int cport_reset(struct greybus_host_device *hd, u16 cport_id) +static int cport_reset(struct gb_host_device *hd, u16 cport_id) { struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; @@ -410,7 +410,7 @@ static int cport_reset(struct greybus_host_device *hd, u16 cport_id) return 0; } -static int cport_enable(struct greybus_host_device *hd, u16 cport_id) +static int cport_enable(struct gb_host_device *hd, u16 cport_id) { int retval; @@ -423,7 +423,7 @@ static int cport_enable(struct greybus_host_device *hd, u16 cport_id) return 0; } -static int latency_tag_enable(struct greybus_host_device *hd, u16 cport_id) +static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) { int retval; struct es2_ap_dev *es2 = hd_to_es2(hd); @@ -447,7 +447,7 @@ static int latency_tag_enable(struct greybus_host_device *hd, u16 cport_id) return retval; } -static int latency_tag_disable(struct greybus_host_device *hd, u16 cport_id) +static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) { int retval; struct es2_ap_dev *es2 = hd_to_es2(hd); @@ -556,7 +556,7 @@ static void ap_disconnect(struct usb_interface *interface) static void cport_in_callback(struct urb *urb) { - struct greybus_host_device *hd = urb->context; + struct gb_host_device *hd = urb->context; struct device *dev = &urb->dev->dev; struct gb_operation_msg_hdr *header; int status = check_urb_status(urb); @@ -596,7 +596,7 @@ exit: static void cport_out_callback(struct urb *urb) { struct gb_message *message = urb->context; - struct greybus_host_device *hd = message->operation->connection->hd; + struct gb_host_device *hd = message->operation->connection->hd; struct es2_ap_dev *es2 = hd_to_es2(hd); int status = check_urb_status(urb); unsigned long flags; @@ -787,7 +787,7 @@ static int ap_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct es2_ap_dev *es2; - struct greybus_host_device *hd; + struct gb_host_device *hd; struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 8eb63e020c2d..1b6f56f9e506 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -129,7 +129,7 @@ static inline int is_gb_bundle(const struct device *dev) return dev->type == &greybus_bundle_type; } -static inline bool cport_id_valid(struct greybus_host_device *hd, u16 cport_id) +static inline bool cport_id_valid(struct gb_host_device *hd, u16 cport_id) { return cport_id != CPORT_ID_BAD && cport_id < hd->num_cports; } diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index e5e3cc0ce0a8..d38f9e0810a7 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -15,7 +15,7 @@ #include struct gb_message; -struct greybus_host_device; +struct gb_host_device; DECLARE_EVENT_CLASS(gb_message, @@ -108,7 +108,7 @@ DEFINE_EVENT(gb_message, gb_message_cancel_incoming, DECLARE_EVENT_CLASS(gb_host_device, - TP_PROTO(struct greybus_host_device *hd, u16 intf_cport_id, + TP_PROTO(struct gb_host_device *hd, u16 intf_cport_id, size_t payload_size), TP_ARGS(hd, intf_cport_id, payload_size), @@ -136,7 +136,7 @@ DECLARE_EVENT_CLASS(gb_host_device, */ DEFINE_EVENT(gb_host_device, gb_host_device_send, - TP_PROTO(struct greybus_host_device *hd, u16 intf_cport_id, + TP_PROTO(struct gb_host_device *hd, u16 intf_cport_id, size_t payload_size), TP_ARGS(hd, intf_cport_id, payload_size) @@ -149,7 +149,7 @@ DEFINE_EVENT(gb_host_device, gb_host_device_send, */ DEFINE_EVENT(gb_host_device, gb_host_device_recv, - TP_PROTO(struct greybus_host_device *hd, u16 intf_cport_id, + TP_PROTO(struct gb_host_device *hd, u16 intf_cport_id, size_t payload_size), TP_ARGS(hd, intf_cport_id, payload_size) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 3ac85077b431..a09a9200ed03 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -19,21 +19,21 @@ static DEFINE_MUTEX(hd_mutex); static void free_hd(struct kref *kref) { - struct greybus_host_device *hd; + struct gb_host_device *hd; - hd = container_of(kref, struct greybus_host_device, kref); + hd = container_of(kref, struct gb_host_device, kref); ida_destroy(&hd->cport_id_map); kfree(hd); mutex_unlock(&hd_mutex); } -struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver, +struct gb_host_device *greybus_create_hd(struct greybus_host_driver *driver, struct device *parent, size_t buffer_size_max, size_t num_cports) { - struct greybus_host_device *hd; + struct gb_host_device *hd; /* * Validate that the driver implements all of the callbacks @@ -96,7 +96,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver } EXPORT_SYMBOL_GPL(greybus_create_hd); -void greybus_remove_hd(struct greybus_host_device *hd) +void greybus_remove_hd(struct gb_host_device *hd) { /* * Tear down all interfaces, modules, and the endo that is associated diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index df953d43a634..122790d03b42 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -10,7 +10,7 @@ #ifndef __HD_H #define __HD_H -struct greybus_host_device; +struct gb_host_device; struct gb_message; /* Greybus "Host driver" structure, needed by a host controller driver to be @@ -19,17 +19,16 @@ struct gb_message; struct greybus_host_driver { size_t hd_priv_size; - int (*cport_enable)(struct greybus_host_device *hd, u16 cport_id); - int (*cport_disable)(struct greybus_host_device *hd, u16 cport_id); - int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, + int (*cport_enable)(struct gb_host_device *hd, u16 cport_id); + int (*cport_disable)(struct gb_host_device *hd, u16 cport_id); + int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); - int (*latency_tag_enable)(struct greybus_host_device *hd, u16 cport_id); - int (*latency_tag_disable)(struct greybus_host_device *hd, - u16 cport_id); + int (*latency_tag_enable)(struct gb_host_device *hd, u16 cport_id); + int (*latency_tag_disable)(struct gb_host_device *hd, u16 cport_id); }; -struct greybus_host_device { +struct gb_host_device { struct kref kref; struct device *parent; const struct greybus_host_driver *driver; @@ -52,10 +51,10 @@ struct greybus_host_device { unsigned long hd_priv[0] __aligned(sizeof(s64)); }; -struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, +struct gb_host_device *greybus_create_hd(struct greybus_host_driver *hd, struct device *parent, size_t buffer_size_max, size_t num_cports); -void greybus_remove_hd(struct greybus_host_device *hd); +void greybus_remove_hd(struct gb_host_device *hd); #endif /* __HD_H */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 5d238d3e907b..275a8c7df8e1 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -44,7 +44,7 @@ static DEFINE_SPINLOCK(gb_interfaces_lock); // FIXME, odds are you don't want to call this function, rework the caller to // not need it please. -struct gb_interface *gb_interface_find(struct greybus_host_device *hd, +struct gb_interface *gb_interface_find(struct gb_host_device *hd, u8 interface_id) { struct gb_interface *intf; @@ -120,7 +120,7 @@ int gb_create_bundle_connection(struct gb_interface *intf, u8 class) * Returns a pointer to the new interfce or a null pointer if a * failure occurs due to memory exhaustion. */ -struct gb_interface *gb_interface_create(struct greybus_host_device *hd, +struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id) { struct gb_module *module; @@ -199,7 +199,7 @@ void gb_interface_remove(struct gb_interface *intf) put_device(&module->dev); } -void gb_interfaces_remove(struct greybus_host_device *hd) +void gb_interfaces_remove(struct gb_host_device *hd) { struct gb_interface *intf, *temp; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 9bce94f680a4..248c9918a9cc 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -16,7 +16,7 @@ struct gb_interface { struct gb_control *control; struct list_head bundles; - struct list_head links; /* greybus_host_device->interfaces */ + struct list_head links; /* gb_host_device->interfaces */ struct list_head manifest_descs; u8 interface_id; /* Physical location within the Endo */ u8 device_id; /* Device id allocated for the interface block by the SVC */ @@ -35,7 +35,7 @@ struct gb_interface { u32 ara_prod_id; struct gb_module *module; - struct greybus_host_device *hd; + struct gb_host_device *hd; /* The interface needs to boot over unipro */ bool boot_over_unipro; @@ -55,15 +55,15 @@ static inline void *gb_interface_get_drvdata(struct gb_interface *intf) /* Greybus "private" definitions */ -struct gb_interface *gb_interface_find(struct greybus_host_device *hd, +struct gb_interface *gb_interface_find(struct gb_host_device *hd, u8 interface_id); -struct gb_interface *gb_interface_create(struct greybus_host_device *hd, +struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id); void gb_interface_destroy(struct gb_interface *intf); int gb_interface_init(struct gb_interface *intf, u8 device_id); void gb_interface_remove(struct gb_interface *intf); -void gb_interfaces_remove(struct greybus_host_device *hd); +void gb_interfaces_remove(struct gb_host_device *hd); int gb_create_bundle_connection(struct gb_interface *intf, u8 class); #endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 43e8bab91caf..e3d7a66e692e 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -108,7 +108,7 @@ static int module_find(struct device *dev, void *data) * Search the list of modules in the system. If one is found, return it, with * the reference count incremented. */ -struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id) +struct gb_module *gb_module_find(struct gb_host_device *hd, u8 module_id) { struct device *dev; struct gb_module *module = NULL; diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h index 3b3f421b244f..2b36fe9dfa7f 100644 --- a/drivers/staging/greybus/module.h +++ b/drivers/staging/greybus/module.h @@ -16,10 +16,10 @@ struct gb_module { }; #define to_gb_module(d) container_of(d, struct gb_module, dev) -struct greybus_host_device; +struct gb_host_device; /* Greybus "private" definitions */ -struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id); +struct gb_module *gb_module_find(struct gb_host_device *hd, u8 module_id); struct gb_module *gb_module_create(struct device *parent, u8 module_id); void gb_module_remove_all(struct gb_endo *endo); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 3e29d211fbcf..16438803fce2 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -210,7 +210,7 @@ static int gb_message_send(struct gb_message *message, gfp_t gfp) */ static void gb_message_cancel(struct gb_message *message) { - struct greybus_host_device *hd = message->operation->connection->hd; + struct gb_host_device *hd = message->operation->connection->hd; hd->driver->message_cancel(message); } @@ -269,7 +269,7 @@ static void gb_operation_work(struct work_struct *work) gb_operation_put(operation); } -static void gb_operation_message_init(struct greybus_host_device *hd, +static void gb_operation_message_init(struct gb_host_device *hd, struct gb_message *message, u16 operation_id, size_t payload_size, u8 type) { @@ -320,7 +320,7 @@ static void gb_operation_message_init(struct greybus_host_device *hd, * message payload / the message size */ static struct gb_message * -gb_operation_message_alloc(struct greybus_host_device *hd, u8 type, +gb_operation_message_alloc(struct gb_host_device *hd, u8 type, size_t payload_size, gfp_t gfp_flags) { struct gb_message *message; @@ -430,7 +430,7 @@ static u8 gb_operation_errno_map(int errno) bool gb_operation_response_alloc(struct gb_operation *operation, size_t response_size, gfp_t gfp) { - struct greybus_host_device *hd = operation->connection->hd; + struct gb_host_device *hd = operation->connection->hd; struct gb_operation_msg_hdr *request_header; struct gb_message *response; u8 type; @@ -482,7 +482,7 @@ gb_operation_create_common(struct gb_connection *connection, u8 type, size_t request_size, size_t response_size, unsigned long op_flags, gfp_t gfp_flags) { - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; struct gb_operation *operation; operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); @@ -548,7 +548,7 @@ EXPORT_SYMBOL_GPL(gb_operation_create); size_t gb_operation_get_payload_size_max(struct gb_connection *connection) { - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; return hd->buffer_size_max - sizeof(struct gb_operation_msg_hdr); } @@ -777,7 +777,7 @@ err_put: /* * This function is called when a message send request has completed. */ -void greybus_message_sent(struct greybus_host_device *hd, +void greybus_message_sent(struct gb_host_device *hd, struct gb_message *message, int status) { struct gb_operation *operation = message->operation; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 0f44ec844106..c3f7ce71b089 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -150,7 +150,7 @@ gb_operation_request_send_sync(struct gb_operation *operation) void gb_operation_cancel(struct gb_operation *operation, int errno); void gb_operation_cancel_incoming(struct gb_operation *operation, int errno); -void greybus_message_sent(struct greybus_host_device *hd, +void greybus_message_sent(struct gb_host_device *hd, struct gb_message *message, int status); int gb_operation_sync_timeout(struct gb_connection *connection, int type, diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index e5cd8971b2cf..c2633e25d43b 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -42,7 +42,7 @@ struct svc_hotplug { * This is a temporary connection, used only at initial bootup. */ struct gb_connection * -gb_ap_svc_connection_create(struct greybus_host_device *hd) +gb_ap_svc_connection_create(struct gb_host_device *hd) { struct gb_connection *connection; @@ -61,7 +61,7 @@ gb_ap_svc_connection_create(struct greybus_host_device *hd) * 'partially' initialized one svc connection. */ static struct gb_interface * -gb_ap_interface_create(struct greybus_host_device *hd, +gb_ap_interface_create(struct gb_host_device *hd, struct gb_connection *connection, u8 interface_id) { struct gb_interface *intf; @@ -183,7 +183,7 @@ EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set); */ static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf) { - struct greybus_host_device *hd = intf->hd; + struct gb_host_device *hd = intf->hd; int ret; u32 value; @@ -338,7 +338,7 @@ static int gb_svc_version_request(struct gb_operation *op) static int gb_svc_hello(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; struct gb_svc_hello_request *hello_request; struct gb_interface *intf; u16 endo_id; @@ -381,7 +381,7 @@ static int gb_svc_hello(struct gb_operation *op) static void svc_intf_remove(struct gb_connection *connection, struct gb_interface *intf) { - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; struct gb_svc *svc = connection->private; u8 intf_id = intf->interface_id; u8 device_id; @@ -408,7 +408,7 @@ static void svc_process_hotplug(struct work_struct *work) struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data; struct gb_connection *connection = svc_hotplug->connection; struct gb_svc *svc = connection->private; - struct greybus_host_device *hd = connection->hd; + struct gb_host_device *hd = connection->hd; struct gb_interface *intf; u8 intf_id, device_id; int ret; @@ -552,7 +552,7 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) { struct gb_message *request = op->request; struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload; - struct greybus_host_device *hd = op->connection->hd; + struct gb_host_device *hd = op->connection->hd; struct gb_interface *intf; u8 intf_id; diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 3357d317e9d9..d2f406fa798a 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -25,6 +25,6 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -struct gb_connection * -gb_ap_svc_connection_create(struct greybus_host_device *hd); +struct gb_connection *gb_ap_svc_connection_create(struct gb_host_device *hd); + #endif /* __SVC_H */ -- cgit v1.2.3-59-g8ed1b From a8cc020f3f8ec684f0c48d30524ae8198b56038b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 18:03:24 +0100 Subject: greybus: hd: rename host-driver structure Rename host-driver structure to gb_hd_driver to more clearly separate it from the host-device structure. Also remove an outdated description of the struct. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 2 +- drivers/staging/greybus/es2.c | 2 +- drivers/staging/greybus/hd.c | 4 ++-- drivers/staging/greybus/hd.h | 9 +++------ 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 07e1a14e6726..0156db263197 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -325,7 +325,7 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) return retval; } -static struct greybus_host_driver es1_driver = { +static struct gb_hd_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 8860f60f6fa4..903e3213453b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -471,7 +471,7 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) return retval; } -static struct greybus_host_driver es2_driver = { +static struct gb_hd_driver es2_driver = { .hd_priv_size = sizeof(struct es2_ap_dev), .message_send = message_send, .message_cancel = message_cancel, diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index a09a9200ed03..910e1d2bd49f 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -28,7 +28,7 @@ static void free_hd(struct kref *kref) mutex_unlock(&hd_mutex); } -struct gb_host_device *greybus_create_hd(struct greybus_host_driver *driver, +struct gb_host_device *greybus_create_hd(struct gb_hd_driver *driver, struct device *parent, size_t buffer_size_max, size_t num_cports) @@ -40,7 +40,7 @@ struct gb_host_device *greybus_create_hd(struct greybus_host_driver *driver, * so that we don't have to every time we make them. */ if ((!driver->message_send) || (!driver->message_cancel)) { - pr_err("Must implement all greybus_host_driver callbacks!\n"); + pr_err("Must implement all gb_hd_driver callbacks!\n"); return ERR_PTR(-EINVAL); } diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 122790d03b42..7aea0c54338d 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -13,10 +13,7 @@ struct gb_host_device; struct gb_message; -/* Greybus "Host driver" structure, needed by a host controller driver to be - * able to handle both SVC control as well as "real" greybus messages - */ -struct greybus_host_driver { +struct gb_hd_driver { size_t hd_priv_size; int (*cport_enable)(struct gb_host_device *hd, u16 cport_id); @@ -31,7 +28,7 @@ struct greybus_host_driver { struct gb_host_device { struct kref kref; struct device *parent; - const struct greybus_host_driver *driver; + const struct gb_hd_driver *driver; struct list_head interfaces; struct list_head connections; @@ -51,7 +48,7 @@ struct gb_host_device { unsigned long hd_priv[0] __aligned(sizeof(s64)); }; -struct gb_host_device *greybus_create_hd(struct greybus_host_driver *hd, +struct gb_host_device *greybus_create_hd(struct gb_hd_driver *driver, struct device *parent, size_t buffer_size_max, size_t num_cports); -- cgit v1.2.3-59-g8ed1b From d6e139bc15118ceb9173ee03e3f2db63b57e0f77 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Nov 2015 18:03:25 +0100 Subject: greybus: hd: use common prefix for exported functions Rename the exported functions using the common gb_-prefix. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 6 +++--- drivers/staging/greybus/es2.c | 6 +++--- drivers/staging/greybus/hd.c | 14 +++++++------- drivers/staging/greybus/hd.h | 10 +++++----- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 0156db263197..23860870d4f1 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -396,7 +396,7 @@ static void ap_disconnect(struct usb_interface *interface) usb_set_intfdata(interface, NULL); udev = es1->usb_dev; - greybus_remove_hd(es1->hd); + gb_hd_remove(es1->hd); usb_put_dev(udev); } @@ -613,8 +613,8 @@ static int ap_probe(struct usb_interface *interface, udev = usb_get_dev(interface_to_usbdev(interface)); - hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX, - CPORT_COUNT); + hd = gb_hd_create(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX, + CPORT_COUNT); if (IS_ERR(hd)) { usb_put_dev(udev); return PTR_ERR(hd); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 903e3213453b..1e786a6ef7d7 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -548,7 +548,7 @@ static void ap_disconnect(struct usb_interface *interface) usb_set_intfdata(interface, NULL); udev = es2->usb_dev; - greybus_remove_hd(es2->hd); + gb_hd_remove(es2->hd); kfree(es2->cport_to_ep); usb_put_dev(udev); @@ -807,8 +807,8 @@ static int ap_probe(struct usb_interface *interface, return num_cports; } - hd = greybus_create_hd(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX, - num_cports); + hd = gb_hd_create(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX, + num_cports); if (IS_ERR(hd)) { usb_put_dev(udev); return PTR_ERR(hd); diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 910e1d2bd49f..6f29eb49993a 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -28,10 +28,10 @@ static void free_hd(struct kref *kref) mutex_unlock(&hd_mutex); } -struct gb_host_device *greybus_create_hd(struct gb_hd_driver *driver, - struct device *parent, - size_t buffer_size_max, - size_t num_cports) +struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, + struct device *parent, + size_t buffer_size_max, + size_t num_cports) { struct gb_host_device *hd; @@ -94,9 +94,9 @@ struct gb_host_device *greybus_create_hd(struct gb_hd_driver *driver, return hd; } -EXPORT_SYMBOL_GPL(greybus_create_hd); +EXPORT_SYMBOL_GPL(gb_hd_create); -void greybus_remove_hd(struct gb_host_device *hd) +void gb_hd_remove(struct gb_host_device *hd) { /* * Tear down all interfaces, modules, and the endo that is associated @@ -112,4 +112,4 @@ void greybus_remove_hd(struct gb_host_device *hd) kref_put_mutex(&hd->kref, free_hd, &hd_mutex); } -EXPORT_SYMBOL_GPL(greybus_remove_hd); +EXPORT_SYMBOL_GPL(gb_hd_remove); diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 7aea0c54338d..91fcccb46df1 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -48,10 +48,10 @@ struct gb_host_device { unsigned long hd_priv[0] __aligned(sizeof(s64)); }; -struct gb_host_device *greybus_create_hd(struct gb_hd_driver *driver, - struct device *parent, - size_t buffer_size_max, - size_t num_cports); -void greybus_remove_hd(struct gb_host_device *hd); +struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, + struct device *parent, + size_t buffer_size_max, + size_t num_cports); +void gb_hd_remove(struct gb_host_device *hd); #endif /* __HD_H */ -- cgit v1.2.3-59-g8ed1b From ddc88eff2b0848e9c95bc4c39b53c52ed7991285 Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Wed, 4 Nov 2015 09:47:21 -0700 Subject: greybus: audio: Remove I2S Bridged-PHY Protcol based audio driver The Greybus I2S Bridged-PHY Protocol is now deprecated so remove the audio driver that is based on it. CC: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 6 +- drivers/staging/greybus/audio-dai.c | 112 -------- drivers/staging/greybus/audio-gb-cmds.c | 188 ------------- drivers/staging/greybus/audio-pcm.c | 308 --------------------- drivers/staging/greybus/audio.c | 470 -------------------------------- drivers/staging/greybus/audio.h | 113 -------- drivers/staging/greybus/gpbridge.c | 7 - drivers/staging/greybus/protocol.h | 3 - 8 files changed, 1 insertion(+), 1206 deletions(-) delete mode 100644 drivers/staging/greybus/audio-dai.c delete mode 100644 drivers/staging/greybus/audio-gb-cmds.c delete mode 100644 drivers/staging/greybus/audio-pcm.c delete mode 100644 drivers/staging/greybus/audio.c delete mode 100644 drivers/staging/greybus/audio.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index ba60430ef760..cf4cf2178ed6 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -21,11 +21,7 @@ gb-phy-y := gpbridge.o \ hid.o \ i2c.o \ spi.o \ - usb.o \ - audio.o \ - audio-pcm.o \ - audio-dai.o \ - audio-gb-cmds.o + usb.o # Prefix all modules with gb- gb-vibrator-y := vibrator.o diff --git a/drivers/staging/greybus/audio-dai.c b/drivers/staging/greybus/audio-dai.c deleted file mode 100644 index 9b162bfc2d17..000000000000 --- a/drivers/staging/greybus/audio-dai.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Greybus audio Digital Audio Interface (DAI) driver - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "greybus.h" -#include "audio.h" - -/* - * This is the greybus cpu dai logic. It really doesn't do much - * other then provide the TRIGGER_START/STOP hooks that start - * and stop the timer sending audio data in the pcm logic. - */ - - -static int gb_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct gb_snd *snd_dev; - - - snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - gb_pcm_hrtimer_start(snd_dev); - break; - case SNDRV_PCM_TRIGGER_STOP: - gb_pcm_hrtimer_stop(snd_dev); - break; - default: - return -EINVAL; - } - return 0; -} - -/* - * XXX This is annoying, if we don't have a set_fmt function - * the subsystem returns -ENOTSUPP, which causes applications - * to fail, so add a dummy function here. - */ -static int gb_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - return 0; -} - -static const struct snd_soc_dai_ops gb_dai_ops = { - .trigger = gb_dai_trigger, - .set_fmt = gb_dai_set_fmt, -}; - -static struct snd_soc_dai_driver gb_cpu_dai = { - .name = "gb-cpu-dai", - .playback = { - .rates = GB_RATES, - .formats = GB_FMTS, - .channels_min = 1, - .channels_max = 2, - }, - .ops = &gb_dai_ops, -}; - -static const struct snd_soc_component_driver gb_soc_component = { - .name = "gb-component", -}; - -static int gb_plat_probe(struct platform_device *pdev) -{ - struct gb_snd *snd_dev; - int ret; - - snd_dev = (struct gb_snd *)pdev->dev.platform_data; - dev_set_drvdata(&pdev->dev, snd_dev); - - ret = snd_soc_register_component(&pdev->dev, &gb_soc_component, - &gb_cpu_dai, 1); - return ret; -} - -static int gb_plat_remove(struct platform_device *pdev) -{ - snd_soc_unregister_component(&pdev->dev); - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -struct platform_driver gb_audio_plat_driver = { - .driver = { - .name = "gb-dai-audio", - }, - .probe = gb_plat_probe, - .remove = gb_plat_remove, -}; diff --git a/drivers/staging/greybus/audio-gb-cmds.c b/drivers/staging/greybus/audio-gb-cmds.c deleted file mode 100644 index 112aed994fd2..000000000000 --- a/drivers/staging/greybus/audio-gb-cmds.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Greybus audio commands - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include - -#include "greybus.h" -#include "audio.h" - -/*********************************** - * GB I2S helper functions - ***********************************/ -int gb_i2s_mgmt_activate_cport(struct gb_connection *connection, - uint16_t cport) -{ - struct gb_i2s_mgmt_activate_cport_request request; - - memset(&request, 0, sizeof(request)); - request.cport = cpu_to_le16(cport); - - return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_ACTIVATE_CPORT, - &request, sizeof(request), NULL, 0); -} - -int gb_i2s_mgmt_deactivate_cport(struct gb_connection *connection, - uint16_t cport) -{ - struct gb_i2s_mgmt_deactivate_cport_request request; - - memset(&request, 0, sizeof(request)); - request.cport = cpu_to_le16(cport); - - return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT, - &request, sizeof(request), NULL, 0); -} - -int gb_i2s_mgmt_get_supported_configurations( - struct gb_connection *connection, - struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg, - size_t size) -{ - return gb_operation_sync(connection, - GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS, - NULL, 0, get_cfg, size); -} - -int gb_i2s_mgmt_set_configuration(struct gb_connection *connection, - struct gb_i2s_mgmt_set_configuration_request *set_cfg) -{ - return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_SET_CONFIGURATION, - set_cfg, sizeof(*set_cfg), NULL, 0); -} - -int gb_i2s_mgmt_set_samples_per_message( - struct gb_connection *connection, - uint16_t samples_per_message) -{ - struct gb_i2s_mgmt_set_samples_per_message_request request; - - memset(&request, 0, sizeof(request)); - request.samples_per_message = cpu_to_le16(samples_per_message); - - return gb_operation_sync(connection, - GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE, - &request, sizeof(request), NULL, 0); -} - -int gb_i2s_mgmt_get_cfgs(struct gb_snd *snd_dev, - struct gb_connection *connection) -{ - struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg; - size_t size; - int ret; - - size = sizeof(*get_cfg) + - (CONFIG_COUNT_MAX * sizeof(get_cfg->config[0])); - - get_cfg = kzalloc(size, GFP_KERNEL); - if (!get_cfg) - return -ENOMEM; - - ret = gb_i2s_mgmt_get_supported_configurations(connection, get_cfg, - size); - if (ret) { - pr_err("get_supported_config failed: %d\n", ret); - goto err_free_get_cfg; - } - - snd_dev->i2s_configs = get_cfg; - - return 0; - -err_free_get_cfg: - kfree(get_cfg); - return ret; -} - -void gb_i2s_mgmt_free_cfgs(struct gb_snd *snd_dev) -{ - kfree(snd_dev->i2s_configs); - snd_dev->i2s_configs = NULL; -} - -int gb_i2s_mgmt_set_cfg(struct gb_snd *snd_dev, int rate, int chans, - int bytes_per_chan, int is_le) -{ - struct gb_i2s_mgmt_set_configuration_request set_cfg; - struct gb_i2s_mgmt_configuration *cfg; - int i, ret; - u8 byte_order = GB_I2S_MGMT_BYTE_ORDER_NA; - - if (bytes_per_chan > 1) { - if (is_le) - byte_order = GB_I2S_MGMT_BYTE_ORDER_LE; - else - byte_order = GB_I2S_MGMT_BYTE_ORDER_BE; - } - - for (i = 0, cfg = snd_dev->i2s_configs->config; - i < CONFIG_COUNT_MAX; - i++, cfg++) { - if ((cfg->sample_frequency == cpu_to_le32(rate)) && - (cfg->num_channels == chans) && - (cfg->bytes_per_channel == bytes_per_chan) && - (cfg->byte_order & byte_order) && - (cfg->ll_protocol & - cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S)) && - (cfg->ll_mclk_role & GB_I2S_MGMT_ROLE_MASTER) && - (cfg->ll_bclk_role & GB_I2S_MGMT_ROLE_MASTER) && - (cfg->ll_wclk_role & GB_I2S_MGMT_ROLE_MASTER) && - (cfg->ll_wclk_polarity & GB_I2S_MGMT_POLARITY_NORMAL) && - (cfg->ll_wclk_change_edge & GB_I2S_MGMT_EDGE_FALLING) && - (cfg->ll_wclk_tx_edge & GB_I2S_MGMT_EDGE_RISING) && - (cfg->ll_wclk_rx_edge & GB_I2S_MGMT_EDGE_FALLING) && - (cfg->ll_data_offset == 1)) - break; - } - - if (i >= CONFIG_COUNT_MAX) { - pr_err("No valid configuration\n"); - return -EINVAL; - } - - memcpy(&set_cfg, cfg, sizeof(set_cfg)); - set_cfg.config.byte_order = byte_order; - set_cfg.config.ll_protocol = cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S); - set_cfg.config.ll_mclk_role = GB_I2S_MGMT_ROLE_MASTER; - set_cfg.config.ll_bclk_role = GB_I2S_MGMT_ROLE_MASTER; - set_cfg.config.ll_wclk_role = GB_I2S_MGMT_ROLE_MASTER; - set_cfg.config.ll_wclk_polarity = GB_I2S_MGMT_POLARITY_NORMAL; - set_cfg.config.ll_wclk_change_edge = GB_I2S_MGMT_EDGE_FALLING; - set_cfg.config.ll_wclk_tx_edge = GB_I2S_MGMT_EDGE_RISING; - set_cfg.config.ll_wclk_rx_edge = GB_I2S_MGMT_EDGE_FALLING; - - ret = gb_i2s_mgmt_set_configuration(snd_dev->mgmt_connection, &set_cfg); - if (ret) - pr_err("set_configuration failed: %d\n", ret); - - return ret; -} - -int gb_i2s_send_data(struct gb_connection *connection, - void *req_buf, void *source_addr, - size_t len, int sample_num) -{ - struct gb_i2s_send_data_request *gb_req; - int ret; - - gb_req = req_buf; - gb_req->sample_number = cpu_to_le32(sample_num); - - memcpy((void *)&gb_req->data[0], source_addr, len); - - if (len < MAX_SEND_DATA_LEN) - for (; len < MAX_SEND_DATA_LEN; len++) - gb_req->data[len] = gb_req->data[len - SAMPLE_SIZE]; - - gb_req->size = cpu_to_le32(len); - - ret = gb_operation_sync(connection, GB_I2S_DATA_TYPE_SEND_DATA, - (void *) gb_req, SEND_DATA_BUF_LEN, NULL, 0); - return ret; -} diff --git a/drivers/staging/greybus/audio-pcm.c b/drivers/staging/greybus/audio-pcm.c deleted file mode 100644 index e62152a7be9e..000000000000 --- a/drivers/staging/greybus/audio-pcm.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Greybus audio Pulse Code Modulation (PCM) driver - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "greybus.h" -#include "audio.h" - -/* - * timer/workqueue logic for pushing pcm data. - * - * Since when we are playing audio, we don't get any - * status or feedback from the codec, we have to use a - * hrtimer to trigger sending data to the remote codec. - * However since the hrtimer runs in irq context, so we - * have to schedule a workqueue to actually send the - * greybus data. - */ - -static void gb_pcm_work(struct work_struct *work) -{ - struct gb_snd *snd_dev = container_of(work, struct gb_snd, work); - struct snd_pcm_substream *substream = snd_dev->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int stride, frames, oldptr; - int period_elapsed, ret; - char *address; - long len; - - if (!snd_dev) - return; - - if (!atomic_read(&snd_dev->running)) { - if (snd_dev->cport_active) { - ret = gb_i2s_mgmt_deactivate_cport( - snd_dev->mgmt_connection, - snd_dev->i2s_tx_connection->intf_cport_id); - if (ret) /* XXX Do what else with failure? */ - pr_err("deactivate_cport failed: %d\n", ret); - - snd_dev->cport_active = false; - snd_dev->send_data_sample_count = 0; - } - - return; - } else if (!snd_dev->cport_active) { - ret = gb_i2s_mgmt_activate_cport(snd_dev->mgmt_connection, - snd_dev->i2s_tx_connection->intf_cport_id); - if (ret) - pr_err("activate_cport failed: %d\n", ret); - - snd_dev->cport_active = true; - } - - address = runtime->dma_area + snd_dev->hwptr_done; - - len = frames_to_bytes(runtime, - runtime->buffer_size) - snd_dev->hwptr_done; - len = min(len, MAX_SEND_DATA_LEN); - gb_i2s_send_data(snd_dev->i2s_tx_connection, snd_dev->send_data_req_buf, - address, len, snd_dev->send_data_sample_count); - - snd_dev->send_data_sample_count += CONFIG_SAMPLES_PER_MSG; - - stride = runtime->frame_bits >> 3; - frames = len/stride; - - snd_pcm_stream_lock(substream); - oldptr = snd_dev->hwptr_done; - snd_dev->hwptr_done += len; - if (snd_dev->hwptr_done >= runtime->buffer_size * stride) - snd_dev->hwptr_done -= runtime->buffer_size * stride; - - frames = (len + (oldptr % stride)) / stride; - - period_elapsed = 0; - snd_dev->transfer_done += frames; - if (snd_dev->transfer_done >= runtime->period_size) { - snd_dev->transfer_done -= runtime->period_size; - period_elapsed = 1; - } - - snd_pcm_stream_unlock(substream); - if (period_elapsed) - snd_pcm_period_elapsed(snd_dev->substream); -} - -static enum hrtimer_restart gb_pcm_timer_function(struct hrtimer *hrtimer) -{ - struct gb_snd *snd_dev = container_of(hrtimer, struct gb_snd, timer); - - if (!atomic_read(&snd_dev->running)) - return HRTIMER_NORESTART; - queue_work(snd_dev->workqueue, &snd_dev->work); - hrtimer_forward_now(hrtimer, ns_to_ktime(CONFIG_PERIOD_NS)); - return HRTIMER_RESTART; -} - -void gb_pcm_hrtimer_start(struct gb_snd *snd_dev) -{ - atomic_set(&snd_dev->running, 1); - queue_work(snd_dev->workqueue, &snd_dev->work); /* Activates CPort */ - hrtimer_start(&snd_dev->timer, ns_to_ktime(CONFIG_PERIOD_NS), - HRTIMER_MODE_REL); -} - -void gb_pcm_hrtimer_stop(struct gb_snd *snd_dev) -{ - atomic_set(&snd_dev->running, 0); - hrtimer_cancel(&snd_dev->timer); - queue_work(snd_dev->workqueue, &snd_dev->work); /* Deactivates CPort */ -} - -static int gb_pcm_hrtimer_init(struct gb_snd *snd_dev) -{ - hrtimer_init(&snd_dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - snd_dev->timer.function = gb_pcm_timer_function; - atomic_set(&snd_dev->running, 0); - snd_dev->workqueue = alloc_workqueue("gb-audio", WQ_HIGHPRI, 0); - if (!snd_dev->workqueue) - return -ENOMEM; - INIT_WORK(&snd_dev->work, gb_pcm_work); - return 0; -} - - -/* - * Core gb pcm structure - */ -static struct snd_pcm_hardware gb_plat_pcm_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, - .formats = GB_FMTS, - .rates = GB_RATES, - .rate_min = 8000, - .rate_max = GB_SAMPLE_RATE, - .channels_min = 1, - .channels_max = 2, - /* XXX - All the values below are junk */ - .buffer_bytes_max = 64 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 8192, - .periods_min = 2, - .periods_max = 32, -}; - -static snd_pcm_uframes_t gb_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct gb_snd *snd_dev; - - snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); - - return snd_dev->hwptr_done / (substream->runtime->frame_bits >> 3); -} - -static int gb_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct gb_snd *snd_dev; - - snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); - snd_dev->hwptr_done = 0; - snd_dev->transfer_done = 0; - return 0; -} - -static unsigned int rates[] = {GB_SAMPLE_RATE}; -static struct snd_pcm_hw_constraint_list constraints_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, -}; - -static int gb_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct gb_snd *snd_dev; - unsigned long flags; - int ret; - - snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); - - spin_lock_irqsave(&snd_dev->lock, flags); - runtime->private_data = snd_dev; - snd_dev->substream = substream; - ret = gb_pcm_hrtimer_init(snd_dev); - spin_unlock_irqrestore(&snd_dev->lock, flags); - - if (ret) - return ret; - - snd_soc_set_runtime_hwparams(substream, &gb_plat_pcm_hardware); - - ret = snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_rates); - if (ret < 0) - return ret; - - return snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); -} - -static int gb_pcm_close(struct snd_pcm_substream *substream) -{ - substream->runtime->private_data = NULL; - return 0; -} - -static int gb_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct gb_snd *snd_dev; - int rate, chans, bytes_per_chan, is_le, ret; - - snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); - - rate = params_rate(hw_params); - chans = params_channels(hw_params); - bytes_per_chan = snd_pcm_format_width(params_format(hw_params)) / 8; - is_le = snd_pcm_format_little_endian(params_format(hw_params)); - - ret = gb_i2s_mgmt_set_cfg(snd_dev, rate, chans, bytes_per_chan, is_le); - if (ret) - return ret; - - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); -} - -static int gb_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - -static struct snd_pcm_ops gb_pcm_ops = { - .open = gb_pcm_open, - .close = gb_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = gb_pcm_hw_params, - .hw_free = gb_pcm_hw_free, - .prepare = gb_pcm_prepare, - .pointer = gb_pcm_pointer, -}; - -static void gb_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - -static int gb_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_pcm *pcm = rtd->pcm; - - return snd_pcm_lib_preallocate_pages_for_all( - pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); -} - -static struct snd_soc_platform_driver gb_soc_platform = { - .ops = &gb_pcm_ops, - .pcm_new = gb_pcm_new, - .pcm_free = gb_pcm_free, -}; - -static int gb_soc_platform_probe(struct platform_device *pdev) -{ - return snd_soc_register_platform(&pdev->dev, &gb_soc_platform); -} - -static int gb_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -struct platform_driver gb_audio_pcm_driver = { - .driver = { - .name = "gb-pcm-audio", - .owner = THIS_MODULE, - }, - .probe = gb_soc_platform_probe, - .remove = gb_soc_platform_remove, -}; diff --git a/drivers/staging/greybus/audio.c b/drivers/staging/greybus/audio.c deleted file mode 100644 index 684229ccab6c..000000000000 --- a/drivers/staging/greybus/audio.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Greybus audio driver - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "greybus.h" -#include "audio.h" - - -#define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data" -#define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt" - -#define RT5647_I2C_ADAPTER_NR 6 -#define RT5647_I2C_ADDR 0x1b - -/* - * gb_snd management functions - */ -static DEFINE_SPINLOCK(gb_snd_list_lock); -static LIST_HEAD(gb_snd_list); -static int device_count; - -static struct gb_snd *gb_find_snd(int bundle_id) -{ - struct gb_snd *tmp, *ret = NULL; - unsigned long flags; - - spin_lock_irqsave(&gb_snd_list_lock, flags); - list_for_each_entry(tmp, &gb_snd_list, list) - if (tmp->gb_bundle_id == bundle_id) { - ret = tmp; - break; - } - spin_unlock_irqrestore(&gb_snd_list_lock, flags); - return ret; -} - -static struct gb_snd *gb_get_snd(int bundle_id) -{ - struct gb_snd *snd_dev; - unsigned long flags; - - snd_dev = gb_find_snd(bundle_id); - if (snd_dev) - return snd_dev; - - snd_dev = kzalloc(sizeof(*snd_dev), GFP_KERNEL); - if (!snd_dev) - return NULL; - - spin_lock_init(&snd_dev->lock); - snd_dev->device_count = device_count++; - snd_dev->gb_bundle_id = bundle_id; - spin_lock_irqsave(&gb_snd_list_lock, flags); - list_add(&snd_dev->list, &gb_snd_list); - spin_unlock_irqrestore(&gb_snd_list_lock, flags); - return snd_dev; -} - -static void gb_free_snd(struct gb_snd *snd) -{ - unsigned long flags; - - spin_lock_irqsave(&gb_snd_list_lock, flags); - if (!snd->i2s_tx_connection && - !snd->mgmt_connection) { - list_del(&snd->list); - spin_unlock_irqrestore(&gb_snd_list_lock, flags); - kfree(snd); - } else { - spin_unlock_irqrestore(&gb_snd_list_lock, flags); - } -} - - - - -/* - * This is the ASoC simple card binds the platform codec, - * cpu-dai and codec-dai togheter - */ -struct gb_card_info_object { - struct asoc_simple_card_info card_info; - char codec_name[255]; - char platform_name[255]; - char dai_name[255]; -}; - - -static struct asoc_simple_card_info *setup_card_info(int device_count) -{ - struct gb_card_info_object *obj; - - obj = kzalloc(sizeof(struct gb_card_info_object), GFP_KERNEL); - if (!obj) - return NULL; - - obj->card_info.name = "Greybus Audio Module"; - obj->card_info.card = "gb-card"; - obj->card_info.codec = obj->codec_name; - obj->card_info.platform = obj->platform_name; - obj->card_info.cpu_dai.name = obj->dai_name; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) - obj->card_info.cpu_dai.fmt = SND_SOC_DAIFMT_CBM_CFM; -#endif -#if USE_RT5645 - obj->card_info.daifmt = SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_I2S; - sprintf(obj->codec_name, "rt5645.%d-%04x", RT5647_I2C_ADAPTER_NR, - RT5647_I2C_ADDR); - obj->card_info.codec_dai.name = "rt5645-aif1"; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) - obj->card_info.codec_dai.fmt = SND_SOC_DAIFMT_CBS_CFS; -#endif - obj->card_info.codec_dai.sysclk = 12288000; -#else - sprintf(obj->codec_name, "spdif-dit"); - obj->card_info.codec_dai.name = "dit-hifi"; -#endif - sprintf(obj->platform_name, "gb-pcm-audio.%i", device_count); - sprintf(obj->dai_name, "gb-dai-audio.%i", device_count); - - return &obj->card_info; -} - -static void free_card_info(struct asoc_simple_card_info *ci) -{ - struct gb_card_info_object *obj; - - obj = container_of(ci, struct gb_card_info_object, card_info); - kfree(obj); -} - - -/* - * XXX this is sort of cruddy but I get warnings if - * we don't have dev.release handler set. - */ -static void default_release(struct device *dev) -{ -} - -/* - * GB connection hooks - */ -static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) -{ - struct gb_snd *snd_dev; - struct platform_device *codec, *dai; - struct asoc_simple_card_info *simple_card; -#if USE_RT5645 - struct i2c_board_info rt5647_info; - struct i2c_adapter *i2c_adap; -#endif - unsigned long flags; - int ret; - - snd_dev = gb_get_snd(connection->bundle->id); - if (!snd_dev) - return -ENOMEM; - - codec = platform_device_register_simple("spdif-dit", -1, NULL, 0); - if (!codec) { - ret = -ENOMEM; - goto out; - } - - dai = platform_device_register_simple("gb-pcm-audio", snd_dev->device_count, NULL, 0); - if (!dai) { - ret = -ENOMEM; - goto out; - } - - simple_card = setup_card_info(snd_dev->device_count); - if (!simple_card) { - ret = -ENOMEM; - goto out; - } - - spin_lock_irqsave(&snd_dev->lock, flags); - snd_dev->card.name = "asoc-simple-card"; - snd_dev->card.id = snd_dev->device_count; - snd_dev->card.dev.release = default_release; /* XXX - suspicious */ - - snd_dev->cpu_dai.name = "gb-dai-audio"; - snd_dev->cpu_dai.id = snd_dev->device_count; - snd_dev->cpu_dai.dev.release = default_release; /* XXX - suspicious */ - - - snd_dev->simple_card_info = simple_card; - snd_dev->card.dev.platform_data = simple_card; - - snd_dev->codec = codec; - snd_dev->i2s_tx_connection = connection; - snd_dev->cpu_dai.dev.platform_data = snd_dev; - snd_dev->i2s_tx_connection->private = snd_dev; - spin_unlock_irqrestore(&snd_dev->lock, flags); - - ret = platform_device_register(&snd_dev->cpu_dai); - if (ret) { - pr_err("cpu_dai platform_device register failed\n"); - goto out_dai; - } - - ret = platform_device_register(&snd_dev->card); - if (ret) { - pr_err("card platform_device register failed\n"); - goto out_card; - } - -#if USE_RT5645 - rt5647_info.addr = RT5647_I2C_ADDR; - strlcpy(rt5647_info.type, "rt5647", I2C_NAME_SIZE); - - i2c_adap = i2c_get_adapter(RT5647_I2C_ADAPTER_NR); - if (!i2c_adap) { - pr_err("codec unavailable\n"); - ret = -ENODEV; - goto out_get_ver; - } - - snd_dev->rt5647 = i2c_new_device(i2c_adap, &rt5647_info); - if (!snd_dev->rt5647) { - pr_err("can't create rt5647 i2c device\n"); - goto out_get_ver; - } -#endif - - return 0; - -#if USE_RT5645 -out_get_ver: - platform_device_unregister(&snd_dev->card); -#endif -out_card: - platform_device_unregister(&snd_dev->cpu_dai); -out_dai: - platform_device_unregister(codec); -out: - gb_free_snd(snd_dev); - return ret; -} - -static void gb_i2s_transmitter_connection_exit(struct gb_connection *connection) -{ - struct gb_snd *snd_dev; - - snd_dev = (struct gb_snd *)connection->private; - -#if USE_RT5645 - i2c_unregister_device(snd_dev->rt5647); -#endif - - platform_device_unregister(&snd_dev->card); - platform_device_unregister(&snd_dev->cpu_dai); - platform_device_unregister(snd_dev->codec); - - free_card_info(snd_dev->simple_card_info); - snd_dev->i2s_tx_connection = NULL; - gb_free_snd(snd_dev); -} - -static int gb_i2s_mgmt_connection_init(struct gb_connection *connection) -{ - struct gb_snd *snd_dev; - unsigned long flags; - int ret; - - snd_dev = gb_get_snd(connection->bundle->id); - if (!snd_dev) - return -ENOMEM; - - spin_lock_irqsave(&snd_dev->lock, flags); - snd_dev->mgmt_connection = connection; - connection->private = snd_dev; - spin_unlock_irqrestore(&snd_dev->lock, flags); - - ret = gb_i2s_mgmt_get_cfgs(snd_dev, connection); - if (ret) { - pr_err("can't get i2s configurations: %d\n", ret); - goto err_free_snd_dev; - } - - ret = gb_i2s_mgmt_set_samples_per_message(snd_dev->mgmt_connection, - CONFIG_SAMPLES_PER_MSG); - if (ret) { - pr_err("set_samples_per_msg failed: %d\n", ret); - goto err_free_i2s_configs; - } - - snd_dev->send_data_req_buf = kzalloc(SEND_DATA_BUF_LEN, GFP_KERNEL); - - if (!snd_dev->send_data_req_buf) { - ret = -ENOMEM; - goto err_free_i2s_configs; - } - - return 0; - -err_free_i2s_configs: - gb_i2s_mgmt_free_cfgs(snd_dev); -err_free_snd_dev: - gb_free_snd(snd_dev); - return ret; -} - -static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection) -{ - struct gb_snd *snd_dev = (struct gb_snd *)connection->private; - - gb_i2s_mgmt_free_cfgs(snd_dev); - - kfree(snd_dev->send_data_req_buf); - snd_dev->send_data_req_buf = NULL; - - snd_dev->mgmt_connection = NULL; - gb_free_snd(snd_dev); -} - -static int gb_i2s_mgmt_report_event_recv(u8 type, struct gb_operation *op) -{ - struct gb_connection *connection = op->connection; - struct gb_i2s_mgmt_report_event_request *req = op->request->payload; - char *event_name; - - if (type != GB_I2S_MGMT_TYPE_REPORT_EVENT) { - dev_err(&connection->bundle->dev, "Invalid request type: %d\n", - type); - return -EINVAL; - } - - if (op->request->payload_size < sizeof(*req)) { - dev_err(&connection->bundle->dev, - "Short request received (%zu < %zu)\n", - op->request->payload_size, sizeof(*req)); - return -EINVAL; - } - - switch (req->event) { - case GB_I2S_MGMT_EVENT_UNSPECIFIED: - event_name = "UNSPECIFIED"; - break; - case GB_I2S_MGMT_EVENT_HALT: - /* XXX Should stop streaming now */ - event_name = "HALT"; - break; - case GB_I2S_MGMT_EVENT_INTERNAL_ERROR: - event_name = "INTERNAL_ERROR"; - break; - case GB_I2S_MGMT_EVENT_PROTOCOL_ERROR: - event_name = "PROTOCOL_ERROR"; - break; - case GB_I2S_MGMT_EVENT_FAILURE: - event_name = "FAILURE"; - break; - case GB_I2S_MGMT_EVENT_OUT_OF_SEQUENCE: - event_name = "OUT_OF_SEQUENCE"; - break; - case GB_I2S_MGMT_EVENT_UNDERRUN: - event_name = "UNDERRUN"; - break; - case GB_I2S_MGMT_EVENT_OVERRUN: - event_name = "OVERRUN"; - break; - case GB_I2S_MGMT_EVENT_CLOCKING: - event_name = "CLOCKING"; - break; - case GB_I2S_MGMT_EVENT_DATA_LEN: - event_name = "DATA_LEN"; - break; - default: - dev_warn(&connection->bundle->dev, - "Unknown I2S Event received: %d\n", req->event); - return -EINVAL; - } - - dev_warn(&connection->bundle->dev, "I2S Event received: %d - '%s'\n", - req->event, event_name); - - return 0; -} - -static struct gb_protocol gb_i2s_receiver_protocol = { - .name = GB_AUDIO_DATA_DRIVER_NAME, - .id = GREYBUS_PROTOCOL_I2S_RECEIVER, - .major = 0, - .minor = 1, - .connection_init = gb_i2s_transmitter_connection_init, - .connection_exit = gb_i2s_transmitter_connection_exit, - .request_recv = NULL, -}; - -static struct gb_protocol gb_i2s_mgmt_protocol = { - .name = GB_AUDIO_MGMT_DRIVER_NAME, - .id = GREYBUS_PROTOCOL_I2S_MGMT, - .major = 0, - .minor = 1, - .connection_init = gb_i2s_mgmt_connection_init, - .connection_exit = gb_i2s_mgmt_connection_exit, - .request_recv = gb_i2s_mgmt_report_event_recv, -}; - - -/* - * This is the basic hook get things initialized and registered w/ gb - */ - -int gb_audio_protocol_init(void) -{ - int err; - - err = gb_protocol_register(&gb_i2s_mgmt_protocol); - if (err) { - pr_err("Can't register i2s mgmt protocol driver: %d\n", -err); - return err; - } - - err = gb_protocol_register(&gb_i2s_receiver_protocol); - if (err) { - pr_err("Can't register Audio protocol driver: %d\n", -err); - goto err_unregister_i2s_mgmt; - } - - err = platform_driver_register(&gb_audio_plat_driver); - if (err) { - pr_err("Can't register platform driver: %d\n", -err); - goto err_unregister_plat; - } - - err = platform_driver_register(&gb_audio_pcm_driver); - if (err) { - pr_err("Can't register pcm driver: %d\n", -err); - goto err_unregister_pcm; - } - - return 0; - -err_unregister_pcm: - platform_driver_unregister(&gb_audio_plat_driver); -err_unregister_plat: - gb_protocol_deregister(&gb_i2s_receiver_protocol); -err_unregister_i2s_mgmt: - gb_protocol_deregister(&gb_i2s_mgmt_protocol); - return err; -} - -void gb_audio_protocol_exit(void) -{ - platform_driver_unregister(&gb_audio_pcm_driver); - platform_driver_unregister(&gb_audio_plat_driver); - gb_protocol_deregister(&gb_i2s_receiver_protocol); - gb_protocol_deregister(&gb_i2s_mgmt_protocol); -} diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h deleted file mode 100644 index e4975930f29a..000000000000 --- a/drivers/staging/greybus/audio.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Greybus audio - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __GB_AUDIO_H -#define __GB_AUDIO_H -#include -#include -#include -#include -#include -#include -#include - -#include "greybus.h" - -#define GB_SAMPLE_RATE 48000 -#define GB_RATES SNDRV_PCM_RATE_48000 -#define GB_FMTS SNDRV_PCM_FMTBIT_S16_LE -#define PREALLOC_BUFFER (32 * 1024) -#define PREALLOC_BUFFER_MAX (32 * 1024) - -/* assuming 1 ms samples @ 48KHz */ -#define CONFIG_SAMPLES_PER_MSG 48L -#define CONFIG_PERIOD_NS 1000000 /* send msg every 1ms */ - -#define CONFIG_COUNT_MAX 20 - -/* Switch between dummy spdif and jetson rt5645 codec */ -#define USE_RT5645 0 - -#define SAMPLE_SIZE 4 -#define MAX_SEND_DATA_LEN (CONFIG_SAMPLES_PER_MSG * SAMPLE_SIZE) -#define SEND_DATA_BUF_LEN (sizeof(struct gb_i2s_send_data_request) + \ - MAX_SEND_DATA_LEN) - - -/* - * This is the gb_snd structure which ties everything together - * and fakes DMA interrupts via a timer. - */ -struct gb_snd { - struct platform_device card; - struct platform_device cpu_dai; - struct platform_device *codec; - struct asoc_simple_card_info *simple_card_info; - struct i2c_client *rt5647; - struct gb_connection *mgmt_connection; - struct gb_connection *i2s_tx_connection; - struct gb_connection *i2s_rx_connection; - struct gb_i2s_mgmt_get_supported_configurations_response - *i2s_configs; - char *send_data_req_buf; - long send_data_sample_count; - int gb_bundle_id; - int device_count; - struct snd_pcm_substream *substream; - struct hrtimer timer; - atomic_t running; - bool cport_active; - struct workqueue_struct *workqueue; - struct work_struct work; - int hwptr_done; - int transfer_done; - struct list_head list; - spinlock_t lock; -}; - - -/* - * GB I2S cmd functions - */ -int gb_i2s_mgmt_activate_cport(struct gb_connection *connection, - uint16_t cport); -int gb_i2s_mgmt_deactivate_cport(struct gb_connection *connection, - uint16_t cport); -int gb_i2s_mgmt_get_supported_configurations( - struct gb_connection *connection, - struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg, - size_t size); -int gb_i2s_mgmt_set_configuration(struct gb_connection *connection, - struct gb_i2s_mgmt_set_configuration_request *set_cfg); -int gb_i2s_mgmt_set_samples_per_message(struct gb_connection *connection, - uint16_t samples_per_message); -int gb_i2s_mgmt_get_cfgs(struct gb_snd *snd_dev, - struct gb_connection *connection); -void gb_i2s_mgmt_free_cfgs(struct gb_snd *snd_dev); -int gb_i2s_mgmt_set_cfg(struct gb_snd *snd_dev, int rate, int chans, - int bytes_per_chan, int is_le); -int gb_i2s_send_data(struct gb_connection *connection, void *req_buf, - void *source_addr, size_t len, int sample_num); - - -/* - * GB PCM hooks - */ -void gb_pcm_hrtimer_start(struct gb_snd *snd_dev); -void gb_pcm_hrtimer_stop(struct gb_snd *snd_dev); - - -/* - * Platform drivers - */ -extern struct platform_driver gb_audio_pcm_driver; -extern struct platform_driver gb_audio_plat_driver; - - -#endif /* __GB_AUDIO_H */ diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 2324270e5c55..404684a3c516 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -53,15 +53,9 @@ static int __init gpbridge_init(void) pr_err("error initializing hid protocol\n"); goto error_hid; } - if (gb_audio_protocol_init()) { - pr_err("error initializing audio protocols\n"); - goto error_audio; - } return 0; -error_audio: - gb_hid_protocol_exit(); error_hid: gb_spi_protocol_exit(); error_spi: @@ -83,7 +77,6 @@ module_init(gpbridge_init); static void __exit gpbridge_exit(void) { - gb_audio_protocol_exit(); gb_hid_protocol_exit(); gb_spi_protocol_exit(); gb_i2c_protocol_exit(); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 2e0f4d667897..ad9f543206a6 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -85,9 +85,6 @@ extern void gb_spi_protocol_exit(void); extern int gb_hid_protocol_init(void); extern void gb_hid_protocol_exit(void); -extern int gb_audio_protocol_init(void); -extern void gb_audio_protocol_exit(void); - /* __protocol: Pointer to struct gb_protocol */ #define gb_protocol_driver(__protocol) \ static int __init protocol_init(void) \ -- cgit v1.2.3-59-g8ed1b From 357499df455d30290860a23a33b2eb723f9597ac Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Wed, 4 Nov 2015 09:47:22 -0700 Subject: greybus: i2s: Remove deprecated I2S Bridged-PHY Protocol The Greybus I2S Bridged-PHY Protocol is deprecated so remove all support for it from the Greybus code. CC: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 12 +-- drivers/staging/greybus/greybus_protocols.h | 138 ---------------------------- 2 files changed, 6 insertions(+), 144 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 687adf2cb645..f29f470909c9 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -32,7 +32,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_SDIO = 0x07, GREYBUS_PROTOCOL_BATTERY = 0x08, GREYBUS_PROTOCOL_PWM = 0x09, - GREYBUS_PROTOCOL_I2S_MGMT = 0x0a, + /* 0x0a is unused */ GREYBUS_PROTOCOL_SPI = 0x0b, GREYBUS_PROTOCOL_DISPLAY = 0x0c, GREYBUS_PROTOCOL_CAMERA = 0x0d, @@ -40,8 +40,8 @@ enum greybus_protocol { GREYBUS_PROTOCOL_LIGHTS = 0x0f, GREYBUS_PROTOCOL_VIBRATOR = 0x10, GREYBUS_PROTOCOL_LOOPBACK = 0x11, - GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12, - GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13, + /* 0x12 is unused */ + /* 0x13 is unused */ GREYBUS_PROTOCOL_SVC = 0x14, GREYBUS_PROTOCOL_FIRMWARE = 0x15, /* ... */ @@ -60,7 +60,7 @@ enum greybus_class_type { GREYBUS_CLASS_SDIO = 0x07, GREYBUS_CLASS_BATTERY = 0x08, GREYBUS_CLASS_PWM = 0x09, - GREYBUS_CLASS_I2S = 0x0a, + /* 0x0a is unused */ GREYBUS_CLASS_SPI = 0x0b, GREYBUS_CLASS_DISPLAY = 0x0c, GREYBUS_CLASS_CAMERA = 0x0d, @@ -68,8 +68,8 @@ enum greybus_class_type { GREYBUS_CLASS_LIGHTS = 0x0f, GREYBUS_CLASS_VIBRATOR = 0x10, GREYBUS_CLASS_LOOPBACK = 0x11, - GREYBUS_CLASS_I2S_RECEIVER = 0x12, - GREYBUS_CLASS_I2S_TRANSMITTER = 0x13, + /* 0x12 is unused */ + /* 0x13 is unused */ GREYBUS_CLASS_SVC = 0x14, GREYBUS_CLASS_FIRMWARE = 0x15, /* ... */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 02ba40f1666c..1142b2094901 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -511,144 +511,6 @@ struct gb_pwm_disable_request { __u8 which; } __packed; -/* I2S */ - -#define GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS 0x02 -#define GB_I2S_MGMT_TYPE_SET_CONFIGURATION 0x03 -#define GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE 0x04 -#define GB_I2S_MGMT_TYPE_GET_PROCESSING_DELAY 0x05 -#define GB_I2S_MGMT_TYPE_SET_START_DELAY 0x06 -#define GB_I2S_MGMT_TYPE_ACTIVATE_CPORT 0x07 -#define GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT 0x08 -#define GB_I2S_MGMT_TYPE_REPORT_EVENT 0x09 - -#define GB_I2S_MGMT_BYTE_ORDER_NA BIT(0) -#define GB_I2S_MGMT_BYTE_ORDER_BE BIT(1) -#define GB_I2S_MGMT_BYTE_ORDER_LE BIT(2) - -#define GB_I2S_MGMT_SPATIAL_LOCATION_FL BIT(0) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FR BIT(1) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FC BIT(2) -#define GB_I2S_MGMT_SPATIAL_LOCATION_LFE BIT(3) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BL BIT(4) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BR BIT(5) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FLC BIT(6) -#define GB_I2S_MGMT_SPATIAL_LOCATION_FRC BIT(7) -#define GB_I2S_MGMT_SPATIAL_LOCATION_C BIT(8) /* BC in USB */ -#define GB_I2S_MGMT_SPATIAL_LOCATION_SL BIT(9) -#define GB_I2S_MGMT_SPATIAL_LOCATION_SR BIT(10) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TC BIT(11) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFL BIT(12) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFC BIT(13) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFR BIT(14) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TBL BIT(15) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TBC BIT(16) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TBR BIT(17) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFLC BIT(18) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TFRC BIT(19) -#define GB_I2S_MGMT_SPATIAL_LOCATION_LLFE BIT(20) -#define GB_I2S_MGMT_SPATIAL_LOCATION_RLFE BIT(21) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TSL BIT(22) -#define GB_I2S_MGMT_SPATIAL_LOCATION_TSR BIT(23) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BC BIT(24) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BLC BIT(25) -#define GB_I2S_MGMT_SPATIAL_LOCATION_BRC BIT(26) -#define GB_I2S_MGMT_SPATIAL_LOCATION_RD BIT(31) - -#define GB_I2S_MGMT_PROTOCOL_PCM BIT(0) -#define GB_I2S_MGMT_PROTOCOL_I2S BIT(1) -#define GB_I2S_MGMT_PROTOCOL_LR_STEREO BIT(2) - -#define GB_I2S_MGMT_ROLE_MASTER BIT(0) -#define GB_I2S_MGMT_ROLE_SLAVE BIT(1) - -#define GB_I2S_MGMT_POLARITY_NORMAL BIT(0) -#define GB_I2S_MGMT_POLARITY_REVERSED BIT(1) - -#define GB_I2S_MGMT_EDGE_RISING BIT(0) -#define GB_I2S_MGMT_EDGE_FALLING BIT(1) - -#define GB_I2S_MGMT_EVENT_UNSPECIFIED 0x1 -#define GB_I2S_MGMT_EVENT_HALT 0x2 -#define GB_I2S_MGMT_EVENT_INTERNAL_ERROR 0x3 -#define GB_I2S_MGMT_EVENT_PROTOCOL_ERROR 0x4 -#define GB_I2S_MGMT_EVENT_FAILURE 0x5 -#define GB_I2S_MGMT_EVENT_OUT_OF_SEQUENCE 0x6 -#define GB_I2S_MGMT_EVENT_UNDERRUN 0x7 -#define GB_I2S_MGMT_EVENT_OVERRUN 0x8 -#define GB_I2S_MGMT_EVENT_CLOCKING 0x9 -#define GB_I2S_MGMT_EVENT_DATA_LEN 0xa - -struct gb_i2s_mgmt_configuration { - __le32 sample_frequency; - __u8 num_channels; - __u8 bytes_per_channel; - __u8 byte_order; - __u8 pad; - __le32 spatial_locations; - __le32 ll_protocol; - __u8 ll_mclk_role; - __u8 ll_bclk_role; - __u8 ll_wclk_role; - __u8 ll_wclk_polarity; - __u8 ll_wclk_change_edge; - __u8 ll_wclk_tx_edge; - __u8 ll_wclk_rx_edge; - __u8 ll_data_offset; -} __packed; - -/* get supported configurations request has no payload */ -struct gb_i2s_mgmt_get_supported_configurations_response { - __u8 config_count; - __u8 pad[3]; - struct gb_i2s_mgmt_configuration config[0]; -} __packed; - -struct gb_i2s_mgmt_set_configuration_request { - struct gb_i2s_mgmt_configuration config; -} __packed; -/* set configuration response has no payload */ - -struct gb_i2s_mgmt_set_samples_per_message_request { - __le16 samples_per_message; -} __packed; -/* set samples per message response has no payload */ - -/* get processing request delay has no payload */ -struct gb_i2s_mgmt_get_processing_delay_response { - __le32 microseconds; -} __packed; - -struct gb_i2s_mgmt_set_start_delay_request { - __le32 microseconds; -} __packed; -/* set start delay response has no payload */ - -struct gb_i2s_mgmt_activate_cport_request { - __le16 cport; -} __packed; -/* activate cport response has no payload */ - -struct gb_i2s_mgmt_deactivate_cport_request { - __le16 cport; -} __packed; -/* deactivate cport response has no payload */ - -struct gb_i2s_mgmt_report_event_request { - __u8 event; -} __packed; -/* report event response has no payload */ - -#define GB_I2S_DATA_TYPE_SEND_DATA 0x02 - -struct gb_i2s_send_data_request { - __le32 sample_number; - __le32 size; - __u8 data[0]; -} __packed; -/* send data has no response at all */ - - /* SPI */ /* Version of the Greybus spi protocol we support */ -- cgit v1.2.3-59-g8ed1b From a51e8551e298841f26ccf02193caf2b69da2434c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:12 +0100 Subject: greybus: es2: fix use-after-free at disconnect The interface private data is released as part of host-device removal and must not be accessed afterwards. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 1e786a6ef7d7..ebf41f740ccc 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -510,6 +510,7 @@ static void ap_disconnect(struct usb_interface *interface) { struct es2_ap_dev *es2; struct usb_device *udev; + int *cport_to_ep; int bulk_in; int i; @@ -548,9 +549,10 @@ static void ap_disconnect(struct usb_interface *interface) usb_set_intfdata(interface, NULL); udev = es2->usb_dev; + cport_to_ep = es2->cport_to_ep; gb_hd_remove(es2->hd); - kfree(es2->cport_to_ep); + kfree(cport_to_ep); usb_put_dev(udev); } -- cgit v1.2.3-59-g8ed1b From b6d808525aef6703f2bf7662c7ef99f5e7349bb4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:13 +0100 Subject: greybus: es2: fix endpoint requirement check The current es2 implementation only checks for no IN or OUT bulk endpoints, but still assumes there are precisely NUM_BULKS of each. Specifically, urbs could be allocated and initialised to default values, which amounts to bulk urbs being submitted to the control endpoint. We should be able to handle any number of endpoints, but for now let's just require NUM_BULKS of each. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index ebf41f740ccc..4bb500f65873 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -848,8 +848,7 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if ((bulk_in == 0) || - (bulk_out == 0)) { + if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } -- cgit v1.2.3-59-g8ed1b From 0ce68ce41779c353be780f635234a446ea30c49d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:14 +0100 Subject: greybus: es2: separate urb allocation and submission Refactor in-urb submission, and make sure to separate allocation and submission. Eventually we'll be submitting the urbs at cport enable rather than at probe, and this is also needed for the driver-model rework. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 4bb500f65873..156269645f46 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -209,6 +209,35 @@ static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id) } #endif +static int es2_cport_in_enable(struct es2_ap_dev *es2, + struct es2_cport_in *cport_in) +{ + struct urb *urb; + int ret; + int i; + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + urb = cport_in->urb[i]; + + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + dev_err(&es2->usb_dev->dev, + "failed to submit in-urb: %d\n", ret); + goto err_kill_urbs; + } + } + + return 0; + +err_kill_urbs: + for (--i; i >= 0; --i) { + urb = cport_in->urb[i]; + usb_kill_urb(urb); + } + + return ret; +} + static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask) { struct urb *urb = NULL; @@ -853,7 +882,7 @@ static int ap_probe(struct usb_interface *interface, goto error; } - /* Allocate buffers for our cport in messages and start them up */ + /* Allocate buffers for our cport in messages */ for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { struct es2_cport_in *cport_in = &es2->cport_in[bulk_in]; @@ -875,9 +904,6 @@ static int ap_probe(struct usb_interface *interface, cport_in_callback, hd); cport_in->urb[i] = urb; cport_in->buffer[i] = buffer; - retval = usb_submit_urb(urb, GFP_KERNEL); - if (retval) - goto error; } } @@ -898,6 +924,13 @@ static int ap_probe(struct usb_interface *interface, (S_IWUSR | S_IRUGO), gb_debugfs_get(), es2, &apb_log_enable_fops); + + for (i = 0; i < NUM_BULKS; ++i) { + retval = es2_cport_in_enable(es2, &es2->cport_in[i]); + if (retval) + goto error; + } + return 0; error: ap_disconnect(interface); -- cgit v1.2.3-59-g8ed1b From 8dfd3fe56467ceabaac39ea884e0dc882c1832cd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:15 +0100 Subject: greybus: es1: separate urb allocation and submission Separate in-urb allocation and submission. This is needed for the driver-model rework. Compile-tested only. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 23860870d4f1..57247712ce60 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -650,7 +650,7 @@ static int ap_probe(struct usb_interface *interface, goto error; } - /* Allocate buffers for our cport in messages and start them up */ + /* Allocate buffers for our cport in messages */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { struct urb *urb; u8 *buffer; @@ -668,9 +668,6 @@ static int ap_probe(struct usb_interface *interface, cport_in_callback, hd); es1->cport_in_urb[i] = urb; es1->cport_in_buffer[i] = buffer; - retval = usb_submit_urb(urb, GFP_KERNEL); - if (retval) - goto error; } /* Allocate urbs for our CPort OUT messages */ @@ -689,6 +686,13 @@ static int ap_probe(struct usb_interface *interface, (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, &apb1_log_enable_fops); + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + retval = usb_submit_urb(es1->cport_in_urb[i], GFP_KERNEL); + if (retval) + goto error; + } + return 0; error: ap_disconnect(interface); -- cgit v1.2.3-59-g8ed1b From f6624ca77cbb4ce799a0642207fdb20e98d32777 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:16 +0100 Subject: greybus: es2: separate stopping and deallocating urbs Separate stopping and deallocating our in-urbs. This will facilitate implementing proper host-device life-time management. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 156269645f46..75052af70abf 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -238,6 +238,18 @@ err_kill_urbs: return ret; } +static void es2_cport_in_disable(struct es2_ap_dev *es2, + struct es2_cport_in *cport_in) +{ + struct urb *urb; + int i; + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + urb = cport_in->urb[i]; + usb_kill_urb(urb); + } +} + static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask) { struct urb *urb = NULL; @@ -547,6 +559,9 @@ static void ap_disconnect(struct usb_interface *interface) if (!es2) return; + for (i = 0; i < NUM_BULKS; ++i) + es2_cport_in_disable(es2, &es2->cport_in[i]); + usb_log_disable(es2); /* Tear down everything! */ @@ -569,7 +584,6 @@ static void ap_disconnect(struct usb_interface *interface) if (!urb) break; - usb_kill_urb(urb); usb_free_urb(urb); kfree(cport_in->buffer[i]); cport_in->buffer[i] = NULL; -- cgit v1.2.3-59-g8ed1b From 715d7a7280697540f9b6a4d82c3c9d1fa208c48d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:17 +0100 Subject: greybus: es1: separate stopping and deallocating urbs Separate stopping and deallocating our in-urbs. This will facilitate implementing proper host-device life-time management. Compile-only tested. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 57247712ce60..ddca5f9ba74c 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -369,6 +369,9 @@ static void ap_disconnect(struct usb_interface *interface) if (!es1) return; + for (i = 0; i < NUM_CPORT_IN_URB; ++i) + usb_kill_urb(es1->cport_in_urb[i]); + usb_log_disable(es1); /* Tear down everything! */ @@ -377,7 +380,6 @@ static void ap_disconnect(struct usb_interface *interface) if (!urb) break; - usb_kill_urb(urb); usb_free_urb(urb); es1->cport_out_urb[i] = NULL; es1->cport_out_urb_busy[i] = false; /* just to be anal */ -- cgit v1.2.3-59-g8ed1b From 74cd6503e5d4f01146dc18eb28d8fa3976914995 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:18 +0100 Subject: greybus: es2: fix debugfs dentry leak Fix dentry leak due to failure to remove debugfs file. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 75052af70abf..18cfcd6b6578 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -562,6 +562,7 @@ static void ap_disconnect(struct usb_interface *interface) for (i = 0; i < NUM_BULKS; ++i) es2_cport_in_disable(es2, &es2->cport_in[i]); + debugfs_remove(es2->apb_log_enable_dentry); usb_log_disable(es2); /* Tear down everything! */ -- cgit v1.2.3-59-g8ed1b From fb7a4ea22b6e68b043165ccff9601665c51af59d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:19 +0100 Subject: greybus: es1: fix debugfs dentry leak Fix dentry leak due to failure to remove debugfs file. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index ddca5f9ba74c..30477690e54e 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -372,6 +372,7 @@ static void ap_disconnect(struct usb_interface *interface) for (i = 0; i < NUM_CPORT_IN_URB; ++i) usb_kill_urb(es1->cport_in_urb[i]); + debugfs_remove(apb1_log_enable_dentry); usb_log_disable(es1); /* Tear down everything! */ -- cgit v1.2.3-59-g8ed1b From 57bc17ffc566deade99753febcc264c8326f1860 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:20 +0100 Subject: greybus: es2: add more structure to probe error handling Add more structure to probe error handling rather than use the big hammer and call disconnect directly. This is needed to fix some host-device life-time issues. Note that there was never any need to clear the USB interface data as this will be done by driver core. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 18cfcd6b6578..96ba1a74bb69 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -547,21 +547,13 @@ static int check_urb_status(struct urb *urb) return -EAGAIN; } -static void ap_disconnect(struct usb_interface *interface) +static void es2_destroy(struct es2_ap_dev *es2) { - struct es2_ap_dev *es2; struct usb_device *udev; int *cport_to_ep; int bulk_in; int i; - es2 = usb_get_intfdata(interface); - if (!es2) - return; - - for (i = 0; i < NUM_BULKS; ++i) - es2_cport_in_disable(es2, &es2->cport_in[i]); - debugfs_remove(es2->apb_log_enable_dentry); usb_log_disable(es2); @@ -591,7 +583,6 @@ static void ap_disconnect(struct usb_interface *interface) } } - usb_set_intfdata(interface, NULL); udev = es2->usb_dev; cport_to_ep = es2->cport_to_ep; gb_hd_remove(es2->hd); @@ -600,6 +591,17 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(udev); } +static void ap_disconnect(struct usb_interface *interface) +{ + struct es2_ap_dev *es2 = usb_get_intfdata(interface); + int i; + + for (i = 0; i < NUM_BULKS; ++i) + es2_cport_in_disable(es2, &es2->cport_in[i]); + + es2_destroy(es2); +} + static void cport_in_callback(struct urb *urb) { struct gb_host_device *hd = urb->context; @@ -943,12 +945,16 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < NUM_BULKS; ++i) { retval = es2_cport_in_enable(es2, &es2->cport_in[i]); if (retval) - goto error; + goto err_disable_cport_in; } return 0; + +err_disable_cport_in: + for (--i; i >= 0; --i) + es2_cport_in_disable(es2, &es2->cport_in[i]); error: - ap_disconnect(interface); + es2_destroy(es2); return retval; } -- cgit v1.2.3-59-g8ed1b From b4bd3617ed069a0aae701ed451cb6ec370fbd45f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:21 +0100 Subject: greybus: es1: add more structure to probe error handling Add more structure to probe error handling rather than use the big hammer and call disconnect directly. This is needed to fix some host-device life-time issues. Note that there was never any need to clear the USB interface data as this will be done by driver core. Compile-only tested. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 30477690e54e..858c4aa9e28a 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -359,19 +359,11 @@ static int check_urb_status(struct urb *urb) return -EAGAIN; } -static void ap_disconnect(struct usb_interface *interface) +static void es1_destroy(struct es1_ap_dev *es1) { - struct es1_ap_dev *es1; struct usb_device *udev; int i; - es1 = usb_get_intfdata(interface); - if (!es1) - return; - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) - usb_kill_urb(es1->cport_in_urb[i]); - debugfs_remove(apb1_log_enable_dentry); usb_log_disable(es1); @@ -397,13 +389,23 @@ static void ap_disconnect(struct usb_interface *interface) es1->cport_in_buffer[i] = NULL; } - usb_set_intfdata(interface, NULL); udev = es1->usb_dev; gb_hd_remove(es1->hd); usb_put_dev(udev); } +static void ap_disconnect(struct usb_interface *interface) +{ + struct es1_ap_dev *es1 = usb_get_intfdata(interface); + int i; + + for (i = 0; i < NUM_CPORT_IN_URB; ++i) + usb_kill_urb(es1->cport_in_urb[i]); + + es1_destroy(es1); +} + static void cport_in_callback(struct urb *urb) { struct gb_host_device *hd = urb->context; @@ -693,12 +695,16 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < NUM_CPORT_IN_URB; ++i) { retval = usb_submit_urb(es1->cport_in_urb[i], GFP_KERNEL); if (retval) - goto error; + goto err_kill_in_urbs; } return 0; + +err_kill_in_urbs: + for (--i; i >= 0; --i) + usb_kill_urb(es1->cport_in_urb[i]); error: - ap_disconnect(interface); + es1_destroy(es1); return retval; } -- cgit v1.2.3-59-g8ed1b From c1700479845bc36cb48719b2ae341d4df89a73f6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:22 +0100 Subject: greybus: hd: fix host-device life time issues Fix host-device life time issues by separating host-device allocation from registration. This is needed both to make sure that all host-device resources are available before registering the device and to prevent such resources from being deallocated while the device is still in use during device removal. This specifically fixes the following warnings during es1 and es2 disconnect: usb 1-1.1: No free CPort OUT urbs, having to dynamically allocate one! Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es1.c | 9 ++++++++- drivers/staging/greybus/es2.c | 9 ++++++++- drivers/staging/greybus/hd.c | 24 ++++++++++++++++-------- drivers/staging/greybus/hd.h | 4 +++- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 858c4aa9e28a..75253d9a942b 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -390,7 +390,7 @@ static void es1_destroy(struct es1_ap_dev *es1) } udev = es1->usb_dev; - gb_hd_remove(es1->hd); + gb_hd_put(es1->hd); usb_put_dev(udev); } @@ -403,6 +403,8 @@ static void ap_disconnect(struct usb_interface *interface) for (i = 0; i < NUM_CPORT_IN_URB; ++i) usb_kill_urb(es1->cport_in_urb[i]); + gb_hd_del(es1->hd); + es1_destroy(es1); } @@ -692,6 +694,10 @@ static int ap_probe(struct usb_interface *interface, gb_debugfs_get(), es1, &apb1_log_enable_fops); + retval = gb_hd_add(hd); + if (retval) + goto error; + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { retval = usb_submit_urb(es1->cport_in_urb[i], GFP_KERNEL); if (retval) @@ -703,6 +709,7 @@ static int ap_probe(struct usb_interface *interface, err_kill_in_urbs: for (--i; i >= 0; --i) usb_kill_urb(es1->cport_in_urb[i]); + gb_hd_del(hd); error: es1_destroy(es1); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 96ba1a74bb69..aad6451d90de 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -585,7 +585,7 @@ static void es2_destroy(struct es2_ap_dev *es2) udev = es2->usb_dev; cport_to_ep = es2->cport_to_ep; - gb_hd_remove(es2->hd); + gb_hd_put(es2->hd); kfree(cport_to_ep); usb_put_dev(udev); @@ -599,6 +599,8 @@ static void ap_disconnect(struct usb_interface *interface) for (i = 0; i < NUM_BULKS; ++i) es2_cport_in_disable(es2, &es2->cport_in[i]); + gb_hd_del(es2->hd); + es2_destroy(es2); } @@ -942,6 +944,10 @@ static int ap_probe(struct usb_interface *interface, gb_debugfs_get(), es2, &apb_log_enable_fops); + retval = gb_hd_add(hd); + if (retval) + goto error; + for (i = 0; i < NUM_BULKS; ++i) { retval = es2_cport_in_enable(es2, &es2->cport_in[i]); if (retval) @@ -953,6 +959,7 @@ static int ap_probe(struct usb_interface *interface, err_disable_cport_in: for (--i; i >= 0; --i) es2_cport_in_disable(es2, &es2->cport_in[i]); + gb_hd_del(hd); error: es2_destroy(es2); diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 6f29eb49993a..b22d5470508a 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -77,6 +77,12 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, hd->buffer_size_max = buffer_size_max; hd->num_cports = num_cports; + return hd; +} +EXPORT_SYMBOL_GPL(gb_hd_create); + +int gb_hd_add(struct gb_host_device *hd) +{ /* * Initialize AP's SVC protocol connection: * @@ -87,16 +93,14 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, * time we will create a fully initialized svc-connection, as we need * endo-id and AP's interface id for that. */ - if (!gb_ap_svc_connection_create(hd)) { - kref_put_mutex(&hd->kref, free_hd, &hd_mutex); - return ERR_PTR(-ENOMEM); - } + if (!gb_ap_svc_connection_create(hd)) + return -ENOMEM; - return hd; + return 0; } -EXPORT_SYMBOL_GPL(gb_hd_create); +EXPORT_SYMBOL_GPL(gb_hd_add); -void gb_hd_remove(struct gb_host_device *hd) +void gb_hd_del(struct gb_host_device *hd) { /* * Tear down all interfaces, modules, and the endo that is associated @@ -109,7 +113,11 @@ void gb_hd_remove(struct gb_host_device *hd) /* Is the SVC still using the partially uninitialized connection ? */ if (hd->initial_svc_connection) gb_connection_destroy(hd->initial_svc_connection); +} +EXPORT_SYMBOL_GPL(gb_hd_del); +void gb_hd_put(struct gb_host_device *hd) +{ kref_put_mutex(&hd->kref, free_hd, &hd_mutex); } -EXPORT_SYMBOL_GPL(gb_hd_remove); +EXPORT_SYMBOL_GPL(gb_hd_put); diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 91fcccb46df1..6724cfea1e19 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -52,6 +52,8 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, struct device *parent, size_t buffer_size_max, size_t num_cports); -void gb_hd_remove(struct gb_host_device *hd); +int gb_hd_add(struct gb_host_device *hd); +void gb_hd_del(struct gb_host_device *hd); +void gb_hd_put(struct gb_host_device *hd); #endif /* __HD_H */ -- cgit v1.2.3-59-g8ed1b From a52028626accc2ae26c9ce03ec7bba68146b8456 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Nov 2015 18:55:23 +0100 Subject: greybus: es2: clean up es2 destructor Now that the host-device is deregistered separately from deallocation, we can simplify the cport_to_ep-array deallocation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index aad6451d90de..c1a6fe4513c6 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -550,7 +550,6 @@ static int check_urb_status(struct urb *urb) static void es2_destroy(struct es2_ap_dev *es2) { struct usb_device *udev; - int *cport_to_ep; int bulk_in; int i; @@ -583,11 +582,11 @@ static void es2_destroy(struct es2_ap_dev *es2) } } + kfree(es2->cport_to_ep); + udev = es2->usb_dev; - cport_to_ep = es2->cport_to_ep; gb_hd_put(es2->hd); - kfree(cport_to_ep); usb_put_dev(udev); } -- cgit v1.2.3-59-g8ed1b From c9e9de26f912bc0e5bca6a5168e7d5d427d2b2ee Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 4 Nov 2015 20:46:14 -0800 Subject: greybus: hid: hid should not be part of the bridged-phy driver. HID is a stand-alone greybus protocol, not part of the bridged-phy protocols, so make it a stand-alone kernel module. Note, some hard-coded android init script might need to be changed to load the gb-hid.ko kernel module now. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/Makefile | 3 ++- drivers/staging/greybus/gpbridge.c | 7 ------- drivers/staging/greybus/hid.c | 4 +++- drivers/staging/greybus/protocol.h | 3 --- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index cf4cf2178ed6..8b28b4b5a53d 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -18,7 +18,6 @@ gb-phy-y := gpbridge.o \ uart.o \ pwm.o \ gpio.o \ - hid.o \ i2c.o \ spi.o \ usb.o @@ -29,6 +28,7 @@ gb-battery-y := battery.o gb-loopback-y := loopback.o gb-light-y := light.o gb-raw-y := raw.o +gb-hid-y := hid.o gb-es1-y := es1.o gb-es2-y := es2.o gb-db3-y := db3-platform.o @@ -39,6 +39,7 @@ obj-m += gb-vibrator.o obj-m += gb-battery.o obj-m += gb-loopback.o obj-m += gb-light.o +obj-m += gb-hid.o obj-m += gb-raw.o obj-m += gb-es1.o obj-m += gb-es2.o diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 404684a3c516..29f0984f7213 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -49,15 +49,9 @@ static int __init gpbridge_init(void) pr_err("error initializing spi protocol\n"); goto error_spi; } - if (gb_hid_protocol_init()) { - pr_err("error initializing hid protocol\n"); - goto error_hid; - } return 0; -error_hid: - gb_spi_protocol_exit(); error_spi: gb_i2c_protocol_exit(); error_i2c: @@ -77,7 +71,6 @@ module_init(gpbridge_init); static void __exit gpbridge_exit(void) { - gb_hid_protocol_exit(); gb_spi_protocol_exit(); gb_i2c_protocol_exit(); gb_usb_protocol_exit(); diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 3ac9c1049f1d..2adcb1c47d2e 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -474,4 +474,6 @@ static struct gb_protocol hid_protocol = { .request_recv = gb_hid_irq_handler, }; -gb_builtin_protocol_driver(hid_protocol); +gb_protocol_driver(&hid_protocol); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index ad9f543206a6..b1d122c61c44 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -82,9 +82,6 @@ extern void gb_i2c_protocol_exit(void); extern int gb_spi_protocol_init(void); extern void gb_spi_protocol_exit(void); -extern int gb_hid_protocol_init(void); -extern void gb_hid_protocol_exit(void); - /* __protocol: Pointer to struct gb_protocol */ #define gb_protocol_driver(__protocol) \ static int __init protocol_init(void) \ -- cgit v1.2.3-59-g8ed1b From 0d1d6ee209e1129c584c2b00a48a33ff1fb7312c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Nov 2015 10:07:02 +0100 Subject: greybus: loopback: fix layer violation A bundle (protocol) driver has no business creating sysfs entries for an ancestor device. Move the sysfs entries to the bundle device for now. Signed-off-by: Johan Hovold Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index e1b1be0687a8..9a44dcc7d7d7 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -921,7 +921,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) struct gb_loopback *gb; int retval; char name[DEBUGFS_NAMELEN]; - struct kobject *kobj = &connection->hd->endo->dev.kobj; + struct kobject *kobj = &connection->bundle->dev.kobj; gb = kzalloc(sizeof(*gb), GFP_KERNEL); if (!gb) @@ -1010,7 +1010,7 @@ out_sysfs: static void gb_loopback_connection_exit(struct gb_connection *connection) { struct gb_loopback *gb = connection->bundle->private; - struct kobject *kobj = &connection->hd->endo->dev.kobj; + struct kobject *kobj = &connection->bundle->dev.kobj; if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); -- cgit v1.2.3-59-g8ed1b From cd414d472637a30104b4908f5f239f3c06d90fe9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Nov 2015 10:07:03 +0100 Subject: greybus: battery: fix class-device parent Greybus bundle drivers should register their class devices as children to the bundle device that they bind to. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c index 6b354de146d1..e119775fb19b 100644 --- a/drivers/staging/greybus/battery.c +++ b/drivers/staging/greybus/battery.c @@ -244,7 +244,7 @@ static int init_and_register(struct gb_connection *connection, gb->bat.num_properties = ARRAY_SIZE(battery_props); gb->bat.get_property = get_property; - return power_supply_register(&connection->bundle->intf->dev, &gb->bat); + return power_supply_register(&connection->bundle->dev, &gb->bat); } #else static int init_and_register(struct gb_connection *connection, @@ -262,7 +262,7 @@ static int init_and_register(struct gb_connection *connection, gb->desc.num_properties = ARRAY_SIZE(battery_props); gb->desc.get_property = get_property; - gb->bat = power_supply_register(&connection->bundle->intf->dev, + gb->bat = power_supply_register(&connection->bundle->dev, &gb->desc, &cfg); if (IS_ERR(gb->bat)) return PTR_ERR(gb->bat); -- cgit v1.2.3-59-g8ed1b From f865734d3cf58cc9aba3f58962fbca7b97fe47d4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Nov 2015 10:07:04 +0100 Subject: greybus: light: fix class-device parent Greybus bundle drivers should register their class devices as children to the bundle device that they bind to. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 664be973fa96..3723a2ce29b3 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -724,8 +724,7 @@ static int __gb_lights_flash_led_register(struct gb_channel *channel) goto fail; } - ret = led_classdev_flash_register(&connection->bundle->intf->dev, - fled); + ret = led_classdev_flash_register(&connection->bundle->dev, fled); if (ret < 0) goto fail; @@ -812,8 +811,7 @@ static int __gb_lights_led_register(struct gb_channel *channel) struct led_classdev *cdev = get_channel_cdev(channel); int ret; - ret = led_classdev_register(&connection->bundle->intf->dev, - cdev); + ret = led_classdev_register(&connection->bundle->dev, cdev); if (ret < 0) channel->led = NULL; else -- cgit v1.2.3-59-g8ed1b From eeb6a6ff597ee47a6ecc652dab769c3137fd1483 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Nov 2015 10:07:05 +0100 Subject: greybus: interface: fix potential use-after-free at remove Fix resources (accessible through sysfs) being released before interface is deregistered. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 275a8c7df8e1..e496b1646aa5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -60,6 +60,9 @@ static void gb_interface_release(struct device *dev) { struct gb_interface *intf = to_gb_interface(dev); + kfree(intf->product_string); + kfree(intf->vendor_string); + kfree(intf); } @@ -191,9 +194,6 @@ void gb_interface_remove(struct gb_interface *intf) list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); - kfree(intf->product_string); - kfree(intf->vendor_string); - module = intf->module; device_unregister(&intf->dev); put_device(&module->dev); -- cgit v1.2.3-59-g8ed1b From febe25217924983f08a3d729d1a0b96a152ff340 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Nov 2015 10:07:06 +0100 Subject: greybus: interface: fix obsolete comment Fix obsolete comment that was referring to "module" rather than "interface". Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index e496b1646aa5..b864f6879ffb 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -176,7 +176,7 @@ put_module: } /* - * Tear down a previously set up module. + * Tear down a previously set up interface. */ void gb_interface_remove(struct gb_interface *intf) { -- cgit v1.2.3-59-g8ed1b From 35a84ba7f94001c11fac0679b20c8d268fbc0b96 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Nov 2015 10:07:07 +0100 Subject: greybus: use decimal notation for interface and cport ids Fix up the few places where hexadecimal rather than decimal notation was used for interface and cport ids. Note that this includes the endo sysfs-attribute for the AP interface id. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/endo.c | 2 +- drivers/staging/greybus/interface.c | 3 +-- drivers/staging/greybus/svc.c | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index df4afb232c5e..775dbcea539b 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -89,7 +89,7 @@ static ssize_t ap_intf_id_show(struct device *dev, { struct gb_endo *endo = to_gb_endo(dev); - return sprintf(buf, "0x%02x\n", endo->ap_intf_id); + return sprintf(buf, "%u\n", endo->ap_intf_id); } static DEVICE_ATTR_RO(ap_intf_id); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index b864f6879ffb..4c96adc56139 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -157,8 +157,7 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, retval = device_add(&intf->dev); if (retval) { - pr_err("failed to add interface device for id 0x%02hhx\n", - interface_id); + pr_err("failed to add interface %u\n", interface_id); goto free_intf; } diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index c2633e25d43b..acf08e774791 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -268,7 +268,7 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY, &request, sizeof(request), NULL, 0); if (ret) - pr_err("failed to destroy connection (%hhx:%hx %hhx:%hx) %d\n", + pr_err("failed to destroy connection (%hhu:%hu %hhu:%hu) %d\n", intf1_id, cport1_id, intf2_id, cport2_id, ret); } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); @@ -300,7 +300,7 @@ static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_DESTROY, &request, sizeof(request), NULL, 0); if (ret) - pr_err("failed to destroy route (%hhx %hhx) %d\n", + pr_err("failed to destroy route (%hhu %hhu) %d\n", intf1_id, intf2_id, ret); } -- cgit v1.2.3-59-g8ed1b From f6c6c1388ad3ea9adc0ec2652d2b5ef9ad4ae166 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Nov 2015 10:07:08 +0100 Subject: greybus: svc: use bit-macro for cport flags Use BIT-macro for cport flags. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index acf08e774791..6c1a157c10c5 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -12,9 +12,9 @@ #include "greybus.h" -#define CPORT_FLAGS_E2EFC (1) -#define CPORT_FLAGS_CSD_N (2) -#define CPORT_FLAGS_CSV_N (4) +#define CPORT_FLAGS_E2EFC BIT(0) +#define CPORT_FLAGS_CSD_N BIT(1) +#define CPORT_FLAGS_CSV_N BIT(2) enum gb_svc_state { GB_SVC_STATE_RESET, -- cgit v1.2.3-59-g8ed1b From 69166d252536b644f82782840cf31e6d685616dc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 9 Nov 2015 15:05:03 +0530 Subject: greybus: es2: Send cport-id in wValue field to usb_control_msg() wIndex field has a special meaning, as that can be used by the core to index into the possible USB interfaces. And that specifically broke with gbsim, as it has a single USB interface. Other similar requests (REQUEST_LATENCY_TAG_{EN|DIS}) are already using wValue field for passing cport-id. Fix cport_reset() by sending the cport-id in wValue field instead of wIndex. Signed-off-by: Viresh Kumar Tested-by: Fabien Parent Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index c1a6fe4513c6..a0e20376bebf 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -440,7 +440,7 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), REQUEST_RESET_CPORT, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, 0, cport_id, + USB_RECIP_INTERFACE, cport_id, 0, NULL, 0, ES2_TIMEOUT); if (retval < 0) { dev_err(&udev->dev, "failed to reset cport %hu: %d\n", cport_id, -- cgit v1.2.3-59-g8ed1b From 464888410d3499708baf168bc52e13f18d0cf0ae Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 12 Nov 2015 15:35:59 +0000 Subject: greybus: battery: move battery file to power_supply Rename the battery.c to power_supply.c and update the makefile as the Greybus protocol is being renamed from battery to power_supply. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 4 +- drivers/staging/greybus/battery.c | 317 --------------------------------- drivers/staging/greybus/power_supply.c | 317 +++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+), 319 deletions(-) delete mode 100644 drivers/staging/greybus/battery.c create mode 100644 drivers/staging/greybus/power_supply.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 8b28b4b5a53d..d479a0291ed7 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -24,7 +24,7 @@ gb-phy-y := gpbridge.o \ # Prefix all modules with gb- gb-vibrator-y := vibrator.o -gb-battery-y := battery.o +gb-power-supply-y := power_supply.o gb-loopback-y := loopback.o gb-light-y := light.o gb-raw-y := raw.o @@ -36,7 +36,7 @@ gb-db3-y := db3-platform.o obj-m += greybus.o obj-m += gb-phy.o obj-m += gb-vibrator.o -obj-m += gb-battery.o +obj-m += gb-power-supply.o obj-m += gb-loopback.o obj-m += gb-light.o obj-m += gb-hid.o diff --git a/drivers/staging/greybus/battery.c b/drivers/staging/greybus/battery.c deleted file mode 100644 index e119775fb19b..000000000000 --- a/drivers/staging/greybus/battery.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Battery driver for a Greybus module. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include "greybus.h" - -struct gb_battery { - /* - * The power supply api changed in 4.1, so handle both the old - * and new apis in the same driver for now, until this is merged - * upstream, when all of these version checks can be removed. - */ -#ifdef DRIVER_OWNS_PSY_STRUCT - struct power_supply bat; -#define to_gb_battery(x) container_of(x, struct gb_battery, bat) -#else - struct power_supply *bat; - struct power_supply_desc desc; -#define to_gb_battery(x) power_supply_get_drvdata(x) -#endif - // FIXME - // we will want to keep the battery stats in here as we will be getting - // updates from the SVC "on the fly" so we don't have to always go ask - // the battery for some information. Hopefully... - struct gb_connection *connection; - -}; - -static int get_tech(struct gb_battery *gb) -{ - struct gb_battery_technology_response tech_response; - u32 technology; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY, - NULL, 0, - &tech_response, sizeof(tech_response)); - if (retval) - return retval; - - /* - * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optimize the code away to - * nothing. - */ - technology = le32_to_cpu(tech_response.technology); - switch (technology) { - case GB_BATTERY_TECH_NiMH: - technology = POWER_SUPPLY_TECHNOLOGY_NiMH; - break; - case GB_BATTERY_TECH_LION: - technology = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case GB_BATTERY_TECH_LIPO: - technology = POWER_SUPPLY_TECHNOLOGY_LIPO; - break; - case GB_BATTERY_TECH_LiFe: - technology = POWER_SUPPLY_TECHNOLOGY_LiFe; - break; - case GB_BATTERY_TECH_NiCd: - technology = POWER_SUPPLY_TECHNOLOGY_NiCd; - break; - case GB_BATTERY_TECH_LiMn: - technology = POWER_SUPPLY_TECHNOLOGY_LiMn; - break; - case GB_BATTERY_TECH_UNKNOWN: - default: - technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; - break; - } - return technology; -} - -static int get_status(struct gb_battery *gb) -{ - struct gb_battery_status_response status_response; - u16 battery_status; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS, - NULL, 0, - &status_response, sizeof(status_response)); - if (retval) - return retval; - - /* - * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optimize the code away to - * nothing. - */ - battery_status = le16_to_cpu(status_response.battery_status); - switch (battery_status) { - case GB_BATTERY_STATUS_CHARGING: - battery_status = POWER_SUPPLY_STATUS_CHARGING; - break; - case GB_BATTERY_STATUS_DISCHARGING: - battery_status = POWER_SUPPLY_STATUS_DISCHARGING; - break; - case GB_BATTERY_STATUS_NOT_CHARGING: - battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; - break; - case GB_BATTERY_STATUS_FULL: - battery_status = POWER_SUPPLY_STATUS_FULL; - break; - case GB_BATTERY_STATUS_UNKNOWN: - default: - battery_status = POWER_SUPPLY_STATUS_UNKNOWN; - break; - } - return battery_status; -} - -static int get_max_voltage(struct gb_battery *gb) -{ - struct gb_battery_max_voltage_response volt_response; - u32 max_voltage; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE, - NULL, 0, - &volt_response, sizeof(volt_response)); - if (retval) - return retval; - - max_voltage = le32_to_cpu(volt_response.max_voltage); - return max_voltage; -} - -static int get_percent_capacity(struct gb_battery *gb) -{ - struct gb_battery_capacity_response capacity_response; - u32 capacity; - int retval; - - retval = gb_operation_sync(gb->connection, - GB_BATTERY_TYPE_PERCENT_CAPACITY, - NULL, 0, &capacity_response, - sizeof(capacity_response)); - if (retval) - return retval; - - capacity = le32_to_cpu(capacity_response.capacity); - return capacity; -} - -static int get_temp(struct gb_battery *gb) -{ - struct gb_battery_temperature_response temp_response; - u32 temperature; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE, - NULL, 0, - &temp_response, sizeof(temp_response)); - if (retval) - return retval; - - temperature = le32_to_cpu(temp_response.temperature); - return temperature; -} - -static int get_voltage(struct gb_battery *gb) -{ - struct gb_battery_voltage_response voltage_response; - u32 voltage; - int retval; - - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE, - NULL, 0, - &voltage_response, sizeof(voltage_response)); - if (retval) - return retval; - - voltage = le32_to_cpu(voltage_response.voltage); - return voltage; -} - -static int get_property(struct power_supply *b, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct gb_battery *gb = to_gb_battery(b); - - switch (psp) { - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = get_tech(gb); - break; - - case POWER_SUPPLY_PROP_STATUS: - val->intval = get_status(gb); - break; - - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = get_max_voltage(gb); - break; - - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = get_percent_capacity(gb); - break; - - case POWER_SUPPLY_PROP_TEMP: - val->intval = get_temp(gb); - break; - - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = get_voltage(gb); - break; - - default: - return -EINVAL; - } - - return (val->intval < 0) ? val->intval : 0; -} - -// FIXME - verify this list, odds are some can be removed and others added. -static enum power_supply_property battery_props[] = { - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_VOLTAGE_NOW, -}; - -#ifdef DRIVER_OWNS_PSY_STRUCT -static int init_and_register(struct gb_connection *connection, - struct gb_battery *gb) -{ - // FIXME - get a better (i.e. unique) name - // FIXME - anything else needs to be set? - gb->bat.name = "gb_battery"; - gb->bat.type = POWER_SUPPLY_TYPE_BATTERY; - gb->bat.properties = battery_props; - gb->bat.num_properties = ARRAY_SIZE(battery_props); - gb->bat.get_property = get_property; - - return power_supply_register(&connection->bundle->dev, &gb->bat); -} -#else -static int init_and_register(struct gb_connection *connection, - struct gb_battery *gb) -{ - struct power_supply_config cfg = {}; - - cfg.drv_data = gb; - - // FIXME - get a better (i.e. unique) name - // FIXME - anything else needs to be set? - gb->desc.name = "gb_battery"; - gb->desc.type = POWER_SUPPLY_TYPE_BATTERY; - gb->desc.properties = battery_props; - gb->desc.num_properties = ARRAY_SIZE(battery_props); - gb->desc.get_property = get_property; - - gb->bat = power_supply_register(&connection->bundle->dev, - &gb->desc, &cfg); - if (IS_ERR(gb->bat)) - return PTR_ERR(gb->bat); - - return 0; -} -#endif - -static int gb_battery_connection_init(struct gb_connection *connection) -{ - struct gb_battery *gb; - int retval; - - gb = kzalloc(sizeof(*gb), GFP_KERNEL); - if (!gb) - return -ENOMEM; - - gb->connection = connection; - connection->private = gb; - - retval = init_and_register(connection, gb); - if (retval) - kfree(gb); - - return retval; -} - -static void gb_battery_connection_exit(struct gb_connection *connection) -{ - struct gb_battery *gb = connection->private; - -#ifdef DRIVER_OWNS_PSY_STRUCT - power_supply_unregister(&gb->bat); -#else - power_supply_unregister(gb->bat); -#endif - kfree(gb); -} - -static struct gb_protocol battery_protocol = { - .name = "battery", - .id = GREYBUS_PROTOCOL_BATTERY, - .major = GB_BATTERY_VERSION_MAJOR, - .minor = GB_BATTERY_VERSION_MINOR, - .connection_init = gb_battery_connection_init, - .connection_exit = gb_battery_connection_exit, - .request_recv = NULL, /* no incoming requests */ -}; - -gb_protocol_driver(&battery_protocol); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c new file mode 100644 index 000000000000..e119775fb19b --- /dev/null +++ b/drivers/staging/greybus/power_supply.c @@ -0,0 +1,317 @@ +/* + * Battery driver for a Greybus module. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include "greybus.h" + +struct gb_battery { + /* + * The power supply api changed in 4.1, so handle both the old + * and new apis in the same driver for now, until this is merged + * upstream, when all of these version checks can be removed. + */ +#ifdef DRIVER_OWNS_PSY_STRUCT + struct power_supply bat; +#define to_gb_battery(x) container_of(x, struct gb_battery, bat) +#else + struct power_supply *bat; + struct power_supply_desc desc; +#define to_gb_battery(x) power_supply_get_drvdata(x) +#endif + // FIXME + // we will want to keep the battery stats in here as we will be getting + // updates from the SVC "on the fly" so we don't have to always go ask + // the battery for some information. Hopefully... + struct gb_connection *connection; + +}; + +static int get_tech(struct gb_battery *gb) +{ + struct gb_battery_technology_response tech_response; + u32 technology; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY, + NULL, 0, + &tech_response, sizeof(tech_response)); + if (retval) + return retval; + + /* + * Map greybus values to power_supply values. Hopefully these are + * "identical" which should allow gcc to optimize the code away to + * nothing. + */ + technology = le32_to_cpu(tech_response.technology); + switch (technology) { + case GB_BATTERY_TECH_NiMH: + technology = POWER_SUPPLY_TECHNOLOGY_NiMH; + break; + case GB_BATTERY_TECH_LION: + technology = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case GB_BATTERY_TECH_LIPO: + technology = POWER_SUPPLY_TECHNOLOGY_LIPO; + break; + case GB_BATTERY_TECH_LiFe: + technology = POWER_SUPPLY_TECHNOLOGY_LiFe; + break; + case GB_BATTERY_TECH_NiCd: + technology = POWER_SUPPLY_TECHNOLOGY_NiCd; + break; + case GB_BATTERY_TECH_LiMn: + technology = POWER_SUPPLY_TECHNOLOGY_LiMn; + break; + case GB_BATTERY_TECH_UNKNOWN: + default: + technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + break; + } + return technology; +} + +static int get_status(struct gb_battery *gb) +{ + struct gb_battery_status_response status_response; + u16 battery_status; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS, + NULL, 0, + &status_response, sizeof(status_response)); + if (retval) + return retval; + + /* + * Map greybus values to power_supply values. Hopefully these are + * "identical" which should allow gcc to optimize the code away to + * nothing. + */ + battery_status = le16_to_cpu(status_response.battery_status); + switch (battery_status) { + case GB_BATTERY_STATUS_CHARGING: + battery_status = POWER_SUPPLY_STATUS_CHARGING; + break; + case GB_BATTERY_STATUS_DISCHARGING: + battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case GB_BATTERY_STATUS_NOT_CHARGING: + battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case GB_BATTERY_STATUS_FULL: + battery_status = POWER_SUPPLY_STATUS_FULL; + break; + case GB_BATTERY_STATUS_UNKNOWN: + default: + battery_status = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + return battery_status; +} + +static int get_max_voltage(struct gb_battery *gb) +{ + struct gb_battery_max_voltage_response volt_response; + u32 max_voltage; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE, + NULL, 0, + &volt_response, sizeof(volt_response)); + if (retval) + return retval; + + max_voltage = le32_to_cpu(volt_response.max_voltage); + return max_voltage; +} + +static int get_percent_capacity(struct gb_battery *gb) +{ + struct gb_battery_capacity_response capacity_response; + u32 capacity; + int retval; + + retval = gb_operation_sync(gb->connection, + GB_BATTERY_TYPE_PERCENT_CAPACITY, + NULL, 0, &capacity_response, + sizeof(capacity_response)); + if (retval) + return retval; + + capacity = le32_to_cpu(capacity_response.capacity); + return capacity; +} + +static int get_temp(struct gb_battery *gb) +{ + struct gb_battery_temperature_response temp_response; + u32 temperature; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE, + NULL, 0, + &temp_response, sizeof(temp_response)); + if (retval) + return retval; + + temperature = le32_to_cpu(temp_response.temperature); + return temperature; +} + +static int get_voltage(struct gb_battery *gb) +{ + struct gb_battery_voltage_response voltage_response; + u32 voltage; + int retval; + + retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE, + NULL, 0, + &voltage_response, sizeof(voltage_response)); + if (retval) + return retval; + + voltage = le32_to_cpu(voltage_response.voltage); + return voltage; +} + +static int get_property(struct power_supply *b, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct gb_battery *gb = to_gb_battery(b); + + switch (psp) { + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = get_tech(gb); + break; + + case POWER_SUPPLY_PROP_STATUS: + val->intval = get_status(gb); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = get_max_voltage(gb); + break; + + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = get_percent_capacity(gb); + break; + + case POWER_SUPPLY_PROP_TEMP: + val->intval = get_temp(gb); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = get_voltage(gb); + break; + + default: + return -EINVAL; + } + + return (val->intval < 0) ? val->intval : 0; +} + +// FIXME - verify this list, odds are some can be removed and others added. +static enum power_supply_property battery_props[] = { + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +#ifdef DRIVER_OWNS_PSY_STRUCT +static int init_and_register(struct gb_connection *connection, + struct gb_battery *gb) +{ + // FIXME - get a better (i.e. unique) name + // FIXME - anything else needs to be set? + gb->bat.name = "gb_battery"; + gb->bat.type = POWER_SUPPLY_TYPE_BATTERY; + gb->bat.properties = battery_props; + gb->bat.num_properties = ARRAY_SIZE(battery_props); + gb->bat.get_property = get_property; + + return power_supply_register(&connection->bundle->dev, &gb->bat); +} +#else +static int init_and_register(struct gb_connection *connection, + struct gb_battery *gb) +{ + struct power_supply_config cfg = {}; + + cfg.drv_data = gb; + + // FIXME - get a better (i.e. unique) name + // FIXME - anything else needs to be set? + gb->desc.name = "gb_battery"; + gb->desc.type = POWER_SUPPLY_TYPE_BATTERY; + gb->desc.properties = battery_props; + gb->desc.num_properties = ARRAY_SIZE(battery_props); + gb->desc.get_property = get_property; + + gb->bat = power_supply_register(&connection->bundle->dev, + &gb->desc, &cfg); + if (IS_ERR(gb->bat)) + return PTR_ERR(gb->bat); + + return 0; +} +#endif + +static int gb_battery_connection_init(struct gb_connection *connection) +{ + struct gb_battery *gb; + int retval; + + gb = kzalloc(sizeof(*gb), GFP_KERNEL); + if (!gb) + return -ENOMEM; + + gb->connection = connection; + connection->private = gb; + + retval = init_and_register(connection, gb); + if (retval) + kfree(gb); + + return retval; +} + +static void gb_battery_connection_exit(struct gb_connection *connection) +{ + struct gb_battery *gb = connection->private; + +#ifdef DRIVER_OWNS_PSY_STRUCT + power_supply_unregister(&gb->bat); +#else + power_supply_unregister(gb->bat); +#endif + kfree(gb); +} + +static struct gb_protocol battery_protocol = { + .name = "battery", + .id = GREYBUS_PROTOCOL_BATTERY, + .major = GB_BATTERY_VERSION_MAJOR, + .minor = GB_BATTERY_VERSION_MINOR, + .connection_init = gb_battery_connection_init, + .connection_exit = gb_battery_connection_exit, + .request_recv = NULL, /* no incoming requests */ +}; + +gb_protocol_driver(&battery_protocol); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 2724be0305bc8def0ad05b226fb9867549b35c16 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 12 Nov 2015 15:36:00 +0000 Subject: greybus: battery: move implementation to power_supply Rename protocol to POWER_SUPPLY and implementation details from battery to power_supply. Also fix some tabs between define and macro name. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 4 +- drivers/staging/greybus/greybus_protocols.h | 78 +++++++------- drivers/staging/greybus/power_supply.c | 155 ++++++++++++++-------------- 3 files changed, 120 insertions(+), 117 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index f29f470909c9..c36c4d1d30f9 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -30,7 +30,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_HID = 0x05, GREYBUS_PROTOCOL_USB = 0x06, GREYBUS_PROTOCOL_SDIO = 0x07, - GREYBUS_PROTOCOL_BATTERY = 0x08, + GREYBUS_PROTOCOL_POWER_SUPPLY = 0x08, GREYBUS_PROTOCOL_PWM = 0x09, /* 0x0a is unused */ GREYBUS_PROTOCOL_SPI = 0x0b, @@ -58,7 +58,7 @@ enum greybus_class_type { GREYBUS_CLASS_HID = 0x05, GREYBUS_CLASS_USB = 0x06, GREYBUS_CLASS_SDIO = 0x07, - GREYBUS_CLASS_BATTERY = 0x08, + GREYBUS_CLASS_POWER_SUPPLY = 0x08, GREYBUS_CLASS_PWM = 0x09, /* 0x0a is unused */ GREYBUS_CLASS_SPI = 0x0b, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 1142b2094901..b2380403edc6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -198,60 +198,60 @@ struct gb_firmware_ready_to_boot_request { /* Firmware protocol Ready to boot response has no payload */ -/* BATTERY */ - -/* Version of the Greybus battery protocol we support */ -#define GB_BATTERY_VERSION_MAJOR 0x00 -#define GB_BATTERY_VERSION_MINOR 0x01 - -/* Greybus battery request types */ -#define GB_BATTERY_TYPE_TECHNOLOGY 0x02 -#define GB_BATTERY_TYPE_STATUS 0x03 -#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04 -#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05 -#define GB_BATTERY_TYPE_TEMPERATURE 0x06 -#define GB_BATTERY_TYPE_VOLTAGE 0x07 -#define GB_BATTERY_TYPE_CURRENT 0x08 -#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX -#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX - -/* Should match up with battery types in linux/power_supply.h */ -#define GB_BATTERY_TECH_UNKNOWN 0x0000 -#define GB_BATTERY_TECH_NiMH 0x0001 -#define GB_BATTERY_TECH_LION 0x0002 -#define GB_BATTERY_TECH_LIPO 0x0003 -#define GB_BATTERY_TECH_LiFe 0x0004 -#define GB_BATTERY_TECH_NiCd 0x0005 -#define GB_BATTERY_TECH_LiMn 0x0006 - -struct gb_battery_technology_response { +/* Power Supply */ + +/* Version of the Greybus power supply protocol we support */ +#define GB_POWER_SUPPLY_VERSION_MAJOR 0x00 +#define GB_POWER_SUPPLY_VERSION_MINOR 0x01 + +/* Greybus power supply request types */ +#define GB_POWER_SUPPLY_TYPE_TECHNOLOGY 0x02 +#define GB_POWER_SUPPLY_TYPE_STATUS 0x03 +#define GB_POWER_SUPPLY_TYPE_MAX_VOLTAGE 0x04 +#define GB_POWER_SUPPLY_TYPE_PERCENT_CAPACITY 0x05 +#define GB_POWER_SUPPLY_TYPE_TEMPERATURE 0x06 +#define GB_POWER_SUPPLY_TYPE_VOLTAGE 0x07 +#define GB_POWER_SUPPLY_TYPE_CURRENT 0x08 +#define GB_POWER_SUPPLY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX +#define GB_POWER_SUPPLY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX + +/* Should match up with battery technology types in linux/power_supply.h */ +#define GB_POWER_SUPPLY_TECH_UNKNOWN 0x0000 +#define GB_POWER_SUPPLY_TECH_NiMH 0x0001 +#define GB_POWER_SUPPLY_TECH_LION 0x0002 +#define GB_POWER_SUPPLY_TECH_LIPO 0x0003 +#define GB_POWER_SUPPLY_TECH_LiFe 0x0004 +#define GB_POWER_SUPPLY_TECH_NiCd 0x0005 +#define GB_POWER_SUPPLY_TECH_LiMn 0x0006 + +struct gb_power_supply_technology_response { __le32 technology; } __packed; -/* Should match up with battery status in linux/power_supply.h */ -#define GB_BATTERY_STATUS_UNKNOWN 0x0000 -#define GB_BATTERY_STATUS_CHARGING 0x0001 -#define GB_BATTERY_STATUS_DISCHARGING 0x0002 -#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003 -#define GB_BATTERY_STATUS_FULL 0x0004 +/* Should match up with power supply status in linux/power_supply.h */ +#define GB_POWER_SUPPLY_STATUS_UNKNOWN 0x0000 +#define GB_POWER_SUPPLY_STATUS_CHARGING 0x0001 +#define GB_POWER_SUPPLY_STATUS_DISCHARGING 0x0002 +#define GB_POWER_SUPPLY_STATUS_NOT_CHARGING 0x0003 +#define GB_POWER_SUPPLY_STATUS_FULL 0x0004 -struct gb_battery_status_response { - __le16 battery_status; +struct gb_power_supply_status_response { + __le16 psy_status; } __packed; -struct gb_battery_max_voltage_response { +struct gb_power_supply_max_voltage_response { __le32 max_voltage; } __packed; -struct gb_battery_capacity_response { +struct gb_power_supply_capacity_response { __le32 capacity; } __packed; -struct gb_battery_temperature_response { +struct gb_power_supply_temperature_response { __le32 temperature; } __packed; -struct gb_battery_voltage_response { +struct gb_power_supply_voltage_response { __le32 voltage; } __packed; diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index e119775fb19b..d7797a24e00d 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -1,5 +1,5 @@ /* - * Battery driver for a Greybus module. + * Power Supply driver for a Greybus module. * * Copyright 2014 Google Inc. * Copyright 2014 Linaro Ltd. @@ -13,35 +13,36 @@ #include #include "greybus.h" -struct gb_battery { +struct gb_power_supply { /* * The power supply api changed in 4.1, so handle both the old * and new apis in the same driver for now, until this is merged * upstream, when all of these version checks can be removed. */ #ifdef DRIVER_OWNS_PSY_STRUCT - struct power_supply bat; -#define to_gb_battery(x) container_of(x, struct gb_battery, bat) + struct power_supply psy; +#define to_gb_power_supply(x) container_of(x, struct gb_power_supply, psy) #else - struct power_supply *bat; + struct power_supply *psy; struct power_supply_desc desc; -#define to_gb_battery(x) power_supply_get_drvdata(x) +#define to_gb_power_supply(x) power_supply_get_drvdata(x) #endif // FIXME - // we will want to keep the battery stats in here as we will be getting - // updates from the SVC "on the fly" so we don't have to always go ask - // the battery for some information. Hopefully... + // we will want to keep the power supply stats in here as we will be + // getting updates from the SVC "on the fly" so we don't have to always + // go ask the power supply for some information. Hopefully... struct gb_connection *connection; }; -static int get_tech(struct gb_battery *gb) +static int get_tech(struct gb_power_supply *gb) { - struct gb_battery_technology_response tech_response; + struct gb_power_supply_technology_response tech_response; u32 technology; int retval; - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY, + retval = gb_operation_sync(gb->connection, + GB_POWER_SUPPLY_TYPE_TECHNOLOGY, NULL, 0, &tech_response, sizeof(tech_response)); if (retval) @@ -54,25 +55,25 @@ static int get_tech(struct gb_battery *gb) */ technology = le32_to_cpu(tech_response.technology); switch (technology) { - case GB_BATTERY_TECH_NiMH: + case GB_POWER_SUPPLY_TECH_NiMH: technology = POWER_SUPPLY_TECHNOLOGY_NiMH; break; - case GB_BATTERY_TECH_LION: + case GB_POWER_SUPPLY_TECH_LION: technology = POWER_SUPPLY_TECHNOLOGY_LION; break; - case GB_BATTERY_TECH_LIPO: + case GB_POWER_SUPPLY_TECH_LIPO: technology = POWER_SUPPLY_TECHNOLOGY_LIPO; break; - case GB_BATTERY_TECH_LiFe: + case GB_POWER_SUPPLY_TECH_LiFe: technology = POWER_SUPPLY_TECHNOLOGY_LiFe; break; - case GB_BATTERY_TECH_NiCd: + case GB_POWER_SUPPLY_TECH_NiCd: technology = POWER_SUPPLY_TECHNOLOGY_NiCd; break; - case GB_BATTERY_TECH_LiMn: + case GB_POWER_SUPPLY_TECH_LiMn: technology = POWER_SUPPLY_TECHNOLOGY_LiMn; break; - case GB_BATTERY_TECH_UNKNOWN: + case GB_POWER_SUPPLY_TECH_UNKNOWN: default: technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; break; @@ -80,13 +81,13 @@ static int get_tech(struct gb_battery *gb) return technology; } -static int get_status(struct gb_battery *gb) +static int get_status(struct gb_power_supply *gb) { - struct gb_battery_status_response status_response; - u16 battery_status; + struct gb_power_supply_status_response status_response; + u16 psy_status; int retval; - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS, + retval = gb_operation_sync(gb->connection, GB_POWER_SUPPLY_TYPE_STATUS, NULL, 0, &status_response, sizeof(status_response)); if (retval) @@ -97,35 +98,36 @@ static int get_status(struct gb_battery *gb) * "identical" which should allow gcc to optimize the code away to * nothing. */ - battery_status = le16_to_cpu(status_response.battery_status); - switch (battery_status) { - case GB_BATTERY_STATUS_CHARGING: - battery_status = POWER_SUPPLY_STATUS_CHARGING; + psy_status = le16_to_cpu(status_response.psy_status); + switch (psy_status) { + case GB_POWER_SUPPLY_STATUS_CHARGING: + psy_status = POWER_SUPPLY_STATUS_CHARGING; break; - case GB_BATTERY_STATUS_DISCHARGING: - battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + case GB_POWER_SUPPLY_STATUS_DISCHARGING: + psy_status = POWER_SUPPLY_STATUS_DISCHARGING; break; - case GB_BATTERY_STATUS_NOT_CHARGING: - battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + case GB_POWER_SUPPLY_STATUS_NOT_CHARGING: + psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING; break; - case GB_BATTERY_STATUS_FULL: - battery_status = POWER_SUPPLY_STATUS_FULL; + case GB_POWER_SUPPLY_STATUS_FULL: + psy_status = POWER_SUPPLY_STATUS_FULL; break; - case GB_BATTERY_STATUS_UNKNOWN: + case GB_POWER_SUPPLY_STATUS_UNKNOWN: default: - battery_status = POWER_SUPPLY_STATUS_UNKNOWN; + psy_status = POWER_SUPPLY_STATUS_UNKNOWN; break; } - return battery_status; + return psy_status; } -static int get_max_voltage(struct gb_battery *gb) +static int get_max_voltage(struct gb_power_supply *gb) { - struct gb_battery_max_voltage_response volt_response; + struct gb_power_supply_max_voltage_response volt_response; u32 max_voltage; int retval; - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE, + retval = gb_operation_sync(gb->connection, + GB_POWER_SUPPLY_TYPE_MAX_VOLTAGE, NULL, 0, &volt_response, sizeof(volt_response)); if (retval) @@ -135,14 +137,14 @@ static int get_max_voltage(struct gb_battery *gb) return max_voltage; } -static int get_percent_capacity(struct gb_battery *gb) +static int get_percent_capacity(struct gb_power_supply *gb) { - struct gb_battery_capacity_response capacity_response; + struct gb_power_supply_capacity_response capacity_response; u32 capacity; int retval; retval = gb_operation_sync(gb->connection, - GB_BATTERY_TYPE_PERCENT_CAPACITY, + GB_POWER_SUPPLY_TYPE_PERCENT_CAPACITY, NULL, 0, &capacity_response, sizeof(capacity_response)); if (retval) @@ -152,13 +154,14 @@ static int get_percent_capacity(struct gb_battery *gb) return capacity; } -static int get_temp(struct gb_battery *gb) +static int get_temp(struct gb_power_supply *gb) { - struct gb_battery_temperature_response temp_response; + struct gb_power_supply_temperature_response temp_response; u32 temperature; int retval; - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE, + retval = gb_operation_sync(gb->connection, + GB_POWER_SUPPLY_TYPE_TEMPERATURE, NULL, 0, &temp_response, sizeof(temp_response)); if (retval) @@ -168,13 +171,13 @@ static int get_temp(struct gb_battery *gb) return temperature; } -static int get_voltage(struct gb_battery *gb) +static int get_voltage(struct gb_power_supply *gb) { - struct gb_battery_voltage_response voltage_response; + struct gb_power_supply_voltage_response voltage_response; u32 voltage; int retval; - retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE, + retval = gb_operation_sync(gb->connection, GB_POWER_SUPPLY_TYPE_VOLTAGE, NULL, 0, &voltage_response, sizeof(voltage_response)); if (retval) @@ -188,7 +191,7 @@ static int get_property(struct power_supply *b, enum power_supply_property psp, union power_supply_propval *val) { - struct gb_battery *gb = to_gb_battery(b); + struct gb_power_supply *gb = to_gb_power_supply(b); switch (psp) { case POWER_SUPPLY_PROP_TECHNOLOGY: @@ -223,7 +226,7 @@ static int get_property(struct power_supply *b, } // FIXME - verify this list, odds are some can be removed and others added. -static enum power_supply_property battery_props[] = { +static enum power_supply_property psy_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, @@ -238,17 +241,17 @@ static int init_and_register(struct gb_connection *connection, { // FIXME - get a better (i.e. unique) name // FIXME - anything else needs to be set? - gb->bat.name = "gb_battery"; - gb->bat.type = POWER_SUPPLY_TYPE_BATTERY; - gb->bat.properties = battery_props; - gb->bat.num_properties = ARRAY_SIZE(battery_props); - gb->bat.get_property = get_property; + gb->psy.name = "gb_battery"; + gb->psy.type = POWER_SUPPLY_TYPE_BATTERY; + gb->psy.properties = psy_props; + gb->psy.num_properties = ARRAY_SIZE(psy_props); + gb->psy.get_property = get_property; - return power_supply_register(&connection->bundle->dev, &gb->bat); + return power_supply_register(&connection->bundle->dev, &gb->psy); } #else static int init_and_register(struct gb_connection *connection, - struct gb_battery *gb) + struct gb_power_supply *gb) { struct power_supply_config cfg = {}; @@ -258,22 +261,22 @@ static int init_and_register(struct gb_connection *connection, // FIXME - anything else needs to be set? gb->desc.name = "gb_battery"; gb->desc.type = POWER_SUPPLY_TYPE_BATTERY; - gb->desc.properties = battery_props; - gb->desc.num_properties = ARRAY_SIZE(battery_props); + gb->desc.properties = psy_props; + gb->desc.num_properties = ARRAY_SIZE(psy_props); gb->desc.get_property = get_property; - gb->bat = power_supply_register(&connection->bundle->dev, + gb->psy = power_supply_register(&connection->bundle->dev, &gb->desc, &cfg); - if (IS_ERR(gb->bat)) - return PTR_ERR(gb->bat); + if (IS_ERR(gb->psy)) + return PTR_ERR(gb->psy); return 0; } #endif -static int gb_battery_connection_init(struct gb_connection *connection) +static int gb_power_supply_connection_init(struct gb_connection *connection) { - struct gb_battery *gb; + struct gb_power_supply *gb; int retval; gb = kzalloc(sizeof(*gb), GFP_KERNEL); @@ -290,28 +293,28 @@ static int gb_battery_connection_init(struct gb_connection *connection) return retval; } -static void gb_battery_connection_exit(struct gb_connection *connection) +static void gb_power_supply_connection_exit(struct gb_connection *connection) { - struct gb_battery *gb = connection->private; + struct gb_power_supply *gb = connection->private; #ifdef DRIVER_OWNS_PSY_STRUCT - power_supply_unregister(&gb->bat); + power_supply_unregister(&gb->psy); #else - power_supply_unregister(gb->bat); + power_supply_unregister(gb->psy); #endif kfree(gb); } -static struct gb_protocol battery_protocol = { - .name = "battery", - .id = GREYBUS_PROTOCOL_BATTERY, - .major = GB_BATTERY_VERSION_MAJOR, - .minor = GB_BATTERY_VERSION_MINOR, - .connection_init = gb_battery_connection_init, - .connection_exit = gb_battery_connection_exit, +static struct gb_protocol power_supply_protocol = { + .name = "power_supply", + .id = GREYBUS_PROTOCOL_POWER_SUPPLY, + .major = GB_POWER_SUPPLY_VERSION_MAJOR, + .minor = GB_POWER_SUPPLY_VERSION_MINOR, + .connection_init = gb_power_supply_connection_init, + .connection_exit = gb_power_supply_connection_exit, .request_recv = NULL, /* no incoming requests */ }; -gb_protocol_driver(&battery_protocol); +gb_protocol_driver(&power_supply_protocol); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 783e373a9fead8591e5d22fd12e76a99b234337e Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 12 Nov 2015 15:36:01 +0000 Subject: greybus: makefile: add power_supply config check Add check for POWER_SUPPLY config option to guarantee that it is enabled. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index d479a0291ed7..c3572b35e992 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -51,7 +51,7 @@ INSTALL_MOD_PATH ?= /.. PWD := $(shell pwd) # kernel config option that shall be enable -CONFIG_OPTIONS_ENABLE := PWM SYSFS SPI USB SND_SOC MMC LEDS_CLASS +CONFIG_OPTIONS_ENABLE := POWER_SUPPLY PWM SYSFS SPI USB SND_SOC MMC LEDS_CLASS # kernel config option that shall be disable CONFIG_OPTIONS_DISABLE := -- cgit v1.2.3-59-g8ed1b From ffe2e2487a388cf01ec44439346363aa8d654cd4 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 12 Nov 2015 15:36:02 +0000 Subject: greybus: power_supply: rework and operation changes This is a major rework and changes to the current implementation of the battery protocol. The previous implementation lack the support of a more dynamic handle of power supply properties and updating of status. Also, reflect the actual state of the greybus specification So, with this new approach a set of operations to fetch the battery module configuration and properties is add, new methods to cache and update the values of properties, new operation to set properties if declared writable and an event operation that can be triggered by the module to force an update read on the properties values. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 187 ++++-- drivers/staging/greybus/kernel_ver.h | 7 + drivers/staging/greybus/power_supply.c | 844 +++++++++++++++++++++------- 3 files changed, 793 insertions(+), 245 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b2380403edc6..9fcbbe4eb636 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -205,17 +205,14 @@ struct gb_firmware_ready_to_boot_request { #define GB_POWER_SUPPLY_VERSION_MINOR 0x01 /* Greybus power supply request types */ -#define GB_POWER_SUPPLY_TYPE_TECHNOLOGY 0x02 -#define GB_POWER_SUPPLY_TYPE_STATUS 0x03 -#define GB_POWER_SUPPLY_TYPE_MAX_VOLTAGE 0x04 -#define GB_POWER_SUPPLY_TYPE_PERCENT_CAPACITY 0x05 -#define GB_POWER_SUPPLY_TYPE_TEMPERATURE 0x06 -#define GB_POWER_SUPPLY_TYPE_VOLTAGE 0x07 -#define GB_POWER_SUPPLY_TYPE_CURRENT 0x08 -#define GB_POWER_SUPPLY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX -#define GB_POWER_SUPPLY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX - -/* Should match up with battery technology types in linux/power_supply.h */ +#define GB_POWER_SUPPLY_TYPE_GET_SUPPLIES 0x02 +#define GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION 0x03 +#define GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS 0x04 +#define GB_POWER_SUPPLY_TYPE_GET_PROPERTY 0x05 +#define GB_POWER_SUPPLY_TYPE_SET_PROPERTY 0x06 +#define GB_POWER_SUPPLY_TYPE_EVENT 0x07 + +/* Should match up with battery technologies in linux/power_supply.h */ #define GB_POWER_SUPPLY_TECH_UNKNOWN 0x0000 #define GB_POWER_SUPPLY_TECH_NiMH 0x0001 #define GB_POWER_SUPPLY_TECH_LION 0x0002 @@ -224,35 +221,145 @@ struct gb_firmware_ready_to_boot_request { #define GB_POWER_SUPPLY_TECH_NiCd 0x0005 #define GB_POWER_SUPPLY_TECH_LiMn 0x0006 -struct gb_power_supply_technology_response { - __le32 technology; -} __packed; - -/* Should match up with power supply status in linux/power_supply.h */ -#define GB_POWER_SUPPLY_STATUS_UNKNOWN 0x0000 -#define GB_POWER_SUPPLY_STATUS_CHARGING 0x0001 -#define GB_POWER_SUPPLY_STATUS_DISCHARGING 0x0002 -#define GB_POWER_SUPPLY_STATUS_NOT_CHARGING 0x0003 -#define GB_POWER_SUPPLY_STATUS_FULL 0x0004 - -struct gb_power_supply_status_response { - __le16 psy_status; -} __packed; - -struct gb_power_supply_max_voltage_response { - __le32 max_voltage; -} __packed; - -struct gb_power_supply_capacity_response { - __le32 capacity; -} __packed; - -struct gb_power_supply_temperature_response { - __le32 temperature; -} __packed; - -struct gb_power_supply_voltage_response { - __le32 voltage; +/* Should match up with power supply types in linux/power_supply.h */ +#define GB_POWER_SUPPLY_UNKNOWN_TYPE 0x0000 +#define GB_POWER_SUPPLY_BATTERY_TYPE 0x0001 +#define GB_POWER_SUPPLY_UPS_TYPE 0x0002 +#define GB_POWER_SUPPLY_MAINS_TYPE 0x0003 +#define GB_POWER_SUPPLY_USB_TYPE 0x0004 +#define GB_POWER_SUPPLY_USB_DCP_TYPE 0x0005 +#define GB_POWER_SUPPLY_USB_CDP_TYPE 0x0006 +#define GB_POWER_SUPPLY_USB_ACA_TYPE 0x0007 + +/* Should match up with power supply health in linux/power_supply.h */ +#define GB_POWER_SUPPLY_HEALTH_UNKNOWN 0x0000 +#define GB_POWER_SUPPLY_HEALTH_GOOD 0x0001 +#define GB_POWER_SUPPLY_HEALTH_OVERHEAT 0x0002 +#define GB_POWER_SUPPLY_HEALTH_DEAD 0x0003 +#define GB_POWER_SUPPLY_HEALTH_OVERVOLTAGE 0x0004 +#define GB_POWER_SUPPLY_HEALTH_UNSPEC_FAILURE 0x0005 +#define GB_POWER_SUPPLY_HEALTH_COLD 0x0006 +#define GB_POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE 0x0007 +#define GB_POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE 0x0008 + +/* Should match up with battery status in linux/power_supply.h */ +#define GB_POWER_SUPPLY_STATUS_UNKNOWN 0x0000 +#define GB_POWER_SUPPLY_STATUS_CHARGING 0x0001 +#define GB_POWER_SUPPLY_STATUS_DISCHARGING 0x0002 +#define GB_POWER_SUPPLY_STATUS_NOT_CHARGING 0x0003 +#define GB_POWER_SUPPLY_STATUS_FULL 0x0004 + +struct gb_power_supply_get_supplies_response { + __u8 supplies_count; +} __packed; + +struct gb_power_supply_get_description_request { + __u8 psy_id; +} __packed; + +struct gb_power_supply_get_description_response { + __u8 manufacturer[32]; + __u8 model[32]; + __u8 serial_number[32]; + __le16 type; + __u8 properties_count; +} __packed; + +struct gb_power_supply_props_desc { + __u8 property; +#define GB_POWER_SUPPLY_PROP_STATUS 0x00 +#define GB_POWER_SUPPLY_PROP_CHARGE_TYPE 0x01 +#define GB_POWER_SUPPLY_PROP_HEALTH 0x02 +#define GB_POWER_SUPPLY_PROP_PRESENT 0x03 +#define GB_POWER_SUPPLY_PROP_ONLINE 0x04 +#define GB_POWER_SUPPLY_PROP_AUTHENTIC 0x05 +#define GB_POWER_SUPPLY_PROP_TECHNOLOGY 0x06 +#define GB_POWER_SUPPLY_PROP_CYCLE_COUNT 0x07 +#define GB_POWER_SUPPLY_PROP_VOLTAGE_MAX 0x08 +#define GB_POWER_SUPPLY_PROP_VOLTAGE_MIN 0x09 +#define GB_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN 0x0A +#define GB_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN 0x0B +#define GB_POWER_SUPPLY_PROP_VOLTAGE_NOW 0x0C +#define GB_POWER_SUPPLY_PROP_VOLTAGE_AVG 0x0D +#define GB_POWER_SUPPLY_PROP_VOLTAGE_OCV 0x0E +#define GB_POWER_SUPPLY_PROP_VOLTAGE_BOOT 0x0F +#define GB_POWER_SUPPLY_PROP_CURRENT_MAX 0x10 +#define GB_POWER_SUPPLY_PROP_CURRENT_NOW 0x11 +#define GB_POWER_SUPPLY_PROP_CURRENT_AVG 0x12 +#define GB_POWER_SUPPLY_PROP_CURRENT_BOOT 0x13 +#define GB_POWER_SUPPLY_PROP_POWER_NOW 0x14 +#define GB_POWER_SUPPLY_PROP_POWER_AVG 0x15 +#define GB_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN 0x16 +#define GB_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN 0x17 +#define GB_POWER_SUPPLY_PROP_CHARGE_FULL 0x18 +#define GB_POWER_SUPPLY_PROP_CHARGE_EMPTY 0x19 +#define GB_POWER_SUPPLY_PROP_CHARGE_NOW 0x1A +#define GB_POWER_SUPPLY_PROP_CHARGE_AVG 0x1B +#define GB_POWER_SUPPLY_PROP_CHARGE_COUNTER 0x1C +#define GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT 0x1D +#define GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX 0x1E +#define GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE 0x1F +#define GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX 0x20 +#define GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT 0x21 +#define GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX 0x22 +#define GB_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT 0x23 +#define GB_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN 0x24 +#define GB_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN 0x25 +#define GB_POWER_SUPPLY_PROP_ENERGY_FULL 0x26 +#define GB_POWER_SUPPLY_PROP_ENERGY_EMPTY 0x27 +#define GB_POWER_SUPPLY_PROP_ENERGY_NOW 0x28 +#define GB_POWER_SUPPLY_PROP_ENERGY_AVG 0x29 +#define GB_POWER_SUPPLY_PROP_CAPACITY 0x2A +#define GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN 0x2B +#define GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX 0x2C +#define GB_POWER_SUPPLY_PROP_CAPACITY_LEVEL 0x2D +#define GB_POWER_SUPPLY_PROP_TEMP 0x2E +#define GB_POWER_SUPPLY_PROP_TEMP_MAX 0x2F +#define GB_POWER_SUPPLY_PROP_TEMP_MIN 0x30 +#define GB_POWER_SUPPLY_PROP_TEMP_ALERT_MIN 0x31 +#define GB_POWER_SUPPLY_PROP_TEMP_ALERT_MAX 0x32 +#define GB_POWER_SUPPLY_PROP_TEMP_AMBIENT 0x33 +#define GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN 0x34 +#define GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX 0x35 +#define GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW 0x36 +#define GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG 0x37 +#define GB_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW 0x38 +#define GB_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG 0x39 +#define GB_POWER_SUPPLY_PROP_TYPE 0x3A +#define GB_POWER_SUPPLY_PROP_SCOPE 0x3B +#define GB_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT 0x3C +#define GB_POWER_SUPPLY_PROP_CALIBRATE 0x3D + __u8 is_writeable; +} __packed; + +struct gb_power_supply_get_property_descriptors_request { + __u8 psy_id; +} __packed; + +struct gb_power_supply_get_property_descriptors_response { + __u8 properties_count; + struct gb_power_supply_props_desc props[]; +} __packed; + +struct gb_power_supply_get_property_request { + __u8 psy_id; + __u8 property; +} __packed; + +struct gb_power_supply_get_property_response { + __le32 prop_val; +}; + +struct gb_power_supply_set_property_request { + __u8 psy_id; + __u8 property; + __le32 prop_val; +} __packed; + +struct gb_power_supply_event_request { + __u8 psy_id; + __u8 event; +#define GB_POWER_SUPPLY_UPDATE 0x01 } __packed; diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index f3cb1895c092..d9bf159329e5 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -289,4 +289,11 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev) #include #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +/* + * Power supply get by name need to drop reference after call + */ +#define PSY_HAVE_PUT +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index d7797a24e00d..4a7381650bb7 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -1,308 +1,742 @@ /* * Power Supply driver for a Greybus module. * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ #include #include -#include #include +#include + #include "greybus.h" +#define PROP_MAX 32 + +struct gb_power_supply_prop { + enum power_supply_property prop; + u32 val; + u32 previous_val; + bool is_writeable; +}; + struct gb_power_supply { - /* - * The power supply api changed in 4.1, so handle both the old - * and new apis in the same driver for now, until this is merged - * upstream, when all of these version checks can be removed. - */ + u8 id; #ifdef DRIVER_OWNS_PSY_STRUCT - struct power_supply psy; + struct power_supply psy; #define to_gb_power_supply(x) container_of(x, struct gb_power_supply, psy) #else - struct power_supply *psy; - struct power_supply_desc desc; + struct power_supply *psy; + struct power_supply_desc desc; #define to_gb_power_supply(x) power_supply_get_drvdata(x) #endif - // FIXME - // we will want to keep the power supply stats in here as we will be - // getting updates from the SVC "on the fly" so we don't have to always - // go ask the power supply for some information. Hopefully... - struct gb_connection *connection; + char name[64]; + struct gb_power_supplies *supplies; + struct delayed_work work; + char *manufacturer; + char *model_name; + char *serial_number; + u8 type; + u8 properties_count; + u8 properties_count_str; + unsigned long last_update; + unsigned int update_interval; + bool changed; + struct gb_power_supply_prop *props; + enum power_supply_property *props_raw; +}; + +struct gb_power_supplies { + struct gb_connection *connection; + u8 supplies_count; + struct gb_power_supply *supply; + struct mutex supplies_lock; +}; + +/* cache time in milliseconds, if cache_time is set to 0 cache is disable */ +static unsigned int cache_time = 1000; +/* + * update interval initial and maximum value, between the two will + * back-off exponential + */ +static unsigned int update_interval_init = 1 * HZ; +static unsigned int update_interval_max = 30 * HZ; + +struct gb_power_supply_changes { + enum power_supply_property prop; + u32 tolerance_change; +}; +static const struct gb_power_supply_changes psy_props_changes[] = { + { .prop = GB_POWER_SUPPLY_PROP_STATUS, + .tolerance_change = 0, + }, + { .prop = GB_POWER_SUPPLY_PROP_TEMP, + .tolerance_change = 500, + }, + { .prop = GB_POWER_SUPPLY_PROP_ONLINE, + .tolerance_change = 0, + }, }; -static int get_tech(struct gb_power_supply *gb) +static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy) { - struct gb_power_supply_technology_response tech_response; - u32 technology; - int retval; + return gbpsy->supplies->connection; +} - retval = gb_operation_sync(gb->connection, - GB_POWER_SUPPLY_TYPE_TECHNOLOGY, - NULL, 0, - &tech_response, sizeof(tech_response)); - if (retval) - return retval; +static struct gb_power_supply_prop *get_psy_prop(struct gb_power_supply *gbpsy, + enum power_supply_property psp) +{ + int i; - /* - * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optimize the code away to - * nothing. - */ - technology = le32_to_cpu(tech_response.technology); - switch (technology) { - case GB_POWER_SUPPLY_TECH_NiMH: - technology = POWER_SUPPLY_TECHNOLOGY_NiMH; - break; - case GB_POWER_SUPPLY_TECH_LION: - technology = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case GB_POWER_SUPPLY_TECH_LIPO: - technology = POWER_SUPPLY_TECHNOLOGY_LIPO; - break; - case GB_POWER_SUPPLY_TECH_LiFe: - technology = POWER_SUPPLY_TECHNOLOGY_LiFe; - break; - case GB_POWER_SUPPLY_TECH_NiCd: - technology = POWER_SUPPLY_TECHNOLOGY_NiCd; - break; - case GB_POWER_SUPPLY_TECH_LiMn: - technology = POWER_SUPPLY_TECHNOLOGY_LiMn; - break; - case GB_POWER_SUPPLY_TECH_UNKNOWN: - default: - technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; - break; + for (i = 0; i < gbpsy->properties_count; i++) + if (gbpsy->props[i].prop == psp) + return &gbpsy->props[i]; + return NULL; +} + +static int is_psy_prop_writeable(struct gb_power_supply *gbpsy, + enum power_supply_property psp) +{ + struct gb_power_supply_prop *prop; + + prop = get_psy_prop(gbpsy, psp); + if (!prop) + return -ENOENT; + return prop->is_writeable ? 1 : 0; +} + +static int is_prop_valint(enum power_supply_property psp) +{ + return ((psp < POWER_SUPPLY_PROP_MODEL_NAME) ? 1 : 0); +} + +static void next_interval(struct gb_power_supply *gbpsy) +{ + if (gbpsy->update_interval == update_interval_max) + return; + + /* do some exponential back-off in the update interval */ + gbpsy->update_interval *= 2; + if (gbpsy->update_interval > update_interval_max) + gbpsy->update_interval = update_interval_max; +} + +#ifdef DRIVER_OWNS_PSY_STRUCT +static void __gb_power_supply_changed(struct gb_power_supply *gbpsy) +{ + power_supply_changed(&gbpsy->psy); +} +#else +static void __gb_power_supply_changed(struct gb_power_supply *gbpsy) +{ + power_supply_changed(gbpsy->psy); +} +#endif + +static void check_changed(struct gb_power_supply *gbpsy, + struct gb_power_supply_prop *prop) +{ + const struct gb_power_supply_changes *psyc; + u32 val = prop->val; + u32 prev_val = prop->previous_val; + int i; + + for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) { + psyc = &psy_props_changes[i]; + if (prop->prop == psyc->prop) { + if (!psyc->tolerance_change) + gbpsy->changed = true; + else if (val < prev_val && + prev_val - val > psyc->tolerance_change) + gbpsy->changed = true; + else if (val > prev_val && + val - prev_val > psyc->tolerance_change) + gbpsy->changed = true; + break; + } } - return technology; } -static int get_status(struct gb_power_supply *gb) +static int total_props(struct gb_power_supply *gbpsy) { - struct gb_power_supply_status_response status_response; - u16 psy_status; - int retval; + /* this return the intval plus the strval properties */ + return (gbpsy->properties_count + gbpsy->properties_count_str); +} - retval = gb_operation_sync(gb->connection, GB_POWER_SUPPLY_TYPE_STATUS, - NULL, 0, - &status_response, sizeof(status_response)); - if (retval) - return retval; +static void prop_append(struct gb_power_supply *gbpsy, + enum power_supply_property prop) +{ + enum power_supply_property *new_props_raw; + + gbpsy->properties_count_str++; + new_props_raw = krealloc(gbpsy->props_raw, total_props(gbpsy) * + sizeof(enum power_supply_property), + GFP_KERNEL); + if (!new_props_raw) + return; + gbpsy->props_raw = new_props_raw; + gbpsy->props_raw[total_props(gbpsy) - 1] = prop; +} + +static int __gb_power_supply_set_name(char *init_name, char *name, size_t len) +{ + unsigned int i = 0; + int ret = 0; + struct power_supply *psy; + + if (!strlen(init_name)) + init_name = "gb_power_supply"; + strlcpy(name, init_name, len); + + while ((ret < len) && (psy = power_supply_get_by_name(name))) { +#ifdef PSY_HAVE_PUT + power_supply_put(psy); +#endif + ret = snprintf(name, len, "%s_%u", init_name, ++i); + } + if (ret >= len) + return -ENOMEM; + return i; +} + +static void _gb_power_supply_append_props(struct gb_power_supply *gbpsy) +{ + if (strlen(gbpsy->manufacturer)) + prop_append(gbpsy, POWER_SUPPLY_PROP_MANUFACTURER); + if (strlen(gbpsy->model_name)) + prop_append(gbpsy, POWER_SUPPLY_PROP_MODEL_NAME); + if (strlen(gbpsy->serial_number)) + prop_append(gbpsy, POWER_SUPPLY_PROP_SERIAL_NUMBER); +} + +static int gb_power_supply_description_get(struct gb_power_supply *gbpsy) +{ + struct gb_connection *connection = get_conn_from_psy(gbpsy); + struct gb_power_supply_get_description_request req; + struct gb_power_supply_get_description_response resp; + int ret; + + req.psy_id = gbpsy->id; + + ret = gb_operation_sync(connection, + GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret < 0) + return ret; + + gbpsy->manufacturer = kstrndup(resp.manufacturer, PROP_MAX, GFP_KERNEL); + if (!gbpsy->manufacturer) + return -ENOMEM; + gbpsy->model_name = kstrndup(resp.model, PROP_MAX, GFP_KERNEL); + if (!gbpsy->model_name) + return -ENOMEM; + gbpsy->serial_number = kstrndup(resp.serial_number, PROP_MAX, + GFP_KERNEL); + if (!gbpsy->serial_number) + return -ENOMEM; + + gbpsy->type = le16_to_cpu(resp.type); + gbpsy->properties_count = resp.properties_count; + + return 0; +} + +static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy) +{ + struct gb_connection *connection = get_conn_from_psy(gbpsy); + struct gb_power_supply_get_property_descriptors_request req; + struct gb_power_supply_get_property_descriptors_response resp; + int ret; + int i; + + if (gbpsy->properties_count == 0) + return 0; + + req.psy_id = gbpsy->id; + + ret = gb_operation_sync(connection, + GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS, + &req, sizeof(req), &resp, + sizeof(resp) + gbpsy->properties_count * + sizeof(struct gb_power_supply_props_desc)); + if (ret < 0) + return ret; + + gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props), + GFP_KERNEL); + if (!gbpsy->props) + return -ENOMEM; + + gbpsy->props_raw = kzalloc(gbpsy->properties_count * + sizeof(*gbpsy->props_raw), GFP_KERNEL); + if (!gbpsy->props_raw) + return -ENOMEM; + + /* Store available properties */ + for (i = 0; i < gbpsy->properties_count; i++) { + gbpsy->props[i].prop = resp.props[i].property; + gbpsy->props_raw[i] = resp.props[i].property; + if (resp.props[i].is_writeable) + gbpsy->props[i].is_writeable = true; + } /* - * Map greybus values to power_supply values. Hopefully these are - * "identical" which should allow gcc to optimize the code away to - * nothing. + * now append the properties that we already got information in the + * get_description operation. (char * ones) */ - psy_status = le16_to_cpu(status_response.psy_status); - switch (psy_status) { - case GB_POWER_SUPPLY_STATUS_CHARGING: - psy_status = POWER_SUPPLY_STATUS_CHARGING; - break; - case GB_POWER_SUPPLY_STATUS_DISCHARGING: - psy_status = POWER_SUPPLY_STATUS_DISCHARGING; + _gb_power_supply_append_props(gbpsy); + + return 0; +} + +static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy, + enum power_supply_property psp) +{ + struct gb_connection *connection = get_conn_from_psy(gbpsy); + struct gb_power_supply_prop *prop; + struct gb_power_supply_get_property_request req; + struct gb_power_supply_get_property_response resp; + u32 val; + int ret; + + prop = get_psy_prop(gbpsy, psp); + if (!prop) + return -EINVAL; + req.psy_id = gbpsy->id; + req.property = (u8)psp; + + ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret < 0) + return ret; + + val = le32_to_cpu(resp.prop_val); + if (val == prop->val) + return 0; + + prop->previous_val = prop->val; + prop->val = val; + + check_changed(gbpsy, prop); + + return 0; +} + +static int __gb_power_supply_property_get(struct gb_power_supply *gbpsy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct gb_power_supply_prop *prop; + + prop = get_psy_prop(gbpsy, psp); + if (!prop) + return -EINVAL; + + val->intval = prop->val; + return 0; +} + +static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = kstrndup(gbpsy->model_name, PROP_MAX, GFP_KERNEL); break; - case GB_POWER_SUPPLY_STATUS_NOT_CHARGING: - psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = kstrndup(gbpsy->manufacturer, PROP_MAX, + GFP_KERNEL); break; - case GB_POWER_SUPPLY_STATUS_FULL: - psy_status = POWER_SUPPLY_STATUS_FULL; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + val->strval = kstrndup(gbpsy->serial_number, PROP_MAX, + GFP_KERNEL); break; - case GB_POWER_SUPPLY_STATUS_UNKNOWN: default: - psy_status = POWER_SUPPLY_STATUS_UNKNOWN; break; } - return psy_status; + + return 0; } -static int get_max_voltage(struct gb_power_supply *gb) +static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy, + enum power_supply_property psp, + union power_supply_propval *val) { - struct gb_power_supply_max_voltage_response volt_response; - u32 max_voltage; - int retval; + struct gb_connection *connection = get_conn_from_psy(gbpsy); + int ret; + + /* + * Properties of type const char *, were already fetched on + * get_description operation and should be cached in gb + */ + if (is_prop_valint(psp)) + ret = __gb_power_supply_property_get(gbpsy, psp, val); + else + ret = __gb_power_supply_property_strval_get(gbpsy, psp, val); - retval = gb_operation_sync(gb->connection, - GB_POWER_SUPPLY_TYPE_MAX_VOLTAGE, - NULL, 0, - &volt_response, sizeof(volt_response)); - if (retval) - return retval; + if (ret < 0) + dev_err(&connection->bundle->dev, "get property %u\n", psp); - max_voltage = le32_to_cpu(volt_response.max_voltage); - return max_voltage; + return 0; } -static int get_percent_capacity(struct gb_power_supply *gb) +static int gb_power_supply_status_get(struct gb_power_supply *gbpsy) { - struct gb_power_supply_capacity_response capacity_response; - u32 capacity; - int retval; + int ret = 0; + int i; + + /* check if cache is good enough */ + if (gbpsy->last_update && + time_is_after_jiffies(gbpsy->last_update + + msecs_to_jiffies(cache_time))) + return 0; + + for (i = 0; i < gbpsy->properties_count; i++) { + ret = __gb_power_supply_property_update(gbpsy, + gbpsy->props[i].prop); + if (ret < 0) + break; + } - retval = gb_operation_sync(gb->connection, - GB_POWER_SUPPLY_TYPE_PERCENT_CAPACITY, - NULL, 0, &capacity_response, - sizeof(capacity_response)); - if (retval) - return retval; + if (ret == 0) + gbpsy->last_update = jiffies; - capacity = le32_to_cpu(capacity_response.capacity); - return capacity; + return ret; } -static int get_temp(struct gb_power_supply *gb) +static void gb_power_supply_status_update(struct gb_power_supply *gbpsy) { - struct gb_power_supply_temperature_response temp_response; - u32 temperature; - int retval; + /* check if there a change that need to be reported */ + gb_power_supply_status_get(gbpsy); - retval = gb_operation_sync(gb->connection, - GB_POWER_SUPPLY_TYPE_TEMPERATURE, - NULL, 0, - &temp_response, sizeof(temp_response)); - if (retval) - return retval; + if (!gbpsy->changed) + return; - temperature = le32_to_cpu(temp_response.temperature); - return temperature; + gbpsy->update_interval = update_interval_init; + __gb_power_supply_changed(gbpsy); + gbpsy->changed = false; } -static int get_voltage(struct gb_power_supply *gb) +static void gb_power_supply_work(struct work_struct *work) { - struct gb_power_supply_voltage_response voltage_response; - u32 voltage; - int retval; + struct gb_power_supply *gbpsy = container_of(work, + struct gb_power_supply, + work.work); - retval = gb_operation_sync(gb->connection, GB_POWER_SUPPLY_TYPE_VOLTAGE, - NULL, 0, - &voltage_response, sizeof(voltage_response)); - if (retval) - return retval; + /* + * if the poll interval is not set, disable polling, this is helpful + * specially at unregister time. + */ + if (!gbpsy->update_interval) + return; - voltage = le32_to_cpu(voltage_response.voltage); - return voltage; + gb_power_supply_status_update(gbpsy); + next_interval(gbpsy); + schedule_delayed_work(&gbpsy->work, gbpsy->update_interval); } static int get_property(struct power_supply *b, enum power_supply_property psp, union power_supply_propval *val) { - struct gb_power_supply *gb = to_gb_power_supply(b); + struct gb_power_supply *gbpsy = to_gb_power_supply(b); - switch (psp) { - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = get_tech(gb); - break; + gb_power_supply_status_get(gbpsy); - case POWER_SUPPLY_PROP_STATUS: - val->intval = get_status(gb); - break; + return _gb_power_supply_property_get(gbpsy, psp, val); +} - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = get_max_voltage(gb); - break; +static int gb_power_supply_property_set(struct gb_power_supply *gbpsy, + enum power_supply_property psp, + int val) +{ + struct gb_connection *connection = get_conn_from_psy(gbpsy); + struct gb_power_supply_prop *prop; + struct gb_power_supply_set_property_request req; + int ret; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = get_percent_capacity(gb); - break; + prop = get_psy_prop(gbpsy, psp); + if (!prop) + return -EINVAL; + req.psy_id = gbpsy->id; + req.property = (u8)psp; + req.prop_val = cpu_to_le32(val); - case POWER_SUPPLY_PROP_TEMP: - val->intval = get_temp(gb); - break; + ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY, + &req, sizeof(req), NULL, 0); + if (ret < 0) + goto out; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = get_voltage(gb); - break; + /* cache immediately the new value */ + prop->val = val; - default: - return -EINVAL; - } +out: + return ret; +} - return (val->intval < 0) ? val->intval : 0; +static int set_property(struct power_supply *b, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct gb_power_supply *gbpsy = to_gb_power_supply(b); + + return gb_power_supply_property_set(gbpsy, psp, val->intval); +} + +static int property_is_writeable(struct power_supply *b, + enum power_supply_property psp) +{ + struct gb_power_supply *gbpsy = to_gb_power_supply(b); + + return is_psy_prop_writeable(gbpsy, psp); } -// FIXME - verify this list, odds are some can be removed and others added. -static enum power_supply_property psy_props[] = { - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_VOLTAGE_NOW, -}; #ifdef DRIVER_OWNS_PSY_STRUCT -static int init_and_register(struct gb_connection *connection, - struct gb_battery *gb) +static int gb_power_supply_register(struct gb_power_supply *gbpsy) { - // FIXME - get a better (i.e. unique) name - // FIXME - anything else needs to be set? - gb->psy.name = "gb_battery"; - gb->psy.type = POWER_SUPPLY_TYPE_BATTERY; - gb->psy.properties = psy_props; - gb->psy.num_properties = ARRAY_SIZE(psy_props); - gb->psy.get_property = get_property; - - return power_supply_register(&connection->bundle->dev, &gb->psy); + struct gb_connection *connection = get_conn_from_psy(gbpsy); + + gbpsy->psy.name = gbpsy->name; + gbpsy->psy.type = gbpsy->type; + gbpsy->psy.properties = gbpsy->props_raw; + gbpsy->psy.num_properties = total_props(gbpsy); + gbpsy->psy.get_property = get_property; + gbpsy->psy.set_property = set_property; + gbpsy->psy.property_is_writeable = property_is_writeable; + + return power_supply_register(&connection->bundle->dev, + &gbpsy->psy); } #else -static int init_and_register(struct gb_connection *connection, - struct gb_power_supply *gb) +static int gb_power_supply_register(struct gb_power_supply *gbpsy) { + struct gb_connection *connection = get_conn_from_psy(gbpsy); struct power_supply_config cfg = {}; - cfg.drv_data = gb; + cfg.drv_data = gbpsy; - // FIXME - get a better (i.e. unique) name - // FIXME - anything else needs to be set? - gb->desc.name = "gb_battery"; - gb->desc.type = POWER_SUPPLY_TYPE_BATTERY; - gb->desc.properties = psy_props; - gb->desc.num_properties = ARRAY_SIZE(psy_props); - gb->desc.get_property = get_property; + gbpsy->desc.name = gbpsy->name; + gbpsy->desc.type = gbpsy->type; + gbpsy->desc.properties = gbpsy->props_raw; + gbpsy->desc.num_properties = total_props(gbpsy); + gbpsy->desc.get_property = get_property; + gbpsy->desc.set_property = set_property; + gbpsy->desc.property_is_writeable = property_is_writeable; - gb->psy = power_supply_register(&connection->bundle->dev, - &gb->desc, &cfg); - if (IS_ERR(gb->psy)) - return PTR_ERR(gb->psy); + gbpsy->psy = power_supply_register(&connection->bundle->dev, + &gbpsy->desc, &cfg); + if (IS_ERR(gbpsy->psy)) + return PTR_ERR(gbpsy->psy); return 0; } #endif +static void _gb_power_supply_free(struct gb_power_supply *gbpsy) +{ + kfree(gbpsy->serial_number); + kfree(gbpsy->model_name); + kfree(gbpsy->manufacturer); + kfree(gbpsy->props_raw); + kfree(gbpsy->props); + kfree(gbpsy); +} + +static void _gb_power_supply_release(struct gb_power_supply *gbpsy) +{ + if (!gbpsy) + return; + + gbpsy->update_interval = 0; + + cancel_delayed_work_sync(&gbpsy->work); +#ifdef DRIVER_OWNS_PSY_STRUCT + power_supply_unregister(&gbpsy->psy); +#else + power_supply_unregister(gbpsy->psy); +#endif + + _gb_power_supply_free(gbpsy); +} + +static void _gb_power_supplies_release(struct gb_power_supplies *supplies) +{ + int i; + + mutex_lock(&supplies->supplies_lock); + for (i = 0; i < supplies->supplies_count; i++) + _gb_power_supply_release(&supplies->supply[i]); + mutex_unlock(&supplies->supplies_lock); +} + +static int gb_power_supplies_get_count(struct gb_power_supplies *supplies) +{ + struct gb_power_supply_get_supplies_response resp; + int ret; + + ret = gb_operation_sync(supplies->connection, + GB_POWER_SUPPLY_TYPE_GET_SUPPLIES, + NULL, 0, &resp, sizeof(resp)); + if (ret < 0) + return ret; + + if (!resp.supplies_count) + return -EINVAL; + + supplies->supplies_count = resp.supplies_count; + + return ret; +} + +static int gb_power_supply_config(struct gb_power_supplies *supplies, int id) +{ + struct gb_power_supply *gbpsy = &supplies->supply[id]; + int ret; + + gbpsy->supplies = supplies; + gbpsy->id = id; + + ret = gb_power_supply_description_get(gbpsy); + if (ret < 0) + goto out; + + ret = gb_power_supply_prop_descriptors_get(gbpsy); + if (ret < 0) + goto out; + + /* guarantee that we have an unique name, before register */ + ret = __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name, + sizeof(gbpsy->name)); + if (ret < 0) + goto out; + + ret = gb_power_supply_register(gbpsy); + if (ret < 0) + goto out; + + gbpsy->update_interval = update_interval_init; + INIT_DELAYED_WORK(&gbpsy->work, gb_power_supply_work); + schedule_delayed_work(&gbpsy->work, 0); + +out: + return ret; +} + +static int gb_power_supplies_setup(struct gb_power_supplies *supplies) +{ + struct gb_connection *connection = supplies->connection; + int ret; + int i; + + mutex_lock(&supplies->supplies_lock); + + ret = gb_power_supplies_get_count(supplies); + if (ret < 0) + goto out; + + supplies->supply = kzalloc(supplies->supplies_count * + sizeof(struct gb_power_supply), + GFP_KERNEL); + + if (!supplies->supply) + return -ENOMEM; + + for (i = 0; i < supplies->supplies_count; i++) { + ret = gb_power_supply_config(supplies, i); + if (ret < 0) { + dev_err(&connection->bundle->dev, + "Fail to configure supplies devices\n"); + goto out; + } + } +out: + mutex_unlock(&supplies->supplies_lock); + return ret; +} + +static int gb_power_supply_event_recv(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_power_supplies *supplies = connection->private; + struct gb_power_supply *gbpsy; + struct gb_message *request; + struct gb_power_supply_event_request *payload; + u8 psy_id; + u8 event; + int ret = 0; + + if (type != GB_POWER_SUPPLY_TYPE_EVENT) { + dev_err(&connection->bundle->dev, + "Unsupported unsolicited event: %u\n", type); + return -EINVAL; + } + + request = op->request; + + if (request->payload_size < sizeof(*payload)) { + dev_err(&connection->bundle->dev, + "Wrong event size received (%zu < %zu)\n", + request->payload_size, sizeof(*payload)); + return -EINVAL; + } + + payload = request->payload; + psy_id = payload->psy_id; + mutex_lock(&supplies->supplies_lock); + if (psy_id >= supplies->supplies_count || !&supplies->supply[psy_id]) { + dev_err(&connection->bundle->dev, + "Event received for unconfigured power_supply id: %d\n", + psy_id); + ret = -EINVAL; + goto out_unlock; + } + + event = payload->event; + /* + * we will only handle events after setup is done and before release is + * running. For that just check update_interval. + */ + gbpsy = &supplies->supply[psy_id]; + if (gbpsy->update_interval) { + ret = -ESHUTDOWN; + goto out_unlock; + } + + if (event & GB_POWER_SUPPLY_UPDATE) + gb_power_supply_status_update(gbpsy); + +out_unlock: + mutex_unlock(&supplies->supplies_lock); + return ret; +} + static int gb_power_supply_connection_init(struct gb_connection *connection) { - struct gb_power_supply *gb; - int retval; + struct gb_power_supplies *supplies; - gb = kzalloc(sizeof(*gb), GFP_KERNEL); - if (!gb) + supplies = kzalloc(sizeof(*supplies), GFP_KERNEL); + if (!supplies) return -ENOMEM; - gb->connection = connection; - connection->private = gb; + supplies->connection = connection; + connection->private = supplies; - retval = init_and_register(connection, gb); - if (retval) - kfree(gb); + mutex_init(&supplies->supplies_lock); - return retval; + return gb_power_supplies_setup(supplies); } static void gb_power_supply_connection_exit(struct gb_connection *connection) { - struct gb_power_supply *gb = connection->private; + struct gb_power_supplies *supplies = connection->private; -#ifdef DRIVER_OWNS_PSY_STRUCT - power_supply_unregister(&gb->psy); -#else - power_supply_unregister(gb->psy); -#endif - kfree(gb); + _gb_power_supplies_release(supplies); } static struct gb_protocol power_supply_protocol = { @@ -312,7 +746,7 @@ static struct gb_protocol power_supply_protocol = { .minor = GB_POWER_SUPPLY_VERSION_MINOR, .connection_init = gb_power_supply_connection_init, .connection_exit = gb_power_supply_connection_exit, - .request_recv = NULL, /* no incoming requests */ + .request_recv = gb_power_supply_event_recv, }; gb_protocol_driver(&power_supply_protocol); -- cgit v1.2.3-59-g8ed1b From bf6a61bd2f36245c3d2d62b411cea48f10b7f4dd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 13 Nov 2015 08:59:49 +0530 Subject: greybus: es2: remove fake vendor/product ids These aren't used anymore, remove them. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index a0e20376bebf..0eb96459b68b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -22,7 +22,6 @@ #define ES2_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { - { USB_DEVICE(0xffff, 0x0002) }, /* Made up number, delete once firmware is fixed to use real number */ { USB_DEVICE(0x18d1, 0x1eaf) }, { }, }; -- cgit v1.2.3-59-g8ed1b From e3c25930d90216f7f6e536136a70daee7450604c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 13 Nov 2015 08:59:50 +0530 Subject: greybus: es1: Remove the (now) unused es1 driver This was used by gbsim earlier, but not anymore. Lets remove it. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 - drivers/staging/greybus/es1.c | 729 -------------------------------- drivers/staging/greybus/greybus_trace.h | 4 +- 3 files changed, 2 insertions(+), 733 deletions(-) delete mode 100644 drivers/staging/greybus/es1.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index c3572b35e992..2c1f9746bae1 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -29,7 +29,6 @@ gb-loopback-y := loopback.o gb-light-y := light.o gb-raw-y := raw.o gb-hid-y := hid.o -gb-es1-y := es1.o gb-es2-y := es2.o gb-db3-y := db3-platform.o @@ -41,7 +40,6 @@ obj-m += gb-loopback.o obj-m += gb-light.o obj-m += gb-hid.o obj-m += gb-raw.o -obj-m += gb-es1.o obj-m += gb-es2.o obj-m += gb-db3.o diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c deleted file mode 100644 index 75253d9a942b..000000000000 --- a/drivers/staging/greybus/es1.c +++ /dev/null @@ -1,729 +0,0 @@ -/* - * Greybus "AP" USB driver for "ES1" controller chips - * - * Copyright 2014-2015 Google Inc. - * Copyright 2014-2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ -#include -#include -#include -#include -#include -#include - -#include "greybus.h" -#include "kernel_ver.h" -#include "connection.h" -#include "greybus_trace.h" - -/* Memory sizes for the buffers sent to/from the ES1 controller */ -#define ES1_GBUF_MSG_SIZE_MAX 2048 - -static const struct usb_device_id id_table[] = { - /* Made up numbers for the SVC USB Bridge in ES1 */ - { USB_DEVICE(0xffff, 0x0001) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -#define APB1_LOG_SIZE SZ_16K -static struct dentry *apb1_log_dentry; -static struct dentry *apb1_log_enable_dentry; -static struct task_struct *apb1_log_task; -static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); - -/* Number of CPorts supported by es1 */ -#define CPORT_COUNT 256 - -/* - * Number of CPort IN urbs in flight at any point in time. - * Adjust if we are having stalls in the USB buffer due to not enough urbs in - * flight. - */ -#define NUM_CPORT_IN_URB 4 - -/* Number of CPort OUT urbs in flight at any point in time. - * Adjust if we get messages saying we are out of urbs in the system log. - */ -#define NUM_CPORT_OUT_URB 8 - -/* vendor request APB1 log */ -#define REQUEST_LOG 0x02 - -/* vendor request to time the latency of messages on a given cport */ -#define REQUEST_LATENCY_TAG_EN 0x06 -#define REQUEST_LATENCY_TAG_DIS 0x07 - -/** - * es1_ap_dev - ES1 USB Bridge to AP structure - * @usb_dev: pointer to the USB device we are. - * @usb_intf: pointer to the USB interface we are bound to. - * @hd: pointer to our gb_host_device structure - * @cport_in_endpoint: bulk in endpoint for CPort data - * @cport-out_endpoint: bulk out endpoint for CPort data - * @cport_in_urb: array of urbs for the CPort in messages - * @cport_in_buffer: array of buffers for the @cport_in_urb urbs - * @cport_out_urb: array of urbs for the CPort out messages - * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or - * not. - * @cport_out_urb_cancelled: array of flags indicating whether the - * corresponding @cport_out_urb is being cancelled - * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" - */ -struct es1_ap_dev { - struct usb_device *usb_dev; - struct usb_interface *usb_intf; - struct gb_host_device *hd; - - __u8 cport_in_endpoint; - __u8 cport_out_endpoint; - - struct urb *cport_in_urb[NUM_CPORT_IN_URB]; - u8 *cport_in_buffer[NUM_CPORT_IN_URB]; - struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; - bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; - bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; - spinlock_t cport_out_urb_lock; -}; - -static inline struct es1_ap_dev *hd_to_es1(struct gb_host_device *hd) -{ - return (struct es1_ap_dev *)&hd->hd_priv; -} - -static void cport_out_callback(struct urb *urb); -static void usb_log_enable(struct es1_ap_dev *es1); -static void usb_log_disable(struct es1_ap_dev *es1); - -#define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ - -static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) -{ - struct urb *urb = NULL; - unsigned long flags; - int i; - - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - - /* Look in our pool of allocated urbs first, as that's the "fastest" */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (es1->cport_out_urb_busy[i] == false && - es1->cport_out_urb_cancelled[i] == false) { - es1->cport_out_urb_busy[i] = true; - urb = es1->cport_out_urb[i]; - break; - } - } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - if (urb) - return urb; - - /* - * Crap, pool is empty, complain to the syslog and go allocate one - * dynamically as we have to succeed. - */ - dev_err(&es1->usb_dev->dev, - "No free CPort OUT urbs, having to dynamically allocate one!\n"); - return usb_alloc_urb(0, gfp_mask); -} - -static void free_urb(struct es1_ap_dev *es1, struct urb *urb) -{ - unsigned long flags; - int i; - /* - * See if this was an urb in our pool, if so mark it "free", otherwise - * we need to free it ourselves. - */ - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (urb == es1->cport_out_urb[i]) { - es1->cport_out_urb_busy[i] = false; - urb = NULL; - break; - } - } - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - - /* If urb is not NULL, then we need to free this urb */ - usb_free_urb(urb); -} - -/* - * We (ab)use the operation-message header pad bytes to transfer the - * cport id in order to minimise overhead. - */ -static void -gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) -{ - header->pad[0] = cport_id; -} - -/* Clear the pad bytes used for the CPort id */ -static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) -{ - header->pad[0] = 0; -} - -/* Extract the CPort id packed into the header, and clear it */ -static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) -{ - u16 cport_id = header->pad[0]; - - gb_message_cport_clear(header); - - return cport_id; -} - -/* - * Returns zero if the message was successfully queued, or a negative errno - * otherwise. - */ -static int message_send(struct gb_host_device *hd, u16 cport_id, - struct gb_message *message, gfp_t gfp_mask) -{ - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; - size_t buffer_size; - int retval; - struct urb *urb; - unsigned long flags; - - /* - * The data actually transferred will include an indication - * of where the data should be sent. Do one last check of - * the target CPort id before filling it in. - */ - if (!cport_id_valid(hd, cport_id)) { - dev_err(&udev->dev, "invalid destination cport 0x%02x\n", - cport_id); - return -EINVAL; - } - - /* Find a free urb */ - urb = next_free_urb(es1, gfp_mask); - if (!urb) - return -ENOMEM; - - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - message->hcpriv = urb; - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - - /* Pack the cport id into the message header */ - gb_message_cport_pack(message->header, cport_id); - - buffer_size = sizeof(*message->header) + message->payload_size; - - usb_fill_bulk_urb(urb, udev, - usb_sndbulkpipe(udev, es1->cport_out_endpoint), - message->buffer, buffer_size, - cport_out_callback, message); - urb->transfer_flags |= URB_ZERO_PACKET; - trace_gb_host_device_send(hd, cport_id, buffer_size); - retval = usb_submit_urb(urb, gfp_mask); - if (retval) { - dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); - - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - message->hcpriv = NULL; - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - - free_urb(es1, urb); - gb_message_cport_clear(message->header); - - return retval; - } - - return 0; -} - -/* - * Can not be called in atomic context. - */ -static void message_cancel(struct gb_message *message) -{ - struct gb_host_device *hd = message->operation->connection->hd; - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct urb *urb; - int i; - - might_sleep(); - - spin_lock_irq(&es1->cport_out_urb_lock); - urb = message->hcpriv; - - /* Prevent dynamically allocated urb from being deallocated. */ - usb_get_urb(urb); - - /* Prevent pre-allocated urb from being reused. */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - if (urb == es1->cport_out_urb[i]) { - es1->cport_out_urb_cancelled[i] = true; - break; - } - } - spin_unlock_irq(&es1->cport_out_urb_lock); - - usb_kill_urb(urb); - - if (i < NUM_CPORT_OUT_URB) { - spin_lock_irq(&es1->cport_out_urb_lock); - es1->cport_out_urb_cancelled[i] = false; - spin_unlock_irq(&es1->cport_out_urb_lock); - } - - usb_free_urb(urb); -} - -static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) -{ - int retval; - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; - - if (!cport_id_valid(hd, cport_id)) { - dev_err(&udev->dev, "invalid destination cport 0x%02x\n", - cport_id); - return -EINVAL; - } - - retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - REQUEST_LATENCY_TAG_EN, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES1_TIMEOUT); - - if (retval < 0) - dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n", - cport_id); - return retval; -} - -static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) -{ - int retval; - struct es1_ap_dev *es1 = hd_to_es1(hd); - struct usb_device *udev = es1->usb_dev; - - if (!cport_id_valid(hd, cport_id)) { - dev_err(&udev->dev, "invalid destination cport 0x%02x\n", - cport_id); - return -EINVAL; - } - - retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - REQUEST_LATENCY_TAG_DIS, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES1_TIMEOUT); - - if (retval < 0) - dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n", - cport_id); - return retval; -} - -static struct gb_hd_driver es1_driver = { - .hd_priv_size = sizeof(struct es1_ap_dev), - .message_send = message_send, - .message_cancel = message_cancel, - .latency_tag_enable = latency_tag_enable, - .latency_tag_disable = latency_tag_disable, -}; - -/* Common function to report consistent warnings based on URB status */ -static int check_urb_status(struct urb *urb) -{ - struct device *dev = &urb->dev->dev; - int status = urb->status; - - switch (status) { - case 0: - return 0; - - case -EOVERFLOW: - dev_err(dev, "%s: overflow actual length is %d\n", - __func__, urb->actual_length); - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - case -EILSEQ: - case -EPROTO: - /* device is gone, stop sending */ - return status; - } - dev_err(dev, "%s: unknown status %d\n", __func__, status); - - return -EAGAIN; -} - -static void es1_destroy(struct es1_ap_dev *es1) -{ - struct usb_device *udev; - int i; - - debugfs_remove(apb1_log_enable_dentry); - usb_log_disable(es1); - - /* Tear down everything! */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb = es1->cport_out_urb[i]; - - if (!urb) - break; - usb_free_urb(urb); - es1->cport_out_urb[i] = NULL; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ - } - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb = es1->cport_in_urb[i]; - - if (!urb) - break; - usb_kill_urb(urb); - usb_free_urb(urb); - kfree(es1->cport_in_buffer[i]); - es1->cport_in_buffer[i] = NULL; - } - - udev = es1->usb_dev; - gb_hd_put(es1->hd); - - usb_put_dev(udev); -} - -static void ap_disconnect(struct usb_interface *interface) -{ - struct es1_ap_dev *es1 = usb_get_intfdata(interface); - int i; - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) - usb_kill_urb(es1->cport_in_urb[i]); - - gb_hd_del(es1->hd); - - es1_destroy(es1); -} - -static void cport_in_callback(struct urb *urb) -{ - struct gb_host_device *hd = urb->context; - struct device *dev = &urb->dev->dev; - struct gb_operation_msg_hdr *header; - int status = check_urb_status(urb); - int retval; - u16 cport_id; - - if (status) { - if ((status == -EAGAIN) || (status == -EPROTO)) - goto exit; - dev_err(dev, "urb cport in error %d (dropped)\n", status); - return; - } - - if (urb->actual_length < sizeof(*header)) { - dev_err(dev, "short message received\n"); - goto exit; - } - - /* Extract the CPort id, which is packed in the message header */ - header = urb->transfer_buffer; - cport_id = gb_message_cport_unpack(header); - - if (cport_id_valid(hd, cport_id)) { - trace_gb_host_device_recv(hd, cport_id, urb->actual_length); - greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, - urb->actual_length); - } else { - dev_err(dev, "invalid cport id 0x%02x received\n", cport_id); - } -exit: - /* put our urb back in the request pool */ - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "failed to resubmit in-urb: %d\n", retval); -} - -static void cport_out_callback(struct urb *urb) -{ - struct gb_message *message = urb->context; - struct gb_host_device *hd = message->operation->connection->hd; - struct es1_ap_dev *es1 = hd_to_es1(hd); - int status = check_urb_status(urb); - unsigned long flags; - - gb_message_cport_clear(message->header); - - spin_lock_irqsave(&es1->cport_out_urb_lock, flags); - message->hcpriv = NULL; - spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags); - - /* - * Tell the submitter that the message send (attempt) is - * complete, and report the status. - */ - greybus_message_sent(hd, message, status); - - free_urb(es1, urb); -} - -#define APB1_LOG_MSG_SIZE 64 -static void apb1_log_get(struct es1_ap_dev *es1, char *buf) -{ - int retval; - - /* SVC messages go down our control pipe */ - do { - retval = usb_control_msg(es1->usb_dev, - usb_rcvctrlpipe(es1->usb_dev, 0), - REQUEST_LOG, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, 0x00, - buf, - APB1_LOG_MSG_SIZE, - ES1_TIMEOUT); - if (retval > 0) - kfifo_in(&apb1_log_fifo, buf, retval); - } while (retval > 0); -} - -static int apb1_log_poll(void *data) -{ - struct es1_ap_dev *es1 = data; - char *buf; - - buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - while (!kthread_should_stop()) { - msleep(1000); - apb1_log_get(es1, buf); - } - - kfree(buf); - - return 0; -} - -static ssize_t apb1_log_read(struct file *f, char __user *buf, - size_t count, loff_t *ppos) -{ - ssize_t ret; - size_t copied; - char *tmp_buf; - - if (count > APB1_LOG_SIZE) - count = APB1_LOG_SIZE; - - tmp_buf = kmalloc(count, GFP_KERNEL); - if (!tmp_buf) - return -ENOMEM; - - copied = kfifo_out(&apb1_log_fifo, tmp_buf, count); - ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied); - - kfree(tmp_buf); - - return ret; -} - -static const struct file_operations apb1_log_fops = { - .read = apb1_log_read, -}; - -static void usb_log_enable(struct es1_ap_dev *es1) -{ - if (!IS_ERR_OR_NULL(apb1_log_task)) - return; - - /* get log from APB1 */ - apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); - if (IS_ERR(apb1_log_task)) - return; - apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, - gb_debugfs_get(), NULL, - &apb1_log_fops); -} - -static void usb_log_disable(struct es1_ap_dev *es1) -{ - if (IS_ERR_OR_NULL(apb1_log_task)) - return; - - debugfs_remove(apb1_log_dentry); - apb1_log_dentry = NULL; - - kthread_stop(apb1_log_task); - apb1_log_task = NULL; -} - -static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, - size_t count, loff_t *ppos) -{ - char tmp_buf[3]; - int enable = !IS_ERR_OR_NULL(apb1_log_task); - - sprintf(tmp_buf, "%d\n", enable); - return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); -} - -static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - int enable; - ssize_t retval; - struct es1_ap_dev *es1 = (struct es1_ap_dev *)f->f_inode->i_private; - - retval = kstrtoint_from_user(buf, count, 10, &enable); - if (retval) - return retval; - - if (enable) - usb_log_enable(es1); - else - usb_log_disable(es1); - - return count; -} - -static const struct file_operations apb1_log_enable_fops = { - .read = apb1_log_enable_read, - .write = apb1_log_enable_write, -}; - -/* - * The ES1 USB Bridge device contains 4 endpoints - * 1 Control - usual USB stuff + AP -> SVC messages - * 1 Interrupt IN - SVC -> AP messages - * 1 Bulk IN - CPort data in - * 1 Bulk OUT - CPort data out - */ -static int ap_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct es1_ap_dev *es1; - struct gb_host_device *hd; - struct usb_device *udev; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - bool bulk_in_found = false; - bool bulk_out_found = false; - int retval = -ENOMEM; - int i; - - /* We need to fit a CPort ID in one byte of a message header */ - BUILD_BUG_ON(CPORT_COUNT > U8_MAX + 1); - - udev = usb_get_dev(interface_to_usbdev(interface)); - - hd = gb_hd_create(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX, - CPORT_COUNT); - if (IS_ERR(hd)) { - usb_put_dev(udev); - return PTR_ERR(hd); - } - - es1 = hd_to_es1(hd); - es1->hd = hd; - es1->usb_intf = interface; - es1->usb_dev = udev; - spin_lock_init(&es1->cport_out_urb_lock); - usb_set_intfdata(interface, es1); - - /* find all 3 of our endpoints */ - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(endpoint)) { - es1->cport_in_endpoint = endpoint->bEndpointAddress; - bulk_in_found = true; - } else if (usb_endpoint_is_bulk_out(endpoint)) { - es1->cport_out_endpoint = endpoint->bEndpointAddress; - bulk_out_found = true; - } else { - dev_err(&udev->dev, - "Unknown endpoint type found, address %x\n", - endpoint->bEndpointAddress); - } - } - if ((bulk_in_found == false) || - (bulk_out_found == false)) { - dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); - goto error; - } - - /* Allocate buffers for our cport in messages */ - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb; - u8 *buffer; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - goto error; - buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL); - if (!buffer) - goto error; - - usb_fill_bulk_urb(urb, udev, - usb_rcvbulkpipe(udev, es1->cport_in_endpoint), - buffer, ES1_GBUF_MSG_SIZE_MAX, - cport_in_callback, hd); - es1->cport_in_urb[i] = urb; - es1->cport_in_buffer[i] = buffer; - } - - /* Allocate urbs for our CPort OUT messages */ - for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - goto error; - - es1->cport_out_urb[i] = urb; - es1->cport_out_urb_busy[i] = false; /* just to be anal */ - } - - apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", - (S_IWUSR | S_IRUGO), - gb_debugfs_get(), es1, - &apb1_log_enable_fops); - - retval = gb_hd_add(hd); - if (retval) - goto error; - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - retval = usb_submit_urb(es1->cport_in_urb[i], GFP_KERNEL); - if (retval) - goto err_kill_in_urbs; - } - - return 0; - -err_kill_in_urbs: - for (--i; i >= 0; --i) - usb_kill_urb(es1->cport_in_urb[i]); - gb_hd_del(hd); -error: - es1_destroy(es1); - - return retval; -} - -static struct usb_driver es1_ap_driver = { - .name = "es1_ap_driver", - .probe = ap_probe, - .disconnect = ap_disconnect, - .id_table = id_table, -}; - -module_usb_driver(es1_ap_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Greg Kroah-Hartman "); diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index d38f9e0810a7..a39fa396d949 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -132,7 +132,7 @@ DECLARE_EVENT_CLASS(gb_host_device, /* * tracepoint name greybus:gb_host_device_send * description tracepoint representing the point data are transmitted - * location es1.c/es2.c:message_send + * location es2.c:message_send */ DEFINE_EVENT(gb_host_device, gb_host_device_send, @@ -145,7 +145,7 @@ DEFINE_EVENT(gb_host_device, gb_host_device_send, /* * tracepoint name greybus:gb_host_device_recv * description tracepoint representing the point data are received - * location es1.c/es2.c:cport_in_callback + * location es2.c:cport_in_callback */ DEFINE_EVENT(gb_host_device, gb_host_device_recv, -- cgit v1.2.3-59-g8ed1b From 80ee842825d5f40a21b4257dd98b74f2c92e5f02 Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Fri, 13 Nov 2015 14:44:13 -0700 Subject: greybus: audio: Allocate protocol and class values Allocate protocol and class values for the Audio Device Class Protocol. Two values of each type are allocated: one for Audio Management Connections and one for Audio Data Connections. Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index c36c4d1d30f9..947b71c88134 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -40,8 +40,8 @@ enum greybus_protocol { GREYBUS_PROTOCOL_LIGHTS = 0x0f, GREYBUS_PROTOCOL_VIBRATOR = 0x10, GREYBUS_PROTOCOL_LOOPBACK = 0x11, - /* 0x12 is unused */ - /* 0x13 is unused */ + GREYBUS_PROTOCOL_AUDIO_MGMT = 0x12, + GREYBUS_PROTOCOL_AUDIO_DATA = 0x13, GREYBUS_PROTOCOL_SVC = 0x14, GREYBUS_PROTOCOL_FIRMWARE = 0x15, /* ... */ @@ -68,8 +68,8 @@ enum greybus_class_type { GREYBUS_CLASS_LIGHTS = 0x0f, GREYBUS_CLASS_VIBRATOR = 0x10, GREYBUS_CLASS_LOOPBACK = 0x11, - /* 0x12 is unused */ - /* 0x13 is unused */ + GREYBUS_CLASS_AUDIO_MGMT = 0x12, + GREYBUS_CLASS_AUDIO_DATA = 0x13, GREYBUS_CLASS_SVC = 0x14, GREYBUS_CLASS_FIRMWARE = 0x15, /* ... */ -- cgit v1.2.3-59-g8ed1b From 5c864e775dfdecda1ea340af2d3b1c94b4dea63b Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Mon, 16 Nov 2015 19:23:25 +0000 Subject: greybus: manifest: fix bundle descriptor parse The descriptor list is walked in two points, in the bundle parsing and cport parsing, this can make the next descriptor pointer in bundle to be already removed by the cport remove descriptor and become invalid. So, just get the next bundle until there no more left. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 3e61b6655a5f..084e07e195c0 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -60,6 +60,18 @@ static void release_manifest_descriptors(struct gb_interface *intf) release_manifest_descriptor(descriptor); } +static struct manifest_desc *get_next_bundle_desc(struct gb_interface *intf) +{ + struct manifest_desc *descriptor; + struct manifest_desc *next; + + list_for_each_entry_safe(descriptor, next, &intf->manifest_descs, links) + if (descriptor->type == GREYBUS_TYPE_BUNDLE) + return descriptor; + + return NULL; +} + /* * Validate the given descriptor. Its reported size must fit within * the number of bytes remaining, and it must have a recognized @@ -282,18 +294,14 @@ exit: static u32 gb_manifest_parse_bundles(struct gb_interface *intf) { struct manifest_desc *desc; - struct manifest_desc *next; struct gb_bundle *bundle; struct gb_bundle *bundle_next; u32 count = 0; u8 bundle_id; - list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { + while ((desc = get_next_bundle_desc(intf))) { struct greybus_descriptor_bundle *desc_bundle; - if (desc->type != GREYBUS_TYPE_BUNDLE) - continue; - /* Found one. Set up its bundle structure*/ desc_bundle = desc->data; bundle_id = desc_bundle->id; -- cgit v1.2.3-59-g8ed1b From 9864756be7542d3fe4e18297e0a45b5e2dec2f62 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Thu, 19 Nov 2015 12:44:46 +0100 Subject: greybus: loopback: fix invalid response size The size of timestamps is not taken into account, which makes the loopback driver in the firmware drop invalid packages. Signed-off-by: Alexandre Bailon Signed-off-by: Bartosz Golaszewski Reviewed-by: Johan Hovold Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 9a44dcc7d7d7..ec68247f5bdb 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -534,8 +534,8 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) } if (len) { - if (!gb_operation_response_alloc(operation, len, - GFP_KERNEL)) { + if (!gb_operation_response_alloc(operation, + len + sizeof(*response), GFP_KERNEL)) { dev_err(dev, "error allocating response\n"); return -ENOMEM; } -- cgit v1.2.3-59-g8ed1b From 81ad6994611706270bc68a8dd951d7c69e2ed175 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Nov 2015 13:46:43 +0100 Subject: greybus: loopback: allocate a response even for a 0-byte request If payload length of a transfer packet is 0, no response is allocated. Send a well-formed response even in that case. Signed-off-by: Bartosz Golaszewski Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ec68247f5bdb..0c38414a3e06 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -533,16 +533,16 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) return -EINVAL; } - if (len) { - if (!gb_operation_response_alloc(operation, - len + sizeof(*response), GFP_KERNEL)) { - dev_err(dev, "error allocating response\n"); - return -ENOMEM; - } - response = operation->response->payload; - response->len = cpu_to_le32(len); - memcpy(response->data, request->data, len); + if (!gb_operation_response_alloc(operation, + len + sizeof(*response), GFP_KERNEL)) { + dev_err(dev, "error allocating response\n"); + return -ENOMEM; } + response = operation->response->payload; + response->len = cpu_to_le32(len); + if (len) + memcpy(response->data, request->data, len); + return 0; default: dev_err(dev, "unsupported request: %hhu\n", type); -- cgit v1.2.3-59-g8ed1b From 98645a9c69f40e46cdbf8c0c1862b4c2ed470319 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Nov 2015 18:27:59 +0100 Subject: greybus: firmware: fix information leak Add missing sanity checks on get_firmware-request offset and size parameters to fix potential information leaks. This prevents remotely controlled information leaks as the requestor currently controls both the 32-bit firmware-image offset and the amount of data that is returned (up to host-device MTU). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index b16f13318be4..4e1530fe9ae5 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -87,6 +87,7 @@ static int gb_firmware_get_firmware(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_firmware *firmware = connection->private; + const struct firmware *fw = firmware->fw; struct gb_firmware_get_firmware_request *firmware_request = op->request->payload; struct gb_firmware_get_firmware_response *firmware_response; struct device *dev = &connection->bundle->dev; @@ -99,7 +100,7 @@ static int gb_firmware_get_firmware(struct gb_operation *op) return -EINVAL; } - if (!firmware->fw) { + if (!fw) { dev_err(dev, "%s: firmware not available\n", __func__); return -EINVAL; } @@ -107,6 +108,12 @@ static int gb_firmware_get_firmware(struct gb_operation *op) offset = le32_to_cpu(firmware_request->offset); size = le32_to_cpu(firmware_request->size); + if (offset >= fw->size || size > fw->size - offset) { + dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", + offset, size); + return -EINVAL; + } + if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); @@ -114,7 +121,7 @@ static int gb_firmware_get_firmware(struct gb_operation *op) } firmware_response = op->response->payload; - memcpy(firmware_response->data, firmware->fw->data + offset, size); + memcpy(firmware_response->data, fw->data + offset, size); return 0; } -- cgit v1.2.3-59-g8ed1b From 87f6c976f4104ae5a439ff2d78ea3fe7ce0ac280 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Nov 2015 18:28:00 +0100 Subject: greybus: firmware: break long lines Break lines longer than 80 cols, and clean up an error message while at it. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 4e1530fe9ae5..a95be08c26d9 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -88,7 +88,7 @@ static int gb_firmware_get_firmware(struct gb_operation *op) struct gb_connection *connection = op->connection; struct gb_firmware *firmware = connection->private; const struct firmware *fw = firmware->fw; - struct gb_firmware_get_firmware_request *firmware_request = op->request->payload; + struct gb_firmware_get_firmware_request *firmware_request; struct gb_firmware_get_firmware_response *firmware_response; struct device *dev = &connection->bundle->dev; unsigned int offset, size; @@ -105,6 +105,7 @@ static int gb_firmware_get_firmware(struct gb_operation *op) return -EINVAL; } + firmware_request = op->request->payload; offset = le32_to_cpu(firmware_request->offset); size = le32_to_cpu(firmware_request->size); @@ -129,7 +130,7 @@ static int gb_firmware_get_firmware(struct gb_operation *op) static int gb_firmware_ready_to_boot(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_firmware_ready_to_boot_request *rtb_request = op->request->payload; + struct gb_firmware_ready_to_boot_request *rtb_request; struct device *dev = &connection->bundle->dev; u8 status; @@ -140,6 +141,7 @@ static int gb_firmware_ready_to_boot(struct gb_operation *op) return -EINVAL; } + rtb_request = op->request->payload; status = rtb_request->status; /* Return error if the blob was invalid */ @@ -192,8 +194,10 @@ static int gb_firmware_connection_init(struct gb_connection *connection) */ ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_AP_READY, NULL, 0, NULL, 0); - if (ret) - dev_err(&connection->bundle->dev, "Failed to send AP READY (%d)\n", ret); + if (ret) { + dev_err(&connection->bundle->dev, + "failed to send AP READY: %d\n", ret); + } return 0; } -- cgit v1.2.3-59-g8ed1b From 55510843c1630f6067aae4c45ff2929a51f56b4d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 19 Nov 2015 18:28:01 +0100 Subject: greybus: svc: fix missing version-request sanity checks Add missing sanity checks on version-request payload size. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 6c1a157c10c5..bd045678493f 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -310,6 +310,14 @@ static int gb_svc_version_request(struct gb_operation *op) struct gb_protocol_version_request *request; struct gb_protocol_version_response *response; + if (op->request->payload_size < sizeof(*request)) { + pr_err("%d: short version request (%zu < %zu)\n", + connection->intf_cport_id, + op->request->payload_size, + sizeof(*request)); + return -EINVAL; + } + request = op->request->payload; if (request->major > GB_SVC_VERSION_MAJOR) { -- cgit v1.2.3-59-g8ed1b From 358e9400f41a74467380cef38a6797c1747f7b1a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 21 Nov 2015 10:51:59 +0100 Subject: greybus: fix bundle-id match macros The matching flags were renamed over a year ago but the so far unused id-macros were never updated. Also rename the GREYBUS_ID_MATCH_DEVICE mask to use the common GREYBUS_ID_MATCH-prefix. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 1b6f56f9e506..4a6b23528490 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -42,16 +42,16 @@ #define GREYBUS_VERSION_MAJOR 0x00 #define GREYBUS_VERSION_MINOR 0x01 -#define GREYBUS_DEVICE_ID_MATCH_DEVICE \ - (GREYBUS_DEVICE_ID_MATCH_VENDOR | GREYBUS_DEVICE_ID_MATCH_PRODUCT) +#define GREYBUS_ID_MATCH_DEVICE \ + (GREYBUS_ID_MATCH_VENDOR | GREYBUS_ID_MATCH_PRODUCT) #define GREYBUS_DEVICE(v, p) \ - .match_flags = GREYBUS_DEVICE_ID_MATCH_DEVICE, \ + .match_flags = GREYBUS_ID_MATCH_DEVICE, \ .vendor = (v), \ .product = (p), #define GREYBUS_DEVICE_SERIAL(s) \ - .match_flags = GREYBUS_DEVICE_ID_MATCH_SERIAL, \ + .match_flags = GREYBUS_ID_MATCH_SERIAL, \ .serial_number = (s), /* Maximum number of CPorts */ -- cgit v1.2.3-59-g8ed1b From 9784a591f00134c02d152d6d07c539a136db7c12 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 21 Nov 2015 10:52:00 +0100 Subject: greybus: remove unique-id matching Remove unique-id matching as it does not make much sense to have a driver match a specific device serial number. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 8 ++------ drivers/staging/greybus/greybus.h | 4 ---- drivers/staging/greybus/greybus_id.h | 2 -- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 01745f40f6e6..fbc043998e08 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -79,10 +79,6 @@ static int gb_bundle_match_one_id(struct gb_bundle *bundle, (id->product != bundle->intf->product)) return 0; - if ((id->match_flags & GREYBUS_ID_MATCH_SERIAL) && - (id->unique_id != bundle->intf->unique_id)) - return 0; - if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) && (id->class != bundle->class)) return 0; @@ -97,8 +93,8 @@ gb_bundle_match_id(struct gb_bundle *bundle, if (id == NULL) return NULL; - for (; id->vendor || id->product || id->unique_id || id->class || - id->driver_info; id++) { + for (; id->vendor || id->product || id->class || id->driver_info; + id++) { if (gb_bundle_match_one_id(bundle, id)) return id; } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4a6b23528490..4f8aa318d2ab 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -50,10 +50,6 @@ .vendor = (v), \ .product = (p), -#define GREYBUS_DEVICE_SERIAL(s) \ - .match_flags = GREYBUS_ID_MATCH_SERIAL, \ - .serial_number = (s), - /* Maximum number of CPorts */ #define CPORT_ID_MAX 4095 /* UniPro max id is 4095 */ #define CPORT_ID_BAD U16_MAX diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index 8e76d4218c14..68d2e959a410 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -14,7 +14,6 @@ struct greybus_bundle_id { __u16 vendor; __u16 product; __u8 class; - __u64 unique_id; kernel_ulong_t driver_info __aligned(sizeof(kernel_ulong_t)); }; @@ -22,7 +21,6 @@ struct greybus_bundle_id { /* Used to match the greybus_bundle_id */ #define GREYBUS_ID_MATCH_VENDOR BIT(0) #define GREYBUS_ID_MATCH_PRODUCT BIT(1) -#define GREYBUS_ID_MATCH_SERIAL BIT(2) #define GREYBUS_ID_MATCH_CLASS BIT(3) #endif /* __LINUX_GREYBUS_ID_H */ -- cgit v1.2.3-59-g8ed1b From 8034bd561dac0c6616097394ba9b5eaee3c4fc9c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 21 Nov 2015 10:52:01 +0100 Subject: greybus: interface: remove unique id Remove the unimplemented interface unique-id. There will eventually be an interface-serial-number attribute provided, but let's not export it or commit to a name for this attribute until we need it. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 7 ------- drivers/staging/greybus/interface.c | 2 -- drivers/staging/greybus/interface.h | 1 - drivers/staging/greybus/manifest.c | 1 - 4 files changed, 11 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 22a0c7f5a9b3..8363f2307049 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -107,13 +107,6 @@ Contact: Greg Kroah-Hartman Description: Product ID string of a Greybus interface block. -What: /sys/bus/greybus/device/endoE:M:I/unique_id -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - Unique ID of a Greybus interface block. - What: /sys/bus/greybus/device/endoE:M:I/vendor Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 4c96adc56139..b57cc09d0f44 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -23,7 +23,6 @@ static DEVICE_ATTR_RO(field) gb_interface_attr(device_id, d); gb_interface_attr(vendor, x); gb_interface_attr(product, x); -gb_interface_attr(unique_id, llX); gb_interface_attr(vendor_string, s); gb_interface_attr(product_string, s); @@ -31,7 +30,6 @@ static struct attribute *interface_attrs[] = { &dev_attr_device_id.attr, &dev_attr_vendor.attr, &dev_attr_product.attr, - &dev_attr_unique_id.attr, &dev_attr_vendor_string.attr, &dev_attr_product_string.attr, NULL, diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 248c9918a9cc..f0ef4e9a6206 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -26,7 +26,6 @@ struct gb_interface { u16 product; char *vendor_string; char *product_string; - u64 unique_id; /* Information taken from the hotplug event */ u32 unipro_mfg_id; diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 084e07e195c0..4b4dd9973c17 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -396,7 +396,6 @@ static bool gb_manifest_parse_interface(struct gb_interface *intf, // Vendor, Product and Unique id must come via control protocol intf->vendor = 0xffff; intf->product = 0x0001; - intf->unique_id = 0; /* Release the interface descriptor, now that we're done with it */ release_manifest_descriptor(interface_desc); -- cgit v1.2.3-59-g8ed1b From f65fa47f28f303c84b701dadda6d4f48fb541691 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 21 Nov 2015 10:52:02 +0100 Subject: greybus: fix vendor and product matching Fix vendor and product matching by matching on the 32-bit Ara vendor and product ids. Remove the "fake" 16-bit vendor and product ids and export the Ara ids using the "vendor" and "product" interface attributes instead. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 2 +- drivers/staging/greybus/greybus_id.h | 4 ++-- drivers/staging/greybus/interface.h | 6 ++---- drivers/staging/greybus/manifest.c | 5 ----- drivers/staging/greybus/svc.c | 4 ++-- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index a95be08c26d9..e99d8d679686 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -41,7 +41,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) snprintf(firmware_name, sizeof(firmware_name), "ara:%08x:%08x:%08x:%08x:%02x.tftf", intf->unipro_mfg_id, intf->unipro_prod_id, - intf->ara_vend_id, intf->ara_prod_id, stage); + intf->vendor, intf->product, stage); return request_firmware(&firmware->fw, firmware_name, &connection->bundle->dev); diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index 68d2e959a410..c91e7be0451c 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -11,8 +11,8 @@ struct greybus_bundle_id { __u16 match_flags; - __u16 vendor; - __u16 product; + __u32 vendor; + __u32 product; __u8 class; kernel_ulong_t driver_info __aligned(sizeof(kernel_ulong_t)); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index f0ef4e9a6206..28d2bac2b7b1 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -22,16 +22,14 @@ struct gb_interface { u8 device_id; /* Device id allocated for the interface block by the SVC */ /* Information taken from the manifest descriptor */ - u16 vendor; - u16 product; char *vendor_string; char *product_string; /* Information taken from the hotplug event */ u32 unipro_mfg_id; u32 unipro_prod_id; - u32 ara_vend_id; - u32 ara_prod_id; + u32 vendor; + u32 product; struct gb_module *module; struct gb_host_device *hd; diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 4b4dd9973c17..c80a849617d7 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -392,11 +392,6 @@ static bool gb_manifest_parse_interface(struct gb_interface *intf, if (IS_ERR(intf->product_string)) goto out_free_vendor_string; - // FIXME - // Vendor, Product and Unique id must come via control protocol - intf->vendor = 0xffff; - intf->product = 0x0001; - /* Release the interface descriptor, now that we're done with it */ release_manifest_descriptor(interface_desc); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index bd045678493f..1acd0f7efc6b 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -460,8 +460,8 @@ static void svc_process_hotplug(struct work_struct *work) intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); intf->unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); - intf->ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id); - intf->ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id); + intf->vendor = le32_to_cpu(hotplug->data.ara_vend_id); + intf->product = le32_to_cpu(hotplug->data.ara_prod_id); /* * Create a device id for the interface: -- cgit v1.2.3-59-g8ed1b From 700001af960af35e57b701e57bd1595404dca613 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 21 Nov 2015 10:52:03 +0100 Subject: greybus: move id-matching back to core Move id-matching back to core and the bus code where it belongs. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 35 ----------------------------------- drivers/staging/greybus/core.c | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index fbc043998e08..4d04f09f0591 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -68,41 +68,6 @@ struct device_type greybus_bundle_type = { .release = gb_bundle_release, }; -static int gb_bundle_match_one_id(struct gb_bundle *bundle, - const struct greybus_bundle_id *id) -{ - if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && - (id->vendor != bundle->intf->vendor)) - return 0; - - if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && - (id->product != bundle->intf->product)) - return 0; - - if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) && - (id->class != bundle->class)) - return 0; - - return 1; -} - -const struct greybus_bundle_id * -gb_bundle_match_id(struct gb_bundle *bundle, - const struct greybus_bundle_id *id) -{ - if (id == NULL) - return NULL; - - for (; id->vendor || id->product || id->class || id->driver_info; - id++) { - if (gb_bundle_match_one_id(bundle, id)) - return id; - } - - return NULL; -} - - /* XXX This could be per-host device or per-module */ static DEFINE_SPINLOCK(gb_bundles_lock); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 4396f90e65d6..56250fa37708 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -29,13 +29,46 @@ int greybus_disabled(void) } EXPORT_SYMBOL_GPL(greybus_disabled); +static int greybus_match_one_id(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && + (id->vendor != bundle->intf->vendor)) + return 0; + + if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && + (id->product != bundle->intf->product)) + return 0; + + if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) && + (id->class != bundle->class)) + return 0; + + return 1; +} + +static const struct greybus_bundle_id * +greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id) +{ + if (id == NULL) + return NULL; + + for (; id->vendor || id->product || id->class || id->driver_info; + id++) { + if (greybus_match_one_id(bundle, id)) + return id; + } + + return NULL; +} + static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(drv); struct gb_bundle *bundle = to_gb_bundle(dev); const struct greybus_bundle_id *id; - id = gb_bundle_match_id(bundle, driver->id_table); + id = greybus_match_id(bundle, driver->id_table); if (id) return 1; /* FIXME - Dynamic ids? */ @@ -97,7 +130,7 @@ static int greybus_probe(struct device *dev) int retval; /* match id */ - id = gb_bundle_match_id(bundle, driver->id_table); + id = greybus_match_id(bundle, driver->id_table); if (!id) return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 3823c614793131afcfd87ba87bd83012ec52a879 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 21 Nov 2015 10:52:04 +0100 Subject: greybus: add class matching macro Add matching macro for bundle class. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4f8aa318d2ab..6da4e78248a8 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -50,6 +50,10 @@ .vendor = (v), \ .product = (p), +#define GREYBUS_DEVICE_CLASS(c) \ + .match_flags = GREYBUS_ID_MATCH_CLASS, \ + .class = (c), + /* Maximum number of CPorts */ #define CPORT_ID_MAX 4095 /* UniPro max id is 4095 */ #define CPORT_ID_BAD U16_MAX -- cgit v1.2.3-59-g8ed1b From d3d2af51f9c2f29a0bf5df278503820004d71e36 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Mon, 23 Nov 2015 15:57:45 +0530 Subject: greybus: Audio: Add skeleton code for GB virtual codec driver This patch adds gb-codec driver with static information for DAPM widgets, controls & dapm_routes. Including some changes in kernel code(machine driver): - Able to register codec and glue it with existing sound card successfully. - Able to view & modify mixer controls: (volume/mute[left/right][input/output]) - Able to view DAPM widgets registered via /debug interface. - Able to establish DAPM path for playback. Since, FE<->BE path not yet verified with default jetson build, registering GB DAI as normal DAI link to verify GB virtual codec specific DAPM path. Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/audio-codec.c | 315 ++++++++++++++++++++++++++++++++++ drivers/staging/greybus/audio.h | 86 ++++++++++ 3 files changed, 403 insertions(+) create mode 100644 drivers/staging/greybus/audio-codec.c create mode 100644 drivers/staging/greybus/audio.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 2c1f9746bae1..40e22ec810ae 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -31,6 +31,7 @@ gb-raw-y := raw.o gb-hid-y := hid.o gb-es2-y := es2.o gb-db3-y := db3-platform.o +gb-audio-codec-y := audio-codec.o obj-m += greybus.o obj-m += gb-phy.o @@ -42,6 +43,7 @@ obj-m += gb-hid.o obj-m += gb-raw.o obj-m += gb-es2.o obj-m += gb-db3.o +obj-m += gb-audio-codec.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/audio-codec.c b/drivers/staging/greybus/audio-codec.c new file mode 100644 index 000000000000..2bc23095ffd0 --- /dev/null +++ b/drivers/staging/greybus/audio-codec.c @@ -0,0 +1,315 @@ +/* + * Greybus audio driver + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include + +#include "audio.h" + +static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + /* Ensure GB speaker is connected */ + + return 0; +} + +static int gbcodec_event_hp(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + /* Ensure GB module supports jack slot */ + + return 0; +} + +static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + /* Ensure GB module supports jack slot */ + + return 0; +} + +static const struct snd_kcontrol_new gbcodec_snd_controls[] = { + SOC_DOUBLE("Playback Mute", GBCODEC_MUTE_REG, 0, 1, 1, 1), + SOC_DOUBLE("Capture Mute", GBCODEC_MUTE_REG, 4, 5, 1, 1), + SOC_DOUBLE_R("Playback Volume", GBCODEC_PB_LVOL_REG, + GBCODEC_PB_RVOL_REG, 0, 127, 0), + SOC_DOUBLE_R("Capture Volume", GBCODEC_CAP_LVOL_REG, + GBCODEC_CAP_RVOL_REG, 0, 127, 0), +}; + +static const struct snd_kcontrol_new spk_amp_ctl = + SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 0, 1, 0); + +static const struct snd_kcontrol_new hp_amp_ctl = + SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 1, 1, 0); + +static const struct snd_kcontrol_new mic_adc_ctl = + SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 4, 1, 0); + +/* APB1-GBSPK source */ +static const char * const gbcodec_apb1_src[] = {"Stereo", "Left", "Right"}; + +static const SOC_ENUM_SINGLE_DECL( + gbcodec_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbcodec_apb1_src); + +static const struct snd_kcontrol_new gbcodec_apb1_rx_mux = + SOC_DAPM_ENUM("APB1 source", gbcodec_apb1_rx_enum); + +static const SOC_ENUM_SINGLE_DECL( + gbcodec_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbcodec_apb1_src); + +static const struct snd_kcontrol_new gbcodec_mic_mux = + SOC_DAPM_ENUM("MIC source", gbcodec_mic_enum); + +static const struct snd_soc_dapm_widget gbcodec_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Spk", gbcodec_event_spk), + SND_SOC_DAPM_SPK("HP", gbcodec_event_hp), + SND_SOC_DAPM_MIC("Int Mic", gbcodec_event_int_mic), + + SND_SOC_DAPM_OUTPUT("SPKOUT"), + SND_SOC_DAPM_OUTPUT("HPOUT"), + + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_INPUT("HSMIC"), + + SND_SOC_DAPM_SWITCH("SPK Amp", SND_SOC_NOPM, 0, 0, &spk_amp_ctl), + SND_SOC_DAPM_SWITCH("HP Amp", SND_SOC_NOPM, 0, 0, &hp_amp_ctl), + SND_SOC_DAPM_SWITCH("MIC ADC", SND_SOC_NOPM, 0, 0, &mic_adc_ctl), + + SND_SOC_DAPM_PGA("SPK DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("SPK Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("APB1_TX Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("APB1_RX Mux", SND_SOC_NOPM, 0, 0, + &gbcodec_apb1_rx_mux), + SND_SOC_DAPM_MUX("MIC Mux", SND_SOC_NOPM, 0, 0, &gbcodec_mic_mux), + + SND_SOC_DAPM_AIF_IN("APB1RX", "APBridgeA1 Playback", 0, SND_SOC_NOPM, 0, + 0), + SND_SOC_DAPM_AIF_OUT("APB1TX", "APBridgeA1 Capture", 0, SND_SOC_NOPM, 0, + 0), +}; + +static const struct snd_soc_dapm_route gbcodec_dapm_routes[] = { + /* Playback path */ + {"Spk", NULL, "SPKOUT"}, + {"SPKOUT", NULL, "SPK Amp"}, + {"SPK Amp", "Switch", "SPK DAC"}, + {"SPK DAC", NULL, "SPK Mixer"}, + + {"HP", NULL, "HPOUT"}, + {"HPOUT", NULL, "HP Amp"}, + {"HP Amp", "Switch", "HP DAC"}, + {"HP DAC", NULL, "HP Mixer"}, + + {"SPK Mixer", NULL, "APB1_RX Mux"}, + {"HP Mixer", NULL, "APB1_RX Mux"}, + + {"APB1_RX Mux", "Left", "APB1RX"}, + {"APB1_RX Mux", "Right", "APB1RX"}, + {"APB1_RX Mux", "Stereo", "APB1RX"}, + + /* Capture path */ + {"MIC", NULL, "Int Mic"}, + {"MIC", NULL, "MIC Mux"}, + {"MIC Mux", "Left", "MIC ADC"}, + {"MIC Mux", "Right", "MIC ADC"}, + {"MIC Mux", "Stereo", "MIC ADC"}, + {"MIC ADC", "Switch", "APB1_TX Mixer"}, + {"APB1_TX Mixer", NULL, "APB1TX"} +}; + +static int gbcodec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return 0; +} + +static void gbcodec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ +} + +static int gbcodec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hwparams, + struct snd_soc_dai *dai) +{ + return 0; +} + +static int gbcodec_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return 0; +} + +static int gbcodec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + return 0; +} + +static int gbcodec_digital_mute(struct snd_soc_dai *dai, int mute) +{ + return 0; +} + +static struct snd_soc_dai_ops gbcodec_dai_ops = { + .startup = gbcodec_startup, + .shutdown = gbcodec_shutdown, + .hw_params = gbcodec_hw_params, + .prepare = gbcodec_prepare, + .set_fmt = gbcodec_set_dai_fmt, + .digital_mute = gbcodec_digital_mute, +}; + +static struct snd_soc_dai_driver gbcodec_dai = { + .playback = { + .stream_name = "APBridgeA1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "APBridgeA1 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &gbcodec_dai_ops, +}; + +static int gbcodec_probe(struct snd_soc_codec *codec) +{ + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + + gbcodec->codec = codec; + + return 0; +} + +static int gbcodec_remove(struct snd_soc_codec *codec) +{ + /* Empty function for now */ + return 0; +} + +static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + int ret = 0; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + u8 *gbcodec_reg = gbcodec->reg; + + if (reg == SND_SOC_NOPM) + return 0; + + if (reg >= GBCODEC_REG_COUNT) + return 0; + + gbcodec_reg[reg] = value; + dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value); + + return ret; +} + +static unsigned int gbcodec_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + unsigned int val = 0; + + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + u8 *gbcodec_reg = gbcodec->reg; + + if (reg == SND_SOC_NOPM) + return 0; + + if (reg >= GBCODEC_REG_COUNT) + return 0; + + val = gbcodec_reg[reg]; + dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val); + + return val; +} + +static struct snd_soc_codec_driver soc_codec_dev_gbcodec = { + .probe = gbcodec_probe, + .remove = gbcodec_remove, + + .read = gbcodec_read, + .write = gbcodec_write, + + .reg_cache_size = GBCODEC_REG_COUNT, + .reg_cache_default = gbcodec_reg_defaults, + .reg_word_size = 1, + + .idle_bias_off = true, + + .controls = gbcodec_snd_controls, + .num_controls = ARRAY_SIZE(gbcodec_snd_controls), + .dapm_widgets = gbcodec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(gbcodec_dapm_widgets), + .dapm_routes = gbcodec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(gbcodec_dapm_routes), +}; + +static int gbaudio_codec_probe(struct platform_device *pdev) +{ + int ret; + struct gbaudio_codec_info *gbcodec; + char dai_name[NAME_SIZE]; + + gbcodec = devm_kzalloc(&pdev->dev, sizeof(struct gbaudio_codec_info), + GFP_KERNEL); + if (!gbcodec) + return -ENOMEM; + platform_set_drvdata(pdev, gbcodec); + + snprintf(dai_name, NAME_SIZE, "%s.%d", "gbcodec_pcm", pdev->id); + gbcodec_dai.name = dai_name; + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbcodec, + &gbcodec_dai, 1); + if (!ret) + gbcodec->registered = 1; + + return ret; +} + +static const struct of_device_id gbcodec_of_match[] = { + { .compatible = "greybus,codec", }, + {}, +}; + +static int gbaudio_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_driver gbaudio_codec_driver = { + .driver = { + .name = "gbaudio-codec", + .owner = THIS_MODULE, + .of_match_table = gbcodec_of_match, + }, + .probe = gbaudio_codec_probe, + .remove = gbaudio_codec_remove, +}; +module_platform_driver(gbaudio_codec_driver); + +MODULE_DESCRIPTION("Greybus Audio virtual codec driver"); +MODULE_AUTHOR("Vaibhav Agarwal "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gbaudio-codec"); diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h new file mode 100644 index 000000000000..5dec00d63e27 --- /dev/null +++ b/drivers/staging/greybus/audio.h @@ -0,0 +1,86 @@ +/* + * Greybus audio driver + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __LINUX_GBAUDIO_H +#define __LINUX_GBAUDIO_H + +#ifdef __KERNEL__ + +#include + +#define NAME_SIZE 32 + +enum { + APB1_PCM = 0, + APB2_PCM, + NUM_CODEC_DAIS, +}; + +enum gbcodec_reg_index { + GBCODEC_CTL_REG, + GBCODEC_MUTE_REG, + GBCODEC_PB_LVOL_REG, + GBCODEC_PB_RVOL_REG, + GBCODEC_CAP_LVOL_REG, + GBCODEC_CAP_RVOL_REG, + GBCODEC_APB1_MUX_REG, + GBCODEC_APB2_MUX_REG, + GBCODEC_REG_COUNT +}; + +/* bit 0-SPK, 1-HP, 2-DAC, + * 4-MIC, 5-HSMIC, 6-MIC2 + */ +#define GBCODEC_CTL_REG_DEFAULT 0x00 + +/* bit 0,1 - APB1-PB-L/R + * bit 2,3 - APB2-PB-L/R + * bit 4,5 - APB1-Cap-L/R + * bit 6,7 - APB2-Cap-L/R + */ +#define GBCODEC_MUTE_REG_DEFAULT 0x00 + +/* 0-127 steps */ +#define GBCODEC_PB_VOL_REG_DEFAULT 0x00 +#define GBCODEC_CAP_VOL_REG_DEFAULT 0x00 + +/* bit 0,1,2 - PB stereo, left, right + * bit 8,9,10 - Cap stereo, left, right + */ +#define GBCODEC_APB1_MUX_REG_DEFAULT 0x00 +#define GBCODEC_APB2_MUX_REG_DEFAULT 0x00 + +static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { + GBCODEC_CTL_REG_DEFAULT, + GBCODEC_MUTE_REG_DEFAULT, + GBCODEC_PB_VOL_REG_DEFAULT, + GBCODEC_PB_VOL_REG_DEFAULT, + GBCODEC_CAP_VOL_REG_DEFAULT, + GBCODEC_CAP_VOL_REG_DEFAULT, + GBCODEC_APB1_MUX_REG_DEFAULT, + GBCODEC_APB2_MUX_REG_DEFAULT, +}; + +struct gbaudio_codec_info { + struct snd_soc_codec *codec; + + bool usable; + u8 reg[GBCODEC_REG_COUNT]; + int registered; + + int num_kcontrols; + int num_dapm_widgets; + int num_dapm_routes; + struct snd_kcontrol_new *kctls; + struct snd_soc_dapm_widget *widgets; + struct snd_soc_dapm_route *routes; + struct mutex lock; +}; + +#endif /* __KERNEL__ */ +#endif /* __LINUX_GBAUDIO_H */ -- cgit v1.2.3-59-g8ed1b From 39f36c8f9ff6203301a1d7feb7f51d2563abbd3a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 21 Nov 2015 10:51:58 +0100 Subject: greybus: connection: fix cport-id range Fix cport-id allocation that failed to include the highest port id in the available cport-id range. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 96dabf7640a4..695f1481ba0f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -219,7 +219,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, { return gb_connection_create_range(bundle->intf->hd, bundle, &bundle->dev, cport_id, protocol_id, - 0, bundle->intf->hd->num_cports - 1); + 0, bundle->intf->hd->num_cports); } /* -- cgit v1.2.3-59-g8ed1b From bfe2c99c1cd346b06a7d34274b9b826d40ab40a1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 22 Nov 2015 10:12:58 +0100 Subject: greybus: hd: fix cport-count check Fix off-by-one error when checking the number of cports a host-device supports. The CPORT_ID_MAX is the largest valid cport id so the maximum number of cports a host-device can use is CPORT_ID_MAX + 1. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index b22d5470508a..3446fec35895 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -49,7 +49,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, return ERR_PTR(-EINVAL); } - if (num_cports == 0 || num_cports > CPORT_ID_MAX) { + if (num_cports == 0 || num_cports > CPORT_ID_MAX + 1) { dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports); return ERR_PTR(-EINVAL); } -- cgit v1.2.3-59-g8ed1b From fc25d9068e80659ea2b3a4516c63c523bdafc20b Mon Sep 17 00:00:00 2001 From: Sachin Pandhare Date: Tue, 24 Nov 2015 07:59:10 +0530 Subject: greybus: manifest: simplify descriptor address calculation This patch doesn't change any functionality. It just improves the readability of the code. Current code to get 'descriptors' pointer looks as if we are forcing the pointer type change. To simplify the address calculations, use 'descriptors' member directly from greybus_manifest structure. Signed-off-by: Sachin Pandhare Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index c80a849617d7..41d51579217f 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -475,7 +475,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) } /* OK, find all the descriptors */ - desc = (struct greybus_descriptor *)(header + 1); + desc = manifest->descriptors; size -= sizeof(*header); while (size) { int desc_size; -- cgit v1.2.3-59-g8ed1b From f6f8aeaa35899de2180b93a0efc27a7d5b0c4ec5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 22 Nov 2015 10:24:08 +0100 Subject: greybus: Documentation/sysfs: update the example sysfs tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the example sysfs-tree layout under Documentation. The new layout reflects changes to the kernel device-model that are needed to eventually be able to handle multiple AP-bridges. The example tree has two AP-bridges, each with its own view of the UniPro network, the bus. . ├── greybus1 │   ├── 1-2 │   │   ├── 1-2.1 │   │   │   ├── class │   │   │   ├── id │   │   │   └── state │   │   ├── 1-2.2 │   │   │   ├── class │   │   │   ├── id │   │   │   └── state │   │   ├── id │   │   ├── product_id │   │   ├── unique_id │   │   └── vendor_id │   ├── 1-4 │   │   ├── 1-4.2 │   │   │   ├── class │   │   │   ├── gpbridge0 │   │   │   │   ├── gpio │   │   │   │   │   └── gpiochip490 │   │   │   │   └── i2c-4 │   │   │   ├── id │   │   │   └── state │   │   ├── id │   │   ├── product_id │   │   ├── unique_id │   │   └── vendor_id │   └── 1-svc │   ├── ap_intf_id │   ├── eject │   ├── endo_id │   └── unique_id └── greybus2 ├── 2-3 │   ├── 2-3.1 │   │   ├── class │   │   ├── id │   │   └── state │   ├── id │   ├── product_id │   ├── unique_id │   └── vendor_id └── 2-svc ├── ap_intf_id ├── eject ├── endo_id └── unique_id Every bus has exactly one svc device (1-svc and 2-svc). For our system, the svc device of each bus will be a representation of the same network-unique SVC device (e.g. endo_id and unique_id will be identical). The first bus has two registered interfaces (1-2 and 1-4), while the second bus has a single interface (2-3). Note that the interface ids (2, 4, and 3) are necessarily unique as these are interfaces on the same network. Interface 1-2 has two bundles (1-2.1 and 1-2.2) and interface 1-4 has a single bundle (1-4.2). The bundle ids are interface-unique and reflect the ids found in each manifest. In the example, bundle 1-4.2 has a gbbridge-device, which is the parent device for a gpiochip device and an i2c bus. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_id | 0 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_name | 0 .../Documentation/sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore | 1 - .../Documentation/sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore | 1 - .../Documentation/sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore | 1 - drivers/staging/greybus/Documentation/sysfs/endo/01/01/firmware | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/01/manifest | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_id | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_name | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/01/state | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/01/uid | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_major | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_minor | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/epm | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/power | 0 drivers/staging/greybus/Documentation/sysfs/endo/01/present | 0 drivers/staging/greybus/Documentation/sysfs/endo/02/02/.gitignore | 1 - drivers/staging/greybus/Documentation/sysfs/endo/ap_intf_id | 0 drivers/staging/greybus/Documentation/sysfs/endo/id | 0 drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/firmware | 0 drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/version | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/state | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/state | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/product_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/vendor_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/class | 0 .../sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore | 1 + .../Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/state | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/product_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/vendor_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/class | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/state | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/product_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/vendor_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/unique_id | 0 56 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_name delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/firmware delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/manifest delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_name delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/state delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/uid delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_major delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_minor delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/epm delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/power delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/01/present delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/02/02/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/ap_intf_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/firmware delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/version create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/unique_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/vendor_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/unique_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/vendor_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/eject create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/unique_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/unique_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/vendor_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/eject create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/unique_id diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_id b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_name b/drivers/staging/greybus/Documentation/sysfs/endo-TYPE/01/01/vendor_name deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/gpio/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/i2c/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/01/01/gpbridge00/usb/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/firmware b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/firmware deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/manifest b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/manifest deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_id b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_name b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/product_name deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/state b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/uid b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/uid deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_major b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_major deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_minor b/drivers/staging/greybus/Documentation/sysfs/endo/01/01/version_minor deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/epm b/drivers/staging/greybus/Documentation/sysfs/endo/01/epm deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/power b/drivers/staging/greybus/Documentation/sysfs/endo/01/power deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/01/present b/drivers/staging/greybus/Documentation/sysfs/endo/01/present deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/02/02/.gitignore b/drivers/staging/greybus/Documentation/sysfs/endo/02/02/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/endo/02/02/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/endo/ap_intf_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/id b/drivers/staging/greybus/Documentation/sysfs/endo/id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/firmware b/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/firmware deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/serial_number b/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/version b/drivers/staging/greybus/Documentation/sysfs/endo/svc/svc/version deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/unique_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/vendor_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id new file mode 100644 index 000000000000..b8626c4cff28 --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id @@ -0,0 +1 @@ +4 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/unique_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/vendor_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/eject new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/unique_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/class b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id new file mode 100644 index 000000000000..00750edc07d6 --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id @@ -0,0 +1 @@ +3 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/unique_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/vendor_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/eject b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/eject new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/unique_id new file mode 100644 index 000000000000..e69de29bb2d1 -- cgit v1.2.3-59-g8ed1b From fda3412566631d7c22edc3da7561f09a6f4dbc96 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:58:55 +0100 Subject: greybus: Documentation/sysfs-bus-greybus: update the bus ABI documentation Update the ABI documentation to match the new device model. Note that the SVC unique_id and version attributes are not yet implemented. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs-bus-greybus | 94 +++++++--------------- 1 file changed, 31 insertions(+), 63 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 8363f2307049..88ed2d8905cc 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -1,13 +1,19 @@ -What: /sys/bus/greybus/device/endoE +What: /sys/bus/greybus/device/greybusN Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The "root" endo devices for the Greybus device tree. E - is replaced with a 2 byte number representing the endo, - mostly 0. + The "root" greybus device for the Greybus device tree, or bus, + where N is a dynamically assigned 1-based id. -What: /sys/bus/greybus/device/endoE/id +What: /sys/bus/greybus/device/N-svc/endo_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The singleton SVC device of bus N. + +What: /sys/bus/greybus/device/N-svc/endo_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -16,112 +22,74 @@ Description: defined by the Endo layout scheme, documented in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endoE/ap_intf_id +What: /sys/bus/greybus/device/N-svc/ap_intf_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The AP interface ID, a 1-byte non-zero integer which - defines the position of the AP module on the Endo. + defines the position of the AP module on the frame. The interface positions are defined in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/endoE/svc/serial_number +What: /sys/bus/greybus/device/N-svc/unique_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The serial number of the SVC device + The unique ID, or serial number, of the SVC device -What: /sys/bus/greybus/device/endoE/svc/version +What: /sys/bus/greybus/device/N-svc/version Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The version number of the firmware in the SVC device. -What: /sys/bus/greybus/device/endoE:M -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - A module slot on the endoE, M is replaced by a 1-byte - number representing the module slot. - -What: /sys/bus/greybus/device/endoE:M/epm -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The EPM (Electropermanent Magnet) control file for - the specific module slot the file is present in. - Writing 1 to it turns it on, writing 0 to it turns it - off. Reading the value returns if it is on or off. - -What: /sys/bus/greybus/device/endoE:M/power_control -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The power control file for the specific module slot that - the file is present in. Writing 1 to it turns power on - to the module, writing 0 to it turns power off to the - module. Reading the value returns if it is on or off. - -What: /sys/bus/greybus/device/endoE:M/present -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The "is a module present in the slot" file for the - specific module slot that the file is present in. - This is read-only, 1 means a module is present, 0 means - no module is present. - -What: /sys/bus/greybus/device/endoE:M:I +What: /sys/bus/greybus/device/N-I Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - An Interface I on the module slot M on the endoE, I is - replaced by a 1-byte number representing the interface. + An Interface I on the bus N, where I is the 1-byte interface + ID. -What: /sys/bus/greybus/device/endoE:M:I/device_id +What: /sys/bus/greybus/device/N-I/interface_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - The device ID of a Greybus interface block. + The ID of a Greybus interface. -What: /sys/bus/greybus/device/endoE:M:I/product +What: /sys/bus/greybus/device/N-I/product_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - Product ID of a Greybus interface block. + Product ID of a Greybus interface. -What: /sys/bus/greybus/device/endoE:M:I/product_string +What: /sys/bus/greybus/device/N-I/product_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - Product ID string of a Greybus interface block. + Product ID string of a Greybus interface. -What: /sys/bus/greybus/device/endoE:M:I/vendor +What: /sys/bus/greybus/device/N-I/vendor_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: - Vendor ID of a Greybus interface block. + Vendor ID of a Greybus interface. -What: /sys/bus/greybus/device/endoE:M:I/vendor_string +What: /sys/bus/greybus/device/N-I/vendor_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: Vendor ID string of a Greybus interface block. -What: /sys/bus/greybus/device/endoE:M:I:B +What: /sys/bus/greybus/device/N-I.B Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman @@ -129,14 +97,14 @@ Description: A bundle B on the Interface I, B is replaced by a 1-byte number representing the bundle. -What: /sys/bus/greybus/device/endoE:M:I:B/class +What: /sys/bus/greybus/device/N-I.B/bundle_class Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The greybus class of the bundle B. -What: /sys/bus/greybus/device/endoE:M:I:B/state +What: /sys/bus/greybus/device/N-I.B/state Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman -- cgit v1.2.3-59-g8ed1b From 9f59263a8976db0877409d9f9813d7101756a653 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:58:56 +0100 Subject: greybus: interface: rename vendor and product attributes Rename vendor and product attributes vendor_id and product_id. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 ++-- drivers/staging/greybus/firmware.c | 2 +- drivers/staging/greybus/interface.c | 8 ++++---- drivers/staging/greybus/interface.h | 4 ++-- drivers/staging/greybus/svc.c | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 56250fa37708..b3c422acf352 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -33,11 +33,11 @@ static int greybus_match_one_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && - (id->vendor != bundle->intf->vendor)) + (id->vendor != bundle->intf->vendor_id)) return 0; if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && - (id->product != bundle->intf->product)) + (id->product != bundle->intf->product_id)) return 0; if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) && diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index e99d8d679686..e41526b62ab6 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -41,7 +41,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) snprintf(firmware_name, sizeof(firmware_name), "ara:%08x:%08x:%08x:%08x:%02x.tftf", intf->unipro_mfg_id, intf->unipro_prod_id, - intf->vendor, intf->product, stage); + intf->vendor_id, intf->product_id, stage); return request_firmware(&firmware->fw, firmware_name, &connection->bundle->dev); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index b57cc09d0f44..9f4cf4d35a73 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -21,15 +21,15 @@ static ssize_t field##_show(struct device *dev, \ static DEVICE_ATTR_RO(field) gb_interface_attr(device_id, d); -gb_interface_attr(vendor, x); -gb_interface_attr(product, x); +gb_interface_attr(vendor_id, x); +gb_interface_attr(product_id, x); gb_interface_attr(vendor_string, s); gb_interface_attr(product_string, s); static struct attribute *interface_attrs[] = { &dev_attr_device_id.attr, - &dev_attr_vendor.attr, - &dev_attr_product.attr, + &dev_attr_vendor_id.attr, + &dev_attr_product_id.attr, &dev_attr_vendor_string.attr, &dev_attr_product_string.attr, NULL, diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 28d2bac2b7b1..71493cd96ec7 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -28,8 +28,8 @@ struct gb_interface { /* Information taken from the hotplug event */ u32 unipro_mfg_id; u32 unipro_prod_id; - u32 vendor; - u32 product; + u32 vendor_id; + u32 product_id; struct gb_module *module; struct gb_host_device *hd; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 1acd0f7efc6b..2e8c444d8b16 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -460,8 +460,8 @@ static void svc_process_hotplug(struct work_struct *work) intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); intf->unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); - intf->vendor = le32_to_cpu(hotplug->data.ara_vend_id); - intf->product = le32_to_cpu(hotplug->data.ara_prod_id); + intf->vendor_id = le32_to_cpu(hotplug->data.ara_vend_id); + intf->product_id = le32_to_cpu(hotplug->data.ara_prod_id); /* * Create a device id for the interface: -- cgit v1.2.3-59-g8ed1b From 1eca63ae79cc90ae3c8cabe21d5c9c717eb82a3a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:58:57 +0100 Subject: greybus: interface: remove device_id attribute The interface device_id attribute is an implementation detail that does not need to be exported to user space. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 9f4cf4d35a73..4ed782e6c7b0 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -20,14 +20,12 @@ static ssize_t field##_show(struct device *dev, \ } \ static DEVICE_ATTR_RO(field) -gb_interface_attr(device_id, d); gb_interface_attr(vendor_id, x); gb_interface_attr(product_id, x); gb_interface_attr(vendor_string, s); gb_interface_attr(product_string, s); static struct attribute *interface_attrs[] = { - &dev_attr_device_id.attr, &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, &dev_attr_vendor_string.attr, -- cgit v1.2.3-59-g8ed1b From 320421a80bdd0f7373dbba0bf01b594e12d57218 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:58:58 +0100 Subject: greybus: interface: add interface_id attribute Add interface_id attribute that user space needs to identify an interface. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 4ed782e6c7b0..b5d9046ead44 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -20,12 +20,14 @@ static ssize_t field##_show(struct device *dev, \ } \ static DEVICE_ATTR_RO(field) +gb_interface_attr(interface_id, u); gb_interface_attr(vendor_id, x); gb_interface_attr(product_id, x); gb_interface_attr(vendor_string, s); gb_interface_attr(product_string, s); static struct attribute *interface_attrs[] = { + &dev_attr_interface_id.attr, &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, &dev_attr_vendor_string.attr, -- cgit v1.2.3-59-g8ed1b From 4396c00b73d1ead961f09016aa9c731ea97c44ab Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:58:59 +0100 Subject: greybus: bundle: rename class attribute Rename the bundle class-attribute "bundle_class" for consistency reasons and to make it self documenting. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 4d04f09f0591..7b539e9d7aea 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -9,14 +9,14 @@ #include "greybus.h" -static ssize_t class_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t bundle_class_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct gb_bundle *bundle = to_gb_bundle(dev); return sprintf(buf, "%d\n", bundle->class); } -static DEVICE_ATTR_RO(class); +static DEVICE_ATTR_RO(bundle_class); static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -48,7 +48,7 @@ static DEVICE_ATTR_RW(state); static struct attribute *bundle_attrs[] = { - &dev_attr_class.attr, + &dev_attr_bundle_class.attr, &dev_attr_state.attr, NULL, }; -- cgit v1.2.3-59-g8ed1b From 2b14daba1d6976a078ba8ccdd1f932f2320061a8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:00 +0100 Subject: greybus: bundle: use hexadecimal notation for class attribute Use hexadecimal notation for class-attribute value. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 7b539e9d7aea..61c9a6d68561 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -14,7 +14,7 @@ static ssize_t bundle_class_show(struct device *dev, { struct gb_bundle *bundle = to_gb_bundle(dev); - return sprintf(buf, "%d\n", bundle->class); + return sprintf(buf, "0x%02x\n", bundle->class); } static DEVICE_ATTR_RO(bundle_class); -- cgit v1.2.3-59-g8ed1b From a97015c9e99d8421b80cdf9652a456f4cd93fc1e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:01 +0100 Subject: greybus: bundle: add bundle_id attribute Add a bundle_id attribute for the interface-unique id of a bundle that user space can use for matching. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 7 +++++++ drivers/staging/greybus/bundle.c | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 88ed2d8905cc..9329a31647cd 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -104,6 +104,13 @@ Contact: Greg Kroah-Hartman Description: The greybus class of the bundle B. +What: /sys/bus/greybus/device/N-I.B/bundle_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The interface-unique id of the bundle B. + What: /sys/bus/greybus/device/N-I.B/state Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 61c9a6d68561..f746438bc962 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -18,6 +18,15 @@ static ssize_t bundle_class_show(struct device *dev, } static DEVICE_ATTR_RO(bundle_class); +static ssize_t bundle_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + + return sprintf(buf, "%u\n", bundle->id); +} +static DEVICE_ATTR_RO(bundle_id); + static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -49,6 +58,7 @@ static DEVICE_ATTR_RW(state); static struct attribute *bundle_attrs[] = { &dev_attr_bundle_class.attr, + &dev_attr_bundle_id.attr, &dev_attr_state.attr, NULL, }; -- cgit v1.2.3-59-g8ed1b From 2adaefb1458f67b3f71111fcf6a15670ab64101d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:02 +0100 Subject: greybus: hd: make host device a device Make the host device a proper device in the kernel device model. Host devices will be our new greybus-bus root devices. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ++-- drivers/staging/greybus/core.c | 14 +++++++- drivers/staging/greybus/endo.c | 18 +++++----- drivers/staging/greybus/greybus.h | 6 ++++ drivers/staging/greybus/greybus_trace.h | 4 +-- drivers/staging/greybus/hd.c | 59 +++++++++++++++++++++++++++------ drivers/staging/greybus/hd.h | 8 +++-- drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/svc.c | 2 +- 9 files changed, 90 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 695f1481ba0f..c076171cba7a 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -55,7 +55,7 @@ void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, connection = gb_connection_hd_find(hd, cport_id); if (!connection) { - dev_err(hd->parent, + dev_err(&hd->dev, "nonexistent connection (%zu bytes dropped)\n", length); return; } @@ -196,7 +196,7 @@ static int gb_connection_hd_cport_enable(struct gb_connection *connection) ret = hd->driver->cport_enable(hd, connection->hd_cport_id); if (ret) { - dev_err(hd->parent, + dev_err(&hd->dev, "failed to enable host cport: %d\n", ret); return ret; } @@ -502,7 +502,7 @@ int gb_connection_bind_protocol(struct gb_connection *connection) connection->major, connection->minor); if (!protocol) { - dev_warn(connection->hd->parent, + dev_warn(&connection->hd->dev, "protocol 0x%02hhx version %hhu.%hhu not found\n", connection->protocol_id, connection->major, connection->minor); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index b3c422acf352..8c8ba02b76e9 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -77,6 +77,7 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { + struct gb_host_device *hd = NULL; struct gb_module *module = NULL; struct gb_interface *intf = NULL; struct gb_bundle *bundle = NULL; @@ -89,7 +90,9 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } - if (is_gb_module(dev)) { + if (is_gb_host_device(dev)) { + hd = to_gb_host_device(dev); + } else if (is_gb_module(dev)) { module = to_gb_module(dev); } else if (is_gb_interface(dev)) { intf = to_gb_interface(dev); @@ -196,6 +199,12 @@ static int __init gb_init(void) goto error_bus; } + retval = gb_hd_init(); + if (retval) { + pr_err("gb_hd_init failed (%d)\n", retval); + goto error_hd; + } + retval = gb_operation_init(); if (retval) { pr_err("gb_operation_init failed (%d)\n", retval); @@ -237,6 +246,8 @@ error_control: error_endo: gb_operation_exit(); error_operation: + gb_hd_exit(); +error_hd: bus_unregister(&greybus_bus_type); error_bus: gb_debugfs_cleanup(); @@ -252,6 +263,7 @@ static void __exit gb_exit(void) gb_control_protocol_exit(); gb_endo_exit(); gb_operation_exit(); + gb_hd_exit(); bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); tracepoint_synchronize_unregister(); diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 775dbcea539b..6fb22fb8e85b 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -219,7 +219,7 @@ static bool validate_front_ribs(struct gb_host_device *hd, layout->front_ribs = 0x5; break; default: - dev_err(hd->parent, + dev_err(&hd->dev, "%s: Invalid endo front mask 0x%02x, id 0x%04x\n", __func__, front_mask, endo_id); return false; @@ -266,21 +266,21 @@ static bool validate_back_ribs(struct gb_host_device *hd, right_ribs = endo_back_right_ribs(endo_id, max_ribs); if (!single_cross_rib(left_ribs, right_ribs)) { - dev_err(hd->parent, + dev_err(&hd->dev, "%s: More than one spanning rib (left 0x%02x right 0x%02x), id 0x%04x\n", __func__, left_ribs, right_ribs, endo_id); return false; } if (modules_oversized(max_ribs, left_ribs)) { - dev_err(hd->parent, + dev_err(&hd->dev, "%s: Oversized module (left) 0x%02x, id 0x%04x\n", __func__, left_ribs, endo_id); return false; } if (modules_oversized(max_ribs, right_ribs)) { - dev_err(hd->parent, + dev_err(&hd->dev, "%s: Oversized module (Right) 0x%02x, id 0x%04x\n", __func__, right_ribs, endo_id); return false; @@ -306,7 +306,7 @@ static bool validate_back_ribs(struct gb_host_device *hd, * are of different widths. */ if (max_ribs != ENDO_BACK_RIBS_MEDIUM && left_ribs < right_ribs) { - dev_err(hd->parent, "%s: Non-canonical endo id 0x%04x\n", __func__, + dev_err(&hd->dev, "%s: Non-canonical endo id 0x%04x\n", __func__, endo_id); return false; } @@ -334,7 +334,7 @@ static int gb_endo_validate_id(struct gb_host_device *hd, /* Mini Endo type */ layout->max_ribs = ENDO_BACK_RIBS_MINI; } else { - dev_err(hd->parent, "%s: Invalid endo type, id 0x%04x\n", + dev_err(&hd->dev, "%s: Invalid endo type, id 0x%04x\n", __func__, endo_id); return -EINVAL; } @@ -447,11 +447,11 @@ static int gb_endo_register(struct gb_host_device *hd, endo->dev_id = dev_id; - endo->dev.parent = hd->parent; + endo->dev.parent = &hd->dev; endo->dev.bus = &greybus_bus_type; endo->dev.type = &greybus_endo_type; endo->dev.groups = endo_groups; - endo->dev.dma_mask = hd->parent->dma_mask; + endo->dev.dma_mask = hd->dev.dma_mask; device_initialize(&endo->dev); dev_set_name(&endo->dev, "endo%hu", endo->dev_id); @@ -463,7 +463,7 @@ static int gb_endo_register(struct gb_host_device *hd, retval = device_add(&endo->dev); if (retval) { - dev_err(hd->parent, "failed to add endo device of id 0x%04x\n", + dev_err(&hd->dev, "failed to add endo device of id 0x%04x\n", endo->id); put_device(&endo->dev); } diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 6da4e78248a8..8ef3a0426a09 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -104,11 +104,17 @@ struct dentry *gb_debugfs_get(void); extern struct bus_type greybus_bus_type; +extern struct device_type greybus_hd_type; extern struct device_type greybus_endo_type; extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_bundle_type; +static inline int is_gb_host_device(const struct device *dev) +{ + return dev->type == &greybus_hd_type; +} + static inline int is_gb_endo(const struct device *dev) { return dev->type == &greybus_endo_type; diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index a39fa396d949..1ca07064f5de 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -114,13 +114,13 @@ DECLARE_EVENT_CLASS(gb_host_device, TP_ARGS(hd, intf_cport_id, payload_size), TP_STRUCT__entry( - __string(name, dev_name(hd->parent)) + __string(name, dev_name(&hd->dev)) __field(u16, intf_cport_id) __field(size_t, payload_size) ), TP_fast_assign( - __assign_str(name, dev_name(hd->parent)) + __assign_str(name, dev_name(&hd->dev)) __entry->intf_cport_id = intf_cport_id; __entry->payload_size = payload_size; ), diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 3446fec35895..88d2f01f79d1 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -14,26 +14,30 @@ #include "greybus.h" -static DEFINE_MUTEX(hd_mutex); +static struct ida gb_hd_bus_id_map; -static void free_hd(struct kref *kref) +static void gb_hd_release(struct device *dev) { - struct gb_host_device *hd; - - hd = container_of(kref, struct gb_host_device, kref); + struct gb_host_device *hd = to_gb_host_device(dev); + ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id); ida_destroy(&hd->cport_id_map); kfree(hd); - mutex_unlock(&hd_mutex); } +struct device_type greybus_hd_type = { + .name = "greybus_host_device", + .release = gb_hd_release, +}; + struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, struct device *parent, size_t buffer_size_max, size_t num_cports) { struct gb_host_device *hd; + int ret; /* * Validate that the driver implements all of the callbacks @@ -68,8 +72,21 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, if (!hd) return ERR_PTR(-ENOMEM); - kref_init(&hd->kref); - hd->parent = parent; + hd->dev.parent = parent; + hd->dev.bus = &greybus_bus_type; + hd->dev.type = &greybus_hd_type; + hd->dev.dma_mask = hd->dev.parent->dma_mask; + device_initialize(&hd->dev); + + ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL); + if (ret < 0) { + kfree(hd); + return ERR_PTR(ret); + } + + hd->bus_id = ret; + dev_set_name(&hd->dev, "greybus%d", hd->bus_id); + hd->driver = driver; INIT_LIST_HEAD(&hd->interfaces); INIT_LIST_HEAD(&hd->connections); @@ -83,6 +100,12 @@ EXPORT_SYMBOL_GPL(gb_hd_create); int gb_hd_add(struct gb_host_device *hd) { + int ret; + + ret = device_add(&hd->dev); + if (ret) + return ret; + /* * Initialize AP's SVC protocol connection: * @@ -93,8 +116,10 @@ int gb_hd_add(struct gb_host_device *hd) * time we will create a fully initialized svc-connection, as we need * endo-id and AP's interface id for that. */ - if (!gb_ap_svc_connection_create(hd)) + if (!gb_ap_svc_connection_create(hd)) { + device_del(&hd->dev); return -ENOMEM; + } return 0; } @@ -113,11 +138,25 @@ void gb_hd_del(struct gb_host_device *hd) /* Is the SVC still using the partially uninitialized connection ? */ if (hd->initial_svc_connection) gb_connection_destroy(hd->initial_svc_connection); + + device_del(&hd->dev); } EXPORT_SYMBOL_GPL(gb_hd_del); void gb_hd_put(struct gb_host_device *hd) { - kref_put_mutex(&hd->kref, free_hd, &hd_mutex); + put_device(&hd->dev); } EXPORT_SYMBOL_GPL(gb_hd_put); + +int __init gb_hd_init(void) +{ + ida_init(&gb_hd_bus_id_map); + + return 0; +} + +void gb_hd_exit(void) +{ + ida_destroy(&gb_hd_bus_id_map); +} diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 6724cfea1e19..6bc9ce3df672 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -26,8 +26,8 @@ struct gb_hd_driver { }; struct gb_host_device { - struct kref kref; - struct device *parent; + struct device dev; + int bus_id; const struct gb_hd_driver *driver; struct list_head interfaces; @@ -47,6 +47,7 @@ struct gb_host_device { /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); }; +#define to_gb_host_device(d) container_of(d, struct gb_host_device, d) struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, struct device *parent, @@ -56,4 +57,7 @@ int gb_hd_add(struct gb_host_device *hd); void gb_hd_del(struct gb_host_device *hd); void gb_hd_put(struct gb_host_device *hd); +int gb_hd_init(void); +void gb_hd_exit(void); + #endif /* __HD_H */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index b5d9046ead44..4c99e3e9b0f5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -149,7 +149,7 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, intf->dev.bus = &greybus_bus_type; intf->dev.type = &greybus_interface_type; intf->dev.groups = interface_groups; - intf->dev.dma_mask = hd->parent->dma_mask; + intf->dev.dma_mask = hd->dev.dma_mask; device_initialize(&intf->dev); dev_set_name(&intf->dev, "%s:%d", dev_name(&module->dev), interface_id); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 2e8c444d8b16..7425c4980c38 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -46,7 +46,7 @@ gb_ap_svc_connection_create(struct gb_host_device *hd) { struct gb_connection *connection; - connection = gb_connection_create_range(hd, NULL, hd->parent, + connection = gb_connection_create_range(hd, NULL, &hd->dev, GB_SVC_CPORT_ID, GREYBUS_PROTOCOL_SVC, GB_SVC_CPORT_ID, -- cgit v1.2.3-59-g8ed1b From 582b3a13920c71ca6e546598d344dc6027168e64 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:03 +0100 Subject: greybus: connection: drop parent parameter from connection create The parent parameter was only used for debug messages and to name the connection workqueue. Use the host-device device for this instead. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 ++++---- drivers/staging/greybus/connection.h | 2 +- drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/svc.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c076171cba7a..cca52934ce37 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -106,7 +106,7 @@ int svc_update_connection(struct gb_interface *intf, */ struct gb_connection * gb_connection_create_range(struct gb_host_device *hd, - struct gb_bundle *bundle, struct device *parent, + struct gb_bundle *bundle, u16 cport_id, u8 protocol_id, u32 ida_start, u32 ida_end) { @@ -152,7 +152,7 @@ gb_connection_create_range(struct gb_host_device *hd, INIT_LIST_HEAD(&connection->operations); connection->wq = alloc_workqueue("%s:%d", WQ_UNBOUND, 1, - dev_name(parent), cport_id); + dev_name(&hd->dev), hd_cport_id); if (!connection->wq) goto err_free_connection; @@ -170,7 +170,7 @@ gb_connection_create_range(struct gb_host_device *hd, retval = gb_connection_bind_protocol(connection); if (retval) { - dev_err(parent, "%d: failed to bind protocol: %d\n", + dev_err(&hd->dev, "%d: failed to bind protocol: %d\n", cport_id, retval); gb_connection_destroy(connection); return NULL; @@ -218,7 +218,7 @@ struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id) { return gb_connection_create_range(bundle->intf->hd, bundle, - &bundle->dev, cport_id, protocol_id, + cport_id, protocol_id, 0, bundle->intf->hd->num_cports); } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 6163082309c6..44ecfbed1d0d 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -54,7 +54,7 @@ int svc_update_connection(struct gb_interface *intf, struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, u8 protocol_id); struct gb_connection *gb_connection_create_range(struct gb_host_device *hd, - struct gb_bundle *bundle, struct device *parent, + struct gb_bundle *bundle, u16 cport_id, u8 protocol_id, u32 ida_start, u32 ida_end); void gb_connection_destroy(struct gb_connection *connection); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 4c99e3e9b0f5..1b13d3791b28 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -101,7 +101,7 @@ int gb_create_bundle_connection(struct gb_interface *intf, u8 class) if (!bundle) return -EINVAL; - if (!gb_connection_create_range(bundle->intf->hd, bundle, &bundle->dev, + if (!gb_connection_create_range(bundle->intf->hd, bundle, cport_id, protocol_id, ida_start, ida_end)) return -EINVAL; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 7425c4980c38..79941583858b 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -46,7 +46,7 @@ gb_ap_svc_connection_create(struct gb_host_device *hd) { struct gb_connection *connection; - connection = gb_connection_create_range(hd, NULL, &hd->dev, + connection = gb_connection_create_range(hd, NULL, GB_SVC_CPORT_ID, GREYBUS_PROTOCOL_SVC, GB_SVC_CPORT_ID, -- cgit v1.2.3-59-g8ed1b From 8b0df4b2eaf2f741ae44e8f5bad4ecb223f87932 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:04 +0100 Subject: greybus: interface: make interfaces children of host devices Make interfaces child devices of host devices. The new interface device name is "-", where bus_id is the dynamically allocated bus id for the host device and intf_id is the svc-allocated interface id. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 17 +++-------------- drivers/staging/greybus/interface.h | 1 - drivers/staging/greybus/loopback.c | 6 ------ 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 1b13d3791b28..5060924f167d 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -124,20 +124,14 @@ int gb_create_bundle_connection(struct gb_interface *intf, u8 class) struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id) { - struct gb_module *module; struct gb_interface *intf; int retval; - module = gb_module_find(hd, endo_get_module_id(hd->endo, interface_id)); - if (!module) - return NULL; - intf = kzalloc(sizeof(*intf), GFP_KERNEL); if (!intf) - goto put_module; + return NULL; intf->hd = hd; /* XXX refcount? */ - intf->module = module; intf->interface_id = interface_id; INIT_LIST_HEAD(&intf->bundles); INIT_LIST_HEAD(&intf->manifest_descs); @@ -145,13 +139,13 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, /* Invalid device id to start with */ intf->device_id = GB_DEVICE_ID_BAD; - intf->dev.parent = &module->dev; + intf->dev.parent = &hd->dev; intf->dev.bus = &greybus_bus_type; intf->dev.type = &greybus_interface_type; intf->dev.groups = interface_groups; intf->dev.dma_mask = hd->dev.dma_mask; device_initialize(&intf->dev); - dev_set_name(&intf->dev, "%s:%d", dev_name(&module->dev), interface_id); + dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id); retval = device_add(&intf->dev); if (retval) { @@ -167,8 +161,6 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, free_intf: put_device(&intf->dev); -put_module: - put_device(&module->dev); return NULL; } @@ -177,7 +169,6 @@ put_module: */ void gb_interface_remove(struct gb_interface *intf) { - struct gb_module *module; struct gb_bundle *bundle; struct gb_bundle *next; @@ -191,9 +182,7 @@ void gb_interface_remove(struct gb_interface *intf) list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); - module = intf->module; device_unregister(&intf->dev); - put_device(&module->dev); } void gb_interfaces_remove(struct gb_host_device *hd) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 71493cd96ec7..1202062a70eb 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -31,7 +31,6 @@ struct gb_interface { u32 vendor_id; u32 product_id; - struct gb_module *module; struct gb_host_device *hd; /* The interface needs to boot over unipro */ diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 0c38414a3e06..ededfef40e9c 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -878,12 +878,6 @@ static int gb_loopback_bus_id_compare(void *priv, struct list_head *lha, struct gb_connection *ca = a->connection; struct gb_connection *cb = b->connection; - if (ca->bundle->intf->module->module_id < - cb->bundle->intf->module->module_id) - return -1; - if (cb->bundle->intf->module->module_id < - ca->bundle->intf->module->module_id) - return 1; if (ca->bundle->intf->interface_id < cb->bundle->intf->interface_id) return -1; if (cb->bundle->intf->interface_id < ca->bundle->intf->interface_id) -- cgit v1.2.3-59-g8ed1b From f0172c7043d772ed0d198046cb6142dd91ab2a5a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:05 +0100 Subject: greybus: bundle: rename bundle devices Rename bundle devices so that the new device names become "-.", where bus_id is the dynamically allocated host-device bus id and intf_id the svc-allocated interface id. Using a period (.) rather than a colon (:) makes dev-messages easier to read as as those already add a colon after the device name, for example: greybus 1-4.15: failed to connect cport: -22 Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index f746438bc962..3df7d5f915f9 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -148,7 +148,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, bundle->dev.type = &greybus_bundle_type; bundle->dev.groups = bundle_groups; device_initialize(&bundle->dev); - dev_set_name(&bundle->dev, "%s:%d", dev_name(&intf->dev), bundle_id); + dev_set_name(&bundle->dev, "%s.%d", dev_name(&intf->dev), bundle_id); retval = device_add(&bundle->dev); if (retval) { -- cgit v1.2.3-59-g8ed1b From efe6ef76ba97f20302d200368d9f99848350bae1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:06 +0100 Subject: greybus: svc: make svc a device Make the svc object a child device of the host-device device and use driver core to manage its lifetime. The svc device name is "-svc", where bus_id is the dynamically assigned id of the host device. Note that there is exactly one svc-device per host device (bus). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 79941583858b..8f4efb340f96 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -23,6 +23,8 @@ enum gb_svc_state { }; struct gb_svc { + struct device dev; + struct gb_connection *connection; enum gb_svc_state state; struct ida device_id_map; @@ -667,19 +669,42 @@ static int gb_svc_request_recv(u8 type, struct gb_operation *op) } } +static void gb_svc_release(struct device *dev) +{ + struct gb_svc *svc = container_of(dev, struct gb_svc, dev); + + ida_destroy(&svc->device_id_map); + kfree(svc); +} + +struct device_type greybus_svc_type = { + .name = "greybus_svc", + .release = gb_svc_release, +}; + static int gb_svc_connection_init(struct gb_connection *connection) { + struct gb_host_device *hd = connection->hd; struct gb_svc *svc; svc = kzalloc(sizeof(*svc), GFP_KERNEL); if (!svc) return -ENOMEM; - connection->hd->svc = svc; + svc->dev.parent = &hd->dev; + svc->dev.bus = &greybus_bus_type; + svc->dev.type = &greybus_svc_type; + svc->dev.dma_mask = svc->dev.parent->dma_mask; + device_initialize(&svc->dev); + + dev_set_name(&svc->dev, "%d-svc", hd->bus_id); + svc->state = GB_SVC_STATE_RESET; svc->connection = connection; connection->private = svc; + hd->svc = svc; + WARN_ON(connection->hd->initial_svc_connection); connection->hd->initial_svc_connection = connection; @@ -692,10 +717,10 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; - ida_destroy(&svc->device_id_map); connection->hd->svc = NULL; connection->private = NULL; - kfree(svc); + + put_device(&svc->dev); } static struct gb_protocol svc_protocol = { -- cgit v1.2.3-59-g8ed1b From 6106e51b742b32954351e6020cdb789bc33bf120 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:07 +0100 Subject: greybus: svc: clean up svc initialisation Make sure to initialise the svc device fully before adding it to the host device. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 8f4efb340f96..5aa21f7c591d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -699,6 +699,7 @@ static int gb_svc_connection_init(struct gb_connection *connection) dev_set_name(&svc->dev, "%d-svc", hd->bus_id); + ida_init(&svc->device_id_map); svc->state = GB_SVC_STATE_RESET; svc->connection = connection; connection->private = svc; @@ -708,8 +709,6 @@ static int gb_svc_connection_init(struct gb_connection *connection) WARN_ON(connection->hd->initial_svc_connection); connection->hd->initial_svc_connection = connection; - ida_init(&svc->device_id_map); - return 0; } -- cgit v1.2.3-59-g8ed1b From 88f7b96da75f816ba022190d6076720562cac050 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:08 +0100 Subject: greybus: svc: register svc device at hello Register the svc device upon reception of the HELLO request. The SVC HELLO request contains the endo id and AP interface id, which will be exposed from the svc device rather than the endo. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 3 +++ drivers/staging/greybus/greybus.h | 6 ++++++ drivers/staging/greybus/svc.c | 25 +++++++++++-------------- drivers/staging/greybus/svc.h | 15 ++++++++++++++- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 8c8ba02b76e9..d1e26c4a26fc 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -81,6 +81,7 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) struct gb_module *module = NULL; struct gb_interface *intf = NULL; struct gb_bundle *bundle = NULL; + struct gb_svc *svc = NULL; if (is_gb_endo(dev)) { /* @@ -99,6 +100,8 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); intf = bundle->intf; + } else if (is_gb_svc(dev)) { + svc = to_gb_svc(dev); } else { dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n"); return -EINVAL; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 8ef3a0426a09..fa81733282d6 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -109,6 +109,7 @@ extern struct device_type greybus_endo_type; extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_bundle_type; +extern struct device_type greybus_svc_type; static inline int is_gb_host_device(const struct device *dev) { @@ -135,6 +136,11 @@ static inline int is_gb_bundle(const struct device *dev) return dev->type == &greybus_bundle_type; } +static inline int is_gb_svc(const struct device *dev) +{ + return dev->type == &greybus_svc_type; +} + static inline bool cport_id_valid(struct gb_host_device *hd, u16 cport_id) { return cport_id != CPORT_ID_BAD && cport_id < hd->num_cports; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5aa21f7c591d..fab5c45c0701 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -16,19 +16,6 @@ #define CPORT_FLAGS_CSD_N BIT(1) #define CPORT_FLAGS_CSV_N BIT(2) -enum gb_svc_state { - GB_SVC_STATE_RESET, - GB_SVC_STATE_PROTOCOL_VERSION, - GB_SVC_STATE_SVC_HELLO, -}; - -struct gb_svc { - struct device dev; - - struct gb_connection *connection; - enum gb_svc_state state; - struct ida device_id_map; -}; struct svc_hotplug { struct work_struct work; @@ -348,6 +335,7 @@ static int gb_svc_version_request(struct gb_operation *op) static int gb_svc_hello(struct gb_operation *op) { struct gb_connection *connection = op->connection; + struct gb_svc *svc = connection->private; struct gb_host_device *hd = connection->hd; struct gb_svc_hello_request *hello_request; struct gb_interface *intf; @@ -370,6 +358,12 @@ static int gb_svc_hello(struct gb_operation *op) endo_id = le16_to_cpu(hello_request->endo_id); interface_id = hello_request->interface_id; + ret = device_add(&svc->dev); + if (ret) { + dev_err(&svc->dev, "failed to register svc device: %d\n", ret); + return ret; + } + /* Setup Endo */ ret = greybus_endo_setup(hd, endo_id, interface_id); if (ret) @@ -671,7 +665,7 @@ static int gb_svc_request_recv(u8 type, struct gb_operation *op) static void gb_svc_release(struct device *dev) { - struct gb_svc *svc = container_of(dev, struct gb_svc, dev); + struct gb_svc *svc = to_gb_svc(dev); ida_destroy(&svc->device_id_map); kfree(svc); @@ -716,6 +710,9 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; + if (device_is_registered(&svc->dev)) + device_del(&svc->dev); + connection->hd->svc = NULL; connection->private = NULL; diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index d2f406fa798a..c9d8866f8f29 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -10,7 +10,20 @@ #ifndef __SVC_H #define __SVC_H -struct gb_svc; +enum gb_svc_state { + GB_SVC_STATE_RESET, + GB_SVC_STATE_PROTOCOL_VERSION, + GB_SVC_STATE_SVC_HELLO, +}; + +struct gb_svc { + struct device dev; + + struct gb_connection *connection; + enum gb_svc_state state; + struct ida device_id_map; +}; +#define to_gb_svc(d) container_of(d, struct gb_svc, d) int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, -- cgit v1.2.3-59-g8ed1b From 66069fb06ce42c1deca543d0620a69508b92ec31 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:09 +0100 Subject: greybus: svc: move endo id and ap interface id to svc Move endo_id and AP interface id to the svc device. The endo abstraction is about to be removed, and these attributes are arguable attributes of the svc anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ++-- drivers/staging/greybus/endo.c | 31 -------------------------- drivers/staging/greybus/svc.c | 43 +++++++++++++++++++++++++++--------- drivers/staging/greybus/svc.h | 3 +++ 4 files changed, 38 insertions(+), 43 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index cca52934ce37..51fc84e76a48 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -268,7 +268,7 @@ gb_connection_svc_connection_create(struct gb_connection *connection) intf = connection->bundle->intf; ret = gb_svc_connection_create(hd->svc, - hd->endo->ap_intf_id, + hd->svc->ap_intf_id, connection->hd_cport_id, intf->interface_id, connection->intf_cport_id, @@ -289,7 +289,7 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection) return; gb_svc_connection_destroy(connection->hd->svc, - connection->hd->endo->ap_intf_id, + connection->hd->svc->ap_intf_id, connection->hd_cport_id, connection->bundle->intf->interface_id, connection->intf_cport_id); diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c index 6fb22fb8e85b..cca2a83449c6 100644 --- a/drivers/staging/greybus/endo.c +++ b/drivers/staging/greybus/endo.c @@ -75,36 +75,7 @@ static const struct attribute_group svc_group = { .name = "svc", }; -static ssize_t id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gb_endo *endo = to_gb_endo(dev); - - return sprintf(buf, "0x%04x\n", endo->id); -} -static DEVICE_ATTR_RO(id); - -static ssize_t ap_intf_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gb_endo *endo = to_gb_endo(dev); - - return sprintf(buf, "%u\n", endo->ap_intf_id); -} -static DEVICE_ATTR_RO(ap_intf_id); - -static struct attribute *endo_attrs[] = { - &dev_attr_id.attr, - &dev_attr_ap_intf_id.attr, - NULL, -}; - -static const struct attribute_group endo_group = { - .attrs = endo_attrs, -}; - static const struct attribute_group *endo_groups[] = { - &endo_group, &svc_group, NULL, }; @@ -490,8 +461,6 @@ struct gb_endo *gb_endo_create(struct gb_host_device *hd, u16 endo_id, retval = -EINVAL; goto free_endo; } - endo->id = endo_id; - endo->ap_intf_id = ap_intf_id; /* Register Endo device */ retval = gb_endo_register(hd, endo); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index fab5c45c0701..c71f41d0ef62 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -24,6 +24,31 @@ struct svc_hotplug { }; +static ssize_t endo_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_svc *svc = to_gb_svc(dev); + + return sprintf(buf, "0x%04x\n", svc->endo_id); +} +static DEVICE_ATTR_RO(endo_id); + +static ssize_t ap_intf_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_svc *svc = to_gb_svc(dev); + + return sprintf(buf, "%u\n", svc->ap_intf_id); +} +static DEVICE_ATTR_RO(ap_intf_id); + +static struct attribute *svc_attrs[] = { + &dev_attr_endo_id.attr, + &dev_attr_ap_intf_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(svc); + /* * AP's SVC cport is required early to get messages from the SVC. This happens * even before the Endo is created and hence any modules or interfaces. @@ -339,8 +364,6 @@ static int gb_svc_hello(struct gb_operation *op) struct gb_host_device *hd = connection->hd; struct gb_svc_hello_request *hello_request; struct gb_interface *intf; - u16 endo_id; - u8 interface_id; int ret; /* @@ -355,8 +378,8 @@ static int gb_svc_hello(struct gb_operation *op) } hello_request = op->request->payload; - endo_id = le16_to_cpu(hello_request->endo_id); - interface_id = hello_request->interface_id; + svc->endo_id = le16_to_cpu(hello_request->endo_id); + svc->ap_intf_id = hello_request->interface_id; ret = device_add(&svc->dev); if (ret) { @@ -365,7 +388,7 @@ static int gb_svc_hello(struct gb_operation *op) } /* Setup Endo */ - ret = greybus_endo_setup(hd, endo_id, interface_id); + ret = greybus_endo_setup(hd, svc->endo_id, svc->ap_intf_id); if (ret) return ret; @@ -373,7 +396,7 @@ static int gb_svc_hello(struct gb_operation *op) * Endo and its modules are ready now, fix AP's partially initialized * svc protocol and its connection. */ - intf = gb_ap_interface_create(hd, connection, interface_id); + intf = gb_ap_interface_create(hd, connection, svc->ap_intf_id); if (!intf) { gb_endo_remove(hd->endo); return ret; @@ -385,7 +408,6 @@ static int gb_svc_hello(struct gb_operation *op) static void svc_intf_remove(struct gb_connection *connection, struct gb_interface *intf) { - struct gb_host_device *hd = connection->hd; struct gb_svc *svc = connection->private; u8 intf_id = intf->interface_id; u8 device_id; @@ -396,7 +418,7 @@ static void svc_intf_remove(struct gb_connection *connection, /* * Destroy the two-way route between the AP and the interface. */ - gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id); + gb_svc_route_destroy(svc, svc->ap_intf_id, intf_id); ida_simple_remove(&svc->device_id_map, device_id); } @@ -486,7 +508,7 @@ static void svc_process_hotplug(struct work_struct *work) /* * Create a two-way route between the AP and the new interface */ - ret = gb_svc_route_create(svc, hd->endo->ap_intf_id, GB_DEVICE_ID_AP, + ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_DEVICE_ID_AP, intf_id, device_id); if (ret) { pr_err("%d: Route create operation failed, interface %hhu device_id %hhu (%d)\n", @@ -504,7 +526,7 @@ static void svc_process_hotplug(struct work_struct *work) goto free_svc_hotplug; destroy_route: - gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id); + gb_svc_route_destroy(svc, svc->ap_intf_id, intf_id); svc_id_free: /* * XXX Should we tell SVC that this id doesn't belong to interface @@ -688,6 +710,7 @@ static int gb_svc_connection_init(struct gb_connection *connection) svc->dev.parent = &hd->dev; svc->dev.bus = &greybus_bus_type; svc->dev.type = &greybus_svc_type; + svc->dev.groups = svc_groups; svc->dev.dma_mask = svc->dev.parent->dma_mask; device_initialize(&svc->dev); diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index c9d8866f8f29..e05785f499f4 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -22,6 +22,9 @@ struct gb_svc { struct gb_connection *connection; enum gb_svc_state state; struct ida device_id_map; + + u16 endo_id; + u8 ap_intf_id; }; #define to_gb_svc(d) container_of(d, struct gb_svc, d) -- cgit v1.2.3-59-g8ed1b From e4048006fd3823760d85057549bf609d3ed2e4c8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:10 +0100 Subject: greybus: interface: kill gb_create_bundle_connection Kill gb_create_bundle_connection, which was only used to create the control bundle and connection, and replace it with a specialised static helper. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 51 +++++++++++++------------------------ drivers/staging/greybus/interface.h | 1 - 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 5060924f167d..c4ec25607230 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -71,40 +71,27 @@ struct device_type greybus_interface_type = { /* * Create kernel structures corresponding to a bundle and connection for - * managing control/svc CPort. + * managing control CPort. */ -int gb_create_bundle_connection(struct gb_interface *intf, u8 class) +static int +gb_interface_create_control_bundle_connection(struct gb_interface *intf) { struct gb_bundle *bundle; - u32 ida_start, ida_end; - u8 bundle_id, protocol_id; - u16 cport_id; - - if (class == GREYBUS_CLASS_CONTROL) { - protocol_id = GREYBUS_PROTOCOL_CONTROL; - bundle_id = GB_CONTROL_BUNDLE_ID; - cport_id = GB_CONTROL_CPORT_ID; - ida_start = 0; - ida_end = intf->hd->num_cports - 1; - } else if (class == GREYBUS_CLASS_SVC) { - protocol_id = GREYBUS_PROTOCOL_SVC; - bundle_id = GB_SVC_BUNDLE_ID; - cport_id = GB_SVC_CPORT_ID; - ida_start = GB_SVC_CPORT_ID; - ida_end = GB_SVC_CPORT_ID + 1; - } else { - WARN_ON(1); - return -EINVAL; - } + struct gb_connection *connection; - bundle = gb_bundle_create(intf, bundle_id, class); - if (!bundle) - return -EINVAL; + bundle = gb_bundle_create(intf, GB_CONTROL_BUNDLE_ID, + GREYBUS_CLASS_CONTROL); + if (!bundle) { + dev_err(&intf->dev, "failed to create control bundle\n"); + return -ENOMEM; + } - if (!gb_connection_create_range(bundle->intf->hd, bundle, - cport_id, protocol_id, ida_start, - ida_end)) - return -EINVAL; + connection = gb_connection_create(bundle, GB_CONTROL_CPORT_ID, + GREYBUS_PROTOCOL_CONTROL); + if (!connection) { + dev_err(&intf->dev, "failed to create control connection\n"); + return -ENOMEM; + } return 0; } @@ -208,11 +195,9 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) intf->device_id = device_id; /* Establish control CPort connection */ - ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_CONTROL); - if (ret) { - dev_err(&intf->dev, "Failed to create control CPort connection (%d)\n", ret); + ret = gb_interface_create_control_bundle_connection(intf); + if (ret) return ret; - } /* Get manifest size using control protocol on CPort */ size = gb_control_get_manifest_size_operation(intf); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 1202062a70eb..61e9c05e75c6 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -61,5 +61,4 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id); void gb_interface_remove(struct gb_interface *intf); void gb_interfaces_remove(struct gb_host_device *hd); -int gb_create_bundle_connection(struct gb_interface *intf, u8 class); #endif /* __INTERFACE_H */ -- cgit v1.2.3-59-g8ed1b From 2566fae6a5eb29e52502a38fd816c6e994547625 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:11 +0100 Subject: greybus: connection: clean up connection-creation interface Clean up the connection-creation interface by clearly separating our two types of connections: static and dynamic. Add two convenience functions for creating static and dynamic connections. A static connection is a pre-setup connection that is defined by a host device and a host-device cport id. Specifically, the remote interface or cport id need not be known. The SVC connection is a static connection. A dynamic connection is defined by a host device and a remote interface and cport id. This is our normal connections where the host-device cport is (generally) allocated dynamically. Note that the new generic interface is marked static, but can be exported later to allow dynamic connections to be created also from fixed host-device cports (e.g. for CSI). Also note that a connection of either type is uniquely identified by its host-device and host-device cport id once created. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 64 ++++++++++++++++++++++++++---------- drivers/staging/greybus/connection.h | 13 ++++---- drivers/staging/greybus/interface.c | 3 +- drivers/staging/greybus/manifest.c | 3 +- drivers/staging/greybus/svc.c | 7 ++-- 5 files changed, 59 insertions(+), 31 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 51fc84e76a48..d87a346d1ce3 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -94,25 +94,32 @@ int svc_update_connection(struct gb_interface *intf, } /* - * Set up a Greybus connection, representing the bidirectional link + * gb_connection_create() - create a Greybus connection + * @hd: host device of the connection + * @hd_cport_id: host-device cport id, or -1 for dynamic allocation + * @intf: remote interface, or NULL for static connections + * @bundle: remote-interface bundle (may be NULL) + * @cport_id: remote-interface cport id, or 0 for static connections + * @protocol_id: protocol id + * + * Create a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on - * another Greybus module. + * another Greybus interface. * * A connection also maintains the state of operations sent over the * connection. * - * Returns a pointer to the new connection if successful, or a null - * pointer otherwise. + * Return: A pointer to the new connection if successful, or NULL otherwise. */ -struct gb_connection * -gb_connection_create_range(struct gb_host_device *hd, - struct gb_bundle *bundle, - u16 cport_id, u8 protocol_id, u32 ida_start, - u32 ida_end) +static struct gb_connection * +gb_connection_create(struct gb_host_device *hd, int hd_cport_id, + struct gb_interface *intf, + struct gb_bundle *bundle, int cport_id, + u8 protocol_id) { struct gb_connection *connection; struct ida *id_map = &hd->cport_id_map; - int hd_cport_id; + int ida_start, ida_end; int retval; u8 major = 0; u8 minor = 1; @@ -128,6 +135,17 @@ gb_connection_create_range(struct gb_host_device *hd, return NULL; } + if (hd_cport_id < 0) { + ida_start = 0; + ida_end = hd->num_cports; + } else if (hd_cport_id < hd->num_cports) { + ida_start = hd_cport_id; + ida_end = hd_cport_id + 1; + } else { + dev_err(&hd->dev, "cport %d not available\n", hd_cport_id); + return NULL; + } + hd_cport_id = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); if (hd_cport_id < 0) return NULL; @@ -139,6 +157,7 @@ gb_connection_create_range(struct gb_host_device *hd, connection->hd_cport_id = hd_cport_id; connection->intf_cport_id = cport_id; connection->hd = hd; + connection->intf = intf; connection->protocol_id = protocol_id; connection->major = major; @@ -186,6 +205,23 @@ err_remove_ida: return NULL; } +struct gb_connection * +gb_connection_create_static(struct gb_host_device *hd, + u16 hd_cport_id, u8 protocol_id) +{ + return gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, + protocol_id); +} + +struct gb_connection * +gb_connection_create_dynamic(struct gb_interface *intf, + struct gb_bundle *bundle, + u16 cport_id, u8 protocol_id) +{ + return gb_connection_create(intf->hd, -1, intf, bundle, cport_id, + protocol_id); +} + static int gb_connection_hd_cport_enable(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; @@ -214,14 +250,6 @@ static void gb_connection_hd_cport_disable(struct gb_connection *connection) hd->driver->cport_disable(hd, connection->hd_cport_id); } -struct gb_connection *gb_connection_create(struct gb_bundle *bundle, - u16 cport_id, u8 protocol_id) -{ - return gb_connection_create_range(bundle->intf->hd, bundle, - cport_id, protocol_id, - 0, bundle->intf->hd->num_cports); -} - /* * Cancel all active operations on a connection. * diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 44ecfbed1d0d..c5499fcbfd13 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -23,6 +23,7 @@ enum gb_connection_state { struct gb_connection { struct gb_host_device *hd; + struct gb_interface *intf; struct gb_bundle *bundle; struct kref kref; u16 hd_cport_id; @@ -51,12 +52,12 @@ struct gb_connection { int svc_update_connection(struct gb_interface *intf, struct gb_connection *connection); -struct gb_connection *gb_connection_create(struct gb_bundle *bundle, - u16 cport_id, u8 protocol_id); -struct gb_connection *gb_connection_create_range(struct gb_host_device *hd, - struct gb_bundle *bundle, - u16 cport_id, u8 protocol_id, u32 ida_start, - u32 ida_end); + +struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, + u16 hd_cport_id, u8 protocol_id); +struct gb_connection *gb_connection_create_dynamic(struct gb_interface *intf, + struct gb_bundle *bundle, u16 cport_id, + u8 protocol_id); void gb_connection_destroy(struct gb_connection *connection); void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c4ec25607230..241518711323 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -86,7 +86,8 @@ gb_interface_create_control_bundle_connection(struct gb_interface *intf) return -ENOMEM; } - connection = gb_connection_create(bundle, GB_CONTROL_CPORT_ID, + connection = gb_connection_create_dynamic(intf, bundle, + GB_CONTROL_CPORT_ID, GREYBUS_PROTOCOL_CONTROL); if (!connection) { dev_err(&intf->dev, "failed to create control connection\n"); diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 41d51579217f..9252bf46bcc4 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -251,7 +251,8 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) goto print_error_exit; } - if (!gb_connection_create(bundle, cport_id, protocol_id)) + if (!gb_connection_create_dynamic(intf, bundle, cport_id, + protocol_id)) goto exit; release_descriptor: diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index c71f41d0ef62..9dd51122ea4c 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -60,11 +60,8 @@ gb_ap_svc_connection_create(struct gb_host_device *hd) { struct gb_connection *connection; - connection = gb_connection_create_range(hd, NULL, - GB_SVC_CPORT_ID, - GREYBUS_PROTOCOL_SVC, - GB_SVC_CPORT_ID, - GB_SVC_CPORT_ID + 1); + connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, + GREYBUS_PROTOCOL_SVC); return connection; } -- cgit v1.2.3-59-g8ed1b From 0daf17b9e4f74981c7546ad4b4224b8bef85e00e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:12 +0100 Subject: greybus: connection: fix potential null-deref on create Make sure that the interface lookup helper can handle static, bundle-less connections without oopsing when creating further connections. Note that the initial svc-connection has always been bundle-less, but did not trigger an oops as a bundle was created for it before further connections were created. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index d87a346d1ce3..a7b803799fa5 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -20,10 +20,12 @@ gb_connection_intf_find(struct gb_interface *intf, u16 cport_id) struct gb_host_device *hd = intf->hd; struct gb_connection *connection; - list_for_each_entry(connection, &hd->connections, hd_links) - if (connection->bundle->intf == intf && + list_for_each_entry(connection, &hd->connections, hd_links) { + if (connection->intf == intf && connection->intf_cport_id == cport_id) return connection; + } + return NULL; } -- cgit v1.2.3-59-g8ed1b From 4ec1574ae99872a9d1922fb1e57b8d46df4d0920 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:13 +0100 Subject: greybus: connection: kill GB_PROTOCOL_SKIP_SVC_CONNECTION Add helper to determine whether a connection is static, and remove the protocol flag GB_PROTOCOL_SKIP_SVC_CONNECTION. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 5 ++--- drivers/staging/greybus/connection.h | 5 +++++ drivers/staging/greybus/protocol.h | 1 - drivers/staging/greybus/svc.c | 3 +-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index a7b803799fa5..3f2fe0103547 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -289,11 +289,10 @@ static int gb_connection_svc_connection_create(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; - struct gb_protocol *protocol = connection->protocol; struct gb_interface *intf; int ret; - if (protocol->flags & GB_PROTOCOL_SKIP_SVC_CONNECTION) + if (gb_connection_is_static(connection)) return 0; intf = connection->bundle->intf; @@ -315,7 +314,7 @@ gb_connection_svc_connection_create(struct gb_connection *connection) static void gb_connection_svc_connection_destroy(struct gb_connection *connection) { - if (connection->protocol->flags & GB_PROTOCOL_SKIP_SVC_CONNECTION) + if (gb_connection_is_static(connection)) return; gb_svc_connection_destroy(connection->hd->svc, diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index c5499fcbfd13..446ffe157a12 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -60,6 +60,11 @@ struct gb_connection *gb_connection_create_dynamic(struct gb_interface *intf, u8 protocol_id); void gb_connection_destroy(struct gb_connection *connection); +static inline bool gb_connection_is_static(struct gb_connection *connection) +{ + return !connection->intf; +} + void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index b1d122c61c44..384ddf8d1600 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -18,7 +18,6 @@ struct gb_operation; #define GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED BIT(1) /* Don't sent disconnected requests */ #define GB_PROTOCOL_NO_BUNDLE BIT(2) /* Protocol May have a bundle-less connection */ #define GB_PROTOCOL_SKIP_VERSION BIT(3) /* Don't send get_version() requests */ -#define GB_PROTOCOL_SKIP_SVC_CONNECTION BIT(4) /* Don't send SVC connection requests */ typedef int (*gb_connection_init_t)(struct gb_connection *); typedef void (*gb_connection_exit_t)(struct gb_connection *); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 9dd51122ea4c..3beb3a2dfb87 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -750,7 +750,6 @@ static struct gb_protocol svc_protocol = { .flags = GB_PROTOCOL_SKIP_CONTROL_CONNECTED | GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED | GB_PROTOCOL_NO_BUNDLE | - GB_PROTOCOL_SKIP_VERSION | - GB_PROTOCOL_SKIP_SVC_CONNECTION, + GB_PROTOCOL_SKIP_VERSION, }; gb_builtin_protocol_driver(svc_protocol); -- cgit v1.2.3-59-g8ed1b From 729b260a67e051bcabdd83c8a29fc13c5617cd6b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:14 +0100 Subject: greybus: connection: add name field Add a name field to connections that can be used in log messages. A connection always belongs to a host-device (bus) and can be uniquely identified by its host-device cport id, but include remote interface and cport id nonetheless on the following format: /: The remote interface and cport id will be zero for static connections. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 17 +++++++++++++++++ drivers/staging/greybus/connection.h | 1 + 2 files changed, 18 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3f2fe0103547..65d72b81ea8e 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -95,6 +95,21 @@ int svc_update_connection(struct gb_interface *intf, return 0; } +static void gb_connection_init_name(struct gb_connection *connection) +{ + u16 hd_cport_id = connection->hd_cport_id; + u16 cport_id = 0; + u8 intf_id = 0; + + if (connection->intf) { + intf_id = connection->intf->interface_id; + cport_id = connection->intf_cport_id; + } + + snprintf(connection->name, sizeof(connection->name), + "%hu/%hhu:%hu", hd_cport_id, intf_id, cport_id); +} + /* * gb_connection_create() - create a Greybus connection * @hd: host device of the connection @@ -179,6 +194,8 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, kref_init(&connection->kref); + gb_connection_init_name(connection); + spin_lock_irq(&gb_connections_lock); list_add(&connection->hd_links, &hd->connections); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 446ffe157a12..028f278b77e0 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -43,6 +43,7 @@ struct gb_connection { enum gb_connection_state state; struct list_head operations; + char name[16]; struct workqueue_struct *wq; atomic_t op_cycle; -- cgit v1.2.3-59-g8ed1b From 4c4b50218656d972295b2ecabd3aacb674e2fe11 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:15 +0100 Subject: greybus: connection: handle static connections Use host-device device and connection name for log messages rather than assume that all connections have a bundle (e.g. not true for static connections). Note that the "initial" svc connection has never had a bundle. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 65d72b81ea8e..0ac3a8e5e486 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -208,8 +208,8 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, retval = gb_connection_bind_protocol(connection); if (retval) { - dev_err(&hd->dev, "%d: failed to bind protocol: %d\n", - cport_id, retval); + dev_err(&hd->dev, "%s: failed to bind protocol: %d\n", + connection->name, retval); gb_connection_destroy(connection); return NULL; } @@ -320,8 +320,9 @@ gb_connection_svc_connection_create(struct gb_connection *connection) connection->intf_cport_id, intf->boot_over_unipro); if (ret) { - dev_err(&connection->bundle->dev, - "failed to create svc connection: %d\n", ret); + dev_err(&connection->hd->dev, + "%s: failed to create svc connection: %d\n", + connection->name, ret); return ret; } @@ -513,8 +514,9 @@ void gb_connection_latency_tag_enable(struct gb_connection *connection) ret = hd->driver->latency_tag_enable(hd, connection->hd_cport_id); if (ret) { - dev_err(&connection->bundle->dev, - "failed to enable latency tag: %d\n", ret); + dev_err(&connection->hd->dev, + "%s: failed to enable latency tag: %d\n", + connection->name, ret); } } EXPORT_SYMBOL_GPL(gb_connection_latency_tag_enable); @@ -529,8 +531,9 @@ void gb_connection_latency_tag_disable(struct gb_connection *connection) ret = hd->driver->latency_tag_disable(hd, connection->hd_cport_id); if (ret) { - dev_err(&connection->bundle->dev, - "failed to disable latency tag: %d\n", ret); + dev_err(&connection->hd->dev, + "%s: failed to disable latency tag: %d\n", + connection->name, ret); } } EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable); -- cgit v1.2.3-59-g8ed1b From 25cdd7aa5e67c09d65a5a2a5da5fc01ebf6483fe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:16 +0100 Subject: greybus: operation: handle static connections Use the host-device device and connection name for error messages, as the operation code can not assume that a connection has a bundle. Note that the "initial" svc connection has never had a bundle. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 59 ++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 16438803fce2..01ad08bc85d0 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -217,7 +217,8 @@ static void gb_message_cancel(struct gb_message *message) static void gb_operation_request_handle(struct gb_operation *operation) { - struct gb_protocol *protocol = operation->connection->protocol; + struct gb_connection *connection = operation->connection; + struct gb_protocol *protocol = connection->protocol; int status; int ret; @@ -227,18 +228,18 @@ static void gb_operation_request_handle(struct gb_operation *operation) if (protocol->request_recv) { status = protocol->request_recv(operation->type, operation); } else { - dev_err(&operation->connection->bundle->dev, - "unexpected incoming request type 0x%02hhx\n", - operation->type); + dev_err(&connection->hd->dev, + "%s: unexpected incoming request of type 0x%02hhx\n", + connection->name, operation->type); status = -EPROTONOSUPPORT; } ret = gb_operation_response_send(operation, status); if (ret) { - dev_err(&operation->connection->bundle->dev, - "failed to send response %d for type 0x%02hhx: %d\n", - status, operation->type, ret); + dev_err(&connection->hd->dev, + "%s: failed to send response %d for type 0x%02hhx: %d\n", + connection->name, status, operation->type, ret); return; } } @@ -743,7 +744,7 @@ static int gb_operation_response_send(struct gb_operation *operation, /* Record the result */ if (!gb_operation_result_set(operation, errno)) { - dev_err(&connection->bundle->dev, "request result already set\n"); + dev_err(&connection->hd->dev, "request result already set\n"); return -EIO; /* Shouldn't happen */ } @@ -795,9 +796,9 @@ void greybus_message_sent(struct gb_host_device *hd, */ if (message == operation->response) { if (status) { - dev_err(&connection->bundle->dev, - "error sending response type 0x%02hhx: %d\n", - operation->type, status); + dev_err(&connection->hd->dev, + "%s: error sending response 0x%02hhx: %d\n", + connection->name, operation->type, status); } gb_operation_put_active(operation); gb_operation_put(operation); @@ -827,8 +828,9 @@ static void gb_connection_recv_request(struct gb_connection *connection, operation = gb_operation_create_incoming(connection, operation_id, type, data, size); if (!operation) { - dev_err(&connection->bundle->dev, - "can't create incoming operation\n"); + dev_err(&connection->hd->dev, + "%s: can't create incoming operation\n", + connection->name); return; } @@ -865,17 +867,19 @@ static void gb_connection_recv_response(struct gb_connection *connection, operation = gb_operation_find_outgoing(connection, operation_id); if (!operation) { - dev_err(&connection->bundle->dev, - "unexpected response 0x%04hx received\n", operation_id); + dev_err(&connection->hd->dev, + "%s: unexpected response id 0x%04hx received\n", + connection->name, operation_id); return; } message = operation->response; message_size = sizeof(*message->header) + message->payload_size; if (!errno && size != message_size) { - dev_err(&connection->bundle->dev, - "malformed response of type 0x%02hhx received (%zu != %zu)\n", - message->header->type, size, message_size); + dev_err(&connection->hd->dev, + "%s: malformed response 0x%02hhx received (%zu != %zu)\n", + connection->name, message->header->type, size, + message_size); errno = -EMSGSIZE; } trace_gb_message_recv_response(operation->response); @@ -902,17 +906,18 @@ void gb_connection_recv(struct gb_connection *connection, void *data, size_t size) { struct gb_operation_msg_hdr header; - struct device *dev = &connection->bundle->dev; + struct device *dev = &connection->hd->dev; size_t msg_size; u16 operation_id; if (connection->state != GB_CONNECTION_STATE_ENABLED) { - dev_warn(dev, "dropping %zu received bytes\n", size); + dev_warn(dev, "%s: dropping %zu received bytes\n", + connection->name, size); return; } if (size < sizeof(header)) { - dev_err(dev, "short message received\n"); + dev_err(dev, "%s: short message received\n", connection->name); return; } @@ -921,9 +926,9 @@ void gb_connection_recv(struct gb_connection *connection, msg_size = le16_to_cpu(header.size); if (size < msg_size) { dev_err(dev, - "incomplete message 0x%04hx of type 0x%02hhx received (%zu < %zu)\n", - le16_to_cpu(header.operation_id), header.type, size, - msg_size); + "%s: incomplete message 0x%04hx of type 0x%02hhx received (%zu < %zu)\n", + connection->name, le16_to_cpu(header.operation_id), + header.type, size, msg_size); return; /* XXX Should still complete operation */ } @@ -1030,9 +1035,9 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { - dev_err(&connection->bundle->dev, - "synchronous operation of type 0x%02hhx failed: %d\n", - type, ret); + dev_err(&connection->hd->dev, + "%s: synchronous operation of type 0x%02hhx failed: %d\n", + connection->name, type, ret); } else { if (response_size) { memcpy(response, operation->response->payload, -- cgit v1.2.3-59-g8ed1b From 87757e325ab8b5b9e0e90e37de77778a631e6c8c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:17 +0100 Subject: greybus: protocol: handle static connections Use host-device device and connection name for log messages, as not all connections have a bundle. Note that the "initial" svc connection has never had a bundle. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/protocol.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 1790d1ac8513..47b747990902 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -165,9 +165,9 @@ int gb_protocol_get_version(struct gb_connection *connection) return retval; if (response.major > connection->protocol->major) { - dev_err(&connection->bundle->dev, - "%d: unsupported major version (%hhu > %hhu)\n", - connection->intf_cport_id, response.major, + dev_err(&connection->hd->dev, + "%s: unsupported major version (%hhu > %hhu)\n", + connection->name, response.major, connection->protocol->major); return -ENOTSUPP; } @@ -175,8 +175,8 @@ int gb_protocol_get_version(struct gb_connection *connection) connection->module_major = response.major; connection->module_minor = response.minor; - dev_dbg(&connection->bundle->dev, - "%d: %s (0x%02hhx) v%hhu.%hhu\n", connection->intf_cport_id, + dev_dbg(&connection->hd->dev, + "%s: %s (0x%02hhx) v%hhu.%hhu\n", connection->name, protocol->name, protocol->id, response.major, response.minor); return 0; -- cgit v1.2.3-59-g8ed1b From 5ef323846ff7d1c32e4fb2441dfc79b10d6092b3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:18 +0100 Subject: greybus: hd: fix svc-connection handling Create the svc connection when registering the host-device and remove the current svc connection hacks that "upgraded" the svc connection once the endo id and ap interface id was known. Note that the old implementation was partly based on a misunderstanding as it was the remote interface id, rather than the local AP interface id, that used to define a connection (but we also needed the endo_id). The remote interface is no longer needed as static connections, such as the svc connection, are now simply defined by the host-device and host cport id. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 18 ----------- drivers/staging/greybus/connection.h | 3 -- drivers/staging/greybus/hd.c | 31 ++++++++++--------- drivers/staging/greybus/hd.h | 2 +- drivers/staging/greybus/svc.c | 59 ------------------------------------ drivers/staging/greybus/svc.h | 2 -- 6 files changed, 17 insertions(+), 98 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 0ac3a8e5e486..7beab7468a0d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -77,24 +77,6 @@ static void gb_connection_kref_release(struct kref *kref) mutex_unlock(&connection_mutex); } -int svc_update_connection(struct gb_interface *intf, - struct gb_connection *connection) -{ - struct gb_bundle *bundle; - - bundle = gb_bundle_create(intf, GB_SVC_BUNDLE_ID, GREYBUS_CLASS_SVC); - if (!bundle) - return -EINVAL; - - connection->bundle = bundle; - - spin_lock_irq(&gb_connections_lock); - list_add(&connection->bundle_links, &bundle->connections); - spin_unlock_irq(&gb_connections_lock); - - return 0; -} - static void gb_connection_init_name(struct gb_connection *connection) { u16 hd_cport_id = connection->hd_cport_id; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 028f278b77e0..800626234b32 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -51,9 +51,6 @@ struct gb_connection { void *private; }; -int svc_update_connection(struct gb_interface *intf, - struct gb_connection *connection); - struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id, u8 protocol_id); struct gb_connection *gb_connection_create_dynamic(struct gb_interface *intf, diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 88d2f01f79d1..2ee5d4ff57fc 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -98,6 +98,18 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, } EXPORT_SYMBOL_GPL(gb_hd_create); +static int gb_hd_create_svc_connection(struct gb_host_device *hd) +{ + hd->svc_connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, + GREYBUS_PROTOCOL_SVC); + if (!hd->svc_connection) { + dev_err(&hd->dev, "failed to create svc connection\n"); + return -ENOMEM; + } + + return 0; +} + int gb_hd_add(struct gb_host_device *hd) { int ret; @@ -106,19 +118,10 @@ int gb_hd_add(struct gb_host_device *hd) if (ret) return ret; - /* - * Initialize AP's SVC protocol connection: - * - * This is required as part of early initialization of the host device - * as we need this connection in order to start any kind of message - * exchange between the AP and the SVC. SVC will start with a - * 'get-version' request followed by a 'svc-hello' message and at that - * time we will create a fully initialized svc-connection, as we need - * endo-id and AP's interface id for that. - */ - if (!gb_ap_svc_connection_create(hd)) { + ret = gb_hd_create_svc_connection(hd); + if (ret) { device_del(&hd->dev); - return -ENOMEM; + return ret; } return 0; @@ -135,9 +138,7 @@ void gb_hd_del(struct gb_host_device *hd) gb_interfaces_remove(hd); gb_endo_remove(hd->endo); - /* Is the SVC still using the partially uninitialized connection ? */ - if (hd->initial_svc_connection) - gb_connection_destroy(hd->initial_svc_connection); + gb_connection_destroy(hd->svc_connection); device_del(&hd->dev); } diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 6bc9ce3df672..72716e0e81b1 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -41,8 +41,8 @@ struct gb_host_device { size_t buffer_size_max; struct gb_endo *endo; - struct gb_connection *initial_svc_connection; struct gb_svc *svc; + struct gb_connection *svc_connection; /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 3beb3a2dfb87..11fa8c91bbe3 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -49,51 +49,6 @@ static struct attribute *svc_attrs[] = { }; ATTRIBUTE_GROUPS(svc); -/* - * AP's SVC cport is required early to get messages from the SVC. This happens - * even before the Endo is created and hence any modules or interfaces. - * - * This is a temporary connection, used only at initial bootup. - */ -struct gb_connection * -gb_ap_svc_connection_create(struct gb_host_device *hd) -{ - struct gb_connection *connection; - - connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, - GREYBUS_PROTOCOL_SVC); - - return connection; -} - -/* - * We know endo-type and AP's interface id now, lets create a proper svc - * connection (and its interface/bundle) now and get rid of the initial - * 'partially' initialized one svc connection. - */ -static struct gb_interface * -gb_ap_interface_create(struct gb_host_device *hd, - struct gb_connection *connection, u8 interface_id) -{ - struct gb_interface *intf; - struct device *dev = &hd->endo->dev; - - intf = gb_interface_create(hd, interface_id); - if (!intf) { - dev_err(dev, "%s: Failed to create interface with id %hhu\n", - __func__, interface_id); - return NULL; - } - - intf->device_id = GB_DEVICE_ID_AP; - svc_update_connection(intf, connection); - - /* Its no longer a partially initialized connection */ - hd->initial_svc_connection = NULL; - - return intf; -} - static int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) { struct gb_svc_intf_device_id_request request; @@ -360,7 +315,6 @@ static int gb_svc_hello(struct gb_operation *op) struct gb_svc *svc = connection->private; struct gb_host_device *hd = connection->hd; struct gb_svc_hello_request *hello_request; - struct gb_interface *intf; int ret; /* @@ -389,16 +343,6 @@ static int gb_svc_hello(struct gb_operation *op) if (ret) return ret; - /* - * Endo and its modules are ready now, fix AP's partially initialized - * svc protocol and its connection. - */ - intf = gb_ap_interface_create(hd, connection, svc->ap_intf_id); - if (!intf) { - gb_endo_remove(hd->endo); - return ret; - } - return 0; } @@ -720,9 +664,6 @@ static int gb_svc_connection_init(struct gb_connection *connection) hd->svc = svc; - WARN_ON(connection->hd->initial_svc_connection); - connection->hd->initial_svc_connection = connection; - return 0; } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index e05785f499f4..99be0411d000 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -41,6 +41,4 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -struct gb_connection *gb_ap_svc_connection_create(struct gb_host_device *hd); - #endif /* __SVC_H */ -- cgit v1.2.3-59-g8ed1b From 684156a9d6eba32db939b11618d3c14d7e92d630 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:19 +0100 Subject: greybus: svc: clean up log messages Use dev_err and friends with the svc device for messages. Clean up error messages. Demote a few warnings to warning level. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 106 +++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 11fa8c91bbe3..62b67cc6f8d9 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -7,7 +7,6 @@ * Released under the GPLv2 only. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include "greybus.h" @@ -87,15 +86,15 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, &request, sizeof(request), &response, sizeof(response)); if (ret) { - pr_err("failed to get DME attribute (%hhu %hx %hu) %d\n", - intf_id, attr, selector, ret); + dev_err(&svc->dev, "failed to get DME attribute (%hhu %hx %hu): %d\n", + intf_id, attr, selector, ret); return ret; } result = le16_to_cpu(response.result_code); if (result) { - pr_err("Unipro error %hu while getting DME attribute (%hhu %hx %hu)\n", - result, intf_id, attr, selector); + dev_err(&svc->dev, "UniPro error while getting DME attribute (%hhu %hx %hu): %hu\n", + intf_id, attr, selector, result); return -EINVAL; } @@ -123,15 +122,15 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, &request, sizeof(request), &response, sizeof(response)); if (ret) { - pr_err("failed to set DME attribute (%hhu %hx %hu %u) %d\n", - intf_id, attr, selector, value, ret); + dev_err(&svc->dev, "failed to set DME attribute (%hhu %hx %hu %u): %d\n", + intf_id, attr, selector, value, ret); return ret; } result = le16_to_cpu(response.result_code); if (result) { - pr_err("Unipro error %hu while setting DME attribute (%hhu %hx %hu %u)\n", - result, intf_id, attr, selector, value); + dev_err(&svc->dev, "UniPro error while setting DME attribute (%hhu %hx %hu %u): %hu\n", + intf_id, attr, selector, value, result); return -EINVAL; } @@ -233,9 +232,10 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY, &request, sizeof(request), NULL, 0); - if (ret) - pr_err("failed to destroy connection (%hhu:%hu %hhu:%hu) %d\n", - intf1_id, cport1_id, intf2_id, cport2_id, ret); + if (ret) { + dev_err(&svc->dev, "failed to destroy connection (%hhu:%hu %hhu:%hu): %d\n", + intf1_id, cport1_id, intf2_id, cport2_id, ret); + } } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); @@ -265,20 +265,21 @@ static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_DESTROY, &request, sizeof(request), NULL, 0); - if (ret) - pr_err("failed to destroy route (%hhu %hhu) %d\n", - intf1_id, intf2_id, ret); + if (ret) { + dev_err(&svc->dev, "failed to destroy route (%hhu %hhu): %d\n", + intf1_id, intf2_id, ret); + } } static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; + struct gb_svc *svc = connection->private; struct gb_protocol_version_request *request; struct gb_protocol_version_response *response; if (op->request->payload_size < sizeof(*request)) { - pr_err("%d: short version request (%zu < %zu)\n", - connection->intf_cport_id, + dev_err(&svc->dev, "short version request (%zu < %zu)\n", op->request->payload_size, sizeof(*request)); return -EINVAL; @@ -287,20 +288,16 @@ static int gb_svc_version_request(struct gb_operation *op) request = op->request->payload; if (request->major > GB_SVC_VERSION_MAJOR) { - pr_err("%d: unsupported major version (%hhu > %hhu)\n", - connection->intf_cport_id, request->major, - GB_SVC_VERSION_MAJOR); + dev_warn(&svc->dev, "unsupported major version (%hhu > %hhu)\n", + request->major, GB_SVC_VERSION_MAJOR); return -ENOTSUPP; } connection->module_major = request->major; connection->module_minor = request->minor; - if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) { - pr_err("%d: error allocating response\n", - connection->intf_cport_id); + if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) return -ENOMEM; - } response = op->response->payload; response->major = connection->module_major; @@ -322,9 +319,9 @@ static int gb_svc_hello(struct gb_operation *op) * request, use that to create an endo. */ if (op->request->payload_size < sizeof(*hello_request)) { - pr_err("%d: Illegal size of hello request (%zu < %zu)\n", - connection->intf_cport_id, op->request->payload_size, - sizeof(*hello_request)); + dev_warn(&svc->dev, "short hello request (%zu < %zu)\n", + op->request->payload_size, + sizeof(*hello_request)); return -EINVAL; } @@ -401,15 +398,15 @@ static void svc_process_hotplug(struct work_struct *work) * Remove the interface and add it again, and let user know * about this with a print message. */ - pr_info("%d: Removed interface (%hhu) to add it again\n", - connection->intf_cport_id, intf_id); + dev_info(&svc->dev, "removing interface %hhu to add it again\n", + intf_id); svc_intf_remove(connection, intf); } intf = gb_interface_create(hd, intf_id); if (!intf) { - pr_err("%d: Failed to create interface with id %hhu\n", - connection->intf_cport_id, intf_id); + dev_err(&svc->dev, "failed to create interface %hhu\n", + intf_id); goto free_svc_hotplug; } @@ -434,15 +431,15 @@ static void svc_process_hotplug(struct work_struct *work) GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); if (device_id < 0) { ret = device_id; - pr_err("%d: Failed to allocate device id for interface with id %hhu (%d)\n", - connection->intf_cport_id, intf_id, ret); + dev_err(&svc->dev, "failed to allocate device id for interface %hhu: %d\n", + intf_id, ret); goto destroy_interface; } ret = gb_svc_intf_device_id(svc, intf_id, device_id); if (ret) { - pr_err("%d: Device id operation failed, interface %hhu device_id %hhu (%d)\n", - connection->intf_cport_id, intf_id, device_id, ret); + dev_err(&svc->dev, "failed to set device id %hhu for interface %hhu: %d\n", + device_id, intf_id, ret); goto ida_put; } @@ -452,15 +449,15 @@ static void svc_process_hotplug(struct work_struct *work) ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_DEVICE_ID_AP, intf_id, device_id); if (ret) { - pr_err("%d: Route create operation failed, interface %hhu device_id %hhu (%d)\n", - connection->intf_cport_id, intf_id, device_id, ret); + dev_err(&svc->dev, "failed to create route to interface %hhu (device id %hhu): %d\n", + intf_id, device_id, ret); goto svc_id_free; } ret = gb_interface_init(intf, device_id); if (ret) { - pr_err("%d: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", - connection->intf_cport_id, intf_id, device_id, ret); + dev_err(&svc->dev, "failed to initialize interface %hhu (device id %hhu): %d\n", + intf_id, device_id, ret); goto destroy_route; } @@ -492,13 +489,14 @@ free_svc_hotplug: */ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) { + struct gb_svc *svc = op->connection->private; struct gb_message *request = op->request; struct svc_hotplug *svc_hotplug; if (request->payload_size < sizeof(svc_hotplug->data)) { - pr_err("%d: short hotplug request received (%zu < %zu)\n", - op->connection->intf_cport_id, request->payload_size, - sizeof(svc_hotplug->data)); + dev_warn(&svc->dev, "short hotplug request received (%zu < %zu)\n", + request->payload_size, + sizeof(svc_hotplug->data)); return -EINVAL; } @@ -517,6 +515,7 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) { + struct gb_svc *svc = op->connection->private; struct gb_message *request = op->request; struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload; struct gb_host_device *hd = op->connection->hd; @@ -524,9 +523,9 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) u8 intf_id; if (request->payload_size < sizeof(*hot_unplug)) { - pr_err("connection %d: short hot unplug request received (%zu < %zu)\n", - op->connection->intf_cport_id, request->payload_size, - sizeof(*hot_unplug)); + dev_warn(&svc->dev, "short hot unplug request received (%zu < %zu)\n", + request->payload_size, + sizeof(*hot_unplug)); return -EINVAL; } @@ -534,8 +533,8 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) intf = gb_interface_find(hd, intf_id); if (!intf) { - pr_err("connection %d: Couldn't find interface for id %hhu\n", - op->connection->intf_cport_id, intf_id); + dev_warn(&svc->dev, "could not find hot-unplug interface %hhu\n", + intf_id); return -EINVAL; } @@ -546,14 +545,14 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) static int gb_svc_intf_reset_recv(struct gb_operation *op) { + struct gb_svc *svc = op->connection->private; struct gb_message *request = op->request; struct gb_svc_intf_reset_request *reset; u8 intf_id; if (request->payload_size < sizeof(*reset)) { - pr_err("connection %d: short reset request received (%zu < %zu)\n", - op->connection->intf_cport_id, request->payload_size, - sizeof(*reset)); + dev_warn(&svc->dev, "short reset request received (%zu < %zu)\n", + request->payload_size, sizeof(*reset)); return -EINVAL; } reset = request->payload; @@ -597,8 +596,8 @@ static int gb_svc_request_recv(u8 type, struct gb_operation *op) } if (ret) { - pr_warn("connection %d: unexpected SVC request 0x%02x received (state %u)\n", - connection->intf_cport_id, type, svc->state); + dev_warn(&svc->dev, "unexpected request 0x%02x received (state %u)\n", + type, svc->state); return ret; } @@ -620,8 +619,7 @@ static int gb_svc_request_recv(u8 type, struct gb_operation *op) case GB_SVC_TYPE_INTF_RESET: return gb_svc_intf_reset_recv(op); default: - pr_err("connection %d: unsupported request: %hhu\n", - connection->intf_cport_id, type); + dev_warn(&svc->dev, "unsupported request 0x%02x\n", type); return -EINVAL; } } -- cgit v1.2.3-59-g8ed1b From dc3da5db2858c3fb33441988d1b6d56c06e53d7d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:20 +0100 Subject: greybus: loopback: remove endo reference Replace reference to "endo0" and generate the raw-latency filename based on the host-device bus id instead. Signed-off-by: Johan Hovold Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ededfef40e9c..ef16ca579ca1 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -922,10 +922,11 @@ static int gb_loopback_connection_init(struct gb_connection *connection) return -ENOMEM; gb_loopback_reset_stats(&gb_dev); - /* If this is the first connection - create a module endo0 entry */ + /* If this is the first connection - create a per-bus entry */ mutex_lock(&gb_dev.mutex); if (!gb_dev.count) { - snprintf(name, sizeof(name), "raw_latency_endo0"); + snprintf(name, sizeof(name), "raw_latency_%d", + connection->bundle->intf->hd->bus_id); gb_dev.file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, &gb_dev, &gb_loopback_debugfs_dev_latency_ops); -- cgit v1.2.3-59-g8ed1b From 0f37860de9a86d25f86ccc95631fa03fa766f31e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:21 +0100 Subject: greybus: kill the endo Remove the now unused endo and module code. Note that the never-implemented serial and version attributes of the endo can be implemented as svc attributes if needed. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 - drivers/staging/greybus/core.c | 20 -- drivers/staging/greybus/endo.c | 520 -------------------------------------- drivers/staging/greybus/endo.h | 63 ----- drivers/staging/greybus/greybus.h | 14 - drivers/staging/greybus/hd.c | 6 - drivers/staging/greybus/hd.h | 1 - drivers/staging/greybus/module.c | 178 ------------- drivers/staging/greybus/module.h | 26 -- drivers/staging/greybus/svc.c | 10 - 10 files changed, 840 deletions(-) delete mode 100644 drivers/staging/greybus/endo.c delete mode 100644 drivers/staging/greybus/endo.h delete mode 100644 drivers/staging/greybus/module.c delete mode 100644 drivers/staging/greybus/module.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 40e22ec810ae..cc5048263111 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -2,8 +2,6 @@ greybus-y := core.o \ debugfs.o \ hd.o \ manifest.o \ - endo.o \ - module.o \ interface.o \ bundle.o \ connection.o \ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index d1e26c4a26fc..9dcb1bf311c0 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -78,23 +78,12 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { struct gb_host_device *hd = NULL; - struct gb_module *module = NULL; struct gb_interface *intf = NULL; struct gb_bundle *bundle = NULL; struct gb_svc *svc = NULL; - if (is_gb_endo(dev)) { - /* - * Not much to do for an endo, just fall through, as the - * "default" attributes are good enough for us. - */ - return 0; - } - if (is_gb_host_device(dev)) { hd = to_gb_host_device(dev); - } else if (is_gb_module(dev)) { - module = to_gb_module(dev); } else if (is_gb_interface(dev)) { intf = to_gb_interface(dev); } else if (is_gb_bundle(dev)) { @@ -214,12 +203,6 @@ static int __init gb_init(void) goto error_operation; } - retval = gb_endo_init(); - if (retval) { - pr_err("gb_endo_init failed (%d)\n", retval); - goto error_endo; - } - retval = gb_control_protocol_init(); if (retval) { pr_err("gb_control_protocol_init failed\n"); @@ -245,8 +228,6 @@ error_firmware: error_svc: gb_control_protocol_exit(); error_control: - gb_endo_exit(); -error_endo: gb_operation_exit(); error_operation: gb_hd_exit(); @@ -264,7 +245,6 @@ static void __exit gb_exit(void) gb_firmware_protocol_exit(); gb_svc_protocol_exit(); gb_control_protocol_exit(); - gb_endo_exit(); gb_operation_exit(); gb_hd_exit(); bus_unregister(&greybus_bus_type); diff --git a/drivers/staging/greybus/endo.c b/drivers/staging/greybus/endo.c deleted file mode 100644 index cca2a83449c6..000000000000 --- a/drivers/staging/greybus/endo.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - * Greybus endo code - * - * Copyright 2014-2015 Google Inc. - * Copyright 2014-2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include "greybus.h" - -/* Endo ID (16 bits long) Masks */ -#define ENDO_ID_MASK 0xFFFF -#define ENDO_LARGE_MASK 0x1000 -#define ENDO_MEDIUM_MASK 0x0400 -#define ENDO_MINI_MASK 0x0100 - -#define ENDO_FRONT_MASK(id) ((id) >> 13) -#define ENDO_BACK_SIDE_RIBS_MASK(ribs) ((1 << (ribs)) - 1) - -/* - * endo_is_medium() should be used only if endo isn't large. And endo_is_mini() - * should be used only if endo isn't large or medium. - */ -#define endo_is_large(id) ((id) & ENDO_LARGE_MASK) -#define endo_is_medium(id) ((id) & ENDO_MEDIUM_MASK) -#define endo_is_mini(id) ((id) & ENDO_MINI_MASK) - -#define endo_back_left_ribs(id, ribs) (((id) >> (ribs)) & ENDO_BACK_SIDE_RIBS_MASK(ribs)) -#define endo_back_right_ribs(id, ribs) ((id) & ENDO_BACK_SIDE_RIBS_MASK(ribs)) - -/* - * An Endo has interface block positions on the front and back. - * Each has numeric ID, starting with 1 (interface 0 represents - * the SVC within the Endo itself). The maximum interface ID is the - * also the number of non-SVC interfaces possible on the endo. - * - * Total number of interfaces: - * - Front: 4 - * - Back left: max_ribs + 1 - * - Back right: max_ribs + 1 - */ -#define max_endo_interface_id(endo_layout) \ - (4 + ((endo_layout)->max_ribs + 1) * 2) - -static struct ida greybus_endo_id_map; - -/* endo sysfs attributes */ -static ssize_t serial_number_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gb_endo *endo = to_gb_endo(dev); - - return sprintf(buf, "%s\n", &endo->svc_info.serial_number[0]); -} -static DEVICE_ATTR_RO(serial_number); - -static ssize_t version_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gb_endo *endo = to_gb_endo(dev); - - return sprintf(buf, "%s\n", &endo->svc_info.version[0]); -} -static DEVICE_ATTR_RO(version); - -static struct attribute *svc_attrs[] = { - &dev_attr_serial_number.attr, - &dev_attr_version.attr, - NULL, -}; - -static const struct attribute_group svc_group = { - .attrs = svc_attrs, - .name = "svc", -}; - -static const struct attribute_group *endo_groups[] = { - &svc_group, - NULL, -}; - -static void gb_endo_release(struct device *dev) -{ - struct gb_endo *endo = to_gb_endo(dev); - - ida_simple_remove(&greybus_endo_id_map, endo->dev_id); - kfree(endo); -} - -struct device_type greybus_endo_type = { - .name = "greybus_endo", - .release = gb_endo_release, -}; - - -/* Validate Endo ID */ - -/* - * The maximum module height is 2 units. This means any adjacent pair of bits - * in the left or right mask must have at least one bit set. - */ -static inline bool modules_oversized(unsigned int count, unsigned int mask) -{ - int i; - - for (i = 0; i < count - 1; i++) - if (!(mask & (0x3 << i))) - return true; - - return false; -} - -/* Reverse a number of least significant bits in a value */ -static u8 reverse_bits(unsigned int value, unsigned int bits) -{ - u8 result = 0; - u8 result_mask = 1 << (bits - 1); - u8 value_mask = 1; - - while (value && result_mask) { - if (value & value_mask) { - result |= result_mask; - value ^= value_mask; - } - value_mask <<= 1; - result_mask >>= 1; - } - - return result; -} - -/* - * An Endo can have at most one instance of a single rib spanning its whole - * width. That is, the left and right bit masks representing the rib positions - * must have at most one bit set in both masks. - */ -static bool single_cross_rib(u8 left_ribs, u8 right_ribs) -{ - u8 span_ribs = left_ribs & right_ribs; - - /* Power of 2 ? */ - if (span_ribs & (span_ribs - 1)) - return false; - return true; -} - -/* - * Each Endo size has its own set of front module configurations. For most, the - * resulting rib mask is the same regardless of the Endo size. The mini Endo - * has a few differences though. - * - * Endo front has 4 interface blocks and 3 rib positions. A maximum of 2 ribs - * are allowed to be present for any endo type. - * - * This routine validates front mask and sets 'front_ribs', its 3 least - * significant bits represent front ribs mask, other are 0. The front values - * should be within range (1..6). - * - * front_ribs bitmask: - * - Bit 0: 1st rib location from top, i.e. between interface 1 and 2. - * - Bit 1: 2nd rib location from top, i.e. between interface 2 and 3. - * - Bit 2: 3rd rib location from top, i.e. between interface 3 and 4. - */ -static bool validate_front_ribs(struct gb_host_device *hd, - struct endo_layout *layout, bool mini, - u16 endo_id) -{ - u8 front_mask = ENDO_FRONT_MASK(endo_id); - - /* Verify front endo mask is in valid range, i.e. 1-6 */ - - switch (front_mask) { - case 1: - layout->front_ribs = 0x0; - break; - case 2: - layout->front_ribs = 0x1; - break; - case 3: - layout->front_ribs = 0x4; - break; - case 4: - layout->front_ribs = mini ? 0x2 : 0x3; - break; - case 5: - layout->front_ribs = mini ? 0x2 : 0x6; - break; - case 6: - layout->front_ribs = 0x5; - break; - default: - dev_err(&hd->dev, - "%s: Invalid endo front mask 0x%02x, id 0x%04x\n", - __func__, front_mask, endo_id); - return false; - } - - return true; -} - -/* - * The rear of an endo has a single vertical "spine", and the modules placed on - * the left and right of that spine are separated by ribs. Only one "cross" - * (i.e. rib that spans the entire width) is allowed of the back of the endo; - * all other ribs reach from the spine to the left or right edge. - * - * The width of the module positions on the left and right of the spine are - * determined by the width of the endo (either 1 or 2 "units"). The height of - * the modules is determined by the placement of the ribs (a module can be - * either 1 or 2 units high). - * - * The lower 13 bits of the 16-bit endo id are used to encode back ribs - * information. The large form factor endo uses all of these bits; the medium - * and mini form factors leave some bits unused (such bits shall be ignored, and - * are 0 for the purposes of this endo id definition). - * - * Each defined bit represents a rib position on one or the other side - * of the spine on the back of an endo. If that bit is set (1), it - * means a rib is present in the corresponding location; otherwise - * there is no rib there. - * - * Rotating an endo 180 degrees does not produce a new rib configuration. A - * single endo id represents a specific configuration of ribs without regard to - * its rotational orientation. We define one canonical id to represent a - * particular endo configuration. - */ -static bool validate_back_ribs(struct gb_host_device *hd, - struct endo_layout *layout, u16 endo_id) -{ - u8 max_ribs = layout->max_ribs; - u8 left_ribs; - u8 right_ribs; - - /* Extract the left and right rib masks */ - left_ribs = endo_back_left_ribs(endo_id, max_ribs); - right_ribs = endo_back_right_ribs(endo_id, max_ribs); - - if (!single_cross_rib(left_ribs, right_ribs)) { - dev_err(&hd->dev, - "%s: More than one spanning rib (left 0x%02x right 0x%02x), id 0x%04x\n", - __func__, left_ribs, right_ribs, endo_id); - return false; - } - - if (modules_oversized(max_ribs, left_ribs)) { - dev_err(&hd->dev, - "%s: Oversized module (left) 0x%02x, id 0x%04x\n", - __func__, left_ribs, endo_id); - return false; - } - - if (modules_oversized(max_ribs, right_ribs)) { - dev_err(&hd->dev, - "%s: Oversized module (Right) 0x%02x, id 0x%04x\n", - __func__, right_ribs, endo_id); - return false; - } - - /* - * The Endo numbering scheme represents the left and right rib - * configuration in a way that's convenient for looking for multiple - * spanning ribs. But it doesn't match the normal Endo interface - * numbering scheme (increasing counter-clockwise around the back). - * Reverse the right bit positions so they do match. - */ - right_ribs = reverse_bits(right_ribs, max_ribs); - - /* - * A mini or large Endo rotated 180 degrees is still the same Endo. In - * most cases that allows two distinct values to represent the same - * Endo; we choose one of them to be the canonical one (and the other is - * invalid). The canonical one is identified by higher value of left - * ribs mask. - * - * This doesn't apply to medium Endos, because the left and right sides - * are of different widths. - */ - if (max_ribs != ENDO_BACK_RIBS_MEDIUM && left_ribs < right_ribs) { - dev_err(&hd->dev, "%s: Non-canonical endo id 0x%04x\n", __func__, - endo_id); - return false; - } - - layout->left_ribs = left_ribs; - layout->right_ribs = right_ribs; - return true; -} - -/* - * Validate the endo-id passed from SVC. Error out if its not a valid Endo, - * else return structure representing ribs positions on front and back of Endo. - */ -static int gb_endo_validate_id(struct gb_host_device *hd, - struct endo_layout *layout, u16 endo_id) -{ - /* Validate Endo Size */ - if (endo_is_large(endo_id)) { - /* Large Endo type */ - layout->max_ribs = ENDO_BACK_RIBS_LARGE; - } else if (endo_is_medium(endo_id)) { - /* Medium Endo type */ - layout->max_ribs = ENDO_BACK_RIBS_MEDIUM; - } else if (endo_is_mini(endo_id)) { - /* Mini Endo type */ - layout->max_ribs = ENDO_BACK_RIBS_MINI; - } else { - dev_err(&hd->dev, "%s: Invalid endo type, id 0x%04x\n", - __func__, endo_id); - return -EINVAL; - } - - if (!validate_back_ribs(hd, layout, endo_id)) - return -EINVAL; - - if (!validate_front_ribs(hd, layout, - layout->max_ribs == ENDO_BACK_RIBS_MINI, - endo_id)) - return -EINVAL; - - return 0; -} - -/* - * Look up which module contains the given interface. - * - * A module's ID is the same as its lowest-numbered interface ID. So the module - * ID for a 1x1 module is always the same as its interface ID. - * - * For Endo Back: - * The module ID for an interface on a 1x2 or 2x2 module (which use two - * interface blocks) can be either the interface ID, or one less than the - * interface ID if there is no rib "above" the interface. - * - * For Endo Front: - * There are three rib locations in front and all of them might be unused, i.e. - * a single module is used for all 4 interfaces. We need to check all ribs in - * that case to find module ID. - */ -u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id) -{ - struct endo_layout *layout = &endo->layout; - unsigned int height = layout->max_ribs + 1; - unsigned int iid = interface_id - 1; - unsigned int mask, rib_mask; - - if (!interface_id) - return 0; - - if (iid < height) { /* back left */ - mask = layout->left_ribs; - } else if (iid < 2 * height) { /* back right */ - mask = layout->right_ribs; - iid -= height; - } else { /* front */ - mask = layout->front_ribs; - iid -= 2 * height; - } - - /* - * Find the next rib *above* this interface to determine the lowest - * interface ID in the module. - */ - rib_mask = 1 << iid; - while ((rib_mask >>= 1) != 0 && !(mask & rib_mask)) - --interface_id; - - return interface_id; -} - -/* - * Creates all possible modules for the Endo. - * - * We try to create modules for all possible interface IDs. If a module is - * already created, we skip creating it again with the help of prev_module_id. - */ -static int create_modules(struct gb_endo *endo) -{ - struct gb_module *module; - int prev_module_id = 0; - int interface_id; - int module_id; - int max_id; - - max_id = max_endo_interface_id(&endo->layout); - - /* Find module corresponding to each interface */ - for (interface_id = 1; interface_id <= max_id; interface_id++) { - module_id = endo_get_module_id(endo, interface_id); - - if (WARN_ON(!module_id)) - continue; - - /* Skip already created modules */ - if (module_id == prev_module_id) - continue; - - prev_module_id = module_id; - - /* New module, create it */ - module = gb_module_create(&endo->dev, module_id); - if (!module) - return -EINVAL; - } - - return 0; -} - -static int gb_endo_register(struct gb_host_device *hd, - struct gb_endo *endo) -{ - int dev_id; - int retval; - - dev_id = ida_simple_get(&greybus_endo_id_map, 0, 0, GFP_KERNEL); - if (dev_id < 0) - return dev_id; - - endo->dev_id = dev_id; - - endo->dev.parent = &hd->dev; - endo->dev.bus = &greybus_bus_type; - endo->dev.type = &greybus_endo_type; - endo->dev.groups = endo_groups; - endo->dev.dma_mask = hd->dev.dma_mask; - device_initialize(&endo->dev); - dev_set_name(&endo->dev, "endo%hu", endo->dev_id); - - // FIXME - // Get the version and serial number from the SVC, right now we are - // using "fake" numbers. - strcpy(&endo->svc_info.serial_number[0], "042"); - strcpy(&endo->svc_info.version[0], "0.0"); - - retval = device_add(&endo->dev); - if (retval) { - dev_err(&hd->dev, "failed to add endo device of id 0x%04x\n", - endo->id); - put_device(&endo->dev); - } - - return retval; -} - -struct gb_endo *gb_endo_create(struct gb_host_device *hd, u16 endo_id, - u8 ap_intf_id) -{ - struct gb_endo *endo; - int retval; - - endo = kzalloc(sizeof(*endo), GFP_KERNEL); - if (!endo) - return ERR_PTR(-ENOMEM); - - /* First check if the value supplied is a valid endo id */ - if (gb_endo_validate_id(hd, &endo->layout, endo_id)) { - retval = -EINVAL; - goto free_endo; - } - if (ap_intf_id > max_endo_interface_id(&endo->layout)) { - retval = -EINVAL; - goto free_endo; - } - - /* Register Endo device */ - retval = gb_endo_register(hd, endo); - if (retval) - goto free_endo; - - /* Create modules/interfaces */ - retval = create_modules(endo); - if (retval) { - gb_endo_remove(endo); - return NULL; - } - - return endo; - -free_endo: - kfree(endo); - - return ERR_PTR(retval); -} - -void gb_endo_remove(struct gb_endo *endo) -{ - if (!endo) - return; - - /* remove all modules for this endo */ - gb_module_remove_all(endo); - - device_unregister(&endo->dev); -} - -int greybus_endo_setup(struct gb_host_device *hd, u16 endo_id, - u8 ap_intf_id) -{ - struct gb_endo *endo; - - endo = gb_endo_create(hd, endo_id, ap_intf_id); - if (IS_ERR(endo)) - return PTR_ERR(endo); - hd->endo = endo; - - return 0; -} -EXPORT_SYMBOL_GPL(greybus_endo_setup); - -int __init gb_endo_init(void) -{ - ida_init(&greybus_endo_id_map); - - return 0; -} - -void gb_endo_exit(void) -{ - ida_destroy(&greybus_endo_id_map); -} diff --git a/drivers/staging/greybus/endo.h b/drivers/staging/greybus/endo.h deleted file mode 100644 index 20a8649561d5..000000000000 --- a/drivers/staging/greybus/endo.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Greybus endo code - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __ENDO_H -#define __ENDO_H - -/* Greybus "public" definitions" */ -struct gb_svc_info { - u8 serial_number[10]; - u8 version[10]; -}; - -/* Max ribs per Endo size */ -#define ENDO_BACK_RIBS_MINI 0x4 -#define ENDO_BACK_RIBS_MEDIUM 0x5 -#define ENDO_BACK_RIBS_LARGE 0x6 - -/** - * struct endo_layout - represents front/back ribs of the endo. - * - * @front_ribs: Mask of present ribs in front. - * @left_ribs: Mask of present ribs in back (left). - * @right_ribs: Mask of present ribs in back (right). - * @max_ribs: Max ribs on endo back, possible values defined above. - */ -struct endo_layout { - u8 front_ribs; - u8 left_ribs; - u8 right_ribs; - u8 max_ribs; -}; - -struct gb_endo { - struct device dev; - struct endo_layout layout; - struct gb_svc_info svc_info; - u16 dev_id; - u16 id; - u8 ap_intf_id; -}; -#define to_gb_endo(d) container_of(d, struct gb_endo, dev) - -/* Greybus "private" definitions */ -struct gb_host_device; - -int gb_endo_init(void); -void gb_endo_exit(void); - -struct gb_endo *gb_endo_create(struct gb_host_device *hd, - u16 endo_id, u8 ap_intf_id); -void gb_endo_remove(struct gb_endo *endo); -int greybus_endo_setup(struct gb_host_device *hd, u16 endo_id, - u8 ap_intf_id); - -u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id); - -#endif /* __ENDO_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index fa81733282d6..405e56540a3a 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -26,10 +26,8 @@ #include "greybus_protocols.h" #include "manifest.h" #include "hd.h" -#include "endo.h" #include "svc.h" #include "firmware.h" -#include "module.h" #include "control.h" #include "interface.h" #include "bundle.h" @@ -105,8 +103,6 @@ struct dentry *gb_debugfs_get(void); extern struct bus_type greybus_bus_type; extern struct device_type greybus_hd_type; -extern struct device_type greybus_endo_type; -extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_bundle_type; extern struct device_type greybus_svc_type; @@ -116,16 +112,6 @@ static inline int is_gb_host_device(const struct device *dev) return dev->type == &greybus_hd_type; } -static inline int is_gb_endo(const struct device *dev) -{ - return dev->type == &greybus_endo_type; -} - -static inline int is_gb_module(const struct device *dev) -{ - return dev->type == &greybus_module_type; -} - static inline int is_gb_interface(const struct device *dev) { return dev->type == &greybus_interface_type; diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 2ee5d4ff57fc..b280925792b5 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -130,13 +130,7 @@ EXPORT_SYMBOL_GPL(gb_hd_add); void gb_hd_del(struct gb_host_device *hd) { - /* - * Tear down all interfaces, modules, and the endo that is associated - * with this host controller before freeing the memory associated with - * the host controller. - */ gb_interfaces_remove(hd); - gb_endo_remove(hd->endo); gb_connection_destroy(hd->svc_connection); diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 72716e0e81b1..5612b489dd24 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -40,7 +40,6 @@ struct gb_host_device { /* Host device buffer constraints */ size_t buffer_size_max; - struct gb_endo *endo; struct gb_svc *svc; struct gb_connection *svc_connection; diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c deleted file mode 100644 index e3d7a66e692e..000000000000 --- a/drivers/staging/greybus/module.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Greybus module code - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include "greybus.h" - - -/* module sysfs attributes */ -static ssize_t epm_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - // FIXME - // Implement something here when we have a working control protocol - return sprintf(buf, "1\n"); -} - -static ssize_t epm_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - // FIXME - // Implement something here when we have a working control protocol - return 0; -} -static DEVICE_ATTR_RW(epm); - -static ssize_t power_control_show(struct device *dev, - struct device_attribute *addr, char *buf) -{ - // FIXME - // Implement something here when we have a working control protocol - return sprintf(buf, "1\n"); -} - -static ssize_t power_control_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - // FIXME - // Implement something here when we have a working control protocol - return 0; -} -static DEVICE_ATTR_RW(power_control); - -static ssize_t present_show(struct device *dev, - struct device_attribute *addr, char *buf) -{ - // FIXME - // Implement something here when we have a working control protocol - return sprintf(buf, "1\n"); -} - -static ssize_t present_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - // FIXME - // Implement something here when we have a working control protocol - return 0; -} -static DEVICE_ATTR_RW(present); - -static struct attribute *module_attrs[] = { - &dev_attr_epm.attr, - &dev_attr_power_control.attr, - &dev_attr_present.attr, - NULL, -}; -ATTRIBUTE_GROUPS(module); - -static void gb_module_release(struct device *dev) -{ - struct gb_module *module = to_gb_module(dev); - - kfree(module); -} - -struct device_type greybus_module_type = { - .name = "greybus_module", - .release = gb_module_release, -}; - -struct module_find { - struct gb_endo *endo; - u8 module_id; -}; - -static int module_find(struct device *dev, void *data) -{ - struct gb_module *module; - struct module_find *find = data; - - if (!is_gb_module(dev)) - return 0; - - module = to_gb_module(dev); - if ((module->module_id == find->module_id) && - (module->dev.parent == &find->endo->dev)) - return 1; - - return 0; -} - -/* - * Search the list of modules in the system. If one is found, return it, with - * the reference count incremented. - */ -struct gb_module *gb_module_find(struct gb_host_device *hd, u8 module_id) -{ - struct device *dev; - struct gb_module *module = NULL; - struct module_find find; - - if (!module_id) - return NULL; - - find.module_id = module_id; - find.endo = hd->endo; - - dev = bus_find_device(&greybus_bus_type, NULL, - &find, module_find); - if (dev) - module = to_gb_module(dev); - - return module; -} - -struct gb_module *gb_module_create(struct device *parent, u8 module_id) -{ - struct gb_module *module; - int retval; - - module = kzalloc(sizeof(*module), GFP_KERNEL); - if (!module) - return NULL; - - module->module_id = module_id; - module->dev.parent = parent; - module->dev.bus = &greybus_bus_type; - module->dev.type = &greybus_module_type; - module->dev.groups = module_groups; - module->dev.dma_mask = parent->dma_mask; - device_initialize(&module->dev); - dev_set_name(&module->dev, "%s:%hhu", dev_name(parent), module_id); - - retval = device_add(&module->dev); - if (retval) { - pr_err("failed to add module device for id 0x%02hhx\n", - module_id); - put_device(&module->dev); - return NULL; - } - - return module; -} - -static int module_remove(struct device *dev, void *data) -{ - struct gb_module *module; - struct gb_endo *endo = data; - - if (!is_gb_module(dev)) - return 0; - - module = to_gb_module(dev); - if (module->dev.parent == &endo->dev) - device_unregister(&module->dev); - - return 0; -} - -void gb_module_remove_all(struct gb_endo *endo) -{ - bus_for_each_dev(&greybus_bus_type, NULL, endo, module_remove); -} diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h deleted file mode 100644 index 2b36fe9dfa7f..000000000000 --- a/drivers/staging/greybus/module.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Greybus module code - * - * Copyright 2014 Google Inc. - * - * Released under the GPLv2 only. - */ - -#ifndef __MODULE_H -#define __MODULE_H - -/* Greybus "public" definitions" */ -struct gb_module { - struct device dev; - u8 module_id; /* Physical location within the Endo */ -}; -#define to_gb_module(d) container_of(d, struct gb_module, dev) - -struct gb_host_device; - -/* Greybus "private" definitions */ -struct gb_module *gb_module_find(struct gb_host_device *hd, u8 module_id); -struct gb_module *gb_module_create(struct device *parent, u8 module_id); -void gb_module_remove_all(struct gb_endo *endo); - -#endif /* __MODULE_H */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 62b67cc6f8d9..42cc7e9a20a4 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -310,14 +310,9 @@ static int gb_svc_hello(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_svc *svc = connection->private; - struct gb_host_device *hd = connection->hd; struct gb_svc_hello_request *hello_request; int ret; - /* - * SVC sends information about the endo and interface-id on the hello - * request, use that to create an endo. - */ if (op->request->payload_size < sizeof(*hello_request)) { dev_warn(&svc->dev, "short hello request (%zu < %zu)\n", op->request->payload_size, @@ -335,11 +330,6 @@ static int gb_svc_hello(struct gb_operation *op) return ret; } - /* Setup Endo */ - ret = greybus_endo_setup(hd, svc->endo_id, svc->ap_intf_id); - if (ret) - return ret; - return 0; } -- cgit v1.2.3-59-g8ed1b From 35822c04a5c232a7899d43a74f0d2fca5077b196 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:22 +0100 Subject: greybus: connection: handle bundle-less connections in svc helpers The svc connection helper functions should not assume that all dynamic connections will have a bundle. This is needed as the control bundle is going away. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7beab7468a0d..7dde8a11935e 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -294,7 +294,7 @@ gb_connection_svc_connection_create(struct gb_connection *connection) if (gb_connection_is_static(connection)) return 0; - intf = connection->bundle->intf; + intf = connection->intf; ret = gb_svc_connection_create(hd->svc, hd->svc->ap_intf_id, connection->hd_cport_id, @@ -320,7 +320,7 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection) gb_svc_connection_destroy(connection->hd->svc, connection->hd->svc->ap_intf_id, connection->hd_cport_id, - connection->bundle->intf->interface_id, + connection->intf->interface_id, connection->intf_cport_id); } -- cgit v1.2.3-59-g8ed1b From 7fa530ad1b2d160ec9e76b3b5a1ac2e302e8f3bf Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:23 +0100 Subject: greybus: control: do not assume a control bundle The control bundle is going away so update the code. Also remove defensive WARN_ON which would not just warn if our implementation is broken, but also leak further memory unnecessarily. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index ffc07a475845..2cc1917adda0 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -22,9 +22,8 @@ int gb_control_get_manifest_size_operation(struct gb_interface *intf) ret = gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST_SIZE, NULL, 0, &response, sizeof(response)); if (ret) { - dev_err(&connection->bundle->dev, - "%s: Manifest size get operation failed (%d)\n", - __func__, ret); + dev_err(&connection->intf->dev, + "failed to get manifest size: %d\n", ret); return ret; } @@ -72,7 +71,7 @@ static int gb_control_connection_init(struct gb_connection *connection) connection->private = control; /* Set interface's control connection */ - connection->bundle->intf->control = control; + connection->intf->control = control; return 0; } @@ -81,10 +80,7 @@ static void gb_control_connection_exit(struct gb_connection *connection) { struct gb_control *control = connection->private; - if (WARN_ON(connection->bundle->intf->control != control)) - return; - - connection->bundle->intf->control = NULL; + connection->intf->control = NULL; kfree(control); } -- cgit v1.2.3-59-g8ed1b From e1442f6910da337a879e44f32909844986714a4b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:24 +0100 Subject: greybus: connection: unconditionally enable connections Remove conditional enabling of connections when binding protocols that served no purpose as a connection either has no bundle or it has an interface with a valid device id. Also remove the now unused GB_PROTOCOL_NO_BUNDLE protocol flag. This is an intermediate step in moving the protocol binding to connection_init, but is also needed as the control bundle is going away. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 18 +++++------------- drivers/staging/greybus/protocol.h | 1 - drivers/staging/greybus/svc.c | 1 - 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7dde8a11935e..2b0784668547 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -541,19 +541,11 @@ int gb_connection_bind_protocol(struct gb_connection *connection) } connection->protocol = protocol; - /* - * If we have a valid device_id for the interface block, then we have an - * active device, so bring up the connection at the same time. - */ - if ((!connection->bundle && - protocol->flags & GB_PROTOCOL_NO_BUNDLE) || - connection->bundle->intf->device_id != GB_DEVICE_ID_BAD) { - ret = gb_connection_init(connection); - if (ret) { - gb_protocol_put(protocol); - connection->protocol = NULL; - return ret; - } + ret = gb_connection_init(connection); + if (ret) { + gb_protocol_put(protocol); + connection->protocol = NULL; + return ret; } return 0; diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 384ddf8d1600..f24281b39db5 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -16,7 +16,6 @@ struct gb_operation; /* Possible flags for protocol drivers */ #define GB_PROTOCOL_SKIP_CONTROL_CONNECTED BIT(0) /* Don't sent connected requests */ #define GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED BIT(1) /* Don't sent disconnected requests */ -#define GB_PROTOCOL_NO_BUNDLE BIT(2) /* Protocol May have a bundle-less connection */ #define GB_PROTOCOL_SKIP_VERSION BIT(3) /* Don't send get_version() requests */ typedef int (*gb_connection_init_t)(struct gb_connection *); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 42cc7e9a20a4..f9ca08b34cd0 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -678,7 +678,6 @@ static struct gb_protocol svc_protocol = { .request_recv = gb_svc_request_recv, .flags = GB_PROTOCOL_SKIP_CONTROL_CONNECTED | GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED | - GB_PROTOCOL_NO_BUNDLE | GB_PROTOCOL_SKIP_VERSION, }; gb_builtin_protocol_driver(svc_protocol); -- cgit v1.2.3-59-g8ed1b From f2152eb33dec35703176e792da2769e88e5a81d3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:25 +0100 Subject: greybus: manifest: refactor cport-descriptor release Add helper function to release cport-descriptors with a given bundle id. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 9252bf46bcc4..4fdb98d19eb8 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -60,6 +60,22 @@ static void release_manifest_descriptors(struct gb_interface *intf) release_manifest_descriptor(descriptor); } +static void release_cport_descriptors(struct list_head *head, u8 bundle_id) +{ + struct manifest_desc *desc, *tmp; + struct greybus_descriptor_cport *desc_cport; + + list_for_each_entry_safe(desc, tmp, head, links) { + desc_cport = desc->data; + + if (desc->type != GREYBUS_TYPE_CPORT) + continue; + + if (desc_cport->bundle == bundle_id) + release_manifest_descriptor(desc); + } +} + static struct manifest_desc *get_next_bundle_desc(struct gb_interface *intf) { struct manifest_desc *descriptor; @@ -275,14 +291,7 @@ exit: * Free all cports for this bundle to avoid 'excess descriptors' * warnings. */ - list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { - struct greybus_descriptor_cport *desc_cport = desc->data; - - if (desc->type != GREYBUS_TYPE_CPORT) - continue; - if (desc_cport->bundle == bundle_id) - release_manifest_descriptor(desc); - } + release_cport_descriptors(&intf->manifest_descs, bundle_id); return 0; /* Error; count should also be 0 */ } -- cgit v1.2.3-59-g8ed1b From 47091af91e7e872748a17158f1e99833962428b4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:59:26 +0100 Subject: greybus: interface: drop the control bundle Drop the control bundle and ignore control descriptors when parsing manifests. Every interface has a control connection with a well defined remote CPort 0 and there's no longer any need to create a bundle for it. As the control connection is setup and enabled before parsing the manifest, ignore any legacy descriptors for control cports and bundles in a manifest. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 42 +++++++-------------------- drivers/staging/greybus/manifest.c | 57 +++++++++---------------------------- 2 files changed, 24 insertions(+), 75 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 241518711323..9ff7464b8459 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -69,34 +69,6 @@ struct device_type greybus_interface_type = { .release = gb_interface_release, }; -/* - * Create kernel structures corresponding to a bundle and connection for - * managing control CPort. - */ -static int -gb_interface_create_control_bundle_connection(struct gb_interface *intf) -{ - struct gb_bundle *bundle; - struct gb_connection *connection; - - bundle = gb_bundle_create(intf, GB_CONTROL_BUNDLE_ID, - GREYBUS_CLASS_CONTROL); - if (!bundle) { - dev_err(&intf->dev, "failed to create control bundle\n"); - return -ENOMEM; - } - - connection = gb_connection_create_dynamic(intf, bundle, - GB_CONTROL_CPORT_ID, - GREYBUS_PROTOCOL_CONTROL); - if (!connection) { - dev_err(&intf->dev, "failed to create control connection\n"); - return -ENOMEM; - } - - return 0; -} - /* * A Greybus module represents a user-replaceable component on an Ara * phone. An interface is the physical connection on that module. A @@ -170,6 +142,9 @@ void gb_interface_remove(struct gb_interface *intf) list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); + if (intf->control) + gb_connection_destroy(intf->control->connection); + device_unregister(&intf->dev); } @@ -190,15 +165,20 @@ void gb_interfaces_remove(struct gb_host_device *hd) */ int gb_interface_init(struct gb_interface *intf, u8 device_id) { + struct gb_connection *connection; int ret, size; void *manifest; intf->device_id = device_id; /* Establish control CPort connection */ - ret = gb_interface_create_control_bundle_connection(intf); - if (ret) - return ret; + connection = gb_connection_create_dynamic(intf, NULL, + GB_CONTROL_CPORT_ID, + GREYBUS_PROTOCOL_CONTROL); + if (!connection) { + dev_err(&intf->dev, "failed to create control connection\n"); + return -ENOMEM; + } /* Get manifest size using control protocol on CPort */ size = gb_control_get_manifest_size_operation(intf); diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 4fdb98d19eb8..ea5ff8682e69 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -254,24 +254,10 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) /* Found one. Set up its function structure */ protocol_id = desc_cport->protocol_id; - /* Validate declarations of the control protocol CPort */ - if (cport_id == GB_CONTROL_CPORT_ID) { - /* This should have protocol set to control protocol*/ - if (protocol_id != GREYBUS_PROTOCOL_CONTROL) - goto print_error_exit; - /* Don't recreate connection for control cport */ - goto release_descriptor; - } - /* Nothing else should have its protocol as control protocol */ - if (protocol_id == GREYBUS_PROTOCOL_CONTROL) { - goto print_error_exit; - } - if (!gb_connection_create_dynamic(intf, bundle, cport_id, protocol_id)) goto exit; -release_descriptor: count++; /* Release the cport descriptor */ @@ -279,12 +265,6 @@ release_descriptor: } return count; -print_error_exit: - /* A control protocol parse error was encountered */ - dev_err(&bundle->dev, - "cport_id, protocol_id 0x%04hx,0x%04hx want 0x%04hx,0x%04hx\n", - cport_id, protocol_id, GB_CONTROL_CPORT_ID, - GREYBUS_PROTOCOL_CONTROL); exit: /* @@ -308,6 +288,7 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) struct gb_bundle *bundle_next; u32 count = 0; u8 bundle_id; + u8 class; while ((desc = get_next_bundle_desc(intf))) { struct greybus_descriptor_bundle *desc_bundle; @@ -315,37 +296,32 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) /* Found one. Set up its bundle structure*/ desc_bundle = desc->data; bundle_id = desc_bundle->id; + class = desc_bundle->class; + + /* Done with this bundle descriptor */ + release_manifest_descriptor(desc); - /* Don't recreate bundle for control cport */ + /* Ignore any legacy control bundles */ if (bundle_id == GB_CONTROL_BUNDLE_ID) { - /* This should have class set to control class */ - if (desc_bundle->class != GREYBUS_CLASS_CONTROL) { - dev_err(&intf->dev, - "bad class 0x%02x for control bundle\n", - desc_bundle->class); - goto cleanup; - } - - bundle = intf->control->connection->bundle; - goto parse_cports; + dev_dbg(&intf->dev, "%s - ignoring control bundle\n", + __func__); + release_cport_descriptors(&intf->manifest_descs, + bundle_id); + continue; } /* Nothing else should have its class set to control class */ - if (desc_bundle->class == GREYBUS_CLASS_CONTROL) { + if (class == GREYBUS_CLASS_CONTROL) { dev_err(&intf->dev, "bundle 0x%02x cannot use control class\n", bundle_id); goto cleanup; } - bundle = gb_bundle_create(intf, bundle_id, desc_bundle->class); + bundle = gb_bundle_create(intf, bundle_id, class); if (!bundle) goto cleanup; -parse_cports: - /* Done with this bundle descriptor */ - release_manifest_descriptor(desc); - /* * Now go set up this bundle's functions and cports. * @@ -362,15 +338,8 @@ parse_cports: * separate entities and don't reject entire interface and its * bundles on failing to initialize a cport. But make sure the * bundle which needs the cport, gets destroyed properly. - * - * The control bundle and its connections are special. The - * entire manifest should be rejected if we failed to initialize - * the control bundle/connections. */ if (!gb_manifest_parse_cports(bundle)) { - if (bundle_id == GB_CONTROL_BUNDLE_ID) - goto cleanup; - gb_bundle_destroy(bundle); continue; } -- cgit v1.2.3-59-g8ed1b From 32945d6ecc89331f10d715496fd2247f958d471e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Nov 2015 15:06:41 +0100 Subject: greybus: Documentation/sysfs: rename interface and bundle attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the example sysfs-tree layout under Documentation due renamed interface and bundle attributes. The interface and bundle "id" attributes have been renamed "interface_id" and "bundle_id" respectively to make them self describing. For consistency reasons the bundle class attribute is renamed as "bundle_class". . ├── greybus1 │   ├── 1-2 │   │   ├── 1-2.1 │   │   │   ├── bundle_class │   │   │   ├── bundle_id │   │   │   └── state │   │   ├── 1-2.2 │   │   │   ├── bundle_class │   │   │   ├── bundle_id │   │   │   └── state │   │   ├── interface_id │   │   ├── product_id │   │   ├── unique_id │   │   └── vendor_id │   ├── 1-4 │   │   ├── 1-4.2 │   │   │   ├── bundle_class │   │   │   ├── bundle_id │   │   │   ├── gpbridge0 │   │   │   │   ├── gpio │   │   │   │   │   └── gpiochip490 │   │   │   │   └── i2c-4 │   │   │   └── state │   │   ├── interface_id │   │   ├── product_id │   │   ├── unique_id │   │   └── vendor_id │   └── 1-svc │   ├── ap_intf_id │   ├── eject │   ├── endo_id │   └── unique_id └── greybus2 ├── 2-3 │   ├── 2-3.1 │   │   ├── bundle_class │   │   ├── bundle_id │   │   └── state │   ├── interface_id │   ├── product_id │   ├── unique_id │   └── vendor_id └── 2-svc ├── ap_intf_id ├── eject ├── endo_id └── unique_id Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id | 1 - .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id | 1 + .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id | 1 + .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_class | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/class | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id | 1 + 22 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/id +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id deleted file mode 100644 index b8626c4cff28..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/id +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id new file mode 100644 index 000000000000..b8626c4cff28 --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id @@ -0,0 +1 @@ +4 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/class b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/id +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id deleted file mode 100644 index 00750edc07d6..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/id +++ /dev/null @@ -1 +0,0 @@ -3 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id new file mode 100644 index 000000000000..00750edc07d6 --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id @@ -0,0 +1 @@ +3 -- cgit v1.2.3-59-g8ed1b From f1e941a6e4b71a5b94d584277121bd2bee8809b0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 26 Nov 2015 15:33:46 +0530 Subject: greybus: firmware: Fetch es2 VID/PID to distinguish module vendors The es2 chip doesn't have VID/PID programmed into the hardware and we need to hack that up to distinguish different modules and their firmware packages. This fetches VID/PID (over firmware protocol) for es2 chip only, when VID/PID already sent during hotplug are 0. Since only the bootrom contains a firmware protocol cport, this only affects bootrom's working and not nuttx. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 51 ++++++++++++++++++++++++++++- drivers/staging/greybus/greybus_protocols.h | 7 ++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index e41526b62ab6..cd2184cec758 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -11,9 +11,14 @@ #include "greybus.h" +#define ES2_UNIPRO_MFG_ID 0x00000126 +#define ES2_UNIPRO_PROD_ID 0x00001000 + struct gb_firmware { struct gb_connection *connection; const struct firmware *fw; + u32 vendor_id; + u32 product_id; }; static void free_firmware(struct gb_firmware *firmware) @@ -22,6 +27,45 @@ static void free_firmware(struct gb_firmware *firmware) firmware->fw = NULL; } +/* + * The es2 chip doesn't have VID/PID programmed into the hardware and we need to + * hack that up to distinguish different modules and their firmware blobs. + * + * This fetches VID/PID (over firmware protocol) for es2 chip only, when VID/PID + * already sent during hotplug are 0. + * + * Otherwise, we keep firmware->vendor_id/product_id same as what's passed + * during hotplug. + */ +static void firmware_es2_fixup_vid_pid(struct gb_firmware *firmware) +{ + struct gb_firmware_get_vid_pid_response response; + struct gb_connection *connection = firmware->connection; + struct gb_interface *intf = connection->bundle->intf; + int ret; + + /* + * Use VID/PID specified at hotplug if: + * - Bridge ASIC chip isn't ES2 + * - Received non-zero Vendor/Product ids + */ + if (intf->unipro_mfg_id != ES2_UNIPRO_MFG_ID || + intf->unipro_prod_id != ES2_UNIPRO_PROD_ID || + intf->vendor_id != 0 || intf->product_id != 0) + return; + + ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_GET_VID_PID, + NULL, 0, &response, sizeof(response)); + if (ret) { + dev_err(&connection->bundle->dev, + "Firmware get vid/pid operation failed (%d)\n", ret); + return; + } + + firmware->vendor_id = le32_to_cpu(response.vendor_id); + firmware->product_id = le32_to_cpu(response.product_id); +} + /* This returns path of the firmware blob on the disk */ static int download_firmware(struct gb_firmware *firmware, u8 stage) { @@ -41,7 +85,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) snprintf(firmware_name, sizeof(firmware_name), "ara:%08x:%08x:%08x:%08x:%02x.tftf", intf->unipro_mfg_id, intf->unipro_prod_id, - intf->vendor_id, intf->product_id, stage); + firmware->vendor_id, firmware->product_id, stage); return request_firmware(&firmware->fw, firmware_name, &connection->bundle->dev); @@ -183,6 +227,11 @@ static int gb_firmware_connection_init(struct gb_connection *connection) firmware->connection = connection; connection->private = firmware; + firmware->vendor_id = connection->intf->vendor_id; + firmware->product_id = connection->intf->product_id; + + firmware_es2_fixup_vid_pid(firmware); + /* * Module's Bootrom needs a way to know (currently), when to start * sending requests to the AP. The version request is sent before this diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9fcbbe4eb636..00593b0a3597 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -158,6 +158,7 @@ struct gb_control_disconnected_request { #define GB_FIRMWARE_TYPE_GET_FIRMWARE 0x03 #define GB_FIRMWARE_TYPE_READY_TO_BOOT 0x04 #define GB_FIRMWARE_TYPE_AP_READY 0x05 /* Request with no-payload */ +#define GB_FIRMWARE_TYPE_GET_VID_PID 0x06 /* Request with no-payload */ /* Greybus firmware boot stages */ #define GB_FIRMWARE_BOOT_STAGE_ONE 0x01 /* Reserved for the boot ROM */ @@ -197,6 +198,12 @@ struct gb_firmware_ready_to_boot_request { } __packed; /* Firmware protocol Ready to boot response has no payload */ +/* Firmware protocol get VID/PID request has no payload */ +struct gb_firmware_get_vid_pid_response { + __le32 vendor_id; + __le32 product_id; +} __packed; + /* Power Supply */ -- cgit v1.2.3-59-g8ed1b From d34a3643fe8d20b4e19779f82dfb29b5e6678a9d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Dec 2015 18:23:26 +0100 Subject: greybus: svc: clean up hotplug/unplug request handlers Clean up hotplug/unplug request handlers somewhat. Also add a debug message to both handlers that includes the interface id. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index f9ca08b34cd0..2c2dd8e66d7d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -480,22 +480,25 @@ free_svc_hotplug: static int gb_svc_intf_hotplug_recv(struct gb_operation *op) { struct gb_svc *svc = op->connection->private; - struct gb_message *request = op->request; + struct gb_svc_intf_hotplug_request *request; struct svc_hotplug *svc_hotplug; - if (request->payload_size < sizeof(svc_hotplug->data)) { + if (op->request->payload_size < sizeof(*request)) { dev_warn(&svc->dev, "short hotplug request received (%zu < %zu)\n", - request->payload_size, - sizeof(svc_hotplug->data)); + op->request->payload_size, sizeof(*request)); return -EINVAL; } + request = op->request->payload; + + dev_dbg(&svc->dev, "%s - id = %u\n", __func__, request->intf_id); + svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_KERNEL); if (!svc_hotplug) return -ENOMEM; svc_hotplug->connection = op->connection; - memcpy(&svc_hotplug->data, op->request->payload, sizeof(svc_hotplug->data)); + memcpy(&svc_hotplug->data, request, sizeof(svc_hotplug->data)); INIT_WORK(&svc_hotplug->work, svc_process_hotplug); queue_work(system_unbound_wq, &svc_hotplug->work); @@ -506,20 +509,21 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) { struct gb_svc *svc = op->connection->private; - struct gb_message *request = op->request; - struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload; + struct gb_svc_intf_hot_unplug_request *request; struct gb_host_device *hd = op->connection->hd; struct gb_interface *intf; u8 intf_id; - if (request->payload_size < sizeof(*hot_unplug)) { + if (op->request->payload_size < sizeof(*request)) { dev_warn(&svc->dev, "short hot unplug request received (%zu < %zu)\n", - request->payload_size, - sizeof(*hot_unplug)); + op->request->payload_size, sizeof(*request)); return -EINVAL; } - intf_id = hot_unplug->intf_id; + request = op->request->payload; + intf_id = request->intf_id; + + dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); intf = gb_interface_find(hd, intf_id); if (!intf) { -- cgit v1.2.3-59-g8ed1b From 24456a09a830dc44ace4d0db8e29d1e367937b58 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Dec 2015 18:23:27 +0100 Subject: greybus: svc: clean up deferred hotplug handler Rename the hotplug request message, and clarify that the message size has already been verified by the primary handler. Also add a debug message that includes the interface id. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 2c2dd8e66d7d..476914c88832 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -359,7 +359,7 @@ static void svc_process_hotplug(struct work_struct *work) { struct svc_hotplug *svc_hotplug = container_of(work, struct svc_hotplug, work); - struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data; + struct gb_svc_intf_hotplug_request *request; struct gb_connection *connection = svc_hotplug->connection; struct gb_svc *svc = connection->private; struct gb_host_device *hd = connection->hd; @@ -367,10 +367,11 @@ static void svc_process_hotplug(struct work_struct *work) u8 intf_id, device_id; int ret; - /* - * Grab the information we need. - */ - intf_id = hotplug->intf_id; + /* The request message size has already been verified. */ + request = &svc_hotplug->data; + intf_id = request->intf_id; + + dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); intf = gb_interface_find(hd, intf_id); if (intf) { @@ -404,10 +405,10 @@ static void svc_process_hotplug(struct work_struct *work) if (ret) goto destroy_interface; - intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); - intf->unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); - intf->vendor_id = le32_to_cpu(hotplug->data.ara_vend_id); - intf->product_id = le32_to_cpu(hotplug->data.ara_prod_id); + intf->unipro_mfg_id = le32_to_cpu(request->data.unipro_mfg_id); + intf->unipro_prod_id = le32_to_cpu(request->data.unipro_prod_id); + intf->vendor_id = le32_to_cpu(request->data.ara_vend_id); + intf->product_id = le32_to_cpu(request->data.ara_prod_id); /* * Create a device id for the interface: -- cgit v1.2.3-59-g8ed1b From b4ee82ece86d21c73213abae77e32abe40136eb4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Dec 2015 18:23:28 +0100 Subject: greybus: svc: clean up interface-remove helper Pass the svc rather than its connection to the interface remove helper. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 476914c88832..d974b3670ba6 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -333,10 +333,8 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } -static void svc_intf_remove(struct gb_connection *connection, - struct gb_interface *intf) +static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) { - struct gb_svc *svc = connection->private; u8 intf_id = intf->interface_id; u8 device_id; @@ -391,7 +389,7 @@ static void svc_process_hotplug(struct work_struct *work) */ dev_info(&svc->dev, "removing interface %hhu to add it again\n", intf_id); - svc_intf_remove(connection, intf); + gb_svc_intf_remove(svc, intf); } intf = gb_interface_create(hd, intf_id); @@ -533,7 +531,7 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) return -EINVAL; } - svc_intf_remove(op->connection, intf); + gb_svc_intf_remove(svc, intf); return 0; } -- cgit v1.2.3-59-g8ed1b From 9ae4109e5db02d4fae37bbd1399a8157efb583c4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Dec 2015 18:23:29 +0100 Subject: greybus: svc: generalise deferred request handling Clean up and generalise deferred request handling by simply storing a reference-counted pointer to the operation itself in the work context. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 78 +++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index d974b3670ba6..6428052b2c30 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -16,10 +16,9 @@ #define CPORT_FLAGS_CSV_N BIT(2) -struct svc_hotplug { +struct gb_svc_deferred_request { struct work_struct work; - struct gb_connection *connection; - struct gb_svc_intf_hotplug_request data; + struct gb_operation *operation; }; @@ -349,16 +348,10 @@ static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) ida_simple_remove(&svc->device_id_map, device_id); } -/* - * 'struct svc_hotplug' should be freed by svc_process_hotplug() before it - * returns, irrespective of success or Failure in bringing up the module. - */ -static void svc_process_hotplug(struct work_struct *work) +static void gb_svc_process_intf_hotplug(struct gb_operation *operation) { - struct svc_hotplug *svc_hotplug = container_of(work, struct svc_hotplug, - work); struct gb_svc_intf_hotplug_request *request; - struct gb_connection *connection = svc_hotplug->connection; + struct gb_connection *connection = operation->connection; struct gb_svc *svc = connection->private; struct gb_host_device *hd = connection->hd; struct gb_interface *intf; @@ -366,7 +359,7 @@ static void svc_process_hotplug(struct work_struct *work) int ret; /* The request message size has already been verified. */ - request = &svc_hotplug->data; + request = operation->request->payload; intf_id = request->intf_id; dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); @@ -396,7 +389,7 @@ static void svc_process_hotplug(struct work_struct *work) if (!intf) { dev_err(&svc->dev, "failed to create interface %hhu\n", intf_id); - goto free_svc_hotplug; + return; } ret = gb_svc_read_and_clear_module_boot_status(intf); @@ -450,7 +443,7 @@ static void svc_process_hotplug(struct work_struct *work) goto destroy_route; } - goto free_svc_hotplug; + return; destroy_route: gb_svc_route_destroy(svc, svc->ap_intf_id, intf_id); @@ -463,8 +456,48 @@ ida_put: ida_simple_remove(&svc->device_id_map, device_id); destroy_interface: gb_interface_remove(intf); -free_svc_hotplug: - kfree(svc_hotplug); +} + +static void gb_svc_process_deferred_request(struct work_struct *work) +{ + struct gb_svc_deferred_request *dr; + struct gb_operation *operation; + struct gb_svc *svc; + u8 type; + + dr = container_of(work, struct gb_svc_deferred_request, work); + operation = dr->operation; + svc = operation->connection->private; + type = operation->request->header->type; + + switch (type) { + case GB_SVC_TYPE_INTF_HOTPLUG: + gb_svc_process_intf_hotplug(operation); + break; + default: + dev_err(&svc->dev, "bad deferred request type: %02x\n", type); + } + + gb_operation_put(operation); + kfree(dr); +} + +static int gb_svc_queue_deferred_request(struct gb_operation *operation) +{ + struct gb_svc_deferred_request *dr; + + dr = kmalloc(sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + gb_operation_get(operation); + + dr->operation = operation; + INIT_WORK(&dr->work, gb_svc_process_deferred_request); + + queue_work(system_unbound_wq, &dr->work); + + return 0; } /* @@ -480,7 +513,6 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) { struct gb_svc *svc = op->connection->private; struct gb_svc_intf_hotplug_request *request; - struct svc_hotplug *svc_hotplug; if (op->request->payload_size < sizeof(*request)) { dev_warn(&svc->dev, "short hotplug request received (%zu < %zu)\n", @@ -492,17 +524,7 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) dev_dbg(&svc->dev, "%s - id = %u\n", __func__, request->intf_id); - svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_KERNEL); - if (!svc_hotplug) - return -ENOMEM; - - svc_hotplug->connection = op->connection; - memcpy(&svc_hotplug->data, request, sizeof(svc_hotplug->data)); - - INIT_WORK(&svc_hotplug->work, svc_process_hotplug); - queue_work(system_unbound_wq, &svc_hotplug->work); - - return 0; + return gb_svc_queue_deferred_request(op); } static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) -- cgit v1.2.3-59-g8ed1b From 57ccd4b08767bc3c44d3752bddcb6d6cad78830c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Dec 2015 18:23:30 +0100 Subject: greybus: svc: defer processing of hot-unplug requests Defer processing also of hot-unplug events. This is a step towards serialising hot-plug and unplug event processing. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 44 ++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 6428052b2c30..34713d6b0074 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -458,6 +458,30 @@ destroy_interface: gb_interface_remove(intf); } +static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) +{ + struct gb_svc *svc = operation->connection->private; + struct gb_svc_intf_hot_unplug_request *request; + struct gb_host_device *hd = operation->connection->hd; + struct gb_interface *intf; + u8 intf_id; + + /* The request message size has already been verified. */ + request = operation->request->payload; + intf_id = request->intf_id; + + dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); + + intf = gb_interface_find(hd, intf_id); + if (!intf) { + dev_warn(&svc->dev, "could not find hot-unplug interface %hhu\n", + intf_id); + return; + } + + gb_svc_intf_remove(svc, intf); +} + static void gb_svc_process_deferred_request(struct work_struct *work) { struct gb_svc_deferred_request *dr; @@ -474,6 +498,9 @@ static void gb_svc_process_deferred_request(struct work_struct *work) case GB_SVC_TYPE_INTF_HOTPLUG: gb_svc_process_intf_hotplug(operation); break; + case GB_SVC_TYPE_INTF_HOT_UNPLUG: + gb_svc_process_intf_hot_unplug(operation); + break; default: dev_err(&svc->dev, "bad deferred request type: %02x\n", type); } @@ -531,9 +558,6 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) { struct gb_svc *svc = op->connection->private; struct gb_svc_intf_hot_unplug_request *request; - struct gb_host_device *hd = op->connection->hd; - struct gb_interface *intf; - u8 intf_id; if (op->request->payload_size < sizeof(*request)) { dev_warn(&svc->dev, "short hot unplug request received (%zu < %zu)\n", @@ -542,20 +566,10 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) } request = op->request->payload; - intf_id = request->intf_id; - - dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); - intf = gb_interface_find(hd, intf_id); - if (!intf) { - dev_warn(&svc->dev, "could not find hot-unplug interface %hhu\n", - intf_id); - return -EINVAL; - } - - gb_svc_intf_remove(svc, intf); + dev_dbg(&svc->dev, "%s - id = %u\n", __func__, request->intf_id); - return 0; + return gb_svc_queue_deferred_request(op); } static int gb_svc_intf_reset_recv(struct gb_operation *op) -- cgit v1.2.3-59-g8ed1b From 3e48acac26c8b5321ff5b752c8650e1cf4d04344 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Dec 2015 18:23:31 +0100 Subject: greybus: svc: fix racy hotplug handling Fix racy hotplug handling by serialising all processing of hot-plug and unplug requests using a single-threaded dedicated workqueue. This fixes a reported crash during enumeration when processing multiple events. The current svc implementation does not handle concurrency at all (e.g. no interface list lock or refcounting) so we need to use the big hammer for now. Note that we will eventually want to process events for different interfaces in parallel, but that we'd still need a workqueue in order not to starve other svc requests (e.g. for timesync). Reported-by: Vaibhav Hiremath Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 12 ++++++++++-- drivers/staging/greybus/svc.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 34713d6b0074..af41e8e49c31 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -511,6 +511,7 @@ static void gb_svc_process_deferred_request(struct work_struct *work) static int gb_svc_queue_deferred_request(struct gb_operation *operation) { + struct gb_svc *svc = operation->connection->private; struct gb_svc_deferred_request *dr; dr = kmalloc(sizeof(*dr), GFP_KERNEL); @@ -522,7 +523,7 @@ static int gb_svc_queue_deferred_request(struct gb_operation *operation) dr->operation = operation; INIT_WORK(&dr->work, gb_svc_process_deferred_request); - queue_work(system_unbound_wq, &dr->work); + queue_work(svc->wq, &dr->work); return 0; } @@ -532,7 +533,7 @@ static int gb_svc_queue_deferred_request(struct gb_operation *operation) * initialization on the module side. Over that, we may also need to download * the firmware first and flash that on the module. * - * In order to make other hotplug events to not wait for all this to finish, + * In order not to make other svc events wait for all this to finish, * handle most of module hotplug stuff outside of the hotplug callback, with * help of a workqueue. */ @@ -658,6 +659,7 @@ static void gb_svc_release(struct device *dev) struct gb_svc *svc = to_gb_svc(dev); ida_destroy(&svc->device_id_map); + destroy_workqueue(svc->wq); kfree(svc); } @@ -675,6 +677,12 @@ static int gb_svc_connection_init(struct gb_connection *connection) if (!svc) return -ENOMEM; + svc->wq = alloc_workqueue("%s:svc", WQ_UNBOUND, 1, dev_name(&hd->dev)); + if (!svc->wq) { + kfree(svc); + return -ENOMEM; + } + svc->dev.parent = &hd->dev; svc->dev.bus = &greybus_bus_type; svc->dev.type = &greybus_svc_type; diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 99be0411d000..3acfa07cc73b 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -22,6 +22,7 @@ struct gb_svc { struct gb_connection *connection; enum gb_svc_state state; struct ida device_id_map; + struct workqueue_struct *wq; u16 endo_id; u8 ap_intf_id; -- cgit v1.2.3-59-g8ed1b From b455c84655d5bdfc5157d2100279e3ff2454b596 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 2 Dec 2015 11:12:27 +0000 Subject: greybus: spi: add rdwr field to transfer descriptor Add read and/or write field to transfer descriptor to make it possible to identify the type of transfer. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 3 +++ drivers/staging/greybus/spi.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 00593b0a3597..23d4cce8b727 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -695,6 +695,9 @@ struct gb_spi_transfer { __le16 delay_usecs; __u8 cs_change; __u8 bits_per_word; + __u8 rdwr; +#define GB_SPI_XFER_READ 0x01 +#define GB_SPI_XFER_WRITE 0x02 } __packed; struct gb_spi_transfer_request { diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 33e86f9c3182..1b9c97336451 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -107,13 +107,16 @@ gb_spi_operation_create(struct gb_connection *connection, gb_xfer->delay_usecs = cpu_to_le16(xfer->delay_usecs); gb_xfer->cs_change = xfer->cs_change; gb_xfer->bits_per_word = xfer->bits_per_word; - gb_xfer++; /* Copy tx data */ if (xfer->tx_buf) { memcpy(tx_data, xfer->tx_buf, xfer->len); tx_data += xfer->len; + gb_xfer->rdwr |= GB_SPI_XFER_WRITE; } + if (xfer->rx_buf) + gb_xfer->rdwr |= GB_SPI_XFER_READ; + gb_xfer++; } return operation; -- cgit v1.2.3-59-g8ed1b From b343f6afd7635c86e1c001f1623e07f218c694db Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 2 Dec 2015 11:12:28 +0000 Subject: greybus: spi: add master and device config operations Add master and device config operations, one is to merge all the master operations and the device config will allow to fetch and add devices for each chip select. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 31 +++---- drivers/staging/greybus/spi.c | 138 ++++++++++++---------------- 2 files changed, 74 insertions(+), 95 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 23d4cce8b727..e8c4bce96ee0 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -651,30 +651,29 @@ struct gb_pwm_disable_request { #define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */ /* Greybus spi operation types */ -#define GB_SPI_TYPE_MODE 0x02 -#define GB_SPI_TYPE_FLAGS 0x03 -#define GB_SPI_TYPE_BITS_PER_WORD_MASK 0x04 -#define GB_SPI_TYPE_NUM_CHIPSELECT 0x05 -#define GB_SPI_TYPE_TRANSFER 0x06 +#define GB_SPI_TYPE_MASTER_CONFIG 0x02 +#define GB_SPI_TYPE_DEVICE_CONFIG 0x03 +#define GB_SPI_TYPE_TRANSFER 0x04 /* mode request has no payload */ -struct gb_spi_mode_response { +struct gb_spi_master_config_response { + __le32 bits_per_word_mask; + __le32 min_speed_hz; + __le32 max_speed_hz; __le16 mode; -} __packed; - -/* flags request has no payload */ -struct gb_spi_flags_response { __le16 flags; + __le16 num_chipselect; } __packed; -/* bits-per-word request has no payload */ -struct gb_spi_bpw_response { - __le32 bits_per_word_mask; +struct gb_spi_device_config_request { + __le16 chip_select; } __packed; -/* num-chipselects request has no payload */ -struct gb_spi_chipselect_response { - __le16 num_chipselect; +struct gb_spi_device_config_response { + __le16 mode; + __u8 bits_per_word; + __le32 max_speed_hz; + __u8 name[32]; } __packed; /** diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 1b9c97336451..8e0e403795f4 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -1,8 +1,8 @@ /* * SPI bridge driver for the Greybus "generic" SPI module. * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. * * Released under the GPLv2 only. */ @@ -17,30 +17,20 @@ struct gb_spi { struct gb_connection *connection; - - /* Modes supported by spi controller */ u16 mode; - /* constraints of the spi controller */ u16 flags; - - /* - * copied from kernel: - * - * A mask indicating which values of bits_per_word are supported by the - * controller. Bit n indicates that a bits_per_word n+1 is suported. If - * set, the SPI core will reject any transfer with an unsupported - * bits_per_word. If not set, this value is simply ignored, and it's up - * to the individual driver to perform any validation. - */ u32 bits_per_word_mask; - - /* - * chipselects will be integral to many controllers; some others might - * use board-specific GPIOs. - */ u16 num_chipselect; + u32 min_speed_hz; + u32 max_speed_hz; + struct spi_device *spi_devices; }; +static struct spi_master *get_master_from_spi(struct gb_spi *spi) +{ + return spi->connection->private; +} + /* Routines to transfer data */ static struct gb_operation * gb_spi_operation_create(struct gb_connection *connection, @@ -181,7 +171,7 @@ static void gb_spi_cleanup(struct spi_device *spi) } -/* Routines to get controller infomation */ +/* Routines to get controller information */ /* * Map Greybus spi mode bits/flags/bpw into Linux ones. @@ -190,103 +180,86 @@ static void gb_spi_cleanup(struct spi_device *spi) #define gb_spi_mode_map(mode) mode #define gb_spi_flags_map(flags) flags -static int gb_spi_mode_operation(struct gb_spi *spi) +static int gb_spi_get_master_config(struct gb_spi *spi) { - struct gb_spi_mode_response response; - u16 mode; + struct gb_spi_master_config_response response; + u16 mode, flags; int ret; - ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_MODE, + ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_MASTER_CONFIG, NULL, 0, &response, sizeof(response)); - if (ret) + if (ret < 0) return ret; mode = le16_to_cpu(response.mode); spi->mode = gb_spi_mode_map(mode); - return 0; -} - -static int gb_spi_flags_operation(struct gb_spi *spi) -{ - struct gb_spi_flags_response response; - u16 flags; - int ret; - - ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_FLAGS, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - flags = le16_to_cpu(response.flags); spi->flags = gb_spi_flags_map(flags); - return 0; -} - -static int gb_spi_bpw_operation(struct gb_spi *spi) -{ - struct gb_spi_bpw_response response; - int ret; - - ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_BITS_PER_WORD_MASK, - NULL, 0, &response, sizeof(response)); - if (ret) - return ret; - spi->bits_per_word_mask = le32_to_cpu(response.bits_per_word_mask); + spi->num_chipselect = le16_to_cpu(response.num_chipselect); + + spi->min_speed_hz = le32_to_cpu(response.min_speed_hz); + spi->max_speed_hz = le32_to_cpu(response.max_speed_hz); return 0; } -static int gb_spi_chipselect_operation(struct gb_spi *spi) +static int gb_spi_setup_device(struct gb_spi *spi, uint16_t cs) { - struct gb_spi_chipselect_response response; + struct spi_master *master = get_master_from_spi(spi); + struct gb_spi_device_config_request request; + struct gb_spi_device_config_response response; + struct spi_board_info spi_board = { {0} }; + struct spi_device *spidev = &spi->spi_devices[cs]; int ret; - ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_NUM_CHIPSELECT, - NULL, 0, &response, sizeof(response)); - if (ret) + request.chip_select = cpu_to_le16(cs); + + ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_DEVICE_CONFIG, + &request, sizeof(request), + &response, sizeof(response)); + if (ret < 0) return ret; - spi->num_chipselect = le16_to_cpu(response.num_chipselect); + memcpy(spi_board.modalias, response.name, sizeof(spi_board.modalias)); + spi_board.mode = le16_to_cpu(response.mode); + spi_board.bus_num = master->bus_num; + spi_board.chip_select = cs; + spi_board.max_speed_hz = le32_to_cpu(response.max_speed_hz); + + spidev = spi_new_device(master, &spi_board); + if (!spidev) + ret = -EINVAL; return 0; } -/* - * Initialize the spi device. This includes verifying we can support it (based - * on the protocol version it advertises). If that's OK, we get and cached its - * mode bits & flags. - */ static int gb_spi_init(struct gb_spi *spi) { int ret; - /* mode never changes, just get it once */ - ret = gb_spi_mode_operation(spi); - if (ret) - return ret; - - /* flags never changes, just get it once */ - ret = gb_spi_flags_operation(spi); + /* get master configuration */ + ret = gb_spi_get_master_config(spi); if (ret) return ret; - /* total number of chipselects never changes, just get it once */ - ret = gb_spi_chipselect_operation(spi); - if (ret) - return ret; + spi->spi_devices = kcalloc(spi->num_chipselect, + sizeof(struct spi_device), GFP_KERNEL); + if (!spi->spi_devices) + return -ENOMEM; - /* bits-per-word-mask never changes, just get it once */ - return gb_spi_bpw_operation(spi); + return ret; } + static int gb_spi_connection_init(struct gb_connection *connection) { struct gb_spi *spi; struct spi_master *master; int ret; + int i; /* Allocate master with space for data */ master = spi_alloc_master(&connection->bundle->dev, sizeof(*spi)); @@ -315,8 +288,15 @@ static int gb_spi_connection_init(struct gb_connection *connection) master->transfer_one_message = gb_spi_transfer_one_message; ret = spi_register_master(master); - if (!ret) - return 0; + + /* now, fetch the devices configuration */ + for (i = 0; i < spi->num_chipselect; i++) { + ret = gb_spi_setup_device(spi, i); + if (ret < 0) + break; + } + + return ret; out_err: spi_master_put(master); -- cgit v1.2.3-59-g8ed1b From 11aa7121edfa4c32b06e9278e6d0e51d197d3560 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Thu, 3 Dec 2015 12:32:30 -0800 Subject: greybus: build: remove Android makefile There is no standard way of building a kernel from source in Android. Each device/SoC can (and do) implement it in their own way. To that end, let's remove this makefile and let each device define how they want to build the modules. Signed-off-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Android.mk | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 drivers/staging/greybus/Android.mk diff --git a/drivers/staging/greybus/Android.mk b/drivers/staging/greybus/Android.mk deleted file mode 100644 index 74948cd2c00b..000000000000 --- a/drivers/staging/greybus/Android.mk +++ /dev/null @@ -1,32 +0,0 @@ -.PHONY: build-greybus - -ifneq ($(TARGET_NO_KERNEL), true) -$(PRODUCT_OUT)/ramdisk.img: build-greybus -endif - -GREYBUS_MODULE_OUT_PATH := $(PRODUCT_OUT)/root/lib/modules -GREYBUS_CC_PREFIX := $(shell cd `dirname $(TARGET_TOOLS_PREFIX)` && pwd)/$(shell basename $(TARGET_TOOLS_PREFIX)) - -include $(CLEAR_VARS) -GREYBUS_SRC_PATH := $(ANDROID_BUILD_TOP)/external/greybus/ -LOCAL_PATH := $(GREYBUS_SRC_PATH) -LOCAL_SRC_FILES := greybus.ko -LOCAL_MODULE := $(LOCAL_SRC_FILES) -LOCAL_MODULE_CLASS := EXECUTABLES -LOCAL_MODULE_PATH := $(GREYBUS_MODULE_OUT_PATH) -$(LOCAL_PATH)/$(LOCAL_SRC_FILES): build-greybus -include $(BUILD_PREBUILT) - -KDIRARG := KERNELDIR="${ANDROID_PRODUCT_OUT}/obj/kernel" -ARCHARG := ARCH=$(TARGET_ARCH) -FLAGARG := EXTRA_CFLAGS+=-fno-pic -ARGS := $(KDIRARG) $(ARCHARG) $(FLAGARG) - -build-greybus: $(INSTALLED_KERNEL_TARGET) - make clean -C $(GREYBUS_SRC_PATH) - cd $(GREYBUS_SRC_PATH) &&\ - $(MAKE) -j$(MAKE_JOBS) CROSS_COMPILE=$(GREYBUS_CC_PREFIX) $(ARGS) - mkdir -p $(GREYBUS_MODULE_OUT_PATH) - ko=`find $(GREYBUS_SRC_PATH) -type f -name "*.ko"`;\ - for i in $$ko; do $(GREYBUS_CC_PREFIX)strip --strip-unneeded $$i;\ - mv $$i $(GREYBUS_MODULE_OUT_PATH)/; done; -- cgit v1.2.3-59-g8ed1b From 31bc2c9bcd3054b90322acf85a53aacbb9e29ee4 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 2 Dec 2015 11:12:29 +0000 Subject: greybus: spi: fix transfers bigger than greybus payload Add helper functions calculate the tx and rx size possible that fit a greybus payload size and change the operation creation to adjust to that. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/spi.c | 102 +++++++++++++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 8e0e403795f4..094d6d8c4f34 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -31,6 +31,51 @@ static struct spi_master *get_master_from_spi(struct gb_spi *spi) return spi->connection->private; } +static int tx_header_fit_operation(u32 tx_size, u32 count, size_t data_max) +{ + size_t headers_size; + + data_max -= sizeof(struct gb_spi_transfer_request); + headers_size = (count + 1) * sizeof(struct gb_spi_transfer); + + return tx_size + headers_size > data_max ? 0 : 1; +} + +static size_t calc_rx_xfer_size(u32 rx_size, u32 *tx_xfer_size, u32 len, + size_t data_max) +{ + size_t rx_xfer_size; + + data_max -= sizeof(struct gb_spi_transfer_response); + + if (rx_size + len > data_max) + rx_xfer_size = data_max - rx_size; + else + rx_xfer_size = len; + + /* if this is a write_read, for symmetry read the same as write */ + if (*tx_xfer_size && rx_xfer_size > *tx_xfer_size) + rx_xfer_size = *tx_xfer_size; + if (*tx_xfer_size && rx_xfer_size < *tx_xfer_size) + *tx_xfer_size = rx_xfer_size; + + return rx_xfer_size; +} + +static size_t calc_tx_xfer_size(u32 tx_size, u32 count, size_t len, + size_t data_max) +{ + size_t headers_size; + + data_max -= sizeof(struct gb_spi_transfer_request); + headers_size = (count + 1) * sizeof(struct gb_spi_transfer); + + if (tx_size + headers_size + len > data_max) + return data_max - (tx_size + sizeof(struct gb_spi_transfer)); + + return len; +} + /* Routines to transfer data */ static struct gb_operation * gb_spi_operation_create(struct gb_connection *connection, @@ -41,9 +86,14 @@ gb_spi_operation_create(struct gb_connection *connection, struct spi_transfer *xfer; struct gb_spi_transfer *gb_xfer; struct gb_operation *operation; - u32 tx_size = 0, rx_size = 0, count = 0, request_size; + struct spi_transfer *last_xfer = NULL; + u32 tx_size = 0, rx_size = 0, count = 0, xfer_len = 0, request_size; + u32 tx_xfer_size = 0, rx_xfer_size = 0, last_xfer_size = 0; + size_t data_max; void *tx_data; + data_max = gb_operation_get_payload_size_max(connection); + /* Find number of transfers queued and tx/rx length in the message */ list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!xfer->tx_buf && !xfer->rx_buf) { @@ -51,21 +101,33 @@ gb_spi_operation_create(struct gb_connection *connection, "bufferless transfer, length %u\n", xfer->len); return NULL; } + last_xfer = xfer; - if (xfer->tx_buf) - tx_size += xfer->len; - if (xfer->rx_buf) - rx_size += xfer->len; + tx_xfer_size = 0; + rx_xfer_size = 0; + + if (xfer->tx_buf) { + if (!tx_header_fit_operation(tx_size, count, data_max)) + break; + tx_xfer_size = calc_tx_xfer_size(tx_size, count, + xfer->len, data_max); + last_xfer_size = tx_xfer_size; + } + + if (xfer->rx_buf) { + rx_xfer_size = calc_rx_xfer_size(rx_size, &tx_xfer_size, + xfer->len, data_max); + last_xfer_size = rx_xfer_size; + } + + tx_size += tx_xfer_size; + rx_size += rx_xfer_size; - *total_len += xfer->len; + *total_len += last_xfer_size; count++; - } - /* Too many transfers ? */ - if (count > (u32)U16_MAX) { - dev_err(&connection->bundle->dev, - "transfer count (%u) too big\n", count); - return NULL; + if (xfer->len != last_xfer_size) + break; } /* @@ -92,20 +154,30 @@ gb_spi_operation_create(struct gb_connection *connection, /* Fill in the transfers array */ list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (last_xfer && xfer == last_xfer) + xfer_len = last_xfer_size; + else + xfer_len = xfer->len; + gb_xfer->speed_hz = cpu_to_le32(xfer->speed_hz); - gb_xfer->len = cpu_to_le32(xfer->len); + gb_xfer->len = cpu_to_le32(xfer_len); gb_xfer->delay_usecs = cpu_to_le16(xfer->delay_usecs); gb_xfer->cs_change = xfer->cs_change; gb_xfer->bits_per_word = xfer->bits_per_word; /* Copy tx data */ if (xfer->tx_buf) { - memcpy(tx_data, xfer->tx_buf, xfer->len); - tx_data += xfer->len; gb_xfer->rdwr |= GB_SPI_XFER_WRITE; + memcpy(tx_data, xfer->tx_buf, xfer_len); + tx_data += xfer_len; } + if (xfer->rx_buf) gb_xfer->rdwr |= GB_SPI_XFER_READ; + + if (last_xfer && xfer == last_xfer) + break; + gb_xfer++; } -- cgit v1.2.3-59-g8ed1b From e6420dac3d5d45c60886221afcf31ecaf255d992 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 3 Dec 2015 13:54:57 +0530 Subject: greybus: Documentation: Fix N-svc directory name endo_id was present in place of the directory N-svc, fix it. Fixes: 4f7b1833e78f ("Documentation/sysfs-bus-greybus: update the bus ABI documentation") Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 9329a31647cd..9a6bbc3a61fb 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -6,7 +6,7 @@ Description: The "root" greybus device for the Greybus device tree, or bus, where N is a dynamically assigned 1-based id. -What: /sys/bus/greybus/device/N-svc/endo_id +What: /sys/bus/greybus/device/N-svc Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman -- cgit v1.2.3-59-g8ed1b From 3f85c787b74c26f3816017e64288af907f291462 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 3 Dec 2015 16:07:47 +0000 Subject: greybus: lights: add v4l2 flash operations We do not implement any of the v4l2 flash operations, as the default ones are ok for now, however the init needs anything define, if not it will return an error. So, just define it and have an error free v4l2 flash init. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 3723a2ce29b3..01dcf18c9601 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -456,6 +456,8 @@ static void gb_lights_led_operations_set(struct gb_channel *channel, #ifdef V4L2_HAVE_FLASH /* V4L2 specific helpers */ +static const struct v4l2_flash_ops v4l2_flash_ops; + static void __gb_lights_channel_v4l2_config(struct led_flash_setting *channel_s, struct led_flash_setting *v4l2_s) { @@ -507,7 +509,7 @@ static int gb_lights_light_v4l2_register(struct gb_light *light) LED_FAULT_LED_OVER_TEMPERATURE; light->v4l2_flash = v4l2_flash_init(dev, NULL, fled, iled, - NULL, sd_cfg); + &v4l2_flash_ops, sd_cfg); if (IS_ERR_OR_NULL(light->v4l2_flash)) { ret = PTR_ERR(light->v4l2_flash); goto out_free; -- cgit v1.2.3-59-g8ed1b From d644181fe60f4fcfca7e9e9e9b0503993e391e69 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 3 Dec 2015 16:07:48 +0000 Subject: greybus: lights: avoid channel torch double free When attaching torch to a flash we release the channel torch resources, but afterwards we do it again when releasing all the channels. Just free all the resource at channel release. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 01dcf18c9601..c49cc7ea4b28 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -681,9 +681,6 @@ static int __gb_lights_channel_torch_attach(struct gb_channel *channel, kfree(channel->led->name); channel->led->name = name; - /* free original torch channel resources */ - gb_lights_channel_free(channel_torch); - channel_torch->led = channel->led; return 0; -- cgit v1.2.3-59-g8ed1b From 1700507d8c1b3578152c8afa8062a66e78e3a4bb Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 3 Dec 2015 16:07:49 +0000 Subject: greybus: lights: default value for v4l2 flash controls V4l2 flash will return erro ERANGE if val(which is the default value) is not defined. Just set it to the max value reported by the module. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index c49cc7ea4b28..153c4f5479f0 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -702,6 +702,7 @@ static int __gb_lights_flash_led_register(struct gb_channel *channel) fset->min = channel->intensity_uA.min; fset->max = channel->intensity_uA.max; fset->step = channel->intensity_uA.step; + fset->val = channel->intensity_uA.max; /* Only the flash mode have the timeout constraints settings */ if (channel->mode & GB_CHANNEL_MODE_FLASH) { @@ -709,6 +710,7 @@ static int __gb_lights_flash_led_register(struct gb_channel *channel) fset->min = channel->timeout_us.min; fset->max = channel->timeout_us.max; fset->step = channel->timeout_us.step; + fset->val = channel->timeout_us.max; } /* -- cgit v1.2.3-59-g8ed1b From d9fb3754ecf807f6c6bbe63ff2fbd29e00fad131 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 3 Dec 2015 17:29:38 +0000 Subject: greybus: loopback: Relax locking during loopback operations Currently a per-connection mutex is held during calls to gb_operation_send_sync. It is not necessary to hold this lock and later patches supporting multiple-outstanding bi-directional operations need to take the per-connection lock and the gb_dev level lock. Since gb_dev must always be taken before per-connection locks, it is both desirable and safe to drop the lock now. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ef16ca579ca1..e0be130afb70 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -774,6 +774,8 @@ static int gb_loopback_fn(void *data) mutex_unlock(&gb->mutex); goto sleep; } + mutex_unlock(&gb->mutex); + /* Else operations to perform */ gb->apbridge_latency_ts = 0; gb->gpbridge_latency_ts = 0; @@ -783,7 +785,6 @@ static int gb_loopback_fn(void *data) error = gb_loopback_transfer(gb, size); else if (type == GB_LOOPBACK_TYPE_SINK) error = gb_loopback_sink(gb, size); - mutex_unlock(&gb->mutex); mutex_lock(&gb_dev.mutex); mutex_lock(&gb->mutex); -- cgit v1.2.3-59-g8ed1b From d6a1a3b5ea08c7828a177b48e6e39943b0fadf3c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 3 Dec 2015 17:29:39 +0000 Subject: greybus: loopback: Move latency_ts initialization to transfer routine Initializing the bridge specific latency variables is only relevant to the transfer operation, so make it loopback-transfer specific. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index e0be130afb70..7d582cd8127b 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -463,6 +463,9 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) struct gb_loopback_transfer_response *response; int retval; + gb->apbridge_latency_ts = 0; + gb->gpbridge_latency_ts = 0; + request = kmalloc(len + sizeof(*request), GFP_KERNEL); if (!request) return -ENOMEM; @@ -777,8 +780,6 @@ static int gb_loopback_fn(void *data) mutex_unlock(&gb->mutex); /* Else operations to perform */ - gb->apbridge_latency_ts = 0; - gb->gpbridge_latency_ts = 0; if (type == GB_LOOPBACK_TYPE_PING) error = gb_loopback_ping(gb); else if (type == GB_LOOPBACK_TYPE_TRANSFER) -- cgit v1.2.3-59-g8ed1b From 9673dcebe472f298ba9781db84c5880d55372cc9 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 3 Dec 2015 17:29:40 +0000 Subject: greybus: loopback: Drop unused timeval variables start and end aren't used and should be dropped. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 7d582cd8127b..c1943f2b83fb 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -53,9 +53,6 @@ struct gb_loopback_device { int ms_wait; u32 error; - struct timeval start; - struct timeval end; - /* Overall stats */ struct gb_loopback_stats latency; struct gb_loopback_stats throughput; @@ -577,8 +574,6 @@ static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev) } /* Reset aggregate stats */ - memset(&gb_dev->start, 0, sizeof(struct timeval)); - memset(&gb_dev->end, 0, sizeof(struct timeval)); memcpy(&gb_dev->latency, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb_dev->throughput, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb_dev->requests_per_second, &reset, -- cgit v1.2.3-59-g8ed1b From 8e1d6c336d74977403b0d7ca81e1e9a098518ff0 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 3 Dec 2015 17:29:41 +0000 Subject: greybus: loopback: drop bus aggregate calculation At some point we had a statement in a Jira work item to pull out 'bus level' data from greybus and to have messages to different interfaces be synchronized with respect to each other. Synchronizing threads with respect to each other is slow and it turns out we can get the same 'bus level' stastics by making the user-space test application smarter. That's great news for the in-kernel code since it means we can cut out a whole lot of code to-do with calculating 'bus level' aggregate data and we can stop forcing threads to hit a rendezvous before sending out another loopback operation. So this patch drops bus level aggregates in favour of doing that in user-space. It subtracts a lot of code and cycles that in practice nobody cares about anyway. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 474 +++++++++++-------------------------- 1 file changed, 133 insertions(+), 341 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index c1943f2b83fb..8524ce128738 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -36,29 +36,11 @@ struct gb_loopback_stats { struct gb_loopback_device { struct dentry *root; - struct dentry *file; u32 count; + size_t size_max; - struct kfifo kfifo; struct mutex mutex; struct list_head list; - wait_queue_head_t wq; - - int type; - u32 mask; - u32 size; - u32 iteration_max; - u32 iteration_count; - size_t size_max; - int ms_wait; - u32 error; - - /* Overall stats */ - struct gb_loopback_stats latency; - struct gb_loopback_stats throughput; - struct gb_loopback_stats requests_per_second; - struct gb_loopback_stats apbridge_unipro_latency; - struct gb_loopback_stats gpbridge_firmware_latency; }; static struct gb_loopback_device gb_dev; @@ -72,6 +54,7 @@ struct gb_loopback { struct mutex mutex; struct task_struct *task; struct list_head entry; + wait_queue_head_t wq; /* Per connection stats */ struct gb_loopback_stats latency; @@ -80,10 +63,15 @@ struct gb_loopback { struct gb_loopback_stats apbridge_unipro_latency; struct gb_loopback_stats gpbridge_firmware_latency; - u32 lbid; + int type; + u32 mask; + u32 size; + u32 iteration_max; u32 iteration_count; - u64 elapsed_nsecs; + int ms_wait; u32 error; + u32 lbid; + u64 elapsed_nsecs; u32 apbridge_latency_ts; u32 gpbridge_latency_ts; }; @@ -99,42 +87,34 @@ module_param(kfifo_depth, uint, 0444); #define GB_LOOPBACK_MS_WAIT_MAX 1000 /* interface sysfs attributes */ -#define gb_loopback_ro_attr(field, pfx, conn) \ -static ssize_t field##_##pfx##_show(struct device *dev, \ +#define gb_loopback_ro_attr(field) \ +static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_bundle *bundle; \ struct gb_loopback *gb; \ - if (conn) { \ - bundle = to_gb_bundle(dev); \ - gb = bundle->private; \ - return sprintf(buf, "%u\n", gb->field); \ - } else { \ - return sprintf(buf, "%u\n", gb_dev.field); \ - } \ + bundle = to_gb_bundle(dev); \ + gb = bundle->private; \ + return sprintf(buf, "%u\n", gb->field); \ } \ -static DEVICE_ATTR_RO(field##_##pfx) +static DEVICE_ATTR_RO(field) -#define gb_loopback_ro_stats_attr(name, field, type, pfx, conn) \ -static ssize_t name##_##field##_##pfx##_show(struct device *dev, \ +#define gb_loopback_ro_stats_attr(name, field, type) \ +static ssize_t name##_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_bundle *bundle; \ struct gb_loopback *gb; \ - if (conn) { \ - bundle = to_gb_bundle(dev); \ - gb = bundle->private; \ - return sprintf(buf, "%"#type"\n", gb->name.field); \ - } else { \ - return sprintf(buf, "%"#type"\n", gb_dev.name.field); \ - } \ + bundle = to_gb_bundle(dev); \ + gb = bundle->private; \ + return sprintf(buf, "%"#type"\n", gb->name.field); \ } \ -static DEVICE_ATTR_RO(name##_##field##_##pfx) +static DEVICE_ATTR_RO(name##_##field) -#define gb_loopback_ro_avg_attr(name, pfx, conn) \ -static ssize_t name##_avg_##pfx##_show(struct device *dev, \ +#define gb_loopback_ro_avg_attr(name) \ +static ssize_t name##_avg_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ @@ -143,24 +123,20 @@ static ssize_t name##_avg_##pfx##_show(struct device *dev, \ struct gb_loopback *gb; \ u64 avg; \ u32 count, rem; \ - if (conn) { \ - bundle = to_gb_bundle(dev); \ - gb = bundle->private; \ - stats = &gb->name; \ - } else { \ - stats = &gb_dev.name; \ - } \ + bundle = to_gb_bundle(dev); \ + gb = bundle->private; \ + stats = &gb->name; \ count = stats->count ? stats->count : 1; \ avg = stats->sum + count / 2; /* round closest */ \ rem = do_div(avg, count); \ return sprintf(buf, "%llu.%06u\n", avg, 1000000 * rem / count); \ } \ -static DEVICE_ATTR_RO(name##_avg_##pfx) +static DEVICE_ATTR_RO(name##_avg) -#define gb_loopback_stats_attrs(field, pfx, conn) \ - gb_loopback_ro_stats_attr(field, min, u, pfx, conn); \ - gb_loopback_ro_stats_attr(field, max, u, pfx, conn); \ - gb_loopback_ro_avg_attr(field, pfx, conn) +#define gb_loopback_stats_attrs(field) \ + gb_loopback_ro_stats_attr(field, min, u); \ + gb_loopback_ro_stats_attr(field, max, u); \ + gb_loopback_ro_avg_attr(field) #define gb_loopback_attr(field, type) \ static ssize_t field##_show(struct device *dev, \ @@ -178,13 +154,14 @@ static ssize_t field##_store(struct device *dev, \ { \ int ret; \ struct gb_bundle *bundle = to_gb_bundle(dev); \ - mutex_lock(&gb_dev.mutex); \ + struct gb_loopback *gb = bundle->private; \ + mutex_lock(&gb->mutex); \ ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ len = -EINVAL; \ else \ - gb_loopback_check_attr(&gb_dev, bundle); \ - mutex_unlock(&gb_dev.mutex); \ + gb_loopback_check_attr(gb, bundle); \ + mutex_unlock(&gb->mutex); \ return len; \ } \ static DEVICE_ATTR_RW(field) @@ -194,7 +171,9 @@ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - return sprintf(buf, "%u\n", gb_dev.field); \ + struct gb_bundle *bundle = to_gb_bundle(dev); \ + struct gb_loopback *gb = bundle->private; \ + return sprintf(buf, "%u\n", gb->field); \ } \ static DEVICE_ATTR_RO(field) @@ -203,7 +182,9 @@ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - return sprintf(buf, "%"#type"\n", gb_dev.field); \ + struct gb_bundle *bundle = to_gb_bundle(dev); \ + struct gb_loopback *gb = bundle->private; \ + return sprintf(buf, "%"#type"\n", gb->field); \ } \ static ssize_t field##_store(struct device *dev, \ struct device_attribute *attr, \ @@ -212,76 +193,63 @@ static ssize_t field##_store(struct device *dev, \ { \ int ret; \ struct gb_bundle *bundle = to_gb_bundle(dev); \ - mutex_lock(&gb_dev.mutex); \ - ret = sscanf(buf, "%"#type, &gb_dev.field); \ + struct gb_loopback *gb = bundle->private; \ + mutex_lock(&gb->mutex); \ + ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ len = -EINVAL; \ else \ - gb_loopback_check_attr(&gb_dev, bundle); \ - mutex_unlock(&gb_dev.mutex); \ + gb_loopback_check_attr(gb, bundle); \ + mutex_unlock(&gb->mutex); \ return len; \ } \ static DEVICE_ATTR_RW(field) -static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev); -static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, +static void gb_loopback_reset_stats(struct gb_loopback *gb); +static void gb_loopback_check_attr(struct gb_loopback *gb, struct gb_bundle *bundle) { - struct gb_loopback *gb; - - if (gb_dev->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) - gb_dev->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; - if (gb_dev->size > gb_dev->size_max) - gb_dev->size = gb_dev->size_max; - gb_dev->iteration_count = 0; - gb_dev->error = 0; - - list_for_each_entry(gb, &gb_dev->list, entry) { - mutex_lock(&gb->mutex); - gb->iteration_count = 0; - gb->error = 0; - if (kfifo_depth < gb_dev->iteration_max) { - dev_warn(&bundle->dev, - "cannot log bytes %u kfifo_depth %u\n", - gb_dev->iteration_max, kfifo_depth); - } - kfifo_reset_out(&gb->kfifo_lat); - kfifo_reset_out(&gb->kfifo_ts); - mutex_unlock(&gb->mutex); + if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) + gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; + if (gb->size > gb_dev.size_max) + gb->size = gb_dev.size_max; + gb->iteration_count = 0; + gb->error = 0; + + if (kfifo_depth < gb->iteration_max) { + dev_warn(&bundle->dev, + "cannot log bytes %u kfifo_depth %u\n", + gb->iteration_max, kfifo_depth); } + kfifo_reset_out(&gb->kfifo_lat); + kfifo_reset_out(&gb->kfifo_ts); - switch (gb_dev->type) { + switch (gb->type) { case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: case GB_LOOPBACK_TYPE_SINK: - kfifo_reset_out(&gb_dev->kfifo); - gb_loopback_reset_stats(gb_dev); - wake_up(&gb_dev->wq); + gb_loopback_reset_stats(gb); + wake_up(&gb->wq); break; default: - gb_dev->type = 0; + gb->type = 0; break; } } /* Time to send and receive one message */ -gb_loopback_stats_attrs(latency, dev, false); -gb_loopback_stats_attrs(latency, con, true); +gb_loopback_stats_attrs(latency); /* Number of requests sent per second on this cport */ -gb_loopback_stats_attrs(requests_per_second, dev, false); -gb_loopback_stats_attrs(requests_per_second, con, true); +gb_loopback_stats_attrs(requests_per_second); /* Quantity of data sent and received on this cport */ -gb_loopback_stats_attrs(throughput, dev, false); -gb_loopback_stats_attrs(throughput, con, true); +gb_loopback_stats_attrs(throughput); /* Latency across the UniPro link from APBridge's perspective */ -gb_loopback_stats_attrs(apbridge_unipro_latency, dev, false); -gb_loopback_stats_attrs(apbridge_unipro_latency, con, true); +gb_loopback_stats_attrs(apbridge_unipro_latency); /* Firmware induced overhead in the GPBridge */ -gb_loopback_stats_attrs(gpbridge_firmware_latency, dev, false); -gb_loopback_stats_attrs(gpbridge_firmware_latency, con, true); +gb_loopback_stats_attrs(gpbridge_firmware_latency); + /* Number of errors encountered during loop */ -gb_loopback_ro_attr(error, dev, false); -gb_loopback_ro_attr(error, con, true); +gb_loopback_ro_attr(error); /* * Type of loopback message to send based on protocol type definitions @@ -303,53 +271,32 @@ gb_dev_loopback_ro_attr(iteration_count, false); /* A bit-mask of destination connecitons to include in the test run */ gb_dev_loopback_rw_attr(mask, u); -static struct attribute *loopback_dev_attrs[] = { - &dev_attr_latency_min_dev.attr, - &dev_attr_latency_max_dev.attr, - &dev_attr_latency_avg_dev.attr, - &dev_attr_requests_per_second_min_dev.attr, - &dev_attr_requests_per_second_max_dev.attr, - &dev_attr_requests_per_second_avg_dev.attr, - &dev_attr_throughput_min_dev.attr, - &dev_attr_throughput_max_dev.attr, - &dev_attr_throughput_avg_dev.attr, - &dev_attr_apbridge_unipro_latency_min_dev.attr, - &dev_attr_apbridge_unipro_latency_max_dev.attr, - &dev_attr_apbridge_unipro_latency_avg_dev.attr, - &dev_attr_gpbridge_firmware_latency_min_dev.attr, - &dev_attr_gpbridge_firmware_latency_max_dev.attr, - &dev_attr_gpbridge_firmware_latency_avg_dev.attr, +static struct attribute *loopback_attrs[] = { + &dev_attr_latency_min.attr, + &dev_attr_latency_max.attr, + &dev_attr_latency_avg.attr, + &dev_attr_requests_per_second_min.attr, + &dev_attr_requests_per_second_max.attr, + &dev_attr_requests_per_second_avg.attr, + &dev_attr_throughput_min.attr, + &dev_attr_throughput_max.attr, + &dev_attr_throughput_avg.attr, + &dev_attr_apbridge_unipro_latency_min.attr, + &dev_attr_apbridge_unipro_latency_max.attr, + &dev_attr_apbridge_unipro_latency_avg.attr, + &dev_attr_gpbridge_firmware_latency_min.attr, + &dev_attr_gpbridge_firmware_latency_max.attr, + &dev_attr_gpbridge_firmware_latency_avg.attr, &dev_attr_type.attr, &dev_attr_size.attr, &dev_attr_ms_wait.attr, &dev_attr_iteration_count.attr, &dev_attr_iteration_max.attr, &dev_attr_mask.attr, - &dev_attr_error_dev.attr, + &dev_attr_error.attr, NULL, }; -ATTRIBUTE_GROUPS(loopback_dev); - -static struct attribute *loopback_con_attrs[] = { - &dev_attr_latency_min_con.attr, - &dev_attr_latency_max_con.attr, - &dev_attr_latency_avg_con.attr, - &dev_attr_requests_per_second_min_con.attr, - &dev_attr_requests_per_second_max_con.attr, - &dev_attr_requests_per_second_avg_con.attr, - &dev_attr_throughput_min_con.attr, - &dev_attr_throughput_max_con.attr, - &dev_attr_throughput_avg_con.attr, - &dev_attr_apbridge_unipro_latency_min_con.attr, - &dev_attr_apbridge_unipro_latency_max_con.attr, - &dev_attr_apbridge_unipro_latency_avg_con.attr, - &dev_attr_gpbridge_firmware_latency_min_con.attr, - &dev_attr_gpbridge_firmware_latency_max_con.attr, - &dev_attr_gpbridge_firmware_latency_avg_con.attr, - &dev_attr_error_con.attr, - NULL, -}; -ATTRIBUTE_GROUPS(loopback_con); +ATTRIBUTE_GROUPS(loopback); static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs) { @@ -385,11 +332,6 @@ static void gb_loopback_push_latency_ts(struct gb_loopback *gb, kfifo_in(&gb->kfifo_ts, (unsigned char *)te, sizeof(*te)); } -static int gb_loopback_active(struct gb_loopback *gb) -{ - return (gb_dev.mask == 0 || (gb_dev.mask & gb->lbid)); -} - static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, void *request, int request_size, void *response, int response_size) @@ -550,37 +492,32 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) } } -static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev) +static void gb_loopback_reset_stats(struct gb_loopback *gb) { struct gb_loopback_stats reset = { .min = U32_MAX, }; - struct gb_loopback *gb; /* Reset per-connection stats */ - list_for_each_entry(gb, &gb_dev->list, entry) { - mutex_lock(&gb->mutex); - memcpy(&gb->latency, &reset, - sizeof(struct gb_loopback_stats)); - memcpy(&gb->throughput, &reset, - sizeof(struct gb_loopback_stats)); - memcpy(&gb->requests_per_second, &reset, - sizeof(struct gb_loopback_stats)); - memcpy(&gb->apbridge_unipro_latency, &reset, - sizeof(struct gb_loopback_stats)); - memcpy(&gb->gpbridge_firmware_latency, &reset, - sizeof(struct gb_loopback_stats)); - mutex_unlock(&gb->mutex); - } + memcpy(&gb->latency, &reset, + sizeof(struct gb_loopback_stats)); + memcpy(&gb->throughput, &reset, + sizeof(struct gb_loopback_stats)); + memcpy(&gb->requests_per_second, &reset, + sizeof(struct gb_loopback_stats)); + memcpy(&gb->apbridge_unipro_latency, &reset, + sizeof(struct gb_loopback_stats)); + memcpy(&gb->gpbridge_firmware_latency, &reset, + sizeof(struct gb_loopback_stats)); /* Reset aggregate stats */ - memcpy(&gb_dev->latency, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb_dev->throughput, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb_dev->requests_per_second, &reset, + memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); + memcpy(&gb->requests_per_second, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb_dev->apbridge_unipro_latency, &reset, + memcpy(&gb->apbridge_unipro_latency, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb_dev->gpbridge_firmware_latency, &reset, + memcpy(&gb->gpbridge_firmware_latency, &reset, sizeof(struct gb_loopback_stats)); } @@ -599,7 +536,6 @@ static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency) u32 req = USEC_PER_SEC; do_div(req, latency); - gb_loopback_update_stats(&gb_dev.requests_per_second, req); gb_loopback_update_stats(&gb->requests_per_second, req); } @@ -608,17 +544,17 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) u32 throughput; u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2; - switch (gb_dev.type) { + switch (gb->type) { case GB_LOOPBACK_TYPE_PING: break; case GB_LOOPBACK_TYPE_SINK: aggregate_size += sizeof(struct gb_loopback_transfer_request) + - gb_dev.size; + gb->size; break; case GB_LOOPBACK_TYPE_TRANSFER: aggregate_size += sizeof(struct gb_loopback_transfer_request) + sizeof(struct gb_loopback_transfer_response) + - gb_dev.size * 2; + gb->size * 2; break; default: return; @@ -628,65 +564,9 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) throughput = USEC_PER_SEC; do_div(throughput, latency); throughput *= aggregate_size; - gb_loopback_update_stats(&gb_dev.throughput, throughput); gb_loopback_update_stats(&gb->throughput, throughput); } -static int gb_loopback_calculate_aggregate_stats(void) -{ - struct gb_loopback *gb; - struct timeval ts; - struct timeval te; - u64 t1, t2; - u64 ts_min; - u64 te_max; - u64 elapsed_nsecs; - u32 lat; - int i, latched; - int rollover = 0; - - for (i = 0; i < gb_dev.iteration_max; i++) { - latched = 0; - ts_min = 0; - te_max = 0; - list_for_each_entry(gb, &gb_dev.list, entry) { - if (!gb_loopback_active(gb)) - continue; - if (kfifo_out(&gb->kfifo_ts, &ts, sizeof(ts)) < sizeof(ts)) - goto error; - if (kfifo_out(&gb->kfifo_ts, &te, sizeof(te)) < sizeof(te)) - goto error; - t1 = timeval_to_ns(&ts); - t2 = timeval_to_ns(&te); - - /* minimum timestamp is always what we want */ - if (latched == 0 || t1 < ts_min) - ts_min = t1; - - /* maximum timestamp needs to handle rollover */ - if (t2 > t1) { - if (latched == 0 || t2 > te_max) - te_max = t2; - } else { - if (latched == 0 || rollover == 0) - te_max = t2; - if (rollover == 1 && t2 > te_max) - te_max = t2; - rollover = 1; - } - latched = 1; - } - /* Calculate the aggregate timestamp */ - elapsed_nsecs = __gb_loopback_calc_latency(ts_min, te_max); - lat = gb_loopback_nsec_to_usec_latency(elapsed_nsecs); - kfifo_in(&gb_dev.kfifo, (unsigned char *)&lat, sizeof(lat)); - } - return 0; -error: - kfifo_reset_out(&gb_dev.kfifo); - return -ENOMEM; -} - static void gb_loopback_calculate_stats(struct gb_loopback *gb) { u32 lat; @@ -695,7 +575,6 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) lat = gb_loopback_nsec_to_usec_latency(gb->elapsed_nsecs); /* Log latency stastic */ - gb_loopback_update_stats(&gb_dev.latency, lat); gb_loopback_update_stats(&gb->latency, lat); /* Raw latency log on a per thread basis */ @@ -706,12 +585,8 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) gb_loopback_requests_update(gb, lat); /* Log the firmware supplied latency values */ - gb_loopback_update_stats(&gb_dev.apbridge_unipro_latency, - gb->apbridge_latency_ts); gb_loopback_update_stats(&gb->apbridge_unipro_latency, gb->apbridge_latency_ts); - gb_loopback_update_stats(&gb_dev.gpbridge_firmware_latency, - gb->gpbridge_latency_ts); gb_loopback_update_stats(&gb->gpbridge_firmware_latency, gb->gpbridge_latency_ts); } @@ -722,56 +597,29 @@ static int gb_loopback_fn(void *data) int ms_wait = 0; int type; u32 size; - u32 low_count; struct gb_loopback *gb = data; - struct gb_loopback *gb_list; while (1) { - if (!gb_dev.type) - wait_event_interruptible(gb_dev.wq, gb_dev.type || + if (!gb->type) + wait_event_interruptible(gb->wq, gb->type || kthread_should_stop()); if (kthread_should_stop()) break; - mutex_lock(&gb_dev.mutex); - if (!gb_loopback_active(gb)) { - ms_wait = 100; - goto unlock_continue; - } - if (gb_dev.iteration_max) { - /* Determine overall lowest count */ - low_count = gb->iteration_count; - list_for_each_entry(gb_list, &gb_dev.list, entry) { - if (!gb_loopback_active(gb_list)) - continue; - if (gb_list->iteration_count < low_count) - low_count = gb_list->iteration_count; - } - /* All threads achieved at least low_count iterations */ - if (gb_dev.iteration_count < low_count) { - gb_dev.iteration_count = low_count; - sysfs_notify(&gb->connection->bundle->dev.kobj, - NULL, "iteration_count"); - } - /* Optionally terminate */ - if (gb_dev.iteration_count == gb_dev.iteration_max) { - gb_loopback_calculate_aggregate_stats(); - gb_dev.type = 0; - goto unlock_continue; - } - } - size = gb_dev.size; - ms_wait = gb_dev.ms_wait; - type = gb_dev.type; - mutex_unlock(&gb_dev.mutex); - mutex_lock(&gb->mutex); - if (gb->iteration_count >= gb_dev.iteration_max) { - /* If this thread finished before siblings then sleep */ - ms_wait = 1; + + sysfs_notify(&gb->connection->bundle->dev.kobj, + NULL, "iteration_count"); + + /* Optionally terminate */ + if (gb->iteration_count == gb->iteration_max) { + gb->type = 0; mutex_unlock(&gb->mutex); - goto sleep; + continue; } + size = gb->size; + ms_wait = gb->ms_wait; + type = gb->type; mutex_unlock(&gb->mutex); /* Else operations to perform */ @@ -782,20 +630,12 @@ static int gb_loopback_fn(void *data) else if (type == GB_LOOPBACK_TYPE_SINK) error = gb_loopback_sink(gb, size); - mutex_lock(&gb_dev.mutex); - mutex_lock(&gb->mutex); - - if (error) { - gb_dev.error++; + if (error) gb->error++; - } + gb_loopback_calculate_stats(gb); gb->iteration_count++; - mutex_unlock(&gb->mutex); -unlock_continue: - mutex_unlock(&gb_dev.mutex); -sleep: if (ms_wait) msleep(ms_wait); } @@ -846,27 +686,6 @@ static const struct file_operations gb_loopback_debugfs_latency_ops = { .release = single_release, }; -static int gb_loopback_dbgfs_dev_latency_show(struct seq_file *s, void *unused) -{ - struct gb_loopback_device *gb_dev = s->private; - - return gb_loopback_dbgfs_latency_show_common(s, &gb_dev->kfifo, - &gb_dev->mutex); -} - -static int gb_loopback_dev_latency_open(struct inode *inode, struct file *file) -{ - return single_open(file, gb_loopback_dbgfs_dev_latency_show, - inode->i_private); -} - -static const struct file_operations gb_loopback_debugfs_dev_latency_ops = { - .open = gb_loopback_dev_latency_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static int gb_loopback_bus_id_compare(void *priv, struct list_head *lha, struct list_head *lhb) { @@ -912,31 +731,22 @@ static int gb_loopback_connection_init(struct gb_connection *connection) struct gb_loopback *gb; int retval; char name[DEBUGFS_NAMELEN]; - struct kobject *kobj = &connection->bundle->dev.kobj; gb = kzalloc(sizeof(*gb), GFP_KERNEL); if (!gb) return -ENOMEM; - gb_loopback_reset_stats(&gb_dev); - /* If this is the first connection - create a per-bus entry */ + init_waitqueue_head(&gb->wq); + gb_loopback_reset_stats(gb); + mutex_lock(&gb_dev.mutex); if (!gb_dev.count) { - snprintf(name, sizeof(name), "raw_latency_%d", - connection->bundle->intf->hd->bus_id); - gb_dev.file = debugfs_create_file(name, S_IFREG | S_IRUGO, - gb_dev.root, &gb_dev, - &gb_loopback_debugfs_dev_latency_ops); - retval = sysfs_create_groups(kobj, loopback_dev_groups); - if (retval) - goto out_sysfs; - /* Calculate maximum payload */ gb_dev.size_max = gb_operation_get_payload_size_max(connection); if (gb_dev.size_max <= sizeof(struct gb_loopback_transfer_request)) { retval = -EINVAL; - goto out_sysfs_dev; + goto out_sysfs; } gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request); } @@ -949,9 +759,9 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb->connection = connection; connection->bundle->private = gb; retval = sysfs_create_groups(&connection->bundle->dev.kobj, - loopback_con_groups); + loopback_groups); if (retval) - goto out_sysfs_dev; + goto out_sysfs; /* Allocate kfifo */ if (kfifo_alloc(&gb->kfifo_lat, kfifo_depth * sizeof(u32), @@ -984,15 +794,10 @@ out_kfifo1: out_kfifo0: kfifo_free(&gb->kfifo_lat); out_sysfs_conn: - sysfs_remove_groups(&connection->bundle->dev.kobj, loopback_con_groups); -out_sysfs_dev: - if (!gb_dev.count) { - sysfs_remove_groups(kobj, loopback_dev_groups); - debugfs_remove(gb_dev.file); - } + sysfs_remove_groups(&connection->bundle->dev.kobj, loopback_groups); +out_sysfs: debugfs_remove(gb->file); connection->bundle->private = NULL; -out_sysfs: mutex_unlock(&gb_dev.mutex); kfree(gb); @@ -1002,7 +807,6 @@ out_sysfs: static void gb_loopback_connection_exit(struct gb_connection *connection) { struct gb_loopback *gb = connection->bundle->private; - struct kobject *kobj = &connection->bundle->dev.kobj; if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); @@ -1014,12 +818,8 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) kfifo_free(&gb->kfifo_ts); gb_connection_latency_tag_disable(connection); gb_dev.count--; - if (!gb_dev.count) { - sysfs_remove_groups(kobj, loopback_dev_groups); - debugfs_remove(gb_dev.file); - } sysfs_remove_groups(&connection->bundle->dev.kobj, - loopback_con_groups); + loopback_groups); debugfs_remove(gb->file); list_del(&gb->entry); mutex_unlock(&gb_dev.mutex); @@ -1040,21 +840,14 @@ static int loopback_init(void) { int retval; - init_waitqueue_head(&gb_dev.wq); INIT_LIST_HEAD(&gb_dev.list); mutex_init(&gb_dev.mutex); gb_dev.root = debugfs_create_dir("gb_loopback", NULL); - if (kfifo_alloc(&gb_dev.kfifo, kfifo_depth * sizeof(u32), GFP_KERNEL)) { - retval = -ENOMEM; - goto error_debugfs; - } - retval = gb_protocol_register(&loopback_protocol); if (!retval) return retval; -error_debugfs: debugfs_remove_recursive(gb_dev.root); return retval; } @@ -1063,7 +856,6 @@ module_init(loopback_init); static void __exit loopback_exit(void) { debugfs_remove_recursive(gb_dev.root); - kfifo_free(&gb_dev.kfifo); gb_protocol_deregister(&loopback_protocol); } module_exit(loopback_exit); -- cgit v1.2.3-59-g8ed1b From 1cacb456fd6b5fd99f0274bef2b2d04b5f259392 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 3 Dec 2015 17:29:00 +0100 Subject: greybus: svc: flush workqueue at connection exit Make sure to flush the workqueue from hotplug and hotunplug events when the svc connection is tore down. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index af41e8e49c31..f65aba6446e2 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -709,6 +709,8 @@ static void gb_svc_connection_exit(struct gb_connection *connection) if (device_is_registered(&svc->dev)) device_del(&svc->dev); + flush_workqueue(svc->wq); + connection->hd->svc = NULL; connection->private = NULL; -- cgit v1.2.3-59-g8ed1b From f0960d05f5ece96a7b2539e0adb92bfbfc83bf3e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 3 Dec 2015 19:18:02 +0100 Subject: greybus: core: add bus id to uevents Add the bus id to all greybus uevents. This is needed to identify devices that are being removed (e.g. at hot-unplug). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 12 +++++++----- drivers/staging/greybus/svc.c | 1 + drivers/staging/greybus/svc.h | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9dcb1bf311c0..3d8b7ef589bb 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -77,7 +77,7 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct gb_host_device *hd = NULL; + struct gb_host_device *hd; struct gb_interface *intf = NULL; struct gb_bundle *bundle = NULL; struct gb_svc *svc = NULL; @@ -86,16 +86,22 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) hd = to_gb_host_device(dev); } else if (is_gb_interface(dev)) { intf = to_gb_interface(dev); + hd = intf->hd; } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); intf = bundle->intf; + hd = intf->hd; } else if (is_gb_svc(dev)) { svc = to_gb_svc(dev); + hd = svc->hd; } else { dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n"); return -EINVAL; } + if (add_uevent_var(env, "BUS=%u", hd->bus_id)) + return -ENOMEM; + if (bundle) { // FIXME // add a uevent that can "load" a bundle type @@ -104,10 +110,6 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } - // FIXME - // "just" a module, be vague here, nothing binds to a module except - // the greybus core, so there's not much, if anything, we need to - // advertise. return 0; } diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index f65aba6446e2..7037125e5cb7 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -695,6 +695,7 @@ static int gb_svc_connection_init(struct gb_connection *connection) ida_init(&svc->device_id_map); svc->state = GB_SVC_STATE_RESET; svc->connection = connection; + svc->hd = hd; connection->private = svc; hd->svc = svc; diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 3acfa07cc73b..ca0f71d059b1 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -19,6 +19,7 @@ enum gb_svc_state { struct gb_svc { struct device dev; + struct gb_host_device *hd; struct gb_connection *connection; enum gb_svc_state state; struct ida device_id_map; -- cgit v1.2.3-59-g8ed1b From c5e6b05ce4a18e7d6b73be0579c0698c8cc31b6c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 3 Dec 2015 19:18:04 +0100 Subject: greybus: core: add interface id to interface and bundle uevents Add the interface id to interface and bundle uevents. This is needed to identify interfaces that are being removed (e.g. at hot-unplug). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 3d8b7ef589bb..4ec7988eea26 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -102,6 +102,11 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "BUS=%u", hd->bus_id)) return -ENOMEM; + if (intf) { + if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) + return -ENOMEM; + } + if (bundle) { // FIXME // add a uevent that can "load" a bundle type -- cgit v1.2.3-59-g8ed1b From c29c016f7e41404aa7288aad8088162cac291594 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 4 Dec 2015 10:44:24 +0100 Subject: greybus: core: add bundle id to bundle uevents Add the bundle id to bundle uevents. This is needed to identify bundles that are being removed (e.g. at hot-unplug). Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 4ec7988eea26..209e1cd62838 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -112,7 +112,9 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) // add a uevent that can "load" a bundle type // This is what we need to bind a driver to so use the info // in gmod here as well - return 0; + + if (add_uevent_var(env, "BUNDLE=%u", bundle->id)) + return -ENOMEM; } return 0; -- cgit v1.2.3-59-g8ed1b From c5b6df9c8dbe0329e154410e9d39526ab01c0b51 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Dec 2015 21:30:07 +0530 Subject: greybus: Documentation: Arrange entries in alphabetical order The order of entries in sysfs-bus-greybus file doesn't match the order files/directories in sysfs on a real board. More specifically, N-svc comes at last and ap_interface_id comes before endo_id within the svc. Fix that. Reviewed-by: Johan Hovold Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs-bus-greybus | 80 +++++++++++----------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 9a6bbc3a61fb..a0363bfbdd4b 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -6,46 +6,6 @@ Description: The "root" greybus device for the Greybus device tree, or bus, where N is a dynamically assigned 1-based id. -What: /sys/bus/greybus/device/N-svc -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The singleton SVC device of bus N. - -What: /sys/bus/greybus/device/N-svc/endo_id -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The Endo ID, which is a 2-byte hexadecimal value - defined by the Endo layout scheme, documented in - the ARA Module Developer Kit. - -What: /sys/bus/greybus/device/N-svc/ap_intf_id -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The AP interface ID, a 1-byte non-zero integer which - defines the position of the AP module on the frame. - The interface positions are defined in the ARA - Module Developer Kit. - -What: /sys/bus/greybus/device/N-svc/unique_id -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The unique ID, or serial number, of the SVC device - -What: /sys/bus/greybus/device/N-svc/version -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The version number of the firmware in the SVC device. - What: /sys/bus/greybus/device/N-I Date: October 2015 KernelVersion: 4.XX @@ -123,3 +83,43 @@ Description: process watching the file will be woken up, and the new value can be read. It's a "poor-man's IPC", yes, but simplifies the Android userspace code immensely. + +What: /sys/bus/greybus/device/N-svc +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The singleton SVC device of bus N. + +What: /sys/bus/greybus/device/N-svc/ap_intf_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The AP interface ID, a 1-byte non-zero integer which + defines the position of the AP module on the frame. + The interface positions are defined in the ARA + Module Developer Kit. + +What: /sys/bus/greybus/device/N-svc/endo_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The Endo ID, which is a 2-byte hexadecimal value + defined by the Endo layout scheme, documented in + the ARA Module Developer Kit. + +What: /sys/bus/greybus/device/N-svc/unique_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The unique ID, or serial number, of the SVC device + +What: /sys/bus/greybus/device/N-svc/version +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + The version number of the firmware in the SVC device. -- cgit v1.2.3-59-g8ed1b From 4aac6c5a144921448237d2a2bff50d4fba0b0faf Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Dec 2015 21:30:08 +0530 Subject: greybus: svc: Use -EIO instead of -EINVAL for unipro errors -EIO fits better here, rather than -EINVAL as the arguments to the routine itself are valid, just that we failed while doing unipro transfers. Suggested-by: Johan Hovold Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 7037125e5cb7..220aed0168af 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -94,7 +94,7 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, if (result) { dev_err(&svc->dev, "UniPro error while getting DME attribute (%hhu %hx %hu): %hu\n", intf_id, attr, selector, result); - return -EINVAL; + return -EIO; } if (value) @@ -130,7 +130,7 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, if (result) { dev_err(&svc->dev, "UniPro error while setting DME attribute (%hhu %hx %hu %u): %hu\n", intf_id, attr, selector, value, result); - return -EINVAL; + return -EIO; } return 0; -- cgit v1.2.3-59-g8ed1b From 2f3db927cdf7627aa5359ff46c80ab72f7971980 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Dec 2015 21:30:09 +0530 Subject: greybus: don't use %h and %hh for printing short and char variables Because the width of our fields is already known, we can use %0Nx (for hex) to print N bytes and %u (for unsigned decimal), instead of using %h and %hh, which isn't that readable. This patch makes following changes: - s/%hx/%04x - s/%04hx/%04x - s/%hhx/%02x - s/%02hhx/%02x - s/%hhu/%u - s/%hu/%u - s/%x/%02x for u8 value (only at a single place) Suggested-by: Johan Hovold Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 4 ++-- drivers/staging/greybus/connection.c | 6 +++--- drivers/staging/greybus/es2.c | 4 ++-- drivers/staging/greybus/firmware.c | 2 +- drivers/staging/greybus/hid.c | 2 +- drivers/staging/greybus/loopback.c | 2 +- drivers/staging/greybus/manifest.c | 6 +++--- drivers/staging/greybus/operation.c | 14 +++++++------- drivers/staging/greybus/protocol.c | 4 ++-- drivers/staging/greybus/svc.c | 28 ++++++++++++++-------------- 10 files changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 3df7d5f915f9..97a8195ab035 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -128,7 +128,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, * the interface bundle list locked here. */ if (gb_bundle_find(intf, bundle_id)) { - pr_err("duplicate bundle id 0x%02hhx\n", bundle_id); + pr_err("duplicate bundle id 0x%02x\n", bundle_id); return NULL; } @@ -152,7 +152,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, retval = device_add(&bundle->dev); if (retval) { - pr_err("failed to add bundle device for id 0x%02hhx\n", + pr_err("failed to add bundle device for id 0x%02x\n", bundle_id); put_device(&bundle->dev); return NULL; diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2b0784668547..674e9a83962d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -89,7 +89,7 @@ static void gb_connection_init_name(struct gb_connection *connection) } snprintf(connection->name, sizeof(connection->name), - "%hu/%hhu:%hu", hd_cport_id, intf_id, cport_id); + "%u/%u:%u", hd_cport_id, intf_id, cport_id); } /* @@ -129,7 +129,7 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, * about holding the connection lock. */ if (bundle && gb_connection_intf_find(bundle->intf, cport_id)) { - dev_err(&bundle->dev, "cport 0x%04hx already connected\n", + dev_err(&bundle->dev, "cport 0x%04x already connected\n", cport_id); return NULL; } @@ -534,7 +534,7 @@ int gb_connection_bind_protocol(struct gb_connection *connection) connection->minor); if (!protocol) { dev_warn(&connection->hd->dev, - "protocol 0x%02hhx version %hhu.%hhu not found\n", + "protocol 0x%02x version %u.%u not found\n", connection->protocol_id, connection->major, connection->minor); return 0; diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 0eb96459b68b..ed22b6c0a73e 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -442,7 +442,7 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) USB_RECIP_INTERFACE, cport_id, 0, NULL, 0, ES2_TIMEOUT); if (retval < 0) { - dev_err(&udev->dev, "failed to reset cport %hu: %d\n", cport_id, + dev_err(&udev->dev, "failed to reset cport %u: %d\n", cport_id, retval); return retval; } @@ -890,7 +890,7 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress; } else { dev_err(&udev->dev, - "Unknown endpoint type found, address %x\n", + "Unknown endpoint type found, address %02x\n", endpoint->bEndpointAddress); } } diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index cd2184cec758..9d1739d78cca 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -210,7 +210,7 @@ static int gb_firmware_request_recv(u8 type, struct gb_operation *op) return gb_firmware_ready_to_boot(op); default: dev_err(&op->connection->bundle->dev, - "unsupported request: %hhu\n", type); + "unsupported request: %u\n", type); return -EINVAL; } } diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 2adcb1c47d2e..f45b444716ba 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -411,7 +411,7 @@ static int gb_hid_init(struct gb_hid *ghid) // hid->bus = BUS_GREYBUS; /* Need a bustype for GREYBUS in */ /* Set HID device's name */ - snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", + snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", dev_name(&ghid->connection->bundle->dev), hid->vendor, hid->product); diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 8524ce128738..689ebfdb456e 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -487,7 +487,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) return 0; default: - dev_err(dev, "unsupported request: %hhu\n", type); + dev_err(dev, "unsupported request: %u\n", type); return -EINVAL; } } diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index ea5ff8682e69..72400e38b392 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -140,7 +140,7 @@ static int identify_descriptor(struct gb_interface *intf, break; case GREYBUS_TYPE_INVALID: default: - pr_err("invalid descriptor type (%hhu)\n", desc_header->type); + pr_err("invalid descriptor type (%u)\n", desc_header->type); return -EINVAL; } @@ -440,14 +440,14 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) header = &manifest->header; manifest_size = le16_to_cpu(header->size); if (manifest_size != size) { - pr_err("manifest size mismatch (%zu != %hu)\n", + pr_err("manifest size mismatch (%zu != %u)\n", size, manifest_size); return false; } /* Validate major/minor number */ if (header->version_major > GREYBUS_VERSION_MAJOR) { - pr_err("manifest version too new (%hhu.%hhu > %hhu.%hhu)\n", + pr_err("manifest version too new (%u.%u > %u.%u)\n", header->version_major, header->version_minor, GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); return false; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 01ad08bc85d0..ae3ada0b54e3 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -229,7 +229,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) status = protocol->request_recv(operation->type, operation); } else { dev_err(&connection->hd->dev, - "%s: unexpected incoming request of type 0x%02hhx\n", + "%s: unexpected incoming request of type 0x%02x\n", connection->name, operation->type); status = -EPROTONOSUPPORT; @@ -238,7 +238,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) ret = gb_operation_response_send(operation, status); if (ret) { dev_err(&connection->hd->dev, - "%s: failed to send response %d for type 0x%02hhx: %d\n", + "%s: failed to send response %d for type 0x%02x: %d\n", connection->name, status, operation->type, ret); return; } @@ -797,7 +797,7 @@ void greybus_message_sent(struct gb_host_device *hd, if (message == operation->response) { if (status) { dev_err(&connection->hd->dev, - "%s: error sending response 0x%02hhx: %d\n", + "%s: error sending response 0x%02x: %d\n", connection->name, operation->type, status); } gb_operation_put_active(operation); @@ -868,7 +868,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, operation = gb_operation_find_outgoing(connection, operation_id); if (!operation) { dev_err(&connection->hd->dev, - "%s: unexpected response id 0x%04hx received\n", + "%s: unexpected response id 0x%04x received\n", connection->name, operation_id); return; } @@ -877,7 +877,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, message_size = sizeof(*message->header) + message->payload_size; if (!errno && size != message_size) { dev_err(&connection->hd->dev, - "%s: malformed response 0x%02hhx received (%zu != %zu)\n", + "%s: malformed response 0x%02x received (%zu != %zu)\n", connection->name, message->header->type, size, message_size); errno = -EMSGSIZE; @@ -926,7 +926,7 @@ void gb_connection_recv(struct gb_connection *connection, msg_size = le16_to_cpu(header.size); if (size < msg_size) { dev_err(dev, - "%s: incomplete message 0x%04hx of type 0x%02hhx received (%zu < %zu)\n", + "%s: incomplete message 0x%04x of type 0x%02x received (%zu < %zu)\n", connection->name, le16_to_cpu(header.operation_id), header.type, size, msg_size); return; /* XXX Should still complete operation */ @@ -1036,7 +1036,7 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { dev_err(&connection->hd->dev, - "%s: synchronous operation of type 0x%02hhx failed: %d\n", + "%s: synchronous operation of type 0x%02x failed: %d\n", connection->name, type, ret); } else { if (response_size) { diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index 47b747990902..aadb793912fd 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -166,7 +166,7 @@ int gb_protocol_get_version(struct gb_connection *connection) if (response.major > connection->protocol->major) { dev_err(&connection->hd->dev, - "%s: unsupported major version (%hhu > %hhu)\n", + "%s: unsupported major version (%u > %u)\n", connection->name, response.major, connection->protocol->major); return -ENOTSUPP; @@ -176,7 +176,7 @@ int gb_protocol_get_version(struct gb_connection *connection) connection->module_minor = response.minor; dev_dbg(&connection->hd->dev, - "%s: %s (0x%02hhx) v%hhu.%hhu\n", connection->name, + "%s: %s (0x%02x) v%u.%u\n", connection->name, protocol->name, protocol->id, response.major, response.minor); return 0; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 220aed0168af..4514e869a288 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -85,14 +85,14 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&svc->dev, "failed to get DME attribute (%hhu %hx %hu): %d\n", + dev_err(&svc->dev, "failed to get DME attribute (%u %04x %u): %d\n", intf_id, attr, selector, ret); return ret; } result = le16_to_cpu(response.result_code); if (result) { - dev_err(&svc->dev, "UniPro error while getting DME attribute (%hhu %hx %hu): %hu\n", + dev_err(&svc->dev, "UniPro error while getting DME attribute (%u %04x %u): %u\n", intf_id, attr, selector, result); return -EIO; } @@ -121,14 +121,14 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&svc->dev, "failed to set DME attribute (%hhu %hx %hu %u): %d\n", + dev_err(&svc->dev, "failed to set DME attribute (%u %04x %u %u): %d\n", intf_id, attr, selector, value, ret); return ret; } result = le16_to_cpu(response.result_code); if (result) { - dev_err(&svc->dev, "UniPro error while setting DME attribute (%hhu %hx %hu %u): %hu\n", + dev_err(&svc->dev, "UniPro error while setting DME attribute (%u %04x %u %u): %u\n", intf_id, attr, selector, value, result); return -EIO; } @@ -232,7 +232,7 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY, &request, sizeof(request), NULL, 0); if (ret) { - dev_err(&svc->dev, "failed to destroy connection (%hhu:%hu %hhu:%hu): %d\n", + dev_err(&svc->dev, "failed to destroy connection (%u:%u %u:%u): %d\n", intf1_id, cport1_id, intf2_id, cport2_id, ret); } } @@ -265,7 +265,7 @@ static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_DESTROY, &request, sizeof(request), NULL, 0); if (ret) { - dev_err(&svc->dev, "failed to destroy route (%hhu %hhu): %d\n", + dev_err(&svc->dev, "failed to destroy route (%u %u): %d\n", intf1_id, intf2_id, ret); } } @@ -287,7 +287,7 @@ static int gb_svc_version_request(struct gb_operation *op) request = op->request->payload; if (request->major > GB_SVC_VERSION_MAJOR) { - dev_warn(&svc->dev, "unsupported major version (%hhu > %hhu)\n", + dev_warn(&svc->dev, "unsupported major version (%u > %u)\n", request->major, GB_SVC_VERSION_MAJOR); return -ENOTSUPP; } @@ -380,14 +380,14 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) * Remove the interface and add it again, and let user know * about this with a print message. */ - dev_info(&svc->dev, "removing interface %hhu to add it again\n", + dev_info(&svc->dev, "removing interface %u to add it again\n", intf_id); gb_svc_intf_remove(svc, intf); } intf = gb_interface_create(hd, intf_id); if (!intf) { - dev_err(&svc->dev, "failed to create interface %hhu\n", + dev_err(&svc->dev, "failed to create interface %u\n", intf_id); return; } @@ -413,14 +413,14 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); if (device_id < 0) { ret = device_id; - dev_err(&svc->dev, "failed to allocate device id for interface %hhu: %d\n", + dev_err(&svc->dev, "failed to allocate device id for interface %u: %d\n", intf_id, ret); goto destroy_interface; } ret = gb_svc_intf_device_id(svc, intf_id, device_id); if (ret) { - dev_err(&svc->dev, "failed to set device id %hhu for interface %hhu: %d\n", + dev_err(&svc->dev, "failed to set device id %u for interface %u: %d\n", device_id, intf_id, ret); goto ida_put; } @@ -431,14 +431,14 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_DEVICE_ID_AP, intf_id, device_id); if (ret) { - dev_err(&svc->dev, "failed to create route to interface %hhu (device id %hhu): %d\n", + dev_err(&svc->dev, "failed to create route to interface %u (device id %u): %d\n", intf_id, device_id, ret); goto svc_id_free; } ret = gb_interface_init(intf, device_id); if (ret) { - dev_err(&svc->dev, "failed to initialize interface %hhu (device id %hhu): %d\n", + dev_err(&svc->dev, "failed to initialize interface %u (device id %u): %d\n", intf_id, device_id, ret); goto destroy_route; } @@ -474,7 +474,7 @@ static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) intf = gb_interface_find(hd, intf_id); if (!intf) { - dev_warn(&svc->dev, "could not find hot-unplug interface %hhu\n", + dev_warn(&svc->dev, "could not find hot-unplug interface %u\n", intf_id); return; } -- cgit v1.2.3-59-g8ed1b From b933fa4a40962c77254405a5274c8927f53b5074 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Dec 2015 21:30:10 +0530 Subject: greybus: Prefix hexadecimal values with 0x while printing them To clearly specify the base for printed values, prefix hexadecimal values with 0x. Suggested-by: Johan Hovold Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 2 +- drivers/staging/greybus/sdio.c | 4 ++-- drivers/staging/greybus/svc.c | 10 +++++----- drivers/staging/greybus/uart.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index ed22b6c0a73e..02cd2f6b8d3b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -890,7 +890,7 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress; } else { dev_err(&udev->dev, - "Unknown endpoint type found, address %02x\n", + "Unknown endpoint type found, address 0x%02x\n", endpoint->bEndpointAddress); } } diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index beb35747a4f8..da9093738d4a 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -398,7 +398,7 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd) cmd_flags = GB_SDIO_RSP_R3_R4; break; default: - dev_err(mmc_dev(host->mmc), "cmd flag invalid %04x\n", + dev_err(mmc_dev(host->mmc), "cmd flag invalid 0x%04x\n", mmc_resp_type(cmd)); ret = -EINVAL; goto out; @@ -418,7 +418,7 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd) cmd_type = GB_SDIO_CMD_ADTC; break; default: - dev_err(mmc_dev(host->mmc), "cmd type invalid %04x\n", + dev_err(mmc_dev(host->mmc), "cmd type invalid 0x%04x\n", mmc_cmd_type(cmd)); ret = -EINVAL; goto out; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 4514e869a288..5ae64d7d3ecf 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -85,14 +85,14 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&svc->dev, "failed to get DME attribute (%u %04x %u): %d\n", + dev_err(&svc->dev, "failed to get DME attribute (%u 0x%04x %u): %d\n", intf_id, attr, selector, ret); return ret; } result = le16_to_cpu(response.result_code); if (result) { - dev_err(&svc->dev, "UniPro error while getting DME attribute (%u %04x %u): %u\n", + dev_err(&svc->dev, "UniPro error while getting DME attribute (%u 0x%04x %u): %u\n", intf_id, attr, selector, result); return -EIO; } @@ -121,14 +121,14 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&svc->dev, "failed to set DME attribute (%u %04x %u %u): %d\n", + dev_err(&svc->dev, "failed to set DME attribute (%u 0x%04x %u %u): %d\n", intf_id, attr, selector, value, ret); return ret; } result = le16_to_cpu(response.result_code); if (result) { - dev_err(&svc->dev, "UniPro error while setting DME attribute (%u %04x %u %u): %u\n", + dev_err(&svc->dev, "UniPro error while setting DME attribute (%u 0x%04x %u %u): %u\n", intf_id, attr, selector, value, result); return -EIO; } @@ -502,7 +502,7 @@ static void gb_svc_process_deferred_request(struct work_struct *work) gb_svc_process_intf_hot_unplug(operation); break; default: - dev_err(&svc->dev, "bad deferred request type: %02x\n", type); + dev_err(&svc->dev, "bad deferred request type: 0x%02x\n", type); } gb_operation_put(operation); diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index ec978a451b17..1ba8476ce982 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -122,7 +122,7 @@ static int gb_uart_request_recv(u8 type, struct gb_operation *op) break; default: dev_err(&connection->bundle->dev, - "unsupported unsolicited request: %02x\n", type); + "unsupported unsolicited request: 0x%02x\n", type); ret = -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 2e238d71edadf03bed470cf58514ee10795a806b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 7 Dec 2015 01:59:05 +0000 Subject: greybus: loopback: Convert cross-thread mutex to spinlock while relaxing connect locks This patch converts the cross-thread mutex used to synchronize threads with respect to each other to a spinlock. This is done to enable taking of locks in the following patches while in atomic context. A small re-order of locking in connection setup/tear-down is done to minimize the amount of time spent in spinlock_irqsave(). Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 689ebfdb456e..b65e3e591105 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -39,7 +40,8 @@ struct gb_loopback_device { u32 count; size_t size_max; - struct mutex mutex; + /* We need to take a lock in atomic context */ + spinlock_t lock; struct list_head list; }; @@ -731,6 +733,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) struct gb_loopback *gb; int retval; char name[DEBUGFS_NAMELEN]; + unsigned long flags; gb = kzalloc(sizeof(*gb), GFP_KERNEL); if (!gb) @@ -739,14 +742,13 @@ static int gb_loopback_connection_init(struct gb_connection *connection) init_waitqueue_head(&gb->wq); gb_loopback_reset_stats(gb); - mutex_lock(&gb_dev.mutex); if (!gb_dev.count) { /* Calculate maximum payload */ gb_dev.size_max = gb_operation_get_payload_size_max(connection); if (gb_dev.size_max <= sizeof(struct gb_loopback_transfer_request)) { retval = -EINVAL; - goto out_sysfs; + goto out_kzalloc; } gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request); } @@ -783,10 +785,12 @@ static int gb_loopback_connection_init(struct gb_connection *connection) goto out_kfifo1; } + spin_lock_irqsave(&gb_dev.lock, flags); gb_loopback_insert_id(gb); - gb_connection_latency_tag_enable(connection); gb_dev.count++; - mutex_unlock(&gb_dev.mutex); + spin_unlock_irqrestore(&gb_dev.lock, flags); + + gb_connection_latency_tag_enable(connection); return 0; out_kfifo1: @@ -798,7 +802,7 @@ out_sysfs_conn: out_sysfs: debugfs_remove(gb->file); connection->bundle->private = NULL; - mutex_unlock(&gb_dev.mutex); +out_kzalloc: kfree(gb); return retval; @@ -807,22 +811,24 @@ out_sysfs: static void gb_loopback_connection_exit(struct gb_connection *connection) { struct gb_loopback *gb = connection->bundle->private; + unsigned long flags; if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); - mutex_lock(&gb_dev.mutex); - connection->bundle->private = NULL; kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); gb_connection_latency_tag_disable(connection); - gb_dev.count--; sysfs_remove_groups(&connection->bundle->dev.kobj, loopback_groups); debugfs_remove(gb->file); + + spin_lock_irqsave(&gb_dev.lock, flags); + gb_dev.count--; list_del(&gb->entry); - mutex_unlock(&gb_dev.mutex); + spin_unlock_irqrestore(&gb_dev.lock, flags); + kfree(gb); } @@ -841,7 +847,7 @@ static int loopback_init(void) int retval; INIT_LIST_HEAD(&gb_dev.list); - mutex_init(&gb_dev.mutex); + spin_lock_init(&gb_dev.lock); gb_dev.root = debugfs_create_dir("gb_loopback", NULL); retval = gb_protocol_register(&loopback_protocol); -- cgit v1.2.3-59-g8ed1b From 12927835d21127d7e528b9ed56fc334ac96db985 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 7 Dec 2015 01:59:06 +0000 Subject: greybus: loopback: Add asynchronous bi-directional support A particular ask from the firmware people for some time now has been the ability to drive multiple outstanding bi-directional operations from loopback to loopback Interfaces. This patch implments that change. The approach taken is to make a call to gb_operation_send() and have loopback capture the completion callback itself, with a parallel timer to timeout completion callbacks that take too long. The calling thread will issue each gb_operation_send() as fast as it can within the constraints of thread-safety. In order to support this addition the following new sysfs entries are created on a per-connection basis. - async Zero indicates loopback should use the traditional synchronous model i.e. gb_operation_request_send_sync(). Non-zero indicates loopback should use the new asynchronous model i.e. gb_operation_send() - requests_completed This value indicates the number of requests successfully completed. - requests_timedout This value indicates the number of requests which timed out. - timeout The number of microseconds to give an individual asynchronous request before timing that request out. - timeout_min Read-only attribute informs user-space of the minimum allowed timeout. - timeout_max Read-only attribute informs user-space of the maximum allowed timeout. Note requests_completed + requests_timedout should always equal iteration_max, once iteration_count == iteration_max. Also, at this time we support either synchronous or asynchronous operations in one set of transactions. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 388 +++++++++++++++++++++++++++++++++++-- 1 file changed, 369 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index b65e3e591105..392f9854ff56 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -43,10 +44,24 @@ struct gb_loopback_device { /* We need to take a lock in atomic context */ spinlock_t lock; struct list_head list; + struct list_head list_op_async; + wait_queue_head_t wq; }; static struct gb_loopback_device gb_dev; +struct gb_loopback_async_operation { + struct gb_loopback *gb; + struct gb_operation *operation; + struct timeval ts; + struct timer_list timer; + struct list_head entry; + struct work_struct work; + struct kref kref; + bool pending; + int (*completion)(struct gb_loopback_async_operation *op_async); +}; + struct gb_loopback { struct gb_connection *connection; @@ -66,18 +81,29 @@ struct gb_loopback { struct gb_loopback_stats gpbridge_firmware_latency; int type; + int async; u32 mask; u32 size; u32 iteration_max; u32 iteration_count; int ms_wait; u32 error; + u32 requests_completed; + u32 requests_timedout; + u32 timeout; + u32 jiffy_timeout; + u32 timeout_min; + u32 timeout_max; u32 lbid; u64 elapsed_nsecs; u32 apbridge_latency_ts; u32 gpbridge_latency_ts; }; +/* Min/max values in jiffies */ +#define GB_LOOPBACK_TIMEOUT_MIN 1 +#define GB_LOOPBACK_TIMEOUT_MAX 10000 + #define GB_LOOPBACK_FIFO_DEFAULT 8192 static unsigned kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT; @@ -215,6 +241,8 @@ static void gb_loopback_check_attr(struct gb_loopback *gb, gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; if (gb->size > gb_dev.size_max) gb->size = gb_dev.size_max; + gb->requests_timedout = 0; + gb->requests_completed = 0; gb->iteration_count = 0; gb->error = 0; @@ -230,6 +258,11 @@ static void gb_loopback_check_attr(struct gb_loopback *gb, case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: case GB_LOOPBACK_TYPE_SINK: + gb->jiffy_timeout = usecs_to_jiffies(gb->timeout); + if (!gb->jiffy_timeout) + gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MIN; + else if (gb->jiffy_timeout > GB_LOOPBACK_TIMEOUT_MAX) + gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MAX; gb_loopback_reset_stats(gb); wake_up(&gb->wq); break; @@ -252,6 +285,14 @@ gb_loopback_stats_attrs(gpbridge_firmware_latency); /* Number of errors encountered during loop */ gb_loopback_ro_attr(error); +/* Number of requests successfully completed async */ +gb_loopback_ro_attr(requests_completed); +/* Number of requests timed out async */ +gb_loopback_ro_attr(requests_timedout); +/* Timeout minimum in useconds */ +gb_loopback_ro_attr(timeout_min); +/* Timeout minimum in useconds */ +gb_loopback_ro_attr(timeout_max); /* * Type of loopback message to send based on protocol type definitions @@ -270,8 +311,12 @@ gb_dev_loopback_rw_attr(ms_wait, d); gb_dev_loopback_rw_attr(iteration_max, u); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ gb_dev_loopback_ro_attr(iteration_count, false); -/* A bit-mask of destination connecitons to include in the test run */ +/* A bit-mask of destination connections to include in the test run */ gb_dev_loopback_rw_attr(mask, u); +/* A flag to indicate synchronous or asynchronous operations */ +gb_dev_loopback_rw_attr(async, u); +/* Timeout of an individual asynchronous request */ +gb_dev_loopback_rw_attr(timeout, u); static struct attribute *loopback_attrs[] = { &dev_attr_latency_min.attr, @@ -295,11 +340,19 @@ static struct attribute *loopback_attrs[] = { &dev_attr_iteration_count.attr, &dev_attr_iteration_max.attr, &dev_attr_mask.attr, + &dev_attr_async.attr, &dev_attr_error.attr, + &dev_attr_requests_completed.attr, + &dev_attr_requests_timedout.attr, + &dev_attr_timeout.attr, + &dev_attr_timeout_min.attr, + &dev_attr_timeout_max.attr, NULL, }; ATTRIBUTE_GROUPS(loopback); +static void gb_loopback_calculate_stats(struct gb_loopback *gb); + static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs) { u32 lat; @@ -381,7 +434,200 @@ error: return ret; } -static int gb_loopback_sink(struct gb_loopback *gb, u32 len) +static void __gb_loopback_async_operation_destroy(struct kref *kref) +{ + struct gb_loopback_async_operation *op_async; + + op_async = container_of(kref, struct gb_loopback_async_operation, kref); + + list_del(&op_async->entry); + if (op_async->operation) + gb_operation_put(op_async->operation); + kfree(op_async); +} + +static void gb_loopback_async_operation_get(struct gb_loopback_async_operation + *op_async) +{ + kref_get(&op_async->kref); +} + +static void gb_loopback_async_operation_put(struct gb_loopback_async_operation + *op_async) +{ + unsigned long flags; + + spin_lock_irqsave(&gb_dev.lock, flags); + kref_put(&op_async->kref, __gb_loopback_async_operation_destroy); + spin_unlock_irqrestore(&gb_dev.lock, flags); +} + +static struct gb_loopback_async_operation * + gb_loopback_operation_find(u16 id) +{ + struct gb_loopback_async_operation *op_async; + bool found = false; + unsigned long flags; + + spin_lock_irqsave(&gb_dev.lock, flags); + list_for_each_entry(op_async, &gb_dev.list_op_async, entry) { + if (op_async->operation->id == id) { + gb_loopback_async_operation_get(op_async); + found = true; + break; + } + } + spin_unlock_irqrestore(&gb_dev.lock, flags); + + return found ? op_async : NULL; +} + +static void gb_loopback_async_operation_callback(struct gb_operation *operation) +{ + struct gb_loopback_async_operation *op_async; + struct gb_loopback *gb; + struct timeval te; + bool err = false; + + do_gettimeofday(&te); + op_async = gb_loopback_operation_find(operation->id); + if (!op_async) + return; + + gb = op_async->gb; + mutex_lock(&gb->mutex); + + if (!op_async->pending || gb_operation_result(operation)) { + err = true; + } else { + if (op_async->completion) + if (op_async->completion(op_async)) + err = true; + } + + if (err) { + gb->error++; + } else { + gb->requests_completed++; + gb_loopback_push_latency_ts(gb, &op_async->ts, &te); + gb->elapsed_nsecs = gb_loopback_calc_latency(&op_async->ts, + &te); + gb_loopback_calculate_stats(gb); + } + + if (op_async->pending) { + gb->iteration_count++; + op_async->pending = false; + del_timer_sync(&op_async->timer); + gb_loopback_async_operation_put(op_async); + } + mutex_unlock(&gb->mutex); + + dev_dbg(&gb->connection->bundle->dev, "complete operation %d\n", + operation->id); + + gb_loopback_async_operation_put(op_async); +} + +static void gb_loopback_async_operation_work(struct work_struct *work) +{ + struct gb_loopback *gb; + struct gb_operation *operation; + struct gb_loopback_async_operation *op_async; + + op_async = container_of(work, struct gb_loopback_async_operation, work); + if (!op_async) + return; + + gb = op_async->gb; + operation = op_async->operation; + + mutex_lock(&gb->mutex); + if (op_async->pending) { + gb->requests_timedout++; + gb->error++; + gb->iteration_count++; + op_async->pending = false; + gb_loopback_async_operation_put(op_async); + } + mutex_unlock(&gb->mutex); + + dev_dbg(&gb->connection->bundle->dev, "timeout operation %d\n", + operation->id); + + gb_operation_cancel(operation, -ETIMEDOUT); + gb_loopback_async_operation_put(op_async); +} + +static void gb_loopback_async_operation_timeout(unsigned long data) +{ + struct gb_loopback_async_operation *op_async; + u16 id = data; + + op_async = gb_loopback_operation_find(id); + if (!op_async) { + pr_err("operation %d not found - time out ?\n", id); + return; + } + schedule_work(&op_async->work); +} + +static int gb_loopback_async_operation(struct gb_loopback *gb, int type, + void *request, int request_size, + int response_size, + void *completion) +{ + struct gb_loopback_async_operation *op_async; + struct gb_operation *operation; + int ret; + unsigned long flags; + + op_async = kzalloc(sizeof(*op_async), GFP_KERNEL); + if (!op_async) + return -ENOMEM; + + INIT_WORK(&op_async->work, gb_loopback_async_operation_work); + init_timer(&op_async->timer); + kref_init(&op_async->kref); + + operation = gb_operation_create(gb->connection, type, request_size, + response_size, GFP_KERNEL); + if (!operation) { + ret = -ENOMEM; + goto error; + } + + if (request_size) + memcpy(operation->request->payload, request, request_size); + + op_async->gb = gb; + op_async->operation = operation; + op_async->completion = completion; + + spin_lock_irqsave(&gb_dev.lock, flags); + list_add_tail(&op_async->entry, &gb_dev.list_op_async); + spin_unlock_irqrestore(&gb_dev.lock, flags); + + do_gettimeofday(&op_async->ts); + op_async->pending = true; + ret = gb_operation_request_send(operation, + gb_loopback_async_operation_callback, + GFP_KERNEL); + if (ret) + goto error; + + op_async->timer.function = gb_loopback_async_operation_timeout; + op_async->timer.expires = jiffies + gb->jiffy_timeout; + op_async->timer.data = (unsigned long)operation->id; + add_timer(&op_async->timer); + + return ret; +error: + gb_loopback_async_operation_put(op_async); + return ret; +} + +static int gb_loopback_sync_sink(struct gb_loopback *gb, u32 len) { struct gb_loopback_transfer_request *request; int retval; @@ -398,7 +644,7 @@ static int gb_loopback_sink(struct gb_loopback *gb, u32 len) return retval; } -static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) +static int gb_loopback_sync_transfer(struct gb_loopback *gb, u32 len) { struct gb_loopback_transfer_request *request; struct gb_loopback_transfer_response *response; @@ -440,12 +686,91 @@ gb_error: return retval; } -static int gb_loopback_ping(struct gb_loopback *gb) +static int gb_loopback_sync_ping(struct gb_loopback *gb) { return gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_PING, NULL, 0, NULL, 0); } +static int gb_loopback_async_sink(struct gb_loopback *gb, u32 len) +{ + struct gb_loopback_transfer_request *request; + int retval; + + request = kmalloc(len + sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->len = cpu_to_le32(len); + retval = gb_loopback_async_operation(gb, GB_LOOPBACK_TYPE_SINK, + request, len + sizeof(*request), + 0, NULL); + kfree(request); + return retval; +} + +static int gb_loopback_async_transfer_complete( + struct gb_loopback_async_operation *op_async) +{ + struct gb_loopback *gb; + struct gb_operation *operation; + struct gb_loopback_transfer_request *request; + struct gb_loopback_transfer_response *response; + size_t len; + int retval = 0; + + gb = op_async->gb; + operation = op_async->operation; + request = operation->request->payload; + response = operation->response->payload; + len = le32_to_cpu(request->len); + + if (memcmp(request->data, response->data, len)) { + dev_err(&gb->connection->bundle->dev, + "Loopback Data doesn't match operation id %d\n", + operation->id); + retval = -EREMOTEIO; + } else { + gb->apbridge_latency_ts = + (u32)__le32_to_cpu(response->reserved0); + gb->gpbridge_latency_ts = + (u32)__le32_to_cpu(response->reserved1); + } + + return retval; +} + +static int gb_loopback_async_transfer(struct gb_loopback *gb, u32 len) +{ + struct gb_loopback_transfer_request *request; + int retval, response_len; + + request = kmalloc(len + sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + memset(request->data, 0x5A, len); + + request->len = cpu_to_le32(len); + response_len = sizeof(struct gb_loopback_transfer_response); + retval = gb_loopback_async_operation(gb, GB_LOOPBACK_TYPE_TRANSFER, + request, len + sizeof(*request), + len + response_len, + gb_loopback_async_transfer_complete); + if (retval) + goto gb_error; + +gb_error: + kfree(request); + return retval; +} + +static int gb_loopback_async_ping(struct gb_loopback *gb) +{ + return gb_loopback_async_operation(gb, GB_LOOPBACK_TYPE_PING, + NULL, 0, 0, NULL); +} + static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) { struct gb_connection *connection = operation->connection; @@ -512,6 +837,10 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) memcpy(&gb->gpbridge_firmware_latency, &reset, sizeof(struct gb_loopback_stats)); + /* Set values to report min/max timeout to user-space */ + gb->timeout_min = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MIN); + gb->timeout_max = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MAX); + /* Reset aggregate stats */ memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); @@ -599,23 +928,25 @@ static int gb_loopback_fn(void *data) int ms_wait = 0; int type; u32 size; + u32 send_count = 0; struct gb_loopback *gb = data; while (1) { if (!gb->type) wait_event_interruptible(gb->wq, gb->type || kthread_should_stop()); + if (kthread_should_stop()) break; mutex_lock(&gb->mutex); - sysfs_notify(&gb->connection->bundle->dev.kobj, NULL, "iteration_count"); /* Optionally terminate */ - if (gb->iteration_count == gb->iteration_max) { + if (send_count == gb->iteration_max) { gb->type = 0; + send_count = 0; mutex_unlock(&gb->mutex); continue; } @@ -625,19 +956,33 @@ static int gb_loopback_fn(void *data) mutex_unlock(&gb->mutex); /* Else operations to perform */ - if (type == GB_LOOPBACK_TYPE_PING) - error = gb_loopback_ping(gb); - else if (type == GB_LOOPBACK_TYPE_TRANSFER) - error = gb_loopback_transfer(gb, size); - else if (type == GB_LOOPBACK_TYPE_SINK) - error = gb_loopback_sink(gb, size); - - if (error) - gb->error++; - - gb_loopback_calculate_stats(gb); - gb->iteration_count++; - + if (gb->async) { + if (type == GB_LOOPBACK_TYPE_PING) { + error = gb_loopback_async_ping(gb); + gb_loopback_calculate_stats(gb); + } else if (type == GB_LOOPBACK_TYPE_TRANSFER) { + error = gb_loopback_async_transfer(gb, size); + } else if (type == GB_LOOPBACK_TYPE_SINK) { + error = gb_loopback_async_sink(gb, size); + } + + if (error) + gb->error++; + } else { + /* We are effectively single threaded here */ + if (type == GB_LOOPBACK_TYPE_PING) + error = gb_loopback_sync_ping(gb); + else if (type == GB_LOOPBACK_TYPE_TRANSFER) + error = gb_loopback_sync_transfer(gb, size); + else if (type == GB_LOOPBACK_TYPE_SINK) + error = gb_loopback_sync_sink(gb, size); + + if (error) + gb->error++; + gb->iteration_count++; + gb_loopback_calculate_stats(gb); + } + send_count++; if (ms_wait) msleep(ms_wait); } @@ -742,6 +1087,10 @@ static int gb_loopback_connection_init(struct gb_connection *connection) init_waitqueue_head(&gb->wq); gb_loopback_reset_stats(gb); + /* Reported values to user-space for min/max timeouts */ + gb->timeout_min = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MIN); + gb->timeout_max = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MAX); + if (!gb_dev.count) { /* Calculate maximum payload */ gb_dev.size_max = gb_operation_get_payload_size_max(connection); @@ -847,6 +1196,7 @@ static int loopback_init(void) int retval; INIT_LIST_HEAD(&gb_dev.list); + INIT_LIST_HEAD(&gb_dev.list_op_async); spin_lock_init(&gb_dev.lock); gb_dev.root = debugfs_create_dir("gb_loopback", NULL); -- cgit v1.2.3-59-g8ed1b From b36f04fa9417c58d53b1c54bf868b9da8f73810f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 7 Dec 2015 01:59:07 +0000 Subject: greybus: loopback: Convert thread delay to microseconds Currently the loopback code allows a delay between operations specified in milliseconds. Having added asynchronous bi-directional support to loopback its obvious that the delay value would be far more useful specified in microseconds than milliseconds. So, this patch makes the necessary conversion. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 392f9854ff56..b8297d6b7e60 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -86,7 +86,7 @@ struct gb_loopback { u32 size; u32 iteration_max; u32 iteration_count; - int ms_wait; + int us_wait; u32 error; u32 requests_completed; u32 requests_timedout; @@ -112,7 +112,7 @@ module_param(kfifo_depth, uint, 0444); /* Maximum size of any one send data buffer we support */ #define MAX_PACKET_SIZE (PAGE_SIZE * 2) -#define GB_LOOPBACK_MS_WAIT_MAX 1000 +#define GB_LOOPBACK_US_WAIT_MAX 1000000 /* interface sysfs attributes */ #define gb_loopback_ro_attr(field) \ @@ -237,8 +237,8 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb); static void gb_loopback_check_attr(struct gb_loopback *gb, struct gb_bundle *bundle) { - if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX) - gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX; + if (gb->us_wait > GB_LOOPBACK_US_WAIT_MAX) + gb->us_wait = GB_LOOPBACK_US_WAIT_MAX; if (gb->size > gb_dev.size_max) gb->size = gb_dev.size_max; gb->requests_timedout = 0; @@ -306,7 +306,7 @@ gb_dev_loopback_rw_attr(type, d); /* Size of transfer message payload: 0-4096 bytes */ gb_dev_loopback_rw_attr(size, u); /* Time to wait between two messages: 0-1000 ms */ -gb_dev_loopback_rw_attr(ms_wait, d); +gb_dev_loopback_rw_attr(us_wait, d); /* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */ gb_dev_loopback_rw_attr(iteration_max, u); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ @@ -336,7 +336,7 @@ static struct attribute *loopback_attrs[] = { &dev_attr_gpbridge_firmware_latency_avg.attr, &dev_attr_type.attr, &dev_attr_size.attr, - &dev_attr_ms_wait.attr, + &dev_attr_us_wait.attr, &dev_attr_iteration_count.attr, &dev_attr_iteration_max.attr, &dev_attr_mask.attr, @@ -925,7 +925,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) static int gb_loopback_fn(void *data) { int error = 0; - int ms_wait = 0; + int us_wait = 0; int type; u32 size; u32 send_count = 0; @@ -951,7 +951,7 @@ static int gb_loopback_fn(void *data) continue; } size = gb->size; - ms_wait = gb->ms_wait; + us_wait = gb->us_wait; type = gb->type; mutex_unlock(&gb->mutex); @@ -983,8 +983,8 @@ static int gb_loopback_fn(void *data) gb_loopback_calculate_stats(gb); } send_count++; - if (ms_wait) - msleep(ms_wait); + if (us_wait) + udelay(us_wait); } return 0; } -- cgit v1.2.3-59-g8ed1b From f42a6891d18a156f41813e3cb924470d1e2ff9ad Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 7 Dec 2015 01:59:08 +0000 Subject: greybus: loopback: Ensure we reset stats once and once only 9445c54c ('greybus/loopback: drop bus aggregate calculation') removed the aggregation of data in-kernel but instead of dropping the reset of aggregate stastics, converted that reset into a second reset of the connection-level stats. While this doesn't result in anything bad it's also definitely a dumb thing to be doing, so, drop it now. Also ensure we reset the bridge-specific tracking variables at least once. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index b8297d6b7e60..626a841b1fe2 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -837,19 +837,9 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) memcpy(&gb->gpbridge_firmware_latency, &reset, sizeof(struct gb_loopback_stats)); - /* Set values to report min/max timeout to user-space */ - gb->timeout_min = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MIN); - gb->timeout_max = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MAX); - - /* Reset aggregate stats */ - memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb->requests_per_second, &reset, - sizeof(struct gb_loopback_stats)); - memcpy(&gb->apbridge_unipro_latency, &reset, - sizeof(struct gb_loopback_stats)); - memcpy(&gb->gpbridge_firmware_latency, &reset, - sizeof(struct gb_loopback_stats)); + /* Should be initialized at least once per transaction set */ + gb->apbridge_latency_ts = 0; + gb->gpbridge_latency_ts = 0; } static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) -- cgit v1.2.3-59-g8ed1b From b395754a8e03a3d424a93b2601f6e41a4ad59a05 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:28 +0100 Subject: greybus: svc: add missing boot-status error message Make sure to print an error message when aborting hotplug processing due to failure to clear the interface boot status. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5ae64d7d3ecf..2a8f79e95b3c 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -393,8 +393,11 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) } ret = gb_svc_read_and_clear_module_boot_status(intf); - if (ret) + if (ret) { + dev_err(&svc->dev, "failed to clear boot status of interface %u: %d\n", + intf_id, ret); goto destroy_interface; + } intf->unipro_mfg_id = le32_to_cpu(request->data.unipro_mfg_id); intf->unipro_prod_id = le32_to_cpu(request->data.unipro_prod_id); -- cgit v1.2.3-59-g8ed1b From a2cc7404e18cb75555b14831685a0b227135bc47 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:29 +0100 Subject: greybus: bundle: remove obsolete function prototype Remove obsolete function prototype that was left after a recent code relocation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 24ddd449034c..662c4a6b92f5 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -36,8 +36,4 @@ void gb_bundle_destroy(struct gb_bundle *bundle); struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id); void gb_bundle_bind_protocols(void); -const struct greybus_bundle_id * - gb_bundle_match_id(struct gb_bundle *bundle, - const struct greybus_bundle_id *id); - #endif /* __BUNDLE_H */ -- cgit v1.2.3-59-g8ed1b From 2d54e4db4efd244181aa6a2f54f0ec362adc9d4d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:30 +0100 Subject: greybus: bundle: kill gb_bundle_bind_protocols Remove gb_bundle_bind_protocols() that was used to iterate over all registered bundles and bind protocols to them should a protocol become available post bundle creation. The protocol abstraction as a generic construct is going away in favour of class drivers. Connections will be setup when a class driver is probed, and driver modules can be loaded on-demand by user space based on uevents and modalias. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 30 ------------------------------ drivers/staging/greybus/bundle.h | 1 - drivers/staging/greybus/connection.c | 6 +++++- drivers/staging/greybus/connection.h | 2 -- drivers/staging/greybus/protocol.c | 6 ------ 5 files changed, 5 insertions(+), 40 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 97a8195ab035..e53833697a56 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -81,36 +81,6 @@ struct device_type greybus_bundle_type = { /* XXX This could be per-host device or per-module */ static DEFINE_SPINLOCK(gb_bundles_lock); -static int __bundle_bind_protocols(struct device *dev, void *data) -{ - struct gb_bundle *bundle; - struct gb_connection *connection; - - if (!is_gb_bundle(dev)) - return 0; - - bundle = to_gb_bundle(dev); - - list_for_each_entry(connection, &bundle->connections, bundle_links) { - gb_connection_bind_protocol(connection); - } - - return 0; -} - -/* - * Walk all bundles in the system, and see if any connections are not bound to a - * specific prototcol. If they are not, then try to find one for it and bind it - * to it. - * - * This is called after registering a new protocol. - */ -void gb_bundle_bind_protocols(void) -{ - bus_for_each_dev(&greybus_bus_type, NULL, NULL, - __bundle_bind_protocols); -} - /* * Create a gb_bundle structure to represent a discovered * bundle. Returns a pointer to the new bundle or a null diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 662c4a6b92f5..70d7b9d897da 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -34,6 +34,5 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, void gb_bundle_destroy(struct gb_bundle *bundle); struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id); -void gb_bundle_bind_protocols(void); #endif /* __BUNDLE_H */ diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 674e9a83962d..bf28dad230b7 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -11,6 +11,10 @@ #include "greybus.h" + +static int gb_connection_bind_protocol(struct gb_connection *connection); + + static DEFINE_SPINLOCK(gb_connections_lock); /* This is only used at initialization time; no locking is required. */ @@ -520,7 +524,7 @@ void gb_connection_latency_tag_disable(struct gb_connection *connection) } EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable); -int gb_connection_bind_protocol(struct gb_connection *connection) +static int gb_connection_bind_protocol(struct gb_connection *connection) { struct gb_protocol *protocol; int ret; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 800626234b32..77f77bf64a4e 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -66,8 +66,6 @@ static inline bool gb_connection_is_static(struct gb_connection *connection) void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length); -int gb_connection_bind_protocol(struct gb_connection *connection); - void gb_connection_latency_tag_enable(struct gb_connection *connection); void gb_connection_latency_tag_disable(struct gb_connection *connection); diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index aadb793912fd..d69f64801b4f 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -90,12 +90,6 @@ int __gb_protocol_register(struct gb_protocol *protocol, struct module *module) pr_info("Registered %s protocol.\n", protocol->name); - /* - * Go try to bind any unbound connections, as we have a - * new protocol in the system - */ - gb_bundle_bind_protocols(); - return 0; } EXPORT_SYMBOL_GPL(__gb_protocol_register); -- cgit v1.2.3-59-g8ed1b From 74c831653309872b4a514cb27f456f20a7914173 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:31 +0100 Subject: greybus: connection: fail on missing protocol Make sure to fail properly when a protocol is missing. This prevents the connection from being created, which is fine as we currently never bind protocols post creation. This is an intermediate step in moving protocol binding to connection_init. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index bf28dad230b7..ae8cb6603a5e 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -537,11 +537,11 @@ static int gb_connection_bind_protocol(struct gb_connection *connection) connection->major, connection->minor); if (!protocol) { - dev_warn(&connection->hd->dev, + dev_err(&connection->hd->dev, "protocol 0x%02x version %u.%u not found\n", connection->protocol_id, connection->major, connection->minor); - return 0; + return -EPROTONOSUPPORT; } connection->protocol = protocol; -- cgit v1.2.3-59-g8ed1b From 30c2de77aedeb0f576356fe152e1e22c4806d239 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:32 +0100 Subject: greybus: connection: bind protocol at init Bind protocol at connection init. This is an intermediate step in separating connection creation and enabling. Note that the protocol is currently still unbound when the connection is destroyed. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ae8cb6603a5e..38803604e603 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -13,6 +13,7 @@ static int gb_connection_bind_protocol(struct gb_connection *connection); +static int gb_connection_init(struct gb_connection *connection); static DEFINE_SPINLOCK(gb_connections_lock); @@ -192,9 +193,9 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, spin_unlock_irq(&gb_connections_lock); - retval = gb_connection_bind_protocol(connection); + retval = gb_connection_init(connection); if (retval) { - dev_err(&hd->dev, "%s: failed to bind protocol: %d\n", + dev_err(&hd->dev, "%s: failed to initialize connection: %d\n", connection->name, retval); gb_connection_destroy(connection); return NULL; @@ -396,9 +397,12 @@ static int gb_connection_protocol_get_version(struct gb_connection *connection) static int gb_connection_init(struct gb_connection *connection) { - struct gb_protocol *protocol = connection->protocol; int ret; + ret = gb_connection_bind_protocol(connection); + if (ret) + return ret; + ret = gb_connection_hd_cport_enable(connection); if (ret) return ret; @@ -420,7 +424,7 @@ static int gb_connection_init(struct gb_connection *connection) if (ret) goto err_disconnect; - ret = protocol->connection_init(connection); + ret = connection->protocol->connection_init(connection); if (ret) goto err_disconnect; @@ -527,7 +531,6 @@ EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable); static int gb_connection_bind_protocol(struct gb_connection *connection) { struct gb_protocol *protocol; - int ret; /* If we already have a protocol bound here, just return */ if (connection->protocol) @@ -545,12 +548,5 @@ static int gb_connection_bind_protocol(struct gb_connection *connection) } connection->protocol = protocol; - ret = gb_connection_init(connection); - if (ret) { - gb_protocol_put(protocol); - connection->protocol = NULL; - return ret; - } - return 0; } -- cgit v1.2.3-59-g8ed1b From 36173112354a4f5993d464f95b04d41e1eec10a0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:33 +0100 Subject: greybus: connection: unbind protocol at exit Unbind protocol at connection exit rather than when the connection is destroyed. Now a protocol is only bound while a connection is enabled. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 38803604e603..980244a873b8 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -13,6 +13,7 @@ static int gb_connection_bind_protocol(struct gb_connection *connection); +static void gb_connection_unbind_protocol(struct gb_connection *connection); static int gb_connection_init(struct gb_connection *connection); @@ -405,7 +406,7 @@ static int gb_connection_init(struct gb_connection *connection) ret = gb_connection_hd_cport_enable(connection); if (ret) - return ret; + goto err_unbind_protocol; ret = gb_connection_svc_connection_create(connection); if (ret) @@ -440,15 +441,14 @@ err_svc_destroy: gb_connection_svc_connection_destroy(connection); err_hd_cport_disable: gb_connection_hd_cport_disable(connection); +err_unbind_protocol: + gb_connection_unbind_protocol(connection); return ret; } static void gb_connection_exit(struct gb_connection *connection) { - if (!connection->protocol) - return; - spin_lock_irq(&connection->lock); if (connection->state != GB_CONNECTION_STATE_ENABLED) { spin_unlock_irq(&connection->lock); @@ -463,6 +463,7 @@ static void gb_connection_exit(struct gb_connection *connection) gb_connection_control_disconnected(connection); gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); + gb_connection_unbind_protocol(connection); } /* @@ -482,10 +483,6 @@ void gb_connection_destroy(struct gb_connection *connection) list_del(&connection->hd_links); spin_unlock_irq(&gb_connections_lock); - if (connection->protocol) - gb_protocol_put(connection->protocol); - connection->protocol = NULL; - id_map = &connection->hd->cport_id_map; ida_simple_remove(id_map, connection->hd_cport_id); connection->hd_cport_id = CPORT_ID_BAD; @@ -532,10 +529,6 @@ static int gb_connection_bind_protocol(struct gb_connection *connection) { struct gb_protocol *protocol; - /* If we already have a protocol bound here, just return */ - if (connection->protocol) - return 0; - protocol = gb_protocol_get(connection->protocol_id, connection->major, connection->minor); @@ -550,3 +543,12 @@ static int gb_connection_bind_protocol(struct gb_connection *connection) return 0; } + +static void gb_connection_unbind_protocol(struct gb_connection *connection) +{ + struct gb_protocol *protocol = connection->protocol; + + gb_protocol_put(protocol); + + connection->protocol = NULL; +} -- cgit v1.2.3-59-g8ed1b From 0bf1f2441979e290945f4e5af40d76582c61da1e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:34 +0100 Subject: greybus: connection: separate connection creation and enabling Separate connection creation from enabling. This will ultimately allow connection structures to be created while parsing manifests, but the connections to not be enabled until a driver is bound. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 12 +----------- drivers/staging/greybus/connection.h | 2 ++ drivers/staging/greybus/hd.c | 7 +++++++ drivers/staging/greybus/interface.c | 6 ++++++ drivers/staging/greybus/manifest.c | 14 ++++++++++++-- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 980244a873b8..c93650268b99 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -14,7 +14,6 @@ static int gb_connection_bind_protocol(struct gb_connection *connection); static void gb_connection_unbind_protocol(struct gb_connection *connection); -static int gb_connection_init(struct gb_connection *connection); static DEFINE_SPINLOCK(gb_connections_lock); @@ -125,7 +124,6 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, struct gb_connection *connection; struct ida *id_map = &hd->cport_id_map; int ida_start, ida_end; - int retval; u8 major = 0; u8 minor = 1; @@ -194,14 +192,6 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, spin_unlock_irq(&gb_connections_lock); - retval = gb_connection_init(connection); - if (retval) { - dev_err(&hd->dev, "%s: failed to initialize connection: %d\n", - connection->name, retval); - gb_connection_destroy(connection); - return NULL; - } - return connection; err_free_connection: @@ -396,7 +386,7 @@ static int gb_connection_protocol_get_version(struct gb_connection *connection) return 0; } -static int gb_connection_init(struct gb_connection *connection) +int gb_connection_init(struct gb_connection *connection) { int ret; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 77f77bf64a4e..329b705bd425 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -63,6 +63,8 @@ static inline bool gb_connection_is_static(struct gb_connection *connection) return !connection->intf; } +int gb_connection_init(struct gb_connection *connection); + void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length); diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index b280925792b5..74569a38698a 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -124,6 +124,13 @@ int gb_hd_add(struct gb_host_device *hd) return ret; } + ret = gb_connection_init(hd->svc_connection); + if (ret) { + gb_connection_destroy(hd->svc_connection); + device_del(&hd->dev); + return ret; + } + return 0; } EXPORT_SYMBOL_GPL(gb_hd_add); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 9ff7464b8459..e8eed858b545 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -180,6 +180,12 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) return -ENOMEM; } + ret = gb_connection_init(connection); + if (ret) { + gb_connection_destroy(connection); + return ret; + } + /* Get manifest size using control protocol on CPort */ size = gb_control_get_manifest_size_operation(intf); if (size <= 0) { diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 72400e38b392..2d470500222e 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -228,6 +228,7 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) */ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) { + struct gb_connection *connection; struct gb_interface *intf = bundle->intf; struct manifest_desc *desc; struct manifest_desc *next; @@ -235,6 +236,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) u8 protocol_id; u16 cport_id; u32 count = 0; + int ret; /* Set up all cport descriptors associated with this bundle */ list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { @@ -254,10 +256,18 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) /* Found one. Set up its function structure */ protocol_id = desc_cport->protocol_id; - if (!gb_connection_create_dynamic(intf, bundle, cport_id, - protocol_id)) + connection = gb_connection_create_dynamic(intf, bundle, + cport_id, + protocol_id); + if (!connection) goto exit; + ret = gb_connection_init(connection); + if (ret) { + gb_connection_destroy(connection); + goto exit; + } + count++; /* Release the cport descriptor */ -- cgit v1.2.3-59-g8ed1b From d4c80bad59dbc69a989ca2c4efbcc0fb772a713b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:35 +0100 Subject: greybus: hd: initialise device last Initialise the struct device last when creating the host device. After device_initialize(), or rather dev_set_name(), we must use put_device to release the host device. Initialising last will allow for a simpler release callback. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hd.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 74569a38698a..e38c41c31f04 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -72,20 +72,12 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, if (!hd) return ERR_PTR(-ENOMEM); - hd->dev.parent = parent; - hd->dev.bus = &greybus_bus_type; - hd->dev.type = &greybus_hd_type; - hd->dev.dma_mask = hd->dev.parent->dma_mask; - device_initialize(&hd->dev); - ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL); if (ret < 0) { kfree(hd); return ERR_PTR(ret); } - hd->bus_id = ret; - dev_set_name(&hd->dev, "greybus%d", hd->bus_id); hd->driver = driver; INIT_LIST_HEAD(&hd->interfaces); @@ -94,6 +86,13 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, hd->buffer_size_max = buffer_size_max; hd->num_cports = num_cports; + hd->dev.parent = parent; + hd->dev.bus = &greybus_bus_type; + hd->dev.type = &greybus_hd_type; + hd->dev.dma_mask = hd->dev.parent->dma_mask; + device_initialize(&hd->dev); + dev_set_name(&hd->dev, "greybus%d", hd->bus_id); + return hd; } EXPORT_SYMBOL_GPL(gb_hd_create); -- cgit v1.2.3-59-g8ed1b From 2c848944c14cf9c74c298b2459cc39dc3c757f25 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:36 +0100 Subject: greybus: hd: make svc-connection life time coincide with host-device Create the svc-connection as part of the host device, and destroy it in the host-device destructor. The svc-connection is enabled when registering the host device, and disabled when the host device is deregistered. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/connection.h | 1 + drivers/staging/greybus/hd.c | 23 +++++++---------------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c93650268b99..38604e8971d1 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -437,7 +437,7 @@ err_unbind_protocol: return ret; } -static void gb_connection_exit(struct gb_connection *connection) +void gb_connection_exit(struct gb_connection *connection) { spin_lock_irq(&connection->lock); if (connection->state != GB_CONNECTION_STATE_ENABLED) { diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 329b705bd425..b795b44c1859 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -64,6 +64,7 @@ static inline bool gb_connection_is_static(struct gb_connection *connection) } int gb_connection_init(struct gb_connection *connection); +void gb_connection_exit(struct gb_connection *connection); void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length); diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index e38c41c31f04..469b31e00237 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -21,6 +21,8 @@ static void gb_hd_release(struct device *dev) { struct gb_host_device *hd = to_gb_host_device(dev); + if (hd->svc_connection) + gb_connection_destroy(hd->svc_connection); ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id); ida_destroy(&hd->cport_id_map); kfree(hd); @@ -93,21 +95,17 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, device_initialize(&hd->dev); dev_set_name(&hd->dev, "greybus%d", hd->bus_id); - return hd; -} -EXPORT_SYMBOL_GPL(gb_hd_create); - -static int gb_hd_create_svc_connection(struct gb_host_device *hd) -{ hd->svc_connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, GREYBUS_PROTOCOL_SVC); if (!hd->svc_connection) { dev_err(&hd->dev, "failed to create svc connection\n"); - return -ENOMEM; + put_device(&hd->dev); + return ERR_PTR(-ENOMEM); } - return 0; + return hd; } +EXPORT_SYMBOL_GPL(gb_hd_create); int gb_hd_add(struct gb_host_device *hd) { @@ -117,15 +115,8 @@ int gb_hd_add(struct gb_host_device *hd) if (ret) return ret; - ret = gb_hd_create_svc_connection(hd); - if (ret) { - device_del(&hd->dev); - return ret; - } - ret = gb_connection_init(hd->svc_connection); if (ret) { - gb_connection_destroy(hd->svc_connection); device_del(&hd->dev); return ret; } @@ -138,7 +129,7 @@ void gb_hd_del(struct gb_host_device *hd) { gb_interfaces_remove(hd); - gb_connection_destroy(hd->svc_connection); + gb_connection_exit(hd->svc_connection); device_del(&hd->dev); } -- cgit v1.2.3-59-g8ed1b From 7adeaae7132e06cd760b86576b8aefd2f0feb4d1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:37 +0100 Subject: greybus: svc: create svc along with host device Create svc device along with host-device and move the svc-connection to the svc structure. The svc connection is enabled when registering the host device, but as the SVC protocol is currently driven by the SVC, we need to defer registration of the svc device to the connection request handler. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hd.c | 15 +++++----- drivers/staging/greybus/hd.h | 2 -- drivers/staging/greybus/svc.c | 68 ++++++++++++++++++++++++++++++++++--------- drivers/staging/greybus/svc.h | 5 ++++ 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 469b31e00237..bff6861b8af7 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -21,8 +21,8 @@ static void gb_hd_release(struct device *dev) { struct gb_host_device *hd = to_gb_host_device(dev); - if (hd->svc_connection) - gb_connection_destroy(hd->svc_connection); + if (hd->svc) + gb_svc_put(hd->svc); ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id); ida_destroy(&hd->cport_id_map); kfree(hd); @@ -95,10 +95,9 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, device_initialize(&hd->dev); dev_set_name(&hd->dev, "greybus%d", hd->bus_id); - hd->svc_connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, - GREYBUS_PROTOCOL_SVC); - if (!hd->svc_connection) { - dev_err(&hd->dev, "failed to create svc connection\n"); + hd->svc = gb_svc_create(hd); + if (!hd->svc) { + dev_err(&hd->dev, "failed to create svc\n"); put_device(&hd->dev); return ERR_PTR(-ENOMEM); } @@ -115,7 +114,7 @@ int gb_hd_add(struct gb_host_device *hd) if (ret) return ret; - ret = gb_connection_init(hd->svc_connection); + ret = gb_svc_add(hd->svc); if (ret) { device_del(&hd->dev); return ret; @@ -129,7 +128,7 @@ void gb_hd_del(struct gb_host_device *hd) { gb_interfaces_remove(hd); - gb_connection_exit(hd->svc_connection); + gb_svc_del(hd->svc); device_del(&hd->dev); } diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 5612b489dd24..d828129475cd 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -41,8 +41,6 @@ struct gb_host_device { size_t buffer_size_max; struct gb_svc *svc; - struct gb_connection *svc_connection; - /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); }; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 2a8f79e95b3c..c013083d79f6 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -661,6 +661,8 @@ static void gb_svc_release(struct device *dev) { struct gb_svc *svc = to_gb_svc(dev); + if (svc->connection) + gb_connection_destroy(svc->connection); ida_destroy(&svc->device_id_map); destroy_workqueue(svc->wq); kfree(svc); @@ -671,19 +673,18 @@ struct device_type greybus_svc_type = { .release = gb_svc_release, }; -static int gb_svc_connection_init(struct gb_connection *connection) +struct gb_svc *gb_svc_create(struct gb_host_device *hd) { - struct gb_host_device *hd = connection->hd; struct gb_svc *svc; svc = kzalloc(sizeof(*svc), GFP_KERNEL); if (!svc) - return -ENOMEM; + return NULL; svc->wq = alloc_workqueue("%s:svc", WQ_UNBOUND, 1, dev_name(&hd->dev)); if (!svc->wq) { kfree(svc); - return -ENOMEM; + return NULL; } svc->dev.parent = &hd->dev; @@ -697,30 +698,71 @@ static int gb_svc_connection_init(struct gb_connection *connection) ida_init(&svc->device_id_map); svc->state = GB_SVC_STATE_RESET; - svc->connection = connection; svc->hd = hd; - connection->private = svc; - hd->svc = svc; + svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, + GREYBUS_PROTOCOL_SVC); + if (!svc->connection) { + dev_err(&svc->dev, "failed to create connection\n"); + put_device(&svc->dev); + return NULL; + } + + svc->connection->private = svc; - return 0; + return svc; } -static void gb_svc_connection_exit(struct gb_connection *connection) +int gb_svc_add(struct gb_svc *svc) { - struct gb_svc *svc = connection->private; + int ret; + /* + * The SVC protocol is currently driven by the SVC, so the SVC device + * is added from the connection request handler when enough + * information has been received. + */ + ret = gb_connection_init(svc->connection); + if (ret) + return ret; + + return 0; +} + +void gb_svc_del(struct gb_svc *svc) +{ + /* + * The SVC device may have been registered from the request handler. + */ if (device_is_registered(&svc->dev)) device_del(&svc->dev); - flush_workqueue(svc->wq); + gb_connection_exit(svc->connection); - connection->hd->svc = NULL; - connection->private = NULL; + flush_workqueue(svc->wq); +} +void gb_svc_put(struct gb_svc *svc) +{ put_device(&svc->dev); } +static int gb_svc_connection_init(struct gb_connection *connection) +{ + struct gb_svc *svc = connection->private; + + dev_dbg(&svc->dev, "%s\n", __func__); + + return 0; +} + +static void gb_svc_connection_exit(struct gb_connection *connection) +{ + struct gb_svc *svc = connection->private; + + dev_dbg(&svc->dev, "%s\n", __func__); +} + static struct gb_protocol svc_protocol = { .name = "svc", .id = GREYBUS_PROTOCOL_SVC, diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index ca0f71d059b1..b7cb7e4c6cf5 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -30,6 +30,11 @@ struct gb_svc { }; #define to_gb_svc(d) container_of(d, struct gb_svc, d) +struct gb_svc *gb_svc_create(struct gb_host_device *hd); +int gb_svc_add(struct gb_svc *svc); +void gb_svc_del(struct gb_svc *svc); +void gb_svc_put(struct gb_svc *svc); + int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id, bool boot_over_unipro); -- cgit v1.2.3-59-g8ed1b From 100e90000840741b630d1a369119ccb57ea4e49c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:38 +0100 Subject: greybus: use decimal notation for interfaces, bundles and cports Fix up the last few places where hexadecimal rather than decimal notation was used for interface, bundle and cport ids. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 5 ++--- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/es2.c | 11 ++++------- drivers/staging/greybus/greybus_trace.h | 4 ++-- drivers/staging/greybus/manifest.c | 2 +- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index e53833697a56..d7975edb3230 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -98,7 +98,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, * the interface bundle list locked here. */ if (gb_bundle_find(intf, bundle_id)) { - pr_err("duplicate bundle id 0x%02x\n", bundle_id); + pr_err("duplicate bundle id %u\n", bundle_id); return NULL; } @@ -122,8 +122,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, retval = device_add(&bundle->dev); if (retval) { - pr_err("failed to add bundle device for id 0x%02x\n", - bundle_id); + pr_err("failed to add bundle device for id %u\n", bundle_id); put_device(&bundle->dev); return NULL; } diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 38604e8971d1..5a24dbef98f2 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -133,7 +133,7 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, * about holding the connection lock. */ if (bundle && gb_connection_intf_find(bundle->intf, cport_id)) { - dev_err(&bundle->dev, "cport 0x%04x already connected\n", + dev_err(&bundle->dev, "cport %u already connected\n", cport_id); return NULL; } diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 02cd2f6b8d3b..b1b6ad3a512b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -348,8 +348,7 @@ static int message_send(struct gb_host_device *hd, u16 cport_id, * the target CPort id before filling it in. */ if (!cport_id_valid(hd, cport_id)) { - dev_err(&udev->dev, "invalid destination cport 0x%02x\n", - cport_id); + dev_err(&udev->dev, "invalid cport %u\n", cport_id); return -EINVAL; } @@ -470,8 +469,7 @@ static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) struct usb_device *udev = es2->usb_dev; if (!cport_id_valid(hd, cport_id)) { - dev_err(&udev->dev, "invalid destination cport 0x%02x\n", - cport_id); + dev_err(&udev->dev, "invalid cport %u\n", cport_id); return -EINVAL; } @@ -494,8 +492,7 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) struct usb_device *udev = es2->usb_dev; if (!cport_id_valid(hd, cport_id)) { - dev_err(&udev->dev, "invalid destination cport 0x%02x\n", - cport_id); + dev_err(&udev->dev, "invalid cport %u\n", cport_id); return -EINVAL; } @@ -632,7 +629,7 @@ static void cport_in_callback(struct urb *urb) greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); } else { - dev_err(dev, "invalid cport id 0x%02x received\n", cport_id); + dev_err(dev, "invalid cport id %u received\n", cport_id); } exit: /* put our urb back in the request pool */ diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 1ca07064f5de..96c515113b98 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -41,7 +41,7 @@ DECLARE_EVENT_CLASS(gb_message, __entry->payload_size = message->payload_size; ), - TP_printk("greybus:%s op=%04x if_id=%04x hd_id=%04x l=%zu", + TP_printk("greybus:%s op=%04x if_id=%u hd_id=%u l=%zu", __get_str(name), __entry->op_id, __entry->intf_cport_id, __entry->hd_cport_id, __entry->payload_size) ); @@ -125,7 +125,7 @@ DECLARE_EVENT_CLASS(gb_host_device, __entry->payload_size = payload_size; ), - TP_printk("greybus:%s if_id=%04x l=%zu", __get_str(name), + TP_printk("greybus:%s if_id=%u l=%zu", __get_str(name), __entry->intf_cport_id, __entry->payload_size) ); diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 2d470500222e..8310f199f0f1 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -323,7 +323,7 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) /* Nothing else should have its class set to control class */ if (class == GREYBUS_CLASS_CONTROL) { dev_err(&intf->dev, - "bundle 0x%02x cannot use control class\n", + "bundle %u cannot use control class\n", bundle_id); goto cleanup; } -- cgit v1.2.3-59-g8ed1b From 1e6fb9a1451f01f8ea092e49f27338ef9d3c7811 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:39 +0100 Subject: greybus: interface: remove defensive WARN_ON Remove defensive WARN_ON testing for a NULL-interface when removing an interface. Every call site has just dereferenced the interface. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index e8eed858b545..27215cf30180 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -132,9 +132,6 @@ void gb_interface_remove(struct gb_interface *intf) struct gb_bundle *bundle; struct gb_bundle *next; - if (WARN_ON(!intf)) - return; - spin_lock_irq(&gb_interfaces_lock); list_del(&intf->links); spin_unlock_irq(&gb_interfaces_lock); -- cgit v1.2.3-59-g8ed1b From 1db1b24304a064b9b4213ba1812450ac054c2c58 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:40 +0100 Subject: greybus: bundle: kill the bundle lock Kill the bundle lock, which looked like it protected the interface bundle lists but really did not as lock-less manipulations were still made. No locking for the interface bundle list is in fact needed as bundles are created along with the interface, and the list is only used to check for duplicate bundle ids when parsing the manifest and to destroy the bundles when removing the interface itself. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 33 +++++++++++++-------------------- drivers/staging/greybus/bundle.h | 2 -- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index d7975edb3230..c2dbd24a80d5 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -65,6 +65,19 @@ static struct attribute *bundle_attrs[] = { ATTRIBUTE_GROUPS(bundle); +static struct gb_bundle *gb_bundle_find(struct gb_interface *intf, + u8 bundle_id) +{ + struct gb_bundle *bundle; + + list_for_each_entry(bundle, &intf->bundles, links) { + if (bundle->id == bundle_id) + return bundle; + } + + return NULL; +} + static void gb_bundle_release(struct device *dev) { struct gb_bundle *bundle = to_gb_bundle(dev); @@ -78,9 +91,6 @@ struct device_type greybus_bundle_type = { .release = gb_bundle_release, }; -/* XXX This could be per-host device or per-module */ -static DEFINE_SPINLOCK(gb_bundles_lock); - /* * Create a gb_bundle structure to represent a discovered * bundle. Returns a pointer to the new bundle or a null @@ -127,9 +137,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, return NULL; } - spin_lock_irq(&gb_bundles_lock); list_add(&bundle->links, &intf->bundles); - spin_unlock_irq(&gb_bundles_lock); return bundle; } @@ -149,25 +157,10 @@ static void gb_bundle_connections_exit(struct gb_bundle *bundle) */ void gb_bundle_destroy(struct gb_bundle *bundle) { - spin_lock_irq(&gb_bundles_lock); list_del(&bundle->links); - spin_unlock_irq(&gb_bundles_lock); gb_bundle_connections_exit(bundle); device_unregister(&bundle->dev); } -struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id) -{ - struct gb_bundle *bundle; - spin_lock_irq(&gb_bundles_lock); - list_for_each_entry(bundle, &intf->bundles, links) - if (bundle->id == bundle_id) - goto found; - bundle = NULL; -found: - spin_unlock_irq(&gb_bundles_lock); - - return bundle; -} diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 70d7b9d897da..eae375c12100 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -33,6 +33,4 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, u8 class); void gb_bundle_destroy(struct gb_bundle *bundle); -struct gb_bundle *gb_bundle_find(struct gb_interface *intf, u8 bundle_id); - #endif /* __BUNDLE_H */ -- cgit v1.2.3-59-g8ed1b From a234792d715345ebcd684196b05ef0f7454a64cb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:41 +0100 Subject: greybus: bundle: use dev_err for bundle-creation errors Use dev_err to report duplicate bundle ids when creating a bundle. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index c2dbd24a80d5..5d058a947413 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -108,7 +108,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, * the interface bundle list locked here. */ if (gb_bundle_find(intf, bundle_id)) { - pr_err("duplicate bundle id %u\n", bundle_id); + dev_err(&intf->dev, "duplicate bundle id %u\n", bundle_id); return NULL; } -- cgit v1.2.3-59-g8ed1b From bdc37354aa933d299c959144487eba777e707b63 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:42 +0100 Subject: greybus: bundle: separate connection disabling and destruction Separate bundle-connection disabling and destruction, and destroy the connections along with the bundle. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 5d058a947413..0f3a00d8d4f2 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -81,6 +81,13 @@ static struct gb_bundle *gb_bundle_find(struct gb_interface *intf, static void gb_bundle_release(struct device *dev) { struct gb_bundle *bundle = to_gb_bundle(dev); + struct gb_connection *connection; + struct gb_connection *tmp; + + list_for_each_entry_safe(connection, tmp, &bundle->connections, + bundle_links) { + gb_connection_destroy(connection); + } kfree(bundle->state); kfree(bundle); @@ -145,11 +152,9 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, static void gb_bundle_connections_exit(struct gb_bundle *bundle) { struct gb_connection *connection; - struct gb_connection *next; - list_for_each_entry_safe(connection, next, &bundle->connections, - bundle_links) - gb_connection_destroy(connection); + list_for_each_entry(connection, &bundle->connections, bundle_links) + gb_connection_exit(connection); } /* -- cgit v1.2.3-59-g8ed1b From a7e36d0eac7fe9d8c66547ac4ec660b026ca4691 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:43 +0100 Subject: greybus: bundle: separate bundle creation and registration Separate bundle creation and registration. Note that the bundle connections still needs to be initialised post registration as protocol drivers create child devices to the bundle. This will ultimately allow connection structures to be created while parsing manifests, but the connections to not be enabled until a driver is bound. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 33 +++++++++++++++++++-------------- drivers/staging/greybus/bundle.h | 1 + drivers/staging/greybus/manifest.c | 26 +++++++++++++++++++------- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 0f3a00d8d4f2..fb775ff1895d 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -107,7 +107,6 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, u8 class) { struct gb_bundle *bundle; - int retval; /* * Reject any attempt to reuse a bundle id. We initialize @@ -128,8 +127,6 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, bundle->class = class; INIT_LIST_HEAD(&bundle->connections); - /* Build up the bundle device structures and register it with the - * driver core */ bundle->dev.parent = &intf->dev; bundle->dev.bus = &greybus_bus_type; bundle->dev.type = &greybus_bundle_type; @@ -137,18 +134,24 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, device_initialize(&bundle->dev); dev_set_name(&bundle->dev, "%s.%d", dev_name(&intf->dev), bundle_id); - retval = device_add(&bundle->dev); - if (retval) { - pr_err("failed to add bundle device for id %u\n", bundle_id); - put_device(&bundle->dev); - return NULL; - } - list_add(&bundle->links, &intf->bundles); return bundle; } +int gb_bundle_add(struct gb_bundle *bundle) +{ + int ret; + + ret = device_add(&bundle->dev); + if (ret) { + dev_err(&bundle->dev, "failed to register bundle: %d\n", ret); + return ret; + } + + return 0; +} + static void gb_bundle_connections_exit(struct gb_bundle *bundle) { struct gb_connection *connection; @@ -162,10 +165,12 @@ static void gb_bundle_connections_exit(struct gb_bundle *bundle) */ void gb_bundle_destroy(struct gb_bundle *bundle) { - list_del(&bundle->links); - gb_bundle_connections_exit(bundle); - device_unregister(&bundle->dev); -} + if (device_is_registered(&bundle->dev)) + device_del(&bundle->dev); + list_del(&bundle->links); + + put_device(&bundle->dev); +} diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index eae375c12100..682ec327f8a3 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -31,6 +31,7 @@ struct gb_bundle { /* Greybus "private" definitions" */ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, u8 class); +int gb_bundle_add(struct gb_bundle *bundle); void gb_bundle_destroy(struct gb_bundle *bundle); #endif /* __BUNDLE_H */ diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 8310f199f0f1..51201128498d 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -236,7 +236,6 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) u8 protocol_id; u16 cport_id; u32 count = 0; - int ret; /* Set up all cport descriptors associated with this bundle */ list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { @@ -262,12 +261,6 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) if (!connection) goto exit; - ret = gb_connection_init(connection); - if (ret) { - gb_connection_destroy(connection); - goto exit; - } - count++; /* Release the cport descriptor */ @@ -293,12 +286,14 @@ exit: */ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) { + struct gb_connection *connection; struct manifest_desc *desc; struct gb_bundle *bundle; struct gb_bundle *bundle_next; u32 count = 0; u8 bundle_id; u8 class; + int ret; while ((desc = get_next_bundle_desc(intf))) { struct greybus_descriptor_bundle *desc_bundle; @@ -354,6 +349,23 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) continue; } + ret = gb_bundle_add(bundle); + if (ret) { + gb_bundle_destroy(bundle); + continue; + } + + list_for_each_entry(connection, &bundle->connections, + bundle_links) { + ret = gb_connection_init(connection); + if (ret) + break; + } + if (ret) { + gb_bundle_destroy(bundle); + continue; + } + count++; } -- cgit v1.2.3-59-g8ed1b From 708d07a9ea2f32ee3cf4fcbbba8d52c42302b39b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:44 +0100 Subject: greybus: interface: separate manifest parsing from bundle registration Separate manifest parsing, including bundle and connection creation, from bundle registration. Note that this is also needed to allow the interface to not be registered until the manifest has been parsed. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 28 ++++++++++++++++++++-------- drivers/staging/greybus/manifest.c | 19 ------------------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 27215cf30180..98a1ba1e485d 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -162,6 +162,7 @@ void gb_interfaces_remove(struct gb_host_device *hd) */ int gb_interface_init(struct gb_interface *intf, u8 device_id) { + struct gb_bundle *bundle, *tmp; struct gb_connection *connection; int ret, size; void *manifest; @@ -215,14 +216,25 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) goto free_manifest; } - /* - * XXX - * We've successfully parsed the manifest. Now we need to - * allocate CPort Id's for connecting to the CPorts found on - * other modules. For each of these, establish a connection - * between the local and remote CPorts (including - * configuring the switch to allow them to communicate). - */ + /* Register the interface bundles. */ + list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) { + ret = gb_bundle_add(bundle); + if (ret) { + gb_bundle_destroy(bundle); + continue; + } + + list_for_each_entry(connection, &bundle->connections, + bundle_links) { + ret = gb_connection_init(connection); + if (ret) + break; + } + if (ret) + gb_bundle_destroy(bundle); + } + + ret = 0; free_manifest: kfree(manifest); diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 51201128498d..357f9c64821b 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -286,14 +286,12 @@ exit: */ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) { - struct gb_connection *connection; struct manifest_desc *desc; struct gb_bundle *bundle; struct gb_bundle *bundle_next; u32 count = 0; u8 bundle_id; u8 class; - int ret; while ((desc = get_next_bundle_desc(intf))) { struct greybus_descriptor_bundle *desc_bundle; @@ -349,23 +347,6 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf) continue; } - ret = gb_bundle_add(bundle); - if (ret) { - gb_bundle_destroy(bundle); - continue; - } - - list_for_each_entry(connection, &bundle->connections, - bundle_links) { - ret = gb_connection_init(connection); - if (ret) - break; - } - if (ret) { - gb_bundle_destroy(bundle); - continue; - } - count++; } -- cgit v1.2.3-59-g8ed1b From ab66dd786b4b56875baabef7c188cff862231717 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:45 +0100 Subject: greybus: interface: fix interface-registration race Fix race with user space when registering the interface. The interface was registered before having been fully initialised, something which could lead to user space accessing not-yet-initialised attribute values (e.g. zero vendor and product ids or empty vendor and product strings). Note that this is also needed to be able to let attribute visibility depend on manifest data (e.g. interface unlock). Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 98a1ba1e485d..1c6a8a4ac4b5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -85,7 +85,6 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id) { struct gb_interface *intf; - int retval; intf = kzalloc(sizeof(*intf), GFP_KERNEL); if (!intf) @@ -107,21 +106,11 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, device_initialize(&intf->dev); dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id); - retval = device_add(&intf->dev); - if (retval) { - pr_err("failed to add interface %u\n", interface_id); - goto free_intf; - } - spin_lock_irq(&gb_interfaces_lock); list_add(&intf->links, &hd->interfaces); spin_unlock_irq(&gb_interfaces_lock); return intf; - -free_intf: - put_device(&intf->dev); - return NULL; } /* @@ -132,17 +121,20 @@ void gb_interface_remove(struct gb_interface *intf) struct gb_bundle *bundle; struct gb_bundle *next; - spin_lock_irq(&gb_interfaces_lock); - list_del(&intf->links); - spin_unlock_irq(&gb_interfaces_lock); - list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); + if (device_is_registered(&intf->dev)) + device_del(&intf->dev); + if (intf->control) gb_connection_destroy(intf->control->connection); - device_unregister(&intf->dev); + spin_lock_irq(&gb_interfaces_lock); + list_del(&intf->links); + spin_unlock_irq(&gb_interfaces_lock); + + put_device(&intf->dev); } void gb_interfaces_remove(struct gb_host_device *hd) @@ -216,7 +208,13 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) goto free_manifest; } - /* Register the interface bundles. */ + /* Register the interface and its bundles. */ + ret = device_add(&intf->dev); + if (ret) { + dev_err(&intf->dev, "failed to register interface: %d\n", ret); + goto free_manifest; + } + list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) { ret = gb_bundle_add(bundle); if (ret) { -- cgit v1.2.3-59-g8ed1b From 5626e0bf99a065d53a0f9966ee335fb0707bd005 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Dec 2015 15:05:46 +0100 Subject: greybus: interface: clean up error messages Drop redundant __func__ and add missing errno values. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 1c6a8a4ac4b5..85fcae76928c 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -179,8 +179,7 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) /* Get manifest size using control protocol on CPort */ size = gb_control_get_manifest_size_operation(intf); if (size <= 0) { - dev_err(&intf->dev, "%s: Failed to get manifest size (%d)\n", - __func__, size); + dev_err(&intf->dev, "failed to get manifest size: %d\n", size); if (size) return size; else @@ -194,7 +193,7 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) /* Get manifest using control protocol on CPort */ ret = gb_control_get_manifest_operation(intf, manifest, size); if (ret) { - dev_err(&intf->dev, "%s: Failed to get manifest\n", __func__); + dev_err(&intf->dev, "failed to get manifest: %d\n", ret); goto free_manifest; } @@ -203,7 +202,7 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) * what's in it. */ if (!gb_manifest_parse(intf, manifest, size)) { - dev_err(&intf->dev, "%s: Failed to parse manifest\n", __func__); + dev_err(&intf->dev, "failed to parse manifest\n"); ret = -EINVAL; goto free_manifest; } -- cgit v1.2.3-59-g8ed1b From a75fd8ba5c8c263d2d0341123822c769e7d95918 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 8 Dec 2015 19:54:59 +0200 Subject: greybus: es2: Reserve CPorts 16 and 17 CPorts 16 and 17 are reserved for CDSI0 and CDSI1 by the ES2 APB, make sure they won't be allocated dynamically. Signed-off-by: Laurent Pinchart Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index b1b6ad3a512b..86972a959b07 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -841,6 +841,7 @@ static int ap_probe(struct usb_interface *interface, int retval = -ENOMEM; int i; int num_cports; + int cport_id; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -859,6 +860,14 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } + /* + * CPorts 16 and 17 are reserved for CDSI0 and CDSI1, make sure they + * won't be allocated dynamically. + */ + do { + cport_id = ida_simple_get(&hd->cport_id_map, 16, 18, GFP_KERNEL); + } while (cport_id > 0); + es2 = hd_to_es2(hd); es2->hd = hd; es2->usb_intf = interface; -- cgit v1.2.3-59-g8ed1b From 50014e0774b25666ebdeb537964527e0b9c95c13 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 10 Dec 2015 14:24:58 +0000 Subject: greybus: spi: move chipselect to one byte size Fixed in the specification, some values for chipselect count and index were different in size, just fix that for all reference to chipselect and move all to one byte size and remove byte order operations. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 ++-- drivers/staging/greybus/spi.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e8c4bce96ee0..d3f957f8afcf 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -662,11 +662,11 @@ struct gb_spi_master_config_response { __le32 max_speed_hz; __le16 mode; __le16 flags; - __le16 num_chipselect; + __u8 num_chipselect; } __packed; struct gb_spi_device_config_request { - __le16 chip_select; + __u8 chip_select; } __packed; struct gb_spi_device_config_response { diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 094d6d8c4f34..15886a27b347 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -20,7 +20,7 @@ struct gb_spi { u16 mode; u16 flags; u32 bits_per_word_mask; - u16 num_chipselect; + u8 num_chipselect; u32 min_speed_hz; u32 max_speed_hz; struct spi_device *spi_devices; @@ -270,7 +270,7 @@ static int gb_spi_get_master_config(struct gb_spi *spi) spi->flags = gb_spi_flags_map(flags); spi->bits_per_word_mask = le32_to_cpu(response.bits_per_word_mask); - spi->num_chipselect = le16_to_cpu(response.num_chipselect); + spi->num_chipselect = response.num_chipselect; spi->min_speed_hz = le32_to_cpu(response.min_speed_hz); spi->max_speed_hz = le32_to_cpu(response.max_speed_hz); @@ -278,7 +278,7 @@ static int gb_spi_get_master_config(struct gb_spi *spi) return 0; } -static int gb_spi_setup_device(struct gb_spi *spi, uint16_t cs) +static int gb_spi_setup_device(struct gb_spi *spi, u8 cs) { struct spi_master *master = get_master_from_spi(spi); struct gb_spi_device_config_request request; @@ -287,7 +287,7 @@ static int gb_spi_setup_device(struct gb_spi *spi, uint16_t cs) struct spi_device *spidev = &spi->spi_devices[cs]; int ret; - request.chip_select = cpu_to_le16(cs); + request.chip_select = cs; ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_DEVICE_CONFIG, &request, sizeof(request), @@ -331,7 +331,7 @@ static int gb_spi_connection_init(struct gb_connection *connection) struct gb_spi *spi; struct spi_master *master; int ret; - int i; + u8 i; /* Allocate master with space for data */ master = spi_alloc_master(&connection->bundle->dev, sizeof(*spi)); -- cgit v1.2.3-59-g8ed1b From 3647a313de367e98840c4dfab491dfbeaf2cf9a4 Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Tue, 8 Dec 2015 18:26:31 +0100 Subject: greybus: loopback: remove mask attribute The mask attribute is not used on the driver anymore and can be removed. Signed-off-by: Axel Haslam Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 626a841b1fe2..6a5eee35e9c0 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -82,7 +82,6 @@ struct gb_loopback { int type; int async; - u32 mask; u32 size; u32 iteration_max; u32 iteration_count; @@ -311,8 +310,6 @@ gb_dev_loopback_rw_attr(us_wait, d); gb_dev_loopback_rw_attr(iteration_max, u); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ gb_dev_loopback_ro_attr(iteration_count, false); -/* A bit-mask of destination connections to include in the test run */ -gb_dev_loopback_rw_attr(mask, u); /* A flag to indicate synchronous or asynchronous operations */ gb_dev_loopback_rw_attr(async, u); /* Timeout of an individual asynchronous request */ @@ -339,7 +336,6 @@ static struct attribute *loopback_attrs[] = { &dev_attr_us_wait.attr, &dev_attr_iteration_count.attr, &dev_attr_iteration_max.attr, - &dev_attr_mask.attr, &dev_attr_async.attr, &dev_attr_error.attr, &dev_attr_requests_completed.attr, -- cgit v1.2.3-59-g8ed1b From 079fa32ba53a205a745e75b045361beeffdfeaba Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Fri, 11 Dec 2015 11:49:45 +0100 Subject: greybus: loopback: register a struct device. Instead of having the loopback attributes in the bundle device, Add a struct device to the gb_loopback struct and register it on connection_init, deregister it at connection_exit, and move the loopback attribute group over to the new device. Use device_create_with_groups to create sysfs attributes together with device. Suggested-by: Bryan O'Donoghue Suggested-by: Johan Hovold Signed-off-by: Axel Haslam Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 95 +++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 6a5eee35e9c0..68372918f653 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -71,6 +71,7 @@ struct gb_loopback { struct mutex mutex; struct task_struct *task; struct list_head entry; + struct device *dev; wait_queue_head_t wq; /* Per connection stats */ @@ -82,6 +83,7 @@ struct gb_loopback { int type; int async; + int id; u32 size; u32 iteration_max; u32 iteration_count; @@ -99,6 +101,12 @@ struct gb_loopback { u32 gpbridge_latency_ts; }; +static struct class loopback_class = { + .name = "gb_loopback", + .owner = THIS_MODULE, +}; +static DEFINE_IDA(loopback_ida); + /* Min/max values in jiffies */ #define GB_LOOPBACK_TIMEOUT_MIN 1 #define GB_LOOPBACK_TIMEOUT_MAX 10000 @@ -119,10 +127,7 @@ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_bundle *bundle; \ - struct gb_loopback *gb; \ - bundle = to_gb_bundle(dev); \ - gb = bundle->private; \ + struct gb_loopback *gb = dev_get_drvdata(dev); \ return sprintf(buf, "%u\n", gb->field); \ } \ static DEVICE_ATTR_RO(field) @@ -132,10 +137,7 @@ static ssize_t name##_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_bundle *bundle; \ - struct gb_loopback *gb; \ - bundle = to_gb_bundle(dev); \ - gb = bundle->private; \ + struct gb_loopback *gb = dev_get_drvdata(dev); \ return sprintf(buf, "%"#type"\n", gb->name.field); \ } \ static DEVICE_ATTR_RO(name##_##field) @@ -146,12 +148,10 @@ static ssize_t name##_avg_show(struct device *dev, \ char *buf) \ { \ struct gb_loopback_stats *stats; \ - struct gb_bundle *bundle; \ struct gb_loopback *gb; \ u64 avg; \ u32 count, rem; \ - bundle = to_gb_bundle(dev); \ - gb = bundle->private; \ + gb = dev_get_drvdata(dev); \ stats = &gb->name; \ count = stats->count ? stats->count : 1; \ avg = stats->sum + count / 2; /* round closest */ \ @@ -170,8 +170,7 @@ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_bundle *bundle = to_gb_bundle(dev); \ - struct gb_loopback *gb = bundle->private; \ + struct gb_loopback *gb = dev_get_drvdata(dev); \ return sprintf(buf, "%"#type"\n", gb->field); \ } \ static ssize_t field##_store(struct device *dev, \ @@ -180,8 +179,7 @@ static ssize_t field##_store(struct device *dev, \ size_t len) \ { \ int ret; \ - struct gb_bundle *bundle = to_gb_bundle(dev); \ - struct gb_loopback *gb = bundle->private; \ + struct gb_loopback *gb = dev_get_drvdata(dev); \ mutex_lock(&gb->mutex); \ ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ @@ -198,8 +196,7 @@ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_bundle *bundle = to_gb_bundle(dev); \ - struct gb_loopback *gb = bundle->private; \ + struct gb_loopback *gb = dev_get_drvdata(dev); \ return sprintf(buf, "%u\n", gb->field); \ } \ static DEVICE_ATTR_RO(field) @@ -209,8 +206,7 @@ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct gb_bundle *bundle = to_gb_bundle(dev); \ - struct gb_loopback *gb = bundle->private; \ + struct gb_loopback *gb = dev_get_drvdata(dev); \ return sprintf(buf, "%"#type"\n", gb->field); \ } \ static ssize_t field##_store(struct device *dev, \ @@ -219,22 +215,20 @@ static ssize_t field##_store(struct device *dev, \ size_t len) \ { \ int ret; \ - struct gb_bundle *bundle = to_gb_bundle(dev); \ - struct gb_loopback *gb = bundle->private; \ + struct gb_loopback *gb = dev_get_drvdata(dev); \ mutex_lock(&gb->mutex); \ ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ len = -EINVAL; \ else \ - gb_loopback_check_attr(gb, bundle); \ + gb_loopback_check_attr(gb); \ mutex_unlock(&gb->mutex); \ return len; \ } \ static DEVICE_ATTR_RW(field) static void gb_loopback_reset_stats(struct gb_loopback *gb); -static void gb_loopback_check_attr(struct gb_loopback *gb, - struct gb_bundle *bundle) +static void gb_loopback_check_attr(struct gb_loopback *gb) { if (gb->us_wait > GB_LOOPBACK_US_WAIT_MAX) gb->us_wait = GB_LOOPBACK_US_WAIT_MAX; @@ -246,7 +240,7 @@ static void gb_loopback_check_attr(struct gb_loopback *gb, gb->error = 0; if (kfifo_depth < gb->iteration_max) { - dev_warn(&bundle->dev, + dev_warn(gb->dev, "cannot log bytes %u kfifo_depth %u\n", gb->iteration_max, kfifo_depth); } @@ -1062,6 +1056,7 @@ static void gb_loopback_insert_id(struct gb_loopback *gb) static int gb_loopback_connection_init(struct gb_connection *connection) { struct gb_loopback *gb; + struct device *dev; int retval; char name[DEBUGFS_NAMELEN]; unsigned long flags; @@ -1095,16 +1090,28 @@ static int gb_loopback_connection_init(struct gb_connection *connection) &gb_loopback_debugfs_latency_ops); gb->connection = connection; connection->bundle->private = gb; - retval = sysfs_create_groups(&connection->bundle->dev.kobj, - loopback_groups); - if (retval) - goto out_sysfs; + + gb->id = ida_simple_get(&loopback_ida, 0, 0, GFP_KERNEL); + if (gb->id < 0) { + retval = gb->id; + goto out_ida; + } + + dev = device_create_with_groups(&loopback_class, + &connection->bundle->dev, + MKDEV(0, 0), gb, loopback_groups, + "gb_loopback%d", gb->id); + if (IS_ERR(dev)) { + retval = PTR_ERR(dev); + goto out_dev; + } + gb->dev = dev; /* Allocate kfifo */ if (kfifo_alloc(&gb->kfifo_lat, kfifo_depth * sizeof(u32), GFP_KERNEL)) { retval = -ENOMEM; - goto out_sysfs_conn; + goto out_conn; } if (kfifo_alloc(&gb->kfifo_ts, kfifo_depth * sizeof(struct timeval) * 2, GFP_KERNEL)) { @@ -1132,9 +1139,11 @@ out_kfifo1: kfifo_free(&gb->kfifo_ts); out_kfifo0: kfifo_free(&gb->kfifo_lat); -out_sysfs_conn: - sysfs_remove_groups(&connection->bundle->dev.kobj, loopback_groups); -out_sysfs: +out_conn: + device_unregister(dev); +out_dev: + ida_simple_remove(&loopback_ida, gb->id); +out_ida: debugfs_remove(gb->file); connection->bundle->private = NULL; out_kzalloc: @@ -1155,8 +1164,6 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); gb_connection_latency_tag_disable(connection); - sysfs_remove_groups(&connection->bundle->dev.kobj, - loopback_groups); debugfs_remove(gb->file); spin_lock_irqsave(&gb_dev.lock, flags); @@ -1164,6 +1171,9 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) list_del(&gb->entry); spin_unlock_irqrestore(&gb_dev.lock, flags); + device_unregister(gb->dev); + ida_simple_remove(&loopback_ida, gb->id); + kfree(gb); } @@ -1186,10 +1196,19 @@ static int loopback_init(void) spin_lock_init(&gb_dev.lock); gb_dev.root = debugfs_create_dir("gb_loopback", NULL); + retval = class_register(&loopback_class); + if (retval) + goto err; + retval = gb_protocol_register(&loopback_protocol); - if (!retval) - return retval; + if (retval) + goto err_unregister; + + return 0; +err_unregister: + class_unregister(&loopback_class); +err: debugfs_remove_recursive(gb_dev.root); return retval; } @@ -1199,6 +1218,8 @@ static void __exit loopback_exit(void) { debugfs_remove_recursive(gb_dev.root); gb_protocol_deregister(&loopback_protocol); + class_unregister(&loopback_class); + ida_destroy(&loopback_ida); } module_exit(loopback_exit); -- cgit v1.2.3-59-g8ed1b From 578a0ab8dd02f1de4b3b0e7b227ff78ae54fa4e1 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 11 Dec 2015 13:46:50 +0000 Subject: greybus: loopback: Drop NULL check on container_of pointer container_of cannot return NULL and the pointer passed to this context uses reference counter bumped inside a spinlock, so the base pointer will be valid at this point. Suggested-by: Johan Hovold Signed-off-by: Bryan O'Donoghue Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 68372918f653..0828772fc901 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -526,9 +526,6 @@ static void gb_loopback_async_operation_work(struct work_struct *work) struct gb_loopback_async_operation *op_async; op_async = container_of(work, struct gb_loopback_async_operation, work); - if (!op_async) - return; - gb = op_async->gb; operation = op_async->operation; -- cgit v1.2.3-59-g8ed1b From c7aae4e613ef471f9f785e4b85de4d46d552c86b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 11 Dec 2015 13:46:51 +0000 Subject: greybus: loopback: Retrun -ENOMEM if operation allocation fails If operation allocation fails we should return -ENOMEM in the asynchronous operation send routine. If we don't return here then the gb_loopback_async_operation_put() later can dereference a NULL pointer if the previous gb_operation_create() failed. Reported-by: Johan Hovold Signed-off-by: Bryan O'Donoghue Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 0828772fc901..eb6a0138fb1d 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -580,8 +580,8 @@ static int gb_loopback_async_operation(struct gb_loopback *gb, int type, operation = gb_operation_create(gb->connection, type, request_size, response_size, GFP_KERNEL); if (!operation) { - ret = -ENOMEM; - goto error; + kfree(op_async); + return -ENOMEM; } if (request_size) -- cgit v1.2.3-59-g8ed1b From 36f241fff4720f205e36bda5900d4ef7b6662bd7 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 11 Dec 2015 13:46:52 +0000 Subject: greybus: loopback: Wait for all async operations to complete on exit On gb_loopback_connection_exit() we should ensure every issued asynchronous operation completes before exiting connection_exit(). This patch introduces a waitqueue with a counter which represents the number of incomplete asynchronous operations. When the counter reaches zero connection_exit() will complete. At the point which we wait for outstanding operations to complete the connection-specific loopback thread will have ceased to issue new operations. Tested with both synchronous and asynchronous operations. Reviewed-by: Johan Hovold Suggested-by: Johan Hovold Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index eb6a0138fb1d..4814f948eea3 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -73,6 +74,8 @@ struct gb_loopback { struct list_head entry; struct device *dev; wait_queue_head_t wq; + wait_queue_head_t wq_completion; + atomic_t outstanding_operations; /* Per connection stats */ struct gb_loopback_stats latency; @@ -433,6 +436,8 @@ static void __gb_loopback_async_operation_destroy(struct kref *kref) list_del(&op_async->entry); if (op_async->operation) gb_operation_put(op_async->operation); + atomic_dec(&op_async->gb->outstanding_operations); + wake_up(&op_async->gb->wq_completion); kfree(op_async); } @@ -472,6 +477,12 @@ static struct gb_loopback_async_operation * return found ? op_async : NULL; } +static void gb_loopback_async_wait_all(struct gb_loopback *gb) +{ + wait_event(gb->wq_completion, + !atomic_read(&gb->outstanding_operations)); +} + static void gb_loopback_async_operation_callback(struct gb_operation *operation) { struct gb_loopback_async_operation *op_async; @@ -597,6 +608,7 @@ static int gb_loopback_async_operation(struct gb_loopback *gb, int type, do_gettimeofday(&op_async->ts); op_async->pending = true; + atomic_inc(&gb->outstanding_operations); ret = gb_operation_request_send(operation, gb_loopback_async_operation_callback, GFP_KERNEL); @@ -1063,6 +1075,8 @@ static int gb_loopback_connection_init(struct gb_connection *connection) return -ENOMEM; init_waitqueue_head(&gb->wq); + init_waitqueue_head(&gb->wq_completion); + atomic_set(&gb->outstanding_operations, 0); gb_loopback_reset_stats(gb); /* Reported values to user-space for min/max timeouts */ @@ -1162,6 +1176,7 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) kfifo_free(&gb->kfifo_ts); gb_connection_latency_tag_disable(connection); debugfs_remove(gb->file); + gb_loopback_async_wait_all(gb); spin_lock_irqsave(&gb_dev.lock, flags); gb_dev.count--; -- cgit v1.2.3-59-g8ed1b From 8e3fba55d379bb67732c878988cf9859bfea2812 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 11 Dec 2015 13:46:53 +0000 Subject: greybus: loopback: Add asynchronous backoff A specific request from the firmware people is the ability to back-off from sending more asynchronous operations once a specific number of operations are in-flight. This patch adds that ability - with a new sysfs parameter 'outstanding_operations_max' which controls the maximum number of operations that can be outstanding/in-flight at any time. When outstanding_operations_max contains a non-zero value and asynchronous operations are being used - we will back-off until the completion counter is < outstanding_operations_max. Tested in both synchronous and asynchronous mode and with gb_loopback_connection_exit() interrupting in-flight operations. Suggested-by: Johan Hovold Signed-off-by: Bryan O'Donoghue Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 4814f948eea3..c35d22733a36 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -98,6 +98,7 @@ struct gb_loopback { u32 jiffy_timeout; u32 timeout_min; u32 timeout_max; + u32 outstanding_operations_max; u32 lbid; u64 elapsed_nsecs; u32 apbridge_latency_ts; @@ -311,6 +312,8 @@ gb_dev_loopback_ro_attr(iteration_count, false); gb_dev_loopback_rw_attr(async, u); /* Timeout of an individual asynchronous request */ gb_dev_loopback_rw_attr(timeout, u); +/* Maximum number of in-flight operations before back-off */ +gb_dev_loopback_rw_attr(outstanding_operations_max, u); static struct attribute *loopback_attrs[] = { &dev_attr_latency_min.attr, @@ -338,6 +341,7 @@ static struct attribute *loopback_attrs[] = { &dev_attr_requests_completed.attr, &dev_attr_requests_timedout.attr, &dev_attr_timeout.attr, + &dev_attr_outstanding_operations_max.attr, &dev_attr_timeout_min.attr, &dev_attr_timeout_max.attr, NULL, @@ -911,6 +915,16 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) gb->gpbridge_latency_ts); } +static void gb_loopback_async_wait_to_send(struct gb_loopback *gb) +{ + if (!(gb->async && gb->outstanding_operations_max)) + return; + wait_event_interruptible(gb->wq_completion, + (atomic_read(&gb->outstanding_operations) < + gb->outstanding_operations_max) || + kthread_should_stop()); +} + static int gb_loopback_fn(void *data) { int error = 0; @@ -924,7 +938,11 @@ static int gb_loopback_fn(void *data) if (!gb->type) wait_event_interruptible(gb->wq, gb->type || kthread_should_stop()); + if (kthread_should_stop()) + break; + /* Limit the maximum number of in-flight async operations */ + gb_loopback_async_wait_to_send(gb); if (kthread_should_stop()) break; -- cgit v1.2.3-59-g8ed1b From e61a2a7114bc9d87d4db2994bbe49a28f8331a3a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 15 Dec 2015 03:18:05 +0200 Subject: greybus: Add camera protocol definition Define the camera data protocol ID and all the protocol operations data structures. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 3 +- drivers/staging/greybus/greybus_protocols.h | 71 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 947b71c88134..32602b1951b8 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -35,7 +35,7 @@ enum greybus_protocol { /* 0x0a is unused */ GREYBUS_PROTOCOL_SPI = 0x0b, GREYBUS_PROTOCOL_DISPLAY = 0x0c, - GREYBUS_PROTOCOL_CAMERA = 0x0d, + GREYBUS_PROTOCOL_CAMERA_MGMT = 0x0d, GREYBUS_PROTOCOL_SENSOR = 0x0e, GREYBUS_PROTOCOL_LIGHTS = 0x0f, GREYBUS_PROTOCOL_VIBRATOR = 0x10, @@ -44,6 +44,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_AUDIO_DATA = 0x13, GREYBUS_PROTOCOL_SVC = 0x14, GREYBUS_PROTOCOL_FIRMWARE = 0x15, + GREYBUS_PROTOCOL_CAMERA_DATA = 0x16, /* ... */ GREYBUS_PROTOCOL_RAW = 0xfe, GREYBUS_PROTOCOL_VENDOR = 0xff, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index d3f957f8afcf..f024acdc0c43 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1100,6 +1100,77 @@ struct gb_sdio_event_request { #define GB_SDIO_WP 0x04 } __packed; +/* Camera */ + +#define GB_CAMERA_VERSION_MAJOR 0x00 +#define GB_CAMERA_VERSION_MINOR 0x01 + +/* Greybus Camera request types */ +#define GB_CAMERA_TYPE_CAPABILITIES 0x02 +#define GB_CAMERA_TYPE_CONFIGURE_STREAMS 0x03 +#define GB_CAMERA_TYPE_CAPTURE 0x04 +#define GB_CAMERA_TYPE_FLUSH 0x05 +#define GB_CAMERA_TYPE_METADATA 0x06 + +#define GB_CAMERA_MAX_STREAMS 4 +#define GB_CAMERA_MAX_SETTINGS_SIZE 8192 + +/* Greybus Camera Configure Streams request payload */ +struct gb_camera_stream_config_request { + __le16 width; + __le16 height; + __le16 format; + __le16 padding; +} __packed; + +struct gb_camera_configure_streams_request { + __le16 num_streams; + __le16 padding; + struct gb_camera_stream_config_request config[0]; +} __packed; + +/* Greybus Camera Configure Streams response payload */ +struct gb_camera_stream_config_response { + __le16 width; + __le16 height; + __le16 format; + __u8 virtual_channel; + __u8 data_type[2]; + __u8 padding[3]; + __le32 max_size; +} __packed; + +struct gb_camera_configure_streams_response { + __le16 num_streams; +#define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED 0x01 + u8 flags; + u8 padding; + struct gb_camera_stream_config_response config[0]; +} __packed; + +/* Greybus Camera Capture request payload - response has no payload */ +struct gb_camera_capture_request { + __le32 request_id; + u8 streams; + u8 padding; + __le16 num_frames; + u8 settings[0]; +} __packed; + +/* Greybus Camera Flush response payload - request has no payload */ +struct gb_camera_flush_response { + __le32 request_id; +} __packed; + +/* Greybus Camera Metadata request payload - operation has no response */ +struct gb_camera_metadata_request { + __le32 request_id; + __le16 frame_number; + __u8 stream; + __u8 padding; + u8 metadata[0]; +} __packed; + /* Lights */ #define GB_LIGHTS_VERSION_MAJOR 0x00 -- cgit v1.2.3-59-g8ed1b From 3265edaf0d70433699eece915fcca6509332c0e8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 15 Dec 2015 03:18:06 +0200 Subject: greybus: Add driver for the camera class protocol Integration with the V4L2 camera drivers isn't available yet, a debugfs interface is exposed instead to call the camera Greybus operations. The debugfs interface will be kept for module testing purpose in order to exercise all the protocol operations with various valid and invalid parameters. Signed-off-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/camera.c | 611 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 613 insertions(+) create mode 100644 drivers/staging/greybus/camera.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index cc5048263111..e9e1cd0d4848 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -30,6 +30,7 @@ gb-hid-y := hid.o gb-es2-y := es2.o gb-db3-y := db3-platform.o gb-audio-codec-y := audio-codec.o +gb-camera-y := camera.o obj-m += greybus.o obj-m += gb-phy.o @@ -42,6 +43,7 @@ obj-m += gb-raw.o obj-m += gb-es2.o obj-m += gb-db3.o obj-m += gb-audio-codec.o +obj-m += gb-camera.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c new file mode 100644 index 000000000000..38b209c93eab --- /dev/null +++ b/drivers/staging/greybus/camera.c @@ -0,0 +1,611 @@ +/* + * Greybus Camera protocol driver. + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "greybus.h" +#include "greybus_protocols.h" + +enum gb_camera_debugs_buffer_id { + GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, + GB_CAMERA_DEBUGFS_BUFFER_STREAMS, + GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, + GB_CAMERA_DEBUGFS_BUFFER_FLUSH, + GB_CAMERA_DEBUGFS_BUFFER_MAX, +}; + +struct gb_camera_debugfs_buffer { + char data[PAGE_SIZE]; + size_t length; +}; + +/** + * struct gb_camera - A Greybus Camera Device + * @connection: the greybus connection for camera control + * @data_connected: whether the data connection has been established + * @debugfs: debugfs entries for camera protocol operations testing + */ +struct gb_camera { + struct gb_connection *connection; + bool data_connected; + + struct { + struct dentry *root; + struct gb_camera_debugfs_buffer *buffers; + } debugfs; +}; + +struct gb_camera_stream_config { + unsigned int width; + unsigned int height; + unsigned int format; + unsigned int vc; + unsigned int dt[2]; + unsigned int max_size; +}; + +#define ES2_APB_CDSI0_CPORT 16 +#define ES2_APB_CDSI1_CPORT 17 + +#define GB_CAMERA_MAX_SETTINGS_SIZE 8192 + +#define gcam_dbg(gcam, format...) \ + dev_dbg(&gcam->connection->bundle->dev, format) +#define gcam_info(gcam, format...) \ + dev_info(&gcam->connection->bundle->dev, format) +#define gcam_err(gcam, format...) \ + dev_err(&gcam->connection->bundle->dev, format) + +/* ----------------------------------------------------------------------------- + * Camera Protocol Operations + */ + +static int gb_camera_configure_streams(struct gb_camera *gcam, + unsigned int nstreams, + struct gb_camera_stream_config *streams) +{ + struct gb_camera_configure_streams_request *req; + struct gb_camera_configure_streams_response *resp; + unsigned int i; + size_t req_size; + size_t resp_size; + int ret; + + if (nstreams > GB_CAMERA_MAX_STREAMS) + return -EINVAL; + + req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); + resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); + + req = kmalloc(req_size, GFP_KERNEL); + resp = kmalloc(resp_size, GFP_KERNEL); + if (!req || !resp) { + ret = -ENOMEM; + goto done; + } + + req->num_streams = nstreams; + req->padding = 0; + + for (i = 0; i < nstreams; ++i) { + struct gb_camera_stream_config_request *cfg = &req->config[i]; + + cfg->width = streams[i].width; + cfg->height = streams[i].height; + cfg->format = streams[i].format; + cfg->padding = 0; + } + + ret = gb_operation_sync(gcam->connection, + GB_CAMERA_TYPE_CONFIGURE_STREAMS, + req, req_size, resp, resp_size); + if (ret < 0) + return ret; + + if (resp->num_streams > nstreams) { + gcam_dbg(gcam, "got #streams %u > request %u\n", + resp->num_streams, nstreams); + ret = -EIO; + goto done; + } + + if (resp->padding != 0) { + gcam_dbg(gcam, "response padding != 0"); + ret = -EIO; + goto done; + } + + for (i = 0; i < nstreams; ++i) { + struct gb_camera_stream_config_response *cfg = &resp->config[i]; + + streams[i].width = cfg->width; + streams[i].height = cfg->height; + streams[i].format = cfg->format; + streams[i].vc = cfg->virtual_channel; + streams[i].dt[0] = cfg->data_type[0]; + streams[i].dt[1] = cfg->data_type[1]; + streams[i].max_size = cfg->max_size; + + if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) { + gcam_dbg(gcam, "stream #%u padding != 0", i); + ret = -EIO; + goto done; + } + } + + ret = resp->num_streams; + +done: + kfree(req); + kfree(resp); + return ret; +} + +static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, + unsigned int streams, unsigned int num_frames, + size_t settings_size, const void *settings) +{ + struct gb_camera_capture_request *req; + size_t req_size; + + if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) + return -EINVAL; + + req_size = sizeof(*req) + settings_size; + req = kmalloc(req_size, GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->request_id = request_id; + req->streams = streams; + req->padding = 0; + req->num_frames = num_frames; + memcpy(req->settings, settings, settings_size); + + return gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, + req, req_size, NULL, 0); +} + +static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) +{ + struct gb_camera_flush_response resp; + int ret; + + ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, + &resp, sizeof(resp)); + if (ret < 0) + return ret; + + if (request_id) + *request_id = resp.request_id; + + return 0; +} + +static int gb_camera_event_recv(u8 type, struct gb_operation *op) +{ + struct gb_camera *gcam = op->connection->private; + struct gb_camera_metadata_request *payload; + struct gb_message *request; + + if (type != GB_CAMERA_TYPE_METADATA) { + gcam_err(gcam, "Unsupported unsolicited event: %u\n", type); + return -EINVAL; + } + + request = op->request; + + if (request->payload_size < sizeof(*payload)) { + gcam_err(gcam, "Wrong event size received (%zu < %zu)\n", + request->payload_size, sizeof(*payload)); + return -EINVAL; + } + + payload = request->payload; + + gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n", + payload->request_id, payload->frame_number, payload->stream); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * DebugFS + */ +static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, + char *buf, size_t len) +{ + return len; +} + +static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, + char *buf, size_t len) +{ + struct gb_camera_debugfs_buffer *buffer = + &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; + struct gb_camera_stream_config *streams; + unsigned int nstreams; + const char *sep = ";"; + unsigned int i; + char *token; + int ret; + + /* Retrieve number of streams to configure */ + token = strsep(&buf, sep); + if (token == NULL) + return -EINVAL; + + ret = kstrtouint(token, 10, &nstreams); + if (ret < 0) + return ret; + + if (nstreams > GB_CAMERA_MAX_STREAMS) + return -EINVAL; + + /* For each stream to configure parse width, height and format */ + streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); + if (!streams) + return -ENOMEM; + + for (i = 0; i < nstreams; ++i) { + struct gb_camera_stream_config *stream = &streams[i]; + + /* width */ + token = strsep(&buf, ";"); + if (token == NULL) { + ret = -EINVAL; + goto done; + } + ret = kstrtouint(token, 10, &stream->width); + if (ret < 0) + goto done; + + /* height */ + token = strsep(&buf, ";"); + if (token == NULL) + goto done; + + ret = kstrtouint(token, 10, &stream->height); + if (ret < 0) + goto done; + + /* Image format code */ + token = strsep(&buf, ";"); + if (token == NULL) + goto done; + + ret = kstrtouint(token, 16, &stream->format); + if (ret < 0) + goto done; + } + + ret = gb_camera_configure_streams(gcam, nstreams, streams); + if (ret < 0) + goto done; + + nstreams = ret; + buffer->length = sprintf(buffer->data, "%u;", nstreams); + + for (i = 0; i < nstreams; ++i) { + struct gb_camera_stream_config *stream = &streams[i]; + + buffer->length += sprintf(buffer->data + buffer->length, + "%u;%u;%u;%u;%u;%u;%u;", + stream->width, stream->height, + stream->format, stream->vc, + stream->dt[0], stream->dt[1], + stream->max_size); + } + + ret = len; + +done: + kfree(streams); + return ret; +}; + +static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam, + char *buf, size_t len) +{ + unsigned int request_id; + unsigned int streams_mask; + unsigned int num_frames; + char *token; + int ret; + + /* Request id */ + token = strsep(&buf, ";"); + if (token == NULL) + return -EINVAL; + ret = kstrtouint(token, 10, &request_id); + if (ret < 0) + return ret; + + /* Stream mask */ + token = strsep(&buf, ";"); + if (token == NULL) + return -EINVAL; + ret = kstrtouint(token, 16, &streams_mask); + if (ret < 0) + return ret; + + /* number of frames */ + token = strsep(&buf, ";"); + if (token == NULL) + return -EINVAL; + ret = kstrtouint(token, 10, &num_frames); + if (ret < 0) + return ret; + + ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0, + NULL); + if (ret < 0) + return ret; + + return len; +} + +static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam, + char *buf, size_t len) +{ + struct gb_camera_debugfs_buffer *buffer = + &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH]; + unsigned int req_id; + int ret; + + ret = gb_camera_flush(gcam, &req_id); + if (ret < 0) + return ret; + + buffer->length = sprintf(buffer->data, "%u", req_id); + + return len; +} + +struct gb_camera_debugfs_entry { + const char *name; + unsigned int mask; + unsigned int buffer; + ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len); +}; + +static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = { + { + .name = "capabilities", + .mask = S_IFREG | S_IRUGO, + .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, + .execute = gb_camera_debugfs_capabilities, + }, { + .name = "configure_streams", + .mask = S_IFREG | S_IRUGO | S_IWUGO, + .buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS, + .execute = gb_camera_debugfs_configure_streams, + }, { + .name = "capture", + .mask = S_IFREG | S_IRUGO | S_IWUGO, + .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, + .execute = gb_camera_debugfs_capture, + }, { + .name = "flush", + .mask = S_IFREG | S_IRUGO | S_IWUGO, + .buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH, + .execute = gb_camera_debugfs_flush, + }, +}; + +static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + const struct gb_camera_debugfs_entry *op = file->private_data; + struct gb_camera *gcam = file->f_inode->i_private; + struct gb_camera_debugfs_buffer *buffer; + ssize_t ret; + + /* For read-only entries the operation is triggered by a read. */ + if (!(op->mask & S_IWUGO)) { + ret = op->execute(gcam, NULL, 0); + if (ret < 0) + return ret; + } + + buffer = &gcam->debugfs.buffers[op->buffer]; + + return simple_read_from_buffer(buf, len, offset, buffer->data, + buffer->length); +} + +static ssize_t gb_camera_debugfs_write(struct file *file, + const char __user *buf, size_t len, + loff_t *offset) +{ + const struct gb_camera_debugfs_entry *op = file->private_data; + struct gb_camera *gcam = file->f_inode->i_private; + ssize_t ret; + char *kbuf; + + if (len > 1024) + return -EINVAL; + + kbuf = kmalloc(len + 1, GFP_KERNEL); + if (kbuf == NULL) + return -ENOMEM; + + if (copy_from_user(kbuf, buf, len)) { + ret = -EFAULT; + goto done; + } + + kbuf[len] = '\0'; + + ret = op->execute(gcam, kbuf, len); + +done: + kfree(kbuf); + return ret; +} + +static int gb_camera_debugfs_open(struct inode *inode, struct file *file) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { + const struct gb_camera_debugfs_entry *entry = + &gb_camera_debugfs_entries[i]; + + if (!strcmp(file->f_dentry->d_iname, entry->name)) { + file->private_data = (void *)entry; + break; + } + } + + return 0; +} + +static const struct file_operations gb_camera_debugfs_ops = { + .open = gb_camera_debugfs_open, + .read = gb_camera_debugfs_read, + .write = gb_camera_debugfs_write, +}; + +static int gb_camera_debugfs_init(struct gb_camera *gcam) +{ + struct gb_connection *connection = gcam->connection; + char dirname[27]; + unsigned int i; + + /* + * Create root debugfs entry and a file entry for each camera operation. + */ + snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, + connection->bundle->id); + + gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); + if (IS_ERR(gcam->debugfs.root)) { + gcam_err(gcam, "debugfs root create failed (%ld)\n", + PTR_ERR(gcam->debugfs.root)); + return PTR_ERR(gcam->debugfs.root); + } + + gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * + GB_CAMERA_DEBUGFS_BUFFER_MAX); + if (!gcam->debugfs.buffers) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { + const struct gb_camera_debugfs_entry *entry = + &gb_camera_debugfs_entries[i]; + struct dentry *dentry; + + gcam->debugfs.buffers[i].length = 0; + + dentry = debugfs_create_file(entry->name, entry->mask, + gcam->debugfs.root, gcam, + &gb_camera_debugfs_ops); + if (IS_ERR(dentry)) { + gcam_err(gcam, + "debugfs operation %s create failed (%ld)\n", + entry->name, PTR_ERR(gcam->debugfs.root)); + return PTR_ERR(dentry); + } + } + + return 0; +} + +static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) +{ + if (gcam->debugfs.root) + debugfs_remove_recursive(gcam->debugfs.root); + + vfree(gcam->debugfs.buffers); +} + +/* ----------------------------------------------------------------------------- + * Init & Cleanup + */ + +static void gb_camera_cleanup(struct gb_camera *gcam) +{ + gb_camera_debugfs_cleanup(gcam); + + if (gcam->data_connected) { + struct gb_interface *intf = gcam->connection->intf; + struct gb_svc *svc = gcam->connection->hd->svc; + + gb_svc_connection_destroy(svc, intf->interface_id, + ES2_APB_CDSI0_CPORT, svc->ap_intf_id, + ES2_APB_CDSI1_CPORT); + } + + kfree(gcam); +} + +static int gb_camera_connection_init(struct gb_connection *connection) +{ + struct gb_svc *svc = connection->hd->svc; + struct gb_camera *gcam; + int ret; + + gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); + if (!gcam) + return -ENOMEM; + + gcam->connection = connection; + connection->private = gcam; + + /* + * Create the data connection between camera module CDSI0 and APB CDS1. + * The CPort IDs are hardcoded by the ES2 bridges. + */ + ret = gb_svc_connection_create(svc, connection->intf->interface_id, + ES2_APB_CDSI0_CPORT, svc->ap_intf_id, + ES2_APB_CDSI1_CPORT, false); + if (ret < 0) + goto error; + + gcam->data_connected = true; + + ret = gb_camera_debugfs_init(gcam); + if (ret < 0) + goto error; + + return 0; + +error: + gb_camera_cleanup(gcam); + return ret; +} + +static void gb_camera_connection_exit(struct gb_connection *connection) +{ + struct gb_camera *gcam = connection->private; + + gb_camera_cleanup(gcam); +} + +static struct gb_protocol camera_protocol = { + .name = "camera", + .id = GREYBUS_PROTOCOL_CAMERA_MGMT, + .major = GB_CAMERA_VERSION_MAJOR, + .minor = GB_CAMERA_VERSION_MINOR, + .connection_init = gb_camera_connection_init, + .connection_exit = gb_camera_connection_exit, + .request_recv = gb_camera_event_recv, +}; + +gb_protocol_driver(&camera_protocol); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 4dda744c2a26d7ee63ceaf834bfe19ab038e4064 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 14 Dec 2015 18:33:19 -0800 Subject: greybus: Camera: remove f_dentry usage On newer kernels f_dentry is gone, so use f_path.dentry instead. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 38b209c93eab..b7d9384e9471 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -465,7 +465,7 @@ static int gb_camera_debugfs_open(struct inode *inode, struct file *file) const struct gb_camera_debugfs_entry *entry = &gb_camera_debugfs_entries[i]; - if (!strcmp(file->f_dentry->d_iname, entry->name)) { + if (!strcmp(file->f_path.dentry->d_iname, entry->name)) { file->private_data = (void *)entry; break; } -- cgit v1.2.3-59-g8ed1b From ed972e3a36c3402479f46f6d0218e7ef586d0d3e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 15 Dec 2015 15:51:48 +0100 Subject: greybus: es2: demote dynamic-urb error message Demote error message when needing to dynamically allocate an URB to debug level. This isn't really an error as much as a reminder of how the current es2 implementation works: It uses a fixed number of pre-allocated URBs, but allocates URBs dynamically when enough messages are sent in rapid succession to exhaust the URB pool. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 86972a959b07..1be4a7c51185 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -274,7 +274,7 @@ static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask) * Crap, pool is empty, complain to the syslog and go allocate one * dynamically as we have to succeed. */ - dev_err(&es2->usb_dev->dev, + dev_dbg(&es2->usb_dev->dev, "No free CPort OUT urbs, having to dynamically allocate one!\n"); return usb_alloc_urb(0, gfp_mask); } -- cgit v1.2.3-59-g8ed1b From c634650ecc5b3db9ac4f1258387b87ff66386906 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 15 Dec 2015 15:28:56 +0100 Subject: greybus: interface: clean up control-connection handling Clean up control-connection handling by managing it through the control structure and a higher-level control interface. Also make both the control structure and connection lifetimes coincide with that of the interface. The control connection is now only enabled and disabled when the interface is initialised and removed. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 57 +++++++++++++++++++++++++++++++------ drivers/staging/greybus/control.h | 5 ++++ drivers/staging/greybus/interface.c | 28 +++++++++--------- 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 2cc1917adda0..9c282e40142b 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -59,31 +59,70 @@ int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id) sizeof(request), NULL, 0); } -static int gb_control_connection_init(struct gb_connection *connection) +struct gb_control *gb_control_create(struct gb_interface *intf) { struct gb_control *control; control = kzalloc(sizeof(*control), GFP_KERNEL); if (!control) - return -ENOMEM; + return NULL; + + control->connection = gb_connection_create_dynamic(intf, NULL, + GB_CONTROL_CPORT_ID, + GREYBUS_PROTOCOL_CONTROL); + if (!control->connection) { + dev_err(&intf->dev, "failed to create control connection\n"); + kfree(control); + return NULL; + } - control->connection = connection; - connection->private = control; + control->connection->private = control; + + return control; +} + +int gb_control_enable(struct gb_control *control) +{ + int ret; - /* Set interface's control connection */ - connection->intf->control = control; + dev_dbg(&control->connection->intf->dev, "%s\n", __func__); + + ret = gb_connection_init(control->connection); + if (ret) { + dev_err(&control->connection->intf->dev, + "failed to enable control connection: %d\n", + ret); + return ret; + } return 0; } -static void gb_control_connection_exit(struct gb_connection *connection) +void gb_control_disable(struct gb_control *control) { - struct gb_control *control = connection->private; + dev_dbg(&control->connection->intf->dev, "%s\n", __func__); - connection->intf->control = NULL; + gb_connection_exit(control->connection); +} + +void gb_control_destroy(struct gb_control *control) +{ + gb_connection_destroy(control->connection); kfree(control); } +static int gb_control_connection_init(struct gb_connection *connection) +{ + dev_dbg(&connection->intf->dev, "%s\n", __func__); + + return 0; +} + +static void gb_control_connection_exit(struct gb_connection *connection) +{ + dev_dbg(&connection->intf->dev, "%s\n", __func__); +} + static struct gb_protocol control_protocol = { .name = "control", .id = GREYBUS_PROTOCOL_CONTROL, diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 3248d965e593..da0fa6652dca 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -14,6 +14,11 @@ struct gb_control { struct gb_connection *connection; }; +struct gb_control *gb_control_create(struct gb_interface *intf); +int gb_control_enable(struct gb_control *control); +void gb_control_disable(struct gb_control *control); +void gb_control_destroy(struct gb_control *control); + int gb_control_connected_operation(struct gb_control *control, u16 cport_id); int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id); int gb_control_get_manifest_size_operation(struct gb_interface *intf); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 85fcae76928c..aa30dc2a9551 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -61,6 +61,9 @@ static void gb_interface_release(struct device *dev) kfree(intf->product_string); kfree(intf->vendor_string); + if (intf->control) + gb_control_destroy(intf->control); + kfree(intf); } @@ -106,6 +109,12 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, device_initialize(&intf->dev); dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id); + intf->control = gb_control_create(intf); + if (!intf->control) { + put_device(&intf->dev); + return NULL; + } + spin_lock_irq(&gb_interfaces_lock); list_add(&intf->links, &hd->interfaces); spin_unlock_irq(&gb_interfaces_lock); @@ -127,8 +136,7 @@ void gb_interface_remove(struct gb_interface *intf) if (device_is_registered(&intf->dev)) device_del(&intf->dev); - if (intf->control) - gb_connection_destroy(intf->control->connection); + gb_control_disable(intf->control); spin_lock_irq(&gb_interfaces_lock); list_del(&intf->links); @@ -161,20 +169,10 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) intf->device_id = device_id; - /* Establish control CPort connection */ - connection = gb_connection_create_dynamic(intf, NULL, - GB_CONTROL_CPORT_ID, - GREYBUS_PROTOCOL_CONTROL); - if (!connection) { - dev_err(&intf->dev, "failed to create control connection\n"); - return -ENOMEM; - } - - ret = gb_connection_init(connection); - if (ret) { - gb_connection_destroy(connection); + /* Establish control connection */ + ret = gb_control_enable(intf->control); + if (ret) return ret; - } /* Get manifest size using control protocol on CPort */ size = gb_control_get_manifest_size_operation(intf); -- cgit v1.2.3-59-g8ed1b From 141af4f05435ac217b9e1fc8260b42e460997fb6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 15 Dec 2015 15:28:57 +0100 Subject: greybus: interface: fix hot-unplug control-connection timeouts Set an interface disconnected flag when the interface has been hot-unplugged (e.g. forcibly removed or after a reboot), and use it to disable the control connection early when deregistering the interface and its bundles. This avoids a one-second (default) timeout for every enabled connection (e.g. one per bundle) at hot-unplug, something which for the default gpbridge manifest currently amounts to five seconds. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 3 +++ drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/svc.c | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index aa30dc2a9551..ecc64bedbef5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -130,6 +130,9 @@ void gb_interface_remove(struct gb_interface *intf) struct gb_bundle *bundle; struct gb_bundle *next; + if (intf->disconnected) + gb_control_disable(intf->control); + list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 61e9c05e75c6..ca123ce11e76 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -35,6 +35,7 @@ struct gb_interface { /* The interface needs to boot over unipro */ bool boot_over_unipro; + bool disconnected; }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index c013083d79f6..5c4ca7938387 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -335,9 +335,10 @@ static int gb_svc_hello(struct gb_operation *op) static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) { u8 intf_id = intf->interface_id; - u8 device_id; + u8 device_id = intf->device_id; + + intf->disconnected = true; - device_id = intf->device_id; gb_interface_remove(intf); /* -- cgit v1.2.3-59-g8ed1b From 5fbd1a0bcb02a2fda30849539dee0b91c4009c42 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 15 Dec 2015 19:09:54 +0000 Subject: greybus: spi: rename label to undo operation Rename error path label to a more significant name related to the free operation done. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 15886a27b347..41aa0ec1ced7 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -346,7 +346,7 @@ static int gb_spi_connection_init(struct gb_connection *connection) ret = gb_spi_init(spi); if (ret) - goto out_err; + goto out_put_master; master->bus_num = -1; /* Allow spi-core to allocate it dynamically */ master->num_chipselect = spi->num_chipselect; @@ -370,7 +370,7 @@ static int gb_spi_connection_init(struct gb_connection *connection) return ret; -out_err: +out_put_master: spi_master_put(master); return ret; -- cgit v1.2.3-59-g8ed1b From a92a2d46acd0bfb97ba409d9d663c270cb240715 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 15 Dec 2015 19:09:55 +0000 Subject: greybus: spi: validate spi master register Check for error in registering spi master, even though the current code will fail a little more ahead when trying to register devices in the master. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/spi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 41aa0ec1ced7..6e1146d474df 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -360,6 +360,8 @@ static int gb_spi_connection_init(struct gb_connection *connection) master->transfer_one_message = gb_spi_transfer_one_message; ret = spi_register_master(master); + if (ret < 0) + goto out_put_master; /* now, fetch the devices configuration */ for (i = 0; i < spi->num_chipselect; i++) { -- cgit v1.2.3-59-g8ed1b From 4a0c4453e3f99335db3e7786b1a66e3b6cfa0653 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 15 Dec 2015 19:09:56 +0000 Subject: greybus: spi: unregister master on device add fail When registering devices if any of it fail, just cleanup and release spi master. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/spi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 6e1146d474df..e49dc56495b5 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -366,8 +366,12 @@ static int gb_spi_connection_init(struct gb_connection *connection) /* now, fetch the devices configuration */ for (i = 0; i < spi->num_chipselect; i++) { ret = gb_spi_setup_device(spi, i); - if (ret < 0) + if (ret < 0) { + dev_err(&connection->bundle->dev, + "failed to allocated spi device: %d\n", ret); + spi_unregister_master(master); break; + } } return ret; -- cgit v1.2.3-59-g8ed1b From 65fabd1848f2ddd00a1af5a1eaeb4f95dcb25b77 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 15 Dec 2015 19:09:57 +0000 Subject: greybus: spi: fix and cleanup spi devices handling Cleanup and remove the spi_devices from the greybus spi handling as they are not needed and they were completely misused. With this the gb_spi_init does not make sense to exist anymore, so just remove it and handle the master config directly from connection init. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/spi.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index e49dc56495b5..ad4a1d62587d 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -23,7 +23,6 @@ struct gb_spi { u8 num_chipselect; u32 min_speed_hz; u32 max_speed_hz; - struct spi_device *spi_devices; }; static struct spi_master *get_master_from_spi(struct gb_spi *spi) @@ -284,7 +283,7 @@ static int gb_spi_setup_device(struct gb_spi *spi, u8 cs) struct gb_spi_device_config_request request; struct gb_spi_device_config_response response; struct spi_board_info spi_board = { {0} }; - struct spi_device *spidev = &spi->spi_devices[cs]; + struct spi_device *spidev; int ret; request.chip_select = cs; @@ -308,24 +307,6 @@ static int gb_spi_setup_device(struct gb_spi *spi, u8 cs) return 0; } -static int gb_spi_init(struct gb_spi *spi) -{ - int ret; - - /* get master configuration */ - ret = gb_spi_get_master_config(spi); - if (ret) - return ret; - - spi->spi_devices = kcalloc(spi->num_chipselect, - sizeof(struct spi_device), GFP_KERNEL); - if (!spi->spi_devices) - return -ENOMEM; - - return ret; -} - - static int gb_spi_connection_init(struct gb_connection *connection) { struct gb_spi *spi; @@ -344,7 +325,8 @@ static int gb_spi_connection_init(struct gb_connection *connection) spi->connection = connection; connection->private = master; - ret = gb_spi_init(spi); + /* get master configuration */ + ret = gb_spi_get_master_config(spi); if (ret) goto out_put_master; -- cgit v1.2.3-59-g8ed1b From 8e77c83e2a6ea1977de0b6e144b90faf1bd4f418 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 17 Dec 2015 19:56:23 +0000 Subject: greybus: camera: fix data types of operations In some operations definitions it was introduce some new fields with the wrong data types, u8, instead of __u8. And because of this gbsim build was broken. Fixes: 3a1d7aa15bf6 ("greybus: Add camera protocol definition") Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index f024acdc0c43..770a162e55f3 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1143,18 +1143,18 @@ struct gb_camera_stream_config_response { struct gb_camera_configure_streams_response { __le16 num_streams; #define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED 0x01 - u8 flags; - u8 padding; + __u8 flags; + __u8 padding; struct gb_camera_stream_config_response config[0]; } __packed; /* Greybus Camera Capture request payload - response has no payload */ struct gb_camera_capture_request { __le32 request_id; - u8 streams; - u8 padding; + __u8 streams; + __u8 padding; __le16 num_frames; - u8 settings[0]; + __u8 settings[0]; } __packed; /* Greybus Camera Flush response payload - request has no payload */ @@ -1168,7 +1168,7 @@ struct gb_camera_metadata_request { __le16 frame_number; __u8 stream; __u8 padding; - u8 metadata[0]; + __u8 metadata[0]; } __packed; /* Lights */ -- cgit v1.2.3-59-g8ed1b From 6b0658f68786110f41cd82558bd97447e4b14203 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 15 Dec 2015 12:46:22 -0800 Subject: greybus: tools: Add tools directory to greybus repo and add loopback Move the loopback test to the greybus main repo, as we will be adding more tests over time and it doesn't need to be burried in the gbsim repo. This moves the latest version from gbsim to this repo and fixes up the Makefile to be a bit more "smart" when building the code. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/.gitignore | 1 + drivers/staging/greybus/Makefile | 6 +- drivers/staging/greybus/tools/Android.mk | 10 + drivers/staging/greybus/tools/Makefile | 31 + drivers/staging/greybus/tools/README.loopback | 198 ++++++ drivers/staging/greybus/tools/lbtest | 168 +++++ drivers/staging/greybus/tools/loopback_test.c | 958 ++++++++++++++++++++++++++ 7 files changed, 1371 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/greybus/tools/Android.mk create mode 100644 drivers/staging/greybus/tools/Makefile create mode 100644 drivers/staging/greybus/tools/README.loopback create mode 100755 drivers/staging/greybus/tools/lbtest create mode 100644 drivers/staging/greybus/tools/loopback_test.c diff --git a/drivers/staging/greybus/.gitignore b/drivers/staging/greybus/.gitignore index 83957733724c..faf45ee4dafd 100644 --- a/drivers/staging/greybus/.gitignore +++ b/drivers/staging/greybus/.gitignore @@ -12,3 +12,4 @@ tags cscope.* ncscope.* *.patch +tools/loopback_test diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index e9e1cd0d4848..7fd1fc392809 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -85,7 +85,10 @@ ccflags-y := -Wall # needed for trace events ccflags-y += -I$(src) -all: module +all: module tools + +tools:: + $(MAKE) -C tools KERNELDIR=$(realpath $(KERNELDIR)) module: $(MAKE) -C $(KERNELDIR) M=$(PWD) @@ -97,6 +100,7 @@ clean: rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c rm -f Module.markers Module.symvers modules.order rm -rf .tmp_versions Modules.symvers + $(MAKE) -C tools clean coccicheck: $(MAKE) -C $(KERNELDIR) M=$(PWD) coccicheck diff --git a/drivers/staging/greybus/tools/Android.mk b/drivers/staging/greybus/tools/Android.mk new file mode 100644 index 000000000000..fdadbf611757 --- /dev/null +++ b/drivers/staging/greybus/tools/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= loopback_test.c +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := gb_loopback_test + +include $(BUILD_EXECUTABLE) + diff --git a/drivers/staging/greybus/tools/Makefile b/drivers/staging/greybus/tools/Makefile new file mode 100644 index 000000000000..852b12b71149 --- /dev/null +++ b/drivers/staging/greybus/tools/Makefile @@ -0,0 +1,31 @@ +ifeq ($(strip $(V)), 1) + Q = +else + Q = @ +endif + +CFLAGS += -std=gnu99 -Wall -Wextra -g \ + -D_GNU_SOURCE \ + -Wno-unused-parameter \ + -Wmaybe-uninitialized \ + -Wredundant-decls \ + -Wcast-align \ + -Wsign-compare \ + -Wno-missing-field-initializers + +CC := $(CROSS_COMPILE)gcc + +TOOLS = loopback_test + +all: $(TOOLS) + +%.o: %.c ../greybus_protocols.h + @echo ' TARGET_CC $@' + $(Q)$(CC) $(CFLAGS) -c $< -o $@ + +loopback_%: loopback_%.o + @echo ' TARGET_LD $@' + $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ + +clean:: + rm -f *.o $(TOOLS) diff --git a/drivers/staging/greybus/tools/README.loopback b/drivers/staging/greybus/tools/README.loopback new file mode 100644 index 000000000000..845b08dc4696 --- /dev/null +++ b/drivers/staging/greybus/tools/README.loopback @@ -0,0 +1,198 @@ + + + 1 - LOOPBACK DRIVER + +The driver implements the main logic of the loopback test and provides +sysfs files to configure the test and retrieve the results. +A user could run a test without the need of the test application given +that he understands the sysfs interface of the loopback driver. + +The loopback kernel driver needs to be loaded and at least one module +with the loopback feature enabled must be present for the sysfs files to be +created and for the loopback test application to be able to run. + +To load the module: +# modprobe gb-loopback + + +When the module is probed, New files are available on the sysfs +directory of the detected loopback device. +(typically under "/sys/bus/graybus/devices"). + +Here is a short summary of the sysfs interface files that should be visible: + +* Loopback Configuration Files: + async - Use asynchronous operations. + iteration_max - Number of tests iterations to perform. + size - payload size of the transfer. + timeout - The number of microseconds to give an individual + asynchronous request before timing out. + us_wait - Time to wait between 2 messages + type - By writing the test type to this file, the test starts. + Valid tests are: + 0 stop the test + 2 - ping + 3 - transfer + 4 - sink + +* Loopback feedback files: + error - number of errors that have occurred. + iteration_count - Number of iterations performed. + requests_completed - Number of requests successfully completed. + requests_timedout - Number of requests that have timed out. + timeout_max - Max allowed timeout + timeout_min - Min allowed timeout. + +* Loopback result files: + apbridge_unipro_latency_avg + apbridge_unipro_latency_max + apbridge_unipro_latency_min + gpbridge_firmware_latency_avg + gpbridge_firmware_latency_max + gpbridge_firmware_latency_min + requests_per_second_avg + requests_per_second_max + requests_per_second_min + latency_avg + latency_max + latency_min + throughput_avg + throughput_max + throughput_min + + + + 2 - LOOPBACK TEST APPLICATION + +The loopback test application manages and formats the results provided by +the loopback kernel module. The purpose of this application +is to: + - Start and manage multiple loopback device tests concurrently. + - Calculate the aggregate results for multiple devices. + - Gather and format test results (csv or human readable). + +The best way to get up to date usage information for the application is +usually to pass the "-h" parameter. +Here is the summary of the available options: + + Mandatory arguments + -t must be one of the test names - sink, transfer or ping + -i iteration count - the number of iterations to run the test over + Optional arguments + -S sysfs location - location for greybus 'endo' entires default /sys/bus/greybus/devices/ + -D debugfs location - location for loopback debugfs entries default /sys/kernel/debug/gb_loopback/ + -s size of data packet to send during test - defaults to zero + -m mask - a bit mask of connections to include example: -m 8 = 4th connection -m 9 = 1st and 4th connection etc + default is zero which means broadcast to all connections + -v verbose output + -d debug output + -r raw data output - when specified the full list of latency values are included in the output CSV + -p porcelain - when specified printout is in a user-friendly non-CSV format. This option suppresses writing to CSV file + -a aggregate - show aggregation of all enabled devies + -l list found loopback devices and exit. + -x Async - Enable async transfers. + -o Timeout - Timeout in microseconds for async operations. + + + + 3 - REAL WORLD EXAMPLE USAGES + + 3.1 - Using the driver sysfs files to run a test on a single device: + +* Run a 1000 transfers of a 100 byte packet. Each transfer is started only +after the previous one finished successfully: + echo 0 > /sys/bus/greybus/devices/1-2.17/type + echo 0 > /sys/bus/greybus/devices/1-2.17/async + echo 2000 > /sys/bus/greybus/devices/1-2.17/us_wait + echo 100 > /sys/bus/greybus/devices/1-2.17/size + echo 1000 > /sys/bus/greybus/devices/1-2.17/iteration_max + echo 0 > /sys/bus/greybus/devices/1-2.17/mask + echo 200000 > /sys/bus/greybus/devices/1-2.17/timeout + echo 3 > /sys/bus/greybus/devices/1-2.17/type + +* Run a 1000 transfers of a 100 byte packet. Transfers are started without +waiting for the previous one to finish: + echo 0 > /sys/bus/greybus/devices/1-2.17/type + echo 3 > /sys/bus/greybus/devices/1-2.17/async + echo 0 > /sys/bus/greybus/devices/1-2.17/us_wait + echo 100 > /sys/bus/greybus/devices/1-2.17/size + echo 1000 > /sys/bus/greybus/devices/1-2.17/iteration_max + echo 0 > /sys/bus/greybus/devices/1-2.17/mask + echo 200000 > /sys/bus/greybus/devices/1-2.17/timeout + echo 3 > /sys/bus/greybus/devices/1-2.17/type + +* Read the results from sysfs: + cat /sys/bus/greybus/devices/1-2.17/requests_per_second_min + cat /sys/bus/greybus/devices/1-2.17/requests_per_second_max + cat /sys/bus/greybus/devices/1-2.17/requests_per_second_avg + + cat /sys/bus/greybus/devices/1-2.17/latency_min + cat /sys/bus/greybus/devices/1-2.17/latency_max + cat /sys/bus/greybus/devices/1-2.17/latency_avg + + cat /sys/bus/greybus/devices/1-2.17/apbridge_unipro_latency_min + cat /sys/bus/greybus/devices/1-2.17/apbridge_unipro_latency_max + cat /sys/bus/greybus/devices/1-2.17/apbridge_unipro_latency_avg + + cat /sys/bus/greybus/devices/1-2.17/gpbridge_firmware_latency_min + cat /sys/bus/greybus/devices/1-2.17/gpbridge_firmware_latency_max + cat /sys/bus/greybus/devices/1-2.17/gpbridge_firmware_latency_avg + + cat /sys/bus/greybus/devices/1-2.17/error + cat /sys/bus/greybus/devices/1-2.17/requests_completed + cat /sys/bus/greybus/devices/1-2.17/requests_timedout + + +3.2 - using the test application: + +* Run a transfer test 10 iterations of size 100 bytes on all available devices + #/loopback_test -t transfer -i 10 -s 100 + 1970-1-1 0:10:7,transfer,1-4.17,100,10,0,443,509,471.700012,66,1963,2256,2124.600098,293,102776,118088,109318.898438,15312,1620,1998,1894.099976,378,56,57,56.799999,1 + 1970-1-1 0:10:7,transfer,1-5.17,100,10,0,399,542,463.399994,143,1845,2505,2175.800049,660,92568,125744,107393.296875,33176,1469,2305,1806.500000,836,56,57,56.799999,1 + + +* Show the aggregate results of both devices. ("-a") + #/loopback_test -t transfer -i 10 -s 100 -a + 1970-1-1 0:10:35,transfer,1-4.17,100,10,0,448,580,494.100006,132,1722,2230,2039.400024,508,103936,134560,114515.703125,30624,1513,1980,1806.900024,467,56,57,57.299999,1 + 1970-1-1 0:10:35,transfer,1-5.17,100,10,0,383,558,478.600006,175,1791,2606,2115.199951,815,88856,129456,110919.703125,40600,1457,2246,1773.599976,789,56,57,57.099998,1 + 1970-1-1 0:10:35,transfer,aggregate,100,10,0,383,580,486.000000,197,1722,2606,2077.000000,884,88856,134560,112717.000000,45704,1457,2246,1789.000000,789,56,57,57.000000,1 + +* Example usage of the mask option to select which devices will + run the test (1st, 2nd, or both devices): + # /loopback_test -t transfer -i 10 -s 100 -m 1 + 1970-1-1 0:11:56,transfer,1-4.17,100,10,0,514,558,544.900024,44,1791,1943,1836.599976,152,119248,129456,126301.296875,10208,1600,1001609,101613.601562,1000009,56,57,56.900002,1 + # /loopback_test -t transfer -i 10 -s 100 -m 2 + 1970-1-1 0:12:0,transfer,1-5.17,100,10,0,468,554,539.000000,86,1804,2134,1859.500000,330,108576,128528,124932.500000,19952,1606,1626,1619.300049,20,56,57,57.400002,1 + # /loopback_test -t transfer -i 10 -s 100 -m 3 + 1970-1-1 0:12:3,transfer,1-4.17,100,10,0,432,510,469.399994,78,1959,2313,2135.800049,354,100224,118320,108785.296875,18096,1610,2024,1893.500000,414,56,57,57.200001,1 + 1970-1-1 0:12:3,transfer,1-5.17,100,10,0,404,542,468.799988,138,1843,2472,2152.500000,629,93728,125744,108646.101562,32016,1504,2247,1853.099976,743,56,57,57.099998,1 + +* Show output in human readable format ("-p") + # /loopback_test -t transfer -i 10 -s 100 -m 3 -p + + 1970-1-1 0:12:37 + test: transfer + path: 1-4.17 + size: 100 + iterations: 10 + errors: 0 + async: Disabled + requests per-sec: min=390, max=547, average=469.299988, jitter=157 + ap-throughput B/s: min=90480 max=126904 average=108762.101562 jitter=36424 + ap-latency usec: min=1826 max=2560 average=2146.000000 jitter=734 + apbridge-latency usec: min=1620 max=1982 average=1882.099976 jitter=362 + gpbridge-latency usec: min=56 max=57 average=57.099998 jitter=1 + + + 1970-1-1 0:12:37 + test: transfer + path: 1-5.17 + size: 100 + iterations: 10 + errors: 0 + async: Disabled + requests per-sec: min=397, max=538, average=461.700012, jitter=141 + ap-throughput B/s: min=92104 max=124816 average=106998.898438 jitter=32712 + ap-latency usec: min=1856 max=2514 average=2185.699951 jitter=658 + apbridge-latency usec: min=1460 max=2296 average=1828.599976 jitter=836 + gpbridge-latency usec: min=56 max=57 average=57.099998 jitter=1 diff --git a/drivers/staging/greybus/tools/lbtest b/drivers/staging/greybus/tools/lbtest new file mode 100755 index 000000000000..d7353f1a2a6f --- /dev/null +++ b/drivers/staging/greybus/tools/lbtest @@ -0,0 +1,168 @@ +#!/usr/bin/env python + +# Copyright (c) 2015 Google, Inc. +# Copyright (c) 2015 Linaro, Ltd. +# 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. +# 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 __future__ import print_function +import csv +import datetime +import sys +import time + +dict = {'ping': '2', 'transfer': '3', 'sink': '4'} +verbose = 1 + +def abort(): + sys.exit(1) + +def usage(): + print('Usage: looptest TEST SIZE ITERATIONS PATH\n\n' + ' Run TEST for a number of ITERATIONS with operation data SIZE bytes\n' + ' TEST may be \'ping\' \'transfer\' or \'sink\'\n' + ' SIZE indicates the size of transfer <= greybus max payload bytes\n' + ' ITERATIONS indicates the number of times to execute TEST at SIZE bytes\n' + ' Note if ITERATIONS is set to zero then this utility will\n' + ' initiate an infinite (non terminating) test and exit\n' + ' without logging any metrics data\n' + ' PATH indicates the sysfs path for the loopback greybus entries e.g.\n' + ' /sys/bus/greybus/devices/endo0:1:1:1:1/\n' + 'Examples:\n' + ' looptest transfer 128 10000\n' + ' looptest ping 0 128\n' + ' looptest sink 2030 32768\n' + .format(sys.argv[0]), file=sys.stderr) + + abort() + +def read_sysfs_int(path): + try: + f = open(path, "r"); + val = f.read(); + f.close() + return int(val) + except IOError as e: + print("I/O error({0}): {1}".format(e.errno, e.strerror)) + print("Invalid path %s" % path) + +def write_sysfs_val(path, val): + try: + f = open(path, "r+") + f.write(val) + f.close() + except IOError as e: + print("I/O error({0}): {1}".format(e.errno, e.strerror)) + print("Invalid path %s" % path) + +def log_csv(test_name, size, iteration_max, sys_pfx): + # file name will test_name_size_iteration_max.csv + # every time the same test with the same parameters is run we will then + # append to the same CSV with datestamp - representing each test dataset + fname = test_name + '_' + size + '_' + str(iteration_max) + '.csv' + + try: + # gather data set + date = str(datetime.datetime.now()) + error = read_sysfs_int(sys_pfx + 'error') + request_min = read_sysfs_int(sys_pfx + 'requests_per_second_min') + request_max = read_sysfs_int(sys_pfx + 'requests_per_second_max') + request_avg = read_sysfs_int(sys_pfx + 'requests_per_second_avg') + latency_min = read_sysfs_int(sys_pfx + 'latency_min') + latency_max = read_sysfs_int(sys_pfx + 'latency_max') + latency_avg = read_sysfs_int(sys_pfx + 'latency_avg') + throughput_min = read_sysfs_int(sys_pfx + 'throughput_min') + throughput_max = read_sysfs_int(sys_pfx + 'throughput_max') + throughput_avg = read_sysfs_int(sys_pfx + 'throughput_avg') + + # derive jitter + request_jitter = request_max - request_min + latency_jitter = latency_max - latency_min + throughput_jitter = throughput_max - throughput_min + + # append data set to file + with open(fname, 'a') as csvf: + row = csv.writer(csvf, delimiter=",", quotechar="'", + quoting=csv.QUOTE_MINIMAL) + row.writerow([date, test_name, size, iteration_max, error, + request_min, request_max, request_avg, request_jitter, + latency_min, latency_max, latency_avg, latency_jitter, + throughput_min, throughput_max, throughput_avg, throughput_jitter]) + except IOError as e: + print("I/O error({0}): {1}".format(e.errno, e.strerror)) + +def loopback_run(test_name, size, iteration_max, sys_pfx): + test_id = dict[test_name] + try: + # Terminate any currently running test + write_sysfs_val(sys_pfx + 'type', '0') + # Set parameter for no wait between messages + write_sysfs_val(sys_pfx + 'ms_wait', '0') + # Set operation size + write_sysfs_val(sys_pfx + 'size', size) + # Set iterations + write_sysfs_val(sys_pfx + 'iteration_max', str(iteration_max)) + # Initiate by setting loopback operation type + write_sysfs_val(sys_pfx + 'type', test_id) + time.sleep(1) + + if iteration_max == 0: + print ("Infinite test initiated CSV won't be logged\n") + return + + previous = 0 + err = 0 + while True: + # get current count bail out if it hasn't changed + iteration_count = read_sysfs_int(sys_pfx + 'iteration_count') + if previous == iteration_count: + err = 1 + break + elif iteration_count == iteration_max: + break + previous = iteration_count + if verbose: + print('%02d%% complete %d of %d ' % + (100 * iteration_count / iteration_max, + iteration_count, iteration_max)) + time.sleep(1) + if err: + print ('\nError executing test\n') + else: + log_csv(test_name, size, iteration_max, sys_pfx) + except ValueError as ve: + print("Error: %s " % format(e.strerror), file=sys.stderr) + abort() + +def main(): + if len(sys.argv) < 5: + usage() + + if sys.argv[1] in dict.keys(): + loopback_run(sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4]) + else: + usage() +if __name__ == '__main__': + main() diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c new file mode 100644 index 000000000000..55b3102d5a6e --- /dev/null +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -0,0 +1,958 @@ +/* + * Loopback test application + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Provided under the three clause BSD license found in the LICENSE file. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_NUM_DEVICES 50 +#define MAX_SYSFS_PATH 0x200 +#define CSV_MAX_LINE 0x1000 +#define SYSFS_MAX_INT 0x20 +#define MAX_STR_LEN 255 +#define MAX_TIMEOUT_COUNT 5 +#define TIMEOUT_SEC 1 +#define DEFAULT_ASYNC_TIMEOUT 200000 + +struct dict { + char *name; + int type; +}; + +static struct dict dict[] = { + {"ping", 2}, + {"transfer", 3}, + {"sink", 4} +}; + +struct loopback_results { + float latency_avg; + uint32_t latency_max; + uint32_t latency_min; + uint32_t latency_jitter; + + float request_avg; + uint32_t request_max; + uint32_t request_min; + uint32_t request_jitter; + + float throughput_avg; + uint32_t throughput_max; + uint32_t throughput_min; + uint32_t throughput_jitter; + + float apbridge_unipro_latency_avg; + uint32_t apbridge_unipro_latency_max; + uint32_t apbridge_unipro_latency_min; + uint32_t apbridge_unipro_latency_jitter; + + float gpbridge_firmware_latency_avg; + uint32_t gpbridge_firmware_latency_max; + uint32_t gpbridge_firmware_latency_min; + uint32_t gpbridge_firmware_latency_jitter; + + uint32_t error; +}; + +struct loopback_device { + char name[MAX_SYSFS_PATH]; + char sysfs_entry[MAX_SYSFS_PATH]; + char debugfs_entry[MAX_SYSFS_PATH]; + int inotify_wd; + struct loopback_results results; +}; + +struct loopback_test { + int verbose; + int debug; + int raw_data_dump; + int porcelain; + int mask; + int size; + int iteration_max; + int aggregate_output; + int test_id; + int device_count; + int inotify_fd; + int list_devices; + int use_async; + int async_timeout; + int async_outstanding_operations; + int us_wait; + char test_name[MAX_STR_LEN]; + char sysfs_prefix[MAX_SYSFS_PATH]; + char debugfs_prefix[MAX_SYSFS_PATH]; + struct loopback_device devices[MAX_NUM_DEVICES]; + struct loopback_results aggregate_results; +}; +struct loopback_test t; + +/* Helper macros to calculate the aggregate results for all devices */ +static inline int device_enabled(struct loopback_test *t, int dev_idx); + +#define GET_MAX(field) \ +static int get_##field##_aggregate(struct loopback_test *t) \ +{ \ + uint32_t max = 0; \ + int i; \ + for (i = 0; i < t->device_count; i++) { \ + if (!device_enabled(t, i)) \ + continue; \ + if (t->devices[i].results.field > max) \ + max = t->devices[i].results.field; \ + } \ + return max; \ +} \ + +#define GET_MIN(field) \ +static int get_##field##_aggregate(struct loopback_test *t) \ +{ \ + uint32_t min = ~0; \ + int i; \ + for (i = 0; i < t->device_count; i++) { \ + if (!device_enabled(t, i)) \ + continue; \ + if (t->devices[i].results.field < min) \ + min = t->devices[i].results.field; \ + } \ + return min; \ +} \ + +#define GET_AVG(field) \ +static int get_##field##_aggregate(struct loopback_test *t) \ +{ \ + uint32_t val = 0; \ + uint32_t count = 0; \ + int i; \ + for (i = 0; i < t->device_count; i++) { \ + if (!device_enabled(t, i)) \ + continue; \ + count++; \ + val += t->devices[i].results.field; \ + } \ + if (count) \ + val /= count; \ + return val; \ +} \ + +GET_MAX(throughput_max); +GET_MAX(request_max); +GET_MAX(latency_max); +GET_MAX(apbridge_unipro_latency_max); +GET_MAX(gpbridge_firmware_latency_max); +GET_MIN(throughput_min); +GET_MIN(request_min); +GET_MIN(latency_min); +GET_MIN(apbridge_unipro_latency_min); +GET_MIN(gpbridge_firmware_latency_min); +GET_AVG(throughput_avg); +GET_AVG(request_avg); +GET_AVG(latency_avg); +GET_AVG(apbridge_unipro_latency_avg); +GET_AVG(gpbridge_firmware_latency_avg); + +void abort() +{ + _exit(1); +} + +void usage(void) +{ + fprintf(stderr, "Usage: loopback_test TEST [SIZE] ITERATIONS [SYSPATH] [DBGPATH]\n\n" + " Run TEST for a number of ITERATIONS with operation data SIZE bytes\n" + " TEST may be \'ping\' \'transfer\' or \'sink\'\n" + " SIZE indicates the size of transfer <= greybus max payload bytes\n" + " ITERATIONS indicates the number of times to execute TEST at SIZE bytes\n" + " Note if ITERATIONS is set to zero then this utility will\n" + " initiate an infinite (non terminating) test and exit\n" + " without logging any metrics data\n" + " SYSPATH indicates the sysfs path for the loopback greybus entries e.g.\n" + " /sys/bus/greybus/devices\n" + " DBGPATH indicates the debugfs path for the loopback greybus entries e.g.\n" + " /sys/kernel/debug/gb_loopback/\n" + " Mandatory arguments\n" + " -t must be one of the test names - sink, transfer or ping\n" + " -i iteration count - the number of iterations to run the test over\n" + " Optional arguments\n" + " -S sysfs location - location for greybus 'endo' entires default /sys/bus/greybus/devices/\n" + " -D debugfs location - location for loopback debugfs entries default /sys/kernel/debug/gb_loopback/\n" + " -s size of data packet to send during test - defaults to zero\n" + " -m mask - a bit mask of connections to include example: -m 8 = 4th connection -m 9 = 1st and 4th connection etc\n" + " default is zero which means broadcast to all connections\n" + " -v verbose output\n" + " -d debug output\n" + " -r raw data output - when specified the full list of latency values are included in the output CSV\n" + " -p porcelain - when specified printout is in a user-friendly non-CSV format. This option suppresses writing to CSV file\n" + " -a aggregate - show aggregation of all enabled devices\n" + " -l list found loopback devices and exit\n" + " -x Async - Enable async transfers\n" + " -o Async Timeout - Timeout in uSec for async operations\n" + " -c Max number of outstanding operations for async operations\n" + " -w Wait in uSec between operations\n" + "Examples:\n" + " Send 10000 transfers with a packet size of 128 bytes to all active connections\n" + " loopback_test -t transfer -s 128 -i 10000 -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n" + " loopback_test -t transfer -s 128 -i 10000 -m 0\n" + " Send 10000 transfers with a packet size of 128 bytes to connection 1 and 4\n" + " loopback_test -t transfer -s 128 -i 10000 -m 9\n" + " loopback_test -t ping -s 0 128 -i -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n" + " loopback_test -t sink -s 2030 -i 32768 -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n"); + abort(); +} + +static inline int device_enabled(struct loopback_test *t, int dev_idx) +{ + if (!t->mask || (t->mask & (1 << dev_idx))) + return 1; + + return 0; +} + +static void show_loopback_devices(struct loopback_test *t) +{ + int i; + + if (t->device_count == 0) { + printf("No loopback devices.\n"); + return; + } + + for (i = 0; i < t->device_count; i++) + printf("device[%d] = %s\n", i, t->devices[i].name); + +} + +int open_sysfs(const char *sys_pfx, const char *node, int flags) +{ + int fd; + char path[MAX_SYSFS_PATH]; + + snprintf(path, sizeof(path), "%s%s", sys_pfx, node); + fd = open(path, flags); + if (fd < 0) { + fprintf(stderr, "unable to open %s\n", path); + abort(); + } + return fd; +} + +int read_sysfs_int_fd(int fd, const char *sys_pfx, const char *node) +{ + char buf[SYSFS_MAX_INT]; + + if (read(fd, buf, sizeof(buf)) < 0) { + fprintf(stderr, "unable to read from %s%s %s\n", sys_pfx, node, + strerror(errno)); + close(fd); + abort(); + } + return atoi(buf); +} + +float read_sysfs_float_fd(int fd, const char *sys_pfx, const char *node) +{ + char buf[SYSFS_MAX_INT]; + + if (read(fd, buf, sizeof(buf)) < 0) { + + fprintf(stderr, "unable to read from %s%s %s\n", sys_pfx, node, + strerror(errno)); + close(fd); + abort(); + } + return atof(buf); +} + +int read_sysfs_int(const char *sys_pfx, const char *node) +{ + int fd, val; + + fd = open_sysfs(sys_pfx, node, O_RDONLY); + val = read_sysfs_int_fd(fd, sys_pfx, node); + close(fd); + return val; +} + +float read_sysfs_float(const char *sys_pfx, const char *node) +{ + int fd; + float val; + + fd = open_sysfs(sys_pfx, node, O_RDONLY); + val = read_sysfs_float_fd(fd, sys_pfx, node); + close(fd); + return val; +} + +void write_sysfs_val(const char *sys_pfx, const char *node, int val) +{ + int fd, len; + char buf[SYSFS_MAX_INT]; + + fd = open_sysfs(sys_pfx, node, O_RDWR); + len = snprintf(buf, sizeof(buf), "%d", val); + if (write(fd, buf, len) < 0) { + fprintf(stderr, "unable to write to %s%s %s\n", sys_pfx, node, + strerror(errno)); + close(fd); + abort(); + } + close(fd); +} + +static int get_results(struct loopback_test *t) +{ + struct loopback_device *d; + struct loopback_results *r; + int i; + + for (i = 0; i < t->device_count; i++) { + if (!device_enabled(t, i)) + continue; + + d = &t->devices[i]; + r = &d->results; + + r->error = read_sysfs_int(d->sysfs_entry, "error"); + r->request_min = read_sysfs_int(d->sysfs_entry, "requests_per_second_min"); + r->request_max = read_sysfs_int(d->sysfs_entry, "requests_per_second_max"); + r->request_avg = read_sysfs_float(d->sysfs_entry, "requests_per_second_avg"); + + r->latency_min = read_sysfs_int(d->sysfs_entry, "latency_min"); + r->latency_max = read_sysfs_int(d->sysfs_entry, "latency_max"); + r->latency_avg = read_sysfs_float(d->sysfs_entry, "latency_avg"); + + r->throughput_min = read_sysfs_int(d->sysfs_entry, "throughput_min"); + r->throughput_max = read_sysfs_int(d->sysfs_entry, "throughput_max"); + r->throughput_avg = read_sysfs_float(d->sysfs_entry, "throughput_avg"); + + r->apbridge_unipro_latency_min = + read_sysfs_int(d->sysfs_entry, "apbridge_unipro_latency_min"); + r->apbridge_unipro_latency_max = + read_sysfs_int(d->sysfs_entry, "apbridge_unipro_latency_max"); + r->apbridge_unipro_latency_avg = + read_sysfs_float(d->sysfs_entry, "apbridge_unipro_latency_avg"); + + r->gpbridge_firmware_latency_min = + read_sysfs_int(d->sysfs_entry, "gpbridge_firmware_latency_min"); + r->gpbridge_firmware_latency_max = + read_sysfs_int(d->sysfs_entry, "gpbridge_firmware_latency_max"); + r->gpbridge_firmware_latency_avg = + read_sysfs_float(d->sysfs_entry, "gpbridge_firmware_latency_avg"); + + r->request_jitter = r->request_max - r->request_min; + r->latency_jitter = r->latency_max - r->latency_min; + r->throughput_jitter = r->throughput_max - r->throughput_min; + r->apbridge_unipro_latency_jitter = + r->apbridge_unipro_latency_max - r->apbridge_unipro_latency_min; + r->gpbridge_firmware_latency_jitter = + r->gpbridge_firmware_latency_max - r->gpbridge_firmware_latency_min; + + } + + /*calculate the aggregate results of all enabled devices */ + if (t->aggregate_output) { + r = &t->aggregate_results; + + r->request_min = get_request_min_aggregate(t); + r->request_max = get_request_max_aggregate(t); + r->request_avg = get_request_avg_aggregate(t); + + r->latency_min = get_latency_min_aggregate(t); + r->latency_max = get_latency_max_aggregate(t); + r->latency_avg = get_latency_avg_aggregate(t); + + r->throughput_min = get_throughput_min_aggregate(t); + r->throughput_max = get_throughput_max_aggregate(t); + r->throughput_avg = get_throughput_avg_aggregate(t); + + r->apbridge_unipro_latency_min = + get_apbridge_unipro_latency_min_aggregate(t); + r->apbridge_unipro_latency_max = + get_apbridge_unipro_latency_max_aggregate(t); + r->apbridge_unipro_latency_avg = + get_apbridge_unipro_latency_avg_aggregate(t); + + r->gpbridge_firmware_latency_min = + get_gpbridge_firmware_latency_min_aggregate(t); + r->gpbridge_firmware_latency_max = + get_gpbridge_firmware_latency_max_aggregate(t); + r->gpbridge_firmware_latency_avg = + get_gpbridge_firmware_latency_avg_aggregate(t); + + r->request_jitter = r->request_max - r->request_min; + r->latency_jitter = r->latency_max - r->latency_min; + r->throughput_jitter = r->throughput_max - r->throughput_min; + r->apbridge_unipro_latency_jitter = + r->apbridge_unipro_latency_max - r->apbridge_unipro_latency_min; + r->gpbridge_firmware_latency_jitter = + r->gpbridge_firmware_latency_max - r->gpbridge_firmware_latency_min; + + } + + return 0; +} + +void log_csv_error(int len, int err) +{ + fprintf(stderr, "unable to write %d bytes to csv %s\n", len, + strerror(err)); +} + +int format_output(struct loopback_test *t, + struct loopback_results *r, + const char *dev_name, + char *buf, int buf_len, + struct tm *tm) +{ + int len = 0; + + memset(buf, 0x00, buf_len); + len = snprintf(buf, buf_len, "%u-%u-%u %u:%u:%u", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + if (t->porcelain) { + len += snprintf(&buf[len], buf_len - len, + "\n test:\t\t\t%s\n path:\t\t\t%s\n size:\t\t\t%u\n iterations:\t\t%u\n errors:\t\t%u\n async:\t\t\t%s\n", + t->test_name, + dev_name, + t->size, + t->iteration_max, + r->error, + t->use_async ? "Enabled" : "Disabled"); + + len += snprintf(&buf[len], buf_len - len, + " requests per-sec:\tmin=%u, max=%u, average=%f, jitter=%u\n", + r->request_min, + r->request_max, + r->request_avg, + r->request_jitter); + + len += snprintf(&buf[len], buf_len - len, + " ap-throughput B/s:\tmin=%u max=%u average=%f jitter=%u\n", + r->throughput_min, + r->throughput_max, + r->throughput_avg, + r->throughput_jitter); + len += snprintf(&buf[len], buf_len - len, + " ap-latency usec:\tmin=%u max=%u average=%f jitter=%u\n", + r->latency_min, + r->latency_max, + r->latency_avg, + r->latency_jitter); + len += snprintf(&buf[len], buf_len - len, + " apbridge-latency usec:\tmin=%u max=%u average=%f jitter=%u\n", + r->apbridge_unipro_latency_min, + r->apbridge_unipro_latency_max, + r->apbridge_unipro_latency_avg, + r->apbridge_unipro_latency_jitter); + + len += snprintf(&buf[len], buf_len - len, + " gpbridge-latency usec:\tmin=%u max=%u average=%f jitter=%u\n", + r->gpbridge_firmware_latency_min, + r->gpbridge_firmware_latency_max, + r->gpbridge_firmware_latency_avg, + r->gpbridge_firmware_latency_jitter); + + } else { + len += snprintf(&buf[len], buf_len- len, ",%s,%s,%u,%u,%u", + t->test_name, dev_name, t->size, t->iteration_max, + r->error); + + len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", + r->request_min, + r->request_max, + r->request_avg, + r->request_jitter); + + len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", + r->latency_min, + r->latency_max, + r->latency_avg, + r->latency_jitter); + + len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", + r->throughput_min, + r->throughput_max, + r->throughput_avg, + r->throughput_jitter); + + len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", + r->apbridge_unipro_latency_min, + r->apbridge_unipro_latency_max, + r->apbridge_unipro_latency_avg, + r->apbridge_unipro_latency_jitter); + + len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", + r->gpbridge_firmware_latency_min, + r->gpbridge_firmware_latency_max, + r->gpbridge_firmware_latency_avg, + r->gpbridge_firmware_latency_jitter); + } + + printf("\n%s\n", buf); + + return len; +} + +static int log_results(struct loopback_test *t) +{ + int fd, i, len, ret; + struct tm tm; + time_t local_time; + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + char file_name[MAX_SYSFS_PATH]; + char data[CSV_MAX_LINE]; + + local_time = time(NULL); + tm = *localtime(&local_time); + + /* + * file name will test_name_size_iteration_max.csv + * every time the same test with the same parameters is run we will then + * append to the same CSV with datestamp - representing each test + * dataset. + */ + if (!t->porcelain) { + snprintf(file_name, sizeof(file_name), "%s_%d_%d.csv", + t->test_name, t->size, t->iteration_max); + + fd = open(file_name, O_WRONLY | O_CREAT | O_APPEND, mode); + if (fd < 0) { + fprintf(stderr, "unable to open %s for appendation\n", file_name); + abort(); + } + + } + for (i = 0; i < t->device_count; i++) { + if (!device_enabled(t, i)) + continue; + + len = format_output(t, &t->devices[i].results, + t->devices[i].name, + data, sizeof(data), &tm); + if (!t->porcelain) { + ret = write(fd, data, len); + if (ret == -1) + fprintf(stderr, "unable to write %d bytes to csv.\n", len); + } + + } + + + if (t->aggregate_output) { + len = format_output(t, &t->aggregate_results, "aggregate", + data, sizeof(data), &tm); + if (!t->porcelain) { + ret = write(fd, data, len); + if (ret == -1) + fprintf(stderr, "unable to write %d bytes to csv.\n", len); + } + } + + if (!t->porcelain) + close(fd); + + return 0; +} + +int is_loopback_device(const char *path, const char *node) +{ + char file[MAX_SYSFS_PATH]; + + snprintf(file, MAX_SYSFS_PATH, "%s%s/iteration_count", path, node); + if (access(file, F_OK) == 0) + return 1; + return 0; +} + +int find_loopback_devices(struct loopback_test *t) +{ + struct dirent **namelist; + int i, n, ret; + unsigned int dev_id; + struct loopback_device *d; + + n = scandir(t->sysfs_prefix, &namelist, NULL, alphasort); + if (n < 0) { + perror("scandir"); + ret = -ENODEV; + goto baddir; + } + + /* Don't include '.' and '..' */ + if (n <= 2) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < n; i++) { + ret = sscanf(namelist[i]->d_name, "gb_loopback%u", &dev_id); + if (ret != 1) + continue; + + if (!is_loopback_device(t->sysfs_prefix, namelist[i]->d_name)) + continue; + + if (t->device_count == MAX_NUM_DEVICES) { + fprintf(stderr, "max number of devices reached!\n"); + break; + } + + d = &t->devices[t->device_count++]; + snprintf(d->name, MAX_STR_LEN, "gb_loopback%u", dev_id); + + snprintf(d->sysfs_entry, MAX_SYSFS_PATH, "%s%s/", + t->sysfs_prefix, d->name); + + snprintf(d->debugfs_entry, MAX_SYSFS_PATH, "%sraw_latency_%s", + t->debugfs_prefix, d->name); + + if (t->debug) + printf("add %s %s\n", d->sysfs_entry, + d->debugfs_entry); + } + + ret = 0; +done: + for (i = 0; i < n; i++) + free(namelist[n]); + free(namelist); +baddir: + return ret; +} + + +static int register_for_notification(struct loopback_test *t) +{ + char buf[MAX_SYSFS_PATH]; + int i; + + t->inotify_fd = inotify_init(); + if (t->inotify_fd < 0) { + fprintf(stderr, "inotify_init fail %s\n", strerror(errno)); + abort(); + } + + for (i = 0; i < t->device_count; i++) { + if (!device_enabled(t, i)) + continue; + + snprintf(buf, sizeof(buf), "%s%s", t->devices[i].sysfs_entry, + "iteration_count"); + + t->devices[i].inotify_wd = inotify_add_watch(t->inotify_fd, + buf, IN_MODIFY); + if (t->devices[i].inotify_wd < 0) { + fprintf(stderr, "inotify_add_watch %s fail %s\n", + buf, strerror(errno)); + close(t->inotify_fd); + abort(); + } + } + + return 0; +} + +static int unregister_for_notification(struct loopback_test *t) +{ + int i; + int ret = 0; + + for (i = 0; i < t->device_count; i++) { + if (!device_enabled(t, i)) + continue; + + ret = inotify_rm_watch(t->inotify_fd, t->devices[i].inotify_wd); + if (ret) { + fprintf(stderr, "inotify_rm_watch error.\n"); + return ret; + } + } + + close(t->inotify_fd); + return 0; +} + +static int is_complete(struct loopback_test *t) +{ + uint32_t iteration_count = 0; + int i; + + for (i = 0; i < t->device_count; i++) { + if (!device_enabled(t, i)) + continue; + + iteration_count = read_sysfs_int(t->devices[i].sysfs_entry, + "iteration_count"); + + /* at least one device did not finish yet */ + if (iteration_count != t->iteration_max) + return 0; + } + + return 1; +} + +static int wait_for_complete(struct loopback_test *t) +{ + int remaining_timeouts = MAX_TIMEOUT_COUNT; + char buf[MAX_SYSFS_PATH]; + struct timeval timeout; + fd_set read_fds; + int ret; + + while (1) { + /* Wait for change */ + timeout.tv_sec = TIMEOUT_SEC; + timeout.tv_usec = 0; + FD_ZERO(&read_fds); + FD_SET(t->inotify_fd, &read_fds); + ret = select(FD_SETSIZE, &read_fds, NULL, NULL, &timeout); + if (ret < 0) { + fprintf(stderr, "Select error.\n"); + return -1; + } + + /* timeout - test may be finished.*/ + if (!FD_ISSET(t->inotify_fd, &read_fds)) { + remaining_timeouts--; + + if (is_complete(t)) + return 0; + + if (!remaining_timeouts) { + fprintf(stderr, "Too many timeouts\n"); + return -1; + } + } else { + /* read to clear the event */ + ret = read(t->inotify_fd, buf, sizeof(buf)); + } + } + + return 0; +} + +static void prepare_devices(struct loopback_test *t) +{ + int i; + + /* Cancel any running tests */ + for (i = 0; i < t->device_count; i++) + write_sysfs_val(t->devices[i].sysfs_entry, "type", 0); + + + for (i = 0; i < t->device_count; i++) { + if (!device_enabled(t, i)) + continue; + + write_sysfs_val(t->devices[i].sysfs_entry, "us_wait", + t->us_wait); + + /* Set operation size */ + write_sysfs_val(t->devices[i].sysfs_entry, "size", t->size); + + /* Set iterations */ + write_sysfs_val(t->devices[i].sysfs_entry, "iteration_max", + t->iteration_max); + + if (t->use_async) { + write_sysfs_val(t->devices[i].sysfs_entry, + "async", 1); + write_sysfs_val(t->devices[i].sysfs_entry, + "timeout", t->async_timeout); + write_sysfs_val(t->devices[i].sysfs_entry, + "outstanding_operations_max", + t->async_outstanding_operations); + } else + write_sysfs_val(t->devices[i].sysfs_entry, + "async", 0); + } +} + +static int start(struct loopback_test *t) +{ + int i; + + /* the test starts by writing test_id to the type file. */ + for (i = 0; i < t->device_count; i++) { + if (!device_enabled(t, i)) + continue; + + write_sysfs_val(t->devices[i].sysfs_entry, "type", t->test_id); + } + + return 0; +} + + +void loopback_run(struct loopback_test *t) +{ + int i; + int ret; + + for (i = 0; i < sizeof(dict) / sizeof(struct dict); i++) { + if (strstr(dict[i].name, t->test_name)) + t->test_id = dict[i].type; + } + if (!t->test_id) { + fprintf(stderr, "invalid test %s\n", t->test_name); + usage(); + return; + } + + prepare_devices(t); + + ret = register_for_notification(t); + if (ret) + goto err; + + start(t); + + sleep(1); + + wait_for_complete(t); + + unregister_for_notification(t); + + get_results(t); + + log_results(t); + + return; + +err: + printf("Error running test\n"); + return; +} + +static int sanity_check(struct loopback_test *t) +{ + int i; + + if (t->device_count == 0) { + fprintf(stderr, "No loopback devices found\n"); + return -1; + } + + for (i = 0; i < MAX_NUM_DEVICES; i++) { + if (!device_enabled(t, i)) + continue; + + if (t->mask && !strcmp(t->devices[i].name, "")) { + fprintf(stderr, "Bad device mask %x\n", (1 << i)); + return -1; + } + + } + + + return 0; +} +int main(int argc, char *argv[]) +{ + int o, ret; + char *sysfs_prefix = "/sys/class/gb_loopback/"; + char *debugfs_prefix = "/sys/kernel/debug/gb_loopback/"; + + memset(&t, 0, sizeof(t)); + + while ((o = getopt(argc, argv, + "t:s:i:S:D:m:v::d::r::p::a::l::x::o:c:w:")) != -1) { + switch (o) { + case 't': + snprintf(t.test_name, MAX_STR_LEN, "%s", optarg); + break; + case 's': + t.size = atoi(optarg); + break; + case 'i': + t.iteration_max = atoi(optarg); + break; + case 'S': + snprintf(t.sysfs_prefix, MAX_SYSFS_PATH, "%s", optarg); + break; + case 'D': + snprintf(t.debugfs_prefix, MAX_SYSFS_PATH, "%s", optarg); + break; + case 'm': + t.mask = atol(optarg); + break; + case 'v': + t.verbose = 1; + break; + case 'd': + t.debug = 1; + break; + case 'r': + t.raw_data_dump = 1; + break; + case 'p': + t.porcelain = 1; + break; + case 'a': + t.aggregate_output = 1; + break; + case 'l': + t.list_devices = 1; + break; + case 'x': + t.use_async = 1; + break; + case 'o': + t.async_timeout = atoi(optarg); + break; + case 'c': + t.async_outstanding_operations = atoi(optarg); + break; + case 'w': + t.us_wait = atoi(optarg); + break; + default: + usage(); + return -EINVAL; + } + } + + if (!strcmp(t.sysfs_prefix, "")) + snprintf(t.sysfs_prefix, MAX_SYSFS_PATH, "%s", sysfs_prefix); + + if (!strcmp(t.debugfs_prefix, "")) + snprintf(t.debugfs_prefix, MAX_SYSFS_PATH, "%s", debugfs_prefix); + + ret = find_loopback_devices(&t); + if (ret) + return ret; + ret = sanity_check(&t); + if (ret) + return ret; + + if (t.list_devices) { + show_loopback_devices(&t); + return 0; + } + + if (t.test_name[0] == '\0' || t.iteration_max == 0) + usage(); + + if (t.async_timeout == 0) + t.async_timeout = DEFAULT_ASYNC_TIMEOUT; + + loopback_run(&t); + + return 0; +} -- cgit v1.2.3-59-g8ed1b From 6bfff1dcb2766ebef60a288601ff4cccbb1f6e54 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 15 Dec 2015 13:55:05 -0800 Subject: greybus: loopback_test: fix warning about signed/unsigned comparison We read an int, don't treat it as a unsigned value, especially when comparing it to a signed value. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/tools/loopback_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index 55b3102d5a6e..5c2a9fcbd68b 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -689,7 +689,7 @@ static int unregister_for_notification(struct loopback_test *t) static int is_complete(struct loopback_test *t) { - uint32_t iteration_count = 0; + int iteration_count; int i; for (i = 0; i < t->device_count; i++) { -- cgit v1.2.3-59-g8ed1b From d5bc9607f749a241b2491f8f8b07986ba7655350 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 15 Dec 2015 13:57:22 -0800 Subject: greybus: loopback_test: null terminate the dict structure This lets us test for any number of entries, no need to do an ARRAY_SIZE-type comparison. This fixes a build warning of comparing signed/unsigned values.) Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/tools/loopback_test.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index 5c2a9fcbd68b..27c733d1c82e 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -34,7 +34,8 @@ struct dict { static struct dict dict[] = { {"ping", 2}, {"transfer", 3}, - {"sink", 4} + {"sink", 4}, + {NULL,} /* list termination */ }; struct loopback_results { @@ -805,7 +806,7 @@ void loopback_run(struct loopback_test *t) int i; int ret; - for (i = 0; i < sizeof(dict) / sizeof(struct dict); i++) { + for (i = 0; dict[i].name != NULL; i++) { if (strstr(dict[i].name, t->test_name)) t->test_id = dict[i].type; } -- cgit v1.2.3-59-g8ed1b From 70b3b3e77a87d40ceae6e5f4f6afedbd725fd636 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 17 Dec 2015 22:32:03 -0800 Subject: greybus: tools: don't always build them. This breaks the kernel-only build as it can't find any userspace headers with the cross-compiler, so don't build the tools by "default" unless you ask for them. Reported-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 7fd1fc392809..29c4c8b25f13 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -85,7 +85,7 @@ ccflags-y := -Wall # needed for trace events ccflags-y += -I$(src) -all: module tools +all: module tools:: $(MAKE) -C tools KERNELDIR=$(realpath $(KERNELDIR)) -- cgit v1.2.3-59-g8ed1b From c6622216ffaacc6286189121e63cdaae1b6bcbce Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 18 Dec 2015 21:23:21 +0200 Subject: greybus: camera: Fix endian conversion issues Convert all Greybus operation fields between CPU and protocol endianness. Signed-off-by: Laurent Pinchart Reported-by: Rui Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index b7d9384e9471..545ed632901a 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -103,9 +103,9 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, for (i = 0; i < nstreams; ++i) { struct gb_camera_stream_config_request *cfg = &req->config[i]; - cfg->width = streams[i].width; - cfg->height = streams[i].height; - cfg->format = streams[i].format; + cfg->width = cpu_to_le16(streams[i].width); + cfg->height = cpu_to_le16(streams[i].height); + cfg->format = cpu_to_le16(streams[i].format); cfg->padding = 0; } @@ -131,13 +131,13 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, for (i = 0; i < nstreams; ++i) { struct gb_camera_stream_config_response *cfg = &resp->config[i]; - streams[i].width = cfg->width; - streams[i].height = cfg->height; - streams[i].format = cfg->format; + streams[i].width = le16_to_cpu(cfg->width); + streams[i].height = le16_to_cpu(cfg->height); + streams[i].format = le16_to_cpu(cfg->format); streams[i].vc = cfg->virtual_channel; streams[i].dt[0] = cfg->data_type[0]; streams[i].dt[1] = cfg->data_type[1]; - streams[i].max_size = cfg->max_size; + streams[i].max_size = le32_to_cpu(cfg->max_size); if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) { gcam_dbg(gcam, "stream #%u padding != 0", i); @@ -169,10 +169,10 @@ static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, if (!req) return -ENOMEM; - req->request_id = request_id; + req->request_id = cpu_to_le32(request_id); req->streams = streams; req->padding = 0; - req->num_frames = num_frames; + req->num_frames = cpu_to_le16(num_frames); memcpy(req->settings, settings, settings_size); return gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, @@ -190,7 +190,7 @@ static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) return ret; if (request_id) - *request_id = resp.request_id; + *request_id = le32_to_cpu(resp.request_id); return 0; } -- cgit v1.2.3-59-g8ed1b From 784f87614a633f9cca12c35ebb3cbdb1e80452c6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 18 Dec 2015 21:23:22 +0200 Subject: greybus: svc: Add support for the link config operation Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 15 +++++++++++++++ drivers/staging/greybus/svc.c | 18 ++++++++++++++++++ drivers/staging/greybus/svc.h | 3 +++ 3 files changed, 36 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 770a162e55f3..a50823c9fae3 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -726,6 +726,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_DME_PEER_SET 0x0a #define GB_SVC_TYPE_ROUTE_CREATE 0x0b #define GB_SVC_TYPE_ROUTE_DESTROY 0x0c +#define GB_SVC_TYPE_LINK_CONFIG 0x0d /* * SVC version request/response has the same payload as @@ -806,6 +807,20 @@ struct gb_svc_dme_peer_set_response { __le16 result_code; } __packed; +#define GB_SVC_LINK_CONFIG_BURST_PWM 0 +#define GB_SVC_LINK_CONFIG_BURST_HS_A 1 +#define GB_SVC_LINK_CONFIG_BURST_HS_B 2 +#define GB_SVC_LINK_CONFIG_FLAG_AUTO_SLEEP (1 << 0) + +struct gb_svc_link_config_request { + __u8 intf_id; + __u8 burst; + __u8 gear; + __u8 nlanes; + __u8 flags; +} __packed; +/* link config response has no payload */ + /* Attributes for peer get/set operations */ #define DME_ATTR_SELECTOR_INDEX 0 #define DME_ATTR_T_TST_SRC_INCREMENT 0x4083 diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5c4ca7938387..ff865b3b4825 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -270,6 +270,24 @@ static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) } } +/* Creates bi-directional routes between the devices */ +int gb_svc_link_config(struct gb_svc *svc, u8 intf_id, + unsigned int burst, unsigned int gear, + unsigned int nlanes, unsigned int flags) +{ + struct gb_svc_link_config_request request; + + request.intf_id = intf_id; + request.burst = burst; + request.gear = gear; + request.nlanes = nlanes; + request.flags = flags; + + return gb_operation_sync(svc->connection, GB_SVC_TYPE_LINK_CONFIG, + &request, sizeof(request), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_svc_link_config); + static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index b7cb7e4c6cf5..23060357e3ad 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -44,6 +44,9 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value); int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 value); +int gb_svc_link_config(struct gb_svc *svc, u8 intf_id, unsigned int burst, + unsigned int gear, unsigned int nlanes, + unsigned int flags); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From bcc050be962179f8004629b0869db8996eb8596c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 18 Dec 2015 21:23:23 +0200 Subject: greybus: camera: Configure link speed for the data connection Hardcode the speed to HS-G1 for now. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 545ed632901a..f163689f50a5 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -576,6 +576,16 @@ static int gb_camera_connection_init(struct gb_connection *connection) if (ret < 0) goto error; + ret = gb_svc_link_config(svc, connection->intf->interface_id, + GB_SVC_LINK_CONFIG_BURST_HS_A, 1, 1, 0); + if (ret < 0) + goto error; + + ret = gb_svc_link_config(svc, svc->ap_intf_id, + GB_SVC_LINK_CONFIG_BURST_HS_A, 1, 1, 0); + if (ret < 0) + goto error; + gcam->data_connected = true; ret = gb_camera_debugfs_init(gcam); -- cgit v1.2.3-59-g8ed1b From 8e2b7daa23760e7be642e87b2ea5fea78a18fd79 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 18 Dec 2015 21:23:24 +0200 Subject: greybus: es2: Add support for CSI transmitter configuration Export a function from the es2 driver to configure the CSI transmitter through the corresponding USB vendor control request. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 48 +++++++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/es2.h | 27 ++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 drivers/staging/greybus/es2.h diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 1be4a7c51185..334ed06b8900 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -13,6 +13,7 @@ #include #include +#include "es2.h" #include "greybus.h" #include "kernel_ver.h" #include "connection.h" @@ -60,6 +61,9 @@ MODULE_DEVICE_TABLE(usb, id_table); #define REQUEST_LATENCY_TAG_EN 0x06 #define REQUEST_LATENCY_TAG_DIS 0x07 +/* vendor request to control the CSI transmitter */ +#define REQUEST_CSI_TX_CONTROL 0x08 + /* * @endpoint: bulk in endpoint for CPort data * @urb: array of urbs for the CPort in messages @@ -130,6 +134,14 @@ struct cport_to_ep { __u8 endpoint_out; }; +struct es2_ap_csi_config_request { + u8 csi_id; + u8 clock_mode; + u8 num_lanes; + u8 padding; + __le32 bus_freq; +} __attribute__((__packed__)); + static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) { return (struct es2_ap_dev *)&hd->hd_priv; @@ -208,6 +220,42 @@ static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id) } #endif +int es2_ap_csi_setup(struct gb_host_device *hd, bool start, + struct es2_ap_csi_config *cfg) +{ + struct es2_ap_csi_config_request cfg_req; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + int retval; + + cfg_req.csi_id = cfg->csi_id; + + if (start) { + cfg_req.clock_mode = cfg->clock_mode; + cfg_req.num_lanes = cfg->num_lanes; + cfg_req.padding = 0; + cfg_req.bus_freq = cfg->bus_freq; + } else { + cfg_req.clock_mode = 0; + cfg_req.num_lanes = 0; + cfg_req.padding = 0; + cfg_req.bus_freq = 0; + } + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_CSI_TX_CONTROL, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, &cfg_req, + sizeof(cfg_req), ES2_TIMEOUT); + if (retval < 0) { + dev_err(&udev->dev, "failed to setup csi: %d\n", retval); + return retval; + } + + return 0; +} +EXPORT_SYMBOL_GPL(es2_ap_csi_setup); + static int es2_cport_in_enable(struct es2_ap_dev *es2, struct es2_cport_in *cport_in) { diff --git a/drivers/staging/greybus/es2.h b/drivers/staging/greybus/es2.h new file mode 100644 index 000000000000..ea2977049481 --- /dev/null +++ b/drivers/staging/greybus/es2.h @@ -0,0 +1,27 @@ +/* + * Greybus "AP" USB driver for "ES2" controller chips + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __ES2_H +#define __ES2_H + +#include + +struct gb_host_device; + +struct es2_ap_csi_config { + u8 csi_id; + u8 clock_mode; + u8 num_lanes; + u32 bus_freq; +}; + +int es2_ap_csi_setup(struct gb_host_device *hd, bool start, + struct es2_ap_csi_config *cfg); + +#endif /* __ES2_H */ -- cgit v1.2.3-59-g8ed1b From 142b21fee07e858a85bb1b8091d9803cf9fa13e3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 18 Dec 2015 21:23:25 +0200 Subject: greybus: camera: Configure the bridge CSI transmitter Start or stop the CSI transmitter when configuring and unconfiguring the streams respectively. The CSI configuration parameters are currently hardcoded. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index f163689f50a5..556226a19ada 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -16,6 +16,7 @@ #include #include +#include "es2.h" #include "greybus.h" #include "greybus_protocols.h" @@ -79,6 +80,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, { struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; + struct es2_ap_csi_config csi_cfg; unsigned int i; size_t req_size; size_t resp_size; @@ -146,6 +148,27 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, } } + /* Configure the CSI transmitter. Hardcode the parameters for now. */ + if (nstreams && !(resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)) { + csi_cfg.csi_id = 1; + csi_cfg.clock_mode = 0; + csi_cfg.num_lanes = 2; + csi_cfg.bus_freq = 250000000; + + ret = es2_ap_csi_setup(gcam->connection->hd, true, &csi_cfg); + } else if (nstreams == 0) { + csi_cfg.csi_id = 1; + csi_cfg.clock_mode = 0; + csi_cfg.num_lanes = 0; + csi_cfg.bus_freq = 0; + + ret = es2_ap_csi_setup(gcam->connection->hd, false, &csi_cfg); + } + + if (ret < 0) + gcam_err(gcam, "failed to %s the CSI transmitter\n", + nstreams ? "start" : "stop"); + ret = resp->num_streams; done: -- cgit v1.2.3-59-g8ed1b From b1c7d67e886be3baf1b2e1f061d09f758709ba9b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 19 Dec 2015 08:38:56 +0200 Subject: greybus: camera: Set link power mode to HS-G2 with 2 lanes HS-G1 isn't enough for all camera modules. The gear will need to be computed dynamically, but for now hardcode it to 2 with 2 lanes. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 556226a19ada..592c3efefb65 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -600,12 +600,12 @@ static int gb_camera_connection_init(struct gb_connection *connection) goto error; ret = gb_svc_link_config(svc, connection->intf->interface_id, - GB_SVC_LINK_CONFIG_BURST_HS_A, 1, 1, 0); + GB_SVC_LINK_CONFIG_BURST_HS_A, 2, 2, 0); if (ret < 0) goto error; ret = gb_svc_link_config(svc, svc->ap_intf_id, - GB_SVC_LINK_CONFIG_BURST_HS_A, 1, 1, 0); + GB_SVC_LINK_CONFIG_BURST_HS_A, 2, 2, 0); if (ret < 0) goto error; -- cgit v1.2.3-59-g8ed1b From 98ce3b0a71c23f924dd031d41171a96eee7fe313 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 22 Dec 2015 03:00:35 +0200 Subject: greybus: camera: Fix remaining endian conversion issues Convert all Greybus operation fields between CPU and protocol endianness. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 592c3efefb65..c742fea29492 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -99,7 +99,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto done; } - req->num_streams = nstreams; + req->num_streams = cpu_to_le16(nstreams); req->padding = 0; for (i = 0; i < nstreams; ++i) { @@ -117,9 +117,9 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, if (ret < 0) return ret; - if (resp->num_streams > nstreams) { + if (le16_to_cpu(resp->num_streams) > nstreams) { gcam_dbg(gcam, "got #streams %u > request %u\n", - resp->num_streams, nstreams); + le16_to_cpu(resp->num_streams), nstreams); ret = -EIO; goto done; } @@ -169,7 +169,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, gcam_err(gcam, "failed to %s the CSI transmitter\n", nstreams ? "start" : "stop"); - ret = resp->num_streams; + ret = le16_to_cpu(resp->num_streams); done: kfree(req); -- cgit v1.2.3-59-g8ed1b From e715a54f4b3fa7d46802984833dc918e44b608e5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 22 Dec 2015 03:00:36 +0200 Subject: greybus: es2: Fix endian conversion issues Convert all USB request fields between CPU and protocol endianness. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 334ed06b8900..5a991c0c4924 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -234,7 +234,7 @@ int es2_ap_csi_setup(struct gb_host_device *hd, bool start, cfg_req.clock_mode = cfg->clock_mode; cfg_req.num_lanes = cfg->num_lanes; cfg_req.padding = 0; - cfg_req.bus_freq = cfg->bus_freq; + cfg_req.bus_freq = cpu_to_le32(cfg->bus_freq); } else { cfg_req.clock_mode = 0; cfg_req.num_lanes = 0; -- cgit v1.2.3-59-g8ed1b From ccb58035915d8e915fbf39c389e95bb11fe9aff3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 22 Dec 2015 03:00:37 +0200 Subject: greybus: es2: Don't use stack memory as USB request data USB request data must be DMAble memory, allocate it with kzalloc() instead of declaring it as a local variable. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 5a991c0c4924..7b07ab70717b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -223,36 +223,33 @@ static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id) int es2_ap_csi_setup(struct gb_host_device *hd, bool start, struct es2_ap_csi_config *cfg) { - struct es2_ap_csi_config_request cfg_req; + struct es2_ap_csi_config_request *cfg_req; struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; int retval; - cfg_req.csi_id = cfg->csi_id; + cfg_req = kzalloc(sizeof(*cfg_req), GFP_KERNEL); + if (!cfg_req) + return -ENOMEM; + + cfg_req->csi_id = cfg->csi_id; if (start) { - cfg_req.clock_mode = cfg->clock_mode; - cfg_req.num_lanes = cfg->num_lanes; - cfg_req.padding = 0; - cfg_req.bus_freq = cpu_to_le32(cfg->bus_freq); - } else { - cfg_req.clock_mode = 0; - cfg_req.num_lanes = 0; - cfg_req.padding = 0; - cfg_req.bus_freq = 0; + cfg_req->clock_mode = cfg->clock_mode; + cfg_req->num_lanes = cfg->num_lanes; + cfg_req->bus_freq = cpu_to_le32(cfg->bus_freq); } retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), REQUEST_CSI_TX_CONTROL, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, 0, 0, &cfg_req, - sizeof(cfg_req), ES2_TIMEOUT); - if (retval < 0) { + USB_RECIP_INTERFACE, 0, 0, cfg_req, + sizeof(*cfg_req), ES2_TIMEOUT); + if (retval < 0) dev_err(&udev->dev, "failed to setup csi: %d\n", retval); - return retval; - } - return 0; + kfree(cfg_req); + return retval; } EXPORT_SYMBOL_GPL(es2_ap_csi_setup); -- cgit v1.2.3-59-g8ed1b From 2c7df74468506f390cd53c0af3457943e8076bd8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 18 Dec 2015 15:04:27 +0530 Subject: greybus: interface: Prefix hexadecimal values with '0x' In order to clearly specify the base of values printed using sysfs files, prefix hexadecimal values with '0x'. Also force the minimum width (to be printed) for hexadecimal values to their sizes. To make it more readable make the second argument to gb_interface_attr() a proper string. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index ecc64bedbef5..c43992d783f4 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -16,15 +16,15 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct gb_interface *intf = to_gb_interface(dev); \ - return scnprintf(buf, PAGE_SIZE, "%"#type"\n", intf->field); \ + return scnprintf(buf, PAGE_SIZE, type"\n", intf->field); \ } \ static DEVICE_ATTR_RO(field) -gb_interface_attr(interface_id, u); -gb_interface_attr(vendor_id, x); -gb_interface_attr(product_id, x); -gb_interface_attr(vendor_string, s); -gb_interface_attr(product_string, s); +gb_interface_attr(interface_id, "%u"); +gb_interface_attr(vendor_id, "0x%08x"); +gb_interface_attr(product_id, "0x%08x"); +gb_interface_attr(vendor_string, "%s"); +gb_interface_attr(product_string, "%s"); static struct attribute *interface_attrs[] = { &dev_attr_interface_id.attr, -- cgit v1.2.3-59-g8ed1b From 61a748a30d883996c80cc2a4cda9b1e056c75c1b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Dec 2015 17:55:52 +0100 Subject: greybus: svc: drop copy-pasted function header Drop function header that was copied but never updated or removed when adding gb_svc_link_config. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ff865b3b4825..24b6cdf2c440 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -270,7 +270,6 @@ static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) } } -/* Creates bi-directional routes between the devices */ int gb_svc_link_config(struct gb_svc *svc, u8 intf_id, unsigned int burst, unsigned int gear, unsigned int nlanes, unsigned int flags) -- cgit v1.2.3-59-g8ed1b From a5a7723a0d3762387fc765e69f0ab61fd4d47f00 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Dec 2015 17:55:53 +0100 Subject: greybus: es2: clean up csi-config request Use __packed and __u8 for csi-config request that is going out on the wire. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 7b07ab70717b..af5d7496c6be 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -135,12 +135,12 @@ struct cport_to_ep { }; struct es2_ap_csi_config_request { - u8 csi_id; - u8 clock_mode; - u8 num_lanes; - u8 padding; + __u8 csi_id; + __u8 clock_mode; + __u8 num_lanes; + __u8 padding; __le32 bus_freq; -} __attribute__((__packed__)); +} __packed; static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) { -- cgit v1.2.3-59-g8ed1b From b32a5c5346bdaac0ad6ef12cb444a9ad69d2b24b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 22 Dec 2015 22:04:33 +0530 Subject: greybus: interface: Prefix DDBL1 attributes with ddbl1_ instead of unipro_ The Device descriptor block Level 1 (DDBL1) attributes are specified by the MIPI standard and prefixing them with 'unipro_' isn't the best thing to do. They should be prefixed with DDBL1 instead. To make it more readable/clear: - rename macros and variable by prefixing them with ddbl1_. - write full names for mfg and prod ids as manufacturer and product ids. - replace mfg (manufacturing) with mfr (manufacturer) Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 10 +++++----- drivers/staging/greybus/greybus_protocols.h | 4 ++-- drivers/staging/greybus/interface.h | 4 ++-- drivers/staging/greybus/svc.c | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 9d1739d78cca..5ec3efdd8f01 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -11,8 +11,8 @@ #include "greybus.h" -#define ES2_UNIPRO_MFG_ID 0x00000126 -#define ES2_UNIPRO_PROD_ID 0x00001000 +#define ES2_DDBL1_MFR_ID 0x00000126 +#define ES2_DDBL1_PROD_ID 0x00001000 struct gb_firmware { struct gb_connection *connection; @@ -49,8 +49,8 @@ static void firmware_es2_fixup_vid_pid(struct gb_firmware *firmware) * - Bridge ASIC chip isn't ES2 * - Received non-zero Vendor/Product ids */ - if (intf->unipro_mfg_id != ES2_UNIPRO_MFG_ID || - intf->unipro_prod_id != ES2_UNIPRO_PROD_ID || + if (intf->ddbl1_manufacturer_id != ES2_DDBL1_MFR_ID || + intf->ddbl1_product_id != ES2_DDBL1_PROD_ID || intf->vendor_id != 0 || intf->product_id != 0) return; @@ -84,7 +84,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) */ snprintf(firmware_name, sizeof(firmware_name), "ara:%08x:%08x:%08x:%08x:%02x.tftf", - intf->unipro_mfg_id, intf->unipro_prod_id, + intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, firmware->vendor_id, firmware->product_id, stage); return request_firmware(&firmware->fw, firmware_name, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index a50823c9fae3..76416b5ad848 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -749,8 +749,8 @@ struct gb_svc_intf_device_id_request { struct gb_svc_intf_hotplug_request { __u8 intf_id; struct { - __le32 unipro_mfg_id; - __le32 unipro_prod_id; + __le32 ddbl1_mfr_id; + __le32 ddbl1_prod_id; __le32 ara_vend_id; __le32 ara_prod_id; } data; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index ca123ce11e76..d192b74ab5e1 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -26,8 +26,8 @@ struct gb_interface { char *product_string; /* Information taken from the hotplug event */ - u32 unipro_mfg_id; - u32 unipro_prod_id; + u32 ddbl1_manufacturer_id; + u32 ddbl1_product_id; u32 vendor_id; u32 product_id; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 24b6cdf2c440..864051b95376 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -417,8 +417,8 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) goto destroy_interface; } - intf->unipro_mfg_id = le32_to_cpu(request->data.unipro_mfg_id); - intf->unipro_prod_id = le32_to_cpu(request->data.unipro_prod_id); + intf->ddbl1_manufacturer_id = le32_to_cpu(request->data.ddbl1_mfr_id); + intf->ddbl1_product_id = le32_to_cpu(request->data.ddbl1_prod_id); intf->vendor_id = le32_to_cpu(request->data.ara_vend_id); intf->product_id = le32_to_cpu(request->data.ara_prod_id); -- cgit v1.2.3-59-g8ed1b From 0e9403a0fbf704b40ae80c2379b643caed924a5e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 22 Dec 2015 22:04:34 +0530 Subject: greybus: interface: Expose DDBL1 manufacturing and production id in sysfs These ids are already fetched from the SVC, but were never exposed to sysfs. Userspace may be interested in using these values and hence these must be exposed to it. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 16 ++++++++++++++++ .../sysfs/greybus1/1-2/ddbl1_manufacturer_id | 0 .../Documentation/sysfs/greybus1/1-2/ddbl1_product_id | 0 .../sysfs/greybus1/1-4/ddbl1_manufacturer_id | 0 .../Documentation/sysfs/greybus1/1-4/ddbl1_product_id | 0 .../sysfs/greybus2/2-3/ddbl1_manufacturer_id | 0 .../Documentation/sysfs/greybus2/2-3/ddbl1_product_id | 0 drivers/staging/greybus/interface.c | 4 ++++ 8 files changed, 20 insertions(+) create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_manufacturer_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_manufacturer_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_manufacturer_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_product_id diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index a0363bfbdd4b..2cd17c706940 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -14,6 +14,22 @@ Description: An Interface I on the bus N, where I is the 1-byte interface ID. +What: /sys/bus/greybus/device/N-I/ddbl1_manufacturer_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + Unipro Device Descriptor Block Level 1 manufacturer ID for the + greybus Interface. + +What: /sys/bus/greybus/device/N-I/ddbl1_product_id +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + Unipro Device Descriptor Block Level 1 product ID for the + greybus Interface. + What: /sys/bus/greybus/device/N-I/interface_id Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_manufacturer_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_manufacturer_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_manufacturer_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c43992d783f4..c4b9e1486828 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -20,6 +20,8 @@ static ssize_t field##_show(struct device *dev, \ } \ static DEVICE_ATTR_RO(field) +gb_interface_attr(ddbl1_manufacturer_id, "0x%08x"); +gb_interface_attr(ddbl1_product_id, "0x%08x"); gb_interface_attr(interface_id, "%u"); gb_interface_attr(vendor_id, "0x%08x"); gb_interface_attr(product_id, "0x%08x"); @@ -27,6 +29,8 @@ gb_interface_attr(vendor_string, "%s"); gb_interface_attr(product_string, "%s"); static struct attribute *interface_attrs[] = { + &dev_attr_ddbl1_manufacturer_id.attr, + &dev_attr_ddbl1_product_id.attr, &dev_attr_interface_id.attr, &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, -- cgit v1.2.3-59-g8ed1b From 3563ff88e65e544f09569195b07841dd48d60e57 Mon Sep 17 00:00:00 2001 From: Eli Sennesh Date: Tue, 22 Dec 2015 17:26:57 -0500 Subject: greybus: firmware and svc: detect the difference between ES2 and ES3 chips The Greybus SVC code needs to read and clear the module boot status upon hotplug; this requires reading two different attributes depending on whether we're running on ES2 or ES3. On Marti Bolivar's (mbolivar@leaflabs.com) advice, we detect ES2 using the unique ES2_DDBL1_MFR_ID and ES2_DDBL1_PROD_ID for ES2 hardware, and treat all other chips as ES3 appropriately. This patch detects the difference and adds the appropriate definitions for ES3 hardware. Signed-off-by: Eli Sennesh Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 3 --- drivers/staging/greybus/greybus_protocols.h | 20 +++++++++----- drivers/staging/greybus/svc.c | 41 ++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 5ec3efdd8f01..c65f2948be02 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -11,9 +11,6 @@ #include "greybus.h" -#define ES2_DDBL1_MFR_ID 0x00000126 -#define ES2_DDBL1_PROD_ID 0x00001000 - struct gb_firmware { struct gb_connection *connection; const struct firmware *fw; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 76416b5ad848..53c1dea6b16d 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -160,6 +160,10 @@ struct gb_control_disconnected_request { #define GB_FIRMWARE_TYPE_AP_READY 0x05 /* Request with no-payload */ #define GB_FIRMWARE_TYPE_GET_VID_PID 0x06 /* Request with no-payload */ +/* FIXME: remove all ES2-specific identifiers from the kernel */ +#define ES2_DDBL1_MFR_ID 0x00000126 +#define ES2_DDBL1_PROD_ID 0x00001000 + /* Greybus firmware boot stages */ #define GB_FIRMWARE_BOOT_STAGE_ONE 0x01 /* Reserved for the boot ROM */ #define GB_FIRMWARE_BOOT_STAGE_TWO 0x02 /* Firmware package to be loaded by the boot ROM */ @@ -823,14 +827,16 @@ struct gb_svc_link_config_request { /* Attributes for peer get/set operations */ #define DME_ATTR_SELECTOR_INDEX 0 +/* FIXME: remove ES2 support and DME_ATTR_T_TST_SRC_INCREMENT */ #define DME_ATTR_T_TST_SRC_INCREMENT 0x4083 - -/* Return value from TST_SRC_INCREMENT */ -#define DME_TSI_SPI_BOOT_STARTED 0x02 -#define DME_TSI_TRUSTED_SPI_BOOT_FINISHED 0x03 -#define DME_TSI_UNTRUSTED_SPI_BOOT_FINISHED 0x04 -#define DME_TSI_UNIPRO_BOOT_STARTED 0x06 -#define DME_TSI_FALLBACK_UNIPRO_BOOT_STARTED 0x09 +#define DME_ATTR_ES3_INIT_STATUS 0x6101 + +/* Return value from init-status attributes listed above */ +#define DME_DIS_SPI_BOOT_STARTED 0x02 +#define DME_DIS_TRUSTED_SPI_BOOT_FINISHED 0x03 +#define DME_DIS_UNTRUSTED_SPI_BOOT_FINISHED 0x04 +#define DME_DIS_UNIPRO_BOOT_STARTED 0x06 +#define DME_DIS_FALLBACK_UNIPRO_BOOT_STARTED 0x09 struct gb_svc_route_create_request { __u8 intf1_id; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 864051b95376..3e760e604b37 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -139,8 +139,8 @@ EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set); /* * T_TstSrcIncrement is written by the module on ES2 as a stand-in for boot - * status attribute. AP needs to read and clear it, after reading a non-zero - * value from it. + * status attribute ES3_INIT_STATUS. AP needs to read and clear it, after + * reading a non-zero value from it. * * FIXME: This is module-hardware dependent and needs to be extended for every * type of module we want to support. @@ -150,10 +150,22 @@ static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf) struct gb_host_device *hd = intf->hd; int ret; u32 value; + u16 attr; + u8 init_status; - /* Read and clear boot status in T_TstSrcIncrement */ - ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, - DME_ATTR_T_TST_SRC_INCREMENT, + /* + * Check if the module is ES2 or ES3, and choose attr number + * appropriately. + * FIXME: Remove ES2 support from the kernel entirely. + */ + if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && + intf->ddbl1_product_id == ES2_DDBL1_PROD_ID) + attr = DME_ATTR_T_TST_SRC_INCREMENT; + else + attr = DME_ATTR_ES3_INIT_STATUS; + + /* Read and clear boot status in ES3_INIT_STATUS */ + ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr, DME_ATTR_SELECTOR_INDEX, &value); if (ret) @@ -169,19 +181,22 @@ static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf) } /* - * Check if the module needs to boot from unipro. + * Check if the module needs to boot from UniPro. * For ES2: We need to check lowest 8 bits of 'value'. * For ES3: We need to check highest 8 bits out of 32 of 'value'. - * - * FIXME: Add code to find if we are on ES2 or ES3 to have separate - * checks. + * FIXME: Remove ES2 support from the kernel entirely. */ - if (value == DME_TSI_UNIPRO_BOOT_STARTED || - value == DME_TSI_FALLBACK_UNIPRO_BOOT_STARTED) + if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && + intf->ddbl1_product_id == ES2_DDBL1_PROD_ID) + init_status = value; + else + init_status = value >> 24; + + if (init_status == DME_DIS_UNIPRO_BOOT_STARTED || + init_status == DME_DIS_FALLBACK_UNIPRO_BOOT_STARTED) intf->boot_over_unipro = true; - return gb_svc_dme_peer_set(hd->svc, intf->interface_id, - DME_ATTR_T_TST_SRC_INCREMENT, + return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr, DME_ATTR_SELECTOR_INDEX, 0); } -- cgit v1.2.3-59-g8ed1b From 0b8af6a901512a61e687e765c75c8d3ca9867493 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Wed, 16 Dec 2015 16:29:17 +0530 Subject: greybus: db3(arche)-platform: Remove all APB control code Current db3(arche)-platform driver was only handling APB control signals, which is where it all started. But now with completion of DB3/EVT1 bringup we know that platform driver is more than APB control. We have to bring SVC up before APB, as SVC supposed to enable clock to APB's. Note that, in EVT1, AP will have direct control over APB's clock. Then we have dependency between APB and USB HUB, where, APB should be brought up before USB HUB (note that this needs to rootcaused). This patch cleanup the db3(arche)-platform driver to remove all APB control code. The idea here is create another driver for APB control (arche-apb-ctrl.c), which will deal with APB. And this driver will have generic/common platform specific support, currently manages SVC resources. This patch also takes in all the changes from factory branch, discovered during bringup. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/db3-platform.c | 356 +++++++++------------------------ 1 file changed, 97 insertions(+), 259 deletions(-) diff --git a/drivers/staging/greybus/db3-platform.c b/drivers/staging/greybus/db3-platform.c index 31f27f0628c4..eede56bac1c4 100644 --- a/drivers/staging/greybus/db3-platform.c +++ b/drivers/staging/greybus/db3-platform.c @@ -1,5 +1,5 @@ /* - * DB3 Platform driver for AP bridge control interface. + * DB3 Platform driver to enable Unipro link. * * Copyright 2014-2015 Google Inc. * Copyright 2014-2015 Linaro Ltd. @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -23,304 +24,141 @@ #include #include -enum apb_state { - APB_STATE_OFF, - APB_STATE_ACTIVE, - APB_STATE_STANDBY, -}; - -/* - * Control GPIO signals to and from AP <=> AP Bridges - */ -struct apb_ctrl_gpios { - int wake_detect; /* bi-dir, maps to WAKE_MOD and WAKE_FRAME signals */ - int reset; - int boot_ret; - int pwroff; - int wake_in; - int wake_out; - int pwrdn; -}; - -struct apb_ctrl_drvdata { - struct apb_ctrl_gpios ctrl; +struct db3_platform_drvdata { + /* Control GPIO signals to and from AP <=> SVC */ + int svc_reset_gpio; + bool is_reset_act_hi; + int svc_sysboot_gpio; - unsigned int wake_detect_irq; - enum apb_state state; - - struct regulator *vcore; - struct regulator *vio; + unsigned int svc_refclk_req; + struct clk *svc_ref_clk; struct pinctrl *pinctrl; struct pinctrl_state *pin_default; - /* To protect concurrent access of GPIO registers, need protection */ - spinlock_t lock; + int num_apbs; }; -static inline void assert_wake(unsigned int wake_detect) -{ - gpio_set_value(wake_detect, 1); -} - -static inline void deassert_wake(unsigned int wake_detect) -{ - gpio_set_value(wake_detect, 0); -} - -static irqreturn_t apb_ctrl_wake_detect_irq(int irq, void *devid) +static inline void svc_reset_onoff(unsigned int gpio, bool onoff) { - struct apb_ctrl_drvdata *apb_data = devid; - unsigned long flags; - - /* - * TODO: - * Since currently SoC GPIOs are being used we are safe here - * But ideally we should create a workqueue and process the control - * signals, especially when we start using GPIOs over slow - * buses like I2C. - */ - if (!gpio_is_valid(apb_data->ctrl.wake_detect) && - !gpio_is_valid(apb_data->ctrl.reset)) - return IRQ_HANDLED; /* Should it be IRQ_NONE ?? */ - - spin_lock_irqsave(&apb_data->lock, flags); - - if (apb_data->state != APB_STATE_ACTIVE) { - /* Bring bridge out of reset on this event */ - gpio_set_value(apb_data->ctrl.reset, 0); - apb_data->state = APB_STATE_ACTIVE; - } else { - /* - * Assert Wake_OUT signal to APB - * It would resemble WakeDetect module's signal pass-through - */ - /* - * We have to generate the pulse, so we may need to schedule - * workqueue here. - * - * Also, since we are using both rising and falling edge for - * interrupt trigger, we may not need workqueue. Just pass - * through the value to bridge. - * Just read GPIO value and pass it to the bridge - */ - } - - spin_unlock_irqrestore(&apb_data->lock, flags); - - return IRQ_HANDLED; + gpio_set_value(gpio, onoff); } -static void apb_ctrl_cleanup(struct apb_ctrl_drvdata *apb_data) +static void db3_platform_cleanup(struct db3_platform_drvdata *db3_pdata) { - if (apb_data->vcore && regulator_is_enabled(apb_data->vcore) > 0) - regulator_disable(apb_data->vcore); - - if (apb_data->vio && regulator_is_enabled(apb_data->vio) > 0) - regulator_disable(apb_data->vio); - /* As part of exit, put APB back in reset state */ - if (gpio_is_valid(apb_data->ctrl.reset)) - gpio_set_value(apb_data->ctrl.reset, 1); - - /* TODO: May have to send an event to SVC about this exit */ + if (gpio_is_valid(db3_pdata->svc_reset_gpio)) + svc_reset_onoff(db3_pdata->svc_reset_gpio, + db3_pdata->is_reset_act_hi); } -/* - * Note: Please do not modify the below sequence, as it is as per the spec - */ -static int apb_ctrl_init_seq(struct device *dev, - struct apb_ctrl_drvdata *apb_data) +static int db3_platform_probe(struct platform_device *pdev) { + struct db3_platform_drvdata *db3_pdata; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; int ret; - pinctrl_select_state(apb_data->pinctrl, apb_data->pin_default); + db3_pdata = devm_kzalloc(&pdev->dev, sizeof(*db3_pdata), GFP_KERNEL); + if (!db3_pdata) + return -ENOMEM; - /* Hold APB in reset state */ - ret = devm_gpio_request_one(dev, apb_data->ctrl.reset, - GPIOF_OUT_INIT_LOW, "reset"); - if (ret) { - dev_err(dev, "Failed requesting reset gpio %d\n", - apb_data->ctrl.reset); + /* setup svc reset gpio */ + db3_pdata->is_reset_act_hi = of_property_read_bool(np, "svc,reset-active-high"); + db3_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); + if (db3_pdata->svc_reset_gpio < 0) { + dev_err(dev, "failed to get reset-gpio\n"); + ret = -ENODEV; return ret; } - - /* Enable power to APB */ - if (apb_data->vcore) { - ret = regulator_enable(apb_data->vcore); - if (ret) { - dev_err(dev, "failed to enable core regulator\n"); - return ret; - } - } - - if (apb_data->vio) { - ret = regulator_enable(apb_data->vio); - if (ret) { - dev_err(dev, "failed to enable IO regulator\n"); - return ret; - } - } - - /* - * We should be safe here to deassert boot retention signal, as - * we are only supporting cold boot as of now. - */ - ret = devm_gpio_request_one(dev, apb_data->ctrl.boot_ret, - GPIOF_OUT_INIT_LOW, "boot retention"); + ret = devm_gpio_request(dev, db3_pdata->svc_reset_gpio, "svc-reset"); if (ret) { - dev_err(dev, "Failed requesting reset gpio %d\n", - apb_data->ctrl.boot_ret); + dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); return ret; } - - ret = devm_gpio_request(dev, apb_data->ctrl.wake_detect, "wake detect"); - if (ret) - dev_err(dev, "Failed requesting wake_detect gpio %d\n", - apb_data->ctrl.wake_detect); - - return ret; -} - -static int apb_ctrl_get_devtree_data(struct device *dev, - struct apb_ctrl_drvdata *apb_data) -{ - struct device_node *np = dev->of_node; - - /* fetch control signals */ - apb_data->ctrl.wake_detect = of_get_named_gpio(np, "wake-detect-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.wake_detect)) { - dev_err(dev, "failed to get wake detect gpio\n"); - return apb_data->ctrl.wake_detect; - } - - apb_data->ctrl.reset = of_get_named_gpio(np, "reset-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.reset)) { - dev_err(dev, "failed to get reset gpio\n"); - return apb_data->ctrl.reset; + ret = gpio_direction_output(db3_pdata->svc_reset_gpio, db3_pdata->is_reset_act_hi); + if (ret) { + dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); + return ret; } - apb_data->ctrl.boot_ret = of_get_named_gpio(np, "boot-ret-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.boot_ret)) { - dev_err(dev, "failed to get boot retention gpio\n"); - return apb_data->ctrl.boot_ret; + db3_pdata->svc_sysboot_gpio = of_get_named_gpio(np, "svc,sysboot-gpio", 0); + if (db3_pdata->svc_sysboot_gpio < 0) { + dev_err(dev, "failed to get sysboot gpio\n"); + ret = -ENODEV; + return ret; } - - /* It's not mandatory to support power management interface */ - apb_data->ctrl.pwroff = of_get_named_gpio(np, "pwr-off-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.pwroff)) - dev_info(dev, "failed to get power off gpio\n"); - - apb_data->ctrl.pwrdn = of_get_named_gpio(np, "pwr-down-gpios", 0); - if (!gpio_is_valid(apb_data->ctrl.pwrdn)) - dev_info(dev, "failed to get power down gpio\n"); - - /* Regulators are optional, as we may have fixed supply coming in */ - apb_data->vcore = devm_regulator_get(dev, "vcore"); - if (IS_ERR_OR_NULL(apb_data->vcore)) { - dev_info(dev, "no core regulator found\n"); - apb_data->vcore = NULL; + ret = devm_gpio_request(dev, db3_pdata->svc_sysboot_gpio, "sysboot0"); + if (ret) { + dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); + return ret; } - - apb_data->vio = devm_regulator_get(dev, "vio"); - if (IS_ERR_OR_NULL(apb_data->vio)) { - dev_info(dev, "no IO regulator found\n"); - apb_data->vio = NULL; + ret = gpio_direction_output(db3_pdata->svc_sysboot_gpio, 0); + if (ret) { + dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); + return ret; } - apb_data->pinctrl = devm_pinctrl_get(dev); - if (IS_ERR(apb_data->pinctrl)) { - dev_err(dev, "could not get pinctrl handle\n"); - return PTR_ERR(apb_data->pinctrl); + /* setup the clock request gpio first */ + db3_pdata->svc_refclk_req = of_get_named_gpio(np, "svc,refclk-req-gpio", 0); + if (db3_pdata->svc_refclk_req < 0) { + dev_err(dev, "failed to get svc clock-req gpio\n"); + return -ENODEV; } - apb_data->pin_default = pinctrl_lookup_state(apb_data->pinctrl, "default"); - if (IS_ERR(apb_data->pin_default)) { - dev_err(dev, "could not get default pin state\n"); - return PTR_ERR(apb_data->pin_default); + ret = devm_gpio_request(dev, db3_pdata->svc_refclk_req, "svc-clk-req"); + if (ret) { + dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); + return ret; } - - return 0; -} - -static int apb_ctrl_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct apb_ctrl_drvdata *apb_data; - int ret; - - apb_data = devm_kzalloc(&pdev->dev, sizeof(*apb_data), GFP_KERNEL); - if (!apb_data) - return -ENOMEM; - - ret = apb_ctrl_get_devtree_data(dev, apb_data); + ret = gpio_direction_input(db3_pdata->svc_refclk_req); if (ret) { - dev_err(dev, "failed to get devicetree data %d\n", ret); + dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); return ret; } - ret = apb_ctrl_init_seq(dev, apb_data); + /* setup refclk2 to follow the pin */ + db3_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); + if (IS_ERR(db3_pdata->svc_ref_clk)) { + ret = PTR_ERR(db3_pdata->svc_ref_clk); + dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(db3_pdata->svc_ref_clk); if (ret) { - dev_err(dev, "failed to set init state of control signal %d\n", - ret); - goto exit; + dev_err(dev, "failed to enable svc_ref_clk: %d\n", ret); + return ret; } - platform_set_drvdata(pdev, apb_data); - - apb_data->state = APB_STATE_OFF; - /* - * Assert AP module detect signal by pulling wake_detect low - */ - deassert_wake(apb_data->ctrl.wake_detect); + platform_set_drvdata(pdev, db3_pdata); - /* - * In order to receive an interrupt, the GPIO must be set to input mode - * - * As per WDM spec, for the cold boot, the wake pulse must be - * >= 5000 usec, but at this stage it is power up sequence, - * so we always treat it as cold boot. - */ - gpio_direction_input(apb_data->ctrl.wake_detect); + db3_pdata->num_apbs = of_get_child_count(np); + dev_dbg(dev, "Number of APB's available - %d\n", db3_pdata->num_apbs); - ret = devm_request_irq(dev, gpio_to_irq(apb_data->ctrl.wake_detect), - apb_ctrl_wake_detect_irq, - IRQF_TRIGGER_RISING, - "wake detect", apb_data); - if (ret) { - dev_err(&pdev->dev, "failed to request wake detect IRQ\n"); - goto exit; - } + /* bring SVC out of reset */ + svc_reset_onoff(db3_pdata->svc_reset_gpio, !db3_pdata->is_reset_act_hi); - /* - * Interrupt handling for WAKE_IN (from bridge) signal is required - * - * Assumption here is, AP already would have woken up and in the - * WAKE_IN/WAKE_FRAME event from bridge, as AP would pass-through event - * to SVC. - * - * Not sure anything else needs to take care here. - */ - dev_info(dev, "Device registered successfully \n"); - return 0; + /* probe all childs here */ + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) + dev_err(dev, "no child node found\n"); -exit: - apb_ctrl_cleanup(apb_data); + dev_info(dev, "Device registered successfully\n"); return ret; } -static int apb_ctrl_remove(struct platform_device *pdev) +static int db3_platform_remove(struct platform_device *pdev) { - struct apb_ctrl_drvdata *apb_data = platform_get_drvdata(pdev); + struct db3_platform_drvdata *db3_pdata = platform_get_drvdata(pdev); - if (apb_data) - apb_ctrl_cleanup(apb_data); + if (db3_pdata) + db3_platform_cleanup(db3_pdata); platform_set_drvdata(pdev, NULL); return 0; } -static int apb_ctrl_suspend(struct device *dev) +static int db3_platform_suspend(struct device *dev) { /* * If timing profile premits, we may shutdown bridge @@ -334,7 +172,7 @@ static int apb_ctrl_suspend(struct device *dev) return 0; } -static int apb_ctrl_resume(struct device *dev) +static int db3_platform_resume(struct device *dev) { /* * Atleast for ES2 we have to meet the delay requirement between @@ -348,26 +186,26 @@ static int apb_ctrl_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(apb_ctrl_pm_ops, apb_ctrl_suspend, apb_ctrl_resume); +static SIMPLE_DEV_PM_OPS(db3_platform_pm_ops, db3_platform_suspend, db3_platform_resume); -static struct of_device_id apb_ctrl_of_match[] = { - { .compatible = "usbffff,2", }, +static struct of_device_id db3_platform_of_match[] = { + { .compatible = "google,db3-platform", }, /* Use PID/VID of SVC device */ { }, }; -MODULE_DEVICE_TABLE(of, apb_ctrl_of_match); +MODULE_DEVICE_TABLE(of, db3_platform_of_match); -static struct platform_driver apb_ctrl_device_driver = { - .probe = apb_ctrl_probe, - .remove = apb_ctrl_remove, +static struct platform_driver db3_platform_device_driver = { + .probe = db3_platform_probe, + .remove = db3_platform_remove, .driver = { - .name = "unipro-APbridge", - .pm = &apb_ctrl_pm_ops, - .of_match_table = of_match_ptr(apb_ctrl_of_match), + .name = "db3-platform-ctrl", + .pm = &db3_platform_pm_ops, + .of_match_table = of_match_ptr(db3_platform_of_match), } }; -module_platform_driver(apb_ctrl_device_driver); +module_platform_driver(db3_platform_device_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vaibhav Hiremath "); -MODULE_DESCRIPTION("AP Bridge Control Driver for DB3 platform"); +MODULE_DESCRIPTION("DB3 Platform Driver"); -- cgit v1.2.3-59-g8ed1b From 7fa60654752bd4fbbe1e35e310a4bc96b6a3dec3 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Wed, 16 Dec 2015 16:29:18 +0530 Subject: greybus: arche-platform: Rename db3-platform to arche-platform With multiple platforms getting rolled into ara, db3 name is confusing. And this driver is applicable to all arche platforms, so make sense to rename it to arche-platform.c. Also rename all internal functions accordingly. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 4 +- drivers/staging/greybus/arche-platform.c | 216 +++++++++++++++++++++++++++++++ drivers/staging/greybus/db3-platform.c | 211 ------------------------------ 3 files changed, 218 insertions(+), 213 deletions(-) create mode 100644 drivers/staging/greybus/arche-platform.c delete mode 100644 drivers/staging/greybus/db3-platform.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 29c4c8b25f13..bc4de85b0552 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -28,7 +28,7 @@ gb-light-y := light.o gb-raw-y := raw.o gb-hid-y := hid.o gb-es2-y := es2.o -gb-db3-y := db3-platform.o +gb-arche-y := arche-platform.o gb-audio-codec-y := audio-codec.o gb-camera-y := camera.o @@ -41,7 +41,7 @@ obj-m += gb-light.o obj-m += gb-hid.o obj-m += gb-raw.o obj-m += gb-es2.o -obj-m += gb-db3.o +obj-m += gb-arche.o obj-m += gb-audio-codec.o obj-m += gb-camera.o diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c new file mode 100644 index 000000000000..7d90f5dee1d3 --- /dev/null +++ b/drivers/staging/greybus/arche-platform.c @@ -0,0 +1,216 @@ +/* + * Arche Platform driver to enable Unipro link. + * + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct arche_platform_drvdata { + /* Control GPIO signals to and from AP <=> SVC */ + int svc_reset_gpio; + bool is_reset_act_hi; + int svc_sysboot_gpio; + + unsigned int svc_refclk_req; + struct clk *svc_ref_clk; + + struct pinctrl *pinctrl; + struct pinctrl_state *pin_default; + + int num_apbs; +}; + +static inline void svc_reset_onoff(unsigned int gpio, bool onoff) +{ + gpio_set_value(gpio, onoff); +} + +static void arche_platform_cleanup(struct arche_platform_drvdata *arche_pdata) +{ + /* As part of exit, put APB back in reset state */ + if (gpio_is_valid(arche_pdata->svc_reset_gpio)) + svc_reset_onoff(arche_pdata->svc_reset_gpio, + arche_pdata->is_reset_act_hi); +} + +static int arche_platform_probe(struct platform_device *pdev) +{ + struct arche_platform_drvdata *arche_pdata; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret; + + arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); + if (!arche_pdata) + return -ENOMEM; + + /* setup svc reset gpio */ + arche_pdata->is_reset_act_hi = of_property_read_bool(np, + "svc,reset-active-high"); + arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); + if (arche_pdata->svc_reset_gpio < 0) { + dev_err(dev, "failed to get reset-gpio\n"); + return -ENODEV; + } + ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); + if (ret) { + dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); + return ret; + } + ret = gpio_direction_output(arche_pdata->svc_reset_gpio, + arche_pdata->is_reset_act_hi); + if (ret) { + dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); + return ret; + } + + arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, + "svc,sysboot-gpio", 0); + if (arche_pdata->svc_sysboot_gpio < 0) { + dev_err(dev, "failed to get sysboot gpio\n"); + return -ENODEV; + } + ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); + if (ret) { + dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); + return ret; + } + ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); + if (ret) { + dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); + return ret; + } + + /* setup the clock request gpio first */ + arche_pdata->svc_refclk_req = of_get_named_gpio(np, + "svc,refclk-req-gpio", 0); + if (arche_pdata->svc_refclk_req < 0) { + dev_err(dev, "failed to get svc clock-req gpio\n"); + return -ENODEV; + } + ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); + if (ret) { + dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); + return ret; + } + ret = gpio_direction_input(arche_pdata->svc_refclk_req); + if (ret) { + dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); + return ret; + } + + /* setup refclk2 to follow the pin */ + arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); + if (IS_ERR(arche_pdata->svc_ref_clk)) { + ret = PTR_ERR(arche_pdata->svc_ref_clk); + dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(arche_pdata->svc_ref_clk); + if (ret) { + dev_err(dev, "failed to enable svc_ref_clk: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, arche_pdata); + + /* bring SVC out of reset */ + svc_reset_onoff(arche_pdata->svc_reset_gpio, + !arche_pdata->is_reset_act_hi); + + arche_pdata->num_apbs = of_get_child_count(np); + dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); + + /* probe all childs here */ + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) + dev_err(dev, "no child node found\n"); + + dev_info(dev, "Device registered successfully\n"); + return ret; +} + +static int arche_platform_remove(struct platform_device *pdev) +{ + struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); + + if (arche_pdata) + arche_platform_cleanup(arche_pdata); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int arche_platform_suspend(struct device *dev) +{ + /* + * If timing profile premits, we may shutdown bridge + * completely + * + * TODO: sequence ?? + * + * Also, need to make sure we meet precondition for unipro suspend + * Precondition: Definition ??? + */ + return 0; +} + +static int arche_platform_resume(struct device *dev) +{ + /* + * Atleast for ES2 we have to meet the delay requirement between + * unipro switch and AP bridge init, depending on whether bridge is in + * OFF state or standby state. + * + * Based on whether bridge is in standby or OFF state we may have to + * assert multiple signals. Please refer to WDM spec, for more info. + * + */ + return 0; +} + +static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, + arche_platform_suspend, + arche_platform_resume); + +static struct of_device_id arche_platform_of_match[] = { + { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ + { }, +}; +MODULE_DEVICE_TABLE(of, arche_platform_of_match); + +static struct platform_driver arche_platform_device_driver = { + .probe = arche_platform_probe, + .remove = arche_platform_remove, + .driver = { + .name = "arche-platform-ctrl", + .pm = &arche_platform_pm_ops, + .of_match_table = of_match_ptr(arche_platform_of_match), + } +}; + +module_platform_driver(arche_platform_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vaibhav Hiremath "); +MODULE_DESCRIPTION("Arche Platform Driver"); diff --git a/drivers/staging/greybus/db3-platform.c b/drivers/staging/greybus/db3-platform.c deleted file mode 100644 index eede56bac1c4..000000000000 --- a/drivers/staging/greybus/db3-platform.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * DB3 Platform driver to enable Unipro link. - * - * Copyright 2014-2015 Google Inc. - * Copyright 2014-2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct db3_platform_drvdata { - /* Control GPIO signals to and from AP <=> SVC */ - int svc_reset_gpio; - bool is_reset_act_hi; - int svc_sysboot_gpio; - - unsigned int svc_refclk_req; - struct clk *svc_ref_clk; - - struct pinctrl *pinctrl; - struct pinctrl_state *pin_default; - - int num_apbs; -}; - -static inline void svc_reset_onoff(unsigned int gpio, bool onoff) -{ - gpio_set_value(gpio, onoff); -} - -static void db3_platform_cleanup(struct db3_platform_drvdata *db3_pdata) -{ - /* As part of exit, put APB back in reset state */ - if (gpio_is_valid(db3_pdata->svc_reset_gpio)) - svc_reset_onoff(db3_pdata->svc_reset_gpio, - db3_pdata->is_reset_act_hi); -} - -static int db3_platform_probe(struct platform_device *pdev) -{ - struct db3_platform_drvdata *db3_pdata; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - int ret; - - db3_pdata = devm_kzalloc(&pdev->dev, sizeof(*db3_pdata), GFP_KERNEL); - if (!db3_pdata) - return -ENOMEM; - - /* setup svc reset gpio */ - db3_pdata->is_reset_act_hi = of_property_read_bool(np, "svc,reset-active-high"); - db3_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); - if (db3_pdata->svc_reset_gpio < 0) { - dev_err(dev, "failed to get reset-gpio\n"); - ret = -ENODEV; - return ret; - } - ret = devm_gpio_request(dev, db3_pdata->svc_reset_gpio, "svc-reset"); - if (ret) { - dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); - return ret; - } - ret = gpio_direction_output(db3_pdata->svc_reset_gpio, db3_pdata->is_reset_act_hi); - if (ret) { - dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); - return ret; - } - - db3_pdata->svc_sysboot_gpio = of_get_named_gpio(np, "svc,sysboot-gpio", 0); - if (db3_pdata->svc_sysboot_gpio < 0) { - dev_err(dev, "failed to get sysboot gpio\n"); - ret = -ENODEV; - return ret; - } - ret = devm_gpio_request(dev, db3_pdata->svc_sysboot_gpio, "sysboot0"); - if (ret) { - dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); - return ret; - } - ret = gpio_direction_output(db3_pdata->svc_sysboot_gpio, 0); - if (ret) { - dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); - return ret; - } - - /* setup the clock request gpio first */ - db3_pdata->svc_refclk_req = of_get_named_gpio(np, "svc,refclk-req-gpio", 0); - if (db3_pdata->svc_refclk_req < 0) { - dev_err(dev, "failed to get svc clock-req gpio\n"); - return -ENODEV; - } - ret = devm_gpio_request(dev, db3_pdata->svc_refclk_req, "svc-clk-req"); - if (ret) { - dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); - return ret; - } - ret = gpio_direction_input(db3_pdata->svc_refclk_req); - if (ret) { - dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); - return ret; - } - - /* setup refclk2 to follow the pin */ - db3_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); - if (IS_ERR(db3_pdata->svc_ref_clk)) { - ret = PTR_ERR(db3_pdata->svc_ref_clk); - dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); - return ret; - } - ret = clk_prepare_enable(db3_pdata->svc_ref_clk); - if (ret) { - dev_err(dev, "failed to enable svc_ref_clk: %d\n", ret); - return ret; - } - - platform_set_drvdata(pdev, db3_pdata); - - db3_pdata->num_apbs = of_get_child_count(np); - dev_dbg(dev, "Number of APB's available - %d\n", db3_pdata->num_apbs); - - /* bring SVC out of reset */ - svc_reset_onoff(db3_pdata->svc_reset_gpio, !db3_pdata->is_reset_act_hi); - - /* probe all childs here */ - ret = of_platform_populate(np, NULL, NULL, dev); - if (ret) - dev_err(dev, "no child node found\n"); - - dev_info(dev, "Device registered successfully\n"); - return ret; -} - -static int db3_platform_remove(struct platform_device *pdev) -{ - struct db3_platform_drvdata *db3_pdata = platform_get_drvdata(pdev); - - if (db3_pdata) - db3_platform_cleanup(db3_pdata); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static int db3_platform_suspend(struct device *dev) -{ - /* - * If timing profile premits, we may shutdown bridge - * completely - * - * TODO: sequence ?? - * - * Also, need to make sure we meet precondition for unipro suspend - * Precondition: Definition ??? - */ - return 0; -} - -static int db3_platform_resume(struct device *dev) -{ - /* - * Atleast for ES2 we have to meet the delay requirement between - * unipro switch and AP bridge init, depending on whether bridge is in - * OFF state or standby state. - * - * Based on whether bridge is in standby or OFF state we may have to - * assert multiple signals. Please refer to WDM spec, for more info. - * - */ - return 0; -} - -static SIMPLE_DEV_PM_OPS(db3_platform_pm_ops, db3_platform_suspend, db3_platform_resume); - -static struct of_device_id db3_platform_of_match[] = { - { .compatible = "google,db3-platform", }, /* Use PID/VID of SVC device */ - { }, -}; -MODULE_DEVICE_TABLE(of, db3_platform_of_match); - -static struct platform_driver db3_platform_device_driver = { - .probe = db3_platform_probe, - .remove = db3_platform_remove, - .driver = { - .name = "db3-platform-ctrl", - .pm = &db3_platform_pm_ops, - .of_match_table = of_match_ptr(db3_platform_of_match), - } -}; - -module_platform_driver(db3_platform_device_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Vaibhav Hiremath "); -MODULE_DESCRIPTION("DB3 Platform Driver"); -- cgit v1.2.3-59-g8ed1b From 5a78178718b9e50ee60a1129431758125cfa2f6a Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Wed, 16 Dec 2015 16:29:19 +0530 Subject: greybus: arche-apb-ctrl: Add APB control driver It was messy to integrate both SVC, APB (and any other arche platform specific control) into one single driver. Especially due to cross-dependency. AP first needs to bringup SVC, as SVC should enable clock to APB. APB should come up before HUB, as due to some reason HUB wouldn't enumerate APB's is APB comes up later. And on top of that we should have clean picture of hardware description in DT file. So this patch introduces APB control driver. Mostly copied from original arche-platform driver + fixed boot sequence. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/arche-apb-ctrl.c | 400 +++++++++++++++++++++++++++++++ 2 files changed, 402 insertions(+) create mode 100644 drivers/staging/greybus/arche-apb-ctrl.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index bc4de85b0552..b0d53f5228c0 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -29,6 +29,7 @@ gb-raw-y := raw.o gb-hid-y := hid.o gb-es2-y := es2.o gb-arche-y := arche-platform.o +gb-arche-apb-ctrl-y := arche-apb-ctrl.o gb-audio-codec-y := audio-codec.o gb-camera-y := camera.o @@ -42,6 +43,7 @@ obj-m += gb-hid.o obj-m += gb-raw.o obj-m += gb-es2.o obj-m += gb-arche.o +obj-m += gb-arche-apb-ctrl.o obj-m += gb-audio-codec.o obj-m += gb-camera.o diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c new file mode 100644 index 000000000000..efc1e42f8c61 --- /dev/null +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -0,0 +1,400 @@ +/* + * Arche Platform driver to control APB. + * + * Copyright 2014-2015 Google Inc. + * Copyright 2014-2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum apb_state { + APB_STATE_OFF, + APB_STATE_ACTIVE, + APB_STATE_STANDBY, +}; + +struct arche_apb_ctrl_drvdata { + /* Control GPIO signals to and from AP <=> AP Bridges */ + int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ + int resetn_gpio; + int boot_ret_gpio; + int pwroff_gpio; + int wake_in_gpio; + int wake_out_gpio; + int pwrdn_gpio; + + unsigned int wake_detect_irq; + enum apb_state state; + + struct regulator *vcore; + struct regulator *vio; + + unsigned int clk_en_gpio; + struct clk *clk; + + struct pinctrl *pinctrl; + struct pinctrl_state *pin_default; + + /* To protect concurrent access of GPIO registers, need protection */ + spinlock_t lock; +}; + +/* + * Note that these low level api's are active high + */ +static inline void assert_gpio(unsigned int gpio) +{ + gpio_set_value(gpio, 1); + msleep(500); +} + +static inline void deassert_gpio(unsigned int gpio) +{ + gpio_set_value(gpio, 0); +} + +static irqreturn_t apb_ctrl_wake_detect_irq(int irq, void *devid) +{ + struct arche_apb_ctrl_drvdata *apb = devid; + unsigned long flags; + + /* + * TODO: + * Since currently SoC GPIOs are being used we are safe here + * But ideally we should create a workqueue and process the control + * signals, especially when we start using GPIOs over slow + * buses like I2C. + */ + if (!gpio_is_valid(apb->wake_detect_gpio) && + !gpio_is_valid(apb->resetn_gpio)) + return IRQ_HANDLED; /* Should it be IRQ_NONE ?? */ + + spin_lock_irqsave(&apb->lock, flags); + + if (apb->state != APB_STATE_ACTIVE) { + /* Bring bridge out of reset on this event */ + gpio_set_value(apb->resetn_gpio, 1); + apb->state = APB_STATE_ACTIVE; + } else { + /* + * Assert Wake_OUT signal to APB + * It would resemble WakeDetect module's signal pass-through + */ + /* + * We have to generate the pulse, so we may need to schedule + * workqueue here. + * + * Also, since we are using both rising and falling edge for + * interrupt trigger, we may not need workqueue. Just pass + * through the value to bridge. + * Just read GPIO value and pass it to the bridge + */ + } + + spin_unlock_irqrestore(&apb->lock, flags); + + return IRQ_HANDLED; +} + +/* + * Note: Please do not modify the below sequence, as it is as per the spec + */ +static int apb_ctrl_init_seq(struct platform_device *pdev, + struct arche_apb_ctrl_drvdata *apb) +{ + struct device *dev = &pdev->dev; + int ret; + + /* On DB3 clock was not mandatory */ + if (gpio_is_valid(apb->clk_en_gpio)) { + ret = devm_gpio_request(dev, apb->clk_en_gpio, "apb_clk_en"); + if (ret) + dev_err(dev, "Failed requesting APB clock en gpio %d\n", + apb->clk_en_gpio); + ret = gpio_direction_output(apb->clk_en_gpio, 1); + if (ret) + dev_err(dev, "failed to set APB clock en gpio dir:%d\n", ret); + } + /* Hold APB in reset state */ + ret = devm_gpio_request(dev, apb->resetn_gpio, "apb-reset"); + if (ret) { + dev_err(dev, "Failed requesting reset gpio %d\n", + apb->resetn_gpio); + return ret; + } + ret = gpio_direction_output(apb->resetn_gpio, 0); + if (ret) { + dev_err(dev, "failed to set reset gpio dir:%d\n", ret); + return ret; + } + + ret = devm_gpio_request(dev, apb->pwroff_gpio, "pwroff_n"); + if (ret) { + dev_err(dev, "Failed requesting pwroff_n gpio %d\n", + apb->pwroff_gpio); + return ret; + } + ret = gpio_direction_input(apb->pwroff_gpio); + if (ret) { + dev_err(dev, "failed to set pwroff gpio dir:%d\n", ret); + return ret; + } + + /* Enable power to APB */ + if (apb->vcore) { + ret = regulator_enable(apb->vcore); + if (ret) { + dev_err(dev, "failed to enable core regulator\n"); + return ret; + } + } + if (apb->vio) { + ret = regulator_enable(apb->vio); + if (ret) { + dev_err(dev, "failed to enable IO regulator\n"); + return ret; + } + } + + ret = devm_gpio_request_one(dev, apb->boot_ret_gpio, + GPIOF_OUT_INIT_LOW, "boot retention"); + if (ret) { + dev_err(dev, "Failed requesting bootret gpio %d\n", + apb->boot_ret_gpio); + return ret; + } + gpio_set_value(apb->boot_ret_gpio, 0); + udelay(50); + + ret = devm_gpio_request(dev, apb->wake_detect_gpio, "wake detect"); + if (ret) + dev_err(dev, "Failed requesting wake_detect gpio %d\n", + apb->wake_detect_gpio); + + return ret; +} + +static int apb_ctrl_get_devtree_data(struct platform_device *pdev, + struct arche_apb_ctrl_drvdata *apb) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + apb->wake_detect_gpio = of_get_named_gpio(np, "wake-detect-gpios", 0); + if (!gpio_is_valid(apb->wake_detect_gpio)) { + dev_err(dev, "failed to get wake detect gpio\n"); + return apb->wake_detect_gpio; + } + + apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0); + if (!gpio_is_valid(apb->resetn_gpio)) { + dev_err(dev, "failed to get reset gpio\n"); + return apb->resetn_gpio; + } + + apb->boot_ret_gpio = of_get_named_gpio(np, "boot-ret-gpios", 0); + if (!gpio_is_valid(apb->boot_ret_gpio)) { + dev_err(dev, "failed to get boot retention gpio\n"); + return apb->boot_ret_gpio; + } + + /* It's not mandatory to support power management interface */ + apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0); + if (!gpio_is_valid(apb->pwroff_gpio)) { + dev_info(dev, "failed to get power off gpio\n"); + return apb->pwroff_gpio; + } + + /* Do not make clock mandatory as of now (for DB3) */ + apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0); + if (!gpio_is_valid(apb->clk_en_gpio)) + dev_err(dev, "failed to get clock en gpio\n"); + + apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0); + if (!gpio_is_valid(apb->pwrdn_gpio)) + dev_info(dev, "failed to get power down gpio\n"); + + /* Regulators are optional, as we may have fixed supply coming in */ + apb->vcore = devm_regulator_get(dev, "vcore"); + if (IS_ERR_OR_NULL(apb->vcore)) { + dev_info(dev, "no core regulator found\n"); + apb->vcore = NULL; + } + + apb->vio = devm_regulator_get(dev, "vio"); + if (IS_ERR_OR_NULL(apb->vio)) { + dev_info(dev, "no IO regulator found\n"); + apb->vio = NULL; + } + + apb->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(apb->pinctrl)) { + dev_err(&pdev->dev, "could not get pinctrl handle\n"); + return PTR_ERR(apb->pinctrl); + } + apb->pin_default = pinctrl_lookup_state(apb->pinctrl, "default"); + if (IS_ERR(apb->pin_default)) { + dev_err(&pdev->dev, "could not get default pin state\n"); + return PTR_ERR(apb->pin_default); + } + + return 0; +} + +static void apb_ctrl_cleanup(struct arche_apb_ctrl_drvdata *apb) +{ + unsigned long flags; + + if (apb->vcore && regulator_is_enabled(apb->vcore) > 0) + regulator_disable(apb->vcore); + + if (apb->vio && regulator_is_enabled(apb->vio) > 0) + regulator_disable(apb->vio); + + spin_lock_irqsave(&apb->lock, flags); + /* As part of exit, put APB back in reset state */ + if (gpio_is_valid(apb->resetn_gpio)) + gpio_set_value(apb->resetn_gpio, 0); + + apb->state = APB_STATE_OFF; + spin_unlock_irqrestore(&apb->lock, flags); + + /* TODO: May have to send an event to SVC about this exit */ +} + +static int arche_apb_ctrl_probe(struct platform_device *pdev) +{ + int ret; + struct arche_apb_ctrl_drvdata *apb; + struct device *dev = &pdev->dev; + + apb = devm_kzalloc(&pdev->dev, sizeof(*apb), GFP_KERNEL); + if (!apb) + return -ENOMEM; + + ret = apb_ctrl_get_devtree_data(pdev, apb); + if (ret) { + dev_err(dev, "failed to get apb devicetree data %d\n", ret); + return ret; + } + + ret = apb_ctrl_init_seq(pdev, apb); + if (ret) { + dev_err(dev, "failed to set init state of control signal %d\n", + ret); + goto exit; + } + + spin_lock_init(&apb->lock); + + apb->state = APB_STATE_OFF; + /* + * Assert AP module detect signal by pulling wake_detect low + */ + assert_gpio(apb->wake_detect_gpio); + + /* + * In order to receive an interrupt, the GPIO must be set to input mode + */ + gpio_direction_input(apb->wake_detect_gpio); + + ret = devm_request_irq(dev, gpio_to_irq(apb->wake_detect_gpio), + apb_ctrl_wake_detect_irq, IRQF_TRIGGER_FALLING, + "wake detect", apb); + if (ret) { + dev_err(dev, "failed to request wake detect IRQ\n"); + goto exit; + } + + platform_set_drvdata(pdev, apb); + + dev_info(&pdev->dev, "Device registered successfully\n"); + return 0; + +exit: + apb_ctrl_cleanup(apb); + return ret; +} + +static int arche_apb_ctrl_remove(struct platform_device *pdev) +{ + struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); + + if (apb) + apb_ctrl_cleanup(apb); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int arche_apb_ctrl_suspend(struct device *dev) +{ + /* + * If timing profile premits, we may shutdown bridge + * completely + * + * TODO: sequence ?? + * + * Also, need to make sure we meet precondition for unipro suspend + * Precondition: Definition ??? + */ + return 0; +} + +static int arche_apb_ctrl_resume(struct device *dev) +{ + /* + * Atleast for ES2 we have to meet the delay requirement between + * unipro switch and AP bridge init, depending on whether bridge is in + * OFF state or standby state. + * + * Based on whether bridge is in standby or OFF state we may have to + * assert multiple signals. Please refer to WDM spec, for more info. + * + */ + return 0; +} + +static SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, + arche_apb_ctrl_suspend, + arche_apb_ctrl_resume); + +static struct of_device_id arche_apb_ctrl_of_match[] = { + { .compatible = "usbffff,2", }, + { }, +}; +MODULE_DEVICE_TABLE(of, arche_apb_ctrl_of_match); + +static struct platform_driver arche_apb_ctrl_device_driver = { + .probe = arche_apb_ctrl_probe, + .remove = arche_apb_ctrl_remove, + .driver = { + .name = "arche-apb-ctrl", + .pm = &arche_apb_ctrl_pm_ops, + .of_match_table = of_match_ptr(arche_apb_ctrl_of_match), + } +}; + +module_platform_driver(arche_apb_ctrl_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vaibhav Hiremath "); +MODULE_DESCRIPTION("Arche APB control Driver"); -- cgit v1.2.3-59-g8ed1b From 166f0aed325f7bcb7c1ee8c2665d5cb05d36d931 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Wed, 16 Dec 2015 16:29:20 +0530 Subject: greybus: arche-apb-ctrl: Assert reset to APB at the end of probe Until we have proper handshake mechanism implemented with SVC assert reset to APB at the end of probe. We are safe here to do that, as SVC always enables clock to APB's currently. And also from EVT1 perspective, we should be good, as clock control signals are now moved to AP. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index efc1e42f8c61..68f8d3cc8e97 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -325,6 +325,8 @@ static int arche_apb_ctrl_probe(struct platform_device *pdev) platform_set_drvdata(pdev, apb); + assert_gpio(apb->resetn_gpio); + dev_info(&pdev->dev, "Device registered successfully\n"); return 0; -- cgit v1.2.3-59-g8ed1b From 5b22521c895111ca191c82a6573214f995c47709 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 16 Dec 2015 16:29:29 +0530 Subject: greybus: control: Use Macro's instead of direct values for major/minor We already have macro's defined for this, use them. Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 9c282e40142b..09ff79753313 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -126,8 +126,8 @@ static void gb_control_connection_exit(struct gb_connection *connection) static struct gb_protocol control_protocol = { .name = "control", .id = GREYBUS_PROTOCOL_CONTROL, - .major = 0, - .minor = 1, + .major = GB_CONTROL_VERSION_MAJOR, + .minor = GB_CONTROL_VERSION_MINOR, .connection_init = gb_control_connection_init, .connection_exit = gb_control_connection_exit, .flags = GB_PROTOCOL_SKIP_CONTROL_CONNECTED | -- cgit v1.2.3-59-g8ed1b From 72e53aed1d077166b63537b2b08dc89af60d6c9a Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 23 Dec 2015 16:48:59 +0000 Subject: greybus: svc: Change GB_SVC_TYPE_LINK_CONFIG to 0x10 The greybus specification reserves SVC type 0x0d-0x0f for timesync. 53124d73 ('svc: Add support for the link config operation') allocated the next available type 0x0d which conflicts with the specification. Change the type to 0x10 to ensure kernel and specification agree. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 53c1dea6b16d..773e1ab789c0 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -730,7 +730,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_DME_PEER_SET 0x0a #define GB_SVC_TYPE_ROUTE_CREATE 0x0b #define GB_SVC_TYPE_ROUTE_DESTROY 0x0c -#define GB_SVC_TYPE_LINK_CONFIG 0x0d +#define GB_SVC_TYPE_LINK_CONFIG 0x10 /* * SVC version request/response has the same payload as -- cgit v1.2.3-59-g8ed1b From 63d742b68abd09977dfa9f488e5494ec65485ef0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 23 Dec 2015 09:07:42 +0530 Subject: greybus: svc: Set interface's hotplug attributes before using them gb_svc_read_and_clear_module_boot_status() relies on the values of ddbl1_manufacturer_id and ddbl1_product_id to distinguish between ES2 and ES3 chips, but those values are set for the interface structure only after gb_svc_read_and_clear_module_boot_status() is called. This makes ES2 module to fail with following errors: greybus 1-2: Module not ready yet greybus 1-svc: failed to clear boot status of interface 2: -19 Fix this by setting these values before calling gb_svc_read_and_clear_module_boot_status(). Fixes: 51f1dc421b1f ("firmware and svc: detect the difference between ES2 and ES3 chips") Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 3e760e604b37..417869942d11 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -425,6 +425,11 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) return; } + intf->ddbl1_manufacturer_id = le32_to_cpu(request->data.ddbl1_mfr_id); + intf->ddbl1_product_id = le32_to_cpu(request->data.ddbl1_prod_id); + intf->vendor_id = le32_to_cpu(request->data.ara_vend_id); + intf->product_id = le32_to_cpu(request->data.ara_prod_id); + ret = gb_svc_read_and_clear_module_boot_status(intf); if (ret) { dev_err(&svc->dev, "failed to clear boot status of interface %u: %d\n", @@ -432,11 +437,6 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) goto destroy_interface; } - intf->ddbl1_manufacturer_id = le32_to_cpu(request->data.ddbl1_mfr_id); - intf->ddbl1_product_id = le32_to_cpu(request->data.ddbl1_prod_id); - intf->vendor_id = le32_to_cpu(request->data.ara_vend_id); - intf->product_id = le32_to_cpu(request->data.ara_prod_id); - /* * Create a device id for the interface: * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC -- cgit v1.2.3-59-g8ed1b From 57c6bcc635626e24fff3546bb072b4ba4163872b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 28 Dec 2015 11:59:00 +0530 Subject: greybus: interface: Receive serial-number on hotplug event Two exactly same modules can be uniquely identified using module's serial-number. This patch updates the interface hotplug event to also receive the serial-number of the module. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 8 ++++++++ .../greybus/Documentation/sysfs/greybus1/1-2/serial_number | 0 .../greybus/Documentation/sysfs/greybus1/1-4/serial_number | 0 .../greybus/Documentation/sysfs/greybus2/2-3/serial_number | 0 drivers/staging/greybus/greybus_protocols.h | 1 + drivers/staging/greybus/interface.c | 2 ++ drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/svc.c | 1 + 8 files changed, 13 insertions(+) create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/serial_number create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/serial_number create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/serial_number diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 2cd17c706940..0d16d7649a5a 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -37,6 +37,14 @@ Contact: Greg Kroah-Hartman Description: The ID of a Greybus interface. +What: /sys/bus/greybus/device/N-I/serial_number +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + Serial Number of the Greybus interface, represented by a 64 bit + hexadecimal number. + What: /sys/bus/greybus/device/N-I/product_id Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 773e1ab789c0..f66f4d784f27 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -757,6 +757,7 @@ struct gb_svc_intf_hotplug_request { __le32 ddbl1_prod_id; __le32 ara_vend_id; __le32 ara_prod_id; + __le64 serial_number; } data; } __packed; /* hotplug response has no payload */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c4b9e1486828..69eb7886ceae 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -27,6 +27,7 @@ gb_interface_attr(vendor_id, "0x%08x"); gb_interface_attr(product_id, "0x%08x"); gb_interface_attr(vendor_string, "%s"); gb_interface_attr(product_string, "%s"); +gb_interface_attr(serial_number, "0x%016llx"); static struct attribute *interface_attrs[] = { &dev_attr_ddbl1_manufacturer_id.attr, @@ -36,6 +37,7 @@ static struct attribute *interface_attrs[] = { &dev_attr_product_id.attr, &dev_attr_vendor_string.attr, &dev_attr_product_string.attr, + &dev_attr_serial_number.attr, NULL, }; ATTRIBUTE_GROUPS(interface); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index d192b74ab5e1..a1a1f9f5c2cb 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -30,6 +30,7 @@ struct gb_interface { u32 ddbl1_product_id; u32 vendor_id; u32 product_id; + u64 serial_number; struct gb_host_device *hd; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 417869942d11..fe7bd2850d5d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -429,6 +429,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) intf->ddbl1_product_id = le32_to_cpu(request->data.ddbl1_prod_id); intf->vendor_id = le32_to_cpu(request->data.ara_vend_id); intf->product_id = le32_to_cpu(request->data.ara_prod_id); + intf->serial_number = le64_to_cpu(request->data.serial_number); ret = gb_svc_read_and_clear_module_boot_status(intf); if (ret) { -- cgit v1.2.3-59-g8ed1b From d39bf704acbd254576e3fc880ec13e6cd09e88c7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 28 Dec 2015 11:59:01 +0530 Subject: greybus: interface: Fetch and expose version of interface's firmware The version of the currently running firmware on the module is useful for userspace as it can be used to find if an update is available or not. This patch fetches interface's version with a new control operation and exposes the same in userspace. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs-bus-greybus | 8 ++++++ drivers/staging/greybus/control.c | 29 ++++++++++++++++++++++ drivers/staging/greybus/control.h | 1 + drivers/staging/greybus/greybus_protocols.h | 7 ++++++ drivers/staging/greybus/interface.c | 15 +++++++++++ drivers/staging/greybus/interface.h | 3 +++ 6 files changed, 63 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 0d16d7649a5a..9ce36ddfb4da 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -73,6 +73,14 @@ Contact: Greg Kroah-Hartman Description: Vendor ID string of a Greybus interface block. +What: /sys/bus/greybus/device/N-I/version +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + Interface version represented as <16 bit major number>.<16 bit + minor number>. + What: /sys/bus/greybus/device/N-I.B Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 09ff79753313..4d65dbff6e93 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -59,6 +59,35 @@ int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id) sizeof(request), NULL, 0); } +int gb_control_get_interface_version_operation(struct gb_interface *intf) +{ + struct gb_control_interface_version_response response; + struct gb_connection *connection = intf->control->connection; + int ret; + + /* The ES3 bootrom fails to boot if this request it sent to it */ + if (intf->boot_over_unipro) + return 0; + + ret = gb_operation_sync(connection, GB_CONTROL_TYPE_INTERFACE_VERSION, + NULL, 0, &response, sizeof(response)); + if (ret) { + dev_err(&connection->intf->dev, + "failed to get interface version: %d\n", ret); + /* + * FIXME: Return success until the time we bump version of + * control protocol. The interface-version is already set to + * 0.0, so no need to update that. + */ + return 0; + } + + intf->version_major = le16_to_cpu(response.major); + intf->version_minor = le16_to_cpu(response.minor); + + return 0; +} + struct gb_control *gb_control_create(struct gb_interface *intf) { struct gb_control *control; diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index da0fa6652dca..7cb3dd2290d7 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -24,6 +24,7 @@ int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id); int gb_control_get_manifest_size_operation(struct gb_interface *intf); int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, size_t size); +int gb_control_get_interface_version_operation(struct gb_interface *intf); int gb_control_protocol_init(void); void gb_control_protocol_exit(void); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index f66f4d784f27..a0bddaa36629 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -125,6 +125,7 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_GET_MANIFEST 0x04 #define GB_CONTROL_TYPE_CONNECTED 0x05 #define GB_CONTROL_TYPE_DISCONNECTED 0x06 +#define GB_CONTROL_TYPE_INTERFACE_VERSION 0x0a /* Control protocol manifest get size request has no payload*/ struct gb_control_get_manifest_size_response { @@ -146,6 +147,12 @@ struct gb_control_disconnected_request { } __packed; /* Control protocol [dis]connected response has no payload */ +/* Control protocol interface version request has no payload */ +struct gb_control_interface_version_response { + __le16 major; + __le16 minor; +} __packed; + /* Firmware Protocol */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 69eb7886ceae..edac2383e492 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -29,6 +29,16 @@ gb_interface_attr(vendor_string, "%s"); gb_interface_attr(product_string, "%s"); gb_interface_attr(serial_number, "0x%016llx"); +static ssize_t version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", intf->version_major, + intf->version_minor); +} +static DEVICE_ATTR_RO(version); + static struct attribute *interface_attrs[] = { &dev_attr_ddbl1_manufacturer_id.attr, &dev_attr_ddbl1_product_id.attr, @@ -38,6 +48,7 @@ static struct attribute *interface_attrs[] = { &dev_attr_vendor_string.attr, &dev_attr_product_string.attr, &dev_attr_serial_number.attr, + &dev_attr_version.attr, NULL, }; ATTRIBUTE_GROUPS(interface); @@ -214,6 +225,10 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) goto free_manifest; } + ret = gb_control_get_interface_version_operation(intf); + if (ret) + goto free_manifest; + /* Register the interface and its bundles. */ ret = device_add(&intf->dev); if (ret) { diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index a1a1f9f5c2cb..4168a5752808 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -32,6 +32,9 @@ struct gb_interface { u32 product_id; u64 serial_number; + u16 version_major; + u16 version_minor; + struct gb_host_device *hd; /* The interface needs to boot over unipro */ -- cgit v1.2.3-59-g8ed1b From 12c8b0dcc884cc7f1f925cd1ed08cf8cdc95e7ef Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 30 Dec 2015 11:08:00 +0100 Subject: greybus: camera: fix memory leak in configure-streams error path Fix memory leak in configure-streams error path by making sure to release the operation buffers before returning. Signed-off-by: Johan Hovold Reviewed-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index c742fea29492..8b2eedd29cb3 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -115,7 +115,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, GB_CAMERA_TYPE_CONFIGURE_STREAMS, req, req_size, resp, resp_size); if (ret < 0) - return ret; + goto done; if (le16_to_cpu(resp->num_streams) > nstreams) { gcam_dbg(gcam, "got #streams %u > request %u\n", -- cgit v1.2.3-59-g8ed1b From b9f71bc854105148f38d0c2336ed7c9b50a97800 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 30 Dec 2015 11:08:01 +0100 Subject: greybus: camera: fix memory leak in capture-request handler Fix memory leak in capture-request handler by making sure to release the operation request buffer after sending the request. Signed-off-by: Johan Hovold Reviewed-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 8b2eedd29cb3..ec7d4a082ab1 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -183,6 +183,7 @@ static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, { struct gb_camera_capture_request *req; size_t req_size; + int ret; if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) return -EINVAL; @@ -198,8 +199,12 @@ static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, req->num_frames = cpu_to_le16(num_frames); memcpy(req->settings, settings, settings_size); - return gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, + ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, req, req_size, NULL, 0); + + kfree(req); + + return ret; } static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) -- cgit v1.2.3-59-g8ed1b From 41c23958557d9b37755768849ee39b99a9826a9c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 30 Dec 2015 11:23:51 +0100 Subject: greybus: camera: destroy data connection on link-config errors Make sure to tear down the data connection also on failure to configure the link by setting the data_connected flag immediately after creating the connection. Signed-off-by: Johan Hovold Reviewed-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index ec7d4a082ab1..b181ac49e783 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -604,6 +604,8 @@ static int gb_camera_connection_init(struct gb_connection *connection) if (ret < 0) goto error; + gcam->data_connected = true; + ret = gb_svc_link_config(svc, connection->intf->interface_id, GB_SVC_LINK_CONFIG_BURST_HS_A, 2, 2, 0); if (ret < 0) @@ -614,8 +616,6 @@ static int gb_camera_connection_init(struct gb_connection *connection) if (ret < 0) goto error; - gcam->data_connected = true; - ret = gb_camera_debugfs_init(gcam); if (ret < 0) goto error; -- cgit v1.2.3-59-g8ed1b From bc142bbb4ceba0891db6bf36cfb7be13c9a2dd72 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Mon, 28 Dec 2015 20:06:32 +0530 Subject: greybus: arche_platform: Remove child's platform device as part of _remove() fn It seems we need to delete platform_dev of all childs explicitly, in _remove() fn callback of parent driver. There were some discussions about having of_platform_unpopulate(), but it never made it to mainline. https://lkml.org/lkml/2013/7/19/615 There are some drivers which are removing platform_dev explicitly, as done in this patch. Note that, without this, multiple insmod-rmmod won't work, as I see driver probe gets invoked twice and leads to failure of probe. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 7d90f5dee1d3..697180d36acc 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -149,10 +149,21 @@ static int arche_platform_probe(struct platform_device *pdev) return ret; } +static int arche_remove_child(struct device *dev, void *unused) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + static int arche_platform_remove(struct platform_device *pdev) { struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); + device_for_each_child(&pdev->dev, NULL, arche_remove_child); + if (arche_pdata) arche_platform_cleanup(arche_pdata); -- cgit v1.2.3-59-g8ed1b From 1e5dd1f8279a8a934b9df7adec47b944fe6b10f4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 30 Dec 2015 13:38:33 -0800 Subject: greybus: arche-platform: merge arche-apb-ctrl and arche-platform No need to have two separate arche platform drivers, that's just crazy, so merge them both together to be only one kernel module. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Vaibhav Hiremath Tested-by: Vaibhav Hiremath --- drivers/staging/greybus/Makefile | 4 +-- drivers/staging/greybus/arche-apb-ctrl.c | 31 ++++---------------- drivers/staging/greybus/arche-platform.c | 49 ++++++++++++++++++++++++++++++-- drivers/staging/greybus/arche_platform.h | 17 +++++++++++ 4 files changed, 70 insertions(+), 31 deletions(-) create mode 100644 drivers/staging/greybus/arche_platform.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index b0d53f5228c0..011e87cb32cf 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -28,8 +28,7 @@ gb-light-y := light.o gb-raw-y := raw.o gb-hid-y := hid.o gb-es2-y := es2.o -gb-arche-y := arche-platform.o -gb-arche-apb-ctrl-y := arche-apb-ctrl.o +gb-arche-y := arche-platform.o arche-apb-ctrl.o gb-audio-codec-y := audio-codec.o gb-camera-y := camera.o @@ -43,7 +42,6 @@ obj-m += gb-hid.o obj-m += gb-raw.o obj-m += gb-es2.o obj-m += gb-arche.o -obj-m += gb-arche-apb-ctrl.o obj-m += gb-audio-codec.o obj-m += gb-camera.o diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 68f8d3cc8e97..f02b8add6ebc 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -23,6 +23,7 @@ #include #include #include +#include "arche_platform.h" enum apb_state { APB_STATE_OFF, @@ -279,7 +280,7 @@ static void apb_ctrl_cleanup(struct arche_apb_ctrl_drvdata *apb) /* TODO: May have to send an event to SVC about this exit */ } -static int arche_apb_ctrl_probe(struct platform_device *pdev) +int arche_apb_ctrl_probe(struct platform_device *pdev) { int ret; struct arche_apb_ctrl_drvdata *apb; @@ -335,7 +336,7 @@ exit: return ret; } -static int arche_apb_ctrl_remove(struct platform_device *pdev) +int arche_apb_ctrl_remove(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); @@ -375,28 +376,8 @@ static int arche_apb_ctrl_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, - arche_apb_ctrl_suspend, - arche_apb_ctrl_resume); +SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, + arche_apb_ctrl_suspend, + arche_apb_ctrl_resume); -static struct of_device_id arche_apb_ctrl_of_match[] = { - { .compatible = "usbffff,2", }, - { }, -}; -MODULE_DEVICE_TABLE(of, arche_apb_ctrl_of_match); - -static struct platform_driver arche_apb_ctrl_device_driver = { - .probe = arche_apb_ctrl_probe, - .remove = arche_apb_ctrl_remove, - .driver = { - .name = "arche-apb-ctrl", - .pm = &arche_apb_ctrl_pm_ops, - .of_match_table = of_match_ptr(arche_apb_ctrl_of_match), - } -}; - -module_platform_driver(arche_apb_ctrl_device_driver); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Vaibhav Hiremath "); -MODULE_DESCRIPTION("Arche APB control Driver"); diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 697180d36acc..50699520834f 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -23,6 +23,7 @@ #include #include #include +#include "arche_platform.h" struct arche_platform_drvdata { /* Control GPIO signals to and from AP <=> SVC */ @@ -208,7 +209,18 @@ static struct of_device_id arche_platform_of_match[] = { { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ { }, }; -MODULE_DEVICE_TABLE(of, arche_platform_of_match); + +static struct of_device_id arche_apb_ctrl_of_match[] = { + { .compatible = "usbffff,2", }, + { }, +}; + +static struct of_device_id arche_combined_id[] = { + { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ + { .compatible = "usbffff,2", }, + { }, +}; +MODULE_DEVICE_TABLE(of, arche_combined_id); static struct platform_driver arche_platform_device_driver = { .probe = arche_platform_probe, @@ -216,11 +228,42 @@ static struct platform_driver arche_platform_device_driver = { .driver = { .name = "arche-platform-ctrl", .pm = &arche_platform_pm_ops, - .of_match_table = of_match_ptr(arche_platform_of_match), + .of_match_table = arche_platform_of_match, } }; -module_platform_driver(arche_platform_device_driver); +static struct platform_driver arche_apb_ctrl_device_driver = { + .probe = arche_apb_ctrl_probe, + .remove = arche_apb_ctrl_remove, + .driver = { + .name = "arche-apb-ctrl", + .pm = &arche_apb_ctrl_pm_ops, + .of_match_table = arche_apb_ctrl_of_match, + } +}; + +static int __init arche_init(void) +{ + int retval; + + retval = platform_driver_register(&arche_platform_device_driver); + if (retval) + return retval; + + retval = platform_driver_register(&arche_apb_ctrl_device_driver); + if (retval) + platform_driver_unregister(&arche_platform_device_driver); + + return retval; +} +module_init(arche_init); + +static void __exit arche_exit(void) +{ + platform_driver_unregister(&arche_apb_ctrl_device_driver); + platform_driver_unregister(&arche_platform_device_driver); +} +module_exit(arche_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vaibhav Hiremath "); diff --git a/drivers/staging/greybus/arche_platform.h b/drivers/staging/greybus/arche_platform.h new file mode 100644 index 000000000000..22a968a9197e --- /dev/null +++ b/drivers/staging/greybus/arche_platform.h @@ -0,0 +1,17 @@ +/* + * Arche Platform driver to enable Unipro link. + * + * Copyright 2015-2016 Google Inc. + * Copyright 2015-2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __ARCHE_PLATFORM_H +#define __ARCHE_PLATFORM_H + +int arche_apb_ctrl_probe(struct platform_device *pdev); +int arche_apb_ctrl_remove(struct platform_device *pdev); +extern const struct dev_pm_ops arche_apb_ctrl_pm_ops; + +#endif /* __ARCHE_PLATFORM_H */ -- cgit v1.2.3-59-g8ed1b From 1f67ee5c04c9690c4c99fc4d31f4add46ea7be73 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 31 Dec 2015 04:20:56 +0200 Subject: greybus: camera: Raise the CSI-2 bandwidth Use 4 lanes at 960MHz to support camera modules requiring higher bandwidths until we implement support for dynamic bandwidth calculation. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index b181ac49e783..7be7cfca49bf 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -152,8 +152,8 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, if (nstreams && !(resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)) { csi_cfg.csi_id = 1; csi_cfg.clock_mode = 0; - csi_cfg.num_lanes = 2; - csi_cfg.bus_freq = 250000000; + csi_cfg.num_lanes = 4; + csi_cfg.bus_freq = 960000000; ret = es2_ap_csi_setup(gcam->connection->hd, true, &csi_cfg); } else if (nstreams == 0) { -- cgit v1.2.3-59-g8ed1b From 6da86df3a4aa6dfbb9af350df4a908a7a9254be0 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Wed, 6 Jan 2016 11:31:20 +0530 Subject: greybus: arche-platform: Export gpio (reset & sys_boot) to user In order to allow user to flash the firmware to, SVC: user need to assert the reset first, set sysboot pin and deassert reset. And then issue a flashing command. And APB: User need to assert the reset first, and then issue flashing command. So this patch exports the gpio's to user. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 14 ++++++++++++++ drivers/staging/greybus/arche-platform.c | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index f02b8add6ebc..8046e1eced83 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -71,6 +71,17 @@ static inline void deassert_gpio(unsigned int gpio) gpio_set_value(gpio, 0); } +/* Export gpio's to user space */ +static void export_gpios(struct arche_apb_ctrl_drvdata *apb) +{ + gpio_export(apb->resetn_gpio, false); +} + +static void unexport_gpios(struct arche_apb_ctrl_drvdata *apb) +{ + gpio_unexport(apb->resetn_gpio); +} + static irqreturn_t apb_ctrl_wake_detect_irq(int irq, void *devid) { struct arche_apb_ctrl_drvdata *apb = devid; @@ -328,6 +339,8 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) assert_gpio(apb->resetn_gpio); + export_gpios(apb); + dev_info(&pdev->dev, "Device registered successfully\n"); return 0; @@ -344,6 +357,7 @@ int arche_apb_ctrl_remove(struct platform_device *pdev) apb_ctrl_cleanup(apb); platform_set_drvdata(pdev, NULL); + unexport_gpios(apb); return 0; } diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 50699520834f..e6fe0156e128 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -45,6 +45,19 @@ static inline void svc_reset_onoff(unsigned int gpio, bool onoff) gpio_set_value(gpio, onoff); } +/* Export gpio's to user space */ +static void export_gpios(struct arche_platform_drvdata *arche_pdata) +{ + gpio_export(arche_pdata->svc_reset_gpio, false); + gpio_export(arche_pdata->svc_sysboot_gpio, false); +} + +static void unexport_gpios(struct arche_platform_drvdata *arche_pdata) +{ + gpio_unexport(arche_pdata->svc_reset_gpio); + gpio_unexport(arche_pdata->svc_sysboot_gpio); +} + static void arche_platform_cleanup(struct arche_platform_drvdata *arche_pdata) { /* As part of exit, put APB back in reset state */ @@ -141,6 +154,8 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->num_apbs = of_get_child_count(np); dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); + export_gpios(arche_pdata); + /* probe all childs here */ ret = of_platform_populate(np, NULL, NULL, dev); if (ret) @@ -169,6 +184,7 @@ static int arche_platform_remove(struct platform_device *pdev) arche_platform_cleanup(arche_pdata); platform_set_drvdata(pdev, NULL); + unexport_gpios(arche_pdata); return 0; } -- cgit v1.2.3-59-g8ed1b From ae0bf3a62805fa80c8277dd293d9d9d9b8b4c7c0 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Wed, 6 Jan 2016 23:46:45 +0530 Subject: greybus: arche-apb-ctrl: Set wake_detect gpio to low initially This patch enables handshaking of AP and SVC using wake_detect gpio (WD_8A and WD_8B). Note that WAKE_DETECT polarity is active-high, so in order to enable handshaking between AP <=> SVC, we need to set wake_detect gpio to low initially, so that driver can send WAKE_DET signal (active-high) to SVC and then SVC can send back WAKE_MOD signal (active-low). And on reception of WAKE_MOD signal, driver would bring respective APB out of reset. WD_8A => APB1 WD_8B => APB2 Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 8046e1eced83..d3db7fbb33b4 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -195,7 +195,8 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, gpio_set_value(apb->boot_ret_gpio, 0); udelay(50); - ret = devm_gpio_request(dev, apb->wake_detect_gpio, "wake detect"); + ret = devm_gpio_request_one(dev, apb->wake_detect_gpio, + GPIOF_INIT_LOW, "wake detect"); if (ret) dev_err(dev, "Failed requesting wake_detect gpio %d\n", apb->wake_detect_gpio); -- cgit v1.2.3-59-g8ed1b From e49268cc336eceb0653a03c4a0f9eb53eadf4d64 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Wed, 6 Jan 2016 23:46:46 +0530 Subject: greybus: arche-apb-ctrl: Do not bring APB out of reset in probe With addition of handshaking between AP <=> SVC, driver brings out APB out of reset only on reception of WAKE_MOD signal from SVC. So remove the deassertion from probe. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index d3db7fbb33b4..701cd5654449 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -338,8 +338,6 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) platform_set_drvdata(pdev, apb); - assert_gpio(apb->resetn_gpio); - export_gpios(apb); dev_info(&pdev->dev, "Device registered successfully\n"); -- cgit v1.2.3-59-g8ed1b From aab4a1a3684a46b423af23e4bdc9afe1b28dc389 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 6 Jan 2016 16:16:46 +0200 Subject: greybus: svc: Replace link config hack with standard operation The link config operation was a hack only designed to fulfill the camera driver's needs. Now that a standard operation is defined for the same purpose, implement it and remove the hack. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 16 ++++++--- drivers/staging/greybus/greybus_protocols.h | 50 ++++++++++++++++++++--------- drivers/staging/greybus/svc.c | 33 +++++++++++++------ drivers/staging/greybus/svc.h | 7 ++-- 4 files changed, 74 insertions(+), 32 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 7be7cfca49bf..4e96e1fcfcdf 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -606,13 +606,21 @@ static int gb_camera_connection_init(struct gb_connection *connection) gcam->data_connected = true; - ret = gb_svc_link_config(svc, connection->intf->interface_id, - GB_SVC_LINK_CONFIG_BURST_HS_A, 2, 2, 0); + ret = gb_svc_intf_set_power_mode(svc, connection->intf->interface_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_PWRM_RXTERMINATION | + GB_SVC_PWRM_TXTERMINATION, 0); if (ret < 0) goto error; - ret = gb_svc_link_config(svc, svc->ap_intf_id, - GB_SVC_LINK_CONFIG_BURST_HS_A, 2, 2, 0); + ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_PWRM_RXTERMINATION | + GB_SVC_PWRM_TXTERMINATION, 0); if (ret < 0) goto error; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index a0bddaa36629..72e753c697aa 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -737,7 +737,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_DME_PEER_SET 0x0a #define GB_SVC_TYPE_ROUTE_CREATE 0x0b #define GB_SVC_TYPE_ROUTE_DESTROY 0x0c -#define GB_SVC_TYPE_LINK_CONFIG 0x10 +#define GB_SVC_TYPE_INTF_SET_PWRM 0x10 /* * SVC version request/response has the same payload as @@ -819,20 +819,6 @@ struct gb_svc_dme_peer_set_response { __le16 result_code; } __packed; -#define GB_SVC_LINK_CONFIG_BURST_PWM 0 -#define GB_SVC_LINK_CONFIG_BURST_HS_A 1 -#define GB_SVC_LINK_CONFIG_BURST_HS_B 2 -#define GB_SVC_LINK_CONFIG_FLAG_AUTO_SLEEP (1 << 0) - -struct gb_svc_link_config_request { - __u8 intf_id; - __u8 burst; - __u8 gear; - __u8 nlanes; - __u8 flags; -} __packed; -/* link config response has no payload */ - /* Attributes for peer get/set operations */ #define DME_ATTR_SELECTOR_INDEX 0 /* FIXME: remove ES2 support and DME_ATTR_T_TST_SRC_INCREMENT */ @@ -860,6 +846,40 @@ struct gb_svc_route_destroy_request { } __packed; /* route destroy response has no payload */ +#define GB_SVC_UNIPRO_FAST_MODE 0x01 +#define GB_SVC_UNIPRO_SLOW_MODE 0x02 +#define GB_SVC_UNIPRO_FAST_AUTO_MODE 0x04 +#define GB_SVC_UNIPRO_SLOW_AUTO_MODE 0x05 +#define GB_SVC_UNIPRO_MODE_UNCHANGED 0x07 +#define GB_SVC_UNIPRO_HIBERNATE_MODE 0x11 +#define GB_SVC_UNIPRO_OFF_MODE 0x12 + +#define GB_SVC_PWRM_RXTERMINATION 0x01 +#define GB_SVC_PWRM_TXTERMINATION 0x02 +#define GB_SVC_PWRM_LINE_RESET 0x04 +#define GB_SVC_PWRM_SCRAMBLING 0x20 + +#define GB_SVC_PWRM_QUIRK_HSSER 0x00000001 + +#define GB_SVC_UNIPRO_HS_SERIES_A 0x01 +#define GB_SVC_UNIPRO_HS_SERIES_B 0x02 + +struct gb_svc_intf_set_pwrm_request { + __u8 intf_id; + __u8 hs_series; + __u8 tx_mode; + __u8 tx_gear; + __u8 tx_nlanes; + __u8 rx_mode; + __u8 rx_gear; + __u8 rx_nlanes; + __u8 flags; + __le32 quirks; +} __packed; + +struct gb_svc_intf_set_pwrm_response { + __le16 result_code; +} __packed; /* RAW */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index fe7bd2850d5d..c4c3bb54c9f7 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -285,22 +285,35 @@ static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) } } -int gb_svc_link_config(struct gb_svc *svc, u8 intf_id, - unsigned int burst, unsigned int gear, - unsigned int nlanes, unsigned int flags) +int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, + u8 tx_mode, u8 tx_gear, u8 tx_nlanes, + u8 rx_mode, u8 rx_gear, u8 rx_nlanes, + u8 flags, u32 quirks) { - struct gb_svc_link_config_request request; + struct gb_svc_intf_set_pwrm_request request; + struct gb_svc_intf_set_pwrm_response response; + int ret; request.intf_id = intf_id; - request.burst = burst; - request.gear = gear; - request.nlanes = nlanes; + request.hs_series = hs_series; + request.tx_mode = tx_mode; + request.tx_gear = tx_gear; + request.tx_nlanes = tx_nlanes; + request.rx_mode = rx_mode; + request.rx_gear = rx_gear; + request.rx_nlanes = rx_nlanes; request.flags = flags; + request.quirks = cpu_to_le32(quirks); - return gb_operation_sync(svc->connection, GB_SVC_TYPE_LINK_CONFIG, - &request, sizeof(request), NULL, 0); + ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, + &request, sizeof(request), + &response, sizeof(response)); + if (ret < 0) + return ret; + + return le16_to_cpu(response.result_code); } -EXPORT_SYMBOL_GPL(gb_svc_link_config); +EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); static int gb_svc_version_request(struct gb_operation *op) { diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 23060357e3ad..0ebbed9c791f 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -44,9 +44,10 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value); int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 value); -int gb_svc_link_config(struct gb_svc *svc, u8 intf_id, unsigned int burst, - unsigned int gear, unsigned int nlanes, - unsigned int flags); +int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, + u8 tx_mode, u8 tx_gear, u8 tx_nlanes, + u8 rx_mode, u8 rx_gear, u8 rx_nlanes, + u8 flags, u32 quirks); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From b787d413e0d99ae74e21cdab600ea3c5b0886ec2 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 8 Jan 2016 18:13:20 +0200 Subject: greybus: camera: Add support for flags to stream_configure Add support for the flags field of the stream configure request that was recently added to the camera protocol and update the debugfs arguments parsing accordingly. The stream configure response layout is also updated to the latest protocol specification. Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 24 +++++++++++++++++------- drivers/staging/greybus/greybus_protocols.h | 10 ++++++---- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 4e96e1fcfcdf..06ea5293d389 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -76,6 +76,7 @@ struct gb_camera_stream_config { static int gb_camera_configure_streams(struct gb_camera *gcam, unsigned int nstreams, + unsigned int flags, struct gb_camera_stream_config *streams) { struct gb_camera_configure_streams_request *req; @@ -99,7 +100,8 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto done; } - req->num_streams = cpu_to_le16(nstreams); + req->num_streams = nstreams; + req->flags = flags; req->padding = 0; for (i = 0; i < nstreams; ++i) { @@ -117,9 +119,9 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, if (ret < 0) goto done; - if (le16_to_cpu(resp->num_streams) > nstreams) { + if (resp->num_streams > nstreams) { gcam_dbg(gcam, "got #streams %u > request %u\n", - le16_to_cpu(resp->num_streams), nstreams); + resp->num_streams, nstreams); ret = -EIO; goto done; } @@ -169,7 +171,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, gcam_err(gcam, "failed to %s the CSI transmitter\n", nstreams ? "start" : "stop"); - ret = le16_to_cpu(resp->num_streams); + ret = resp->num_streams; done: kfree(req); @@ -266,13 +268,13 @@ static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; struct gb_camera_stream_config *streams; unsigned int nstreams; - const char *sep = ";"; + unsigned int flags; unsigned int i; char *token; int ret; /* Retrieve number of streams to configure */ - token = strsep(&buf, sep); + token = strsep(&buf, ";"); if (token == NULL) return -EINVAL; @@ -283,6 +285,14 @@ static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, if (nstreams > GB_CAMERA_MAX_STREAMS) return -EINVAL; + token = strsep(&buf, ";"); + if (token == NULL) + return -EINVAL; + + ret = kstrtouint(token, 10, &flags); + if (ret < 0) + return ret; + /* For each stream to configure parse width, height and format */ streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); if (!streams) @@ -320,7 +330,7 @@ static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, goto done; } - ret = gb_camera_configure_streams(gcam, nstreams, streams); + ret = gb_camera_configure_streams(gcam, nstreams, flags, streams); if (ret < 0) goto done; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 72e753c697aa..67f260beba63 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1173,7 +1173,9 @@ struct gb_camera_stream_config_request { } __packed; struct gb_camera_configure_streams_request { - __le16 num_streams; + __u8 num_streams; + __u8 flags; +#define GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY 0x01 __le16 padding; struct gb_camera_stream_config_request config[0]; } __packed; @@ -1190,10 +1192,10 @@ struct gb_camera_stream_config_response { } __packed; struct gb_camera_configure_streams_response { - __le16 num_streams; -#define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED 0x01 + __u8 num_streams; __u8 flags; - __u8 padding; +#define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED 0x01 + __le16 padding; struct gb_camera_stream_config_response config[0]; } __packed; -- cgit v1.2.3-59-g8ed1b From 13da9e11e9b781e54fc5db6b65bdcdf6dd96022d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 8 Jan 2016 20:13:43 +0100 Subject: greybus: core: add drvdata accessors Add greybus driver-data accessors. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 405e56540a3a..27679468e997 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -72,6 +72,16 @@ struct greybus_driver { }; #define to_greybus_driver(d) container_of(d, struct greybus_driver, driver) +static inline void greybus_set_drvdata(struct gb_bundle *bundle, void *data) +{ + dev_set_drvdata(&bundle->dev, data); +} + +static inline void *greybus_get_drvdata(struct gb_bundle *bundle) +{ + return dev_get_drvdata(&bundle->dev); +} + /* Don't call these directly, use the module_greybus_driver() macro instead */ int greybus_register_driver(struct greybus_driver *driver, struct module *module, const char *mod_name); -- cgit v1.2.3-59-g8ed1b From 3c48d1b8a5a9571c9107fc7f2b5aea31d458fd18 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 8 Jan 2016 20:13:42 +0100 Subject: greybus: core: fix greybus driver registration Add missing bus type to driver structure when registering a greybus driver. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 209e1cd62838..c56c0c4a44f5 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -162,6 +162,7 @@ int greybus_register_driver(struct greybus_driver *driver, struct module *owner, if (greybus_disabled()) return -ENODEV; + driver->driver.bus = &greybus_bus_type; driver->driver.name = driver->name; driver->driver.probe = greybus_probe; driver->driver.remove = greybus_remove; -- cgit v1.2.3-59-g8ed1b From b77f9328f2d629220389d44febf7ab6b48848050 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 8 Jan 2016 20:13:41 +0100 Subject: greybus: core: fix greybus device matching The bus code should only match bundle devices for now, and must not assume all greybus devices are bundles. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index c56c0c4a44f5..6b31155d4a9c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -65,9 +65,14 @@ greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id) static int greybus_module_match(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(drv); - struct gb_bundle *bundle = to_gb_bundle(dev); + struct gb_bundle *bundle; const struct greybus_bundle_id *id; + if (!is_gb_bundle(dev)) + return 0; + + bundle = to_gb_bundle(dev); + id = greybus_match_id(bundle, driver->id_table); if (id) return 1; -- cgit v1.2.3-59-g8ed1b From 4e6e3f5cff2dc72c05c8a8fb4539189ac9141133 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 8 Jan 2016 20:13:40 +0100 Subject: greybus: connection: fix version-request error handling Use the host device and connection name when logging errors as the version-request helper must not assume that all connection have a bundle. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 5a24dbef98f2..b356ee0575e2 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -378,8 +378,9 @@ static int gb_connection_protocol_get_version(struct gb_connection *connection) ret = gb_protocol_get_version(connection); if (ret) { - dev_err(&connection->bundle->dev, - "failed to get protocol version: %d\n", ret); + dev_err(&connection->hd->dev, + "%s: failed to get protocol version: %d\n", + connection->name, ret); return ret; } -- cgit v1.2.3-59-g8ed1b From fc41c2da44c510a919f68e2486aeb456f90a50a4 Mon Sep 17 00:00:00 2001 From: Eli Sennesh Date: Fri, 8 Jan 2016 14:11:29 -0500 Subject: greybus: firmware/bootrom: debug output from bootrom protocol The bootrom protocol issues no dynamic debugging messages when it functions successfully. Use dev_dbg() to fix that by issuing kernel logs when firmware download works. Signed-off-by: Eli Sennesh Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index c65f2948be02..b65787c4116e 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -61,6 +61,9 @@ static void firmware_es2_fixup_vid_pid(struct gb_firmware *firmware) firmware->vendor_id = le32_to_cpu(response.vendor_id); firmware->product_id = le32_to_cpu(response.product_id); + + dev_dbg(&connection->bundle->dev, "Firmware got vid (0x%x)/pid (0x%x)\n", + firmware->vendor_id, firmware->product_id); } /* This returns path of the firmware blob on the disk */ @@ -69,6 +72,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) struct gb_connection *connection = firmware->connection; struct gb_interface *intf = connection->bundle->intf; char firmware_name[48]; + int rc; /* Already have a firmware, free it */ if (firmware->fw) @@ -84,8 +88,11 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, firmware->vendor_id, firmware->product_id, stage); - return request_firmware(&firmware->fw, firmware_name, - &connection->bundle->dev); + rc = request_firmware(&firmware->fw, firmware_name, + &connection->bundle->dev); + dev_dbg(&connection->bundle->dev, "Searched for TFTF %s: %d\n", + firmware_name, rc); + return rc; } static int gb_firmware_size_request(struct gb_operation *op) @@ -121,6 +128,8 @@ static int gb_firmware_size_request(struct gb_operation *op) size_response = op->response->payload; size_response->size = cpu_to_le32(firmware->fw->size); + dev_dbg(dev, "%s: firmware size %d bytes\n", __func__, size_response->size); + return 0; } @@ -165,6 +174,9 @@ static int gb_firmware_get_firmware(struct gb_operation *op) firmware_response = op->response->payload; memcpy(firmware_response->data, fw->data + offset, size); + dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, + size); + return 0; } @@ -192,6 +204,7 @@ static int gb_firmware_ready_to_boot(struct gb_operation *op) /* * XXX Should we return error for insecure firmware? */ + dev_dbg(dev, "ready to boot: 0x%x, 0\n", status); return 0; } @@ -245,6 +258,8 @@ static int gb_firmware_connection_init(struct gb_connection *connection) "failed to send AP READY: %d\n", ret); } + dev_dbg(&connection->bundle->dev, "%s: AP_READY sent\n", __func__); + return 0; } @@ -258,6 +273,8 @@ static void gb_firmware_connection_exit(struct gb_connection *connection) connection->private = NULL; kfree(firmware); + + dev_dbg(&connection->bundle->dev, "%s\n", __func__); } static struct gb_protocol firmware_protocol = { -- cgit v1.2.3-59-g8ed1b From 73658f2a285dd8b53160355e619fcb59db3bb98a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:03 +0530 Subject: greybus: arche-platform: arche_pdata is guaranteed to be valid arche_pdata is guaranteed to be valid in arche_platform_remove(). Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index e6fe0156e128..259473cd506d 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -179,10 +179,7 @@ static int arche_platform_remove(struct platform_device *pdev) struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); device_for_each_child(&pdev->dev, NULL, arche_remove_child); - - if (arche_pdata) - arche_platform_cleanup(arche_pdata); - + arche_platform_cleanup(arche_pdata); platform_set_drvdata(pdev, NULL); unexport_gpios(arche_pdata); -- cgit v1.2.3-59-g8ed1b From 8adf71d1b43aa1c449d22dd8e6f6c29957872a7f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:04 +0530 Subject: greybus: arche-platform: Export GPIOs after populating APBs Populating APBs operation can potentially fail and it would be better if we export the GPIOs towards then end of the routine, so that we don't need to unexport them on error cases. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 259473cd506d..93d90b2dc866 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -154,13 +154,13 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->num_apbs = of_get_child_count(np); dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); - export_gpios(arche_pdata); - /* probe all childs here */ ret = of_platform_populate(np, NULL, NULL, dev); if (ret) dev_err(dev, "no child node found\n"); + export_gpios(arche_pdata); + dev_info(dev, "Device registered successfully\n"); return ret; } -- cgit v1.2.3-59-g8ed1b From 72a8c24b6dcae46c4e8c37c1b1d1adb6e6b8a72f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:05 +0530 Subject: greybus: arche-platform: Put APB in reset if of_platform_populate() fails The current implementation around of_platform_populate() is not so great. On error, we first print an error message, followed by a success message and finally we return an error. And over that we don't undo what we did initially. This patch puts the APB back into reset and create a separate error path to make things clear. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 93d90b2dc866..67bbd71ec3d1 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -156,13 +156,16 @@ static int arche_platform_probe(struct platform_device *pdev) /* probe all childs here */ ret = of_platform_populate(np, NULL, NULL, dev); - if (ret) + if (ret) { + arche_platform_cleanup(arche_pdata); dev_err(dev, "no child node found\n"); + return ret; + } export_gpios(arche_pdata); dev_info(dev, "Device registered successfully\n"); - return ret; + return 0; } static int arche_remove_child(struct device *dev, void *unused) -- cgit v1.2.3-59-g8ed1b From 140741ec6384435b1a0f89042a2a6dac8e2eebd2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:06 +0530 Subject: greybus: arche-platform: svc_reset_gpio can't be invalid in arche_platform_cleanup() svc_reset_gpio is guaranteed to be valid in arche_platform_cleanup. Drop the useless check. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 67bbd71ec3d1..93ecd8c8bed7 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -61,9 +61,8 @@ static void unexport_gpios(struct arche_platform_drvdata *arche_pdata) static void arche_platform_cleanup(struct arche_platform_drvdata *arche_pdata) { /* As part of exit, put APB back in reset state */ - if (gpio_is_valid(arche_pdata->svc_reset_gpio)) - svc_reset_onoff(arche_pdata->svc_reset_gpio, - arche_pdata->is_reset_act_hi); + svc_reset_onoff(arche_pdata->svc_reset_gpio, + arche_pdata->is_reset_act_hi); } static int arche_platform_probe(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From f1f251b59b6db97c42d7d746727072cb37604494 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:07 +0530 Subject: greybus: arche-platform: propagate errors returned by gpiolib Propagate errors returned by of_get_named_gpio() instead of sending -ENODEV. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 93ecd8c8bed7..52c79ae792f4 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -82,7 +82,7 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); if (arche_pdata->svc_reset_gpio < 0) { dev_err(dev, "failed to get reset-gpio\n"); - return -ENODEV; + return arche_pdata->svc_reset_gpio; } ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); if (ret) { @@ -100,7 +100,7 @@ static int arche_platform_probe(struct platform_device *pdev) "svc,sysboot-gpio", 0); if (arche_pdata->svc_sysboot_gpio < 0) { dev_err(dev, "failed to get sysboot gpio\n"); - return -ENODEV; + return arche_pdata->svc_sysboot_gpio; } ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); if (ret) { @@ -118,7 +118,7 @@ static int arche_platform_probe(struct platform_device *pdev) "svc,refclk-req-gpio", 0); if (arche_pdata->svc_refclk_req < 0) { dev_err(dev, "failed to get svc clock-req gpio\n"); - return -ENODEV; + return arche_pdata->svc_refclk_req; } ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); if (ret) { -- cgit v1.2.3-59-g8ed1b From 3b538c399c3273ec268f7c641c8ce35c1235763a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:08 +0530 Subject: greybus: arche-apb: Spelling and whitespace fixes - s/premits/permits - Remove blank lines at the end of the file Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 701cd5654449..4fe2d110c4ae 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -364,7 +364,7 @@ int arche_apb_ctrl_remove(struct platform_device *pdev) static int arche_apb_ctrl_suspend(struct device *dev) { /* - * If timing profile premits, we may shutdown bridge + * If timing profile permits, we may shutdown bridge * completely * * TODO: sequence ?? @@ -392,5 +392,3 @@ static int arche_apb_ctrl_resume(struct device *dev) SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, arche_apb_ctrl_suspend, arche_apb_ctrl_resume); - - -- cgit v1.2.3-59-g8ed1b From 977ff250755f4ae01ef50e98d792fabebb199c34 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:09 +0530 Subject: greybus: arche-apb: platform data 'apb' is guaranteed to be valid Platform data 'apb' is guaranteed to be valid in arche_apb_ctrl_remove() and so no need to check it. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 4fe2d110c4ae..3d71093c4697 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -352,9 +352,7 @@ int arche_apb_ctrl_remove(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); - if (apb) - apb_ctrl_cleanup(apb); - + apb_ctrl_cleanup(apb); platform_set_drvdata(pdev, NULL); unexport_gpios(apb); -- cgit v1.2.3-59-g8ed1b From 7541c1a1c6b0e5531545c400e27b8aee2ba71610 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:10 +0530 Subject: greybus: arche-apb: Replace gpio_is_valid() with gpio < 0 checks There can be no invalid values in the DTS. The actual pin numbers are assigned by gpiolib when the gpio controller is registered. And so a simple 'gpio < 0' is enough instead of gpio_is_valid() which also checks for 'gpio < ARCH_NR_GPIOS'. This will make the usage of of_get_named_gpio() similar with how it is done in arche-platform driver. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 3d71093c4697..b06bb3453ca4 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -211,37 +211,37 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, struct device_node *np = dev->of_node; apb->wake_detect_gpio = of_get_named_gpio(np, "wake-detect-gpios", 0); - if (!gpio_is_valid(apb->wake_detect_gpio)) { + if (apb->wake_detect_gpio < 0) { dev_err(dev, "failed to get wake detect gpio\n"); return apb->wake_detect_gpio; } apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0); - if (!gpio_is_valid(apb->resetn_gpio)) { + if (apb->resetn_gpio < 0) { dev_err(dev, "failed to get reset gpio\n"); return apb->resetn_gpio; } apb->boot_ret_gpio = of_get_named_gpio(np, "boot-ret-gpios", 0); - if (!gpio_is_valid(apb->boot_ret_gpio)) { + if (apb->boot_ret_gpio < 0) { dev_err(dev, "failed to get boot retention gpio\n"); return apb->boot_ret_gpio; } /* It's not mandatory to support power management interface */ apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0); - if (!gpio_is_valid(apb->pwroff_gpio)) { + if (apb->pwroff_gpio < 0) { dev_info(dev, "failed to get power off gpio\n"); return apb->pwroff_gpio; } /* Do not make clock mandatory as of now (for DB3) */ apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0); - if (!gpio_is_valid(apb->clk_en_gpio)) + if (apb->clk_en_gpio < 0) dev_err(dev, "failed to get clock en gpio\n"); apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0); - if (!gpio_is_valid(apb->pwrdn_gpio)) + if (apb->pwrdn_gpio < 0) dev_info(dev, "failed to get power down gpio\n"); /* Regulators are optional, as we may have fixed supply coming in */ -- cgit v1.2.3-59-g8ed1b From bd62fa5e1067bd5a3c8421ae3de0021a67223e22 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:11 +0530 Subject: greybus: arche-apb: devm_regulator_get() doesn't return NULL And so we don't need to check for it. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index b06bb3453ca4..1ae2c06c8e33 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -246,13 +246,13 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, /* Regulators are optional, as we may have fixed supply coming in */ apb->vcore = devm_regulator_get(dev, "vcore"); - if (IS_ERR_OR_NULL(apb->vcore)) { + if (IS_ERR(apb->vcore)) { dev_info(dev, "no core regulator found\n"); apb->vcore = NULL; } apb->vio = devm_regulator_get(dev, "vio"); - if (IS_ERR_OR_NULL(apb->vio)) { + if (IS_ERR(apb->vio)) { dev_info(dev, "no IO regulator found\n"); apb->vio = NULL; } -- cgit v1.2.3-59-g8ed1b From dcf77c397918178af09b72f63145eed3fde788ba Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:12 +0530 Subject: greybus: arche-apb: NULL is a valid regulator Since NULL could in theory be a valid regulator we ought to check for IS_ERR() rather than for NULL. In practice this is unlikely to be an issue but it's better for neatness. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 1ae2c06c8e33..acdeb71dde7b 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -170,14 +170,14 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, } /* Enable power to APB */ - if (apb->vcore) { + if (!IS_ERR(apb->vcore)) { ret = regulator_enable(apb->vcore); if (ret) { dev_err(dev, "failed to enable core regulator\n"); return ret; } } - if (apb->vio) { + if (!IS_ERR(apb->vio)) { ret = regulator_enable(apb->vio); if (ret) { dev_err(dev, "failed to enable IO regulator\n"); @@ -246,16 +246,12 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, /* Regulators are optional, as we may have fixed supply coming in */ apb->vcore = devm_regulator_get(dev, "vcore"); - if (IS_ERR(apb->vcore)) { + if (IS_ERR(apb->vcore)) dev_info(dev, "no core regulator found\n"); - apb->vcore = NULL; - } apb->vio = devm_regulator_get(dev, "vio"); - if (IS_ERR(apb->vio)) { + if (IS_ERR(apb->vio)) dev_info(dev, "no IO regulator found\n"); - apb->vio = NULL; - } apb->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(apb->pinctrl)) { @@ -275,10 +271,10 @@ static void apb_ctrl_cleanup(struct arche_apb_ctrl_drvdata *apb) { unsigned long flags; - if (apb->vcore && regulator_is_enabled(apb->vcore) > 0) + if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0) regulator_disable(apb->vcore); - if (apb->vio && regulator_is_enabled(apb->vio) > 0) + if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0) regulator_disable(apb->vio); spin_lock_irqsave(&apb->lock, flags); -- cgit v1.2.3-59-g8ed1b From db0cff554a612715701a04143df595cc87737d55 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:13 +0530 Subject: greybus: arche-apb: Properly use dev_err/info/warn Use dev_err for errors after which we need to abort the currently running routine and dev_warn for resource allocation failure, with which we can continue to work. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index acdeb71dde7b..8582a4849221 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -138,11 +138,11 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, if (gpio_is_valid(apb->clk_en_gpio)) { ret = devm_gpio_request(dev, apb->clk_en_gpio, "apb_clk_en"); if (ret) - dev_err(dev, "Failed requesting APB clock en gpio %d\n", + dev_warn(dev, "Failed requesting APB clock en gpio %d\n", apb->clk_en_gpio); ret = gpio_direction_output(apb->clk_en_gpio, 1); if (ret) - dev_err(dev, "failed to set APB clock en gpio dir:%d\n", ret); + dev_warn(dev, "failed to set APB clock en gpio dir:%d\n", ret); } /* Hold APB in reset state */ ret = devm_gpio_request(dev, apb->resetn_gpio, "apb-reset"); @@ -231,27 +231,27 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, /* It's not mandatory to support power management interface */ apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0); if (apb->pwroff_gpio < 0) { - dev_info(dev, "failed to get power off gpio\n"); + dev_err(dev, "failed to get power off gpio\n"); return apb->pwroff_gpio; } /* Do not make clock mandatory as of now (for DB3) */ apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0); if (apb->clk_en_gpio < 0) - dev_err(dev, "failed to get clock en gpio\n"); + dev_warn(dev, "failed to get clock en gpio\n"); apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0); if (apb->pwrdn_gpio < 0) - dev_info(dev, "failed to get power down gpio\n"); + dev_warn(dev, "failed to get power down gpio\n"); /* Regulators are optional, as we may have fixed supply coming in */ apb->vcore = devm_regulator_get(dev, "vcore"); if (IS_ERR(apb->vcore)) - dev_info(dev, "no core regulator found\n"); + dev_warn(dev, "no core regulator found\n"); apb->vio = devm_regulator_get(dev, "vio"); if (IS_ERR(apb->vio)) - dev_info(dev, "no IO regulator found\n"); + dev_warn(dev, "no IO regulator found\n"); apb->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(apb->pinctrl)) { -- cgit v1.2.3-59-g8ed1b From a7a794ec540c442999598cf16605ba7962aa3861 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:14 +0530 Subject: greybus: arche-apb: Don't use gpio after failing to request it If devm_gpio_request() returns an error, we shouldn't try to set the direction of the same gpio. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 8582a4849221..f83230deb4b4 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -137,12 +137,15 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, /* On DB3 clock was not mandatory */ if (gpio_is_valid(apb->clk_en_gpio)) { ret = devm_gpio_request(dev, apb->clk_en_gpio, "apb_clk_en"); - if (ret) + if (ret) { dev_warn(dev, "Failed requesting APB clock en gpio %d\n", - apb->clk_en_gpio); - ret = gpio_direction_output(apb->clk_en_gpio, 1); - if (ret) - dev_warn(dev, "failed to set APB clock en gpio dir:%d\n", ret); + apb->clk_en_gpio); + } else { + ret = gpio_direction_output(apb->clk_en_gpio, 1); + if (ret) + dev_warn(dev, "failed to set APB clock en gpio dir:%d\n", + ret); + } } /* Hold APB in reset state */ ret = devm_gpio_request(dev, apb->resetn_gpio, "apb-reset"); -- cgit v1.2.3-59-g8ed1b From d258432fb2fbf1a5a4910dbc31aba99d04801268 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:15 +0530 Subject: greybus: arche-apb: Do cleanup within apb_ctrl_init_seq() for error cases Relying on apb_ctrl_cleanup() to do the cleanup for errors that occurred within apb_ctrl_init_seq() isn't a very clean idea. Handle that separately within apb_ctrl_init_seq(). This will clean apb_ctrl_cleanup() in later patches. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index f83230deb4b4..859d22e3f149 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -184,7 +184,7 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, ret = regulator_enable(apb->vio); if (ret) { dev_err(dev, "failed to enable IO regulator\n"); - return ret; + goto out_vcore_disable; } } @@ -193,16 +193,27 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, if (ret) { dev_err(dev, "Failed requesting bootret gpio %d\n", apb->boot_ret_gpio); - return ret; + goto out_vio_disable; } gpio_set_value(apb->boot_ret_gpio, 0); udelay(50); ret = devm_gpio_request_one(dev, apb->wake_detect_gpio, GPIOF_INIT_LOW, "wake detect"); - if (ret) + if (ret) { dev_err(dev, "Failed requesting wake_detect gpio %d\n", apb->wake_detect_gpio); + goto out_vio_disable; + } + + return 0; + +out_vio_disable: + if (!IS_ERR(apb->vio)) + regulator_disable(apb->vio); +out_vcore_disable: + if (!IS_ERR(apb->vcore)) + regulator_disable(apb->vcore); return ret; } @@ -311,7 +322,7 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "failed to set init state of control signal %d\n", ret); - goto exit; + return ret; } spin_lock_init(&apb->lock); @@ -332,7 +343,8 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) "wake detect", apb); if (ret) { dev_err(dev, "failed to request wake detect IRQ\n"); - goto exit; + apb_ctrl_cleanup(apb); + return ret; } platform_set_drvdata(pdev, apb); @@ -341,10 +353,6 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Device registered successfully\n"); return 0; - -exit: - apb_ctrl_cleanup(apb); - return ret; } int arche_apb_ctrl_remove(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 40989cf3c1e3c114d3558ad4d7513344ed612ac2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:16 +0530 Subject: greybus: arche-apb: Drop unnecessary checks wake_detect_gpio and resetn_gpio are guaranteed to be valid in apb_ctrl_cleanup() and irq-handler, no need to check for their validity. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 859d22e3f149..04c44675926e 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -94,10 +94,6 @@ static irqreturn_t apb_ctrl_wake_detect_irq(int irq, void *devid) * signals, especially when we start using GPIOs over slow * buses like I2C. */ - if (!gpio_is_valid(apb->wake_detect_gpio) && - !gpio_is_valid(apb->resetn_gpio)) - return IRQ_HANDLED; /* Should it be IRQ_NONE ?? */ - spin_lock_irqsave(&apb->lock, flags); if (apb->state != APB_STATE_ACTIVE) { @@ -293,9 +289,7 @@ static void apb_ctrl_cleanup(struct arche_apb_ctrl_drvdata *apb) spin_lock_irqsave(&apb->lock, flags); /* As part of exit, put APB back in reset state */ - if (gpio_is_valid(apb->resetn_gpio)) - gpio_set_value(apb->resetn_gpio, 0); - + gpio_set_value(apb->resetn_gpio, 0); apb->state = APB_STATE_OFF; spin_unlock_irqrestore(&apb->lock, flags); -- cgit v1.2.3-59-g8ed1b From 3b858df01fe936bbd329ecf13c3a3d81486f1f16 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 11 Jan 2016 11:29:17 +0530 Subject: greybus: arche: Remove unwanted headers and rearrange others This removes few unwanted headers related to irq, interrupt, regulator, spinlock, etc. Also arrange the rest in alphabetical order to make it more readable. Signed-off-by: Viresh Kumar Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 18 +++++++----------- drivers/staging/greybus/arche-platform.c | 19 ++++++------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 04c44675926e..afdaeca999ca 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -7,22 +7,18 @@ * Released under the GPLv2 only. */ -#include -#include -#include -#include -#include -#include +#include #include -#include #include -#include -#include +#include #include #include -#include -#include +#include #include +#include +#include +#include +#include #include "arche_platform.h" enum apb_state { diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 52c79ae792f4..94e6f5dc6315 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -7,22 +7,15 @@ * Released under the GPLv2 only. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include #include -#include -#include -#include +#include #include +#include +#include #include "arche_platform.h" struct arche_platform_drvdata { -- cgit v1.2.3-59-g8ed1b From 33036178bf04a06ad7e87513c76d7075959088e8 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Mon, 11 Jan 2016 17:41:22 +0530 Subject: greybus: arche-apb-ctrl: Remove wake_detect gpio and related code With TIME_SYNC functionality, assignment of wake/detect pin will change, WD_8A => to bring APB's out of reset WD_8B => TIME_SYNC operation So in order to support this, we can no longer keep wake_detect gpio in apb-ctrl driver. So remove it. In the subsequent patches, wake_detect support will be added to parent SVC driver (arche-platform) who is responsible for SVC control. Note that, this patch also removes ISR related code. The APB state still is maintained, for future use. Signed-off-by: Vaibhav Hiremath Tested-by: Michael Scott Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 81 -------------------------------- 1 file changed, 81 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index afdaeca999ca..5b9a3ed212af 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -29,7 +29,6 @@ enum apb_state { struct arche_apb_ctrl_drvdata { /* Control GPIO signals to and from AP <=> AP Bridges */ - int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ int resetn_gpio; int boot_ret_gpio; int pwroff_gpio; @@ -37,7 +36,6 @@ struct arche_apb_ctrl_drvdata { int wake_out_gpio; int pwrdn_gpio; - unsigned int wake_detect_irq; enum apb_state state; struct regulator *vcore; @@ -48,9 +46,6 @@ struct arche_apb_ctrl_drvdata { struct pinctrl *pinctrl; struct pinctrl_state *pin_default; - - /* To protect concurrent access of GPIO registers, need protection */ - spinlock_t lock; }; /* @@ -78,45 +73,6 @@ static void unexport_gpios(struct arche_apb_ctrl_drvdata *apb) gpio_unexport(apb->resetn_gpio); } -static irqreturn_t apb_ctrl_wake_detect_irq(int irq, void *devid) -{ - struct arche_apb_ctrl_drvdata *apb = devid; - unsigned long flags; - - /* - * TODO: - * Since currently SoC GPIOs are being used we are safe here - * But ideally we should create a workqueue and process the control - * signals, especially when we start using GPIOs over slow - * buses like I2C. - */ - spin_lock_irqsave(&apb->lock, flags); - - if (apb->state != APB_STATE_ACTIVE) { - /* Bring bridge out of reset on this event */ - gpio_set_value(apb->resetn_gpio, 1); - apb->state = APB_STATE_ACTIVE; - } else { - /* - * Assert Wake_OUT signal to APB - * It would resemble WakeDetect module's signal pass-through - */ - /* - * We have to generate the pulse, so we may need to schedule - * workqueue here. - * - * Also, since we are using both rising and falling edge for - * interrupt trigger, we may not need workqueue. Just pass - * through the value to bridge. - * Just read GPIO value and pass it to the bridge - */ - } - - spin_unlock_irqrestore(&apb->lock, flags); - - return IRQ_HANDLED; -} - /* * Note: Please do not modify the below sequence, as it is as per the spec */ @@ -190,14 +146,6 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, gpio_set_value(apb->boot_ret_gpio, 0); udelay(50); - ret = devm_gpio_request_one(dev, apb->wake_detect_gpio, - GPIOF_INIT_LOW, "wake detect"); - if (ret) { - dev_err(dev, "Failed requesting wake_detect gpio %d\n", - apb->wake_detect_gpio); - goto out_vio_disable; - } - return 0; out_vio_disable: @@ -216,12 +164,6 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - apb->wake_detect_gpio = of_get_named_gpio(np, "wake-detect-gpios", 0); - if (apb->wake_detect_gpio < 0) { - dev_err(dev, "failed to get wake detect gpio\n"); - return apb->wake_detect_gpio; - } - apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0); if (apb->resetn_gpio < 0) { dev_err(dev, "failed to get reset gpio\n"); @@ -275,19 +217,15 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, static void apb_ctrl_cleanup(struct arche_apb_ctrl_drvdata *apb) { - unsigned long flags; - if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0) regulator_disable(apb->vcore); if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0) regulator_disable(apb->vio); - spin_lock_irqsave(&apb->lock, flags); /* As part of exit, put APB back in reset state */ gpio_set_value(apb->resetn_gpio, 0); apb->state = APB_STATE_OFF; - spin_unlock_irqrestore(&apb->lock, flags); /* TODO: May have to send an event to SVC about this exit */ } @@ -315,27 +253,8 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) return ret; } - spin_lock_init(&apb->lock); - apb->state = APB_STATE_OFF; - /* - * Assert AP module detect signal by pulling wake_detect low - */ - assert_gpio(apb->wake_detect_gpio); - /* - * In order to receive an interrupt, the GPIO must be set to input mode - */ - gpio_direction_input(apb->wake_detect_gpio); - - ret = devm_request_irq(dev, gpio_to_irq(apb->wake_detect_gpio), - apb_ctrl_wake_detect_irq, IRQF_TRIGGER_FALLING, - "wake detect", apb); - if (ret) { - dev_err(dev, "failed to request wake detect IRQ\n"); - apb_ctrl_cleanup(apb); - return ret; - } platform_set_drvdata(pdev, apb); -- cgit v1.2.3-59-g8ed1b From f1e9cbd5a097aa6ec62f261bdf44879bbaefb72a Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Mon, 11 Jan 2016 17:41:23 +0530 Subject: greybus: arche-apb-ctrl: deassert reset at the end of probe Now the complete handshaking between AP<=>SVC would be offloaded to parent driver (arche-platform) who is responsible for SVC control, so the apb-ctrl driver can just simply bring APB's out of reset during probe itself. Along with deasserting reset, this patch renames the local fn to exclusively use it for reset purpose. Note that, driver is exporting reset gpio to user, so user can still flash FW from prompt. Signed-off-by: Vaibhav Hiremath Tested-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 5b9a3ed212af..65b1e9a700e7 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -51,13 +51,13 @@ struct arche_apb_ctrl_drvdata { /* * Note that these low level api's are active high */ -static inline void assert_gpio(unsigned int gpio) +static inline void deassert_reset(unsigned int gpio) { gpio_set_value(gpio, 1); msleep(500); } -static inline void deassert_gpio(unsigned int gpio) +static inline void assert_reset(unsigned int gpio) { gpio_set_value(gpio, 0); } @@ -224,7 +224,7 @@ static void apb_ctrl_cleanup(struct arche_apb_ctrl_drvdata *apb) regulator_disable(apb->vio); /* As part of exit, put APB back in reset state */ - gpio_set_value(apb->resetn_gpio, 0); + assert_reset(apb->resetn_gpio); apb->state = APB_STATE_OFF; /* TODO: May have to send an event to SVC about this exit */ @@ -253,8 +253,9 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) return ret; } - apb->state = APB_STATE_OFF; - + /* deassert reset to APB : Active-low signal */ + deassert_reset(apb->resetn_gpio); + apb->state = APB_STATE_ACTIVE; platform_set_drvdata(pdev, apb); -- cgit v1.2.3-59-g8ed1b From a463fc1533c9ea2edc99306b31c74f472c62d690 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Mon, 11 Jan 2016 17:41:24 +0530 Subject: greybus: arche-platform: Add wake/detect support along with handshaking with AP Add wake_detect support to arche-platform driver which is responsible for SVC control. This patch also adds code for handshaking between AP <=> SVC. The sequence is, 1. AP boots To keep compatibility between DB3 and EVT platform, SVC will be help in reset and AP driver would release it at appropriate time. wake/detect pin (WD8A) = Low reset (SVC/APB1/APB2) = Asserted (as per polarity) 2. AP Driver gets inserted 2.1. AP will deassert reset to SVC (following power on sequence) 2.2. SVC allows 360 milliseconds to elapse after switch boots to work around bug described in ENG-330. 2.3. AP asserts wake/detect pin (WD8A = HIGH) 3. SVC detects assertion of wake/detect pin, and sends "wake out" signal to AP 4. AP receives "wake out" signal, takes AP Bridges through their power on reset sequence as defined in the bridge ASIC reference manuals 5. AP takes USB3613 through its power on reset sequence 6. AP should enumerates AP Bridges Note: ISR has been deliberately removed (not merged) as we are still not sure how it will be used, in runtime usage context. Driver as such doesn't do anything for runtime assert-n-deassert of reset to SVC/APB's, it just simply offloads it to user by exporting required gpio's. The exported gpio's are required for FW flashing from user space. When it comes to usersace manipulated control sequence, user has to manage. Signed-off-by: Vaibhav Hiremath Tested-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 75 +++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 94e6f5dc6315..d12fa0e16a4b 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -23,6 +24,7 @@ struct arche_platform_drvdata { int svc_reset_gpio; bool is_reset_act_hi; int svc_sysboot_gpio; + int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ unsigned int svc_refclk_req; struct clk *svc_ref_clk; @@ -31,6 +33,9 @@ struct arche_platform_drvdata { struct pinctrl_state *pin_default; int num_apbs; + + struct delayed_work delayed_work; + struct device *dev; }; static inline void svc_reset_onoff(unsigned int gpio, bool onoff) @@ -38,6 +43,52 @@ static inline void svc_reset_onoff(unsigned int gpio, bool onoff) gpio_set_value(gpio, onoff); } +/** + * svc_delayed_work - Time to give SVC to boot. + */ +static void svc_delayed_work(struct work_struct *work) +{ + struct arche_platform_drvdata *arche_pdata = + container_of(work, struct arche_platform_drvdata, delayed_work.work); + struct device *dev = arche_pdata->dev; + struct device_node *np = dev->of_node; + int timeout = 10; + int ret; + + /* + * 1. SVC and AP boot independently, with AP<-->SVC wake/detect pin + * deasserted (LOW in this case) + * 2.1. SVC allows 360 milliseconds to elapse after switch boots to work + * around bug described in ENG-330. + * 2.2. AP asserts wake/detect pin (HIGH) (this can proceed in parallel with 2.1) + * 3. SVC detects assertion of wake/detect pin, and sends "wake out" signal to AP + * 4. AP receives "wake out" signal, takes AP Bridges through their power + * on reset sequence as defined in the bridge ASIC reference manuals + * 5. AP takes USB3613 through its power on reset sequence + * 6. AP enumerates AP Bridges + */ + gpio_set_value(arche_pdata->wake_detect_gpio, 1); + gpio_direction_input(arche_pdata->wake_detect_gpio); + do { + /* Read the wake_detect GPIO, for WAKE_OUT event from SVC */ + if (gpio_get_value(arche_pdata->wake_detect_gpio) == 0) + break; + + msleep(500); + } while(timeout--); + + if (timeout >= 0) { + ret = of_platform_populate(np, NULL, NULL, dev); + if (!ret) + /* Should we set wake_detect gpio to output again? */ + return; + } + + /* FIXME: We may want to limit retries here */ + gpio_direction_output(arche_pdata->wake_detect_gpio, 0); + schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); +} + /* Export gpio's to user space */ static void export_gpios(struct arche_platform_drvdata *arche_pdata) { @@ -146,18 +197,32 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->num_apbs = of_get_child_count(np); dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); - /* probe all childs here */ - ret = of_platform_populate(np, NULL, NULL, dev); + arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); + if (arche_pdata->wake_detect_gpio < 0) { + dev_err(dev, "failed to get wake detect gpio\n"); + ret = arche_pdata->wake_detect_gpio; + goto exit; + } + + ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); if (ret) { - arche_platform_cleanup(arche_pdata); - dev_err(dev, "no child node found\n"); - return ret; + dev_err(dev, "Failed requesting wake_detect gpio %d\n", + arche_pdata->wake_detect_gpio); + goto exit; } + arche_pdata->dev = &pdev->dev; + INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); + schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); + export_gpios(arche_pdata); dev_info(dev, "Device registered successfully\n"); return 0; + +exit: + arche_platform_cleanup(arche_pdata); + return ret; } static int arche_remove_child(struct device *dev, void *unused) -- cgit v1.2.3-59-g8ed1b From e0d91ff127cf68e6369cfccd51af18b07bf462e3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Jan 2016 12:28:29 +0100 Subject: greybus: power_supply: fix lock imbalance in init error path Make sure to release the supplies_lock before returning on errors in gb_power_supplies_setup(). Signed-off-by: Johan Hovold Reviewed-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 4a7381650bb7..1f3884bccf97 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -644,8 +644,10 @@ static int gb_power_supplies_setup(struct gb_power_supplies *supplies) sizeof(struct gb_power_supply), GFP_KERNEL); - if (!supplies->supply) - return -ENOMEM; + if (!supplies->supply) { + ret = -ENOMEM; + goto out; + } for (i = 0; i < supplies->supplies_count; i++) { ret = gb_power_supply_config(supplies, i); -- cgit v1.2.3-59-g8ed1b From 7ccac20d59ca45a2efea93a24a99d57022ff8911 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Fri, 8 Jan 2016 13:53:42 +0000 Subject: greybus: power_supply: do not release failed supply alloc If allocation of memory for each supply fail, we should get out of release any individual supply. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 1f3884bccf97..1ce57f6333da 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -569,6 +569,9 @@ static void _gb_power_supplies_release(struct gb_power_supplies *supplies) { int i; + if (!supplies->supply) + return; + mutex_lock(&supplies->supplies_lock); for (i = 0; i < supplies->supplies_count; i++) _gb_power_supply_release(&supplies->supply[i]); -- cgit v1.2.3-59-g8ed1b From ff85f723ca362986dc984217433cbdd133fc03d6 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Fri, 8 Jan 2016 13:53:43 +0000 Subject: greybus: power_supply: fix unregister on error path If setup fail the release for each supply needs to know the status at unregister time. So, add the field to the structure, update it at setup time and use it at release. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 1ce57f6333da..764d2f640164 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -25,6 +25,7 @@ struct gb_power_supply_prop { struct gb_power_supply { u8 id; + bool registered; #ifdef DRIVER_OWNS_PSY_STRUCT struct power_supply psy; #define to_gb_power_supply(x) container_of(x, struct gb_power_supply, psy) @@ -557,9 +558,11 @@ static void _gb_power_supply_release(struct gb_power_supply *gbpsy) cancel_delayed_work_sync(&gbpsy->work); #ifdef DRIVER_OWNS_PSY_STRUCT - power_supply_unregister(&gbpsy->psy); + if (gbpsy->registered) + power_supply_unregister(&gbpsy->psy); #else - power_supply_unregister(gbpsy->psy); + if (gbpsy->registered) + power_supply_unregister(gbpsy->psy); #endif _gb_power_supply_free(gbpsy); @@ -628,6 +631,9 @@ static int gb_power_supply_config(struct gb_power_supplies *supplies, int id) schedule_delayed_work(&gbpsy->work, 0); out: + /* if everything went fine just mark it for release code to know */ + if (ret == 0) + gbpsy->registered = true; return ret; } -- cgit v1.2.3-59-g8ed1b From 23f25ba6edd4121eb70baf3df12f5592bee8a1e9 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Fri, 8 Jan 2016 13:53:44 +0000 Subject: greybus: power_supply: free supplies at release After freeing each individual power_supply, free the top controller, if not it will leak memory at each module insert/remove. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 764d2f640164..7d1848b99d9b 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -579,6 +579,7 @@ static void _gb_power_supplies_release(struct gb_power_supplies *supplies) for (i = 0; i < supplies->supplies_count; i++) _gb_power_supply_release(&supplies->supply[i]); mutex_unlock(&supplies->supplies_lock); + kfree(supplies); } static int gb_power_supplies_get_count(struct gb_power_supplies *supplies) -- cgit v1.2.3-59-g8ed1b From d9eafd58e3e365d71cd7d477a1c5675db1931027 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Fri, 8 Jan 2016 13:53:45 +0000 Subject: greybus: power_supply: fix error path at supplies setup If something goes wrong at setup time for the supplies, we need to release all the resources allocated already. Call the supplies release function which will handle the correct teardown of the supplies. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 7d1848b99d9b..28548a243240 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -731,6 +731,7 @@ out_unlock: static int gb_power_supply_connection_init(struct gb_connection *connection) { struct gb_power_supplies *supplies; + int ret; supplies = kzalloc(sizeof(*supplies), GFP_KERNEL); if (!supplies) @@ -741,7 +742,11 @@ static int gb_power_supply_connection_init(struct gb_connection *connection) mutex_init(&supplies->supplies_lock); - return gb_power_supplies_setup(supplies); + ret = gb_power_supplies_setup(supplies); + if (ret < 0) + _gb_power_supplies_release(supplies); + + return ret; } static void gb_power_supply_connection_exit(struct gb_connection *connection) -- cgit v1.2.3-59-g8ed1b From f921fb139bd5ee008ec993c76b8a89a535fd11e9 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Fri, 8 Jan 2016 13:53:46 +0000 Subject: greybus: power_supply: fix leak getting string properties When fetching string properties, memory was being allocated and leaked when it was not necessary to do so. Signed-off-by: Rui Miguel Silva Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 28548a243240..3c9bb12351e4 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -347,15 +347,13 @@ static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy, { switch (psp) { case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = kstrndup(gbpsy->model_name, PROP_MAX, GFP_KERNEL); + val->strval = gbpsy->model_name; break; case POWER_SUPPLY_PROP_MANUFACTURER: - val->strval = kstrndup(gbpsy->manufacturer, PROP_MAX, - GFP_KERNEL); + val->strval = gbpsy->manufacturer; break; case POWER_SUPPLY_PROP_SERIAL_NUMBER: - val->strval = kstrndup(gbpsy->serial_number, PROP_MAX, - GFP_KERNEL); + val->strval = gbpsy->serial_number; break; default: break; -- cgit v1.2.3-59-g8ed1b From accad1ba7d62543ab3bcf08813726ea87d237bb6 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Fri, 8 Jan 2016 13:53:47 +0000 Subject: greybus: power_supply: fix use after free of power supply Individual power supply were being freed and checked using the wrong pointers and at the wrong place, which would make several issues, like used after free and so on. Fix it by freeing all allocated memory after release individual power supply. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 3c9bb12351e4..d985e13b5a0d 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -544,13 +544,10 @@ static void _gb_power_supply_free(struct gb_power_supply *gbpsy) kfree(gbpsy->manufacturer); kfree(gbpsy->props_raw); kfree(gbpsy->props); - kfree(gbpsy); } static void _gb_power_supply_release(struct gb_power_supply *gbpsy) { - if (!gbpsy) - return; gbpsy->update_interval = 0; @@ -576,6 +573,7 @@ static void _gb_power_supplies_release(struct gb_power_supplies *supplies) mutex_lock(&supplies->supplies_lock); for (i = 0; i < supplies->supplies_count; i++) _gb_power_supply_release(&supplies->supply[i]); + kfree(supplies->supply); mutex_unlock(&supplies->supplies_lock); kfree(supplies); } -- cgit v1.2.3-59-g8ed1b From ed4596e9b19104ccc1e87bde8fcadee3a473c101 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Dec 2015 18:21:51 -0800 Subject: greybus: host: provide "generic" apbridge output calls Provide a new function, gb_hd_output() to send data to the apbridge. This is useful for the camera and audio drivers that need to do this type of messaging. The camera driver is converted to use this new function, the audio driver can use it when it gets merged later. Signed-off-by: Greg Kroah-Hartman Tested-by: Mark Greer Reviewed-by: Johan Hovold --- drivers/staging/greybus/camera.c | 31 +++++++++---- drivers/staging/greybus/es2.c | 99 ++++++++++++++++++++++++++++------------ drivers/staging/greybus/es2.h | 27 ----------- drivers/staging/greybus/hd.c | 9 ++++ drivers/staging/greybus/hd.h | 4 ++ 5 files changed, 104 insertions(+), 66 deletions(-) delete mode 100644 drivers/staging/greybus/es2.h diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 06ea5293d389..ac9ade367e0a 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -16,7 +16,6 @@ #include #include -#include "es2.h" #include "greybus.h" #include "greybus_protocols.h" @@ -74,6 +73,17 @@ struct gb_camera_stream_config { * Camera Protocol Operations */ +/* vendor request to control the CSI transmitter */ +#define REQUEST_CSI_TX_CONTROL 0x08 + +struct ap_csi_config_request { + __u8 csi_id; + __u8 clock_mode; + __u8 num_lanes; + __u8 padding; + __le32 bus_freq; +} __packed; + static int gb_camera_configure_streams(struct gb_camera *gcam, unsigned int nstreams, unsigned int flags, @@ -81,7 +91,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, { struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; - struct es2_ap_csi_config csi_cfg; + struct ap_csi_config_request csi_cfg; unsigned int i; size_t req_size; size_t resp_size; @@ -150,21 +160,22 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, } } + memset(&csi_cfg, 0, sizeof(csi_cfg)); + /* Configure the CSI transmitter. Hardcode the parameters for now. */ if (nstreams && !(resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)) { csi_cfg.csi_id = 1; csi_cfg.clock_mode = 0; csi_cfg.num_lanes = 4; - csi_cfg.bus_freq = 960000000; - - ret = es2_ap_csi_setup(gcam->connection->hd, true, &csi_cfg); + csi_cfg.bus_freq = cpu_to_le32(960000000); + ret = gb_hd_output(gcam->connection->hd, &csi_cfg, + sizeof(csi_cfg), REQUEST_CSI_TX_CONTROL, + false); } else if (nstreams == 0) { csi_cfg.csi_id = 1; - csi_cfg.clock_mode = 0; - csi_cfg.num_lanes = 0; - csi_cfg.bus_freq = 0; - - ret = es2_ap_csi_setup(gcam->connection->hd, false, &csi_cfg); + ret = gb_hd_output(gcam->connection->hd, &csi_cfg, + sizeof(csi_cfg), REQUEST_CSI_TX_CONTROL, + false); } if (ret < 0) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index af5d7496c6be..1b5487fec903 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -13,7 +13,6 @@ #include #include -#include "es2.h" #include "greybus.h" #include "kernel_ver.h" #include "connection.h" @@ -134,14 +133,6 @@ struct cport_to_ep { __u8 endpoint_out; }; -struct es2_ap_csi_config_request { - __u8 csi_id; - __u8 clock_mode; - __u8 num_lanes; - __u8 padding; - __le32 bus_freq; -} __packed; - static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) { return (struct es2_ap_dev *)&hd->hd_priv; @@ -220,38 +211,87 @@ static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id) } #endif -int es2_ap_csi_setup(struct gb_host_device *hd, bool start, - struct es2_ap_csi_config *cfg) +static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd) { - struct es2_ap_csi_config_request *cfg_req; - struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; + u8 *data; int retval; - cfg_req = kzalloc(sizeof(*cfg_req), GFP_KERNEL); - if (!cfg_req) + data = kmalloc(size, GFP_KERNEL); + if (!data) return -ENOMEM; - - cfg_req->csi_id = cfg->csi_id; - - if (start) { - cfg_req->clock_mode = cfg->clock_mode; - cfg_req->num_lanes = cfg->num_lanes; - cfg_req->bus_freq = cpu_to_le32(cfg->bus_freq); - } + memcpy(data, req, size); retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - REQUEST_CSI_TX_CONTROL, + cmd, USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, 0, 0, cfg_req, - sizeof(*cfg_req), ES2_TIMEOUT); + USB_RECIP_INTERFACE, + 0, 0, data, size, ES2_TIMEOUT); if (retval < 0) - dev_err(&udev->dev, "failed to setup csi: %d\n", retval); + dev_err(&udev->dev, "%s: return error %d\n", __func__, retval); + else + retval = 0; - kfree(cfg_req); + kfree(data); return retval; } -EXPORT_SYMBOL_GPL(es2_ap_csi_setup); + +static void ap_urb_complete(struct urb *urb) +{ + struct usb_ctrlrequest *dr = urb->context; + + kfree(dr); + usb_free_urb(urb); +} + +static int output_async(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd) +{ + struct usb_device *udev = es2->usb_dev; + struct urb *urb; + struct usb_ctrlrequest *dr; + u8 *buf; + int retval; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + dr = kmalloc(sizeof(*dr) + size, GFP_ATOMIC); + if (!dr) { + usb_free_urb(urb); + return -ENOMEM; + } + + buf = (u8 *)dr + sizeof(*dr); + memcpy(buf, req, size); + + dr->bRequest = cmd; + dr->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE; + dr->wValue = 0; + dr->wIndex = 0; + dr->wLength = cpu_to_le16(size); + + usb_fill_control_urb(urb, udev, usb_sndctrlpipe(udev, 0), + (unsigned char *)dr, buf, size, + ap_urb_complete, dr); + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) { + usb_free_urb(urb); + kfree(dr); + } + return retval; +} + +static int output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, + bool async) +{ + struct es2_ap_dev *es2 = hd_to_es2(hd); + + if (async) + return output_async(es2, req, size, cmd); + + return output_sync(es2, req, size, cmd); +} static int es2_cport_in_enable(struct es2_ap_dev *es2, struct es2_cport_in *cport_in) @@ -560,6 +600,7 @@ static struct gb_hd_driver es2_driver = { .cport_enable = cport_enable, .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, + .output = output, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/es2.h b/drivers/staging/greybus/es2.h deleted file mode 100644 index ea2977049481..000000000000 --- a/drivers/staging/greybus/es2.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Greybus "AP" USB driver for "ES2" controller chips - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __ES2_H -#define __ES2_H - -#include - -struct gb_host_device; - -struct es2_ap_csi_config { - u8 csi_id; - u8 clock_mode; - u8 num_lanes; - u32 bus_freq; -}; - -int es2_ap_csi_setup(struct gb_host_device *hd, bool start, - struct es2_ap_csi_config *cfg); - -#endif /* __ES2_H */ diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index bff6861b8af7..b11a63644235 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -17,6 +17,15 @@ static struct ida gb_hd_bus_id_map; +int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, + bool async) +{ + if (!hd || !hd->driver || !hd->driver->output) + return -EINVAL; + return hd->driver->output(hd, req, size, cmd, async); +} +EXPORT_SYMBOL_GPL(gb_hd_output); + static void gb_hd_release(struct device *dev) { struct gb_host_device *hd = to_gb_host_device(dev); diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index d828129475cd..e11359b145e6 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -23,6 +23,8 @@ struct gb_hd_driver { void (*message_cancel)(struct gb_message *message); int (*latency_tag_enable)(struct gb_host_device *hd, u16 cport_id); int (*latency_tag_disable)(struct gb_host_device *hd, u16 cport_id); + int (*output)(struct gb_host_device *hd, void *req, u16 size, u8 cmd, + bool async); }; struct gb_host_device { @@ -53,6 +55,8 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, int gb_hd_add(struct gb_host_device *hd); void gb_hd_del(struct gb_host_device *hd); void gb_hd_put(struct gb_host_device *hd); +int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, + bool in_irq); int gb_hd_init(void); void gb_hd_exit(void); -- cgit v1.2.3-59-g8ed1b From e5273381041fc75215fb50661db39d2f8ed5544e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 31 Dec 2015 11:14:33 -0800 Subject: greybus: APBridge: move APBridge request protocol to a common .h file This moves all of the APBridge request protocol commands that are currently used to a common .h file for everyone to be able to use them in the future, where needed. Signed-off-by: Greg Kroah-Hartman Tested-by: Mark Greer Reviewed-by: Johan Hovold --- drivers/staging/greybus/camera.c | 11 ++++------ drivers/staging/greybus/es2.c | 31 ++++++----------------------- drivers/staging/greybus/greybus_protocols.h | 22 ++++++++++++++++++++ 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index ac9ade367e0a..783a435bbae6 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -73,9 +73,6 @@ struct gb_camera_stream_config { * Camera Protocol Operations */ -/* vendor request to control the CSI transmitter */ -#define REQUEST_CSI_TX_CONTROL 0x08 - struct ap_csi_config_request { __u8 csi_id; __u8 clock_mode; @@ -169,13 +166,13 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, csi_cfg.num_lanes = 4; csi_cfg.bus_freq = cpu_to_le32(960000000); ret = gb_hd_output(gcam->connection->hd, &csi_cfg, - sizeof(csi_cfg), REQUEST_CSI_TX_CONTROL, - false); + sizeof(csi_cfg), + GB_APB_REQUEST_CSI_TX_CONTROL, false); } else if (nstreams == 0) { csi_cfg.csi_id = 1; ret = gb_hd_output(gcam->connection->hd, &csi_cfg, - sizeof(csi_cfg), REQUEST_CSI_TX_CONTROL, - false); + sizeof(csi_cfg), + GB_APB_REQUEST_CSI_TX_CONTROL, false); } if (ret < 0) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 1b5487fec903..e39bd58e3a5c 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -44,25 +44,6 @@ MODULE_DEVICE_TABLE(usb, id_table); */ #define NUM_CPORT_OUT_URB (8 * NUM_BULKS) -/* vendor request APB1 log */ -#define REQUEST_LOG 0x02 - -/* vendor request to map a cport to bulk in and bulk out endpoints */ -#define REQUEST_EP_MAPPING 0x03 - -/* vendor request to get the number of cports available */ -#define REQUEST_CPORT_COUNT 0x04 - -/* vendor request to reset a cport state */ -#define REQUEST_RESET_CPORT 0x05 - -/* vendor request to time the latency of messages on a given cport */ -#define REQUEST_LATENCY_TAG_EN 0x06 -#define REQUEST_LATENCY_TAG_DIS 0x07 - -/* vendor request to control the CSI transmitter */ -#define REQUEST_CSI_TX_CONTROL 0x08 - /* * @endpoint: bulk in endpoint for CPort data * @urb: array of urbs for the CPort in messages @@ -191,7 +172,7 @@ static int map_cport_to_ep(struct es2_ap_dev *es2, retval = usb_control_msg(es2->usb_dev, usb_sndctrlpipe(es2->usb_dev, 0), - REQUEST_EP_MAPPING, + GB_APB_REQUEST_EP_MAPPING, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, (char *)cport_to_ep, @@ -521,7 +502,7 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) int retval; retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - REQUEST_RESET_CPORT, + GB_APB_REQUEST_RESET_CPORT, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, 0, ES2_TIMEOUT); @@ -559,7 +540,7 @@ static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) } retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - REQUEST_LATENCY_TAG_EN, + GB_APB_REQUEST_LATENCY_TAG_EN, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, 0, ES2_TIMEOUT); @@ -582,7 +563,7 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) } retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - REQUEST_LATENCY_TAG_DIS, + GB_APB_REQUEST_LATENCY_TAG_DIS, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, 0, ES2_TIMEOUT); @@ -756,7 +737,7 @@ static void apb_log_get(struct es2_ap_dev *es2, char *buf) do { retval = usb_control_msg(es2->usb_dev, usb_rcvctrlpipe(es2->usb_dev, 0), - REQUEST_LOG, + GB_APB_REQUEST_LOG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, 0x00, buf, @@ -885,7 +866,7 @@ static int apb_get_cport_count(struct usb_device *udev) return -ENOMEM; retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - REQUEST_CPORT_COUNT, + GB_APB_REQUEST_CPORT_COUNT, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, cport_count, sizeof(*cport_count), ES2_TIMEOUT); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 67f260beba63..dfa3d29b78b3 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -154,6 +154,28 @@ struct gb_control_interface_version_response { } __packed; +/* APBridge protocol */ + +/* request APB1 log */ +#define GB_APB_REQUEST_LOG 0x02 + +/* request to map a cport to bulk in and bulk out endpoints */ +#define GB_APB_REQUEST_EP_MAPPING 0x03 + +/* request to get the number of cports available */ +#define GB_APB_REQUEST_CPORT_COUNT 0x04 + +/* request to reset a cport state */ +#define GB_APB_REQUEST_RESET_CPORT 0x05 + +/* request to time the latency of messages on a given cport */ +#define GB_APB_REQUEST_LATENCY_TAG_EN 0x06 +#define GB_APB_REQUEST_LATENCY_TAG_DIS 0x07 + +/* request to control the CSI transmitter */ +#define GB_APB_REQUEST_CSI_TX_CONTROL 0x08 + + /* Firmware Protocol */ /* Version of the Greybus firmware protocol we support */ -- cgit v1.2.3-59-g8ed1b From adb57cff54dd4418f4d5b17f03a976dfdc670226 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 12 Jan 2016 14:38:21 +0000 Subject: greybus: power_supply: fix check for configured supply The correct check for if the power supply is ready to receive event is wrong and it should check for the registered flag. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index d985e13b5a0d..2dc193a081b0 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -697,7 +697,8 @@ static int gb_power_supply_event_recv(u8 type, struct gb_operation *op) payload = request->payload; psy_id = payload->psy_id; mutex_lock(&supplies->supplies_lock); - if (psy_id >= supplies->supplies_count || !&supplies->supply[psy_id]) { + if (psy_id >= supplies->supplies_count || + !supplies->supply[psy_id].registered) { dev_err(&connection->bundle->dev, "Event received for unconfigured power_supply id: %d\n", psy_id); -- cgit v1.2.3-59-g8ed1b From c5d55fb3596db4064491e7e5db6df8bf8f046078 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Mon, 11 Jan 2016 13:46:31 +0000 Subject: greybus: svc: add interface eject operation Add a new svc operation which will be used to send a request to eject a given interface. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman Reviewed-by: Jeffrey Carlyle --- drivers/staging/greybus/greybus_protocols.h | 7 +++++++ drivers/staging/greybus/svc.c | 17 +++++++++++++++++ drivers/staging/greybus/svc.h | 1 + 3 files changed, 25 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index dfa3d29b78b3..2ffe07cb2a01 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -760,6 +760,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_ROUTE_CREATE 0x0b #define GB_SVC_TYPE_ROUTE_DESTROY 0x0c #define GB_SVC_TYPE_INTF_SET_PWRM 0x10 +#define GB_SVC_TYPE_INTF_EJECT 0x11 /* * SVC version request/response has the same payload as @@ -801,6 +802,12 @@ struct gb_svc_intf_reset_request { } __packed; /* interface reset response has no payload */ +#define GB_SVC_EJECT_TIME 9000 +struct gb_svc_intf_eject_request { + __u8 intf_id; +} __packed; +/* interface eject response has no payload */ + struct gb_svc_conn_create_request { __u8 intf1_id; __le16 cport1_id; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index c4c3bb54c9f7..ef10b67c7bf5 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -69,6 +69,23 @@ int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id) } EXPORT_SYMBOL_GPL(gb_svc_intf_reset); +int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) +{ + struct gb_svc_intf_eject_request request; + + request.intf_id = intf_id; + + /* + * The pulse width for module release in svc is long so we need to + * increase the timeout so the operation will not return to soon. + */ + return gb_operation_sync_timeout(svc->connection, + GB_SVC_TYPE_INTF_EJECT, &request, + sizeof(request), NULL, 0, + GB_SVC_EJECT_TIME); +} +EXPORT_SYMBOL_GPL(gb_svc_intf_eject); + int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value) { diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 0ebbed9c791f..8567615068a7 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -40,6 +40,7 @@ int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id, bool boot_over_unipro); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); +int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id); int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value); int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, -- cgit v1.2.3-59-g8ed1b From 2c92bd5235ac72aa659150ebac309dd4f1c01079 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Mon, 11 Jan 2016 13:46:33 +0000 Subject: greybus: svc: add intf_eject attribute Add a new write-only svc attribute to send an eject request to the svc giving the interface number. So, doing: echo 3 > /sys/bus/greybus/devices/1-svc/intf_eject will force eject the module on interface 3 (module slot 1 on EVT1). This can take some seconds as the pulse width for module release is large. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman Reviewed-by: Jeffrey Carlyle --- .../greybus/Documentation/sysfs-bus-greybus | 8 ++++++ drivers/staging/greybus/svc.c | 29 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 9ce36ddfb4da..1550b7f8d776 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -142,6 +142,14 @@ Description: defined by the Endo layout scheme, documented in the ARA Module Developer Kit. +What: /sys/bus/greybus/device/N-svc/intf_eject +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + Write the number of the interface that you wish to + forcibly eject from the system. + What: /sys/bus/greybus/device/N-svc/unique_id Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ef10b67c7bf5..be62d308d1c9 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -40,9 +40,38 @@ static ssize_t ap_intf_id_show(struct device *dev, } static DEVICE_ATTR_RO(ap_intf_id); + +// FIXME +// This is a hack, we need to do this "right" and clean the interface up +// properly, not just forcibly yank the thing out of the system and hope for the +// best. But for now, people want their modules to come out without having to +// throw the thing to the ground or get out a screwdriver. +static ssize_t intf_eject_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct gb_svc *svc = to_gb_svc(dev); + unsigned short intf_id; + int ret; + + ret = kstrtou16(buf, 10, &intf_id); + if (ret < 0) + return ret; + + dev_warn(dev, "Forcibly trying to eject interface %d\n", intf_id); + + ret = gb_svc_intf_eject(svc, intf_id); + if (ret < 0) + return ret; + + return len; +} +static DEVICE_ATTR_WO(intf_eject); + static struct attribute *svc_attrs[] = { &dev_attr_endo_id.attr, &dev_attr_ap_intf_id.attr, + &dev_attr_intf_eject.attr, NULL, }; ATTRIBUTE_GROUPS(svc); -- cgit v1.2.3-59-g8ed1b From 4d0bee11253ba98c4a9c9ebb6df19c3185fe1fd8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 8 Jan 2016 20:13:45 +0100 Subject: greybus: connection: fix connection-state handling Set connection state to ENABLE before sending the control connected message, and set state DISABLE after sending the control disconnected event. Remove the now unused ERROR connection state. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 21 +++++++++++++-------- drivers/staging/greybus/connection.h | 3 +-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index b356ee0575e2..1cb33147aa00 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -403,15 +403,14 @@ int gb_connection_init(struct gb_connection *connection) if (ret) goto err_hd_cport_disable; - ret = gb_connection_control_connected(connection); - if (ret) - goto err_svc_destroy; - - /* Need to enable the connection to initialize it */ spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_ENABLED; spin_unlock_irq(&connection->lock); + ret = gb_connection_control_connected(connection); + if (ret) + goto err_svc_destroy; + ret = gb_connection_protocol_get_version(connection); if (ret) goto err_disconnect; @@ -423,11 +422,11 @@ int gb_connection_init(struct gb_connection *connection) return 0; err_disconnect: + gb_connection_control_disconnected(connection); + spin_lock_irq(&connection->lock); - connection->state = GB_CONNECTION_STATE_ERROR; + connection->state = GB_CONNECTION_STATE_DISABLED; spin_unlock_irq(&connection->lock); - - gb_connection_control_disconnected(connection); err_svc_destroy: gb_connection_svc_connection_destroy(connection); err_hd_cport_disable: @@ -451,7 +450,13 @@ void gb_connection_exit(struct gb_connection *connection) gb_connection_cancel_operations(connection, -ESHUTDOWN); connection->protocol->connection_exit(connection); + gb_connection_control_disconnected(connection); + + spin_lock_irq(&connection->lock); + connection->state = GB_CONNECTION_STATE_DISABLED; + spin_unlock_irq(&connection->lock); + gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); gb_connection_unbind_protocol(connection); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index b795b44c1859..ef31c8d6dfe0 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -17,8 +17,7 @@ enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, GB_CONNECTION_STATE_DISABLED = 1, GB_CONNECTION_STATE_ENABLED = 2, - GB_CONNECTION_STATE_ERROR = 3, - GB_CONNECTION_STATE_DESTROYING = 4, + GB_CONNECTION_STATE_DESTROYING = 3, }; struct gb_connection { -- cgit v1.2.3-59-g8ed1b From 3ea6a81568f5902ff59ada61b073d8d4a6df5fbf Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 8 Jan 2016 20:13:46 +0100 Subject: greybus: connection: refactor connection enable and disable Refactor connection enable and disable. This is a step towards removing the legacy-protocol handling from core. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 69 +++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1cb33147aa00..6295d28a4be4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -387,17 +387,13 @@ static int gb_connection_protocol_get_version(struct gb_connection *connection) return 0; } -int gb_connection_init(struct gb_connection *connection) +int gb_connection_enable(struct gb_connection *connection) { int ret; - ret = gb_connection_bind_protocol(connection); - if (ret) - return ret; - ret = gb_connection_hd_cport_enable(connection); if (ret) - goto err_unbind_protocol; + return ret; ret = gb_connection_svc_connection_create(connection); if (ret) @@ -411,26 +407,58 @@ int gb_connection_init(struct gb_connection *connection) if (ret) goto err_svc_destroy; - ret = gb_connection_protocol_get_version(connection); - if (ret) - goto err_disconnect; + return 0; - ret = connection->protocol->connection_init(connection); - if (ret) - goto err_disconnect; +err_svc_destroy: + spin_lock_irq(&connection->lock); + connection->state = GB_CONNECTION_STATE_DISABLED; + spin_unlock_irq(&connection->lock); - return 0; + gb_connection_svc_connection_destroy(connection); +err_hd_cport_disable: + gb_connection_hd_cport_disable(connection); + + return ret; +} +EXPORT_SYMBOL_GPL(gb_connection_enable); -err_disconnect: +void gb_connection_disable(struct gb_connection *connection) +{ gb_connection_control_disconnected(connection); spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; spin_unlock_irq(&connection->lock); -err_svc_destroy: + gb_connection_svc_connection_destroy(connection); -err_hd_cport_disable: gb_connection_hd_cport_disable(connection); +} +EXPORT_SYMBOL_GPL(gb_connection_disable); + +int gb_connection_init(struct gb_connection *connection) +{ + int ret; + + ret = gb_connection_bind_protocol(connection); + if (ret) + return ret; + + ret = gb_connection_enable(connection); + if (ret) + goto err_unbind_protocol; + + ret = gb_connection_protocol_get_version(connection); + if (ret) + goto err_disable; + + ret = connection->protocol->connection_init(connection); + if (ret) + goto err_disable; + + return 0; + +err_disable: + gb_connection_disable(connection); err_unbind_protocol: gb_connection_unbind_protocol(connection); @@ -448,17 +476,10 @@ void gb_connection_exit(struct gb_connection *connection) spin_unlock_irq(&connection->lock); gb_connection_cancel_operations(connection, -ESHUTDOWN); - connection->protocol->connection_exit(connection); - gb_connection_control_disconnected(connection); + gb_connection_disable(connection); - spin_lock_irq(&connection->lock); - connection->state = GB_CONNECTION_STATE_DISABLED; - spin_unlock_irq(&connection->lock); - - gb_connection_svc_connection_destroy(connection); - gb_connection_hd_cport_disable(connection); gb_connection_unbind_protocol(connection); } -- cgit v1.2.3-59-g8ed1b From 14006ab2f53df868f3835ab0706b5ef94c90708f Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 13 Jan 2016 14:39:55 +0000 Subject: greybus: makefile: add requirement for CONFIG_INPUT To support key events coming from the svc (ara key) it is needed to add the input core as an required config option. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 011e87cb32cf..d45a641076b9 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -51,7 +51,7 @@ INSTALL_MOD_PATH ?= /.. PWD := $(shell pwd) # kernel config option that shall be enable -CONFIG_OPTIONS_ENABLE := POWER_SUPPLY PWM SYSFS SPI USB SND_SOC MMC LEDS_CLASS +CONFIG_OPTIONS_ENABLE := POWER_SUPPLY PWM SYSFS SPI USB SND_SOC MMC LEDS_CLASS INPUT # kernel config option that shall be disable CONFIG_OPTIONS_DISABLE := -- cgit v1.2.3-59-g8ed1b From de141314498b3e52cec7a0e5100e4dd278bde4c7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Jan 2016 19:24:54 -0800 Subject: greybus: uevent: add GREYBUS_ID to uevent This adds the GREYBUS_ID environment variable to all interface uevents to let userspace know the vendor/product id of the module interface that has been added or removed from the system. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar --- drivers/staging/greybus/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 6b31155d4a9c..0e2f99df6cfa 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -110,6 +110,10 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) if (intf) { if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) return -ENOMEM; + if (add_uevent_var(env, "GREYBUS_ID=%04x/%04x", + (u16)(intf->vendor_id & 0xffff), + (u16)(intf->product_id & 0xffff))) + return -ENOMEM; } if (bundle) { -- cgit v1.2.3-59-g8ed1b From b827e1137ca9b495f10f3215ed6dd6ff738f0c81 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 12 Jan 2016 14:35:48 +0000 Subject: greybus: kernel_ver: add handle for lights in 4.5 In v4.5 the LED API is changed related to the set brightness operations, set_sync is removed and the workqueue handling is now done by leds core, so no need for individual drivers to have one. This will be done by removing the SET_SYNC macro and add a new SET_BLOCKING. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index d9bf159329e5..1f8d6a1bb6da 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -267,6 +267,15 @@ static inline size_t sg_pcopy_from_buffer(struct scatterlist *sgl, #include #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +/* + * New change in LED api, the set_sync operation was renamed to set_blocking and + * the workqueue is now handle by core. So, only one set operation is need. + */ +#undef LED_HAVE_SET_SYNC +#define LED_HAVE_SET_BLOCKING +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) /* * From this version upper it was introduced the possibility to disable led -- cgit v1.2.3-59-g8ed1b From 9c06c6a2b9ec4cfa78a4cf5ab123bfca0285e083 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 12 Jan 2016 14:35:49 +0000 Subject: greybus: lights: remove sync operation and work queue In kernel v4.5 there is a change in LED api, which remove the need for individual work queue and rename the set_sync operation to set_blocking. This patch add the handling of this case and avoid compilation failure for this kernel versions. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 153c4f5479f0..3488bbd58198 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -30,7 +30,9 @@ struct gb_channel { struct attribute **attrs; struct attribute_group *attr_group; const struct attribute_group **attr_groups; +#ifndef LED_HAVE_SET_BLOCKING struct work_struct work_brightness_set; +#endif struct led_classdev *led; #ifdef LED_HAVE_FLASH struct led_classdev_flash fled; @@ -381,6 +383,7 @@ static int __gb_lights_brightness_set(struct gb_channel *channel) return ret; } +#ifndef LED_HAVE_SET_BLOCKING static void gb_brightness_set_work(struct work_struct *work) { struct gb_channel *channel = container_of(work, struct gb_channel, @@ -412,6 +415,17 @@ static void gb_brightness_set(struct led_classdev *cdev, cdev->brightness = value; schedule_work(&channel->work_brightness_set); } +#else /* LED_HAVE_SET_BLOCKING */ +static int gb_brightness_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct gb_channel *channel = get_channel_from_cdev(cdev); + + channel->led->brightness = value; + + return __gb_lights_brightness_set(channel); +} +#endif static enum led_brightness gb_brightness_get(struct led_classdev *cdev) @@ -443,12 +457,17 @@ static int gb_blink_set(struct led_classdev *cdev, unsigned long *delay_on, static void gb_lights_led_operations_set(struct gb_channel *channel, struct led_classdev *cdev) { - cdev->brightness_set = gb_brightness_set; cdev->brightness_get = gb_brightness_get; #ifdef LED_HAVE_SET_SYNC cdev->brightness_set_sync = gb_brightness_set_sync; #endif +#ifdef LED_HAVE_SET_BLOCKING + cdev->brightness_set_blocking = gb_brightness_set; +#endif +#ifndef LED_HAVE_SET_BLOCKING + cdev->brightness_set = gb_brightness_set; INIT_WORK(&channel->work_brightness_set, gb_brightness_set_work); +#endif if (channel->flags & GB_LIGHT_CHANNEL_BLINK) cdev->blink_set = gb_blink_set; @@ -986,8 +1005,10 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) static void gb_lights_channel_free(struct gb_channel *channel) { +#ifndef LED_HAVE_SET_BLOCKING if (&channel->work_brightness_set) flush_work(&channel->work_brightness_set); +#endif kfree(channel->attrs); kfree(channel->attr_group); kfree(channel->attr_groups); -- cgit v1.2.3-59-g8ed1b From fc0ddf5adbc2636365b1f8c3c21cd3adf0a8c252 Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos Date: Wed, 13 Jan 2016 21:52:37 +0200 Subject: greybus: camera: HACK: Export GB camera interface Gb camera need to communicate with HOST driver. as temporary solution there will be shared header between gb camera HOST camera. Both need to be in sync since gb drivers are compiled out of the kernel tree. Gb camera register camera operation functions when is created. Currently unregister is not supported. Signed-off-by: Gjorgji Rosikopulos Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gb-camera.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 drivers/staging/greybus/gb-camera.h diff --git a/drivers/staging/greybus/gb-camera.h b/drivers/staging/greybus/gb-camera.h new file mode 100644 index 000000000000..a1824740ef83 --- /dev/null +++ b/drivers/staging/greybus/gb-camera.h @@ -0,0 +1,37 @@ +/* + * Greybus Camera protocol driver. + * + * Copyright 2015 Google Inc. + * + * Released under the GPLv2 only. + */ +#ifndef __GB_CAMERA_H +#define __GB_CAMERA_H + +#include + +struct gb_camera_stream { + unsigned int width; + unsigned int height; + enum v4l2_mbus_pixelcode pixel_code; + unsigned int vc; + unsigned int dt[2]; + unsigned int max_size; +}; + +struct gb_camera_ops { + ssize_t (*capabilities)(void *priv, char *buf, size_t len); + int (*configure_streams)(void *priv, unsigned int nstreams, + struct gb_camera_stream *streams); + int (*capture)(void *priv, u32 request_id, + unsigned int streams, unsigned int num_frames, + size_t settings_size, const void *settings); + int (*flush)(void *priv, u32 *request_id); +}; + +#define gb_camera_call(f, p, op, args...) \ + (((f)->op) ? (f)->op(p, ##args) : -ENOIOCTLCMD) + +int gb_camera_register(struct gb_camera_ops *ops, void *priv); + +#endif /* __GB_CAMERA_H */ -- cgit v1.2.3-59-g8ed1b From 3a8dba4e57d11965fa8f649753dc1507dd8331eb Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos Date: Wed, 13 Jan 2016 21:52:38 +0200 Subject: greybus: camera: HACK: Register gb camera to the HOST camera This change implements gb camera interface and register gb camera to the HOST driver. This implementation need to be reworked after the demo. Tested with db3 with two camera modules. Signed-off-by: Gjorgji Rosikopulos Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 121 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 783a435bbae6..a69797b13c84 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -16,6 +16,7 @@ #include #include +#include "gb-camera.h" #include "greybus.h" #include "greybus_protocols.h" @@ -57,6 +58,31 @@ struct gb_camera_stream_config { unsigned int max_size; }; +struct gb_camera_fmt_map { + enum v4l2_mbus_pixelcode mbus_code; + unsigned int gb_format; +}; + +/* GB format to media code map */ +static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { + { + .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, + .gb_format = 0x01, + }, + { + .mbus_code = V4L2_MBUS_FMT_YUYV8_1_5X8, + .gb_format = 0x16, + }, + { + .mbus_code = V4L2_MBUS_FMT_YVYU8_1_5X8, + .gb_format = 0x17, + }, + { + .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, + .gb_format = 0x40, + } +}; + #define ES2_APB_CDSI0_CPORT 16 #define ES2_APB_CDSI1_CPORT 17 @@ -260,6 +286,97 @@ static int gb_camera_event_recv(u8 type, struct gb_operation *op) return 0; } +/* ----------------------------------------------------------------------------- + * Interface with HOST ara camera. + */ +static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { + if (mbus_to_gbus_format[i].mbus_code == mbus_code) + return mbus_to_gbus_format[i].gb_format; + } + return mbus_to_gbus_format[0].gb_format; +} + +static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { + if (mbus_to_gbus_format[i].gb_format == gb_fmt) + return mbus_to_gbus_format[i].mbus_code; + } + return mbus_to_gbus_format[0].mbus_code; +} + +static int gb_camera_op_configure_streams(void *priv, unsigned int nstreams, + struct gb_camera_stream *streams) +{ + struct gb_camera *gcam = priv; + struct gb_camera_stream_config *gb_streams; + unsigned int i; + int ret; + + if (nstreams > GB_CAMERA_MAX_STREAMS) + return -EINVAL; + + gb_streams = kzalloc(nstreams * sizeof(*gb_streams), GFP_KERNEL); + if (!gb_streams) + return -ENOMEM; + + for (i = 0; i < nstreams; i++) { + gb_streams[i].width = streams[i].width; + gb_streams[i].height = streams[i].height; + gb_streams[i].format = + gb_camera_mbus_to_gb(streams[i].pixel_code); + } + + ret = gb_camera_configure_streams(gcam, nstreams, 0, gb_streams); + if (ret < 0) + goto done; + + for (i = 0; i < nstreams; i++) { + streams[i].width = gb_streams[i].width; + streams[i].height = gb_streams[i].height; + streams[i].vc = gb_streams[i].vc; + streams[i].dt[0] = gb_streams[i].dt[0]; + streams[i].dt[1] = gb_streams[i].dt[1]; + streams[i].max_size = gb_streams[i].max_size; + streams[i].pixel_code = + gb_camera_gb_to_mbus(gb_streams[i].format); + } + +done: + kfree(gb_streams); + return ret; +} + +static int gb_camera_op_capture(void *priv, u32 request_id, + unsigned int streams, unsigned int num_frames, + size_t settings_size, const void *settings) +{ + return gb_camera_capture(priv, request_id, streams, num_frames, + settings_size, settings); +} + +static int gb_camera_op_flush(void *priv, u32 *request_id) +{ + return gb_camera_flush(priv, request_id); +} + +struct gb_camera_ops gb_cam_ops = { + .configure_streams = gb_camera_op_configure_streams, + .capture = gb_camera_op_capture, + .flush = gb_camera_op_flush, +}; + +static int gb_camera_register_intf_ops(struct gb_camera *gcam) +{ + return gb_camera_register(&gb_cam_ops, gcam); +} + /* ----------------------------------------------------------------------------- * DebugFS */ @@ -646,6 +763,10 @@ static int gb_camera_connection_init(struct gb_connection *connection) if (ret < 0) goto error; + ret = gb_camera_register_intf_ops(gcam); + if (ret < 0) + goto error; + return 0; error: -- cgit v1.2.3-59-g8ed1b From aa187d33129ba102756600aa27f6e7992a6c814a Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 14 Jan 2016 02:06:47 +0530 Subject: greybus: arche-apb-ctrl: Enable the clocks after regulator enable It makes more sense to enable the clock after power is enabled to the device, so move clock enable code after regulator_enable and setting up BOOT_RET pin. Signed-off-by: Vaibhav Hiremath Tested-by: Michael Scott Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 65b1e9a700e7..d073543963a5 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -82,19 +82,6 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, struct device *dev = &pdev->dev; int ret; - /* On DB3 clock was not mandatory */ - if (gpio_is_valid(apb->clk_en_gpio)) { - ret = devm_gpio_request(dev, apb->clk_en_gpio, "apb_clk_en"); - if (ret) { - dev_warn(dev, "Failed requesting APB clock en gpio %d\n", - apb->clk_en_gpio); - } else { - ret = gpio_direction_output(apb->clk_en_gpio, 1); - if (ret) - dev_warn(dev, "failed to set APB clock en gpio dir:%d\n", - ret); - } - } /* Hold APB in reset state */ ret = devm_gpio_request(dev, apb->resetn_gpio, "apb-reset"); if (ret) { @@ -128,6 +115,7 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, return ret; } } + if (!IS_ERR(apb->vio)) { ret = regulator_enable(apb->vio); if (ret) { @@ -144,7 +132,22 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, goto out_vio_disable; } gpio_set_value(apb->boot_ret_gpio, 0); - udelay(50); + + /* On DB3 clock was not mandatory */ + if (gpio_is_valid(apb->clk_en_gpio)) { + ret = devm_gpio_request(dev, apb->clk_en_gpio, "apb_clk_en"); + if (ret) { + dev_warn(dev, "Failed requesting APB clock en gpio %d\n", + apb->clk_en_gpio); + } else { + ret = gpio_direction_output(apb->clk_en_gpio, 1); + if (ret) + dev_warn(dev, "failed to set APB clock en gpio dir:%d\n", + ret); + } + } + + usleep_range(100, 200); return 0; -- cgit v1.2.3-59-g8ed1b From fd8f9e12e2a3bb945959cd294768b265fb97480b Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Wed, 13 Jan 2016 14:07:44 -0700 Subject: greybus: audio: Use underscore in file name For consistency with most other files in the Greybus repository, change 'audio-codec.c' to use an underscore instead of a hyphen in its name. Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/audio-codec.c | 315 ---------------------------------- drivers/staging/greybus/audio_codec.c | 315 ++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+), 316 deletions(-) delete mode 100644 drivers/staging/greybus/audio-codec.c create mode 100644 drivers/staging/greybus/audio_codec.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index d45a641076b9..fb0e02316b1c 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -29,7 +29,7 @@ gb-raw-y := raw.o gb-hid-y := hid.o gb-es2-y := es2.o gb-arche-y := arche-platform.o arche-apb-ctrl.o -gb-audio-codec-y := audio-codec.o +gb-audio-codec-y := audio_codec.o gb-camera-y := camera.o obj-m += greybus.o diff --git a/drivers/staging/greybus/audio-codec.c b/drivers/staging/greybus/audio-codec.c deleted file mode 100644 index 2bc23095ffd0..000000000000 --- a/drivers/staging/greybus/audio-codec.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Greybus audio driver - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ -#include - -#include "audio.h" - -static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - /* Ensure GB speaker is connected */ - - return 0; -} - -static int gbcodec_event_hp(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - /* Ensure GB module supports jack slot */ - - return 0; -} - -static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - /* Ensure GB module supports jack slot */ - - return 0; -} - -static const struct snd_kcontrol_new gbcodec_snd_controls[] = { - SOC_DOUBLE("Playback Mute", GBCODEC_MUTE_REG, 0, 1, 1, 1), - SOC_DOUBLE("Capture Mute", GBCODEC_MUTE_REG, 4, 5, 1, 1), - SOC_DOUBLE_R("Playback Volume", GBCODEC_PB_LVOL_REG, - GBCODEC_PB_RVOL_REG, 0, 127, 0), - SOC_DOUBLE_R("Capture Volume", GBCODEC_CAP_LVOL_REG, - GBCODEC_CAP_RVOL_REG, 0, 127, 0), -}; - -static const struct snd_kcontrol_new spk_amp_ctl = - SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 0, 1, 0); - -static const struct snd_kcontrol_new hp_amp_ctl = - SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 1, 1, 0); - -static const struct snd_kcontrol_new mic_adc_ctl = - SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 4, 1, 0); - -/* APB1-GBSPK source */ -static const char * const gbcodec_apb1_src[] = {"Stereo", "Left", "Right"}; - -static const SOC_ENUM_SINGLE_DECL( - gbcodec_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbcodec_apb1_src); - -static const struct snd_kcontrol_new gbcodec_apb1_rx_mux = - SOC_DAPM_ENUM("APB1 source", gbcodec_apb1_rx_enum); - -static const SOC_ENUM_SINGLE_DECL( - gbcodec_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbcodec_apb1_src); - -static const struct snd_kcontrol_new gbcodec_mic_mux = - SOC_DAPM_ENUM("MIC source", gbcodec_mic_enum); - -static const struct snd_soc_dapm_widget gbcodec_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Spk", gbcodec_event_spk), - SND_SOC_DAPM_SPK("HP", gbcodec_event_hp), - SND_SOC_DAPM_MIC("Int Mic", gbcodec_event_int_mic), - - SND_SOC_DAPM_OUTPUT("SPKOUT"), - SND_SOC_DAPM_OUTPUT("HPOUT"), - - SND_SOC_DAPM_INPUT("MIC"), - SND_SOC_DAPM_INPUT("HSMIC"), - - SND_SOC_DAPM_SWITCH("SPK Amp", SND_SOC_NOPM, 0, 0, &spk_amp_ctl), - SND_SOC_DAPM_SWITCH("HP Amp", SND_SOC_NOPM, 0, 0, &hp_amp_ctl), - SND_SOC_DAPM_SWITCH("MIC ADC", SND_SOC_NOPM, 0, 0, &mic_adc_ctl), - - SND_SOC_DAPM_PGA("SPK DAC", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("HP DAC", SND_SOC_NOPM, 0, 0, NULL, 0), - - SND_SOC_DAPM_MIXER("SPK Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("APB1_TX Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - - SND_SOC_DAPM_MUX("APB1_RX Mux", SND_SOC_NOPM, 0, 0, - &gbcodec_apb1_rx_mux), - SND_SOC_DAPM_MUX("MIC Mux", SND_SOC_NOPM, 0, 0, &gbcodec_mic_mux), - - SND_SOC_DAPM_AIF_IN("APB1RX", "APBridgeA1 Playback", 0, SND_SOC_NOPM, 0, - 0), - SND_SOC_DAPM_AIF_OUT("APB1TX", "APBridgeA1 Capture", 0, SND_SOC_NOPM, 0, - 0), -}; - -static const struct snd_soc_dapm_route gbcodec_dapm_routes[] = { - /* Playback path */ - {"Spk", NULL, "SPKOUT"}, - {"SPKOUT", NULL, "SPK Amp"}, - {"SPK Amp", "Switch", "SPK DAC"}, - {"SPK DAC", NULL, "SPK Mixer"}, - - {"HP", NULL, "HPOUT"}, - {"HPOUT", NULL, "HP Amp"}, - {"HP Amp", "Switch", "HP DAC"}, - {"HP DAC", NULL, "HP Mixer"}, - - {"SPK Mixer", NULL, "APB1_RX Mux"}, - {"HP Mixer", NULL, "APB1_RX Mux"}, - - {"APB1_RX Mux", "Left", "APB1RX"}, - {"APB1_RX Mux", "Right", "APB1RX"}, - {"APB1_RX Mux", "Stereo", "APB1RX"}, - - /* Capture path */ - {"MIC", NULL, "Int Mic"}, - {"MIC", NULL, "MIC Mux"}, - {"MIC Mux", "Left", "MIC ADC"}, - {"MIC Mux", "Right", "MIC ADC"}, - {"MIC Mux", "Stereo", "MIC ADC"}, - {"MIC ADC", "Switch", "APB1_TX Mixer"}, - {"APB1_TX Mixer", NULL, "APB1TX"} -}; - -static int gbcodec_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - return 0; -} - -static void gbcodec_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ -} - -static int gbcodec_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hwparams, - struct snd_soc_dai *dai) -{ - return 0; -} - -static int gbcodec_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - return 0; -} - -static int gbcodec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - return 0; -} - -static int gbcodec_digital_mute(struct snd_soc_dai *dai, int mute) -{ - return 0; -} - -static struct snd_soc_dai_ops gbcodec_dai_ops = { - .startup = gbcodec_startup, - .shutdown = gbcodec_shutdown, - .hw_params = gbcodec_hw_params, - .prepare = gbcodec_prepare, - .set_fmt = gbcodec_set_dai_fmt, - .digital_mute = gbcodec_digital_mute, -}; - -static struct snd_soc_dai_driver gbcodec_dai = { - .playback = { - .stream_name = "APBridgeA1 Playback", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "APBridgeA1 Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &gbcodec_dai_ops, -}; - -static int gbcodec_probe(struct snd_soc_codec *codec) -{ - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); - - gbcodec->codec = codec; - - return 0; -} - -static int gbcodec_remove(struct snd_soc_codec *codec) -{ - /* Empty function for now */ - return 0; -} - -static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - int ret = 0; - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); - u8 *gbcodec_reg = gbcodec->reg; - - if (reg == SND_SOC_NOPM) - return 0; - - if (reg >= GBCODEC_REG_COUNT) - return 0; - - gbcodec_reg[reg] = value; - dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value); - - return ret; -} - -static unsigned int gbcodec_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - unsigned int val = 0; - - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); - u8 *gbcodec_reg = gbcodec->reg; - - if (reg == SND_SOC_NOPM) - return 0; - - if (reg >= GBCODEC_REG_COUNT) - return 0; - - val = gbcodec_reg[reg]; - dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val); - - return val; -} - -static struct snd_soc_codec_driver soc_codec_dev_gbcodec = { - .probe = gbcodec_probe, - .remove = gbcodec_remove, - - .read = gbcodec_read, - .write = gbcodec_write, - - .reg_cache_size = GBCODEC_REG_COUNT, - .reg_cache_default = gbcodec_reg_defaults, - .reg_word_size = 1, - - .idle_bias_off = true, - - .controls = gbcodec_snd_controls, - .num_controls = ARRAY_SIZE(gbcodec_snd_controls), - .dapm_widgets = gbcodec_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(gbcodec_dapm_widgets), - .dapm_routes = gbcodec_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(gbcodec_dapm_routes), -}; - -static int gbaudio_codec_probe(struct platform_device *pdev) -{ - int ret; - struct gbaudio_codec_info *gbcodec; - char dai_name[NAME_SIZE]; - - gbcodec = devm_kzalloc(&pdev->dev, sizeof(struct gbaudio_codec_info), - GFP_KERNEL); - if (!gbcodec) - return -ENOMEM; - platform_set_drvdata(pdev, gbcodec); - - snprintf(dai_name, NAME_SIZE, "%s.%d", "gbcodec_pcm", pdev->id); - gbcodec_dai.name = dai_name; - - ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbcodec, - &gbcodec_dai, 1); - if (!ret) - gbcodec->registered = 1; - - return ret; -} - -static const struct of_device_id gbcodec_of_match[] = { - { .compatible = "greybus,codec", }, - {}, -}; - -static int gbaudio_codec_remove(struct platform_device *pdev) -{ - snd_soc_unregister_codec(&pdev->dev); - - return 0; -} - -static struct platform_driver gbaudio_codec_driver = { - .driver = { - .name = "gbaudio-codec", - .owner = THIS_MODULE, - .of_match_table = gbcodec_of_match, - }, - .probe = gbaudio_codec_probe, - .remove = gbaudio_codec_remove, -}; -module_platform_driver(gbaudio_codec_driver); - -MODULE_DESCRIPTION("Greybus Audio virtual codec driver"); -MODULE_AUTHOR("Vaibhav Agarwal "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:gbaudio-codec"); diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c new file mode 100644 index 000000000000..2bc23095ffd0 --- /dev/null +++ b/drivers/staging/greybus/audio_codec.c @@ -0,0 +1,315 @@ +/* + * Greybus audio driver + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include + +#include "audio.h" + +static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + /* Ensure GB speaker is connected */ + + return 0; +} + +static int gbcodec_event_hp(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + /* Ensure GB module supports jack slot */ + + return 0; +} + +static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + /* Ensure GB module supports jack slot */ + + return 0; +} + +static const struct snd_kcontrol_new gbcodec_snd_controls[] = { + SOC_DOUBLE("Playback Mute", GBCODEC_MUTE_REG, 0, 1, 1, 1), + SOC_DOUBLE("Capture Mute", GBCODEC_MUTE_REG, 4, 5, 1, 1), + SOC_DOUBLE_R("Playback Volume", GBCODEC_PB_LVOL_REG, + GBCODEC_PB_RVOL_REG, 0, 127, 0), + SOC_DOUBLE_R("Capture Volume", GBCODEC_CAP_LVOL_REG, + GBCODEC_CAP_RVOL_REG, 0, 127, 0), +}; + +static const struct snd_kcontrol_new spk_amp_ctl = + SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 0, 1, 0); + +static const struct snd_kcontrol_new hp_amp_ctl = + SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 1, 1, 0); + +static const struct snd_kcontrol_new mic_adc_ctl = + SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 4, 1, 0); + +/* APB1-GBSPK source */ +static const char * const gbcodec_apb1_src[] = {"Stereo", "Left", "Right"}; + +static const SOC_ENUM_SINGLE_DECL( + gbcodec_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbcodec_apb1_src); + +static const struct snd_kcontrol_new gbcodec_apb1_rx_mux = + SOC_DAPM_ENUM("APB1 source", gbcodec_apb1_rx_enum); + +static const SOC_ENUM_SINGLE_DECL( + gbcodec_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbcodec_apb1_src); + +static const struct snd_kcontrol_new gbcodec_mic_mux = + SOC_DAPM_ENUM("MIC source", gbcodec_mic_enum); + +static const struct snd_soc_dapm_widget gbcodec_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Spk", gbcodec_event_spk), + SND_SOC_DAPM_SPK("HP", gbcodec_event_hp), + SND_SOC_DAPM_MIC("Int Mic", gbcodec_event_int_mic), + + SND_SOC_DAPM_OUTPUT("SPKOUT"), + SND_SOC_DAPM_OUTPUT("HPOUT"), + + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_INPUT("HSMIC"), + + SND_SOC_DAPM_SWITCH("SPK Amp", SND_SOC_NOPM, 0, 0, &spk_amp_ctl), + SND_SOC_DAPM_SWITCH("HP Amp", SND_SOC_NOPM, 0, 0, &hp_amp_ctl), + SND_SOC_DAPM_SWITCH("MIC ADC", SND_SOC_NOPM, 0, 0, &mic_adc_ctl), + + SND_SOC_DAPM_PGA("SPK DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("SPK Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("APB1_TX Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("APB1_RX Mux", SND_SOC_NOPM, 0, 0, + &gbcodec_apb1_rx_mux), + SND_SOC_DAPM_MUX("MIC Mux", SND_SOC_NOPM, 0, 0, &gbcodec_mic_mux), + + SND_SOC_DAPM_AIF_IN("APB1RX", "APBridgeA1 Playback", 0, SND_SOC_NOPM, 0, + 0), + SND_SOC_DAPM_AIF_OUT("APB1TX", "APBridgeA1 Capture", 0, SND_SOC_NOPM, 0, + 0), +}; + +static const struct snd_soc_dapm_route gbcodec_dapm_routes[] = { + /* Playback path */ + {"Spk", NULL, "SPKOUT"}, + {"SPKOUT", NULL, "SPK Amp"}, + {"SPK Amp", "Switch", "SPK DAC"}, + {"SPK DAC", NULL, "SPK Mixer"}, + + {"HP", NULL, "HPOUT"}, + {"HPOUT", NULL, "HP Amp"}, + {"HP Amp", "Switch", "HP DAC"}, + {"HP DAC", NULL, "HP Mixer"}, + + {"SPK Mixer", NULL, "APB1_RX Mux"}, + {"HP Mixer", NULL, "APB1_RX Mux"}, + + {"APB1_RX Mux", "Left", "APB1RX"}, + {"APB1_RX Mux", "Right", "APB1RX"}, + {"APB1_RX Mux", "Stereo", "APB1RX"}, + + /* Capture path */ + {"MIC", NULL, "Int Mic"}, + {"MIC", NULL, "MIC Mux"}, + {"MIC Mux", "Left", "MIC ADC"}, + {"MIC Mux", "Right", "MIC ADC"}, + {"MIC Mux", "Stereo", "MIC ADC"}, + {"MIC ADC", "Switch", "APB1_TX Mixer"}, + {"APB1_TX Mixer", NULL, "APB1TX"} +}; + +static int gbcodec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return 0; +} + +static void gbcodec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ +} + +static int gbcodec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hwparams, + struct snd_soc_dai *dai) +{ + return 0; +} + +static int gbcodec_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return 0; +} + +static int gbcodec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + return 0; +} + +static int gbcodec_digital_mute(struct snd_soc_dai *dai, int mute) +{ + return 0; +} + +static struct snd_soc_dai_ops gbcodec_dai_ops = { + .startup = gbcodec_startup, + .shutdown = gbcodec_shutdown, + .hw_params = gbcodec_hw_params, + .prepare = gbcodec_prepare, + .set_fmt = gbcodec_set_dai_fmt, + .digital_mute = gbcodec_digital_mute, +}; + +static struct snd_soc_dai_driver gbcodec_dai = { + .playback = { + .stream_name = "APBridgeA1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "APBridgeA1 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &gbcodec_dai_ops, +}; + +static int gbcodec_probe(struct snd_soc_codec *codec) +{ + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + + gbcodec->codec = codec; + + return 0; +} + +static int gbcodec_remove(struct snd_soc_codec *codec) +{ + /* Empty function for now */ + return 0; +} + +static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + int ret = 0; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + u8 *gbcodec_reg = gbcodec->reg; + + if (reg == SND_SOC_NOPM) + return 0; + + if (reg >= GBCODEC_REG_COUNT) + return 0; + + gbcodec_reg[reg] = value; + dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value); + + return ret; +} + +static unsigned int gbcodec_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + unsigned int val = 0; + + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + u8 *gbcodec_reg = gbcodec->reg; + + if (reg == SND_SOC_NOPM) + return 0; + + if (reg >= GBCODEC_REG_COUNT) + return 0; + + val = gbcodec_reg[reg]; + dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val); + + return val; +} + +static struct snd_soc_codec_driver soc_codec_dev_gbcodec = { + .probe = gbcodec_probe, + .remove = gbcodec_remove, + + .read = gbcodec_read, + .write = gbcodec_write, + + .reg_cache_size = GBCODEC_REG_COUNT, + .reg_cache_default = gbcodec_reg_defaults, + .reg_word_size = 1, + + .idle_bias_off = true, + + .controls = gbcodec_snd_controls, + .num_controls = ARRAY_SIZE(gbcodec_snd_controls), + .dapm_widgets = gbcodec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(gbcodec_dapm_widgets), + .dapm_routes = gbcodec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(gbcodec_dapm_routes), +}; + +static int gbaudio_codec_probe(struct platform_device *pdev) +{ + int ret; + struct gbaudio_codec_info *gbcodec; + char dai_name[NAME_SIZE]; + + gbcodec = devm_kzalloc(&pdev->dev, sizeof(struct gbaudio_codec_info), + GFP_KERNEL); + if (!gbcodec) + return -ENOMEM; + platform_set_drvdata(pdev, gbcodec); + + snprintf(dai_name, NAME_SIZE, "%s.%d", "gbcodec_pcm", pdev->id); + gbcodec_dai.name = dai_name; + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbcodec, + &gbcodec_dai, 1); + if (!ret) + gbcodec->registered = 1; + + return ret; +} + +static const struct of_device_id gbcodec_of_match[] = { + { .compatible = "greybus,codec", }, + {}, +}; + +static int gbaudio_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_driver gbaudio_codec_driver = { + .driver = { + .name = "gbaudio-codec", + .owner = THIS_MODULE, + .of_match_table = gbcodec_of_match, + }, + .probe = gbaudio_codec_probe, + .remove = gbaudio_codec_remove, +}; +module_platform_driver(gbaudio_codec_driver); + +MODULE_DESCRIPTION("Greybus Audio virtual codec driver"); +MODULE_AUTHOR("Vaibhav Agarwal "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gbaudio-codec"); -- cgit v1.2.3-59-g8ed1b From ba4144afdebf96a3c27c9a03e0c671cf85cc55e9 Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Wed, 13 Jan 2016 14:07:45 -0700 Subject: greybus: audio: Add Audio Device Class Protocol definitions Add the macros and structures for the Greybus Audio Device Class Protocol. Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 367 ++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 2ffe07cb2a01..1277b74bc618 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1425,5 +1425,372 @@ struct gb_lights_get_flash_fault_response { #define GB_LIGHTS_FLASH_FAULT_LED_OVER_TEMPERATURE 0x00000080 } __packed; +/* Audio */ + +/* Version of the Greybus audio protocol we support */ +#define GB_AUDIO_VERSION_MAJOR 0x00 +#define GB_AUDIO_VERSION_MINOR 0x01 + +#define GB_AUDIO_TYPE_PROTOCOL_VERSION 0x01 +#define GB_AUDIO_TYPE_GET_TOPOLOGY_SIZE 0x02 +#define GB_AUDIO_TYPE_GET_TOPOLOGY 0x03 +#define GB_AUDIO_TYPE_GET_CONTROL 0x04 +#define GB_AUDIO_TYPE_SET_CONTROL 0x05 +#define GB_AUDIO_TYPE_ENABLE_WIDGET 0x06 +#define GB_AUDIO_TYPE_DISABLE_WIDGET 0x07 +#define GB_AUDIO_TYPE_GET_PCM 0x08 +#define GB_AUDIO_TYPE_SET_PCM 0x09 +#define GB_AUDIO_TYPE_SET_TX_DATA_SIZE 0x0a +#define GB_AUDIO_TYPE_GET_TX_DELAY 0x0b +#define GB_AUDIO_TYPE_ACTIVATE_TX 0x0c +#define GB_AUDIO_TYPE_DEACTIVATE_TX 0x0d +#define GB_AUDIO_TYPE_SET_RX_DATA_SIZE 0x0e +#define GB_AUDIO_TYPE_GET_RX_DELAY 0x0f +#define GB_AUDIO_TYPE_ACTIVATE_RX 0x10 +#define GB_AUDIO_TYPE_DEACTIVATE_RX 0x11 +#define GB_AUDIO_TYPE_JACK_EVENT 0x12 +#define GB_AUDIO_TYPE_BUTTON_EVENT 0x13 +#define GB_AUDIO_TYPE_STREAMING_EVENT 0x14 +#define GB_AUDIO_TYPE_SEND_DATA 0x15 + +/* Module must be able to buffer 10ms of audio data, minimum */ +#define GB_AUDIO_SAMPLE_BUFFER_MIN_US 10000 + +#define GB_AUDIO_PCM_NAME_MAX 32 +#define AUDIO_DAI_NAME_MAX 32 +#define AUDIO_CONTROL_NAME_MAX 32 +#define AUDIO_CTL_ELEM_NAME_MAX 44 +#define AUDIO_ENUM_NAME_MAX 64 +#define AUDIO_WIDGET_NAME_MAX 32 + +/* See SNDRV_PCM_FMTBIT_* in Linux source */ +#define GB_AUDIO_PCM_FMT_S8 BIT(0) +#define GB_AUDIO_PCM_FMT_U8 BIT(1) +#define GB_AUDIO_PCM_FMT_S16_LE BIT(2) +#define GB_AUDIO_PCM_FMT_S16_BE BIT(3) +#define GB_AUDIO_PCM_FMT_U16_LE BIT(4) +#define GB_AUDIO_PCM_FMT_U16_BE BIT(5) +#define GB_AUDIO_PCM_FMT_S24_LE BIT(6) +#define GB_AUDIO_PCM_FMT_S24_BE BIT(7) +#define GB_AUDIO_PCM_FMT_U24_LE BIT(8) +#define GB_AUDIO_PCM_FMT_U24_BE BIT(9) +#define GB_AUDIO_PCM_FMT_S32_LE BIT(10) +#define GB_AUDIO_PCM_FMT_S32_BE BIT(11) +#define GB_AUDIO_PCM_FMT_U32_LE BIT(12) +#define GB_AUDIO_PCM_FMT_U32_BE BIT(13) + +/* See SNDRV_PCM_RATE_* in Linux source */ +#define GB_AUDIO_PCM_RATE_5512 BIT(0) +#define GB_AUDIO_PCM_RATE_8000 BIT(1) +#define GB_AUDIO_PCM_RATE_11025 BIT(2) +#define GB_AUDIO_PCM_RATE_16000 BIT(3) +#define GB_AUDIO_PCM_RATE_22050 BIT(4) +#define GB_AUDIO_PCM_RATE_32000 BIT(5) +#define GB_AUDIO_PCM_RATE_44100 BIT(6) +#define GB_AUDIO_PCM_RATE_48000 BIT(7) +#define GB_AUDIO_PCM_RATE_64000 BIT(8) +#define GB_AUDIO_PCM_RATE_88200 BIT(9) +#define GB_AUDIO_PCM_RATE_96000 BIT(10) +#define GB_AUDIO_PCM_RATE_176400 BIT(11) +#define GB_AUDIO_PCM_RATE_192000 BIT(12) + +#define GB_AUDIO_STREAM_TYPE_CAPTURE 0x1 +#define GB_AUDIO_STREAM_TYPE_PLAYBACK 0x2 + +#define GB_AUDIO_CTL_ELEM_ACCESS_READ BIT(0) +#define GB_AUDIO_CTL_ELEM_ACCESS_WRITE BIT(1) + +/* See SNDRV_CTL_ELEM_TYPE_* in Linux source */ +#define GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN 0x01 +#define GB_AUDIO_CTL_ELEM_TYPE_INTEGER 0x02 +#define GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED 0x03 +#define GB_AUDIO_CTL_ELEM_TYPE_INTEGER64 0x06 + +/* See SNDRV_CTL_ELEM_IFACE_* in Linux source */ +#define GB_AUDIO_CTL_ELEM_IFACE_CARD 0x00 +#define GB_AUDIO_CTL_ELEM_IFACE_HWDEP 0x01 +#define GB_AUDIO_CTL_ELEM_IFACE_MIXER 0x02 +#define GB_AUDIO_CTL_ELEM_IFACE_PCM 0x03 +#define GB_AUDIO_CTL_ELEM_IFACE_RAWMIDI 0x04 +#define GB_AUDIO_CTL_ELEM_IFACE_TIMER 0x05 +#define GB_AUDIO_CTL_ELEM_IFACE_SEQUENCER 0x06 + +/* SNDRV_CTL_ELEM_ACCESS_* in Linux source */ +#define GB_AUDIO_ACCESS_READ BIT(0) +#define GB_AUDIO_ACCESS_WRITE BIT(1) +#define GB_AUDIO_ACCESS_VOLATILE BIT(2) +#define GB_AUDIO_ACCESS_TIMESTAMP BIT(3) +#define GB_AUDIO_ACCESS_TLV_READ BIT(4) +#define GB_AUDIO_ACCESS_TLV_WRITE BIT(5) +#define GB_AUDIO_ACCESS_TLV_COMMAND BIT(6) +#define GB_AUDIO_ACCESS_INACTIVE BIT(7) +#define GB_AUDIO_ACCESS_LOCK BIT(8) +#define GB_AUDIO_ACCESS_OWNER BIT(9) + +/* enum snd_soc_dapm_type */ +#define GB_AUDIO_WIDGET_TYPE_INPUT 0x0 +#define GB_AUDIO_WIDGET_TYPE_OUTPUT 0x1 +#define GB_AUDIO_WIDGET_TYPE_MUX 0x2 +#define GB_AUDIO_WIDGET_TYPE_VIRT_MUX 0x3 +#define GB_AUDIO_WIDGET_TYPE_VALUE_MUX 0x4 +#define GB_AUDIO_WIDGET_TYPE_MIXER 0x5 +#define GB_AUDIO_WIDGET_TYPE_MIXER_NAMED_CTL 0x6 +#define GB_AUDIO_WIDGET_TYPE_PGA 0x7 +#define GB_AUDIO_WIDGET_TYPE_OUT_DRV 0x8 +#define GB_AUDIO_WIDGET_TYPE_ADC 0x9 +#define GB_AUDIO_WIDGET_TYPE_DAC 0xa +#define GB_AUDIO_WIDGET_TYPE_MICBIAS 0xb +#define GB_AUDIO_WIDGET_TYPE_MIC 0xc +#define GB_AUDIO_WIDGET_TYPE_HP 0xd +#define GB_AUDIO_WIDGET_TYPE_SPK 0xe +#define GB_AUDIO_WIDGET_TYPE_LINE 0xf +#define GB_AUDIO_WIDGET_TYPE_SWITCH 0x10 +#define GB_AUDIO_WIDGET_TYPE_VMID 0x11 +#define GB_AUDIO_WIDGET_TYPE_PRE 0x12 +#define GB_AUDIO_WIDGET_TYPE_POST 0x13 +#define GB_AUDIO_WIDGET_TYPE_SUPPLY 0x14 +#define GB_AUDIO_WIDGET_TYPE_REGULATOR_SUPPLY 0x15 +#define GB_AUDIO_WIDGET_TYPE_CLOCK_SUPPLY 0x16 +#define GB_AUDIO_WIDGET_TYPE_AIF_IN 0x17 +#define GB_AUDIO_WIDGET_TYPE_AIF_OUT 0x18 +#define GB_AUDIO_WIDGET_TYPE_SIGGEN 0x19 +#define GB_AUDIO_WIDGET_TYPE_DAI_IN 0x1a +#define GB_AUDIO_WIDGET_TYPE_DAI_OUT 0x1b +#define GB_AUDIO_WIDGET_TYPE_DAI_LINK 0x1c + +#define GB_AUDIO_WIDGET_STATE_DISABLED 0x01 +#define GB_AUDIO_WIDGET_STATE_ENAABLED 0x02 + +#define GB_AUDIO_JACK_EVENT_INSERTION 0x1 +#define GB_AUDIO_JACK_EVENT_REMOVAL 0x2 + +#define GB_AUDIO_BUTTON_EVENT_PRESS 0x1 +#define GB_AUDIO_BUTTON_EVENT_RELEASE 0x2 + +#define GB_AUDIO_STREAMING_EVENT_UNSPECIFIED 0x1 +#define GB_AUDIO_STREAMING_EVENT_HALT 0x2 +#define GB_AUDIO_STREAMING_EVENT_INTERNAL_ERROR 0x3 +#define GB_AUDIO_STREAMING_EVENT_PROTOCOL_ERROR 0x4 +#define GB_AUDIO_STREAMING_EVENT_FAILURE 0x5 +#define GB_AUDIO_STREAMING_EVENT_UNDERRUN 0x6 +#define GB_AUDIO_STREAMING_EVENT_OVERRUN 0x7 +#define GB_AUDIO_STREAMING_EVENT_CLOCKING 0x8 +#define GB_AUDIO_STREAMING_EVENT_DATA_LEN 0x9 + +#define GB_AUDIO_INVALID_INDEX 0xff + +struct gb_audio_pcm { + __u8 stream_name[GB_AUDIO_PCM_NAME_MAX]; + __le32 formats; /* GB_AUDIO_PCM_FMT_* */ + __le32 rates; /* GB_AUDIO_PCM_RATE_* */ + __u8 chan_min; + __u8 chan_max; + __u8 sig_bits; /* number of bits of content */ +} __packed; + +struct gb_audio_dai { + __u8 name[AUDIO_DAI_NAME_MAX]; + __le16 data_cport; + struct gb_audio_pcm capture; + struct gb_audio_pcm playback; +} __packed; + +struct gb_audio_integer { + __le32 min; + __le32 max; + __le32 step; +} __packed; + +struct gb_audio_integer64 { + __le64 min; + __le64 max; + __le64 step; +} __packed; + +struct gb_audio_enumerated { + __le32 items; + __le16 names_length; + __u8 names[0]; +} __packed; + +struct gb_audio_ctl_elem_info { /* See snd_ctl_elem_info in Linux source */ + __u8 type; /* GB_AUDIO_CTL_ELEM_TYPE_* */ + __le16 dimen[4]; + union { + struct gb_audio_integer integer; + struct gb_audio_integer64 integer64; + struct gb_audio_enumerated enumerated; + } value; +} __packed; + +struct gb_audio_ctl_elem_value { /* See snd_ctl_elem_value in Linux source */ + __le64 timestamp; /* XXX needed? */ + union { + __le32 integer_value[2]; /* consider CTL_DOUBLE_xxx */ + __le64 integer64_value[2]; + __le32 enumerated_item[2]; + } value; +} __packed; + +struct gb_audio_control { + __u8 name[AUDIO_CONTROL_NAME_MAX]; + __u8 id; /* 0-63 */ + __u8 iface; /* GB_AUDIO_IFACE_* */ + __le16 data_cport; + __le32 access; /* GB_AUDIO_ACCESS_* */ + __u8 count; /* count of same elements */ + __u8 count_values; /* count of values, max=2 for CTL_DOUBLE_xxx */ + struct gb_audio_ctl_elem_info info; +} __packed; + +struct gb_audio_widget { + __u8 name[AUDIO_WIDGET_NAME_MAX]; + __u8 sname[AUDIO_WIDGET_NAME_MAX]; + __u8 id; + __u8 type; /* GB_AUDIO_WIDGET_TYPE_* */ + __u8 state; /* GB_AUDIO_WIDGET_STATE_* */ + __u8 ncontrols; + struct gb_audio_control ctl[0]; /* 'ncontrols' entries */ +} __packed; + +struct gb_audio_route { + __u8 source_id; /* widget id */ + __u8 destination_id; /* widget id */ + __u8 control_id; /* 0-63 */ + __u8 index; /* Selection within the control */ +} __packed; + +struct gb_audio_topology { + __u8 num_dais; + __u8 num_controls; + __u8 num_widgets; + __u8 num_routes; + __u32 size_dais; + __u32 size_controls; + __u32 size_widgets; + __u32 size_routes; + /* + * struct gb_audio_dai dai[num_dais]; + * struct gb_audio_control controls[num_controls]; + * struct gb_audio_widget widgets[num_widgets]; + * struct gb_audio_route routes[num_routes]; + */ + __u8 data[0]; +} __packed; + +struct gb_audio_get_topology_size_response { + __le16 size; +} __packed; + +struct gb_audio_get_topology_response { + struct gb_audio_topology topology; +} __packed; + +struct gb_audio_get_control_request { + __u8 control_id; + __u8 index; +} __packed; + +struct gb_audio_get_control_response { + struct gb_audio_ctl_elem_value value; +} __packed; + +struct gb_audio_set_control_request { + __u8 control_id; + __u8 index; + struct gb_audio_ctl_elem_value value; +} __packed; + +struct gb_audio_enable_widget_request { + __u8 widget_id; +} __packed; + +struct gb_audio_disable_widget_request { + __u8 widget_id; +} __packed; + +struct gb_audio_get_pcm_request { + __le16 data_cport; +} __packed; + +struct gb_audio_get_pcm_response { + __le32 format; + __le32 rate; + __u8 channels; + __u8 sig_bits; +} __packed; + +struct gb_audio_set_pcm_request { + __le16 data_cport; + __le32 format; + __le32 rate; + __u8 channels; + __u8 sig_bits; +} __packed; + +struct gb_audio_set_tx_data_size_request { + __le16 data_cport; + __le16 size; +} __packed; + +struct gb_audio_get_tx_delay_request { + __le16 data_cport; +} __packed; + +struct gb_audio_get_tx_delay_response { + __le32 delay; +} __packed; + +struct gb_audio_activate_tx_request { + __le16 data_cport; +} __packed; + +struct gb_audio_deactivate_tx_request { + __le16 data_cport; +} __packed; + +struct gb_audio_set_rx_data_size_request { + __le16 data_cport; + __le16 size; +} __packed; + +struct gb_audio_get_rx_delay_request { + __le16 data_cport; +} __packed; + +struct gb_audio_get_rx_delay_response { + __le32 delay; +} __packed; + +struct gb_audio_activate_rx_request { + __le16 data_cport; +} __packed; + +struct gb_audio_deactivate_rx_request { + __le16 data_cport; +} __packed; + +struct gb_audio_jack_event_request { + __u8 widget_id; + __u8 widget_type; + __u8 event; +} __packed; + +struct gb_audio_button_event_request { + __u8 widget_id; + __u8 button_id; + __u8 event; +} __packed; + +struct gb_audio_streaming_event_request { + __le16 data_cport; + __u8 event; +} __packed; + +struct gb_audio_send_data_request { + __le64 timestamp; + __u8 data[0]; +} __packed; + #endif /* __GREYBUS_PROTOCOLS_H */ -- cgit v1.2.3-59-g8ed1b From 184992e305f1de3a3d5fa446da3a2bc76be7c54a Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Wed, 13 Jan 2016 14:07:46 -0700 Subject: greybus: audio: Add Greybus Audio Device Class Protocol helper routines Add helper routines to make communicating with audio modules easier. Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/audio.h | 40 ++++++ drivers/staging/greybus/audio_gb.c | 267 +++++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 drivers/staging/greybus/audio_gb.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index fb0e02316b1c..3010f1606dae 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -30,6 +30,7 @@ gb-hid-y := hid.o gb-es2-y := es2.o gb-arche-y := arche-platform.o arche-apb-ctrl.o gb-audio-codec-y := audio_codec.o +gb-audio-gb-y := audio_gb.o gb-camera-y := camera.o obj-m += greybus.o @@ -44,6 +45,7 @@ obj-m += gb-es2.o obj-m += gb-arche.o obj-m += gb-audio-codec.o obj-m += gb-camera.o +obj-m += gb-audio-gb.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index 5dec00d63e27..c8f798eb2afa 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -13,6 +13,9 @@ #include +#include "greybus.h" +#include "greybus_protocols.h" + #define NAME_SIZE 32 enum { @@ -82,5 +85,42 @@ struct gbaudio_codec_info { struct mutex lock; }; +extern int gb_audio_gb_get_topology(struct gb_connection *connection, + struct gb_audio_topology **topology); +extern int gb_audio_gb_get_control(struct gb_connection *connection, + uint8_t control_id, uint8_t index, + struct gb_audio_ctl_elem_value *value); +extern int gb_audio_gb_set_control(struct gb_connection *connection, + uint8_t control_id, uint8_t index, + struct gb_audio_ctl_elem_value *value); +extern int gb_audio_gb_enable_widget(struct gb_connection *connection, + uint8_t widget_id); +extern int gb_audio_gb_disable_widget(struct gb_connection *connection, + uint8_t widget_id); +extern int gb_audio_gb_get_pcm(struct gb_connection *connection, + uint16_t data_cport, uint32_t *format, + uint32_t *rate, uint8_t *channels, + uint8_t *sig_bits); +extern int gb_audio_gb_set_pcm(struct gb_connection *connection, + uint16_t data_cport, uint32_t format, + uint32_t rate, uint8_t channels, + uint8_t sig_bits); +extern int gb_audio_gb_set_tx_data_size(struct gb_connection *connection, + uint16_t data_cport, uint16_t size); +extern int gb_audio_gb_get_tx_delay(struct gb_connection *connection, + uint16_t data_cport, uint32_t *delay); +extern int gb_audio_gb_activate_tx(struct gb_connection *connection, + uint16_t data_cport); +extern int gb_audio_gb_deactivate_tx(struct gb_connection *connection, + uint16_t data_cport); +extern int gb_audio_gb_set_rx_data_size(struct gb_connection *connection, + uint16_t data_cport, uint16_t size); +extern int gb_audio_gb_get_rx_delay(struct gb_connection *connection, + uint16_t data_cport, uint32_t *delay); +extern int gb_audio_gb_activate_rx(struct gb_connection *connection, + uint16_t data_cport); +extern int gb_audio_gb_deactivate_rx(struct gb_connection *connection, + uint16_t data_cport); + #endif /* __KERNEL__ */ #endif /* __LINUX_GBAUDIO_H */ diff --git a/drivers/staging/greybus/audio_gb.c b/drivers/staging/greybus/audio_gb.c new file mode 100644 index 000000000000..08bfdeb87a61 --- /dev/null +++ b/drivers/staging/greybus/audio_gb.c @@ -0,0 +1,267 @@ +/* + * Greybus Audio Device Class Protocol helpers + * + * Copyright 2015-2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" +#include "greybus_protocols.h" +#include "operation.h" + +/* TODO: Split into separate calls */ +int gb_audio_gb_get_topology(struct gb_connection *connection, + struct gb_audio_topology **topology) +{ + struct gb_audio_get_topology_size_response size_resp; + struct gb_audio_topology *topo; + uint16_t size; + int ret; + + ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TOPOLOGY_SIZE, + NULL, 0, &size_resp, sizeof(size_resp)); + if (ret) + return ret; + + size = le16_to_cpu(size_resp.size); + if (size < sizeof(*topo)) + return -ENODATA; + + topo = kzalloc(size, GFP_KERNEL); + if (!topo) + return -ENOMEM; + + ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TOPOLOGY, NULL, 0, + topo, size); + if (ret) { + kfree(topo); + return ret; + } + + *topology = topo; + + return 0; +} +EXPORT_SYMBOL_GPL(gb_audio_gb_get_topology); + +int gb_audio_gb_get_control(struct gb_connection *connection, + uint8_t control_id, uint8_t index, + struct gb_audio_ctl_elem_value *value) +{ + struct gb_audio_get_control_request req; + struct gb_audio_get_control_response resp; + int ret; + + req.control_id = control_id; + req.index = index; + + ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_CONTROL, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret) + return ret; + + memcpy(value, &resp.value, sizeof(*value)); + + return 0; +} +EXPORT_SYMBOL_GPL(gb_audio_gb_get_control); + +int gb_audio_gb_set_control(struct gb_connection *connection, + uint8_t control_id, uint8_t index, + struct gb_audio_ctl_elem_value *value) +{ + struct gb_audio_set_control_request req; + + req.control_id = control_id; + req.index = index; + memcpy(&req.value, value, sizeof(req.value)); + + return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_CONTROL, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_set_control); + +int gb_audio_gb_enable_widget(struct gb_connection *connection, + uint8_t widget_id) +{ + struct gb_audio_enable_widget_request req; + + req.widget_id = widget_id; + + return gb_operation_sync(connection, GB_AUDIO_TYPE_ENABLE_WIDGET, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_enable_widget); + +int gb_audio_gb_disable_widget(struct gb_connection *connection, + uint8_t widget_id) +{ + struct gb_audio_disable_widget_request req; + + req.widget_id = widget_id; + + return gb_operation_sync(connection, GB_AUDIO_TYPE_DISABLE_WIDGET, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_disable_widget); + +int gb_audio_gb_get_pcm(struct gb_connection *connection, uint16_t data_cport, + uint32_t *format, uint32_t *rate, uint8_t *channels, + uint8_t *sig_bits) +{ + struct gb_audio_get_pcm_request req; + struct gb_audio_get_pcm_response resp; + int ret; + + req.data_cport = cpu_to_le16(data_cport); + + ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_PCM, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret) + return ret; + + *format = le32_to_cpu(resp.format); + *rate = le32_to_cpu(resp.rate); + *channels = resp.channels; + *sig_bits = resp.sig_bits; + + return 0; +} +EXPORT_SYMBOL_GPL(gb_audio_gb_get_pcm); + +int gb_audio_gb_set_pcm(struct gb_connection *connection, uint16_t data_cport, + uint32_t format, uint32_t rate, uint8_t channels, + uint8_t sig_bits) +{ + struct gb_audio_set_pcm_request req; + + req.data_cport = cpu_to_le16(data_cport); + req.format = cpu_to_le32(format); + req.rate = cpu_to_le32(rate); + req.channels = channels; + req.sig_bits = sig_bits; + + return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_PCM, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_set_pcm); + +int gb_audio_gb_set_tx_data_size(struct gb_connection *connection, + uint16_t data_cport, uint16_t size) +{ + struct gb_audio_set_tx_data_size_request req; + + req.data_cport = cpu_to_le16(data_cport); + req.size = cpu_to_le16(size); + + return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_TX_DATA_SIZE, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_set_tx_data_size); + +int gb_audio_gb_get_tx_delay(struct gb_connection *connection, + uint16_t data_cport, uint32_t *delay) +{ + struct gb_audio_get_tx_delay_request req; + struct gb_audio_get_tx_delay_response resp; + int ret; + + req.data_cport = cpu_to_le16(data_cport); + + ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TX_DELAY, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret) + return ret; + + *delay = le32_to_cpu(resp.delay); + + return 0; +} +EXPORT_SYMBOL_GPL(gb_audio_gb_get_tx_delay); + +int gb_audio_gb_activate_tx(struct gb_connection *connection, + uint16_t data_cport) +{ + struct gb_audio_activate_tx_request req; + + req.data_cport = cpu_to_le16(data_cport); + + return gb_operation_sync(connection, GB_AUDIO_TYPE_ACTIVATE_TX, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_activate_tx); + +int gb_audio_gb_deactivate_tx(struct gb_connection *connection, + uint16_t data_cport) +{ + struct gb_audio_deactivate_tx_request req; + + req.data_cport = cpu_to_le16(data_cport); + + return gb_operation_sync(connection, GB_AUDIO_TYPE_DEACTIVATE_TX, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_deactivate_tx); + +int gb_audio_gb_set_rx_data_size(struct gb_connection *connection, + uint16_t data_cport, uint16_t size) +{ + struct gb_audio_set_rx_data_size_request req; + + req.data_cport = cpu_to_le16(data_cport); + req.size = cpu_to_le16(size); + + return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_RX_DATA_SIZE, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_set_rx_data_size); + +int gb_audio_gb_get_rx_delay(struct gb_connection *connection, + uint16_t data_cport, uint32_t *delay) +{ + struct gb_audio_get_rx_delay_request req; + struct gb_audio_get_rx_delay_response resp; + int ret; + + req.data_cport = cpu_to_le16(data_cport); + + ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_RX_DELAY, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret) + return ret; + + *delay = le32_to_cpu(resp.delay); + + return 0; +} +EXPORT_SYMBOL_GPL(gb_audio_gb_get_rx_delay); + +int gb_audio_gb_activate_rx(struct gb_connection *connection, + uint16_t data_cport) +{ + struct gb_audio_activate_rx_request req; + + req.data_cport = cpu_to_le16(data_cport); + + return gb_operation_sync(connection, GB_AUDIO_TYPE_ACTIVATE_RX, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_activate_rx); + +int gb_audio_gb_deactivate_rx(struct gb_connection *connection, + uint16_t data_cport) +{ + struct gb_audio_deactivate_rx_request req; + + req.data_cport = cpu_to_le16(data_cport); + + return gb_operation_sync(connection, GB_AUDIO_TYPE_DEACTIVATE_RX, + &req, sizeof(req), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_audio_gb_deactivate_rx); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("greybus:audio-gb"); +MODULE_DESCRIPTION("Greybus Audio Device Class Protocol library"); +MODULE_AUTHOR("Mark Greer "); -- cgit v1.2.3-59-g8ed1b From 4dbf5056405ad3c0ead370f0f3254c17b81b1e04 Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Wed, 13 Jan 2016 14:07:47 -0700 Subject: greybus: audio: Add direct audio streaming control for APBridgeA The latest audio architecture does not stream audio data over the USB link between the AP and APBridgeA. Instead, audio data is streamed directly over the I2S link between the two. To support the Greybus audio driver in setting up the I2S port and controling packeting/depacketizing of audio data to/from Greybus audio messages, define a set of commands and their parameters. These commands and parameters will be placed into a request and sent over the USB control channel to APBridgeA. A corresponding driver on APBridgeA will receive and process the requests. Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/audio.h | 23 ++++ drivers/staging/greybus/audio_apbridgea.c | 156 ++++++++++++++++++++++++++++ drivers/staging/greybus/audio_apbridgea.h | 149 ++++++++++++++++++++++++++ drivers/staging/greybus/greybus_protocols.h | 3 + 5 files changed, 333 insertions(+) create mode 100644 drivers/staging/greybus/audio_apbridgea.c create mode 100644 drivers/staging/greybus/audio_apbridgea.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 3010f1606dae..e2ae24b32ab7 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -31,6 +31,7 @@ gb-es2-y := es2.o gb-arche-y := arche-platform.o arche-apb-ctrl.o gb-audio-codec-y := audio_codec.o gb-audio-gb-y := audio_gb.o +gb-audio-apbridgea-y := audio_apbridgea.o gb-camera-y := camera.o obj-m += greybus.o @@ -46,6 +47,7 @@ obj-m += gb-arche.o obj-m += gb-audio-codec.o obj-m += gb-camera.o obj-m += gb-audio-gb.o +obj-m += gb-audio-apbridgea.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h index c8f798eb2afa..fca60af3ddc1 100644 --- a/drivers/staging/greybus/audio.h +++ b/drivers/staging/greybus/audio.h @@ -121,6 +121,29 @@ extern int gb_audio_gb_activate_rx(struct gb_connection *connection, uint16_t data_cport); extern int gb_audio_gb_deactivate_rx(struct gb_connection *connection, uint16_t data_cport); +extern int gb_audio_apbridgea_set_config(struct gb_connection *connection, + __u16 i2s_port, __u32 format, + __u32 rate, __u32 mclk_freq); +extern int gb_audio_apbridgea_register_cport(struct gb_connection *connection, + __u16 i2s_port, __u16 cportid); +extern int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, + __u16 i2s_port, __u16 cportid); +extern int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, + __u16 i2s_port, __u16 size); +extern int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, + __u16 i2s_port, __u32 *delay); +extern int gb_audio_apbridgea_start_tx(struct gb_connection *connection, + __u16 i2s_port, __u64 timestamp); +extern int gb_audio_apbridgea_stop_tx(struct gb_connection *connection, + __u16 i2s_port); +extern int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, + __u16 i2s_port, __u16 size); +extern int gb_audio_apbridgea_get_rx_delay(struct gb_connection *connection, + __u16 i2s_port, __u32 *delay); +extern int gb_audio_apbridgea_start_rx(struct gb_connection *connection, + __u16 i2s_port); +extern int gb_audio_apbridgea_stop_rx(struct gb_connection *connection, + __u16 i2s_port); #endif /* __KERNEL__ */ #endif /* __LINUX_GBAUDIO_H */ diff --git a/drivers/staging/greybus/audio_apbridgea.c b/drivers/staging/greybus/audio_apbridgea.c new file mode 100644 index 000000000000..20238e68e83a --- /dev/null +++ b/drivers/staging/greybus/audio_apbridgea.c @@ -0,0 +1,156 @@ +/* + * Greybus Audio Device Class Protocol helpers + * + * Copyright 2015-2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" +#include "greybus_protocols.h" +#include "audio_apbridgea.h" + +int gb_audio_apbridgea_set_config(struct gb_connection *connection, + __u16 i2s_port, __u32 format, __u32 rate, + __u32 mclk_freq) +{ + struct audio_apbridgea_set_config_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_SET_CONFIG; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + req.format = cpu_to_le32(format); + req.rate = cpu_to_le32(rate); + req.mclk_freq = cpu_to_le32(mclk_freq); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_config); + +int gb_audio_apbridgea_register_cport(struct gb_connection *connection, + __u16 i2s_port, __u16 cportid) +{ + struct audio_apbridgea_register_cport_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + req.cport = cpu_to_le16(cportid); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_register_cport); + +int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, + __u16 i2s_port, __u16 cportid) +{ + struct audio_apbridgea_unregister_cport_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + req.cport = cpu_to_le16(cportid); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_unregister_cport); + +int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, + __u16 i2s_port, __u16 size) +{ + struct audio_apbridgea_set_tx_data_size_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_SET_TX_DATA_SIZE; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + req.size = cpu_to_le16(size); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_tx_data_size); + +int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, + __u16 i2s_port, __u32 *delay) +{ + /* TODO: implement */ + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_get_tx_delay); + +int gb_audio_apbridgea_start_tx(struct gb_connection *connection, + __u16 i2s_port, __u64 timestamp) +{ + struct audio_apbridgea_start_tx_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_START_TX; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + req.timestamp = cpu_to_le64(timestamp); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_start_tx); + +int gb_audio_apbridgea_stop_tx(struct gb_connection *connection, __u16 i2s_port) +{ + struct audio_apbridgea_stop_tx_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_STOP_TX; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_stop_tx); + +int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, + __u16 i2s_port, __u16 size) +{ + struct audio_apbridgea_set_rx_data_size_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_SET_RX_DATA_SIZE; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + req.size = cpu_to_le16(size); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_rx_data_size); + +int gb_audio_apbridgea_get_rx_delay(struct gb_connection *connection, + __u16 i2s_port, __u32 *delay) +{ + /* TODO: implement */ + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_get_rx_delay); + +int gb_audio_apbridgea_start_rx(struct gb_connection *connection, + __u16 i2s_port) +{ + struct audio_apbridgea_start_rx_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_START_RX; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_start_rx); + +int gb_audio_apbridgea_stop_rx(struct gb_connection *connection, __u16 i2s_port) +{ + struct audio_apbridgea_stop_rx_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_STOP_RX; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_stop_rx); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("greybus:audio-apbridgea"); +MODULE_DESCRIPTION("Greybus Special APBridgeA Audio Protocol library"); +MODULE_AUTHOR("Mark Greer "); diff --git a/drivers/staging/greybus/audio_apbridgea.h b/drivers/staging/greybus/audio_apbridgea.h new file mode 100644 index 000000000000..d1d56b7c1a75 --- /dev/null +++ b/drivers/staging/greybus/audio_apbridgea.h @@ -0,0 +1,149 @@ +/** + * Copyright (c) 2015-2016 Google 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, + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 is a special protocol for configuring communication over the + * I2S bus between the DSP on the MSM8994 and APBridgeA. Therefore, + * we can predefine several low-level attributes of the communication + * because we know that they are supported. In particular, the following + * assumptions are made: + * - there are two channels (i.e., stereo) + * - the low-level protocol is I2S as defined by Philips/NXP + * - the DSP on the MSM8994 is the clock master for MCLK, BCLK, and WCLK + * - WCLK changes on the falling edge of BCLK + * - WCLK low for left channel; high for right channel + * - TX data is sent on the falling edge of BCLK + * - RX data is received/latched on the rising edge of BCLK + */ + +#ifndef __AUDIO_APBRIDGEA_H +#define __AUDIO_APBRIDGEA_H + +#define AUDIO_APBRIDGEA_TYPE_SET_CONFIG 0x01 +#define AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT 0x02 +#define AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT 0x03 +#define AUDIO_APBRIDGEA_TYPE_SET_TX_DATA_SIZE 0x04 +#define AUDIO_APBRIDGEA_TYPE_GET_TX_DELAY 0x05 +#define AUDIO_APBRIDGEA_TYPE_START_TX 0x06 +#define AUDIO_APBRIDGEA_TYPE_STOP_TX 0x07 +#define AUDIO_APBRIDGEA_TYPE_SET_RX_DATA_SIZE 0x08 +#define AUDIO_APBRIDGEA_TYPE_GET_RX_DELAY 0x09 +#define AUDIO_APBRIDGEA_TYPE_START_RX 0x0a +#define AUDIO_APBRIDGEA_TYPE_STOP_RX 0x0b + +#define AUDIO_APBRIDGEA_PCM_FMT_8 BIT(0) +#define AUDIO_APBRIDGEA_PCM_FMT_16 BIT(1) +#define AUDIO_APBRIDGEA_PCM_FMT_24 BIT(2) +#define AUDIO_APBRIDGEA_PCM_FMT_32 BIT(3) +#define AUDIO_APBRIDGEA_PCM_FMT_64 BIT(4) + +#define AUDIO_APBRIDGEA_PCM_RATE_5512 BIT(0) +#define AUDIO_APBRIDGEA_PCM_RATE_8000 BIT(1) +#define AUDIO_APBRIDGEA_PCM_RATE_11025 BIT(2) +#define AUDIO_APBRIDGEA_PCM_RATE_16000 BIT(3) +#define AUDIO_APBRIDGEA_PCM_RATE_22050 BIT(4) +#define AUDIO_APBRIDGEA_PCM_RATE_32000 BIT(5) +#define AUDIO_APBRIDGEA_PCM_RATE_44100 BIT(6) +#define AUDIO_APBRIDGEA_PCM_RATE_48000 BIT(7) +#define AUDIO_APBRIDGEA_PCM_RATE_64000 BIT(8) +#define AUDIO_APBRIDGEA_PCM_RATE_88200 BIT(9) +#define AUDIO_APBRIDGEA_PCM_RATE_96000 BIT(10) +#define AUDIO_APBRIDGEA_PCM_RATE_176400 BIT(11) +#define AUDIO_APBRIDGEA_PCM_RATE_192000 BIT(12) + +/* The I2S port is passed in the 'index' parameter of the USB request */ +/* The CPort is passed in the 'value' parameter of the USB request */ + +struct audio_apbridgea_hdr { + __u8 type; + __le16 i2s_port; + __u8 data[0]; +} __packed; + +struct audio_apbridgea_set_config_request { + struct audio_apbridgea_hdr hdr; + __le32 format; /* AUDIO_APBRIDGEA_PCM_FMT_* */ + __le32 rate; /* AUDIO_APBRIDGEA_PCM_RATE_* */ + __le32 mclk_freq; /* XXX Remove? */ +} __packed; + +struct audio_apbridgea_register_cport_request { + struct audio_apbridgea_hdr hdr; + __le16 cport; +} __packed; + +struct audio_apbridgea_unregister_cport_request { + struct audio_apbridgea_hdr hdr; + __le16 cport; +} __packed; + +struct audio_apbridgea_set_tx_data_size_request { + struct audio_apbridgea_hdr hdr; + __le16 size; +} __packed; + +struct audio_apbridgea_get_tx_delay_request { + struct audio_apbridgea_hdr hdr; +} __packed; + +struct audio_apbridgea_get_tx_delay_response { + struct audio_apbridgea_hdr hdr; + __le16 delay; +} __packed; + +struct audio_apbridgea_start_tx_request { + struct audio_apbridgea_hdr hdr; + __le64 timestamp; +} __packed; + +struct audio_apbridgea_stop_tx_request { + struct audio_apbridgea_hdr hdr; +} __packed; + +struct audio_apbridgea_set_rx_data_size_request { + struct audio_apbridgea_hdr hdr; + __le16 size; +} __packed; + +struct audio_apbridgea_get_rx_delay_request { + struct audio_apbridgea_hdr hdr; +} __packed; + +struct audio_apbridgea_get_rx_delay_response { + struct audio_apbridgea_hdr hdr; + __le16 delay; +} __packed; + +struct audio_apbridgea_start_rx_request { + struct audio_apbridgea_hdr hdr; +} __packed; + +struct audio_apbridgea_stop_rx_request { + struct audio_apbridgea_hdr hdr; +} __packed; + +#endif /*__AUDIO_APBRIDGEA_H */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 1277b74bc618..55e99b7083f3 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -175,6 +175,9 @@ struct gb_control_interface_version_response { /* request to control the CSI transmitter */ #define GB_APB_REQUEST_CSI_TX_CONTROL 0x08 +/* request to control the CSI transmitter */ +#define GB_APB_REQUEST_AUDIO_CONTROL 0x09 + /* Firmware Protocol */ -- cgit v1.2.3-59-g8ed1b From 8db00736d365b75d6af5dfd4a2673a1453fff4b7 Mon Sep 17 00:00:00 2001 From: Svetlin Ankov Date: Wed, 13 Jan 2016 14:07:48 -0700 Subject: greybus: audio: Add Audio Manager This is a simple module that keeps a list of connected GB audio modules. Whenever a device is attached, an appropriate uevent is sent to userspace: UDEV [4941.803215] add /kernel/gb_audio_manager/0 (gb_audio_manager) ACTION=add CPORT=99 DEVICES=0x10 DEVPATH=/kernel/gb_audio_manager/0 NAME=naim PID=64 SEQNUM=1828 SLOT=2 SUBSYSTEM=gb_audio_manager USEC_INITIALIZED=802416 VID=128 And whenever removed: UDEV [4941.836588] remove /kernel/gb_audio_manager/0 (gb_audio_manager) ACTION=remove DEVPATH=/kernel/gb_audio_manager/0 SEQNUM=1833 SUBSYSTEM=gb_audio_manager USEC_INITIALIZED=835681 The API consists of functions for adding, removing and inspecting added device module descriptions (struct gb_audio_module): int gb_audio_manager_add(struct gb_audio_module_descriptor *desc); int gb_audio_manager_remove(int id); int gb_audio_manager_remove_all(void); struct gb_audio_module* gb_audio_manager_get_module(int id); void gb_audio_manager_put_module(struct gb_audio_module *module); int gb_audio_manager_dump_module(int id); void gb_audio_manager_dump_all(void); Devices can be inspected through sysfs in /sys/kernel/gb_audio_manager/{id}/* If GB_AUDIO_MANAGER_SYSFS is exported as 'true', managing devices can be done via the SYSFS as well. For instance: echo name=naim slot=2 vid=128 pid=64 cport=99 devices=0x10 > /sys/kernel/gb_audio_manager/add echo all > /sys/kernel/gb_audio_manager/dump echo 2 > /sys/kernel/gb_audio_manager/dump echo 2 > /sys/kernel/gb_audio_manager/remove Signed-off-by: Svetlin Ankov Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 9 + drivers/staging/greybus/audio_manager.c | 184 ++++++++++++++++++ drivers/staging/greybus/audio_manager.h | 82 ++++++++ drivers/staging/greybus/audio_manager_module.c | 240 ++++++++++++++++++++++++ drivers/staging/greybus/audio_manager_private.h | 28 +++ drivers/staging/greybus/audio_manager_sysfs.c | 101 ++++++++++ 6 files changed, 644 insertions(+) create mode 100644 drivers/staging/greybus/audio_manager.c create mode 100644 drivers/staging/greybus/audio_manager.h create mode 100644 drivers/staging/greybus/audio_manager_module.c create mode 100644 drivers/staging/greybus/audio_manager_private.h create mode 100644 drivers/staging/greybus/audio_manager_sysfs.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index e2ae24b32ab7..4ebdc6b9c89f 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -32,6 +32,8 @@ gb-arche-y := arche-platform.o arche-apb-ctrl.o gb-audio-codec-y := audio_codec.o gb-audio-gb-y := audio_gb.o gb-audio-apbridgea-y := audio_apbridgea.o +gb-audio-manager-y += audio_manager.o +gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o obj-m += greybus.o @@ -48,6 +50,7 @@ obj-m += gb-audio-codec.o obj-m += gb-camera.o obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o +obj-m += gb-audio-manager.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build @@ -89,6 +92,12 @@ ccflags-y := -Wall # needed for trace events ccflags-y += -I$(src) +GB_AUDIO_MANAGER_SYSFS ?= true +ifeq ($(GB_AUDIO_MANAGER_SYSFS),true) +gb-audio-manager-y += audio_manager_sysfs.o +ccflags-y += -DGB_AUDIO_MANAGER_SYSFS +endif + all: module tools:: diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c new file mode 100644 index 000000000000..117676314daf --- /dev/null +++ b/drivers/staging/greybus/audio_manager.c @@ -0,0 +1,184 @@ +/* + * Greybus operations + * + * Copyright 2015-2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include + +#include "audio_manager.h" +#include "audio_manager_private.h" + +static struct kset *manager_kset; + +static LIST_HEAD(modules_list); +static DEFINE_RWLOCK(modules_lock); + +static int current_module_id; + +/* helpers */ +static struct gb_audio_manager_module *gb_audio_manager_get_locked(int id) +{ + struct gb_audio_manager_module *module; + + if (id < 0) + return NULL; + + list_for_each_entry(module, &modules_list, list) { + if (module->id == id) + return module; + } + + return NULL; +} + +/* public API */ +int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc) +{ + struct gb_audio_manager_module *module; + unsigned long flags; + int err; + + err = gb_audio_manager_module_create(&module, manager_kset, + current_module_id++, desc); + if (err) + return err; + + /* Add it to the list */ + write_lock_irqsave(&modules_lock, flags); + list_add_tail(&module->list, &modules_list); + write_unlock_irqrestore(&modules_lock, flags); + + return module->id; +} +EXPORT_SYMBOL_GPL(gb_audio_manager_add); + +int gb_audio_manager_remove(int id) +{ + struct gb_audio_manager_module *module; + unsigned long flags; + + write_lock_irqsave(&modules_lock, flags); + + module = gb_audio_manager_get_locked(id); + if (!module) { + write_unlock_irqrestore(&modules_lock, flags); + return -EINVAL; + } + + list_del(&module->list); + kobject_put(&module->kobj); + write_unlock_irqrestore(&modules_lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(gb_audio_manager_remove); + +void gb_audio_manager_remove_all(void) +{ + struct gb_audio_manager_module *module, *next; + int is_empty = 1; + unsigned long flags; + + write_lock_irqsave(&modules_lock, flags); + + list_for_each_entry_safe(module, next, &modules_list, list) { + list_del(&module->list); + kobject_put(&module->kobj); + } + + is_empty = list_empty(&modules_list); + + write_unlock_irqrestore(&modules_lock, flags); + + if (!is_empty) + pr_warn("Not all nodes were deleted\n"); +} +EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all); + +struct gb_audio_manager_module *gb_audio_manager_get_module(int id) +{ + struct gb_audio_manager_module *module; + unsigned long flags; + + read_lock_irqsave(&modules_lock, flags); + module = gb_audio_manager_get_locked(id); + kobject_get(&module->kobj); + read_unlock_irqrestore(&modules_lock, flags); + return module; +} +EXPORT_SYMBOL_GPL(gb_audio_manager_get_module); + +void gb_audio_manager_put_module(struct gb_audio_manager_module *module) +{ + kobject_put(&module->kobj); +} +EXPORT_SYMBOL_GPL(gb_audio_manager_put_module); + +int gb_audio_manager_dump_module(int id) +{ + struct gb_audio_manager_module *module; + unsigned long flags; + + read_lock_irqsave(&modules_lock, flags); + module = gb_audio_manager_get_locked(id); + read_unlock_irqrestore(&modules_lock, flags); + + if (!module) + return -EINVAL; + + gb_audio_manager_module_dump(module); + return 0; +} +EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module); + +void gb_audio_manager_dump_all(void) +{ + struct gb_audio_manager_module *module; + int count = 0; + unsigned long flags; + + read_lock_irqsave(&modules_lock, flags); + list_for_each_entry(module, &modules_list, list) { + gb_audio_manager_module_dump(module); + count++; + } + read_unlock_irqrestore(&modules_lock, flags); + + pr_info("Number of connected modules: %d\n", count); +} +EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all); + +/* + * module init/deinit + */ +static int __init manager_init(void) +{ + manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL, + kernel_kobj); + if (!manager_kset) + return -ENOMEM; + +#ifdef GB_AUDIO_MANAGER_SYSFS + gb_audio_manager_sysfs_init(&manager_kset->kobj); +#endif + + return 0; +} + +static void __exit manager_exit(void) +{ + gb_audio_manager_remove_all(); + kset_unregister(manager_kset); +} + +module_init(manager_init); +module_exit(manager_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Svetlin Ankov "); diff --git a/drivers/staging/greybus/audio_manager.h b/drivers/staging/greybus/audio_manager.h new file mode 100644 index 000000000000..9ca7ac09719e --- /dev/null +++ b/drivers/staging/greybus/audio_manager.h @@ -0,0 +1,82 @@ +/* + * Greybus operations + * + * Copyright 2015-2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef _GB_AUDIO_MANAGER_H_ +#define _GB_AUDIO_MANAGER_H_ + +#include +#include + +#define GB_AUDIO_MANAGER_NAME "gb_audio_manager" +#define GB_AUDIO_MANAGER_MODULE_NAME_LEN 64 +#define GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "63" + +struct gb_audio_manager_module_descriptor { + char name[GB_AUDIO_MANAGER_MODULE_NAME_LEN]; + int slot; + int vid; + int pid; + int cport; + unsigned int devices; +}; + +struct gb_audio_manager_module { + struct kobject kobj; + struct list_head list; + int id; + struct gb_audio_manager_module_descriptor desc; +}; + +/* + * Creates a new gb_audio_manager_module_descriptor, using the specified + * descriptor. + * + * Returns a negative result on error, or the id of the newly created module. + * + */ +int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc); + +/* + * Removes a connected gb_audio_manager_module_descriptor for the specified ID. + * + * Returns zero on success, or a negative value on error. + */ +int gb_audio_manager_remove(int id); + +/* + * Removes all connected gb_audio_modules + * + * Returns zero on success, or a negative value on error. + */ +void gb_audio_manager_remove_all(void); + +/* + * Retrieves a gb_audio_manager_module_descriptor for the specified id. + * Returns the gb_audio_manager_module_descriptor structure, + * or NULL if there is no module with the specified ID. + */ +struct gb_audio_manager_module *gb_audio_manager_get_module(int id); + +/* + * Decreases the refcount of the module, obtained by the get function. + * Modules are removed via gb_audio_manager_remove + */ +void gb_audio_manager_put_module(struct gb_audio_manager_module *module); + +/* + * Dumps the module for the specified id + * Return 0 on success + */ +int gb_audio_manager_dump_module(int id); + +/* + * Dumps all connected modules + */ +void gb_audio_manager_dump_all(void); + +#endif /* _GB_AUDIO_MANAGER_H_ */ diff --git a/drivers/staging/greybus/audio_manager_module.c b/drivers/staging/greybus/audio_manager_module.c new file mode 100644 index 000000000000..e5cffa362671 --- /dev/null +++ b/drivers/staging/greybus/audio_manager_module.c @@ -0,0 +1,240 @@ +/* + * Greybus operations + * + * Copyright 2015-2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include + +#include "audio_manager.h" +#include "audio_manager_private.h" + +#define to_gb_audio_module_attr(x) \ + container_of(x, struct gb_audio_manager_module_attribute, attr) +#define to_gb_audio_module(x) \ + container_of(x, struct gb_audio_manager_module, kobj) + +struct gb_audio_manager_module_attribute { + struct attribute attr; + ssize_t (*show)(struct gb_audio_manager_module *module, + struct gb_audio_manager_module_attribute *attr, + char *buf); + ssize_t (*store)(struct gb_audio_manager_module *module, + struct gb_audio_manager_module_attribute *attr, + const char *buf, size_t count); +}; + +static ssize_t gb_audio_module_attr_show( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct gb_audio_manager_module_attribute *attribute; + struct gb_audio_manager_module *module; + + attribute = to_gb_audio_module_attr(attr); + module = to_gb_audio_module(kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(module, attribute, buf); +} + +static ssize_t gb_audio_module_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t len) +{ + struct gb_audio_manager_module_attribute *attribute; + struct gb_audio_manager_module *module; + + attribute = to_gb_audio_module_attr(attr); + module = to_gb_audio_module(kobj); + + if (!attribute->store) + return -EIO; + + return attribute->store(module, attribute, buf, len); +} + +static const struct sysfs_ops gb_audio_module_sysfs_ops = { + .show = gb_audio_module_attr_show, + .store = gb_audio_module_attr_store, +}; + +static void gb_audio_module_release(struct kobject *kobj) +{ + struct gb_audio_manager_module *module = to_gb_audio_module(kobj); + + pr_info("Destroying audio module #%d\n", module->id); + /* TODO -> delete from list */ + kfree(module); +} + +static ssize_t gb_audio_module_name_show( + struct gb_audio_manager_module *module, + struct gb_audio_manager_module_attribute *attr, char *buf) +{ + return sprintf(buf, "%s", module->desc.name); +} + +static struct gb_audio_manager_module_attribute gb_audio_module_name_attribute = + __ATTR(name, 0664, gb_audio_module_name_show, NULL); + +static ssize_t gb_audio_module_slot_show( + struct gb_audio_manager_module *module, + struct gb_audio_manager_module_attribute *attr, char *buf) +{ + return sprintf(buf, "%d", module->desc.slot); +} + +static struct gb_audio_manager_module_attribute gb_audio_module_slot_attribute = + __ATTR(slot, 0664, gb_audio_module_slot_show, NULL); + +static ssize_t gb_audio_module_vid_show( + struct gb_audio_manager_module *module, + struct gb_audio_manager_module_attribute *attr, char *buf) +{ + return sprintf(buf, "%d", module->desc.vid); +} + +static struct gb_audio_manager_module_attribute gb_audio_module_vid_attribute = + __ATTR(vid, 0664, gb_audio_module_vid_show, NULL); + +static ssize_t gb_audio_module_pid_show( + struct gb_audio_manager_module *module, + struct gb_audio_manager_module_attribute *attr, char *buf) +{ + return sprintf(buf, "%d", module->desc.pid); +} + +static struct gb_audio_manager_module_attribute gb_audio_module_pid_attribute = + __ATTR(pid, 0664, gb_audio_module_pid_show, NULL); + +static ssize_t gb_audio_module_cport_show( + struct gb_audio_manager_module *module, + struct gb_audio_manager_module_attribute *attr, char *buf) +{ + return sprintf(buf, "%d", module->desc.cport); +} + +static struct gb_audio_manager_module_attribute + gb_audio_module_cport_attribute = + __ATTR(cport, 0664, gb_audio_module_cport_show, NULL); + +static ssize_t gb_audio_module_devices_show( + struct gb_audio_manager_module *module, + struct gb_audio_manager_module_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%X", module->desc.devices); +} + +static struct gb_audio_manager_module_attribute + gb_audio_module_devices_attribute = + __ATTR(devices, 0664, gb_audio_module_devices_show, NULL); + +static struct attribute *gb_audio_module_default_attrs[] = { + &gb_audio_module_name_attribute.attr, + &gb_audio_module_slot_attribute.attr, + &gb_audio_module_vid_attribute.attr, + &gb_audio_module_pid_attribute.attr, + &gb_audio_module_cport_attribute.attr, + &gb_audio_module_devices_attribute.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct kobj_type gb_audio_module_type = { + .sysfs_ops = &gb_audio_module_sysfs_ops, + .release = gb_audio_module_release, + .default_attrs = gb_audio_module_default_attrs, +}; + +static void send_add_uevent(struct gb_audio_manager_module *module) +{ + char name_string[128]; + char slot_string[64]; + char vid_string[64]; + char pid_string[64]; + char cport_string[64]; + char devices_string[64]; + + char *envp[] = { + name_string, + slot_string, + vid_string, + pid_string, + cport_string, + devices_string, + NULL + }; + + snprintf(name_string, 128, "NAME=%s", module->desc.name); + snprintf(slot_string, 64, "SLOT=%d", module->desc.slot); + snprintf(vid_string, 64, "VID=%d", module->desc.vid); + snprintf(pid_string, 64, "PID=%d", module->desc.pid); + snprintf(cport_string, 64, "CPORT=%d", module->desc.cport); + snprintf(devices_string, 64, "DEVICES=0x%X", module->desc.devices); + + kobject_uevent_env(&module->kobj, KOBJ_ADD, envp); +} + +int gb_audio_manager_module_create( + struct gb_audio_manager_module **module, + struct kset *manager_kset, + int id, struct gb_audio_manager_module_descriptor *desc) +{ + int err; + struct gb_audio_manager_module *m; + + m = kzalloc(sizeof(*m), GFP_ATOMIC); + if (!m) + return -ENOMEM; + + /* Initialize the node */ + INIT_LIST_HEAD(&m->list); + + /* Set the module id */ + m->id = id; + + /* Copy the provided descriptor */ + memcpy(&m->desc, desc, sizeof(*desc)); + + /* set the kset */ + m->kobj.kset = manager_kset; + + /* + * Initialize and add the kobject to the kernel. All the default files + * will be created here. As we have already specified a kset for this + * kobject, we don't have to set a parent for the kobject, the kobject + * will be placed beneath that kset automatically. + */ + err = kobject_init_and_add(&m->kobj, &gb_audio_module_type, NULL, "%d", + id); + if (err) { + pr_err("failed initializing kobject for audio module #%d\n", + id); + kobject_put(&m->kobj); + return err; + } + + /* + * Notify the object was created + */ + send_add_uevent(m); + + *module = m; + pr_info("Created audio module #%d\n", id); + return 0; +} + +void gb_audio_manager_module_dump(struct gb_audio_manager_module *module) +{ + pr_info("audio module #%d name=%s slot=%d vid=%d pid=%d cport=%d devices=0x%X\n", + module->id, + module->desc.name, + module->desc.slot, + module->desc.vid, + module->desc.pid, + module->desc.cport, + module->desc.devices); +} diff --git a/drivers/staging/greybus/audio_manager_private.h b/drivers/staging/greybus/audio_manager_private.h new file mode 100644 index 000000000000..079ce953c256 --- /dev/null +++ b/drivers/staging/greybus/audio_manager_private.h @@ -0,0 +1,28 @@ +/* + * Greybus operations + * + * Copyright 2015-2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef _GB_AUDIO_MANAGER_PRIVATE_H_ +#define _GB_AUDIO_MANAGER_PRIVATE_H_ + +#include + +#include "audio_manager.h" + +int gb_audio_manager_module_create( + struct gb_audio_manager_module **module, + struct kset *manager_kset, + int id, struct gb_audio_manager_module_descriptor *desc); + +/* module destroyed via kobject_put */ + +void gb_audio_manager_module_dump(struct gb_audio_manager_module *module); + +/* sysfs control */ +void gb_audio_manager_sysfs_init(struct kobject *kobj); + +#endif /* _GB_AUDIO_MANAGER_PRIVATE_H_ */ diff --git a/drivers/staging/greybus/audio_manager_sysfs.c b/drivers/staging/greybus/audio_manager_sysfs.c new file mode 100644 index 000000000000..c713f5f7aaca --- /dev/null +++ b/drivers/staging/greybus/audio_manager_sysfs.c @@ -0,0 +1,101 @@ +/* + * Greybus operations + * + * Copyright 2015-2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include + +#include "audio_manager.h" +#include "audio_manager_private.h" + +static ssize_t manager_sysfs_add_store( + struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct gb_audio_manager_module_descriptor desc = { {0} }; + + int num = sscanf(buf, + "name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s " + "slot=%d vid=%d pid=%d cport=%d devices=0x%X", + desc.name, &desc.slot, &desc.vid, &desc.pid, + &desc.cport, &desc.devices); + + if (num != 6) + return -EINVAL; + + num = gb_audio_manager_add(&desc); + if (num < 0) + return -EINVAL; + + return count; +} + +static struct kobj_attribute manager_add_attribute = + __ATTR(add, 0664, NULL, manager_sysfs_add_store); + +static ssize_t manager_sysfs_remove_store( + struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int id; + + int num = sscanf(buf, "%d", &id); + + if (num != 1) + return -EINVAL; + + num = gb_audio_manager_remove(id); + if (num) + return num; + + return count; +} + +static struct kobj_attribute manager_remove_attribute = + __ATTR(remove, 0664, NULL, manager_sysfs_remove_store); + +static ssize_t manager_sysfs_dump_store( + struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int id; + + int num = sscanf(buf, "%d", &id); + + if (num == 1) { + num = gb_audio_manager_dump_module(id); + if (num) + return num; + } else if (!strncmp("all", buf, 3)) + gb_audio_manager_dump_all(); + else + return -EINVAL; + + return count; +} + +static struct kobj_attribute manager_dump_attribute = + __ATTR(dump, 0664, NULL, manager_sysfs_dump_store); + +static void manager_sysfs_init_attribute( + struct kobject *kobj, struct kobj_attribute *kattr) +{ + int err; + + err = sysfs_create_file(kobj, &kattr->attr); + if (err) { + pr_warn("creating the sysfs entry for %s failed: %d\n", + kattr->attr.name, err); + } +} + +void gb_audio_manager_sysfs_init(struct kobject *kobj) +{ + manager_sysfs_init_attribute(kobj, &manager_add_attribute); + manager_sysfs_init_attribute(kobj, &manager_remove_attribute); + manager_sysfs_init_attribute(kobj, &manager_dump_attribute); +} -- cgit v1.2.3-59-g8ed1b From 7885342cc2727e2e9acae1597ebc88bedfea8464 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Wed, 13 Jan 2016 14:07:49 -0700 Subject: greybus: audio: Build audio module conditionally Added CONFIG_XXX flag check before compiling audio module. Once we add dynamic DAI link registration from audio driver, this check wil be required to avoid compilation failures with other kernel revisions. Also, renamed header file to better align with .c file name. Signed-off-by: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 3 + drivers/staging/greybus/audio.h | 149 ---------------------------------- drivers/staging/greybus/audio_codec.c | 2 +- drivers/staging/greybus/audio_codec.h | 146 +++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 150 deletions(-) delete mode 100644 drivers/staging/greybus/audio.h create mode 100644 drivers/staging/greybus/audio_codec.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 4ebdc6b9c89f..6759d684027c 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -47,6 +47,9 @@ obj-m += gb-raw.o obj-m += gb-es2.o obj-m += gb-arche.o obj-m += gb-audio-codec.o +ifeq ($(CONFIG_SND_SOC_DYNAMIC_DAILINK),y) + obj-m += gb-audio-codec.o +endif obj-m += gb-camera.o obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o diff --git a/drivers/staging/greybus/audio.h b/drivers/staging/greybus/audio.h deleted file mode 100644 index fca60af3ddc1..000000000000 --- a/drivers/staging/greybus/audio.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Greybus audio driver - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __LINUX_GBAUDIO_H -#define __LINUX_GBAUDIO_H - -#ifdef __KERNEL__ - -#include - -#include "greybus.h" -#include "greybus_protocols.h" - -#define NAME_SIZE 32 - -enum { - APB1_PCM = 0, - APB2_PCM, - NUM_CODEC_DAIS, -}; - -enum gbcodec_reg_index { - GBCODEC_CTL_REG, - GBCODEC_MUTE_REG, - GBCODEC_PB_LVOL_REG, - GBCODEC_PB_RVOL_REG, - GBCODEC_CAP_LVOL_REG, - GBCODEC_CAP_RVOL_REG, - GBCODEC_APB1_MUX_REG, - GBCODEC_APB2_MUX_REG, - GBCODEC_REG_COUNT -}; - -/* bit 0-SPK, 1-HP, 2-DAC, - * 4-MIC, 5-HSMIC, 6-MIC2 - */ -#define GBCODEC_CTL_REG_DEFAULT 0x00 - -/* bit 0,1 - APB1-PB-L/R - * bit 2,3 - APB2-PB-L/R - * bit 4,5 - APB1-Cap-L/R - * bit 6,7 - APB2-Cap-L/R - */ -#define GBCODEC_MUTE_REG_DEFAULT 0x00 - -/* 0-127 steps */ -#define GBCODEC_PB_VOL_REG_DEFAULT 0x00 -#define GBCODEC_CAP_VOL_REG_DEFAULT 0x00 - -/* bit 0,1,2 - PB stereo, left, right - * bit 8,9,10 - Cap stereo, left, right - */ -#define GBCODEC_APB1_MUX_REG_DEFAULT 0x00 -#define GBCODEC_APB2_MUX_REG_DEFAULT 0x00 - -static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { - GBCODEC_CTL_REG_DEFAULT, - GBCODEC_MUTE_REG_DEFAULT, - GBCODEC_PB_VOL_REG_DEFAULT, - GBCODEC_PB_VOL_REG_DEFAULT, - GBCODEC_CAP_VOL_REG_DEFAULT, - GBCODEC_CAP_VOL_REG_DEFAULT, - GBCODEC_APB1_MUX_REG_DEFAULT, - GBCODEC_APB2_MUX_REG_DEFAULT, -}; - -struct gbaudio_codec_info { - struct snd_soc_codec *codec; - - bool usable; - u8 reg[GBCODEC_REG_COUNT]; - int registered; - - int num_kcontrols; - int num_dapm_widgets; - int num_dapm_routes; - struct snd_kcontrol_new *kctls; - struct snd_soc_dapm_widget *widgets; - struct snd_soc_dapm_route *routes; - struct mutex lock; -}; - -extern int gb_audio_gb_get_topology(struct gb_connection *connection, - struct gb_audio_topology **topology); -extern int gb_audio_gb_get_control(struct gb_connection *connection, - uint8_t control_id, uint8_t index, - struct gb_audio_ctl_elem_value *value); -extern int gb_audio_gb_set_control(struct gb_connection *connection, - uint8_t control_id, uint8_t index, - struct gb_audio_ctl_elem_value *value); -extern int gb_audio_gb_enable_widget(struct gb_connection *connection, - uint8_t widget_id); -extern int gb_audio_gb_disable_widget(struct gb_connection *connection, - uint8_t widget_id); -extern int gb_audio_gb_get_pcm(struct gb_connection *connection, - uint16_t data_cport, uint32_t *format, - uint32_t *rate, uint8_t *channels, - uint8_t *sig_bits); -extern int gb_audio_gb_set_pcm(struct gb_connection *connection, - uint16_t data_cport, uint32_t format, - uint32_t rate, uint8_t channels, - uint8_t sig_bits); -extern int gb_audio_gb_set_tx_data_size(struct gb_connection *connection, - uint16_t data_cport, uint16_t size); -extern int gb_audio_gb_get_tx_delay(struct gb_connection *connection, - uint16_t data_cport, uint32_t *delay); -extern int gb_audio_gb_activate_tx(struct gb_connection *connection, - uint16_t data_cport); -extern int gb_audio_gb_deactivate_tx(struct gb_connection *connection, - uint16_t data_cport); -extern int gb_audio_gb_set_rx_data_size(struct gb_connection *connection, - uint16_t data_cport, uint16_t size); -extern int gb_audio_gb_get_rx_delay(struct gb_connection *connection, - uint16_t data_cport, uint32_t *delay); -extern int gb_audio_gb_activate_rx(struct gb_connection *connection, - uint16_t data_cport); -extern int gb_audio_gb_deactivate_rx(struct gb_connection *connection, - uint16_t data_cport); -extern int gb_audio_apbridgea_set_config(struct gb_connection *connection, - __u16 i2s_port, __u32 format, - __u32 rate, __u32 mclk_freq); -extern int gb_audio_apbridgea_register_cport(struct gb_connection *connection, - __u16 i2s_port, __u16 cportid); -extern int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, - __u16 i2s_port, __u16 cportid); -extern int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, - __u16 i2s_port, __u16 size); -extern int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, - __u16 i2s_port, __u32 *delay); -extern int gb_audio_apbridgea_start_tx(struct gb_connection *connection, - __u16 i2s_port, __u64 timestamp); -extern int gb_audio_apbridgea_stop_tx(struct gb_connection *connection, - __u16 i2s_port); -extern int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, - __u16 i2s_port, __u16 size); -extern int gb_audio_apbridgea_get_rx_delay(struct gb_connection *connection, - __u16 i2s_port, __u32 *delay); -extern int gb_audio_apbridgea_start_rx(struct gb_connection *connection, - __u16 i2s_port); -extern int gb_audio_apbridgea_stop_rx(struct gb_connection *connection, - __u16 i2s_port); - -#endif /* __KERNEL__ */ -#endif /* __LINUX_GBAUDIO_H */ diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 2bc23095ffd0..826604ae64df 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -7,7 +7,7 @@ */ #include -#include "audio.h" +#include "audio_codec.h" static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h new file mode 100644 index 000000000000..f8597b3de808 --- /dev/null +++ b/drivers/staging/greybus/audio_codec.h @@ -0,0 +1,146 @@ +/* + * Greybus audio driver + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __LINUX_GBAUDIO_CODEC_H +#define __LINUX_GBAUDIO_CODEC_H + +#include + +#include "greybus.h" +#include "greybus_protocols.h" + +#define NAME_SIZE 32 + +enum { + APB1_PCM = 0, + APB2_PCM, + NUM_CODEC_DAIS, +}; + +enum gbcodec_reg_index { + GBCODEC_CTL_REG, + GBCODEC_MUTE_REG, + GBCODEC_PB_LVOL_REG, + GBCODEC_PB_RVOL_REG, + GBCODEC_CAP_LVOL_REG, + GBCODEC_CAP_RVOL_REG, + GBCODEC_APB1_MUX_REG, + GBCODEC_APB2_MUX_REG, + GBCODEC_REG_COUNT +}; + +/* bit 0-SPK, 1-HP, 2-DAC, + * 4-MIC, 5-HSMIC, 6-MIC2 + */ +#define GBCODEC_CTL_REG_DEFAULT 0x00 + +/* bit 0,1 - APB1-PB-L/R + * bit 2,3 - APB2-PB-L/R + * bit 4,5 - APB1-Cap-L/R + * bit 6,7 - APB2-Cap-L/R + */ +#define GBCODEC_MUTE_REG_DEFAULT 0x00 + +/* 0-127 steps */ +#define GBCODEC_PB_VOL_REG_DEFAULT 0x00 +#define GBCODEC_CAP_VOL_REG_DEFAULT 0x00 + +/* bit 0,1,2 - PB stereo, left, right + * bit 8,9,10 - Cap stereo, left, right + */ +#define GBCODEC_APB1_MUX_REG_DEFAULT 0x00 +#define GBCODEC_APB2_MUX_REG_DEFAULT 0x00 + +static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { + GBCODEC_CTL_REG_DEFAULT, + GBCODEC_MUTE_REG_DEFAULT, + GBCODEC_PB_VOL_REG_DEFAULT, + GBCODEC_PB_VOL_REG_DEFAULT, + GBCODEC_CAP_VOL_REG_DEFAULT, + GBCODEC_CAP_VOL_REG_DEFAULT, + GBCODEC_APB1_MUX_REG_DEFAULT, + GBCODEC_APB2_MUX_REG_DEFAULT, +}; + +struct gbaudio_codec_info { + struct snd_soc_codec *codec; + + bool usable; + u8 reg[GBCODEC_REG_COUNT]; + int registered; + + int num_kcontrols; + int num_dapm_widgets; + int num_dapm_routes; + struct snd_kcontrol_new *kctls; + struct snd_soc_dapm_widget *widgets; + struct snd_soc_dapm_route *routes; + struct mutex lock; +}; + +extern int gb_audio_gb_get_topology(struct gb_connection *connection, + struct gb_audio_topology **topology); +extern int gb_audio_gb_get_control(struct gb_connection *connection, + uint8_t control_id, uint8_t index, + struct gb_audio_ctl_elem_value *value); +extern int gb_audio_gb_set_control(struct gb_connection *connection, + uint8_t control_id, uint8_t index, + struct gb_audio_ctl_elem_value *value); +extern int gb_audio_gb_enable_widget(struct gb_connection *connection, + uint8_t widget_id); +extern int gb_audio_gb_disable_widget(struct gb_connection *connection, + uint8_t widget_id); +extern int gb_audio_gb_get_pcm(struct gb_connection *connection, + uint16_t data_cport, uint32_t *format, + uint32_t *rate, uint8_t *channels, + uint8_t *sig_bits); +extern int gb_audio_gb_set_pcm(struct gb_connection *connection, + uint16_t data_cport, uint32_t format, + uint32_t rate, uint8_t channels, + uint8_t sig_bits); +extern int gb_audio_gb_set_tx_data_size(struct gb_connection *connection, + uint16_t data_cport, uint16_t size); +extern int gb_audio_gb_get_tx_delay(struct gb_connection *connection, + uint16_t data_cport, uint32_t *delay); +extern int gb_audio_gb_activate_tx(struct gb_connection *connection, + uint16_t data_cport); +extern int gb_audio_gb_deactivate_tx(struct gb_connection *connection, + uint16_t data_cport); +extern int gb_audio_gb_set_rx_data_size(struct gb_connection *connection, + uint16_t data_cport, uint16_t size); +extern int gb_audio_gb_get_rx_delay(struct gb_connection *connection, + uint16_t data_cport, uint32_t *delay); +extern int gb_audio_gb_activate_rx(struct gb_connection *connection, + uint16_t data_cport); +extern int gb_audio_gb_deactivate_rx(struct gb_connection *connection, + uint16_t data_cport); +extern int gb_audio_apbridgea_set_config(struct gb_connection *connection, + __u16 i2s_port, __u32 format, + __u32 rate, __u32 mclk_freq); +extern int gb_audio_apbridgea_register_cport(struct gb_connection *connection, + __u16 i2s_port, __u16 cportid); +extern int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, + __u16 i2s_port, __u16 cportid); +extern int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, + __u16 i2s_port, __u16 size); +extern int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, + __u16 i2s_port, __u32 *delay); +extern int gb_audio_apbridgea_start_tx(struct gb_connection *connection, + __u16 i2s_port, __u64 timestamp); +extern int gb_audio_apbridgea_stop_tx(struct gb_connection *connection, + __u16 i2s_port); +extern int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, + __u16 i2s_port, __u16 size); +extern int gb_audio_apbridgea_get_rx_delay(struct gb_connection *connection, + __u16 i2s_port, __u32 *delay); +extern int gb_audio_apbridgea_start_rx(struct gb_connection *connection, + __u16 i2s_port); +extern int gb_audio_apbridgea_stop_rx(struct gb_connection *connection, + __u16 i2s_port); + +#endif /* __LINUX_GBAUDIO_CODEC_H */ -- cgit v1.2.3-59-g8ed1b From 86a685dd453547431794a5c332a9ed742916c9d4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 Jan 2016 16:18:36 -0800 Subject: greybus: Makefile: fix up build test for gb-audio-codec.ko We really only want to build the module if the config is set, not all the time like the current code does. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 6759d684027c..936489758533 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -46,7 +46,6 @@ obj-m += gb-hid.o obj-m += gb-raw.o obj-m += gb-es2.o obj-m += gb-arche.o -obj-m += gb-audio-codec.o ifeq ($(CONFIG_SND_SOC_DYNAMIC_DAILINK),y) obj-m += gb-audio-codec.o endif -- cgit v1.2.3-59-g8ed1b From 2a70e49f9183d72287e84ac4d6a4080e3f2a6475 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Wed, 13 Jan 2016 14:07:50 -0700 Subject: greybus: audio: Use greybus connection device for codec registration Use GB Audio mgmt, data protocol ids to register codec module with GB protocol. And in response to mgmt->connection_init(), register GB codec driver with ASoC. Now, using msm8994 machine to register DAI link dynamically on codec insertion. ToDos: - snd_soc_register_codec() uses driver->name to identify device id. However, for GB device, .driver{} is not yet populated by GB core. Thus, defining dummy structure within codec driver. This should come from GB core itself. Even existing .driver{} may cause problem in case of multiple modules inserted or inserted at a different slot. - Fix logic for gbcodec->dais & gbcodec->dailinks. Current implementation contains some hard coded data with assumption of count=1. - Evaluate definition of 'gbaudio_dailink.be_id' in case of multiple DAI links. Signed-off-by: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 426 +++++++++++++++++++++++++++++++--- drivers/staging/greybus/audio_codec.h | 41 +++- 2 files changed, 432 insertions(+), 35 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 826604ae64df..fd94042d3f33 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -5,10 +5,20 @@ * * Released under the GPLv2 only. */ +#include #include +#include +#include +#include #include "audio_codec.h" +#define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt" +#define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data" + +static DEFINE_MUTEX(gb_codec_list_lock); +static LIST_HEAD(gb_codec_list); + static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -190,10 +200,7 @@ static struct snd_soc_dai_driver gbcodec_dai = { static int gbcodec_probe(struct snd_soc_codec *codec) { - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); - - gbcodec->codec = codec; - + /* Empty function for now */ return 0; } @@ -263,53 +270,412 @@ static struct snd_soc_codec_driver soc_codec_dev_gbcodec = { .num_dapm_routes = ARRAY_SIZE(gbcodec_dapm_routes), }; -static int gbaudio_codec_probe(struct platform_device *pdev) +/* + * GB codec DAI link related + */ +static struct snd_soc_dai_link gbaudio_dailink = { + .name = "PRI_MI2S_RX", + .stream_name = "Primary MI2S Playback", + .platform_name = "msm-pcm-routing", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .no_pcm = 1, + .be_id = 34, +}; + +static void gbaudio_remove_dailinks(struct gbaudio_codec_info *gbcodec) +{ + int i; + + for (i = 0; i < gbcodec->num_dai_links; i++) { + dev_dbg(gbcodec->dev, "Remove %s: DAI link\n", + gbcodec->dailink_name[i]); + devm_kfree(gbcodec->dev, gbcodec->dailink_name[i]); + gbcodec->dailink_name[i] = NULL; + } + gbcodec->num_dai_links = 0; +} + +static int gbaudio_add_dailinks(struct gbaudio_codec_info *gbcodec) +{ + int ret, i; + char *dai_link_name; + struct snd_soc_dai_link *dai; + struct device *dev = gbcodec->dev; + + dai = &gbaudio_dailink; + dai->codec_name = gbcodec->name; + + /* FIXME + * allocate memory for DAI links based on count. + * currently num_dai_links=1, so using static struct + */ + gbcodec->num_dai_links = 1; + + for (i = 0; i < gbcodec->num_dai_links; i++) { + gbcodec->dailink_name[i] = dai_link_name = + devm_kzalloc(dev, NAME_SIZE, GFP_KERNEL); + snprintf(dai_link_name, NAME_SIZE, "GB %d.%d PRI_MI2S_RX", + gbcodec->dev_id, i); + dai->name = dai_link_name; + dai->codec_dai_name = gbcodec->dais[i].name; + } + + ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", dai, 1); + if (ret) { + dev_err(dev, "%d:Error while adding DAI link\n", ret); + goto err_dai_link; + } + + return ret; + +err_dai_link: + gbcodec->num_dai_links = i; + gbaudio_remove_dailinks(gbcodec); + return ret; +} + +/* + * gb_snd management functions + */ +static struct gbaudio_codec_info *gbaudio_find_codec(struct device *dev, + int dev_id) +{ + struct gbaudio_codec_info *tmp, *ret; + + mutex_lock(&gb_codec_list_lock); + list_for_each_entry_safe(ret, tmp, &gb_codec_list, list) { + dev_dbg(dev, "%d:device found\n", ret->dev_id); + if (ret->dev_id == dev_id) { + mutex_unlock(&gb_codec_list_lock); + return ret; + } + } + mutex_unlock(&gb_codec_list_lock); + return NULL; +} + +static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev, + int dev_id) +{ + struct gbaudio_codec_info *gbcodec; + + gbcodec = gbaudio_find_codec(dev, dev_id); + if (gbcodec) + return gbcodec; + + gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL); + if (!gbcodec) + return NULL; + + mutex_init(&gbcodec->lock); + INIT_LIST_HEAD(&gbcodec->dai_list); + gbcodec->dev_id = dev_id; + dev_set_drvdata(dev, gbcodec); + gbcodec->dev = dev; + strlcpy(gbcodec->name, dev_name(dev), NAME_SIZE); + + mutex_lock(&gb_codec_list_lock); + list_add(&gbcodec->list, &gb_codec_list); + mutex_unlock(&gb_codec_list_lock); + dev_dbg(dev, "%d:%s Added to codec list\n", gbcodec->dev_id, + gbcodec->name); + + return gbcodec; +} + +static void gbaudio_free_codec(struct device *dev, + struct gbaudio_codec_info *gbcodec) +{ + mutex_lock(&gb_codec_list_lock); + if (!gbcodec->mgmt_connection && + list_empty(&gbcodec->dai_list)) { + list_del(&gbcodec->list); + mutex_unlock(&gb_codec_list_lock); + dev_set_drvdata(dev, NULL); + devm_kfree(dev, gbcodec); + } else { + mutex_unlock(&gb_codec_list_lock); + } +} + +/* + * This is the basic hook get things initialized and registered w/ gb + */ + +/* + * GB codec module driver ops + */ +struct device_driver gb_codec_driver = { + .name = "1-8", + .owner = THIS_MODULE, +}; + +static int gbaudio_codec_probe(struct gb_connection *connection) { int ret; struct gbaudio_codec_info *gbcodec; - char dai_name[NAME_SIZE]; + struct device *dev = &connection->bundle->dev; + int dev_id = connection->bundle->id; - gbcodec = devm_kzalloc(&pdev->dev, sizeof(struct gbaudio_codec_info), - GFP_KERNEL); + dev_dbg(dev, "Add device:%d:%s\n", dev_id, dev_name(dev)); + /* get gbcodec data */ + gbcodec = gbaudio_get_codec(dev, dev_id); if (!gbcodec) return -ENOMEM; - platform_set_drvdata(pdev, gbcodec); - snprintf(dai_name, NAME_SIZE, "%s.%d", "gbcodec_pcm", pdev->id); - gbcodec_dai.name = dai_name; + gbcodec->mgmt_connection = connection; + + /* update DAI info */ + gbcodec->dais = &gbcodec_dai; + /* FIXME */ + dev->driver = &gb_codec_driver; + + /* register codec */ + ret = snd_soc_register_codec(dev, &soc_codec_dev_gbcodec, + gbcodec->dais, 1); + if (ret) { + dev_err(dev, "%d:Failed to register codec\n", ret); + goto base_error; + } + + /* update DAI links in response to this codec */ + ret = gbaudio_add_dailinks(gbcodec); + if (ret) { + dev_err(dev, "%d: Failed to add DAI links\n", ret); + goto codec_reg_error; + } + + /* set registered flag */ + mutex_lock(&gbcodec->lock); + gbcodec->codec_registered = 1; - ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbcodec, - &gbcodec_dai, 1); - if (!ret) - gbcodec->registered = 1; + mutex_unlock(&gbcodec->lock); return ret; + +codec_reg_error: + snd_soc_unregister_codec(dev); +base_error: + dev->driver = NULL; + gbcodec->mgmt_connection = NULL; + return ret; } -static const struct of_device_id gbcodec_of_match[] = { - { .compatible = "greybus,codec", }, - {}, +static void gbaudio_codec_remove(struct gb_connection *connection) +{ + struct gbaudio_codec_info *gbcodec; + struct device *dev = &connection->bundle->dev; + int dev_id = connection->bundle->id; + + dev_dbg(dev, "Remove device:%d:%s\n", dev_id, dev_name(dev)); + + /* get gbcodec data */ + gbcodec = gbaudio_find_codec(dev, dev_id); + if (!gbcodec) + return; + + msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink, + 1); + gbaudio_remove_dailinks(gbcodec); + + snd_soc_unregister_codec(dev); + dev->driver = NULL; + gbcodec->mgmt_connection = NULL; + mutex_lock(&gbcodec->lock); + gbcodec->codec_registered = 0; + mutex_unlock(&gbcodec->lock); + gbaudio_free_codec(dev, gbcodec); +} + +static int gbaudio_codec_report_event_recv(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_audio_streaming_event_request *req = op->request->payload; + + dev_warn(&connection->bundle->dev, + "Audio Event received: cport: %u, event: %u\n", + req->data_cport, req->event); + + return 0; +} + +static struct gb_protocol gb_audio_mgmt_protocol = { + .name = GB_AUDIO_MGMT_DRIVER_NAME, + .id = GREYBUS_PROTOCOL_AUDIO_MGMT, + .major = 0, + .minor = 1, + .connection_init = gbaudio_codec_probe, + .connection_exit = gbaudio_codec_remove, + .request_recv = gbaudio_codec_report_event_recv, }; -static int gbaudio_codec_remove(struct platform_device *pdev) +static struct gbaudio_dai *gbaudio_allocate_dai(struct gbaudio_codec_info *gb, + int data_cport, + struct gb_connection *connection, + const char *name) +{ + struct gbaudio_dai *dai; + + mutex_lock(&gb->lock); + dai = devm_kzalloc(gb->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) { + dev_err(gb->dev, "%s:DAI Malloc failure\n", name); + mutex_unlock(&gb->lock); + return NULL; + } + + dai->data_cport = data_cport; + dai->connection = connection; + + /* update name */ + if (name) + strlcpy(dai->name, name, NAME_SIZE); + list_add(&dai->list, &gb->dai_list); + dev_dbg(gb->dev, "%d:%s: DAI added\n", data_cport, dai->name); + mutex_unlock(&gb->lock); + + return dai; +} + +struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec, + int data_cport, + struct gb_connection *connection, + const char *name) +{ + struct gbaudio_dai *dai, *_dai; + + /* FIXME need to take care for multiple DAIs */ + mutex_lock(&gbcodec->lock); + if (list_empty(&gbcodec->dai_list)) { + mutex_unlock(&gbcodec->lock); + return gbaudio_allocate_dai(gbcodec, data_cport, connection, + name); + } + + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { + if (dai->data_cport == data_cport) { + if (connection) + dai->connection = connection; + + if (name) + strlcpy(dai->name, name, NAME_SIZE); + dev_dbg(gbcodec->dev, "%d:%s: DAI updated\n", + data_cport, dai->name); + mutex_unlock(&gbcodec->lock); + return dai; + } + } + + dev_err(gbcodec->dev, "%s:DAI not found\n", name); + mutex_unlock(&gbcodec->lock); + return NULL; +} + +static int gbaudio_dai_probe(struct gb_connection *connection) { - snd_soc_unregister_codec(&pdev->dev); + struct gbaudio_dai *dai; + struct device *dev = &connection->bundle->dev; + int dev_id = connection->bundle->id; + struct gbaudio_codec_info *gbcodec = dev_get_drvdata(dev); + + dev_dbg(dev, "Add DAI device:%d:%s\n", dev_id, dev_name(dev)); + + /* get gbcodec data */ + gbcodec = gbaudio_get_codec(dev, dev_id); + if (!gbcodec) + return -ENOMEM; + + /* add/update dai_list*/ + dai = gbaudio_add_dai(gbcodec, connection->intf_cport_id, connection, + NULL); + if (!dai) + return -ENOMEM; + + /* update dai_added count */ + mutex_lock(&gbcodec->lock); + gbcodec->dai_added++; + mutex_unlock(&gbcodec->lock); return 0; } -static struct platform_driver gbaudio_codec_driver = { - .driver = { - .name = "gbaudio-codec", - .owner = THIS_MODULE, - .of_match_table = gbcodec_of_match, - }, - .probe = gbaudio_codec_probe, - .remove = gbaudio_codec_remove, +static void gbaudio_dai_remove(struct gb_connection *connection) +{ + struct device *dev = &connection->bundle->dev; + int dev_id = connection->bundle->id; + struct gbaudio_codec_info *gbcodec; + + dev_dbg(dev, "Remove DAI device:%d:%s\n", dev_id, dev_name(dev)); + + /* get gbcodec data */ + gbcodec = gbaudio_find_codec(dev, dev_id); + if (!gbcodec) + return; + + /* inform uevent to above layers */ + mutex_lock(&gbcodec->lock); + /* update dai_added count */ + gbcodec->dai_added--; + mutex_unlock(&gbcodec->lock); + + gbaudio_free_codec(dev, gbcodec); +} + +static int gbaudio_dai_report_event_recv(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + + dev_warn(&connection->bundle->dev, "Audio Event received\n"); + + return 0; +} + +static struct gb_protocol gb_audio_data_protocol = { + .name = GB_AUDIO_DATA_DRIVER_NAME, + .id = GREYBUS_PROTOCOL_AUDIO_DATA, + .major = 0, + .minor = 1, + .connection_init = gbaudio_dai_probe, + .connection_exit = gbaudio_dai_remove, + .request_recv = gbaudio_dai_report_event_recv, }; -module_platform_driver(gbaudio_codec_driver); -MODULE_DESCRIPTION("Greybus Audio virtual codec driver"); +/* + * This is the basic hook get things initialized and registered w/ gb + */ + +static int __init gb_audio_protocol_init(void) +{ + int err; + + err = gb_protocol_register(&gb_audio_mgmt_protocol); + if (err) { + pr_err("Can't register i2s mgmt protocol driver: %d\n", -err); + return err; + } + + err = gb_protocol_register(&gb_audio_data_protocol); + if (err) { + pr_err("Can't register Audio protocol driver: %d\n", -err); + goto err_unregister_audio_mgmt; + } + + return 0; + +err_unregister_audio_mgmt: + gb_protocol_deregister(&gb_audio_mgmt_protocol); + return err; +} +module_init(gb_audio_protocol_init); + +static void __exit gb_audio_protocol_exit(void) +{ + gb_protocol_deregister(&gb_audio_data_protocol); + gb_protocol_deregister(&gb_audio_mgmt_protocol); +} +module_exit(gb_audio_protocol_exit); + +MODULE_DESCRIPTION("Greybus Audio codec driver"); MODULE_AUTHOR("Vaibhav Agarwal "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:gbaudio-codec"); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index f8597b3de808..39bd995719c2 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -15,6 +15,7 @@ #include "greybus_protocols.h" #define NAME_SIZE 32 +#define MAX_DAIS 2 /* APB1, APB2 */ enum { APB1_PCM = 0, @@ -67,24 +68,54 @@ static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { GBCODEC_APB2_MUX_REG_DEFAULT, }; +struct gbaudio_dai { + __le16 data_cport; + char name[NAME_SIZE]; + struct gb_connection *connection; + struct list_head list; +}; + struct gbaudio_codec_info { + /* module info */ + int dev_id; /* check if it should be bundle_id/hd_cport_id */ + int vid; + int pid; + int slot; + int type; + int dai_added; + int codec_registered; + char vstr[NAME_SIZE]; + char pstr[NAME_SIZE]; + struct list_head list; + char name[NAME_SIZE]; + + /* soc related data */ struct snd_soc_codec *codec; - - bool usable; + struct device *dev; u8 reg[GBCODEC_REG_COUNT]; - int registered; + /* dai_link related */ + char card_name[NAME_SIZE]; + char *dailink_name[MAX_DAIS]; + int num_dai_links; + + /* topology related */ + struct gb_connection *mgmt_connection; + int num_dais; int num_kcontrols; int num_dapm_widgets; int num_dapm_routes; struct snd_kcontrol_new *kctls; struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *routes; + struct snd_soc_dai_driver *dais; + + /* lists */ + struct list_head dai_list; struct mutex lock; }; -extern int gb_audio_gb_get_topology(struct gb_connection *connection, - struct gb_audio_topology **topology); +/* protocol related */ extern int gb_audio_gb_get_control(struct gb_connection *connection, uint8_t control_id, uint8_t index, struct gb_audio_ctl_elem_value *value); -- cgit v1.2.3-59-g8ed1b From 6339d2322c47f4b8ebabf9daf0130328ed72648b Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Wed, 13 Jan 2016 14:07:51 -0700 Subject: greybus: audio: Add topology parser for GB codec For each GB codec module inserted, DAPM widgets, kcontrols, routes and DAIs can be fetched through greybus in a binary chunk and parsed locally to create & populate DAPM graph for the specific module. It is required by each codec module to populate a minimum set of kcontrols with fixed names to support basic audio usecase. To support advanced features of codec module, the same can be polpulated with existing topology parser. However, to use them for different usecase separate mechanism (may be via MSP) is required to inform userspace about their configuration value & enable/disable sequence. ToDos: Currently, support for enumerated kcontrol/dapm control is hardcoded. Need to add complete logic within the parser. Signed-off-by: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/audio_codec.c | 254 ++----- drivers/staging/greybus/audio_codec.h | 31 + drivers/staging/greybus/audio_topology.c | 1114 ++++++++++++++++++++++++++++++ 4 files changed, 1191 insertions(+), 210 deletions(-) create mode 100644 drivers/staging/greybus/audio_topology.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 936489758533..b1272cbd55ee 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -29,7 +29,7 @@ gb-raw-y := raw.o gb-hid-y := hid.o gb-es2-y := es2.o gb-arche-y := arche-platform.o arche-apb-ctrl.o -gb-audio-codec-y := audio_codec.o +gb-audio-codec-y := audio_codec.o audio_topology.o gb-audio-gb-y := audio_gb.o gb-audio-apbridgea-y := audio_apbridgea.o gb-audio-manager-y += audio_manager.o diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index fd94042d3f33..239a9b6b947c 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -19,124 +19,6 @@ static DEFINE_MUTEX(gb_codec_list_lock); static LIST_HEAD(gb_codec_list); -static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - /* Ensure GB speaker is connected */ - - return 0; -} - -static int gbcodec_event_hp(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - /* Ensure GB module supports jack slot */ - - return 0; -} - -static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - /* Ensure GB module supports jack slot */ - - return 0; -} - -static const struct snd_kcontrol_new gbcodec_snd_controls[] = { - SOC_DOUBLE("Playback Mute", GBCODEC_MUTE_REG, 0, 1, 1, 1), - SOC_DOUBLE("Capture Mute", GBCODEC_MUTE_REG, 4, 5, 1, 1), - SOC_DOUBLE_R("Playback Volume", GBCODEC_PB_LVOL_REG, - GBCODEC_PB_RVOL_REG, 0, 127, 0), - SOC_DOUBLE_R("Capture Volume", GBCODEC_CAP_LVOL_REG, - GBCODEC_CAP_RVOL_REG, 0, 127, 0), -}; - -static const struct snd_kcontrol_new spk_amp_ctl = - SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 0, 1, 0); - -static const struct snd_kcontrol_new hp_amp_ctl = - SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 1, 1, 0); - -static const struct snd_kcontrol_new mic_adc_ctl = - SOC_DAPM_SINGLE("Switch", GBCODEC_CTL_REG, 4, 1, 0); - -/* APB1-GBSPK source */ -static const char * const gbcodec_apb1_src[] = {"Stereo", "Left", "Right"}; - -static const SOC_ENUM_SINGLE_DECL( - gbcodec_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbcodec_apb1_src); - -static const struct snd_kcontrol_new gbcodec_apb1_rx_mux = - SOC_DAPM_ENUM("APB1 source", gbcodec_apb1_rx_enum); - -static const SOC_ENUM_SINGLE_DECL( - gbcodec_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbcodec_apb1_src); - -static const struct snd_kcontrol_new gbcodec_mic_mux = - SOC_DAPM_ENUM("MIC source", gbcodec_mic_enum); - -static const struct snd_soc_dapm_widget gbcodec_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Spk", gbcodec_event_spk), - SND_SOC_DAPM_SPK("HP", gbcodec_event_hp), - SND_SOC_DAPM_MIC("Int Mic", gbcodec_event_int_mic), - - SND_SOC_DAPM_OUTPUT("SPKOUT"), - SND_SOC_DAPM_OUTPUT("HPOUT"), - - SND_SOC_DAPM_INPUT("MIC"), - SND_SOC_DAPM_INPUT("HSMIC"), - - SND_SOC_DAPM_SWITCH("SPK Amp", SND_SOC_NOPM, 0, 0, &spk_amp_ctl), - SND_SOC_DAPM_SWITCH("HP Amp", SND_SOC_NOPM, 0, 0, &hp_amp_ctl), - SND_SOC_DAPM_SWITCH("MIC ADC", SND_SOC_NOPM, 0, 0, &mic_adc_ctl), - - SND_SOC_DAPM_PGA("SPK DAC", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("HP DAC", SND_SOC_NOPM, 0, 0, NULL, 0), - - SND_SOC_DAPM_MIXER("SPK Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("APB1_TX Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - - SND_SOC_DAPM_MUX("APB1_RX Mux", SND_SOC_NOPM, 0, 0, - &gbcodec_apb1_rx_mux), - SND_SOC_DAPM_MUX("MIC Mux", SND_SOC_NOPM, 0, 0, &gbcodec_mic_mux), - - SND_SOC_DAPM_AIF_IN("APB1RX", "APBridgeA1 Playback", 0, SND_SOC_NOPM, 0, - 0), - SND_SOC_DAPM_AIF_OUT("APB1TX", "APBridgeA1 Capture", 0, SND_SOC_NOPM, 0, - 0), -}; - -static const struct snd_soc_dapm_route gbcodec_dapm_routes[] = { - /* Playback path */ - {"Spk", NULL, "SPKOUT"}, - {"SPKOUT", NULL, "SPK Amp"}, - {"SPK Amp", "Switch", "SPK DAC"}, - {"SPK DAC", NULL, "SPK Mixer"}, - - {"HP", NULL, "HPOUT"}, - {"HPOUT", NULL, "HP Amp"}, - {"HP Amp", "Switch", "HP DAC"}, - {"HP DAC", NULL, "HP Mixer"}, - - {"SPK Mixer", NULL, "APB1_RX Mux"}, - {"HP Mixer", NULL, "APB1_RX Mux"}, - - {"APB1_RX Mux", "Left", "APB1RX"}, - {"APB1_RX Mux", "Right", "APB1RX"}, - {"APB1_RX Mux", "Stereo", "APB1RX"}, - - /* Capture path */ - {"MIC", NULL, "Int Mic"}, - {"MIC", NULL, "MIC Mux"}, - {"MIC Mux", "Left", "MIC ADC"}, - {"MIC Mux", "Right", "MIC ADC"}, - {"MIC Mux", "Stereo", "MIC ADC"}, - {"MIC ADC", "Switch", "APB1_TX Mixer"}, - {"APB1_TX Mixer", NULL, "APB1TX"} -}; - static int gbcodec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -180,24 +62,9 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { .digital_mute = gbcodec_digital_mute, }; -static struct snd_soc_dai_driver gbcodec_dai = { - .playback = { - .stream_name = "APBridgeA1 Playback", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "APBridgeA1 Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &gbcodec_dai_ops, -}; - +/* + * codec driver ops + */ static int gbcodec_probe(struct snd_soc_codec *codec) { /* Empty function for now */ @@ -261,13 +128,6 @@ static struct snd_soc_codec_driver soc_codec_dev_gbcodec = { .reg_word_size = 1, .idle_bias_off = true, - - .controls = gbcodec_snd_controls, - .num_controls = ARRAY_SIZE(gbcodec_snd_controls), - .dapm_widgets = gbcodec_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(gbcodec_dapm_widgets), - .dapm_routes = gbcodec_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(gbcodec_dapm_routes), }; /* @@ -369,6 +229,9 @@ static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev, mutex_init(&gbcodec->lock); INIT_LIST_HEAD(&gbcodec->dai_list); + INIT_LIST_HEAD(&gbcodec->widget_list); + INIT_LIST_HEAD(&gbcodec->codec_ctl_list); + INIT_LIST_HEAD(&gbcodec->widget_ctl_list); gbcodec->dev_id = dev_id; dev_set_drvdata(dev, gbcodec); gbcodec->dev = dev; @@ -412,8 +275,9 @@ struct device_driver gb_codec_driver = { static int gbaudio_codec_probe(struct gb_connection *connection) { - int ret; + int ret, i; struct gbaudio_codec_info *gbcodec; + struct gb_audio_topology *topology; struct device *dev = &connection->bundle->dev; int dev_id = connection->bundle->id; @@ -425,8 +289,35 @@ static int gbaudio_codec_probe(struct gb_connection *connection) gbcodec->mgmt_connection = connection; + /* fetch topology data */ + ret = gb_audio_gb_get_topology(connection, &topology); + if (ret) { + dev_err(gbcodec->dev, + "%d:Error while fetching topology\n", ret); + goto base_error; + } + + /* process topology data */ + ret = gbaudio_tplg_parse_data(gbcodec, topology); + if (ret) { + dev_err(dev, "%d:Error while parsing topology data\n", + ret); + goto topology_error; + } + gbcodec->topology = topology; + + /* update codec info */ + soc_codec_dev_gbcodec.controls = gbcodec->kctls; + soc_codec_dev_gbcodec.num_controls = gbcodec->num_kcontrols; + soc_codec_dev_gbcodec.dapm_widgets = gbcodec->widgets; + soc_codec_dev_gbcodec.num_dapm_widgets = gbcodec->num_dapm_widgets; + soc_codec_dev_gbcodec.dapm_routes = gbcodec->routes; + soc_codec_dev_gbcodec.num_dapm_routes = gbcodec->num_dapm_routes; + /* update DAI info */ - gbcodec->dais = &gbcodec_dai; + for (i = 0; i < gbcodec->num_dais; i++) + gbcodec->dais[i].ops = &gbcodec_dai_ops; + /* FIXME */ dev->driver = &gb_codec_driver; @@ -435,7 +326,7 @@ static int gbaudio_codec_probe(struct gb_connection *connection) gbcodec->dais, 1); if (ret) { dev_err(dev, "%d:Failed to register codec\n", ret); - goto base_error; + goto parse_error; } /* update DAI links in response to this codec */ @@ -455,8 +346,13 @@ static int gbaudio_codec_probe(struct gb_connection *connection) codec_reg_error: snd_soc_unregister_codec(dev); -base_error: dev->driver = NULL; +parse_error: + gbaudio_tplg_release(gbcodec); + gbcodec->topology = NULL; +topology_error: + kfree(topology); +base_error: gbcodec->mgmt_connection = NULL; return ret; } @@ -480,6 +376,8 @@ static void gbaudio_codec_remove(struct gb_connection *connection) snd_soc_unregister_codec(dev); dev->driver = NULL; + gbaudio_tplg_release(gbcodec); + kfree(gbcodec->topology); gbcodec->mgmt_connection = NULL; mutex_lock(&gbcodec->lock); gbcodec->codec_registered = 0; @@ -509,68 +407,6 @@ static struct gb_protocol gb_audio_mgmt_protocol = { .request_recv = gbaudio_codec_report_event_recv, }; -static struct gbaudio_dai *gbaudio_allocate_dai(struct gbaudio_codec_info *gb, - int data_cport, - struct gb_connection *connection, - const char *name) -{ - struct gbaudio_dai *dai; - - mutex_lock(&gb->lock); - dai = devm_kzalloc(gb->dev, sizeof(*dai), GFP_KERNEL); - if (!dai) { - dev_err(gb->dev, "%s:DAI Malloc failure\n", name); - mutex_unlock(&gb->lock); - return NULL; - } - - dai->data_cport = data_cport; - dai->connection = connection; - - /* update name */ - if (name) - strlcpy(dai->name, name, NAME_SIZE); - list_add(&dai->list, &gb->dai_list); - dev_dbg(gb->dev, "%d:%s: DAI added\n", data_cport, dai->name); - mutex_unlock(&gb->lock); - - return dai; -} - -struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec, - int data_cport, - struct gb_connection *connection, - const char *name) -{ - struct gbaudio_dai *dai, *_dai; - - /* FIXME need to take care for multiple DAIs */ - mutex_lock(&gbcodec->lock); - if (list_empty(&gbcodec->dai_list)) { - mutex_unlock(&gbcodec->lock); - return gbaudio_allocate_dai(gbcodec, data_cport, connection, - name); - } - - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { - if (dai->data_cport == data_cport) { - if (connection) - dai->connection = connection; - - if (name) - strlcpy(dai->name, name, NAME_SIZE); - dev_dbg(gbcodec->dev, "%d:%s: DAI updated\n", - data_cport, dai->name); - mutex_unlock(&gbcodec->lock); - return dai; - } - } - - dev_err(gbcodec->dev, "%s:DAI not found\n", name); - mutex_unlock(&gbcodec->lock); - return NULL; -} - static int gbaudio_dai_probe(struct gb_connection *connection) { struct gbaudio_dai *dai; diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 39bd995719c2..5051e06dfff6 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -68,6 +68,19 @@ static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { GBCODEC_APB2_MUX_REG_DEFAULT, }; +struct gbaudio_widget { + __u8 id; + const char *name; + struct list_head list; +}; + +struct gbaudio_control { + __u8 id; + char *name; + const char * const *texts; + struct list_head list; +}; + struct gbaudio_dai { __le16 data_cport; char name[NAME_SIZE]; @@ -87,6 +100,7 @@ struct gbaudio_codec_info { char vstr[NAME_SIZE]; char pstr[NAME_SIZE]; struct list_head list; + struct gb_audio_topology *topology; char name[NAME_SIZE]; /* soc related data */ @@ -105,6 +119,10 @@ struct gbaudio_codec_info { int num_kcontrols; int num_dapm_widgets; int num_dapm_routes; + unsigned long dai_offset; + unsigned long widget_offset; + unsigned long control_offset; + unsigned long route_offset; struct snd_kcontrol_new *kctls; struct snd_soc_dapm_widget *widgets; struct snd_soc_dapm_route *routes; @@ -112,10 +130,23 @@ struct gbaudio_codec_info { /* lists */ struct list_head dai_list; + struct list_head widget_list; + struct list_head codec_ctl_list; + struct list_head widget_ctl_list; struct mutex lock; }; +struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec, + int data_cport, + struct gb_connection *connection, + const char *name); +int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, + struct gb_audio_topology *tplg_data); +void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec); + /* protocol related */ +extern int gb_audio_gb_get_topology(struct gb_connection *connection, + struct gb_audio_topology **topology); extern int gb_audio_gb_get_control(struct gb_connection *connection, uint8_t control_id, uint8_t index, struct gb_audio_ctl_elem_value *value); diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c new file mode 100644 index 000000000000..8840a9c330de --- /dev/null +++ b/drivers/staging/greybus/audio_topology.c @@ -0,0 +1,1114 @@ +/* + * Greybus audio driver + * Copyright 2015-2016 Google Inc. + * Copyright 2015-2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "audio_codec.h" +#include "greybus_protocols.h" + +#define GBAUDIO_INVALID_ID 0xFF + +/* mixer control */ +struct gb_mixer_control { + int min, max; + unsigned int reg, rreg, shift, rshift, invert; +}; + +struct gbaudio_ctl_pvt { + unsigned int ctl_id; + unsigned int data_cport; + unsigned int access; + unsigned int vcount; + struct gb_audio_ctl_elem_info *info; +}; + +static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec, + __u8 control_id, __u8 index) +{ + struct gbaudio_control *control; + + if (control_id == GBAUDIO_INVALID_ID) + return NULL; + + list_for_each_entry(control, &gbcodec->codec_ctl_list, list) { + if (control->id == control_id) { + if (index == GBAUDIO_INVALID_ID) + return control->name; + return control->texts[index]; + } + } + list_for_each_entry(control, &gbcodec->widget_ctl_list, list) { + if (control->id == control_id) { + if (index == GBAUDIO_INVALID_ID) + return control->name; + return control->texts[index]; + } + } + return NULL; +} + +static int gbaudio_map_widgetname(struct gbaudio_codec_info *gbcodec, + const char *name) +{ + struct gbaudio_widget *widget; + char widget_name[NAME_SIZE]; + char prefix_name[NAME_SIZE]; + + snprintf(prefix_name, NAME_SIZE, "GB %d ", gbcodec->dev_id); + if (strncmp(name, prefix_name, strlen(prefix_name))) + return -EINVAL; + + strlcpy(widget_name, name+strlen(prefix_name), NAME_SIZE); + dev_dbg(gbcodec->dev, "widget_name:%s, truncated widget_name:%s\n", + name, widget_name); + + list_for_each_entry(widget, &gbcodec->widget_list, list) { + if (!strncmp(widget->name, widget_name, NAME_SIZE)) + return widget->id; + } + return -EINVAL; +} + +static const char *gbaudio_map_widgetid(struct gbaudio_codec_info *gbcodec, + __u8 widget_id) +{ + struct gbaudio_widget *widget; + + list_for_each_entry(widget, &gbcodec->widget_list, list) { + if (widget->id == widget_id) + return widget->name; + } + return NULL; +} + +static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int max; + const char *name; + struct gbaudio_ctl_pvt *data; + struct gb_audio_ctl_elem_info *info; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; + info = (struct gb_audio_ctl_elem_info *)data->info; + + if (!info) { + dev_err(gbcodec->dev, "NULL info for %s\n", uinfo->id.name); + return -EINVAL; + } + + /* update uinfo */ + uinfo->access = data->access; + uinfo->count = data->vcount; + uinfo->type = (snd_ctl_elem_type_t)info->type; + + switch (info->type) { + case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: + case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: + uinfo->value.integer.min = info->value.integer.min; + uinfo->value.integer.max = info->value.integer.max; + break; + case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: + max = info->value.enumerated.items; + uinfo->value.enumerated.items = max; + if (uinfo->value.enumerated.item > max - 1) + uinfo->value.enumerated.item = max - 1; + name = gbaudio_map_controlid(gbcodec, data->ctl_id, + uinfo->value.enumerated.item); + strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE); + break; + default: + dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", + info->type, kcontrol->id.name); + break; + } + return 0; +} + +static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + struct gb_audio_ctl_elem_info *info; + struct gbaudio_ctl_pvt *data; + struct gb_audio_ctl_elem_value gbvalue; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; + info = (struct gb_audio_ctl_elem_info *)data->info; + + ret = gb_audio_gb_get_control(gb->mgmt_connection, data->ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, + kcontrol->id.name); + return ret; + } + + /* update ucontrol */ + switch (info->type) { + case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: + case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: + ucontrol->value.integer.value[0] = + gbvalue.value.integer_value[0]; + if (data->vcount == 2) + ucontrol->value.integer.value[1] = + gbvalue.value.integer_value[1]; + break; + case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: + ucontrol->value.enumerated.item[0] = + gbvalue.value.enumerated_item[0]; + if (data->vcount == 2) + ucontrol->value.enumerated.item[1] = + gbvalue.value.enumerated_item[1]; + break; + default: + dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", + info->type, kcontrol->id.name); + ret = -EINVAL; + break; + } + return ret; +} + +static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct gb_audio_ctl_elem_info *info; + struct gbaudio_ctl_pvt *data; + struct gb_audio_ctl_elem_value gbvalue; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; + info = (struct gb_audio_ctl_elem_info *)data->info; + + /* update ucontrol */ + switch (info->type) { + case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: + case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: + gbvalue.value.integer_value[0] = + ucontrol->value.integer.value[0]; + if (data->vcount == 2) + gbvalue.value.integer_value[1] = + ucontrol->value.integer.value[1]; + break; + case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: + gbvalue.value.enumerated_item[0] = + ucontrol->value.enumerated.item[0]; + if (data->vcount == 2) + gbvalue.value.enumerated_item[1] = + ucontrol->value.enumerated.item[1]; + break; + default: + dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", + info->type, kcontrol->id.name); + ret = -EINVAL; + break; + } + + if (ret) + return ret; + + ret = gb_audio_gb_set_control(gb->mgmt_connection, data->ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, + kcontrol->id.name); + } + + return ret; +} + +#define SOC_MIXER_GB(xname, kcount, data) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .count = kcount, .info = gbcodec_mixer_ctl_info, \ + .get = gbcodec_mixer_ctl_get, .put = gbcodec_mixer_ctl_put, \ + .private_value = (unsigned long)data } + +/* + * although below callback functions seems redundant to above functions. + * same are kept to allow provision for different handling in case + * of DAPM related sequencing, etc. + */ +static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int platform_max, platform_min; + struct gbaudio_ctl_pvt *data; + struct gb_audio_ctl_elem_info *info; + + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; + info = (struct gb_audio_ctl_elem_info *)data->info; + + /* update uinfo */ + platform_max = info->value.integer.max; + platform_min = info->value.integer.min; + + if (platform_max == 1 && + !strnstr(kcontrol->id.name, " Volume", NAME_SIZE)) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + + uinfo->count = data->vcount; + uinfo->value.integer.min = 0; + if (info->value.integer.min < 0 && + (uinfo->type == SNDRV_CTL_ELEM_TYPE_INTEGER)) + uinfo->value.integer.max = platform_max - platform_min; + else + uinfo->value.integer.max = platform_max; + + return 0; +} + +static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + struct gb_audio_ctl_elem_info *info; + struct gbaudio_ctl_pvt *data; + struct gb_audio_ctl_elem_value gbvalue; + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; + info = (struct gb_audio_ctl_elem_info *)data->info; + + if (data->vcount == 2) + dev_warn(widget->dapm->dev, + "GB: Control '%s' is stereo, which is not supported\n", + kcontrol->id.name); + + ret = gb_audio_gb_get_control(gb->mgmt_connection, data->ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, + kcontrol->id.name); + return ret; + } + /* update ucontrol */ + ucontrol->value.integer.value[0] = gbvalue.value.integer_value[0]; + + return ret; +} + +static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, wi, max, connect; + unsigned int mask, val; + struct gb_audio_ctl_elem_info *info; + struct gbaudio_ctl_pvt *data; + struct gb_audio_ctl_elem_value gbvalue; + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; + info = (struct gb_audio_ctl_elem_info *)data->info; + + if (data->vcount == 2) + dev_warn(widget->dapm->dev, + "GB: Control '%s' is stereo, which is not supported\n", + kcontrol->id.name); + + max = info->value.integer.max; + mask = (1 << fls(max)) - 1; + val = (ucontrol->value.integer.value[0] & mask); + connect = !!val; + + /* update ucontrol */ + if (gbvalue.value.integer_value[0] != val) { + for (wi = 0; wi < wlist->num_widgets; wi++) { + widget = wlist->widgets[wi]; + + widget->value = val; + widget->dapm->update = NULL; + snd_soc_dapm_mixer_update_power(widget, kcontrol, + connect); + } + gbvalue.value.integer_value[0] = + ucontrol->value.integer.value[0]; + ret = gb_audio_gb_set_control(gb->mgmt_connection, + data->ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err(codec->dev, + "%d:Error in %s for %s\n", ret, __func__, + kcontrol->id.name); + } + } + + return ret; +} + +#define SOC_DAPM_MIXER_GB(xname, kcount, data) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .count = kcount, .info = gbcodec_mixer_dapm_ctl_info, \ + .get = gbcodec_mixer_dapm_ctl_get, .put = gbcodec_mixer_dapm_ctl_put, \ + .private_value = (unsigned long)data} + +static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + /* Ensure GB speaker is connected */ + + return 0; +} + +static int gbcodec_event_hp(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + /* Ensure GB module supports jack slot */ + + return 0; +} + +static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + /* Ensure GB module supports jack slot */ + + return 0; +} + +static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w) +{ + int ret = 0; + + switch (w->type) { + case snd_soc_dapm_spk: + case snd_soc_dapm_hp: + case snd_soc_dapm_mic: + case snd_soc_dapm_output: + case snd_soc_dapm_input: + if (w->ncontrols) + ret = -EINVAL; + break; + case snd_soc_dapm_switch: + case snd_soc_dapm_mux: + if (w->ncontrols != 1) + ret = -EINVAL; + break; + default: + break; + } + + return ret; +} + +static int gbaudio_tplg_create_kcontrol(struct gbaudio_codec_info *gb, + struct snd_kcontrol_new *kctl, + struct gb_audio_control *ctl) +{ + struct gbaudio_ctl_pvt *ctldata; + + switch (ctl->iface) { + case SNDRV_CTL_ELEM_IFACE_MIXER: + ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt), + GFP_KERNEL); + if (!ctldata) + return -ENOMEM; + ctldata->ctl_id = ctl->id; + ctldata->data_cport = ctl->data_cport; + ctldata->access = ctl->access; + ctldata->vcount = ctl->count_values; + ctldata->info = &ctl->info; + *kctl = (struct snd_kcontrol_new) + SOC_MIXER_GB(ctl->name, ctl->count, ctldata); + ctldata = NULL; + break; + default: + return -EINVAL; + } + + dev_dbg(gb->dev, "%s:%d control created\n", ctl->name, ctl->id); + return 0; +} + +static const char * const gbtexts[] = {"Stereo", "Left", "Right"}; + +static const SOC_ENUM_SINGLE_DECL( + gbcodec_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbtexts); + +static const SOC_ENUM_SINGLE_DECL( + gbcodec_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbtexts); + +static int gbaudio_tplg_create_enum_ctl(struct gbaudio_codec_info *gb, + struct snd_kcontrol_new *kctl, + struct gb_audio_control *ctl) +{ + switch (ctl->id) { + case 8: + *kctl = (struct snd_kcontrol_new) + SOC_DAPM_ENUM(ctl->name, gbcodec_apb1_rx_enum); + break; + case 9: + *kctl = (struct snd_kcontrol_new) + SOC_DAPM_ENUM(ctl->name, gbcodec_mic_enum); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_codec_info *gb, + struct snd_kcontrol_new *kctl, + struct gb_audio_control *ctl) +{ + struct gbaudio_ctl_pvt *ctldata; + + ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt), + GFP_KERNEL); + if (!ctldata) + return -ENOMEM; + ctldata->ctl_id = ctl->id; + ctldata->data_cport = ctl->data_cport; + ctldata->access = ctl->access; + ctldata->vcount = ctl->count_values; + ctldata->info = &ctl->info; + *kctl = (struct snd_kcontrol_new) + SOC_DAPM_MIXER_GB(ctl->name, ctl->count, ctldata); + + return 0; +} + +static int gbaudio_tplg_create_wcontrol(struct gbaudio_codec_info *gb, + struct snd_kcontrol_new *kctl, + struct gb_audio_control *ctl) +{ + int ret; + + switch (ctl->iface) { + case SNDRV_CTL_ELEM_IFACE_MIXER: + switch (ctl->info.type) { + case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: + ret = gbaudio_tplg_create_enum_ctl(gb, kctl, ctl); + break; + default: + ret = gbaudio_tplg_create_mixer_ctl(gb, kctl, ctl); + break; + } + break; + default: + return -EINVAL; + + } + + dev_dbg(gb->dev, "%s:%d DAPM control created, ret:%d\n", ctl->name, + ctl->id, ret); + return ret; +} + +static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int wid; + int ret; + struct snd_soc_codec *codec = w->codec; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + /* map name to widget id */ + wid = gbaudio_map_widgetname(gbcodec, w->name); + if (wid < 0) { + dev_err(codec->dev, "Invalid widget name:%s\n", w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = gb_audio_gb_enable_widget(gbcodec->mgmt_connection, wid); + break; + case SND_SOC_DAPM_POST_PMD: + ret = gb_audio_gb_disable_widget(gbcodec->mgmt_connection, wid); + break; + } + if (ret) + dev_err(codec->dev, "%d: widget, event:%d failed:%d\n", wid, + event, ret); + return ret; +} + +static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, + struct snd_soc_dapm_widget *dw, + struct gb_audio_widget *w) +{ + int i, ret; + struct snd_kcontrol_new *widget_kctls; + struct gb_audio_control *curr; + struct gbaudio_control *control, *_control; + size_t size; + + ret = gbaudio_validate_kcontrol_count(w); + if (ret) { + dev_err(gbcodec->dev, "Inavlid kcontrol count=%d for %s\n", + w->ncontrols, w->name); + return ret; + } + + /* allocate memory for kcontrol */ + if (w->ncontrols) { + size = sizeof(struct snd_kcontrol_new) * w->ncontrols; + widget_kctls = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); + if (!widget_kctls) + return -ENOMEM; + } + + /* create relevant kcontrols */ + for (i = 0; i < w->ncontrols; i++) { + curr = &w->ctl[i]; + ret = gbaudio_tplg_create_wcontrol(gbcodec, &widget_kctls[i], + curr); + if (ret) { + dev_err(gbcodec->dev, + "%s:%d type widget_ctl not supported\n", + curr->name, curr->iface); + goto error; + } + control = devm_kzalloc(gbcodec->dev, + sizeof(struct gbaudio_control), + GFP_KERNEL); + if (!control) { + ret = -ENOMEM; + goto error; + } + control->id = curr->id; + control->name = curr->name; + if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) + control->texts = (const char * const *) + curr->info.value.enumerated.names; + list_add(&control->list, &gbcodec->widget_ctl_list); + dev_dbg(gbcodec->dev, "%s: control of type %d created\n", + widget_kctls[i].name, widget_kctls[i].iface); + } + + switch (w->type) { + case snd_soc_dapm_spk: + *dw = (struct snd_soc_dapm_widget) + SND_SOC_DAPM_SPK(w->name, gbcodec_event_spk); + break; + case snd_soc_dapm_hp: + *dw = (struct snd_soc_dapm_widget) + SND_SOC_DAPM_HP(w->name, gbcodec_event_hp); + break; + case snd_soc_dapm_mic: + *dw = (struct snd_soc_dapm_widget) + SND_SOC_DAPM_MIC(w->name, gbcodec_event_int_mic); + break; + case snd_soc_dapm_output: + *dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_OUTPUT(w->name); + break; + case snd_soc_dapm_input: + *dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_INPUT(w->name); + break; + case snd_soc_dapm_switch: + *dw = (struct snd_soc_dapm_widget) + SND_SOC_DAPM_SWITCH_E(w->name, SND_SOC_NOPM, 0, 0, + widget_kctls, gbaudio_widget_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD); + break; + case snd_soc_dapm_pga: + *dw = (struct snd_soc_dapm_widget) + SND_SOC_DAPM_PGA_E(w->name, SND_SOC_NOPM, 0, 0, NULL, 0, + gbaudio_widget_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD); + break; + case snd_soc_dapm_mixer: + *dw = (struct snd_soc_dapm_widget) + SND_SOC_DAPM_MIXER_E(w->name, SND_SOC_NOPM, 0, 0, NULL, + 0, gbaudio_widget_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD); + break; + case snd_soc_dapm_mux: + *dw = (struct snd_soc_dapm_widget) + SND_SOC_DAPM_MUX_E(w->name, SND_SOC_NOPM, 0, 0, + widget_kctls, gbaudio_widget_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD); + break; + case snd_soc_dapm_aif_in: + *dw = (struct snd_soc_dapm_widget) + SND_SOC_DAPM_AIF_IN_E(w->name, w->sname, 0, + SND_SOC_NOPM, + 0, 0, gbaudio_widget_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD); + break; + case snd_soc_dapm_aif_out: + *dw = (struct snd_soc_dapm_widget) + SND_SOC_DAPM_AIF_OUT_E(w->name, w->sname, 0, + SND_SOC_NOPM, + 0, 0, gbaudio_widget_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD); + break; + default: + ret = -EINVAL; + goto error; + } + + dev_dbg(gbcodec->dev, "%s: widget of type %d created\n", dw->name, + dw->id); + return 0; +error: + list_for_each_entry_safe(control, _control, &gbcodec->widget_ctl_list, + list) { + list_del(&control->list); + devm_kfree(gbcodec->dev, control); + } + return ret; +} + +static int gbaudio_tplg_create_dai(struct gbaudio_codec_info *gbcodec, + struct snd_soc_dai_driver *gb_dai, + struct gb_audio_dai *dai) +{ + /* + * do not update name here, + * append dev_id before assigning it here + */ + + gb_dai->playback.stream_name = dai->playback.stream_name; + gb_dai->playback.channels_min = dai->playback.chan_min; + gb_dai->playback.channels_max = dai->playback.chan_max; + gb_dai->playback.formats = dai->playback.formats; + gb_dai->playback.rates = dai->playback.rates; + gb_dai->playback.sig_bits = dai->playback.sig_bits; + + gb_dai->capture.stream_name = dai->capture.stream_name; + gb_dai->capture.channels_min = dai->capture.chan_min; + gb_dai->capture.channels_max = dai->capture.chan_max; + gb_dai->capture.formats = dai->capture.formats; + gb_dai->capture.rates = dai->capture.rates; + gb_dai->capture.sig_bits = dai->capture.sig_bits; + + return 0; +} + +static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec, + struct gb_audio_control *controls) +{ + int i, ret; + struct snd_kcontrol_new *dapm_kctls; + struct gb_audio_control *curr; + struct gbaudio_control *control, *_control; + size_t size; + + size = sizeof(struct snd_kcontrol_new) * gbcodec->num_kcontrols; + dapm_kctls = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); + if (!dapm_kctls) + return -ENOMEM; + + curr = controls; + for (i = 0; i < gbcodec->num_kcontrols; i++) { + ret = gbaudio_tplg_create_kcontrol(gbcodec, &dapm_kctls[i], + curr); + if (ret) { + dev_err(gbcodec->dev, "%s:%d type not supported\n", + curr->name, curr->iface); + goto error; + } + control = devm_kzalloc(gbcodec->dev, sizeof(struct + gbaudio_control), + GFP_KERNEL); + if (!control) { + ret = -ENOMEM; + goto error; + } + control->id = curr->id; + control->name = curr->name; + if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) + control->texts = (const char * const *) + curr->info.value.enumerated.names; + list_add(&control->list, &gbcodec->codec_ctl_list); + dev_dbg(gbcodec->dev, "%d:%s created of type %d\n", curr->id, + curr->name, curr->info.type); + curr++; + } + gbcodec->kctls = dapm_kctls; + + return 0; +error: + list_for_each_entry_safe(control, _control, &gbcodec->codec_ctl_list, + list) { + list_del(&control->list); + devm_kfree(gbcodec->dev, control); + } + devm_kfree(gbcodec->dev, dapm_kctls); + return ret; +} + +static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec, + struct gb_audio_widget *widgets) +{ + int i, ret, ncontrols; + struct snd_soc_dapm_widget *dapm_widgets; + struct gb_audio_widget *curr; + struct gbaudio_widget *widget, *_widget; + size_t size; + + size = sizeof(struct snd_soc_dapm_widget) * gbcodec->num_dapm_widgets; + dapm_widgets = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); + if (!dapm_widgets) + return -ENOMEM; + + curr = widgets; + for (i = 0; i < gbcodec->num_dapm_widgets; i++) { + ret = gbaudio_tplg_create_widget(gbcodec, &dapm_widgets[i], + curr); + if (ret) { + dev_err(gbcodec->dev, "%s:%d type not supported\n", + curr->name, curr->type); + goto error; + } + widget = devm_kzalloc(gbcodec->dev, sizeof(struct + gbaudio_widget), + GFP_KERNEL); + if (!widget) { + ret = -ENOMEM; + goto error; + } + widget->id = curr->id; + widget->name = curr->name; + list_add(&widget->list, &gbcodec->widget_list); + ncontrols = curr->ncontrols; + curr++; + curr += ncontrols * sizeof(struct gb_audio_control); + } + gbcodec->widgets = dapm_widgets; + + return 0; + +error: + list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list, + list) { + list_del(&widget->list); + devm_kfree(gbcodec->dev, widget); + } + devm_kfree(gbcodec->dev, dapm_widgets); + return ret; +} + +static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, + struct gb_audio_dai *dais) +{ + int i, ret; + struct snd_soc_dai_driver *gb_dais; + struct gb_audio_dai *curr; + struct gbaudio_dai *dai, *_dai; + size_t size; + char dai_name[NAME_SIZE]; + + size = sizeof(struct snd_soc_dai_driver) * gbcodec->num_dais; + gb_dais = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); + if (!gb_dais) + return -ENOMEM; + + curr = dais; + for (i = 0; i < gbcodec->num_dais; i++) { + ret = gbaudio_tplg_create_dai(gbcodec, &gb_dais[i], curr); + if (ret) { + dev_err(gbcodec->dev, "%s failed to create\n", + curr->name); + goto error; + } + /* append dev_id to dai_name */ + snprintf(dai_name, NAME_SIZE, "%s.%d", curr->name, + gbcodec->dev_id); + dai = gbaudio_add_dai(gbcodec, curr->data_cport, NULL, + dai_name); + if (!dai) + goto error; + dev_err(gbcodec->dev, "%s:DAI added\n", dai->name); + gb_dais[i].name = dai->name; + curr++; + } + gbcodec->dais = gb_dais; + + return 0; + +error: + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { + list_del(&dai->list); + devm_kfree(gbcodec->dev, dai); + } + devm_kfree(gbcodec->dev, gb_dais); + return ret; +} + +static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec, + struct gb_audio_route *routes) +{ + int i, ret; + struct snd_soc_dapm_route *dapm_routes; + struct gb_audio_route *curr; + size_t size; + + size = sizeof(struct snd_soc_dapm_route) * gbcodec->num_dapm_routes; + dapm_routes = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); + if (!dapm_routes) + return -ENOMEM; + + + gbcodec->routes = dapm_routes; + curr = routes; + + for (i = 0; i < gbcodec->num_dapm_routes; i++) { + dapm_routes->sink = + gbaudio_map_widgetid(gbcodec, curr->destination_id); + if (!dapm_routes->sink) { + dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid sink\n", + curr->source_id, curr->destination_id, + curr->control_id, curr->index); + ret = -EINVAL; + goto error; + } + dapm_routes->source = + gbaudio_map_widgetid(gbcodec, curr->source_id); + if (!dapm_routes->source) { + dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid source\n", + curr->source_id, curr->destination_id, + curr->control_id, curr->index); + ret = -EINVAL; + goto error; + } + dapm_routes->control = + gbaudio_map_controlid(gbcodec, + curr->control_id, + curr->index); + if ((curr->control_id != GBAUDIO_INVALID_ID) && + !dapm_routes->control) { + dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid control\n", + curr->source_id, curr->destination_id, + curr->control_id, curr->index); + ret = -EINVAL; + goto error; + } + dev_dbg(gbcodec->dev, "Route {%s, %s, %s}\n", dapm_routes->sink, + (dapm_routes->control) ? dapm_routes->control:"NULL", + dapm_routes->source); + dapm_routes++; + curr++; + } + + return 0; + +error: + devm_kfree(gbcodec->dev, dapm_routes); + return ret; +} + +static int gbaudio_tplg_process_header(struct gbaudio_codec_info *gbcodec, + struct gb_audio_topology *tplg_data) +{ + /* fetch no. of kcontrols, widgets & routes */ + gbcodec->num_dais = tplg_data->num_dais; + gbcodec->num_kcontrols = tplg_data->num_controls; + gbcodec->num_dapm_widgets = tplg_data->num_widgets; + gbcodec->num_dapm_routes = tplg_data->num_routes; + + /* update block offset */ + gbcodec->dai_offset = (unsigned long)&tplg_data->data; + gbcodec->control_offset = gbcodec->dai_offset + tplg_data->size_dais; + gbcodec->widget_offset = gbcodec->control_offset + + tplg_data->size_controls; + gbcodec->route_offset = gbcodec->widget_offset + + tplg_data->size_widgets; + + dev_dbg(gbcodec->dev, "DAI offset is 0x%lx\n", gbcodec->dai_offset); + dev_dbg(gbcodec->dev, "control offset is %lx\n", + gbcodec->control_offset); + dev_dbg(gbcodec->dev, "widget offset is %lx\n", gbcodec->widget_offset); + dev_dbg(gbcodec->dev, "route offset is %lx\n", gbcodec->route_offset); + + return 0; +} + +static struct gbaudio_dai *gbaudio_allocate_dai(struct gbaudio_codec_info *gb, + int data_cport, + struct gb_connection *connection, + const char *name) +{ + struct gbaudio_dai *dai; + + mutex_lock(&gb->lock); + dai = devm_kzalloc(gb->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) { + dev_err(gb->dev, "%s:DAI Malloc failure\n", name); + mutex_unlock(&gb->lock); + return NULL; + } + + dai->data_cport = data_cport; + dai->connection = connection; + + /* update name */ + if (name) + strlcpy(dai->name, name, NAME_SIZE); + list_add(&dai->list, &gb->dai_list); + dev_dbg(gb->dev, "%d:%s: DAI added\n", data_cport, dai->name); + mutex_unlock(&gb->lock); + + return dai; +} + +struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec, + int data_cport, + struct gb_connection *connection, + const char *name) +{ + struct gbaudio_dai *dai, *_dai; + + /* FIXME need to take care for multiple DAIs */ + mutex_lock(&gbcodec->lock); + if (list_empty(&gbcodec->dai_list)) { + mutex_unlock(&gbcodec->lock); + return gbaudio_allocate_dai(gbcodec, data_cport, connection, + name); + } + + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { + if (dai->data_cport == data_cport) { + if (connection) + dai->connection = connection; + + if (name) + strlcpy(dai->name, name, NAME_SIZE); + dev_dbg(gbcodec->dev, "%d:%s: DAI updated\n", + data_cport, dai->name); + mutex_unlock(&gbcodec->lock); + return dai; + } + } + + dev_err(gbcodec->dev, "%s:DAI not found\n", name); + mutex_unlock(&gbcodec->lock); + return NULL; +} + +int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, + struct gb_audio_topology *tplg_data) +{ + int ret; + struct gb_audio_dai *dais; + struct gb_audio_control *controls; + struct gb_audio_widget *widgets; + struct gb_audio_route *routes; + + if (!tplg_data) + return -EINVAL; + + ret = gbaudio_tplg_process_header(gbcodec, tplg_data); + if (ret) { + dev_err(gbcodec->dev, "%d: Error in parsing topology header\n", + ret); + return ret; + } + + /* process control */ + controls = (struct gb_audio_control *)gbcodec->control_offset; + ret = gbaudio_tplg_process_kcontrols(gbcodec, controls); + if (ret) { + dev_err(gbcodec->dev, + "%d: Error in parsing controls data\n", ret); + return ret; + } + dev_err(gbcodec->dev, "Control parsing finished\n"); + + /* process DAI */ + dais = (struct gb_audio_dai *)gbcodec->dai_offset; + ret = gbaudio_tplg_process_dais(gbcodec, dais); + if (ret) { + dev_err(gbcodec->dev, + "%d: Error in parsing DAIs data\n", ret); + return ret; + } + dev_err(gbcodec->dev, "DAI parsing finished\n"); + + /* process widgets */ + widgets = (struct gb_audio_widget *)gbcodec->widget_offset; + ret = gbaudio_tplg_process_widgets(gbcodec, widgets); + if (ret) { + dev_err(gbcodec->dev, + "%d: Error in parsing widgets data\n", ret); + return ret; + } + dev_err(gbcodec->dev, "Widget parsing finished\n"); + + /* process route */ + routes = (struct gb_audio_route *)gbcodec->route_offset; + ret = gbaudio_tplg_process_routes(gbcodec, routes); + if (ret) { + dev_err(gbcodec->dev, + "%d: Error in parsing routes data\n", ret); + return ret; + } + dev_err(gbcodec->dev, "Route parsing finished\n"); + + return ret; +} + +void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec) +{ + struct gbaudio_dai *dai, *_dai; + struct gbaudio_control *control, *_control; + struct gbaudio_widget *widget, *_widget; + + if (!gbcodec->topology) + return; + + /* release kcontrols */ + list_for_each_entry_safe(control, _control, &gbcodec->codec_ctl_list, + list) { + list_del(&control->list); + devm_kfree(gbcodec->dev, control); + } + if (gbcodec->kctls) + devm_kfree(gbcodec->dev, gbcodec->kctls); + + /* release widget controls */ + list_for_each_entry_safe(control, _control, &gbcodec->widget_ctl_list, + list) { + list_del(&control->list); + devm_kfree(gbcodec->dev, control); + } + + /* release widgets */ + list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list, + list) { + list_del(&widget->list); + devm_kfree(gbcodec->dev, widget); + } + if (gbcodec->widgets) + devm_kfree(gbcodec->dev, gbcodec->widgets); + + /* release routes */ + if (gbcodec->routes) + devm_kfree(gbcodec->dev, gbcodec->routes); + + /* release DAIs */ + mutex_lock(&gbcodec->lock); + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { + list_del(&dai->list); + devm_kfree(gbcodec->dev, dai); + } + mutex_unlock(&gbcodec->lock); +} -- cgit v1.2.3-59-g8ed1b From b7f0088df7369bafc5029be7c0085961904086fc Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Wed, 13 Jan 2016 14:07:52 -0700 Subject: greybus: audio: Add GB Audio class protocol functionality in GB codec DAI ops GB Audio class driver provides APIs to configure GB codec module. This patch adds relevant operations in DAI ops callback functions to configure codec module as per DAPM sequence triggered. Signed-off-by: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 305 +++++++++++++++++++++++++++++++++- 1 file changed, 302 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 239a9b6b947c..a03caa09a68a 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -12,6 +12,7 @@ #include #include "audio_codec.h" +#include "audio_apbridgea.h" #define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt" #define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data" @@ -19,28 +20,325 @@ static DEFINE_MUTEX(gb_codec_list_lock); static LIST_HEAD(gb_codec_list); +/* + * codec DAI ops + */ static int gbcodec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - return 0; + int ret, found; + __u16 i2s_port, cportid; + + struct gbaudio_dai *gb_dai; + struct gbaudio_codec_info *gb = + (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + + /* find the dai */ + found = 0; + list_for_each_entry(gb_dai, &gb->dai_list, list) { + if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { + found = 1; + break; + } + } + + if (!found) { + dev_err(dai->dev, "%s: DAI not registered\n", dai->name); + return -EINVAL; + } + + /* register cport */ + i2s_port = 0; /* fixed for now */ + cportid = gb_dai->connection->hd_cport_id; + ret = gb_audio_apbridgea_register_cport(gb_dai->connection, i2s_port, + cportid); + dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid, + ret); + + return ret; } static void gbcodec_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + int ret, found; + __u16 i2s_port, cportid; + + struct gbaudio_dai *gb_dai; + struct gbaudio_codec_info *gb = + (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + + /* find the dai */ + found = 0; + list_for_each_entry(gb_dai, &gb->dai_list, list) { + if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { + found = 1; + break; + } + } + + if (!found) { + dev_err(dai->dev, "%s: DAI not registered\n", dai->name); + return; + } + + /* deactivate rx/tx */ + cportid = gb_dai->connection->intf_cport_id; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_gb_deactivate_rx(gb->mgmt_connection, cportid); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection, cportid); + break; + default: + dev_err(dai->dev, "Invalid stream type during shutdown\n"); + return; + } + + if (ret) + dev_err(dai->dev, "%d:Error during deactivate\n", ret); + + /* un register cport */ + i2s_port = 0; /* fixed for now */ + ret = gb_audio_apbridgea_unregister_cport(gb_dai->connection, i2s_port, + gb_dai->connection->hd_cport_id); + + dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name, + gb_dai->connection->hd_cport_id, ret); + + return; } static int gbcodec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai) { - return 0; + int ret, found; + uint8_t sig_bits, channels; + uint32_t format, rate; + uint16_t data_cport; + struct gbaudio_dai *gb_dai; + struct gbaudio_codec_info *gb = + (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + + /* find the dai */ + found = 0; + list_for_each_entry(gb_dai, &gb->dai_list, list) { + if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { + found = 1; + break; + } + } + + if (!found) { + dev_err(dai->dev, "%s: DAI not registered\n", dai->name); + return -EINVAL; + } + + /* + * assuming, currently only 48000 Hz, 16BIT_LE, stereo + * is supported, validate params before configuring codec + */ + if (params_channels(hwparams) != 2) { + dev_err(dai->dev, "Invalid channel count:%d\n", + params_channels(hwparams)); + return -EINVAL; + } + channels = params_channels(hwparams); + + if (params_rate(hwparams) != 48000) { + dev_err(dai->dev, "Invalid sampling rate:%d\n", + params_rate(hwparams)); + return -EINVAL; + } + rate = GB_AUDIO_PCM_RATE_48000; + + if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) { + dev_err(dai->dev, "Invalid format:%d\n", + params_format(hwparams)); + return -EINVAL; + } + format = GB_AUDIO_PCM_FMT_S16_LE; + + data_cport = gb_dai->connection->intf_cport_id; + /* XXX check impact of sig_bit + * it should not change ideally + */ + + dev_dbg(dai->dev, "cport:%d, rate:%d, channel %d, format %d, sig_bits:%d\n", + data_cport, rate, channels, format, sig_bits); + ret = gb_audio_gb_set_pcm(gb->mgmt_connection, data_cport, format, + rate, channels, sig_bits); + if (ret) { + dev_err(dai->dev, "%d: Error during set_pcm\n", ret); + return ret; + } + + /* + * XXX need to check if + * set config is always required + * check for mclk_freq as well + */ + ret = gb_audio_apbridgea_set_config(gb_dai->connection, 0, + AUDIO_APBRIDGEA_PCM_FMT_16, + AUDIO_APBRIDGEA_PCM_RATE_48000, + 6144000); + if (ret) + dev_err(dai->dev, "%d: Error during set_config\n", ret); + + return ret; } static int gbcodec_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - return 0; + int ret, found; + uint16_t data_cport; + struct gbaudio_dai *gb_dai; + struct gbaudio_codec_info *gb = + (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + + /* find the dai */ + found = 0; + list_for_each_entry(gb_dai, &gb->dai_list, list) { + if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { + found = 1; + break; + } + } + + if (!found) { + dev_err(dai->dev, "%s: DAI not registered\n", dai->name); + return -EINVAL; + } + + /* deactivate rx/tx */ + data_cport = gb_dai->connection->intf_cport_id; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_gb_set_rx_data_size(gb->mgmt_connection, + data_cport, 192); + if (ret) { + dev_err(dai->dev, + "%d:Error during set_rx_data_size, cport:%d\n", + ret, data_cport); + return ret; + } + ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0, + 192); + if (ret) { + dev_err(dai->dev, + "%d:Error during apbridgea_set_rx_data_size\n", + ret); + return ret; + } + ret = gb_audio_gb_activate_rx(gb->mgmt_connection, data_cport); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_gb_set_tx_data_size(gb->mgmt_connection, + data_cport, 192); + if (ret) { + dev_err(dai->dev, + "%d:Error during module set_tx_data_size, cport:%d\n", + ret, data_cport); + return ret; + } + ret = gb_audio_apbridgea_set_tx_data_size(gb_dai->connection, 0, + 192); + if (ret) { + dev_err(dai->dev, + "%d:Error during apbridgea set_tx_data_size, cport\n", + ret); + return ret; + } + ret = gb_audio_gb_activate_tx(gb->mgmt_connection, data_cport); + break; + default: + dev_err(dai->dev, "Invalid stream type %d during prepare\n", + substream->stream); + return -EINVAL; + } + + if (ret) + dev_err(dai->dev, "%d: Error during activate stream\n", ret); + + return ret; +} + +static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int ret, found; + int tx, rx, start, stop; + struct gbaudio_dai *gb_dai; + struct gbaudio_codec_info *gb = + (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + + /* find the dai */ + found = 0; + list_for_each_entry(gb_dai, &gb->dai_list, list) { + if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { + found = 1; + break; + } + } + + if (!found) { + dev_err(dai->dev, "%s: DAI not registered\n", dai->name); + return -EINVAL; + } + + tx = rx = start = stop = 0; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + start = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stop = 1; + break; + default: + dev_err(dai->dev, "Invalid tigger cmd:%d\n", cmd); + return -EINVAL; + } + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + rx = 1; + break; + case SNDRV_PCM_STREAM_PLAYBACK: + tx = 1; + break; + default: + dev_err(dai->dev, "Invalid stream type:%d\n", + substream->stream); + return -EINVAL; + } + + if (start && tx) + ret = gb_audio_apbridgea_start_tx(gb_dai->connection, 0, 0); + + else if (start && rx) + ret = gb_audio_apbridgea_start_rx(gb_dai->connection, 0); + + else if (stop && tx) + ret = gb_audio_apbridgea_stop_tx(gb_dai->connection, 0); + + else if (stop && rx) + ret = gb_audio_apbridgea_stop_rx(gb_dai->connection, 0); + + else + ret = -EINVAL; + + if (ret) + dev_err(dai->dev, "%d:Error during %s stream\n", ret, + start ? "Start" : "Stop"); + + return ret; } static int gbcodec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) @@ -57,6 +355,7 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { .startup = gbcodec_startup, .shutdown = gbcodec_shutdown, .hw_params = gbcodec_hw_params, + .trigger = gbcodec_trigger, .prepare = gbcodec_prepare, .set_fmt = gbcodec_set_dai_fmt, .digital_mute = gbcodec_digital_mute, -- cgit v1.2.3-59-g8ed1b From 17247da52ee8694429e089f452dd14f4dbda9f06 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Wed, 13 Jan 2016 14:07:53 -0700 Subject: greybus: audio: Report uevent on GB codec module insertion/removal GB-Audio-manager module is currently used to report uevent to above layer in response to any codec module inserted or removed. Signed-off-by: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 50 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/audio_codec.h | 3 +++ 2 files changed, 53 insertions(+) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index a03caa09a68a..93906c8128de 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -13,6 +13,7 @@ #include "audio_codec.h" #include "audio_apbridgea.h" +#include "audio_manager.h" #define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt" #define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data" @@ -577,6 +578,7 @@ static int gbaudio_codec_probe(struct gb_connection *connection) int ret, i; struct gbaudio_codec_info *gbcodec; struct gb_audio_topology *topology; + struct gb_audio_manager_module_descriptor desc; struct device *dev = &connection->bundle->dev; int dev_id = connection->bundle->id; @@ -639,6 +641,21 @@ static int gbaudio_codec_probe(struct gb_connection *connection) mutex_lock(&gbcodec->lock); gbcodec->codec_registered = 1; + /* inform above layer for uevent */ + if (!gbcodec->set_uevent && + (gbcodec->dai_added == gbcodec->num_dais)) { + dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); + /* prepare for the audio manager */ + strlcpy(desc.name, gbcodec->name, + GB_AUDIO_MANAGER_MODULE_NAME_LEN); /* todo */ + desc.slot = 1; /* todo */ + desc.vid = 2; /* todo */ + desc.pid = 3; /* todo */ + desc.cport = gbcodec->dev_id; + desc.devices = 0x2; /* todo */ + gbcodec->manager_id = gb_audio_manager_add(&desc); + gbcodec->set_uevent = 1; + } mutex_unlock(&gbcodec->lock); return ret; @@ -669,6 +686,16 @@ static void gbaudio_codec_remove(struct gb_connection *connection) if (!gbcodec) return; + /* inform uevent to above layers */ + mutex_lock(&gbcodec->lock); + if (gbcodec->set_uevent) { + /* notify the audio manager */ + dev_dbg(dev, "Inform set_event:%d to above layer\n", 0); + gb_audio_manager_remove(gbcodec->manager_id); + gbcodec->set_uevent = 0; + } + mutex_unlock(&gbcodec->lock); + msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink, 1); gbaudio_remove_dailinks(gbcodec); @@ -712,6 +739,7 @@ static int gbaudio_dai_probe(struct gb_connection *connection) struct device *dev = &connection->bundle->dev; int dev_id = connection->bundle->id; struct gbaudio_codec_info *gbcodec = dev_get_drvdata(dev); + struct gb_audio_manager_module_descriptor desc; dev_dbg(dev, "Add DAI device:%d:%s\n", dev_id, dev_name(dev)); @@ -729,6 +757,22 @@ static int gbaudio_dai_probe(struct gb_connection *connection) /* update dai_added count */ mutex_lock(&gbcodec->lock); gbcodec->dai_added++; + + /* inform above layer for uevent */ + if (!gbcodec->set_uevent && gbcodec->codec_registered && + (gbcodec->dai_added == gbcodec->num_dais)) { + /* prepare for the audio manager */ + dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); + strlcpy(desc.name, gbcodec->name, + GB_AUDIO_MANAGER_MODULE_NAME_LEN); /* todo */ + desc.slot = 1; /* todo */ + desc.vid = 2; /* todo */ + desc.pid = 3; /* todo */ + desc.cport = gbcodec->dev_id; + desc.devices = 0x2; /* todo */ + gbcodec->manager_id = gb_audio_manager_add(&desc); + gbcodec->set_uevent = 1; + } mutex_unlock(&gbcodec->lock); return 0; @@ -749,6 +793,12 @@ static void gbaudio_dai_remove(struct gb_connection *connection) /* inform uevent to above layers */ mutex_lock(&gbcodec->lock); + if (gbcodec->set_uevent) { + /* notify the audio manager */ + dev_dbg(dev, "Inform set_event:%d to above layer\n", 0); + gb_audio_manager_remove(gbcodec->manager_id); + gbcodec->set_uevent = 0; + } /* update dai_added count */ gbcodec->dai_added--; mutex_unlock(&gbcodec->lock); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 5051e06dfff6..fcf3a0602176 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -97,10 +97,13 @@ struct gbaudio_codec_info { int type; int dai_added; int codec_registered; + int set_uevent; char vstr[NAME_SIZE]; char pstr[NAME_SIZE]; struct list_head list; struct gb_audio_topology *topology; + /* need to share this info to above user space */ + int manager_id; char name[NAME_SIZE]; /* soc related data */ -- cgit v1.2.3-59-g8ed1b From 538ecb5a05049fcd23043ed5c97e42c379e5ccb0 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Wed, 13 Jan 2016 14:07:54 -0700 Subject: greybus: audio: cleanup unnecessary dev_err messages Replace unnecessary dev_err msg with dev_dbg. Same were added during development to trace topology parser progress. Signed-off-by: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_topology.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 8840a9c330de..b18574e91b5f 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -837,7 +837,7 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, dai_name); if (!dai) goto error; - dev_err(gbcodec->dev, "%s:DAI added\n", dai->name); + dev_dbg(gbcodec->dev, "%s:DAI added\n", dai->name); gb_dais[i].name = dai->name; curr++; } @@ -1031,7 +1031,7 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, "%d: Error in parsing controls data\n", ret); return ret; } - dev_err(gbcodec->dev, "Control parsing finished\n"); + dev_dbg(gbcodec->dev, "Control parsing finished\n"); /* process DAI */ dais = (struct gb_audio_dai *)gbcodec->dai_offset; @@ -1041,7 +1041,7 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, "%d: Error in parsing DAIs data\n", ret); return ret; } - dev_err(gbcodec->dev, "DAI parsing finished\n"); + dev_dbg(gbcodec->dev, "DAI parsing finished\n"); /* process widgets */ widgets = (struct gb_audio_widget *)gbcodec->widget_offset; @@ -1051,7 +1051,7 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, "%d: Error in parsing widgets data\n", ret); return ret; } - dev_err(gbcodec->dev, "Widget parsing finished\n"); + dev_dbg(gbcodec->dev, "Widget parsing finished\n"); /* process route */ routes = (struct gb_audio_route *)gbcodec->route_offset; @@ -1061,7 +1061,7 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, "%d: Error in parsing routes data\n", ret); return ret; } - dev_err(gbcodec->dev, "Route parsing finished\n"); + dev_dbg(gbcodec->dev, "Route parsing finished\n"); return ret; } -- cgit v1.2.3-59-g8ed1b From 25de3491f11064845a45606fa5828a200ecf8c53 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Wed, 13 Jan 2016 14:07:55 -0700 Subject: greybus: audio: Cleanup GB protocol connections in case of abrupt codec removal We need to clean up GB protocl connections, otherwise successive codec insertions fails repeatedly. NOTE: As per suggestion, since codec is already removed, one should not trigger any GB command. It'll cause a delay of atleast TIMEOUT value. HOwever, failing to cleanup GB protocol, causes successive module insertion to fail Signed-off-by: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 58 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/audio_codec.h | 3 ++ 2 files changed, 61 insertions(+) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 93906c8128de..2b4fcbedd161 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -56,6 +56,9 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid, ret); + if (!ret) + atomic_inc(&gb->users); + return ret; } @@ -83,6 +86,8 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, return; } + atomic_dec(&gb->users); + /* deactivate rx/tx */ cportid = gb_dai->connection->intf_cport_id; @@ -428,6 +433,7 @@ static struct snd_soc_codec_driver soc_codec_dev_gbcodec = { .reg_word_size = 1, .idle_bias_off = true, + .ignore_pmdown_time = 1, }; /* @@ -573,6 +579,50 @@ struct device_driver gb_codec_driver = { .owner = THIS_MODULE, }; +/* XXX + * since BE DAI path is not yet properly closed from above layer, + * dsp dai.mi2s_dai_data.status_mask is still set to STATUS_PORT_STARTED + * this causes immediate playback/capture to fail in case relevant mixer + * control is not turned OFF + * user need to try once again after failure to recover DSP state. + */ +static void gb_audio_cleanup(struct gbaudio_codec_info *gb) +{ + int cportid, ret; + struct gbaudio_dai *gb_dai; + struct gb_connection *connection; + struct device *dev = gb->dev; + + list_for_each_entry(gb_dai, &gb->dai_list, list) { + /* + * In case of BE dailink, need to deactivate APBridge + * manually + */ + if (gbaudio_dailink.no_pcm && atomic_read(&gb->users)) { + connection = gb_dai->connection; + /* PB active */ + ret = gb_audio_apbridgea_stop_tx(connection, 0); + if (ret) + dev_info(dev, "%d:Failed during APBridge stop_tx\n", + ret); + cportid = connection->intf_cport_id; + ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection, + cportid); + if (ret) + dev_info(dev, + "%d:Failed during deactivate_tx\n", + ret); + cportid = connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(connection, 0, + cportid); + if (ret) + dev_info(dev, "%d:Failed during unregister cport\n", + ret); + atomic_dec(&gb->users); + } + } +} + static int gbaudio_codec_probe(struct gb_connection *connection) { int ret, i; @@ -641,6 +691,9 @@ static int gbaudio_codec_probe(struct gb_connection *connection) mutex_lock(&gbcodec->lock); gbcodec->codec_registered = 1; + /* codec cleanup related */ + atomic_set(&gbcodec->users, 0); + /* inform above layer for uevent */ if (!gbcodec->set_uevent && (gbcodec->dai_added == gbcodec->num_dais)) { @@ -696,6 +749,11 @@ static void gbaudio_codec_remove(struct gb_connection *connection) } mutex_unlock(&gbcodec->lock); + if (atomic_read(&gbcodec->users)) { + dev_err(dev, "Cleanup Error: BE stream not yet closed\n"); + gb_audio_cleanup(gbcodec); + } + msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink, 1); gbaudio_remove_dailinks(gbcodec); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index fcf3a0602176..0b587ef74d16 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -131,6 +131,9 @@ struct gbaudio_codec_info { struct snd_soc_dapm_route *routes; struct snd_soc_dai_driver *dais; + /* codec users */ + atomic_t users; + /* lists */ struct list_head dai_list; struct list_head widget_list; -- cgit v1.2.3-59-g8ed1b From 83ec628386aee5e14fe1be3e27e84124dfa7b165 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Wed, 13 Jan 2016 14:07:56 -0700 Subject: greybus: audio: Enable codec module detection on different slots driver_name associated with dev_name was hard coded earlier. This limits, audio codec module to be detected on Port#5 only. Now, driver_name is generated dynamically based on dev_name. This enables codec module detection on any 1x2 slot. Also, Update dev_id based on slot number, instead of bundle->id. bundle->id is not unique for multiple modules added, thus now using slot number for unique identification. Signed-off-by: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 2b4fcbedd161..79f2baf607f4 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -575,7 +575,6 @@ static void gbaudio_free_codec(struct device *dev, * GB codec module driver ops */ struct device_driver gb_codec_driver = { - .name = "1-8", .owner = THIS_MODULE, }; @@ -626,11 +625,12 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) static int gbaudio_codec_probe(struct gb_connection *connection) { int ret, i; + char *driver_name; struct gbaudio_codec_info *gbcodec; struct gb_audio_topology *topology; struct gb_audio_manager_module_descriptor desc; struct device *dev = &connection->bundle->dev; - int dev_id = connection->bundle->id; + int dev_id = connection->intf->interface_id; dev_dbg(dev, "Add device:%d:%s\n", dev_id, dev_name(dev)); /* get gbcodec data */ @@ -670,6 +670,10 @@ static int gbaudio_codec_probe(struct gb_connection *connection) gbcodec->dais[i].ops = &gbcodec_dai_ops; /* FIXME */ + driver_name = devm_kzalloc(dev, NAME_SIZE, GFP_KERNEL); + strlcpy(driver_name, gbcodec->name, NAME_SIZE); + gb_codec_driver.name = strsep(&driver_name, "."); + dev_dbg(dev, "driver.name:%s\n", gb_codec_driver.name); dev->driver = &gb_codec_driver; /* register codec */ @@ -730,7 +734,7 @@ static void gbaudio_codec_remove(struct gb_connection *connection) { struct gbaudio_codec_info *gbcodec; struct device *dev = &connection->bundle->dev; - int dev_id = connection->bundle->id; + int dev_id = connection->intf->interface_id; dev_dbg(dev, "Remove device:%d:%s\n", dev_id, dev_name(dev)); @@ -795,7 +799,7 @@ static int gbaudio_dai_probe(struct gb_connection *connection) { struct gbaudio_dai *dai; struct device *dev = &connection->bundle->dev; - int dev_id = connection->bundle->id; + int dev_id = connection->intf->interface_id; struct gbaudio_codec_info *gbcodec = dev_get_drvdata(dev); struct gb_audio_manager_module_descriptor desc; @@ -839,7 +843,7 @@ static int gbaudio_dai_probe(struct gb_connection *connection) static void gbaudio_dai_remove(struct gb_connection *connection) { struct device *dev = &connection->bundle->dev; - int dev_id = connection->bundle->id; + int dev_id = connection->intf->interface_id; struct gbaudio_codec_info *gbcodec; dev_dbg(dev, "Remove DAI device:%d:%s\n", dev_id, dev_name(dev)); -- cgit v1.2.3-59-g8ed1b From 4068487ce3dba22bdc7bf34e7327b474494430b5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 15 Jan 2016 01:33:55 +0200 Subject: greybus: camera: Return the result flags from the configure_streams response And return the response num_streams field through a pointer variable instead of using the return value of the function as both an error code and a positive number of streams, it makes the API more consistent. Signed-off-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index a69797b13c84..deb55da9032e 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -108,13 +108,14 @@ struct ap_csi_config_request { } __packed; static int gb_camera_configure_streams(struct gb_camera *gcam, - unsigned int nstreams, - unsigned int flags, + unsigned int *num_streams, + unsigned int *flags, struct gb_camera_stream_config *streams) { struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; struct ap_csi_config_request csi_cfg; + unsigned int nstreams = *num_streams; unsigned int i; size_t req_size; size_t resp_size; @@ -134,7 +135,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, } req->num_streams = nstreams; - req->flags = flags; + req->flags = *flags; req->padding = 0; for (i = 0; i < nstreams; ++i) { @@ -165,6 +166,8 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto done; } + *flags = resp->flags; + for (i = 0; i < nstreams; ++i) { struct gb_camera_stream_config_response *cfg = &resp->config[i]; @@ -205,7 +208,8 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, gcam_err(gcam, "failed to %s the CSI transmitter\n", nstreams ? "start" : "stop"); - ret = resp->num_streams; + *num_streams = resp->num_streams; + ret = 0; done: kfree(req); @@ -316,6 +320,7 @@ static int gb_camera_op_configure_streams(void *priv, unsigned int nstreams, { struct gb_camera *gcam = priv; struct gb_camera_stream_config *gb_streams; + unsigned int flags = 0; unsigned int i; int ret; @@ -333,7 +338,7 @@ static int gb_camera_op_configure_streams(void *priv, unsigned int nstreams, gb_camera_mbus_to_gb(streams[i].pixel_code); } - ret = gb_camera_configure_streams(gcam, nstreams, 0, gb_streams); + ret = gb_camera_configure_streams(gcam, &nstreams, &flags, gb_streams); if (ret < 0) goto done; @@ -455,12 +460,11 @@ static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, goto done; } - ret = gb_camera_configure_streams(gcam, nstreams, flags, streams); + ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams); if (ret < 0) goto done; - nstreams = ret; - buffer->length = sprintf(buffer->data, "%u;", nstreams); + buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags); for (i = 0; i < nstreams; ++i) { struct gb_camera_stream_config *stream = &streams[i]; -- cgit v1.2.3-59-g8ed1b From 11ca550c226b87c4bf457580a817e4529c7aca62 Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos Date: Fri, 15 Jan 2016 21:34:56 +0200 Subject: greybus: camera: Fix backword compatibility in configure streams Configure streams ret code should be number of streams in gb operation exported to HOST camera. Until gb operation is migrated to new interface return number of streams in ret code. Signed-off-by: Gjorgji Rosikopulos Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index deb55da9032e..8b214337c4f9 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -352,6 +352,8 @@ static int gb_camera_op_configure_streams(void *priv, unsigned int nstreams, streams[i].pixel_code = gb_camera_gb_to_mbus(gb_streams[i].format); } + /* For backward compatibility return number of streams in ret code */ + ret = nstreams; done: kfree(gb_streams); -- cgit v1.2.3-59-g8ed1b From b55d9431d62d6ce762d4b50ec93bbb81dce783c6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 15 Jan 2016 14:41:02 -0800 Subject: greybus: Revert "camera: Fix backword compatibility in configure streams" This reverts commit 48cc91e52dba9abad4c9b56f911994c65071bfc4 as the caller should be changed instead. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 8b214337c4f9..deb55da9032e 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -352,8 +352,6 @@ static int gb_camera_op_configure_streams(void *priv, unsigned int nstreams, streams[i].pixel_code = gb_camera_gb_to_mbus(gb_streams[i].format); } - /* For backward compatibility return number of streams in ret code */ - ret = nstreams; done: kfree(gb_streams); -- cgit v1.2.3-59-g8ed1b From b29906af20cf74bda2ec702626a26dc4501866ef Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Fri, 15 Jan 2016 14:03:17 -0800 Subject: greybus: firmware: replace colons with underscore in firmware file request Due to some issues with handling colons in Android (and possibly future exposure to other problems) we should remove the colons from the firmware file request. Replacing them with underscores. Specifically, we copy firmware into the Android build using a line similar to this: PRODUCT_COPY_FILES += \ source-repo/ara:00000126:00001000:00000001:00000001:02.tftf:system/etc/firmware/ara:00000126:00001000:00000001:00000001:02.tftf There is a colon delimiter between the source and destination in the PRODUCT_COPY_FILES format. The greybus naming logic breaks the parsing routine and generates an Android build break. Signed-off-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index b65787c4116e..11d605b97bd0 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -84,7 +84,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) * XXX Name it properly.. */ snprintf(firmware_name, sizeof(firmware_name), - "ara:%08x:%08x:%08x:%08x:%02x.tftf", + "ara_%08x_%08x_%08x_%08x_%02x.tftf", intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, firmware->vendor_id, firmware->product_id, stage); -- cgit v1.2.3-59-g8ed1b From 24ac4fa490bf105c202b843280eb67b2327c337f Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos Date: Sun, 17 Jan 2016 19:52:20 +0200 Subject: greybus: camera: Add support for configure streams flag in gb interface Update gb interface and export flags needed for latest protocol version. Number of streams also can be changed based on operation result. Caller sets input flags, end fucntion return output flags Input flags supported: - GB_CAMERA_IN_FLAG_TEST - Need to be set when operation is not actually applied. Output flags supported: - GB_CAMERA_OUT_FLAG_ADJUSTED - This is result of the operation if this flag is set, result is adjusted and operation need to be repeat. Signed-off-by: Gjorgji Rosikopulos Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gb-camera.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gb-camera.h b/drivers/staging/greybus/gb-camera.h index a1824740ef83..50af0573737b 100644 --- a/drivers/staging/greybus/gb-camera.h +++ b/drivers/staging/greybus/gb-camera.h @@ -10,6 +10,11 @@ #include +/* Input flags need to be set from the caller */ +#define GB_CAMERA_IN_FLAG_TEST (1 << 0) +/* Output flags returned */ +#define GB_CAMERA_OUT_FLAG_ADJUSTED (1 << 0) + struct gb_camera_stream { unsigned int width; unsigned int height; @@ -21,8 +26,8 @@ struct gb_camera_stream { struct gb_camera_ops { ssize_t (*capabilities)(void *priv, char *buf, size_t len); - int (*configure_streams)(void *priv, unsigned int nstreams, - struct gb_camera_stream *streams); + int (*configure_streams)(void *priv, unsigned int *nstreams, + unsigned int *flags, struct gb_camera_stream *streams); int (*capture)(void *priv, u32 request_id, unsigned int streams, unsigned int num_frames, size_t settings_size, const void *settings); -- cgit v1.2.3-59-g8ed1b From 5b0327103ff0f5498af008cb7189770c1a33b67b Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos Date: Sun, 17 Jan 2016 19:52:21 +0200 Subject: greybus: camera: Update configure stream based on new interface Interface has been changed. return code will not return number of configured streams but just error code. Number of streams is passed as pointer and if operation result is changed number of streams will be updated. Flags are also used for information regarding configure stream operation result. Signed-off-by: Gjorgji Rosikopulos Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index deb55da9032e..6f14848fa854 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -315,34 +315,47 @@ static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt) return mbus_to_gbus_format[0].mbus_code; } -static int gb_camera_op_configure_streams(void *priv, unsigned int nstreams, - struct gb_camera_stream *streams) +static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, + unsigned int *flags, struct gb_camera_stream *streams) { struct gb_camera *gcam = priv; struct gb_camera_stream_config *gb_streams; - unsigned int flags = 0; + unsigned int gb_flags = 0; + unsigned int gb_nstreams = *nstreams; unsigned int i; int ret; - if (nstreams > GB_CAMERA_MAX_STREAMS) + if (gb_nstreams > GB_CAMERA_MAX_STREAMS) return -EINVAL; - gb_streams = kzalloc(nstreams * sizeof(*gb_streams), GFP_KERNEL); + gb_streams = kzalloc(gb_nstreams * sizeof(*gb_streams), GFP_KERNEL); if (!gb_streams) return -ENOMEM; - for (i = 0; i < nstreams; i++) { + for (i = 0; i < gb_nstreams; i++) { gb_streams[i].width = streams[i].width; gb_streams[i].height = streams[i].height; gb_streams[i].format = gb_camera_mbus_to_gb(streams[i].pixel_code); } - ret = gb_camera_configure_streams(gcam, &nstreams, &flags, gb_streams); + if (*flags & GB_CAMERA_IN_FLAG_TEST) + gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY; + + ret = gb_camera_configure_streams(gcam, &gb_nstreams, + &gb_flags, gb_streams); if (ret < 0) goto done; + if (gb_nstreams > *nstreams) { + ret = -EINVAL; + goto done; + } + + *flags = 0; + if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) + *flags |= GB_CAMERA_OUT_FLAG_ADJUSTED; - for (i = 0; i < nstreams; i++) { + for (i = 0; i < gb_nstreams; i++) { streams[i].width = gb_streams[i].width; streams[i].height = gb_streams[i].height; streams[i].vc = gb_streams[i].vc; @@ -352,6 +365,7 @@ static int gb_camera_op_configure_streams(void *priv, unsigned int nstreams, streams[i].pixel_code = gb_camera_gb_to_mbus(gb_streams[i].format); } + *nstreams = gb_nstreams; done: kfree(gb_streams); -- cgit v1.2.3-59-g8ed1b From 5dda7e5a484295f24ecfcba6b64da6d37dccb912 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:50:59 +0100 Subject: greybus: legacy: add legacy-protocol bundle driver Add the first Greybus bundle driver that will be used when transitioning from legacy Greybus protocols to bundle drivers. The legacy-protocol driver initially binds to all current bundle classes. In order to avoid having to update current module-loading scripts, keep this driver internal to greybus core at least until modalias support is added. Note that this prevents unloading any protocol drivers without first tearing down the host device due to a circular module dependency. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 3 +- drivers/staging/greybus/bundle.c | 10 ---- drivers/staging/greybus/connection.c | 2 + drivers/staging/greybus/core.c | 10 ++++ drivers/staging/greybus/interface.c | 10 ---- drivers/staging/greybus/legacy.c | 95 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/legacy.h | 16 ++++++ 7 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 drivers/staging/greybus/legacy.c create mode 100644 drivers/staging/greybus/legacy.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index b1272cbd55ee..1e24509bdb4d 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -9,7 +9,8 @@ greybus-y := core.o \ control.o \ svc.o \ firmware.o \ - operation.o + operation.o \ + legacy.o gb-phy-y := gpbridge.o \ sdio.o \ diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index fb775ff1895d..ec52bdecb5c1 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -152,21 +152,11 @@ int gb_bundle_add(struct gb_bundle *bundle) return 0; } -static void gb_bundle_connections_exit(struct gb_bundle *bundle) -{ - struct gb_connection *connection; - - list_for_each_entry(connection, &bundle->connections, bundle_links) - gb_connection_exit(connection); -} - /* * Tear down a previously set up bundle. */ void gb_bundle_destroy(struct gb_bundle *bundle) { - gb_bundle_connections_exit(bundle); - if (device_is_registered(&bundle->dev)) device_del(&bundle->dev); diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 6295d28a4be4..21e6eb52c437 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -464,6 +464,7 @@ err_unbind_protocol: return ret; } +EXPORT_SYMBOL_GPL(gb_connection_init); void gb_connection_exit(struct gb_connection *connection) { @@ -482,6 +483,7 @@ void gb_connection_exit(struct gb_connection *connection) gb_connection_unbind_protocol(connection); } +EXPORT_SYMBOL_GPL(gb_connection_exit); /* * Tear down a previously set up connection. diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 0e2f99df6cfa..28f48d79b005 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -12,6 +12,7 @@ #define CREATE_TRACE_POINTS #include "greybus.h" #include "greybus_trace.h" +#include "legacy.h" EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_send); EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_recv); @@ -240,8 +241,16 @@ static int __init gb_init(void) goto error_firmware; } + retval = gb_legacy_init(); + if (retval) { + pr_err("gb_legacy_init failed\n"); + goto error_legacy; + } + return 0; /* Success */ +error_legacy: + gb_firmware_protocol_exit(); error_firmware: gb_svc_protocol_exit(); error_svc: @@ -261,6 +270,7 @@ module_init(gb_init); static void __exit gb_exit(void) { + gb_legacy_exit(); gb_firmware_protocol_exit(); gb_svc_protocol_exit(); gb_control_protocol_exit(); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index edac2383e492..c3453502ae22 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -183,7 +183,6 @@ void gb_interfaces_remove(struct gb_host_device *hd) int gb_interface_init(struct gb_interface *intf, u8 device_id) { struct gb_bundle *bundle, *tmp; - struct gb_connection *connection; int ret, size; void *manifest; @@ -242,15 +241,6 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) gb_bundle_destroy(bundle); continue; } - - list_for_each_entry(connection, &bundle->connections, - bundle_links) { - ret = gb_connection_init(connection); - if (ret) - break; - } - if (ret) - gb_bundle_destroy(bundle); } ret = 0; diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c new file mode 100644 index 000000000000..26a7d1a05256 --- /dev/null +++ b/drivers/staging/greybus/legacy.c @@ -0,0 +1,95 @@ +/* + * Greybus legacy-protocol driver + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" +#include "legacy.h" + +static int legacy_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + struct gb_connection *connection; + int ret; + + dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__, + bundle->class); + + list_for_each_entry(connection, &bundle->connections, bundle_links) { + dev_dbg(&bundle->dev, "enabling connection %s\n", + connection->name); + + ret = gb_connection_init(connection); + if (ret) + goto err_connections_disable; + } + + return 0; + +err_connections_disable: + list_for_each_entry_reverse(connection, &bundle->connections, + bundle_links) { + gb_connection_exit(connection); + } + + return ret; +} + +static void legacy_disconnect(struct gb_bundle *bundle) +{ + struct gb_connection *connection; + + dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__, + bundle->class); + + list_for_each_entry_reverse(connection, &bundle->connections, + bundle_links) { + gb_connection_exit(connection); + } +} + +static const struct greybus_bundle_id legacy_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_HID) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_DISPLAY) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SENSOR) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_MGMT) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_DATA) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SVC) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) }, + { } +}; +MODULE_DEVICE_TABLE(greybus, legacy_id_table); + +static struct greybus_driver legacy_driver = { + .name = "legacy", + .probe = legacy_probe, + .disconnect = legacy_disconnect, + .id_table = legacy_id_table, +}; + +int gb_legacy_init(void) +{ + return greybus_register(&legacy_driver); +} + +void gb_legacy_exit(void) +{ + greybus_deregister(&legacy_driver); +} diff --git a/drivers/staging/greybus/legacy.h b/drivers/staging/greybus/legacy.h new file mode 100644 index 000000000000..2177f6aaed3c --- /dev/null +++ b/drivers/staging/greybus/legacy.h @@ -0,0 +1,16 @@ +/* + * Greybus legacy-protocol driver + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __LEGACY_H +#define __LEGACY_H + +int gb_legacy_init(void); +void gb_legacy_exit(void); + +#endif /* __LEGACY_H */ -- cgit v1.2.3-59-g8ed1b From 6d3d95042e6b3bede0199bad0d08f71b4f7735fb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:00 +0100 Subject: greybus: connection: rename legacy init and exit functions Rename legacy connection init and exit functions. This is a step towards removing the legacy-protocol handling from core. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 10 +++++----- drivers/staging/greybus/connection.h | 7 +++++-- drivers/staging/greybus/control.c | 4 ++-- drivers/staging/greybus/legacy.c | 6 +++--- drivers/staging/greybus/svc.c | 4 ++-- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 21e6eb52c437..d0e3f91d00d5 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -435,7 +435,7 @@ void gb_connection_disable(struct gb_connection *connection) } EXPORT_SYMBOL_GPL(gb_connection_disable); -int gb_connection_init(struct gb_connection *connection) +int gb_connection_legacy_init(struct gb_connection *connection) { int ret; @@ -464,9 +464,9 @@ err_unbind_protocol: return ret; } -EXPORT_SYMBOL_GPL(gb_connection_init); +EXPORT_SYMBOL_GPL(gb_connection_legacy_init); -void gb_connection_exit(struct gb_connection *connection) +void gb_connection_legacy_exit(struct gb_connection *connection) { spin_lock_irq(&connection->lock); if (connection->state != GB_CONNECTION_STATE_ENABLED) { @@ -483,7 +483,7 @@ void gb_connection_exit(struct gb_connection *connection) gb_connection_unbind_protocol(connection); } -EXPORT_SYMBOL_GPL(gb_connection_exit); +EXPORT_SYMBOL_GPL(gb_connection_legacy_exit); /* * Tear down a previously set up connection. @@ -495,7 +495,7 @@ void gb_connection_destroy(struct gb_connection *connection) if (WARN_ON(!connection)) return; - gb_connection_exit(connection); + gb_connection_legacy_exit(connection); spin_lock_irq(&gb_connections_lock); list_del(&connection->bundle_links); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index ef31c8d6dfe0..b0a67f279fb1 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -62,8 +62,11 @@ static inline bool gb_connection_is_static(struct gb_connection *connection) return !connection->intf; } -int gb_connection_init(struct gb_connection *connection); -void gb_connection_exit(struct gb_connection *connection); +int gb_connection_enable(struct gb_connection *connection); +void gb_connection_disable(struct gb_connection *connection); + +int gb_connection_legacy_init(struct gb_connection *connection); +void gb_connection_legacy_exit(struct gb_connection *connection); void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length); diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 4d65dbff6e93..1c4994b84629 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -116,7 +116,7 @@ int gb_control_enable(struct gb_control *control) dev_dbg(&control->connection->intf->dev, "%s\n", __func__); - ret = gb_connection_init(control->connection); + ret = gb_connection_legacy_init(control->connection); if (ret) { dev_err(&control->connection->intf->dev, "failed to enable control connection: %d\n", @@ -131,7 +131,7 @@ void gb_control_disable(struct gb_control *control) { dev_dbg(&control->connection->intf->dev, "%s\n", __func__); - gb_connection_exit(control->connection); + gb_connection_legacy_exit(control->connection); } void gb_control_destroy(struct gb_control *control) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 26a7d1a05256..e8d9cb93b831 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -23,7 +23,7 @@ static int legacy_probe(struct gb_bundle *bundle, dev_dbg(&bundle->dev, "enabling connection %s\n", connection->name); - ret = gb_connection_init(connection); + ret = gb_connection_legacy_init(connection); if (ret) goto err_connections_disable; } @@ -33,7 +33,7 @@ static int legacy_probe(struct gb_bundle *bundle, err_connections_disable: list_for_each_entry_reverse(connection, &bundle->connections, bundle_links) { - gb_connection_exit(connection); + gb_connection_legacy_exit(connection); } return ret; @@ -48,7 +48,7 @@ static void legacy_disconnect(struct gb_bundle *bundle) list_for_each_entry_reverse(connection, &bundle->connections, bundle_links) { - gb_connection_exit(connection); + gb_connection_legacy_exit(connection); } } diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index be62d308d1c9..0bbcddaefbb4 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -815,7 +815,7 @@ int gb_svc_add(struct gb_svc *svc) * is added from the connection request handler when enough * information has been received. */ - ret = gb_connection_init(svc->connection); + ret = gb_connection_legacy_init(svc->connection); if (ret) return ret; @@ -830,7 +830,7 @@ void gb_svc_del(struct gb_svc *svc) if (device_is_registered(&svc->dev)) device_del(&svc->dev); - gb_connection_exit(svc->connection); + gb_connection_legacy_exit(svc->connection); flush_workqueue(svc->wq); } -- cgit v1.2.3-59-g8ed1b From 1cbfab3804b185ce2744b5b8064d06ca19491e36 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:01 +0100 Subject: greybus: connection: remove disable from destructor Remove implicit disable of legacy connections from the destructor. This is a step towards removing the legacy-protocol handling from core. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index d0e3f91d00d5..29a8193931b3 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -495,8 +495,6 @@ void gb_connection_destroy(struct gb_connection *connection) if (WARN_ON(!connection)) return; - gb_connection_legacy_exit(connection); - spin_lock_irq(&gb_connections_lock); list_del(&connection->bundle_links); list_del(&connection->hd_links); -- cgit v1.2.3-59-g8ed1b From bfa9a5e2d07937a7620d55ff6eb55b480bc13100 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:02 +0100 Subject: greybus: connection: add per-connection request handlers Add a connection request-handler field to struct gb_connection that is set when the connection is enabled. This is a step towards removing the legacy protocol abstraction from core, and will also be used to implement unidirectional connection states (e.g. only outgoing operations are allowed). Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 19 +++++++++++++++++-- drivers/staging/greybus/connection.h | 9 ++++++++- drivers/staging/greybus/operation.c | 8 ++------ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 29a8193931b3..3d7a9ca9ce2b 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -387,7 +387,8 @@ static int gb_connection_protocol_get_version(struct gb_connection *connection) return 0; } -int gb_connection_enable(struct gb_connection *connection) +int gb_connection_enable(struct gb_connection *connection, + gb_request_handler_t handler) { int ret; @@ -400,6 +401,7 @@ int gb_connection_enable(struct gb_connection *connection) goto err_hd_cport_disable; spin_lock_irq(&connection->lock); + connection->handler = handler; connection->state = GB_CONNECTION_STATE_ENABLED; spin_unlock_irq(&connection->lock); @@ -435,15 +437,28 @@ void gb_connection_disable(struct gb_connection *connection) } EXPORT_SYMBOL_GPL(gb_connection_disable); +static int gb_legacy_request_handler(struct gb_operation *operation) +{ + struct gb_protocol *protocol = operation->connection->protocol; + + return protocol->request_recv(operation->type, operation); +} + int gb_connection_legacy_init(struct gb_connection *connection) { + gb_request_handler_t handler; int ret; ret = gb_connection_bind_protocol(connection); if (ret) return ret; - ret = gb_connection_enable(connection); + if (connection->protocol->request_recv) + handler = gb_legacy_request_handler; + else + handler = NULL; + + ret = gb_connection_enable(connection, handler); if (ret) goto err_unbind_protocol; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index b0a67f279fb1..ec0e46903819 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -20,6 +20,10 @@ enum gb_connection_state { GB_CONNECTION_STATE_DESTROYING = 3, }; +struct gb_operation; + +typedef int (*gb_request_handler_t)(struct gb_operation *); + struct gb_connection { struct gb_host_device *hd; struct gb_interface *intf; @@ -31,6 +35,8 @@ struct gb_connection { struct list_head hd_links; struct list_head bundle_links; + gb_request_handler_t handler; + struct gb_protocol *protocol; u8 protocol_id; u8 major; @@ -62,7 +68,8 @@ static inline bool gb_connection_is_static(struct gb_connection *connection) return !connection->intf; } -int gb_connection_enable(struct gb_connection *connection); +int gb_connection_enable(struct gb_connection *connection, + gb_request_handler_t handler); void gb_connection_disable(struct gb_connection *connection); int gb_connection_legacy_init(struct gb_connection *connection); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index ae3ada0b54e3..4dc79cb12397 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -218,15 +218,11 @@ static void gb_message_cancel(struct gb_message *message) static void gb_operation_request_handle(struct gb_operation *operation) { struct gb_connection *connection = operation->connection; - struct gb_protocol *protocol = connection->protocol; int status; int ret; - if (!protocol) - return; - - if (protocol->request_recv) { - status = protocol->request_recv(operation->type, operation); + if (connection->handler) { + status = connection->handler(operation); } else { dev_err(&connection->hd->dev, "%s: unexpected incoming request of type 0x%02x\n", -- cgit v1.2.3-59-g8ed1b From 81fba24969c0ca427a8cd3d31bea10749f15b737 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:03 +0100 Subject: greybus: connection: always cancel operations on connection disable Always cancel all operations on connection disable and remove the now unused DESTROYING state. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 17 ++++++++--------- drivers/staging/greybus/connection.h | 1 - 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3d7a9ca9ce2b..0fd7c051c2d3 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -426,12 +426,17 @@ EXPORT_SYMBOL_GPL(gb_connection_enable); void gb_connection_disable(struct gb_connection *connection) { + if (connection->state == GB_CONNECTION_STATE_DISABLED) + return; + gb_connection_control_disconnected(connection); spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; spin_unlock_irq(&connection->lock); + gb_connection_cancel_operations(connection, -ESHUTDOWN); + gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); } @@ -483,19 +488,13 @@ EXPORT_SYMBOL_GPL(gb_connection_legacy_init); void gb_connection_legacy_exit(struct gb_connection *connection) { - spin_lock_irq(&connection->lock); - if (connection->state != GB_CONNECTION_STATE_ENABLED) { - spin_unlock_irq(&connection->lock); + if (connection->state == GB_CONNECTION_STATE_DISABLED) return; - } - connection->state = GB_CONNECTION_STATE_DESTROYING; - spin_unlock_irq(&connection->lock); - - gb_connection_cancel_operations(connection, -ESHUTDOWN); - connection->protocol->connection_exit(connection); gb_connection_disable(connection); + connection->protocol->connection_exit(connection); + gb_connection_unbind_protocol(connection); } EXPORT_SYMBOL_GPL(gb_connection_legacy_exit); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index ec0e46903819..b774b9c56744 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -17,7 +17,6 @@ enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, GB_CONNECTION_STATE_DISABLED = 1, GB_CONNECTION_STATE_ENABLED = 2, - GB_CONNECTION_STATE_DESTROYING = 3, }; struct gb_operation; -- cgit v1.2.3-59-g8ed1b From 520c6eae991299720bcc0668326fb1e3a4bba3ba Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:04 +0100 Subject: greybus: connection: clean up operation cancellation on disable Move helper to cancel active operations above gb_connection_enable and simplify locking. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 59 +++++++++++++++++------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 0fd7c051c2d3..f782e8b4a594 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -247,35 +247,6 @@ static void gb_connection_hd_cport_disable(struct gb_connection *connection) hd->driver->cport_disable(hd, connection->hd_cport_id); } -/* - * Cancel all active operations on a connection. - * - * Should only be called during connection tear down. - */ -static void gb_connection_cancel_operations(struct gb_connection *connection, - int errno) -{ - struct gb_operation *operation; - - spin_lock_irq(&connection->lock); - while (!list_empty(&connection->operations)) { - operation = list_last_entry(&connection->operations, - struct gb_operation, links); - gb_operation_get(operation); - spin_unlock_irq(&connection->lock); - - if (gb_operation_is_incoming(operation)) - gb_operation_cancel_incoming(operation, errno); - else - gb_operation_cancel(operation, errno); - - gb_operation_put(operation); - - spin_lock_irq(&connection->lock); - } - spin_unlock_irq(&connection->lock); -} - /* * Request the SVC to create a connection from AP's cport to interface's * cport. @@ -387,6 +358,33 @@ static int gb_connection_protocol_get_version(struct gb_connection *connection) return 0; } +/* + * Cancel all active operations on a connection. + * + * Locking: Called with connection lock held and state set to DISABLED. + */ +static void gb_connection_cancel_operations(struct gb_connection *connection, + int errno) +{ + struct gb_operation *operation; + + while (!list_empty(&connection->operations)) { + operation = list_last_entry(&connection->operations, + struct gb_operation, links); + gb_operation_get(operation); + spin_unlock_irq(&connection->lock); + + if (gb_operation_is_incoming(operation)) + gb_operation_cancel_incoming(operation, errno); + else + gb_operation_cancel(operation, errno); + + gb_operation_put(operation); + + spin_lock_irq(&connection->lock); + } +} + int gb_connection_enable(struct gb_connection *connection, gb_request_handler_t handler) { @@ -433,9 +431,8 @@ void gb_connection_disable(struct gb_connection *connection) spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; - spin_unlock_irq(&connection->lock); - gb_connection_cancel_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); -- cgit v1.2.3-59-g8ed1b From 192bee4b1a7adbf16d12721c77d6268c76428323 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:05 +0100 Subject: greybus: connection: disable operations on enable errors Make sure to cancel all (incoming) operations when failing to enable a connection. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f782e8b4a594..0159fbd9d78e 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -412,6 +412,7 @@ int gb_connection_enable(struct gb_connection *connection, err_svc_destroy: spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; + gb_connection_cancel_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); gb_connection_svc_connection_destroy(connection); -- cgit v1.2.3-59-g8ed1b From 23268785b288c25ab5ee2ec523982546b5353880 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:06 +0100 Subject: greybus: connection: make connection enable/disable thread safe Add connection mutex to protect connection enabling and disabling. This is needed to eventually allow drivers to manage the state of their connections during operation (i.e. post probe and pre disconnect). Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 20 ++++++++++++++++++-- drivers/staging/greybus/connection.h | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 0159fbd9d78e..56588f357b20 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -170,6 +170,7 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, connection->state = GB_CONNECTION_STATE_DISABLED; atomic_set(&connection->op_cycle, 0); + mutex_init(&connection->mutex); spin_lock_init(&connection->lock); INIT_LIST_HEAD(&connection->operations); @@ -390,9 +391,14 @@ int gb_connection_enable(struct gb_connection *connection, { int ret; + mutex_lock(&connection->mutex); + + if (connection->state == GB_CONNECTION_STATE_ENABLED) + goto out_unlock; + ret = gb_connection_hd_cport_enable(connection); if (ret) - return ret; + goto err_unlock; ret = gb_connection_svc_connection_create(connection); if (ret) @@ -407,6 +413,9 @@ int gb_connection_enable(struct gb_connection *connection, if (ret) goto err_svc_destroy; +out_unlock: + mutex_unlock(&connection->mutex); + return 0; err_svc_destroy: @@ -418,6 +427,8 @@ err_svc_destroy: gb_connection_svc_connection_destroy(connection); err_hd_cport_disable: gb_connection_hd_cport_disable(connection); +err_unlock: + mutex_unlock(&connection->mutex); return ret; } @@ -425,8 +436,10 @@ EXPORT_SYMBOL_GPL(gb_connection_enable); void gb_connection_disable(struct gb_connection *connection) { + mutex_lock(&connection->mutex); + if (connection->state == GB_CONNECTION_STATE_DISABLED) - return; + goto out_unlock; gb_connection_control_disconnected(connection); @@ -437,6 +450,9 @@ void gb_connection_disable(struct gb_connection *connection) gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); + +out_unlock: + mutex_unlock(&connection->mutex); } EXPORT_SYMBOL_GPL(gb_connection_disable); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index b774b9c56744..e7c6feb76e80 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -43,6 +43,7 @@ struct gb_connection { u8 module_major; u8 module_minor; + struct mutex mutex; spinlock_t lock; enum gb_connection_state state; struct list_head operations; -- cgit v1.2.3-59-g8ed1b From 570dfa7c55a76258b03a2d93f9db12dacc2ad3c6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:07 +0100 Subject: greybus: connection: add unidirectional enabled state Add a new connection state ENABLED_TX in which only outgoing operations are allowed. This allows drivers to query the device during probe before allocating their state containers without having to worry about racing incoming requests. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 19 ++++++++++++++++++- drivers/staging/greybus/connection.h | 7 ++++++- drivers/staging/greybus/operation.c | 7 +++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 56588f357b20..8ae099d20b48 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -396,6 +396,18 @@ int gb_connection_enable(struct gb_connection *connection, if (connection->state == GB_CONNECTION_STATE_ENABLED) goto out_unlock; + if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) { + if (!handler) + goto out_unlock; + + spin_lock_irq(&connection->lock); + connection->handler = handler; + connection->state = GB_CONNECTION_STATE_ENABLED; + spin_unlock_irq(&connection->lock); + + goto out_unlock; + } + ret = gb_connection_hd_cport_enable(connection); if (ret) goto err_unlock; @@ -406,7 +418,10 @@ int gb_connection_enable(struct gb_connection *connection, spin_lock_irq(&connection->lock); connection->handler = handler; - connection->state = GB_CONNECTION_STATE_ENABLED; + if (handler) + connection->state = GB_CONNECTION_STATE_ENABLED; + else + connection->state = GB_CONNECTION_STATE_ENABLED_TX; spin_unlock_irq(&connection->lock); ret = gb_connection_control_connected(connection); @@ -422,6 +437,7 @@ err_svc_destroy: spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; gb_connection_cancel_operations(connection, -ESHUTDOWN); + connection->handler = NULL; spin_unlock_irq(&connection->lock); gb_connection_svc_connection_destroy(connection); @@ -446,6 +462,7 @@ void gb_connection_disable(struct gb_connection *connection) spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; gb_connection_cancel_operations(connection, -ESHUTDOWN); + connection->handler = NULL; spin_unlock_irq(&connection->lock); gb_connection_svc_connection_destroy(connection); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index e7c6feb76e80..ab2556d81273 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -16,7 +16,8 @@ enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, GB_CONNECTION_STATE_DISABLED = 1, - GB_CONNECTION_STATE_ENABLED = 2, + GB_CONNECTION_STATE_ENABLED_TX = 2, + GB_CONNECTION_STATE_ENABLED = 3, }; struct gb_operation; @@ -70,6 +71,10 @@ static inline bool gb_connection_is_static(struct gb_connection *connection) int gb_connection_enable(struct gb_connection *connection, gb_request_handler_t handler); +static inline int gb_connection_enable_tx(struct gb_connection *connection) +{ + return gb_connection_enable(connection, NULL); +} void gb_connection_disable(struct gb_connection *connection); int gb_connection_legacy_init(struct gb_connection *connection); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 4dc79cb12397..d6b3d1f22b28 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -47,7 +47,9 @@ static int gb_operation_get_active(struct gb_operation *operation) spin_lock_irqsave(&connection->lock, flags); - if (connection->state != GB_CONNECTION_STATE_ENABLED) { + if (connection->state != GB_CONNECTION_STATE_ENABLED && + connection->state != GB_CONNECTION_STATE_ENABLED_TX && + !gb_operation_is_incoming(operation)) { spin_unlock_irqrestore(&connection->lock, flags); return -ENOTCONN; } @@ -906,7 +908,8 @@ void gb_connection_recv(struct gb_connection *connection, size_t msg_size; u16 operation_id; - if (connection->state != GB_CONNECTION_STATE_ENABLED) { + if (connection->state != GB_CONNECTION_STATE_ENABLED && + connection->state != GB_CONNECTION_STATE_ENABLED_TX) { dev_warn(dev, "%s: dropping %zu received bytes\n", connection->name, size); return; -- cgit v1.2.3-59-g8ed1b From beb6b7fede005ae351619b30a8940a04e797b9b2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:08 +0100 Subject: greybus: connection: add helper to disable incoming operations Add helper to disable and flush incoming operations. This is intended to be used by core to flush any incoming requests before calling driver disconnect, but could potentially later be exported for driver use as well. Note that we currently flush all incoming operation and allow the request handlers to run, but cancel any responses sent. This may need to be refined later. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 54 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/connection.h | 1 + 2 files changed, 55 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 8ae099d20b48..c3207c828a45 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -386,6 +386,42 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, } } +/* + * Cancel all active incoming operations on a connection. + * + * Locking: Called with connection lock held and state set to ENABLED_TX. + */ +static void +gb_connection_flush_incoming_operations(struct gb_connection *connection, + int errno) +{ + struct gb_operation *operation; + bool incoming; + + while (!list_empty(&connection->operations)) { + incoming = false; + list_for_each_entry(operation, &connection->operations, + links) { + if (gb_operation_is_incoming(operation)) { + gb_operation_get(operation); + incoming = true; + break; + } + } + + if (!incoming) + break; + + spin_unlock_irq(&connection->lock); + + /* FIXME: flush, not cancel? */ + gb_operation_cancel_incoming(operation, errno); + gb_operation_put(operation); + + spin_lock_irq(&connection->lock); + } +} + int gb_connection_enable(struct gb_connection *connection, gb_request_handler_t handler) { @@ -450,6 +486,24 @@ err_unlock: } EXPORT_SYMBOL_GPL(gb_connection_enable); +void gb_connection_disable_rx(struct gb_connection *connection) +{ + mutex_lock(&connection->mutex); + + spin_lock_irq(&connection->lock); + if (connection->state != GB_CONNECTION_STATE_ENABLED) { + spin_unlock_irq(&connection->lock); + goto out_unlock; + } + connection->state = GB_CONNECTION_STATE_ENABLED_TX; + gb_connection_flush_incoming_operations(connection, -ESHUTDOWN); + connection->handler = NULL; + spin_unlock_irq(&connection->lock); + +out_unlock: + mutex_unlock(&connection->mutex); +} + void gb_connection_disable(struct gb_connection *connection) { mutex_lock(&connection->mutex); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index ab2556d81273..8813f54eab92 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -75,6 +75,7 @@ static inline int gb_connection_enable_tx(struct gb_connection *connection) { return gb_connection_enable(connection, NULL); } +void gb_connection_disable_rx(struct gb_connection *connection); void gb_connection_disable(struct gb_connection *connection); int gb_connection_legacy_init(struct gb_connection *connection); -- cgit v1.2.3-59-g8ed1b From fa8369c130098da3276bafec425822aa74971905 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:09 +0100 Subject: greybus: core: disable incoming operations pre disconnect Disable and flush incoming operations before calling driver disconnect. Bundle drivers are still responsible for disabling their connections in their disconnect callback. Note that specifically the legacy protocols must have incoming operations disabled when their connection_exit callback is called as that is where their state is deallocated. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 28f48d79b005..cf06c9fb5bdc 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -159,6 +159,10 @@ static int greybus_remove(struct device *dev) { struct greybus_driver *driver = to_greybus_driver(dev->driver); struct gb_bundle *bundle = to_gb_bundle(dev); + struct gb_connection *connection; + + list_for_each_entry(connection, &bundle->connections, bundle_links) + gb_connection_disable_rx(connection); driver->disconnect(bundle); return 0; -- cgit v1.2.3-59-g8ed1b From 02a54dd18f1eb831f4bf02ac763a4e9d805e1ba3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:10 +0100 Subject: greybus: core: add defensive connection disable post disconnect Bundle drivers *must* disable their connections in the disconnect callback, but add a defensive test and warn about buggy drivers nonetheless. Note that bundle drivers would generally release their state containers in disconnect so a failure stop I/O could potentially lead to use-after-free bugs in any late operation completion callbacks. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index cf06c9fb5bdc..493f3920aaf0 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -165,6 +165,13 @@ static int greybus_remove(struct device *dev) gb_connection_disable_rx(connection); driver->disconnect(bundle); + + /* Catch buggy drivers that fail to disable their connections. */ + list_for_each_entry(connection, &bundle->connections, bundle_links) { + if (WARN_ON(connection->state != GB_CONNECTION_STATE_DISABLED)) + gb_connection_disable(connection); + } + return 0; } -- cgit v1.2.3-59-g8ed1b From 3e1b8c753d982bd4a63b0896a4ea68888f47502e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:11 +0100 Subject: greybus: firmware: remove skip-disconnected flag Remove the legacy protocol flag that was used to suppress sending the control disconnected operation. Instead rely on the fact that no control operations will be sent by core to an interface that has been removed (e.g. after having received a new hotplug event after the bootrom has jumped to the new image). Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 11d605b97bd0..408c7e2c0b08 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -285,6 +285,5 @@ static struct gb_protocol firmware_protocol = { .connection_init = gb_firmware_connection_init, .connection_exit = gb_firmware_connection_exit, .request_recv = gb_firmware_request_recv, - .flags = GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED, }; gb_builtin_protocol_driver(firmware_protocol); -- cgit v1.2.3-59-g8ed1b From bc3be1705c17e05c4d423a67a49a9f316d235bf4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:12 +0100 Subject: greybus: connection: remove skip-connected legacy protocol flags Remove the legacy protocol flags that were used to suppress the connected and disconnected events. Instead send the connected and disconnected event for all bundle connections and explicitly exclude static connections and control connections. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 16 ++++++++++------ drivers/staging/greybus/control.c | 2 -- drivers/staging/greybus/protocol.h | 2 -- drivers/staging/greybus/svc.c | 4 +--- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c3207c828a45..3f644ca8759e 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -295,15 +295,17 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection) /* Inform Interface about active CPorts */ static int gb_connection_control_connected(struct gb_connection *connection) { - struct gb_protocol *protocol = connection->protocol; struct gb_control *control; u16 cport_id = connection->intf_cport_id; int ret; - if (protocol->flags & GB_PROTOCOL_SKIP_CONTROL_CONNECTED) + if (gb_connection_is_static(connection)) return 0; - control = connection->bundle->intf->control; + control = connection->intf->control; + + if (connection == control->connection) + return 0; ret = gb_control_connected_operation(control, cport_id); if (ret) { @@ -319,15 +321,17 @@ static int gb_connection_control_connected(struct gb_connection *connection) static void gb_connection_control_disconnected(struct gb_connection *connection) { - struct gb_protocol *protocol = connection->protocol; struct gb_control *control; u16 cport_id = connection->intf_cport_id; int ret; - if (protocol->flags & GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED) + if (gb_connection_is_static(connection)) return; - control = connection->bundle->intf->control; + control = connection->intf->control; + + if (connection == control->connection) + return; ret = gb_control_disconnected_operation(control, cport_id); if (ret) { diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 1c4994b84629..12a9ecac4111 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -159,7 +159,5 @@ static struct gb_protocol control_protocol = { .minor = GB_CONTROL_VERSION_MINOR, .connection_init = gb_control_connection_init, .connection_exit = gb_control_connection_exit, - .flags = GB_PROTOCOL_SKIP_CONTROL_CONNECTED | - GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED, }; gb_builtin_protocol_driver(control_protocol); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index f24281b39db5..1f25c13bb0e8 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -14,8 +14,6 @@ struct gb_connection; struct gb_operation; /* Possible flags for protocol drivers */ -#define GB_PROTOCOL_SKIP_CONTROL_CONNECTED BIT(0) /* Don't sent connected requests */ -#define GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED BIT(1) /* Don't sent disconnected requests */ #define GB_PROTOCOL_SKIP_VERSION BIT(3) /* Don't send get_version() requests */ typedef int (*gb_connection_init_t)(struct gb_connection *); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 0bbcddaefbb4..245cf4d43570 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -864,8 +864,6 @@ static struct gb_protocol svc_protocol = { .connection_init = gb_svc_connection_init, .connection_exit = gb_svc_connection_exit, .request_recv = gb_svc_request_recv, - .flags = GB_PROTOCOL_SKIP_CONTROL_CONNECTED | - GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED | - GB_PROTOCOL_SKIP_VERSION, + .flags = GB_PROTOCOL_SKIP_VERSION, }; gb_builtin_protocol_driver(svc_protocol); -- cgit v1.2.3-59-g8ed1b From 6bd6e148930b6f6dff7a6d664b37ba76d6d2c75e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:13 +0100 Subject: greybus: greybus_protocols: remove control-protocol version Remove control-protocol version from the exported protocol definitions as it is an implementation detail that makes no sense to export. Currently gbsim uses the kernel's control-protocol version definitions directly instead of reporting the version of the protocol it actually implements. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 5 +++++ drivers/staging/greybus/greybus_protocols.h | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 12a9ecac4111..d79807c10b98 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -12,6 +12,11 @@ #include #include "greybus.h" +/* Highest control-protocol version supported */ +#define GB_CONTROL_VERSION_MAJOR 0 +#define GB_CONTROL_VERSION_MINOR 1 + + /* Get Manifest's size from the interface */ int gb_control_get_manifest_size_operation(struct gb_interface *intf) { diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 55e99b7083f3..c563e7454d9c 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -115,10 +115,6 @@ struct gb_protocol_version_response { /* Control Protocol */ -/* Version of the Greybus control protocol we support */ -#define GB_CONTROL_VERSION_MAJOR 0x00 -#define GB_CONTROL_VERSION_MINOR 0x01 - /* Greybus control request types */ #define GB_CONTROL_TYPE_PROBE_AP 0x02 #define GB_CONTROL_TYPE_GET_MANIFEST_SIZE 0x03 -- cgit v1.2.3-59-g8ed1b From e217ae762bccb0ba0109887cefec3e20e5549a48 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:14 +0100 Subject: greybus: control: drop legacy-protocol dependency Drop dependency on the legacy protocol abstraction. Instead implement the protocol-specific version request directly, and use the new interface for managing the control connection. Note that the version request is being removed from most protocols, but we need to keep the current request for the control protocol as-is indefinitely to maintain backwards compatibility (e.g. with the ES2/ES3 bootrom). Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 72 +++++++++++++++++++---------- drivers/staging/greybus/control.h | 5 +- drivers/staging/greybus/core.c | 9 ---- drivers/staging/greybus/greybus_protocols.h | 11 +++++ 4 files changed, 62 insertions(+), 35 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index d79807c10b98..30779402b368 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -17,6 +17,43 @@ #define GB_CONTROL_VERSION_MINOR 1 +int gb_control_get_version(struct gb_control *control) +{ + struct gb_interface *intf = control->connection->intf; + struct gb_control_version_request request; + struct gb_control_version_response response; + int ret; + + request.major = GB_CONTROL_VERSION_MAJOR; + request.minor = GB_CONTROL_VERSION_MINOR; + + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_VERSION, + &request, sizeof(request), &response, + sizeof(response)); + if (ret) { + dev_err(&intf->dev, + "failed to get control-protocol version: %d\n", + ret); + return ret; + } + + if (response.major > request.major) { + dev_err(&intf->dev, + "unsupported major control-protocol version (%u > %u)\n", + response.major, request.major); + return -ENOTSUPP; + } + + control->protocol_major = response.major; + control->protocol_minor = response.minor; + + dev_dbg(&intf->dev, "%s - %u.%u\n", __func__, response.major, + response.minor); + + return 0; +} + /* Get Manifest's size from the interface */ int gb_control_get_manifest_size_operation(struct gb_interface *intf) { @@ -121,7 +158,7 @@ int gb_control_enable(struct gb_control *control) dev_dbg(&control->connection->intf->dev, "%s\n", __func__); - ret = gb_connection_legacy_init(control->connection); + ret = gb_connection_enable_tx(control->connection); if (ret) { dev_err(&control->connection->intf->dev, "failed to enable control connection: %d\n", @@ -129,14 +166,23 @@ int gb_control_enable(struct gb_control *control) return ret; } + ret = gb_control_get_version(control); + if (ret) + goto err_disable_connection; + return 0; + +err_disable_connection: + gb_connection_disable(control->connection); + + return ret; } void gb_control_disable(struct gb_control *control) { dev_dbg(&control->connection->intf->dev, "%s\n", __func__); - gb_connection_legacy_exit(control->connection); + gb_connection_disable(control->connection); } void gb_control_destroy(struct gb_control *control) @@ -144,25 +190,3 @@ void gb_control_destroy(struct gb_control *control) gb_connection_destroy(control->connection); kfree(control); } - -static int gb_control_connection_init(struct gb_connection *connection) -{ - dev_dbg(&connection->intf->dev, "%s\n", __func__); - - return 0; -} - -static void gb_control_connection_exit(struct gb_connection *connection) -{ - dev_dbg(&connection->intf->dev, "%s\n", __func__); -} - -static struct gb_protocol control_protocol = { - .name = "control", - .id = GREYBUS_PROTOCOL_CONTROL, - .major = GB_CONTROL_VERSION_MAJOR, - .minor = GB_CONTROL_VERSION_MINOR, - .connection_init = gb_control_connection_init, - .connection_exit = gb_control_connection_exit, -}; -gb_builtin_protocol_driver(control_protocol); diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 7cb3dd2290d7..dd0a2d79da20 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -12,6 +12,9 @@ struct gb_control { struct gb_connection *connection; + + u8 protocol_major; + u8 protocol_minor; }; struct gb_control *gb_control_create(struct gb_interface *intf); @@ -26,6 +29,4 @@ int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, size_t size); int gb_control_get_interface_version_operation(struct gb_interface *intf); -int gb_control_protocol_init(void); -void gb_control_protocol_exit(void); #endif /* __CONTROL_H */ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 493f3920aaf0..6674b2728f98 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -234,12 +234,6 @@ static int __init gb_init(void) goto error_operation; } - retval = gb_control_protocol_init(); - if (retval) { - pr_err("gb_control_protocol_init failed\n"); - goto error_control; - } - retval = gb_svc_protocol_init(); if (retval) { pr_err("gb_svc_protocol_init failed\n"); @@ -265,8 +259,6 @@ error_legacy: error_firmware: gb_svc_protocol_exit(); error_svc: - gb_control_protocol_exit(); -error_control: gb_operation_exit(); error_operation: gb_hd_exit(); @@ -284,7 +276,6 @@ static void __exit gb_exit(void) gb_legacy_exit(); gb_firmware_protocol_exit(); gb_svc_protocol_exit(); - gb_control_protocol_exit(); gb_operation_exit(); gb_hd_exit(); bus_unregister(&greybus_bus_type); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c563e7454d9c..abbb214863c9 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -116,6 +116,7 @@ struct gb_protocol_version_response { /* Control Protocol */ /* Greybus control request types */ +#define GB_CONTROL_TYPE_VERSION 0x01 #define GB_CONTROL_TYPE_PROBE_AP 0x02 #define GB_CONTROL_TYPE_GET_MANIFEST_SIZE 0x03 #define GB_CONTROL_TYPE_GET_MANIFEST 0x04 @@ -123,6 +124,16 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_DISCONNECTED 0x06 #define GB_CONTROL_TYPE_INTERFACE_VERSION 0x0a +struct gb_control_version_request { + __u8 major; + __u8 minor; +} __packed; + +struct gb_control_version_response { + __u8 major; + __u8 minor; +} __packed; + /* Control protocol manifest get size request has no payload*/ struct gb_control_get_manifest_size_response { __le16 size; -- cgit v1.2.3-59-g8ed1b From 84427943d2da5f55d5cc83d83ba2a75c2079d1dd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:15 +0100 Subject: greybus: svc: drop legacy-protocol dependency Drop dependency on the legacy protocol abstraction. Remove the now unused and last legacy-protocol flag GB_PROTOCOL_SKIP_VERSION along with the protocol-flag feature. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 7 +------ drivers/staging/greybus/core.c | 9 --------- drivers/staging/greybus/protocol.h | 4 ---- drivers/staging/greybus/svc.c | 35 ++++------------------------------- 4 files changed, 5 insertions(+), 50 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3f644ca8759e..3339ef956788 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -341,17 +341,12 @@ gb_connection_control_disconnected(struct gb_connection *connection) } /* - * Request protocol version supported by the module. We don't need to do - * this for SVC as that is initiated by the SVC. + * Request protocol version supported by the module. */ static int gb_connection_protocol_get_version(struct gb_connection *connection) { - struct gb_protocol *protocol = connection->protocol; int ret; - if (protocol->flags & GB_PROTOCOL_SKIP_VERSION) - return 0; - ret = gb_protocol_get_version(connection); if (ret) { dev_err(&connection->hd->dev, diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 6674b2728f98..a72191f46751 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -234,12 +234,6 @@ static int __init gb_init(void) goto error_operation; } - retval = gb_svc_protocol_init(); - if (retval) { - pr_err("gb_svc_protocol_init failed\n"); - goto error_svc; - } - retval = gb_firmware_protocol_init(); if (retval) { pr_err("gb_firmware_protocol_init failed\n"); @@ -257,8 +251,6 @@ static int __init gb_init(void) error_legacy: gb_firmware_protocol_exit(); error_firmware: - gb_svc_protocol_exit(); -error_svc: gb_operation_exit(); error_operation: gb_hd_exit(); @@ -275,7 +267,6 @@ static void __exit gb_exit(void) { gb_legacy_exit(); gb_firmware_protocol_exit(); - gb_svc_protocol_exit(); gb_operation_exit(); gb_hd_exit(); bus_unregister(&greybus_bus_type); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 1f25c13bb0e8..26c59efd9ed8 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -13,9 +13,6 @@ struct gb_connection; struct gb_operation; -/* Possible flags for protocol drivers */ -#define GB_PROTOCOL_SKIP_VERSION BIT(3) /* Don't send get_version() requests */ - typedef int (*gb_connection_init_t)(struct gb_connection *); typedef void (*gb_connection_exit_t)(struct gb_connection *); typedef int (*gb_request_recv_t)(u8, struct gb_operation *); @@ -30,7 +27,6 @@ struct gb_protocol { u8 major; u8 minor; u8 count; - unsigned long flags; struct list_head links; /* global list */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 245cf4d43570..fcdee900a6ab 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -690,10 +690,11 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) return 0; } -static int gb_svc_request_recv(u8 type, struct gb_operation *op) +static int gb_svc_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_svc *svc = connection->private; + u8 type = op->type; int ret = 0; /* @@ -815,7 +816,7 @@ int gb_svc_add(struct gb_svc *svc) * is added from the connection request handler when enough * information has been received. */ - ret = gb_connection_legacy_init(svc->connection); + ret = gb_connection_enable(svc->connection, gb_svc_request_handler); if (ret) return ret; @@ -830,7 +831,7 @@ void gb_svc_del(struct gb_svc *svc) if (device_is_registered(&svc->dev)) device_del(&svc->dev); - gb_connection_legacy_exit(svc->connection); + gb_connection_disable(svc->connection); flush_workqueue(svc->wq); } @@ -839,31 +840,3 @@ void gb_svc_put(struct gb_svc *svc) { put_device(&svc->dev); } - -static int gb_svc_connection_init(struct gb_connection *connection) -{ - struct gb_svc *svc = connection->private; - - dev_dbg(&svc->dev, "%s\n", __func__); - - return 0; -} - -static void gb_svc_connection_exit(struct gb_connection *connection) -{ - struct gb_svc *svc = connection->private; - - dev_dbg(&svc->dev, "%s\n", __func__); -} - -static struct gb_protocol svc_protocol = { - .name = "svc", - .id = GREYBUS_PROTOCOL_SVC, - .major = GB_SVC_VERSION_MAJOR, - .minor = GB_SVC_VERSION_MINOR, - .connection_init = gb_svc_connection_init, - .connection_exit = gb_svc_connection_exit, - .request_recv = gb_svc_request_recv, - .flags = GB_PROTOCOL_SKIP_VERSION, -}; -gb_builtin_protocol_driver(svc_protocol); -- cgit v1.2.3-59-g8ed1b From 50dfb87865790bf8ef86a1c6898cde4e0df212fd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:16 +0100 Subject: greybus: connection: move legacy-protocol handling to legacy driver Move legacy protocol and connection handling to the legacy driver. Rename the former global functions using a common legacy_ prefix. Note that all legacy protocols suffer from a connection initialisation race in that the protocol-specific initialisation, which includes allocation of protocol-specific state containers, can not happen until *after* the connection has been enabled. This is a major flaw in the original design that we can now finally address by converting the legacy protocol drivers into proper bundle (class) drivers. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 107 ----------------------------------- drivers/staging/greybus/connection.h | 3 - drivers/staging/greybus/legacy.c | 106 +++++++++++++++++++++++++++++++++- drivers/staging/greybus/protocol.c | 2 + 4 files changed, 105 insertions(+), 113 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3339ef956788..7a967befc897 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -12,10 +12,6 @@ #include "greybus.h" -static int gb_connection_bind_protocol(struct gb_connection *connection); -static void gb_connection_unbind_protocol(struct gb_connection *connection); - - static DEFINE_SPINLOCK(gb_connections_lock); /* This is only used at initialization time; no locking is required. */ @@ -340,24 +336,6 @@ gb_connection_control_disconnected(struct gb_connection *connection) } } -/* - * Request protocol version supported by the module. - */ -static int gb_connection_protocol_get_version(struct gb_connection *connection) -{ - int ret; - - ret = gb_protocol_get_version(connection); - if (ret) { - dev_err(&connection->hd->dev, - "%s: failed to get protocol version: %d\n", - connection->name, ret); - return ret; - } - - return 0; -} - /* * Cancel all active operations on a connection. * @@ -526,63 +504,6 @@ out_unlock: } EXPORT_SYMBOL_GPL(gb_connection_disable); -static int gb_legacy_request_handler(struct gb_operation *operation) -{ - struct gb_protocol *protocol = operation->connection->protocol; - - return protocol->request_recv(operation->type, operation); -} - -int gb_connection_legacy_init(struct gb_connection *connection) -{ - gb_request_handler_t handler; - int ret; - - ret = gb_connection_bind_protocol(connection); - if (ret) - return ret; - - if (connection->protocol->request_recv) - handler = gb_legacy_request_handler; - else - handler = NULL; - - ret = gb_connection_enable(connection, handler); - if (ret) - goto err_unbind_protocol; - - ret = gb_connection_protocol_get_version(connection); - if (ret) - goto err_disable; - - ret = connection->protocol->connection_init(connection); - if (ret) - goto err_disable; - - return 0; - -err_disable: - gb_connection_disable(connection); -err_unbind_protocol: - gb_connection_unbind_protocol(connection); - - return ret; -} -EXPORT_SYMBOL_GPL(gb_connection_legacy_init); - -void gb_connection_legacy_exit(struct gb_connection *connection) -{ - if (connection->state == GB_CONNECTION_STATE_DISABLED) - return; - - gb_connection_disable(connection); - - connection->protocol->connection_exit(connection); - - gb_connection_unbind_protocol(connection); -} -EXPORT_SYMBOL_GPL(gb_connection_legacy_exit); - /* * Tear down a previously set up connection. */ @@ -639,31 +560,3 @@ void gb_connection_latency_tag_disable(struct gb_connection *connection) } } EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable); - -static int gb_connection_bind_protocol(struct gb_connection *connection) -{ - struct gb_protocol *protocol; - - protocol = gb_protocol_get(connection->protocol_id, - connection->major, - connection->minor); - if (!protocol) { - dev_err(&connection->hd->dev, - "protocol 0x%02x version %u.%u not found\n", - connection->protocol_id, - connection->major, connection->minor); - return -EPROTONOSUPPORT; - } - connection->protocol = protocol; - - return 0; -} - -static void gb_connection_unbind_protocol(struct gb_connection *connection) -{ - struct gb_protocol *protocol = connection->protocol; - - gb_protocol_put(protocol); - - connection->protocol = NULL; -} diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 8813f54eab92..33deeb64dd29 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -78,9 +78,6 @@ static inline int gb_connection_enable_tx(struct gb_connection *connection) void gb_connection_disable_rx(struct gb_connection *connection); void gb_connection_disable(struct gb_connection *connection); -int gb_connection_legacy_init(struct gb_connection *connection); -void gb_connection_legacy_exit(struct gb_connection *connection); - void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index e8d9cb93b831..fd847f42376f 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -9,6 +9,106 @@ #include "greybus.h" #include "legacy.h" +#include "protocol.h" + + +static int legacy_connection_get_version(struct gb_connection *connection) +{ + int ret; + + ret = gb_protocol_get_version(connection); + if (ret) { + dev_err(&connection->hd->dev, + "%s: failed to get protocol version: %d\n", + connection->name, ret); + return ret; + } + + return 0; +} + +static int legacy_connection_bind_protocol(struct gb_connection *connection) +{ + struct gb_protocol *protocol; + + protocol = gb_protocol_get(connection->protocol_id, + connection->major, + connection->minor); + if (!protocol) { + dev_err(&connection->hd->dev, + "protocol 0x%02x version %u.%u not found\n", + connection->protocol_id, + connection->major, connection->minor); + return -EPROTONOSUPPORT; + } + connection->protocol = protocol; + + return 0; +} + +static void legacy_connection_unbind_protocol(struct gb_connection *connection) +{ + struct gb_protocol *protocol = connection->protocol; + + gb_protocol_put(protocol); + + connection->protocol = NULL; +} + +static int legacy_request_handler(struct gb_operation *operation) +{ + struct gb_protocol *protocol = operation->connection->protocol; + + return protocol->request_recv(operation->type, operation); +} + +static int legacy_connection_init(struct gb_connection *connection) +{ + gb_request_handler_t handler; + int ret; + + ret = legacy_connection_bind_protocol(connection); + if (ret) + return ret; + + if (connection->protocol->request_recv) + handler = legacy_request_handler; + else + handler = NULL; + + ret = gb_connection_enable(connection, handler); + if (ret) + goto err_unbind_protocol; + + ret = legacy_connection_get_version(connection); + if (ret) + goto err_disable; + + ret = connection->protocol->connection_init(connection); + if (ret) + goto err_disable; + + return 0; + +err_disable: + gb_connection_disable(connection); +err_unbind_protocol: + legacy_connection_unbind_protocol(connection); + + return ret; +} + +static void legacy_connection_exit(struct gb_connection *connection) +{ + if (connection->state == GB_CONNECTION_STATE_DISABLED) + return; + + gb_connection_disable(connection); + + connection->protocol->connection_exit(connection); + + legacy_connection_unbind_protocol(connection); +} static int legacy_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) @@ -23,7 +123,7 @@ static int legacy_probe(struct gb_bundle *bundle, dev_dbg(&bundle->dev, "enabling connection %s\n", connection->name); - ret = gb_connection_legacy_init(connection); + ret = legacy_connection_init(connection); if (ret) goto err_connections_disable; } @@ -33,7 +133,7 @@ static int legacy_probe(struct gb_bundle *bundle, err_connections_disable: list_for_each_entry_reverse(connection, &bundle->connections, bundle_links) { - gb_connection_legacy_exit(connection); + legacy_connection_exit(connection); } return ret; @@ -48,7 +148,7 @@ static void legacy_disconnect(struct gb_bundle *bundle) list_for_each_entry_reverse(connection, &bundle->connections, bundle_links) { - gb_connection_legacy_exit(connection); + legacy_connection_exit(connection); } } diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index d69f64801b4f..057ab6057ee6 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -141,6 +141,7 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) return protocol; } +EXPORT_SYMBOL_GPL(gb_protocol_get); int gb_protocol_get_version(struct gb_connection *connection) { @@ -197,3 +198,4 @@ void gb_protocol_put(struct gb_protocol *protocol) out: spin_unlock_irq(&gb_protocols_lock); } +EXPORT_SYMBOL_GPL(gb_protocol_put); -- cgit v1.2.3-59-g8ed1b From 01547770e6fc283ce74944415fc069d287de082a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:17 +0100 Subject: greybus: legacy: use protocol pointer to determine state Use the protocol pointer to determine the legacy connection state. This is needed to allow core disable connections when an interface has been hot-unplugged without the legacy protocols leaking its resources. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index fd847f42376f..a2c0b9bed9a9 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -100,7 +100,7 @@ err_unbind_protocol: static void legacy_connection_exit(struct gb_connection *connection) { - if (connection->state == GB_CONNECTION_STATE_DISABLED) + if (!connection->protocol) return; gb_connection_disable(connection); -- cgit v1.2.3-59-g8ed1b From 47a2e6769e8194da5570eaed7fc43f5abec18809 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:18 +0100 Subject: greybus: core: disable bundle connections on hot-unplug Disable bundle connections in core before calling driver disconnect in case the interface is already gone. This avoids unnecessary timeouts on hot-unplug when a driver does I/O in its disconnect callback. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index a72191f46751..522d0594eebc 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -161,8 +161,12 @@ static int greybus_remove(struct device *dev) struct gb_bundle *bundle = to_gb_bundle(dev); struct gb_connection *connection; - list_for_each_entry(connection, &bundle->connections, bundle_links) - gb_connection_disable_rx(connection); + list_for_each_entry(connection, &bundle->connections, bundle_links) { + if (bundle->intf->disconnected) + gb_connection_disable(connection); + else + gb_connection_disable_rx(connection); + } driver->disconnect(bundle); -- cgit v1.2.3-59-g8ed1b From 357de006573f0dbbd5e214dd3f2fe808157ec530 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:19 +0100 Subject: greybus: svc: store protocol version Store the "negotiated" protocol version to use in the svc state struct instead of the connection struct. The generic concept of a connection version is going away in favour of bundle-class versions. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 8 ++++---- drivers/staging/greybus/svc.h | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index fcdee900a6ab..bc64f4894b46 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -383,15 +383,15 @@ static int gb_svc_version_request(struct gb_operation *op) return -ENOTSUPP; } - connection->module_major = request->major; - connection->module_minor = request->minor; + svc->protocol_major = request->major; + svc->protocol_minor = request->minor; if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) return -ENOMEM; response = op->response->payload; - response->major = connection->module_major; - response->minor = connection->module_minor; + response->major = svc->protocol_major; + response->minor = svc->protocol_minor; return 0; } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 8567615068a7..4abc5efad807 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -27,6 +27,9 @@ struct gb_svc { u16 endo_id; u8 ap_intf_id; + + u8 protocol_major; + u8 protocol_minor; }; #define to_gb_svc(d) container_of(d, struct gb_svc, d) -- cgit v1.2.3-59-g8ed1b From 96a9b9b0eb2c5b9626bdad02c057fb22f133fc86 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:20 +0100 Subject: greybus: connection: remove broken protocol-version handling Remove the broken legacy protocol-version handling from core and move it to the legacy driver instead. Note that version handling has always been broken as legacy protocols were looked up and bound to connections before the connections were enabled and version negotiation could take place (over that very connection). Document that in the legacy driver as well. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 4 ---- drivers/staging/greybus/connection.h | 2 -- drivers/staging/greybus/legacy.c | 16 +++++++++++++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7a967befc897..ebd038ea6e8a 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -120,8 +120,6 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, struct gb_connection *connection; struct ida *id_map = &hd->cport_id_map; int ida_start, ida_end; - u8 major = 0; - u8 minor = 1; /* * If a manifest tries to reuse a cport, reject it. We @@ -159,8 +157,6 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, connection->intf = intf; connection->protocol_id = protocol_id; - connection->major = major; - connection->minor = minor; connection->bundle = bundle; connection->state = GB_CONNECTION_STATE_DISABLED; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 33deeb64dd29..66f37b2f4a38 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -39,8 +39,6 @@ struct gb_connection { struct gb_protocol *protocol; u8 protocol_id; - u8 major; - u8 minor; u8 module_major; u8 module_minor; diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index a2c0b9bed9a9..186482300682 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -30,15 +30,25 @@ static int legacy_connection_get_version(struct gb_connection *connection) static int legacy_connection_bind_protocol(struct gb_connection *connection) { struct gb_protocol *protocol; + u8 major, minor; + + /* + * The legacy protocols have always been looked up using a hard-coded + * version of 0.1, despite (or perhaps rather, due to) the fact that + * module version negotiation could not take place until after the + * protocol was bound. + */ + major = 0; + minor = 1; protocol = gb_protocol_get(connection->protocol_id, - connection->major, - connection->minor); + major, + minor); if (!protocol) { dev_err(&connection->hd->dev, "protocol 0x%02x version %u.%u not found\n", connection->protocol_id, - connection->major, connection->minor); + major, minor); return -EPROTONOSUPPORT; } connection->protocol = protocol; -- cgit v1.2.3-59-g8ed1b From b807aa7aa51129b1754e7d17f6a2e30e3c6f7a4b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:21 +0100 Subject: greybus: control: add bundle-version operation Add bundle-version operation to fetch the version of the bundle class. Retrieve the bundle version of all bundles when initialising the interface in case the control-protocol version is greater than 0.1. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.h | 4 +++ drivers/staging/greybus/control.c | 51 +++++++++++++++++++++++++++++ drivers/staging/greybus/control.h | 3 ++ drivers/staging/greybus/greybus_protocols.h | 10 ++++++ drivers/staging/greybus/interface.c | 4 +++ 5 files changed, 72 insertions(+) diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 682ec327f8a3..837682d91e5b 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -16,8 +16,12 @@ struct gb_bundle { struct device dev; struct gb_interface *intf; + u8 id; u8 class; + u8 class_major; + u8 class_minor; + struct list_head connections; u8 *state; diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 30779402b368..0e50fd419f3a 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -54,6 +54,54 @@ int gb_control_get_version(struct gb_control *control) return 0; } +static int gb_control_get_bundle_version(struct gb_control *control, + struct gb_bundle *bundle) +{ + struct gb_interface *intf = control->connection->intf; + struct gb_control_bundle_version_request request; + struct gb_control_bundle_version_response response; + int ret; + + request.bundle_id = bundle->id; + + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_BUNDLE_VERSION, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) { + dev_err(&intf->dev, + "failed to get bundle %u class version: %d\n", + bundle->id, ret); + return ret; + } + + bundle->class_major = response.major; + bundle->class_minor = response.minor; + + dev_dbg(&intf->dev, "%s - %u: %u.%u\n", __func__, bundle->id, + response.major, response.minor); + + return 0; +} + +int gb_control_get_bundle_versions(struct gb_control *control) +{ + struct gb_interface *intf = control->connection->intf; + struct gb_bundle *bundle; + int ret; + + if (!control->has_bundle_version) + return 0; + + list_for_each_entry(bundle, &intf->bundles, links) { + ret = gb_control_get_bundle_version(control, bundle); + if (ret) + return ret; + } + + return 0; +} + /* Get Manifest's size from the interface */ int gb_control_get_manifest_size_operation(struct gb_interface *intf) { @@ -170,6 +218,9 @@ int gb_control_enable(struct gb_control *control) if (ret) goto err_disable_connection; + if (control->protocol_major > 0 || control->protocol_minor > 1) + control->has_bundle_version = true; + return 0; err_disable_connection: diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index dd0a2d79da20..d31e7c6d866a 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -15,6 +15,8 @@ struct gb_control { u8 protocol_major; u8 protocol_minor; + + bool has_bundle_version; }; struct gb_control *gb_control_create(struct gb_interface *intf); @@ -22,6 +24,7 @@ int gb_control_enable(struct gb_control *control); void gb_control_disable(struct gb_control *control); void gb_control_destroy(struct gb_control *control); +int gb_control_get_bundle_versions(struct gb_control *control); int gb_control_connected_operation(struct gb_control *control, u16 cport_id); int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id); int gb_control_get_manifest_size_operation(struct gb_interface *intf); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index abbb214863c9..84fb6ab6deca 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -123,6 +123,7 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_CONNECTED 0x05 #define GB_CONTROL_TYPE_DISCONNECTED 0x06 #define GB_CONTROL_TYPE_INTERFACE_VERSION 0x0a +#define GB_CONTROL_TYPE_BUNDLE_VERSION 0x0b struct gb_control_version_request { __u8 major; @@ -134,6 +135,15 @@ struct gb_control_version_response { __u8 minor; } __packed; +struct gb_control_bundle_version_request { + __u8 bundle_id; +} __packed; + +struct gb_control_bundle_version_response { + __u8 major; + __u8 minor; +} __packed; + /* Control protocol manifest get size request has no payload*/ struct gb_control_get_manifest_size_response { __le16 size; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c3453502ae22..9c05d8142d8b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -228,6 +228,10 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) if (ret) goto free_manifest; + ret = gb_control_get_bundle_versions(intf->control); + if (ret) + goto free_manifest; + /* Register the interface and its bundles. */ ret = device_add(&intf->dev); if (ret) { -- cgit v1.2.3-59-g8ed1b From 2edbf5ffbf5ef8b92518e57687e96a7d34ddd3c4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:22 +0100 Subject: greybus: connection: remove WARN_ON from destroy Remove WARN_ON from connection destroy testing for a NULL-connection when destroying a connection. This will allow for simpler driver code when releasing driver-managed connections. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ebd038ea6e8a..d1508e21b378 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -507,7 +507,7 @@ void gb_connection_destroy(struct gb_connection *connection) { struct ida *id_map; - if (WARN_ON(!connection)) + if (!connection) return; spin_lock_irq(&gb_connections_lock); -- cgit v1.2.3-59-g8ed1b From c3681f6c92df6c4ec834fc8e3fa1905e73952630 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:23 +0100 Subject: greybus: connection: destroy workqueue at unregister Destroy the work queue when the connection is destroyed/deregistered instead of when the last reference is dropped. The work queue is not needed once the connection has been deregistered, and no operations will ever be added to it again (handled by checking connection->state) even if the connection object may not be deallocated until the final reference is dropped. The work-queue name is set based on the host-device name and host-device cport id, something which guarantees a unique name. This would no longer be true if the work queue was not destroyed at connection deregistration as a new connection could then be created for that very same host cport. This is not necessarily a problem unless some work queue features are used that require unique work-queue names, but let's try to be well behaved. Also update an obsolete comment and make explicit that a connection must be disabled before being destroyed. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index d1508e21b378..1d7c21e03bfa 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -73,7 +73,7 @@ static void gb_connection_kref_release(struct kref *kref) struct gb_connection *connection; connection = container_of(kref, struct gb_connection, kref); - destroy_workqueue(connection->wq); + kfree(connection); mutex_unlock(&connection_mutex); } @@ -500,9 +500,7 @@ out_unlock: } EXPORT_SYMBOL_GPL(gb_connection_disable); -/* - * Tear down a previously set up connection. - */ +/* Caller must have disabled the connection before destroying it. */ void gb_connection_destroy(struct gb_connection *connection) { struct ida *id_map; @@ -515,6 +513,8 @@ void gb_connection_destroy(struct gb_connection *connection) list_del(&connection->hd_links); spin_unlock_irq(&gb_connections_lock); + destroy_workqueue(connection->wq); + id_map = &connection->hd->cport_id_map; ida_simple_remove(id_map, connection->hd_cport_id); connection->hd_cport_id = CPORT_ID_BAD; -- cgit v1.2.3-59-g8ed1b From c6d64a19cdb39680460ab9f153e33843aa8c825c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:24 +0100 Subject: greybus: connection: drop the connection_mutex Drop the useless connection_mutex that did not, and can not be used to prevent connection lookup races in atomic context and therefore serves no purpose. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1d7c21e03bfa..bfd2c0dee056 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -66,8 +66,6 @@ void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, } EXPORT_SYMBOL_GPL(greybus_data_rcvd); -static DEFINE_MUTEX(connection_mutex); - static void gb_connection_kref_release(struct kref *kref) { struct gb_connection *connection; @@ -75,7 +73,6 @@ static void gb_connection_kref_release(struct kref *kref) connection = container_of(kref, struct gb_connection, kref); kfree(connection); - mutex_unlock(&connection_mutex); } static void gb_connection_init_name(struct gb_connection *connection) @@ -519,8 +516,7 @@ void gb_connection_destroy(struct gb_connection *connection) ida_simple_remove(id_map, connection->hd_cport_id); connection->hd_cport_id = CPORT_ID_BAD; - kref_put_mutex(&connection->kref, gb_connection_kref_release, - &connection_mutex); + kref_put(&connection->kref, gb_connection_kref_release); } void gb_connection_latency_tag_enable(struct gb_connection *connection) -- cgit v1.2.3-59-g8ed1b From 0e46fab7dd5afbb4db97d9520812c57e9301bfc2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:25 +0100 Subject: greybus: connection: fix lookup race Fix longstanding race that could lead to a connection being destroyed while processing an incoming message due to missing reference counting when looking up the connection. Add helpers to manipulate the connection kref. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index bfd2c0dee056..d215c5c6331b 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -12,6 +12,9 @@ #include "greybus.h" +static void gb_connection_kref_release(struct kref *kref); + + static DEFINE_SPINLOCK(gb_connections_lock); /* This is only used at initialization time; no locking is required. */ @@ -30,6 +33,19 @@ gb_connection_intf_find(struct gb_interface *intf, u16 cport_id) return NULL; } +static void gb_connection_get(struct gb_connection *connection) +{ + kref_get(&connection->kref); +} + +static void gb_connection_put(struct gb_connection *connection) +{ + kref_put(&connection->kref, gb_connection_kref_release); +} + +/* + * Returns a reference-counted pointer to the connection if found. + */ static struct gb_connection * gb_connection_hd_find(struct gb_host_device *hd, u16 cport_id) { @@ -38,8 +54,10 @@ gb_connection_hd_find(struct gb_host_device *hd, u16 cport_id) spin_lock_irqsave(&gb_connections_lock, flags); list_for_each_entry(connection, &hd->connections, hd_links) - if (connection->hd_cport_id == cport_id) + if (connection->hd_cport_id == cport_id) { + gb_connection_get(connection); goto found; + } connection = NULL; found: spin_unlock_irqrestore(&gb_connections_lock, flags); @@ -63,6 +81,7 @@ void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, return; } gb_connection_recv(connection, data, length); + gb_connection_put(connection); } EXPORT_SYMBOL_GPL(greybus_data_rcvd); @@ -516,7 +535,7 @@ void gb_connection_destroy(struct gb_connection *connection) ida_simple_remove(id_map, connection->hd_cport_id); connection->hd_cport_id = CPORT_ID_BAD; - kref_put(&connection->kref, gb_connection_kref_release); + gb_connection_put(connection); } void gb_connection_latency_tag_enable(struct gb_connection *connection) -- cgit v1.2.3-59-g8ed1b From 210b508e45f00fd81c1ba35c979836d8ffea3980 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:26 +0100 Subject: greybus: connection: serialise connection creation Serialise connection creation against concurrent creation and destruction using a global mutex. This is needed to prevent two drivers from attempting to create a connection to the same interface CPort and to cope with a racing connection destroy when moving to driver managed connections. Note that the locking can not (easily) be made more fine-grained as not all connections have an interface, but these are not hot paths anyway. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index d215c5c6331b..de44775b0bc5 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -16,6 +16,8 @@ static void gb_connection_kref_release(struct kref *kref); static DEFINE_SPINLOCK(gb_connections_lock); +static DEFINE_MUTEX(gb_connection_mutex); + /* This is only used at initialization time; no locking is required. */ static struct gb_connection * @@ -125,6 +127,9 @@ static void gb_connection_init_name(struct gb_connection *connection) * A connection also maintains the state of operations sent over the * connection. * + * Serialised against concurrent create and destroy using the + * gb_connection_mutex. + * * Return: A pointer to the new connection if successful, or NULL otherwise. */ static struct gb_connection * @@ -159,9 +164,11 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, return NULL; } + mutex_lock(&gb_connection_mutex); + hd_cport_id = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); if (hd_cport_id < 0) - return NULL; + goto err_unlock; connection = kzalloc(sizeof(*connection), GFP_KERNEL); if (!connection) @@ -201,12 +208,16 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, spin_unlock_irq(&gb_connections_lock); + mutex_unlock(&gb_connection_mutex); + return connection; err_free_connection: kfree(connection); err_remove_ida: ida_simple_remove(id_map, hd_cport_id); +err_unlock: + mutex_unlock(&gb_connection_mutex); return NULL; } @@ -524,6 +535,8 @@ void gb_connection_destroy(struct gb_connection *connection) if (!connection) return; + mutex_lock(&gb_connection_mutex); + spin_lock_irq(&gb_connections_lock); list_del(&connection->bundle_links); list_del(&connection->hd_links); @@ -535,6 +548,8 @@ void gb_connection_destroy(struct gb_connection *connection) ida_simple_remove(id_map, connection->hd_cport_id); connection->hd_cport_id = CPORT_ID_BAD; + mutex_unlock(&gb_connection_mutex); + gb_connection_put(connection); } -- cgit v1.2.3-59-g8ed1b From b53e0c9e8dceb170b054f186200b2fe3fb3718f2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:27 +0100 Subject: greybus: connection: make cport lookup thread-safe Use the gb_connection_mutex when making sure a remote CPort is available. This is needed when moving to driver-managed connections that can be created and destroyed at any time. Also check for duplicate bundle-less connections. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index de44775b0bc5..88c8a3fab2c6 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -19,7 +19,7 @@ static DEFINE_SPINLOCK(gb_connections_lock); static DEFINE_MUTEX(gb_connection_mutex); -/* This is only used at initialization time; no locking is required. */ +/* Caller holds gb_connection_mutex. */ static struct gb_connection * gb_connection_intf_find(struct gb_interface *intf, u16 cport_id) { @@ -142,17 +142,6 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, struct ida *id_map = &hd->cport_id_map; int ida_start, ida_end; - /* - * If a manifest tries to reuse a cport, reject it. We - * initialize connections serially so we don't need to worry - * about holding the connection lock. - */ - if (bundle && gb_connection_intf_find(bundle->intf, cport_id)) { - dev_err(&bundle->dev, "cport %u already connected\n", - cport_id); - return NULL; - } - if (hd_cport_id < 0) { ida_start = 0; ida_end = hd->num_cports; @@ -166,6 +155,11 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, mutex_lock(&gb_connection_mutex); + if (intf && gb_connection_intf_find(intf, cport_id)) { + dev_err(&intf->dev, "cport %u already in use\n", cport_id); + goto err_unlock; + } + hd_cport_id = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); if (hd_cport_id < 0) goto err_unlock; -- cgit v1.2.3-59-g8ed1b From 7d81bafa4ca477a4d364dd229bbbeffb1fa7af4e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 19 Jan 2016 12:51:28 +0100 Subject: greybus: legacy: add private driver data Allocate private data structure at probe and release at disconnect. The private data will be used to track the connections managed by the driver, but for now only holds a count of the number of cports the bundle has. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 186482300682..1e878beea8a5 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -12,6 +12,11 @@ #include "protocol.h" +struct legacy_data { + size_t num_cports; +}; + + static int legacy_connection_get_version(struct gb_connection *connection) { int ret; @@ -123,11 +128,23 @@ static void legacy_connection_exit(struct gb_connection *connection) static int legacy_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { + struct legacy_data *data; struct gb_connection *connection; int ret; - dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__, - bundle->class); + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->num_cports = 0; + list_for_each_entry(connection, &bundle->connections, bundle_links) + data->num_cports++; + + dev_dbg(&bundle->dev, + "%s - bundle class = 0x%02x, num_cports = %zu\n", + __func__, bundle->class, data->num_cports); + + greybus_set_drvdata(bundle, data); list_for_each_entry(connection, &bundle->connections, bundle_links) { dev_dbg(&bundle->dev, "enabling connection %s\n", @@ -145,12 +162,14 @@ err_connections_disable: bundle_links) { legacy_connection_exit(connection); } + kfree(data); return ret; } static void legacy_disconnect(struct gb_bundle *bundle) { + struct legacy_data *data = greybus_get_drvdata(bundle); struct gb_connection *connection; dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__, @@ -160,6 +179,8 @@ static void legacy_disconnect(struct gb_bundle *bundle) bundle_links) { legacy_connection_exit(connection); } + + kfree(data); } static const struct greybus_bundle_id legacy_id_table[] = { -- cgit v1.2.3-59-g8ed1b From 4b874134284b1dbb340f063fe0cf5141ffd416b1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 Jan 2016 14:41:40 -0800 Subject: greybus: audio_codec: Don't be tricky with the driver model With the recent changes by Johan to the driver model of the greybus code, the audio_codec no longer should need to provide a "dummy" driver when registering the codec. In fact, having it there causes the greybus core to crash when inserting the module. Removing it all fixes the crash. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 79f2baf607f4..5c02b6534ab2 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -571,13 +571,6 @@ static void gbaudio_free_codec(struct device *dev, * This is the basic hook get things initialized and registered w/ gb */ -/* - * GB codec module driver ops - */ -struct device_driver gb_codec_driver = { - .owner = THIS_MODULE, -}; - /* XXX * since BE DAI path is not yet properly closed from above layer, * dsp dai.mi2s_dai_data.status_mask is still set to STATUS_PORT_STARTED @@ -625,7 +618,6 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) static int gbaudio_codec_probe(struct gb_connection *connection) { int ret, i; - char *driver_name; struct gbaudio_codec_info *gbcodec; struct gb_audio_topology *topology; struct gb_audio_manager_module_descriptor desc; @@ -669,13 +661,6 @@ static int gbaudio_codec_probe(struct gb_connection *connection) for (i = 0; i < gbcodec->num_dais; i++) gbcodec->dais[i].ops = &gbcodec_dai_ops; - /* FIXME */ - driver_name = devm_kzalloc(dev, NAME_SIZE, GFP_KERNEL); - strlcpy(driver_name, gbcodec->name, NAME_SIZE); - gb_codec_driver.name = strsep(&driver_name, "."); - dev_dbg(dev, "driver.name:%s\n", gb_codec_driver.name); - dev->driver = &gb_codec_driver; - /* register codec */ ret = snd_soc_register_codec(dev, &soc_codec_dev_gbcodec, gbcodec->dais, 1); -- cgit v1.2.3-59-g8ed1b From ebe99d61815fea0ecece5ddb530b28339f8d5ed3 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 21 Jan 2016 01:42:17 +0000 Subject: greybus: svc: add key event handling Add a new input device associated with the SVC to handle key events. This new events are transfer over a new greybus svc operation which is unidirectional. It was selected the KEY_A for representing the KEY_ARA_BUTTON key code. Signed-off-by: Rui Miguel Silva Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 10 +++ drivers/staging/greybus/svc.c | 114 ++++++++++++++++++++++++++-- drivers/staging/greybus/svc.h | 3 + 3 files changed, 121 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 84fb6ab6deca..2a48d95ea1b8 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -781,6 +781,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_ROUTE_DESTROY 0x0c #define GB_SVC_TYPE_INTF_SET_PWRM 0x10 #define GB_SVC_TYPE_INTF_EJECT 0x11 +#define GB_SVC_TYPE_KEY_EVENT 0x12 /* * SVC version request/response has the same payload as @@ -930,6 +931,15 @@ struct gb_svc_intf_set_pwrm_response { __le16 result_code; } __packed; +struct gb_svc_key_event_request { + __le16 key_code; +#define GB_KEYCODE_ARA 0x00 + + __u8 key_event; +#define GB_SVC_KEY_RELEASED 0x00 +#define GB_SVC_KEY_PRESSED 0x01 +} __packed; + /* RAW */ /* Version of the Greybus raw protocol we support */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index bc64f4894b46..ad04a952cb36 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -7,6 +7,7 @@ * Released under the GPLv2 only. */ +#include #include #include "greybus.h" @@ -15,6 +16,7 @@ #define CPORT_FLAGS_CSD_N BIT(1) #define CPORT_FLAGS_CSV_N BIT(2) +#define SVC_KEY_ARA_BUTTON KEY_A struct gb_svc_deferred_request { struct work_struct work; @@ -420,6 +422,13 @@ static int gb_svc_hello(struct gb_operation *op) return ret; } + ret = input_register_device(svc->input); + if (ret) { + dev_err(&svc->dev, "failed to register input: %d\n", ret); + device_del(&svc->dev); + return ret; + } + return 0; } @@ -690,6 +699,53 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) return 0; } +static int gb_svc_key_code_map(struct gb_svc *svc, u16 key_code, u16 *code) +{ + switch (key_code) { + case GB_KEYCODE_ARA: + *code = SVC_KEY_ARA_BUTTON; + break; + default: + dev_warn(&svc->dev, "unknown keycode received: %u\n", key_code); + return -EINVAL; + } + + return 0; +} + +static int gb_svc_key_event_recv(struct gb_operation *op) +{ + struct gb_svc *svc = op->connection->private; + struct gb_message *request = op->request; + struct gb_svc_key_event_request *key; + u16 code; + u8 event; + int ret; + + if (request->payload_size < sizeof(*key)) { + dev_warn(&svc->dev, "short key request received (%zu < %zu)\n", + request->payload_size, sizeof(*key)); + return -EINVAL; + } + + key = request->payload; + + ret = gb_svc_key_code_map(svc, le16_to_cpu(key->key_code), &code); + if (ret < 0) + return ret; + + event = key->key_event; + if ((event != GB_SVC_KEY_PRESSED) && (event != GB_SVC_KEY_RELEASED)) { + dev_warn(&svc->dev, "unknown key event received: %u\n", event); + return -EINVAL; + } + + input_report_key(svc->input, code, (event == GB_SVC_KEY_PRESSED)); + input_sync(svc->input); + + return 0; +} + static int gb_svc_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -745,12 +801,42 @@ static int gb_svc_request_handler(struct gb_operation *op) return gb_svc_intf_hot_unplug_recv(op); case GB_SVC_TYPE_INTF_RESET: return gb_svc_intf_reset_recv(op); + case GB_SVC_TYPE_KEY_EVENT: + return gb_svc_key_event_recv(op); default: dev_warn(&svc->dev, "unsupported request 0x%02x\n", type); return -EINVAL; } } +static struct input_dev *gb_svc_input_create(struct gb_svc *svc) +{ + struct input_dev *input_dev; + + input_dev = input_allocate_device(); + if (!input_dev) + return ERR_PTR(-ENOMEM); + + input_dev->name = dev_name(&svc->dev); + svc->input_phys = kasprintf(GFP_KERNEL, "greybus-%s/input0", + input_dev->name); + if (!svc->input_phys) + goto err_free_input; + + input_dev->phys = svc->input_phys; + input_dev->dev.parent = &svc->dev; + + input_set_drvdata(input_dev, svc); + + input_set_capability(input_dev, EV_KEY, SVC_KEY_ARA_BUTTON); + + return input_dev; + +err_free_input: + input_free_device(svc->input); + return ERR_PTR(-ENOMEM); +} + static void gb_svc_release(struct device *dev) { struct gb_svc *svc = to_gb_svc(dev); @@ -759,6 +845,7 @@ static void gb_svc_release(struct device *dev) gb_connection_destroy(svc->connection); ida_destroy(&svc->device_id_map); destroy_workqueue(svc->wq); + kfree(svc->input_phys); kfree(svc); } @@ -794,17 +881,29 @@ struct gb_svc *gb_svc_create(struct gb_host_device *hd) svc->state = GB_SVC_STATE_RESET; svc->hd = hd; + svc->input = gb_svc_input_create(svc); + if (IS_ERR(svc->input)) { + dev_err(&svc->dev, "failed to create input device: %ld\n", + PTR_ERR(svc->input)); + goto err_put_device; + } + svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, GREYBUS_PROTOCOL_SVC); if (!svc->connection) { dev_err(&svc->dev, "failed to create connection\n"); - put_device(&svc->dev); - return NULL; + goto err_free_input; } svc->connection->private = svc; return svc; + +err_free_input: + input_free_device(svc->input); +err_put_device: + put_device(&svc->dev); + return NULL; } int gb_svc_add(struct gb_svc *svc) @@ -825,13 +924,16 @@ int gb_svc_add(struct gb_svc *svc) void gb_svc_del(struct gb_svc *svc) { + gb_connection_disable(svc->connection); + /* - * The SVC device may have been registered from the request handler. + * The SVC device and input device may have been registered + * from the request handler. */ - if (device_is_registered(&svc->dev)) + if (device_is_registered(&svc->dev)) { + input_unregister_device(svc->input); device_del(&svc->dev); - - gb_connection_disable(svc->connection); + } flush_workqueue(svc->wq); } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 4abc5efad807..f079b4dcc6e6 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -30,6 +30,9 @@ struct gb_svc { u8 protocol_major; u8 protocol_minor; + + struct input_dev *input; + char *input_phys; }; #define to_gb_svc(d) container_of(d, struct gb_svc, d) -- cgit v1.2.3-59-g8ed1b From eb8fafdfb9fce95b7d4f4bc9d34a46863176d7f8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 Jan 2016 09:27:05 -0800 Subject: greybus: firmware: log the name of the firmware being requested People are getting confused as to what the firmware file name is for their module, so log it to make it easier to find modules with "blank" vid/pid values and let people know what to name their firmware files. Because we are now logging this, turn the debug message after the firmware request into an error message if something fails. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 408c7e2c0b08..6d8fd0ecbc1f 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -88,10 +88,19 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, firmware->vendor_id, firmware->product_id, stage); + // FIXME: + // Turn to dev_dbg later after everyone has valid bootloaders with good + // ids, but leave this as dev_info for now to make it easier to track + // down "empty" vid/pid modules. + dev_info(&connection->bundle->dev, "Firmware file '%s' requested\n", + firmware_name); + rc = request_firmware(&firmware->fw, firmware_name, &connection->bundle->dev); - dev_dbg(&connection->bundle->dev, "Searched for TFTF %s: %d\n", - firmware_name, rc); + if (rc) + dev_err(&connection->bundle->dev, + "Firware request for %s has failed : %d", + firmware_name, rc); return rc; } -- cgit v1.2.3-59-g8ed1b From 55ec09e898adb43cf69ebd8839e3d5656a0e7345 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 19 Jan 2016 23:30:42 -0800 Subject: greybus: svc: add ping command This implements the SVC "ping" command. It's tiny and simple, but we need something like this in order for us to "know" if all is working well. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 1 + drivers/staging/greybus/svc.c | 7 +++++++ drivers/staging/greybus/svc.h | 1 + 3 files changed, 9 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 2a48d95ea1b8..3f51fb5d6e8b 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -782,6 +782,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_SET_PWRM 0x10 #define GB_SVC_TYPE_INTF_EJECT 0x11 #define GB_SVC_TYPE_KEY_EVENT 0x12 +#define GB_SVC_TYPE_PING 0x13 /* * SVC version request/response has the same payload as diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ad04a952cb36..8c675d3215e1 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -363,6 +363,13 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, } EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); +int gb_svc_ping(struct gb_svc *svc) +{ + return gb_operation_sync(svc->connection, GB_SVC_TYPE_PING, + NULL, 0, NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_svc_ping); + static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index f079b4dcc6e6..f3e8479b9438 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -55,6 +55,7 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, u8 tx_mode, u8 tx_gear, u8 tx_nlanes, u8 rx_mode, u8 rx_gear, u8 rx_nlanes, u8 flags, u32 quirks); +int gb_svc_ping(struct gb_svc *svc); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From 6ce4cc278deca9849bd54783294be7edbe24bac0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 21 Jan 2016 18:13:41 -0800 Subject: greybus: add bundle class to the bundle uevent When bundles are added and then removed, we have a race where we go to read the sysfs file, but it is now for a different bundle than the uevent was originally for. So add the bundle class to the uevent so we "know" what the correct bundle class was. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 522d0594eebc..1bb685bae7d2 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -125,6 +125,8 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "BUNDLE=%u", bundle->id)) return -ENOMEM; + if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class)) + return -ENOMEM; } return 0; -- cgit v1.2.3-59-g8ed1b From 98fdf5a037f0789f1ea6e22431a4a4cc7c41829d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:09 +0100 Subject: greybus: core: defer connection creation to driver probe Defer connection creation to bundle driver probe instead of creating them when initialising the interface and parsing the manifest. Store copies of the CPorts descriptors in the bundle for the drivers to use, and update the legacy driver. This is needed for drivers that need more control over host-device resource management, for example, when a protocol needs to use a dedicated host CPort for traffic offloading (e.g. camera data). This also avoids allocating host CPorts for bundles that are not bound to a driver or for remote CPorts that a driver does not need. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.c | 8 +----- drivers/staging/greybus/bundle.h | 3 ++ drivers/staging/greybus/connection.c | 2 ++ drivers/staging/greybus/core.c | 15 ++++++---- drivers/staging/greybus/legacy.c | 56 +++++++++++++++++++++++++----------- drivers/staging/greybus/manifest.c | 35 +++++++++++++--------- 6 files changed, 77 insertions(+), 42 deletions(-) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index ec52bdecb5c1..1714482bd34d 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -81,15 +81,9 @@ static struct gb_bundle *gb_bundle_find(struct gb_interface *intf, static void gb_bundle_release(struct device *dev) { struct gb_bundle *bundle = to_gb_bundle(dev); - struct gb_connection *connection; - struct gb_connection *tmp; - - list_for_each_entry_safe(connection, tmp, &bundle->connections, - bundle_links) { - gb_connection_destroy(connection); - } kfree(bundle->state); + kfree(bundle->cport_desc); kfree(bundle); } diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 837682d91e5b..48fb3fd76817 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -22,6 +22,9 @@ struct gb_bundle { u8 class_major; u8 class_minor; + size_t num_cports; + struct greybus_descriptor_cport *cport_desc; + struct list_head connections; u8 *state; diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 88c8a3fab2c6..c92af6e1a11f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -232,6 +232,7 @@ gb_connection_create_dynamic(struct gb_interface *intf, return gb_connection_create(intf->hd, -1, intf, bundle, cport_id, protocol_id); } +EXPORT_SYMBOL_GPL(gb_connection_create_dynamic); static int gb_connection_hd_cport_enable(struct gb_connection *connection) { @@ -546,6 +547,7 @@ void gb_connection_destroy(struct gb_connection *connection) gb_connection_put(connection); } +EXPORT_SYMBOL_GPL(gb_connection_destroy); void gb_connection_latency_tag_enable(struct gb_connection *connection) { diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 1bb685bae7d2..b9303c0cd5e4 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -151,8 +151,14 @@ static int greybus_probe(struct device *dev) return -ENODEV; retval = driver->probe(bundle, id); - if (retval) + if (retval) { + /* + * Catch buggy drivers that fail to destroy their connections. + */ + WARN_ON(!list_empty(&bundle->connections)); + return retval; + } return 0; } @@ -172,11 +178,8 @@ static int greybus_remove(struct device *dev) driver->disconnect(bundle); - /* Catch buggy drivers that fail to disable their connections. */ - list_for_each_entry(connection, &bundle->connections, bundle_links) { - if (WARN_ON(connection->state != GB_CONNECTION_STATE_DISABLED)) - gb_connection_disable(connection); - } + /* Catch buggy drivers that fail to destroy their connections. */ + WARN_ON(!list_empty(&bundle->connections)); return 0; } diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 1e878beea8a5..0f3dc8689fb4 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -14,6 +14,7 @@ struct legacy_data { size_t num_cports; + struct gb_connection **connections; }; @@ -128,25 +129,44 @@ static void legacy_connection_exit(struct gb_connection *connection) static int legacy_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { + struct greybus_descriptor_cport *cport_desc; struct legacy_data *data; struct gb_connection *connection; - int ret; + int i; + int ret = -ENOMEM; + + dev_dbg(&bundle->dev, + "%s - bundle class = 0x%02x, num_cports = %zu\n", + __func__, bundle->class, bundle->num_cports); data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->num_cports = 0; - list_for_each_entry(connection, &bundle->connections, bundle_links) - data->num_cports++; + data->num_cports = bundle->num_cports; + data->connections = kcalloc(data->num_cports, + sizeof(*data->connections), + GFP_KERNEL); + if (!data->connections) + goto err_free_data; - dev_dbg(&bundle->dev, - "%s - bundle class = 0x%02x, num_cports = %zu\n", - __func__, bundle->class, data->num_cports); + for (i = 0; i < data->num_cports; ++i) { + cport_desc = &bundle->cport_desc[i]; + + connection = gb_connection_create_dynamic(bundle->intf, + bundle, + le16_to_cpu(cport_desc->id), + cport_desc->protocol_id); + if (!connection) + goto err_connections_destroy; + + data->connections[i] = connection; + } greybus_set_drvdata(bundle, data); - list_for_each_entry(connection, &bundle->connections, bundle_links) { + for (i = 0; i < data->num_cports; ++i) { + connection = data->connections[i]; dev_dbg(&bundle->dev, "enabling connection %s\n", connection->name); @@ -158,10 +178,13 @@ static int legacy_probe(struct gb_bundle *bundle, return 0; err_connections_disable: - list_for_each_entry_reverse(connection, &bundle->connections, - bundle_links) { - legacy_connection_exit(connection); - } + for (--i; i >= 0; --i) + legacy_connection_exit(data->connections[i]); +err_connections_destroy: + for (i = 0; i < data->num_cports; ++i) + gb_connection_destroy(data->connections[i]); + kfree(data->connections); +err_free_data: kfree(data); return ret; @@ -170,16 +193,17 @@ err_connections_disable: static void legacy_disconnect(struct gb_bundle *bundle) { struct legacy_data *data = greybus_get_drvdata(bundle); - struct gb_connection *connection; + int i; dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__, bundle->class); - list_for_each_entry_reverse(connection, &bundle->connections, - bundle_links) { - legacy_connection_exit(connection); + for (i = 0; i < data->num_cports; ++i) { + legacy_connection_exit(data->connections[i]); + gb_connection_destroy(data->connections[i]); } + kfree(data->connections); kfree(data); } diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 357f9c64821b..64c60c2dfaf5 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -228,19 +228,18 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id) */ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) { - struct gb_connection *connection; struct gb_interface *intf = bundle->intf; + struct greybus_descriptor_cport *desc_cport; struct manifest_desc *desc; struct manifest_desc *next; + LIST_HEAD(list); u8 bundle_id = bundle->id; - u8 protocol_id; u16 cport_id; u32 count = 0; + int i; /* Set up all cport descriptors associated with this bundle */ list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { - struct greybus_descriptor_cport *desc_cport; - if (desc->type != GREYBUS_TYPE_CPORT) continue; @@ -252,16 +251,26 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) if (cport_id > CPORT_ID_MAX) goto exit; - /* Found one. Set up its function structure */ - protocol_id = desc_cport->protocol_id; + /* Found one, move it to our temporary list. */ + list_move(&desc->links, &list); + count++; + } - connection = gb_connection_create_dynamic(intf, bundle, - cport_id, - protocol_id); - if (!connection) - goto exit; + if (!count) + return 0; - count++; + bundle->cport_desc = kcalloc(count, sizeof(*bundle->cport_desc), + GFP_KERNEL); + if (!bundle->cport_desc) + goto exit; + + bundle->num_cports = count; + + i = 0; + list_for_each_entry_safe(desc, next, &list, links) { + desc_cport = desc->data; + memcpy(&bundle->cport_desc[i++], desc_cport, + sizeof(*desc_cport)); /* Release the cport descriptor */ release_manifest_descriptor(desc); @@ -269,7 +278,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) return count; exit: - + release_cport_descriptors(&list, bundle_id); /* * Free all cports for this bundle to avoid 'excess descriptors' * warnings. -- cgit v1.2.3-59-g8ed1b From d6fba3dbb04dac64cf7fa6d33ea7fce81ecf862c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:10 +0100 Subject: greybus: manifest: check for duplicate CPort descriptors when parsing Now that connection creation has been separated from interface initialisation, we should explicitly check for duplicate CPort descriptors when parsing the manifest. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 64c60c2dfaf5..5ca36c9001be 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -230,8 +230,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) { struct gb_interface *intf = bundle->intf; struct greybus_descriptor_cport *desc_cport; - struct manifest_desc *desc; - struct manifest_desc *next; + struct manifest_desc *desc, *next, *tmp; LIST_HEAD(list); u8 bundle_id = bundle->id; u16 cport_id; @@ -251,7 +250,19 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) if (cport_id > CPORT_ID_MAX) goto exit; - /* Found one, move it to our temporary list. */ + /* + * Found one, move it to our temporary list after checking for + * duplicates. + */ + list_for_each_entry(tmp, &list, links) { + desc_cport = tmp->data; + if (cport_id == desc_cport->id) { + dev_err(&bundle->dev, + "duplicate CPort %u found\n", + cport_id); + goto exit; + } + } list_move(&desc->links, &list); count++; } -- cgit v1.2.3-59-g8ed1b From bafe3f67908c68a0b1fdc527c79cd7d9106abd85 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:11 +0100 Subject: greybus: connection: drop protocol parameter from static interface Drop legacy protocol parameter from static connection interface. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ++---- drivers/staging/greybus/connection.h | 2 +- drivers/staging/greybus/svc.c | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c92af6e1a11f..9b8112bd16a0 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -217,11 +217,9 @@ err_unlock: } struct gb_connection * -gb_connection_create_static(struct gb_host_device *hd, - u16 hd_cport_id, u8 protocol_id) +gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id) { - return gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, - protocol_id); + return gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, 0); } struct gb_connection * diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 66f37b2f4a38..562bd2b47ac5 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -56,7 +56,7 @@ struct gb_connection { }; struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, - u16 hd_cport_id, u8 protocol_id); + u16 hd_cport_id); struct gb_connection *gb_connection_create_dynamic(struct gb_interface *intf, struct gb_bundle *bundle, u16 cport_id, u8 protocol_id); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 8c675d3215e1..472997e71343 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -895,8 +895,7 @@ struct gb_svc *gb_svc_create(struct gb_host_device *hd) goto err_put_device; } - svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, - GREYBUS_PROTOCOL_SVC); + svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID); if (!svc->connection) { dev_err(&svc->dev, "failed to create connection\n"); goto err_free_input; -- cgit v1.2.3-59-g8ed1b From 59507e2612379356d93de0bd1d6e0f9a36dca0da Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:12 +0100 Subject: greybus: connection: add helper to create control connections Add dedicated helper to create control connections. This will allow us to simplify the generic (dynamic) interface. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 ++++++ drivers/staging/greybus/connection.h | 1 + drivers/staging/greybus/control.c | 4 +--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 9b8112bd16a0..f81f053a2e98 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -222,6 +222,12 @@ gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id) return gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, 0); } +struct gb_connection * +gb_connection_create_control(struct gb_interface *intf) +{ + return gb_connection_create(intf->hd, -1, intf, NULL, 0, 0); +} + struct gb_connection * gb_connection_create_dynamic(struct gb_interface *intf, struct gb_bundle *bundle, diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 562bd2b47ac5..cd4a093f6e9b 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -57,6 +57,7 @@ struct gb_connection { struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id); +struct gb_connection *gb_connection_create_control(struct gb_interface *intf); struct gb_connection *gb_connection_create_dynamic(struct gb_interface *intf, struct gb_bundle *bundle, u16 cport_id, u8 protocol_id); diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 0e50fd419f3a..a766ec8b0228 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -186,9 +186,7 @@ struct gb_control *gb_control_create(struct gb_interface *intf) if (!control) return NULL; - control->connection = gb_connection_create_dynamic(intf, NULL, - GB_CONTROL_CPORT_ID, - GREYBUS_PROTOCOL_CONTROL); + control->connection = gb_connection_create_control(intf); if (!control->connection) { dev_err(&intf->dev, "failed to create control connection\n"); kfree(control); -- cgit v1.2.3-59-g8ed1b From e4c16f8b034047096187a9a9b49035ff5dbc8478 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:13 +0100 Subject: greybus: connection: drop the legacy protocol-id parameter The protocol-id parameter is only used by the legacy driver so remove it from the generic interface and move it to the legacy driver until it can be removed completely. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 15 +++++---------- drivers/staging/greybus/connection.h | 3 +-- drivers/staging/greybus/legacy.c | 5 +++-- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f81f053a2e98..8c7f2eac7323 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -118,7 +118,6 @@ static void gb_connection_init_name(struct gb_connection *connection) * @intf: remote interface, or NULL for static connections * @bundle: remote-interface bundle (may be NULL) * @cport_id: remote-interface cport id, or 0 for static connections - * @protocol_id: protocol id * * Create a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -135,8 +134,7 @@ static void gb_connection_init_name(struct gb_connection *connection) static struct gb_connection * gb_connection_create(struct gb_host_device *hd, int hd_cport_id, struct gb_interface *intf, - struct gb_bundle *bundle, int cport_id, - u8 protocol_id) + struct gb_bundle *bundle, int cport_id) { struct gb_connection *connection; struct ida *id_map = &hd->cport_id_map; @@ -173,8 +171,6 @@ gb_connection_create(struct gb_host_device *hd, int hd_cport_id, connection->hd = hd; connection->intf = intf; - connection->protocol_id = protocol_id; - connection->bundle = bundle; connection->state = GB_CONNECTION_STATE_DISABLED; @@ -219,22 +215,21 @@ err_unlock: struct gb_connection * gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id) { - return gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, 0); + return gb_connection_create(hd, hd_cport_id, NULL, NULL, 0); } struct gb_connection * gb_connection_create_control(struct gb_interface *intf) { - return gb_connection_create(intf->hd, -1, intf, NULL, 0, 0); + return gb_connection_create(intf->hd, -1, intf, NULL, 0); } struct gb_connection * gb_connection_create_dynamic(struct gb_interface *intf, struct gb_bundle *bundle, - u16 cport_id, u8 protocol_id) + u16 cport_id) { - return gb_connection_create(intf->hd, -1, intf, bundle, cport_id, - protocol_id); + return gb_connection_create(intf->hd, -1, intf, bundle, cport_id); } EXPORT_SYMBOL_GPL(gb_connection_create_dynamic); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index cd4a093f6e9b..442bd49b5052 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -59,8 +59,7 @@ struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id); struct gb_connection *gb_connection_create_control(struct gb_interface *intf); struct gb_connection *gb_connection_create_dynamic(struct gb_interface *intf, - struct gb_bundle *bundle, u16 cport_id, - u8 protocol_id); + struct gb_bundle *bundle, u16 cport_id); void gb_connection_destroy(struct gb_connection *connection); static inline bool gb_connection_is_static(struct gb_connection *connection) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 0f3dc8689fb4..c34764fc30b2 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -155,11 +155,12 @@ static int legacy_probe(struct gb_bundle *bundle, connection = gb_connection_create_dynamic(bundle->intf, bundle, - le16_to_cpu(cport_desc->id), - cport_desc->protocol_id); + le16_to_cpu(cport_desc->id)); if (!connection) goto err_connections_destroy; + connection->protocol_id = cport_desc->protocol_id; + data->connections[i] = connection; } -- cgit v1.2.3-59-g8ed1b From 8cff6c6473db471d7da9dd6dfbfcebdc4d617aa8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:14 +0100 Subject: greybus: connection: simplify connection-creation interface Simplify the exported generic interface for creating dynamic connections. Since all driver connections will have a bundle we can drop the redundant interface parameter. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 6 +++--- drivers/staging/greybus/connection.h | 4 ++-- drivers/staging/greybus/legacy.c | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 8c7f2eac7323..227bfe8ba341 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -225,10 +225,10 @@ gb_connection_create_control(struct gb_interface *intf) } struct gb_connection * -gb_connection_create_dynamic(struct gb_interface *intf, - struct gb_bundle *bundle, - u16 cport_id) +gb_connection_create_dynamic(struct gb_bundle *bundle, u16 cport_id) { + struct gb_interface *intf = bundle->intf; + return gb_connection_create(intf->hd, -1, intf, bundle, cport_id); } EXPORT_SYMBOL_GPL(gb_connection_create_dynamic); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 442bd49b5052..863109cb4180 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -58,8 +58,8 @@ struct gb_connection { struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id); struct gb_connection *gb_connection_create_control(struct gb_interface *intf); -struct gb_connection *gb_connection_create_dynamic(struct gb_interface *intf, - struct gb_bundle *bundle, u16 cport_id); +struct gb_connection *gb_connection_create_dynamic(struct gb_bundle *bundle, + u16 cport_id); void gb_connection_destroy(struct gb_connection *connection); static inline bool gb_connection_is_static(struct gb_connection *connection) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index c34764fc30b2..86ab7b7141ec 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -153,8 +153,7 @@ static int legacy_probe(struct gb_bundle *bundle, for (i = 0; i < data->num_cports; ++i) { cport_desc = &bundle->cport_desc[i]; - connection = gb_connection_create_dynamic(bundle->intf, - bundle, + connection = gb_connection_create_dynamic(bundle, le16_to_cpu(cport_desc->id)); if (!connection) goto err_connections_destroy; -- cgit v1.2.3-59-g8ed1b From 96c2af5c6bf7dda9498fdcea57fde1bdc677236a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:15 +0100 Subject: greybus: connection: rename connection-create interface Drop the _dynamic suffix from the exported interface that drivers use to create connections. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 14 +++++++------- drivers/staging/greybus/connection.h | 2 +- drivers/staging/greybus/legacy.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 227bfe8ba341..5d0edbe7e749 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -112,7 +112,7 @@ static void gb_connection_init_name(struct gb_connection *connection) } /* - * gb_connection_create() - create a Greybus connection + * _gb_connection_create() - create a Greybus connection * @hd: host device of the connection * @hd_cport_id: host-device cport id, or -1 for dynamic allocation * @intf: remote interface, or NULL for static connections @@ -132,7 +132,7 @@ static void gb_connection_init_name(struct gb_connection *connection) * Return: A pointer to the new connection if successful, or NULL otherwise. */ static struct gb_connection * -gb_connection_create(struct gb_host_device *hd, int hd_cport_id, +_gb_connection_create(struct gb_host_device *hd, int hd_cport_id, struct gb_interface *intf, struct gb_bundle *bundle, int cport_id) { @@ -215,23 +215,23 @@ err_unlock: struct gb_connection * gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id) { - return gb_connection_create(hd, hd_cport_id, NULL, NULL, 0); + return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0); } struct gb_connection * gb_connection_create_control(struct gb_interface *intf) { - return gb_connection_create(intf->hd, -1, intf, NULL, 0); + return _gb_connection_create(intf->hd, -1, intf, NULL, 0); } struct gb_connection * -gb_connection_create_dynamic(struct gb_bundle *bundle, u16 cport_id) +gb_connection_create(struct gb_bundle *bundle, u16 cport_id) { struct gb_interface *intf = bundle->intf; - return gb_connection_create(intf->hd, -1, intf, bundle, cport_id); + return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id); } -EXPORT_SYMBOL_GPL(gb_connection_create_dynamic); +EXPORT_SYMBOL_GPL(gb_connection_create); static int gb_connection_hd_cport_enable(struct gb_connection *connection) { diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 863109cb4180..0e78f10d91c8 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -58,7 +58,7 @@ struct gb_connection { struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id); struct gb_connection *gb_connection_create_control(struct gb_interface *intf); -struct gb_connection *gb_connection_create_dynamic(struct gb_bundle *bundle, +struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id); void gb_connection_destroy(struct gb_connection *connection); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 86ab7b7141ec..fb0a82edcfb1 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -153,7 +153,7 @@ static int legacy_probe(struct gb_bundle *bundle, for (i = 0; i < data->num_cports; ++i) { cport_desc = &bundle->cport_desc[i]; - connection = gb_connection_create_dynamic(bundle, + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id)); if (!connection) goto err_connections_destroy; -- cgit v1.2.3-59-g8ed1b From 24e094d687a23878024589a854ce66a070b769cc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:16 +0100 Subject: greybus: connection: return error-valued pointer on creation errors Return an ERR_PTR on errors when creating connections. This allows driver probe to fail with a more informative error message as not all connection creation errors are due to memory exhaustion. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 22 +++++++++++++++------- drivers/staging/greybus/control.c | 6 ++++-- drivers/staging/greybus/legacy.c | 10 +++++++--- drivers/staging/greybus/svc.c | 5 +++-- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 5d0edbe7e749..99efe31eee63 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -129,7 +129,8 @@ static void gb_connection_init_name(struct gb_connection *connection) * Serialised against concurrent create and destroy using the * gb_connection_mutex. * - * Return: A pointer to the new connection if successful, or NULL otherwise. + * Return: A pointer to the new connection if successful, or an ERR_PTR + * otherwise. */ static struct gb_connection * _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, @@ -139,6 +140,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, struct gb_connection *connection; struct ida *id_map = &hd->cport_id_map; int ida_start, ida_end; + int ret; if (hd_cport_id < 0) { ida_start = 0; @@ -148,23 +150,27 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, ida_end = hd_cport_id + 1; } else { dev_err(&hd->dev, "cport %d not available\n", hd_cport_id); - return NULL; + return ERR_PTR(-EINVAL); } mutex_lock(&gb_connection_mutex); if (intf && gb_connection_intf_find(intf, cport_id)) { dev_err(&intf->dev, "cport %u already in use\n", cport_id); + ret = -EBUSY; goto err_unlock; } - hd_cport_id = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); - if (hd_cport_id < 0) + ret = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); + if (ret < 0) goto err_unlock; + hd_cport_id = ret; connection = kzalloc(sizeof(*connection), GFP_KERNEL); - if (!connection) + if (!connection) { + ret = -ENOMEM; goto err_remove_ida; + } connection->hd_cport_id = hd_cport_id; connection->intf_cport_id = cport_id; @@ -181,8 +187,10 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, connection->wq = alloc_workqueue("%s:%d", WQ_UNBOUND, 1, dev_name(&hd->dev), hd_cport_id); - if (!connection->wq) + if (!connection->wq) { + ret = -ENOMEM; goto err_free_connection; + } kref_init(&connection->kref); @@ -209,7 +217,7 @@ err_remove_ida: err_unlock: mutex_unlock(&gb_connection_mutex); - return NULL; + return ERR_PTR(ret); } struct gb_connection * diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index a766ec8b0228..fb0bb1c40d99 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -187,8 +187,10 @@ struct gb_control *gb_control_create(struct gb_interface *intf) return NULL; control->connection = gb_connection_create_control(intf); - if (!control->connection) { - dev_err(&intf->dev, "failed to create control connection\n"); + if (IS_ERR(control->connection)) { + dev_err(&intf->dev, + "failed to create control connection: %ld\n", + PTR_ERR(control->connection)); kfree(control); return NULL; } diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index fb0a82edcfb1..fc19b8bc75c9 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -133,7 +133,7 @@ static int legacy_probe(struct gb_bundle *bundle, struct legacy_data *data; struct gb_connection *connection; int i; - int ret = -ENOMEM; + int ret; dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x, num_cports = %zu\n", @@ -147,16 +147,20 @@ static int legacy_probe(struct gb_bundle *bundle, data->connections = kcalloc(data->num_cports, sizeof(*data->connections), GFP_KERNEL); - if (!data->connections) + if (!data->connections) { + ret = -ENOMEM; goto err_free_data; + } for (i = 0; i < data->num_cports; ++i) { cport_desc = &bundle->cport_desc[i]; connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id)); - if (!connection) + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); goto err_connections_destroy; + } connection->protocol_id = cport_desc->protocol_id; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 472997e71343..845f82da0c1d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -896,8 +896,9 @@ struct gb_svc *gb_svc_create(struct gb_host_device *hd) } svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID); - if (!svc->connection) { - dev_err(&svc->dev, "failed to create connection\n"); + if (IS_ERR(svc->connection)) { + dev_err(&svc->dev, "failed to create connection: %ld\n", + PTR_ERR(svc->connection)); goto err_free_input; } -- cgit v1.2.3-59-g8ed1b From 431b3ebb3d9e0c25081006fc7a1f97621dca6192 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:17 +0100 Subject: greybus: legacy: refactor legacy-connection handling Add wrapper structure for legacy connections and add helpers to create and destroy legacy connections. This is a step in removing legacy connection fields from the core structures. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 67 +++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index fc19b8bc75c9..37c421248611 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -12,9 +12,13 @@ #include "protocol.h" +struct legacy_connection { + struct gb_connection *connection; +}; + struct legacy_data { size_t num_cports; - struct gb_connection **connections; + struct legacy_connection *connections; }; @@ -78,12 +82,16 @@ static int legacy_request_handler(struct gb_operation *operation) return protocol->request_recv(operation->type, operation); } -static int legacy_connection_init(struct gb_connection *connection) +static int legacy_connection_init(struct legacy_connection *lc) { + struct gb_connection *connection = lc->connection; gb_request_handler_t handler; int ret; - ret = legacy_connection_bind_protocol(connection); + dev_dbg(&connection->bundle->dev, "%s - %s\n", __func__, + connection->name); + + ret = legacy_connection_bind_protocol(lc->connection); if (ret) return ret; @@ -114,8 +122,10 @@ err_unbind_protocol: return ret; } -static void legacy_connection_exit(struct gb_connection *connection) +static void legacy_connection_exit(struct legacy_connection *lc) { + struct gb_connection *connection = lc->connection; + if (!connection->protocol) return; @@ -126,12 +136,33 @@ static void legacy_connection_exit(struct gb_connection *connection) legacy_connection_unbind_protocol(connection); } +static int legacy_connection_create(struct legacy_connection *lc, + struct gb_bundle *bundle, + struct greybus_descriptor_cport *desc) +{ + struct gb_connection *connection; + + connection = gb_connection_create(bundle, le16_to_cpu(desc->id)); + if (IS_ERR(connection)) + return PTR_ERR(connection); + + lc->connection = connection; + lc->connection->protocol_id = desc->protocol_id; + + return 0; +} + +static void legacy_connection_destroy(struct legacy_connection *lc) +{ + gb_connection_destroy(lc->connection); +} + static int legacy_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { struct greybus_descriptor_cport *cport_desc; struct legacy_data *data; - struct gb_connection *connection; + struct legacy_connection *lc; int i; int ret; @@ -154,27 +185,19 @@ static int legacy_probe(struct gb_bundle *bundle, for (i = 0; i < data->num_cports; ++i) { cport_desc = &bundle->cport_desc[i]; + lc = &data->connections[i]; - connection = gb_connection_create(bundle, - le16_to_cpu(cport_desc->id)); - if (IS_ERR(connection)) { - ret = PTR_ERR(connection); + ret = legacy_connection_create(lc, bundle, cport_desc); + if (ret) goto err_connections_destroy; - } - - connection->protocol_id = cport_desc->protocol_id; - - data->connections[i] = connection; } greybus_set_drvdata(bundle, data); for (i = 0; i < data->num_cports; ++i) { - connection = data->connections[i]; - dev_dbg(&bundle->dev, "enabling connection %s\n", - connection->name); + lc = &data->connections[i]; - ret = legacy_connection_init(connection); + ret = legacy_connection_init(lc); if (ret) goto err_connections_disable; } @@ -183,10 +206,10 @@ static int legacy_probe(struct gb_bundle *bundle, err_connections_disable: for (--i; i >= 0; --i) - legacy_connection_exit(data->connections[i]); + legacy_connection_exit(&data->connections[i]); err_connections_destroy: for (i = 0; i < data->num_cports; ++i) - gb_connection_destroy(data->connections[i]); + legacy_connection_destroy(&data->connections[i]); kfree(data->connections); err_free_data: kfree(data); @@ -203,8 +226,8 @@ static void legacy_disconnect(struct gb_bundle *bundle) bundle->class); for (i = 0; i < data->num_cports; ++i) { - legacy_connection_exit(data->connections[i]); - gb_connection_destroy(data->connections[i]); + legacy_connection_exit(&data->connections[i]); + legacy_connection_destroy(&data->connections[i]); } kfree(data->connections); -- cgit v1.2.3-59-g8ed1b From 8278fae5d727b02af2fb7e1ac518c63569096c9f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:18 +0100 Subject: greybus: legacy: add initialized flag Add initialized flag and use instead of the connection protocol pointer to determine when the legacy connection has been initialised. This is a step in moving legacy connection binding to connection-creation time. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 37c421248611..c7f59e4f5b8e 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -14,6 +14,7 @@ struct legacy_connection { struct gb_connection *connection; + bool initialized; }; struct legacy_data { @@ -112,6 +113,8 @@ static int legacy_connection_init(struct legacy_connection *lc) if (ret) goto err_disable; + lc->initialized = true; + return 0; err_disable: @@ -126,7 +129,7 @@ static void legacy_connection_exit(struct legacy_connection *lc) { struct gb_connection *connection = lc->connection; - if (!connection->protocol) + if (!lc->initialized) return; gb_connection_disable(connection); @@ -134,6 +137,8 @@ static void legacy_connection_exit(struct legacy_connection *lc) connection->protocol->connection_exit(connection); legacy_connection_unbind_protocol(connection); + + lc->initialized = false; } static int legacy_connection_create(struct legacy_connection *lc, -- cgit v1.2.3-59-g8ed1b From 63cf52f79b4c9f73657285031427ca748d11ce86 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:19 +0100 Subject: greybus: connection: remove legacy protocol id from core Remove legacy protocol-id field that is only used by the legacy-protocol driver from the connection structure. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.h | 1 - drivers/staging/greybus/legacy.c | 15 +++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 0e78f10d91c8..8e5284abb377 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -38,7 +38,6 @@ struct gb_connection { gb_request_handler_t handler; struct gb_protocol *protocol; - u8 protocol_id; u8 module_major; u8 module_minor; diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index c7f59e4f5b8e..340d8b5feba7 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -15,6 +15,7 @@ struct legacy_connection { struct gb_connection *connection; bool initialized; + u8 protocol_id; }; struct legacy_data { @@ -38,8 +39,9 @@ static int legacy_connection_get_version(struct gb_connection *connection) return 0; } -static int legacy_connection_bind_protocol(struct gb_connection *connection) +static int legacy_connection_bind_protocol(struct legacy_connection *lc) { + struct gb_connection *connection = lc->connection; struct gb_protocol *protocol; u8 major, minor; @@ -52,14 +54,11 @@ static int legacy_connection_bind_protocol(struct gb_connection *connection) major = 0; minor = 1; - protocol = gb_protocol_get(connection->protocol_id, - major, - minor); + protocol = gb_protocol_get(lc->protocol_id, major, minor); if (!protocol) { dev_err(&connection->hd->dev, "protocol 0x%02x version %u.%u not found\n", - connection->protocol_id, - major, minor); + lc->protocol_id, major, minor); return -EPROTONOSUPPORT; } connection->protocol = protocol; @@ -92,7 +91,7 @@ static int legacy_connection_init(struct legacy_connection *lc) dev_dbg(&connection->bundle->dev, "%s - %s\n", __func__, connection->name); - ret = legacy_connection_bind_protocol(lc->connection); + ret = legacy_connection_bind_protocol(lc); if (ret) return ret; @@ -152,7 +151,7 @@ static int legacy_connection_create(struct legacy_connection *lc, return PTR_ERR(connection); lc->connection = connection; - lc->connection->protocol_id = desc->protocol_id; + lc->protocol_id = desc->protocol_id; return 0; } -- cgit v1.2.3-59-g8ed1b From 2399032d7262f2a5dc1f22380d23b17971ed22e4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:20 +0100 Subject: greybus: legacy: look up protocol at connection creation Look up legacy protocol at connection create instead of at init. Note that we can now look up the protocol before even creating the actual connection. This is needed to be able to set a connection request handler when creating the connection rather than when enabling it. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 91 +++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 49 deletions(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 340d8b5feba7..4f3476c45d3a 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -15,7 +15,7 @@ struct legacy_connection { struct gb_connection *connection; bool initialized; - u8 protocol_id; + struct gb_protocol *protocol; }; struct legacy_data { @@ -39,42 +39,6 @@ static int legacy_connection_get_version(struct gb_connection *connection) return 0; } -static int legacy_connection_bind_protocol(struct legacy_connection *lc) -{ - struct gb_connection *connection = lc->connection; - struct gb_protocol *protocol; - u8 major, minor; - - /* - * The legacy protocols have always been looked up using a hard-coded - * version of 0.1, despite (or perhaps rather, due to) the fact that - * module version negotiation could not take place until after the - * protocol was bound. - */ - major = 0; - minor = 1; - - protocol = gb_protocol_get(lc->protocol_id, major, minor); - if (!protocol) { - dev_err(&connection->hd->dev, - "protocol 0x%02x version %u.%u not found\n", - lc->protocol_id, major, minor); - return -EPROTONOSUPPORT; - } - connection->protocol = protocol; - - return 0; -} - -static void legacy_connection_unbind_protocol(struct gb_connection *connection) -{ - struct gb_protocol *protocol = connection->protocol; - - gb_protocol_put(protocol); - - connection->protocol = NULL; -} - static int legacy_request_handler(struct gb_operation *operation) { struct gb_protocol *protocol = operation->connection->protocol; @@ -91,10 +55,6 @@ static int legacy_connection_init(struct legacy_connection *lc) dev_dbg(&connection->bundle->dev, "%s - %s\n", __func__, connection->name); - ret = legacy_connection_bind_protocol(lc); - if (ret) - return ret; - if (connection->protocol->request_recv) handler = legacy_request_handler; else @@ -102,7 +62,7 @@ static int legacy_connection_init(struct legacy_connection *lc) ret = gb_connection_enable(connection, handler); if (ret) - goto err_unbind_protocol; + return ret; ret = legacy_connection_get_version(connection); if (ret) @@ -118,8 +78,6 @@ static int legacy_connection_init(struct legacy_connection *lc) err_disable: gb_connection_disable(connection); -err_unbind_protocol: - legacy_connection_unbind_protocol(connection); return ret; } @@ -135,8 +93,6 @@ static void legacy_connection_exit(struct legacy_connection *lc) connection->protocol->connection_exit(connection); - legacy_connection_unbind_protocol(connection); - lc->initialized = false; } @@ -145,20 +101,57 @@ static int legacy_connection_create(struct legacy_connection *lc, struct greybus_descriptor_cport *desc) { struct gb_connection *connection; + struct gb_protocol *protocol; + u8 major, minor; + int ret; + + /* + * The legacy protocols have always been looked up using a hard-coded + * version of 0.1, despite (or perhaps rather, due to) the fact that + * module version negotiation could not take place until after the + * protocol was bound. + */ + major = 0; + minor = 1; + + protocol = gb_protocol_get(desc->protocol_id, major, minor); + if (!protocol) { + dev_err(&bundle->dev, + "protocol 0x%02x version %u.%u not found\n", + desc->protocol_id, major, minor); + return -EPROTONOSUPPORT; + } connection = gb_connection_create(bundle, le16_to_cpu(desc->id)); - if (IS_ERR(connection)) - return PTR_ERR(connection); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto err_protocol_put; + } + + /* + * NOTE: We need to keep a pointer to the protocol in the actual + * connection structure for now. + */ + connection->protocol = protocol; lc->connection = connection; - lc->protocol_id = desc->protocol_id; + lc->protocol = protocol; return 0; + +err_protocol_put: + gb_protocol_put(protocol); + + return ret; } static void legacy_connection_destroy(struct legacy_connection *lc) { + lc->connection->protocol = NULL; + gb_connection_destroy(lc->connection); + + gb_protocol_put(lc->protocol); } static int legacy_probe(struct gb_bundle *bundle, -- cgit v1.2.3-59-g8ed1b From f7ee081e3151e187e7478eb8941f61744f125201 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:21 +0100 Subject: greybus: connection: set request handlers at creation Set the connection request handler at creation rather than when enabling the connection. This is possible now that connections are created by the drivers that use them rather than by core at manifest parsing time. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 94 +++++++++++++++++++++++++----------- drivers/staging/greybus/connection.h | 12 ++--- drivers/staging/greybus/legacy.c | 17 ++++--- drivers/staging/greybus/svc.c | 5 +- 4 files changed, 82 insertions(+), 46 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 99efe31eee63..77c2f672b405 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -118,6 +118,7 @@ static void gb_connection_init_name(struct gb_connection *connection) * @intf: remote interface, or NULL for static connections * @bundle: remote-interface bundle (may be NULL) * @cport_id: remote-interface cport id, or 0 for static connections + * @handler: request handler (may be NULL) * * Create a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -135,7 +136,8 @@ static void gb_connection_init_name(struct gb_connection *connection) static struct gb_connection * _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, struct gb_interface *intf, - struct gb_bundle *bundle, int cport_id) + struct gb_bundle *bundle, int cport_id, + gb_request_handler_t handler) { struct gb_connection *connection; struct ida *id_map = &hd->cport_id_map; @@ -176,8 +178,8 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, connection->intf_cport_id = cport_id; connection->hd = hd; connection->intf = intf; - connection->bundle = bundle; + connection->handler = handler; connection->state = GB_CONNECTION_STATE_DISABLED; atomic_set(&connection->op_cycle, 0); @@ -221,23 +223,26 @@ err_unlock: } struct gb_connection * -gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id) +gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id, + gb_request_handler_t handler) { - return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0); + return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, handler); } struct gb_connection * gb_connection_create_control(struct gb_interface *intf) { - return _gb_connection_create(intf->hd, -1, intf, NULL, 0); + return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL); } struct gb_connection * -gb_connection_create(struct gb_bundle *bundle, u16 cport_id) +gb_connection_create(struct gb_bundle *bundle, u16 cport_id, + gb_request_handler_t handler) { struct gb_interface *intf = bundle->intf; - return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id); + return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, + handler); } EXPORT_SYMBOL_GPL(gb_connection_create); @@ -424,39 +429,42 @@ gb_connection_flush_incoming_operations(struct gb_connection *connection, } } -int gb_connection_enable(struct gb_connection *connection, - gb_request_handler_t handler) +/* + * _gb_connection_enable() - enable a connection + * @connection: connection to enable + * @rx: whether to enable incoming requests + * + * Connection-enable helper for DISABLED->ENABLED, DISABLED->ENABLED_TX, and + * ENABLED_TX->ENABLED state transitions. + * + * Locking: Caller holds connection->mutex. + */ +static int _gb_connection_enable(struct gb_connection *connection, bool rx) { int ret; - mutex_lock(&connection->mutex); - - if (connection->state == GB_CONNECTION_STATE_ENABLED) - goto out_unlock; - + /* Handle ENABLED_TX -> ENABLED transitions. */ if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) { - if (!handler) - goto out_unlock; + if (!(connection->handler && rx)) + return 0; spin_lock_irq(&connection->lock); - connection->handler = handler; connection->state = GB_CONNECTION_STATE_ENABLED; spin_unlock_irq(&connection->lock); - goto out_unlock; + return 0; } ret = gb_connection_hd_cport_enable(connection); if (ret) - goto err_unlock; + return ret; ret = gb_connection_svc_connection_create(connection); if (ret) goto err_hd_cport_disable; spin_lock_irq(&connection->lock); - connection->handler = handler; - if (handler) + if (connection->handler && rx) connection->state = GB_CONNECTION_STATE_ENABLED; else connection->state = GB_CONNECTION_STATE_ENABLED_TX; @@ -466,28 +474,60 @@ int gb_connection_enable(struct gb_connection *connection, if (ret) goto err_svc_destroy; -out_unlock: - mutex_unlock(&connection->mutex); - return 0; err_svc_destroy: spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; gb_connection_cancel_operations(connection, -ESHUTDOWN); - connection->handler = NULL; spin_unlock_irq(&connection->lock); gb_connection_svc_connection_destroy(connection); err_hd_cport_disable: gb_connection_hd_cport_disable(connection); -err_unlock: + + return ret; +} + +int gb_connection_enable(struct gb_connection *connection) +{ + int ret = 0; + + mutex_lock(&connection->mutex); + + if (connection->state == GB_CONNECTION_STATE_ENABLED) + goto out_unlock; + + ret = _gb_connection_enable(connection, true); +out_unlock: mutex_unlock(&connection->mutex); return ret; } EXPORT_SYMBOL_GPL(gb_connection_enable); +int gb_connection_enable_tx(struct gb_connection *connection) +{ + int ret = 0; + + mutex_lock(&connection->mutex); + + if (connection->state == GB_CONNECTION_STATE_ENABLED) { + ret = -EINVAL; + goto out_unlock; + } + + if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) + goto out_unlock; + + ret = _gb_connection_enable(connection, false); +out_unlock: + mutex_unlock(&connection->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(gb_connection_enable_tx); + void gb_connection_disable_rx(struct gb_connection *connection) { mutex_lock(&connection->mutex); @@ -499,7 +539,6 @@ void gb_connection_disable_rx(struct gb_connection *connection) } connection->state = GB_CONNECTION_STATE_ENABLED_TX; gb_connection_flush_incoming_operations(connection, -ESHUTDOWN); - connection->handler = NULL; spin_unlock_irq(&connection->lock); out_unlock: @@ -518,7 +557,6 @@ void gb_connection_disable(struct gb_connection *connection) spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; gb_connection_cancel_operations(connection, -ESHUTDOWN); - connection->handler = NULL; spin_unlock_irq(&connection->lock); gb_connection_svc_connection_destroy(connection); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 8e5284abb377..24b7d6f47671 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -55,10 +55,10 @@ struct gb_connection { }; struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, - u16 hd_cport_id); + u16 hd_cport_id, gb_request_handler_t handler); struct gb_connection *gb_connection_create_control(struct gb_interface *intf); struct gb_connection *gb_connection_create(struct gb_bundle *bundle, - u16 cport_id); + u16 cport_id, gb_request_handler_t handler); void gb_connection_destroy(struct gb_connection *connection); static inline bool gb_connection_is_static(struct gb_connection *connection) @@ -66,12 +66,8 @@ static inline bool gb_connection_is_static(struct gb_connection *connection) return !connection->intf; } -int gb_connection_enable(struct gb_connection *connection, - gb_request_handler_t handler); -static inline int gb_connection_enable_tx(struct gb_connection *connection) -{ - return gb_connection_enable(connection, NULL); -} +int gb_connection_enable(struct gb_connection *connection); +int gb_connection_enable_tx(struct gb_connection *connection); void gb_connection_disable_rx(struct gb_connection *connection); void gb_connection_disable(struct gb_connection *connection); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 4f3476c45d3a..18f1a2b043a3 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -49,18 +49,12 @@ static int legacy_request_handler(struct gb_operation *operation) static int legacy_connection_init(struct legacy_connection *lc) { struct gb_connection *connection = lc->connection; - gb_request_handler_t handler; int ret; dev_dbg(&connection->bundle->dev, "%s - %s\n", __func__, connection->name); - if (connection->protocol->request_recv) - handler = legacy_request_handler; - else - handler = NULL; - - ret = gb_connection_enable(connection, handler); + ret = gb_connection_enable(connection); if (ret) return ret; @@ -102,6 +96,7 @@ static int legacy_connection_create(struct legacy_connection *lc, { struct gb_connection *connection; struct gb_protocol *protocol; + gb_request_handler_t handler; u8 major, minor; int ret; @@ -122,7 +117,13 @@ static int legacy_connection_create(struct legacy_connection *lc, return -EPROTONOSUPPORT; } - connection = gb_connection_create(bundle, le16_to_cpu(desc->id)); + if (protocol->request_recv) + handler = legacy_request_handler; + else + handler = NULL; + + connection = gb_connection_create(bundle, le16_to_cpu(desc->id), + handler); if (IS_ERR(connection)) { ret = PTR_ERR(connection); goto err_protocol_put; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 845f82da0c1d..feadb624cfd4 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -895,7 +895,8 @@ struct gb_svc *gb_svc_create(struct gb_host_device *hd) goto err_put_device; } - svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID); + svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, + gb_svc_request_handler); if (IS_ERR(svc->connection)) { dev_err(&svc->dev, "failed to create connection: %ld\n", PTR_ERR(svc->connection)); @@ -922,7 +923,7 @@ int gb_svc_add(struct gb_svc *svc) * is added from the connection request handler when enough * information has been received. */ - ret = gb_connection_enable(svc->connection, gb_svc_request_handler); + ret = gb_connection_enable(svc->connection); if (ret) return ret; -- cgit v1.2.3-59-g8ed1b From 5dd8cc5370dbf68f17ed9443be6bcc54afbef204 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:22 +0100 Subject: greybus: hid: clean up init error paths Separate success and error paths more clearly. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index f45b444716ba..51657b06ea73 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -431,7 +431,7 @@ static int gb_hid_connection_init(struct gb_connection *connection) hid = hid_allocate_device(); if (IS_ERR(hid)) { ret = PTR_ERR(hid); - goto free_ghid; + goto err_free_ghid; } connection->private = ghid; @@ -440,17 +440,19 @@ static int gb_hid_connection_init(struct gb_connection *connection) ret = gb_hid_init(ghid); if (ret) - goto destroy_hid; + goto err_destroy_hid; ret = hid_add_device(hid); - if (!ret) - return 0; + if (ret) { + hid_err(hid, "can't add hid device: %d\n", ret); + goto err_destroy_hid; + } - hid_err(hid, "can't add hid device: %d\n", ret); + return 0; -destroy_hid: +err_destroy_hid: hid_destroy_device(hid); -free_ghid: +err_free_ghid: kfree(ghid); return ret; -- cgit v1.2.3-59-g8ed1b From 4324282d90cb02e041753240780c516f9cfe5b10 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:23 +0100 Subject: greybus: hid: convert to bundle driver Convert the legacy HID protocol driver to a bundle driver. This also fixes a potential crash should a (malicious) module have sent an early request before the private data had been initialised. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 70 +++++++++++++++++++++++++++++----------- drivers/staging/greybus/legacy.c | 1 - 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 51657b06ea73..4db337cf7acc 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -97,13 +97,13 @@ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, return ret; } -static int gb_hid_irq_handler(u8 type, struct gb_operation *op) +static int gb_hid_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_hid *ghid = connection->private; struct gb_hid_input_report_request *request = op->request->payload; - if (type != GB_HID_TYPE_IRQ_EVENT) { + if (op->type != GB_HID_TYPE_IRQ_EVENT) { dev_err(&connection->bundle->dev, "unsupported unsolicited request\n"); return -EINVAL; @@ -418,64 +418,96 @@ static int gb_hid_init(struct gb_hid *ghid) return 0; } -static int gb_hid_connection_init(struct gb_connection *connection) +static int gb_hid_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) { + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; struct hid_device *hid; struct gb_hid *ghid; int ret; + if (bundle->num_cports != 1) + return -ENODEV; + + cport_desc = &bundle->cport_desc[0]; + if (cport_desc->protocol_id != GREYBUS_PROTOCOL_HID) + return -ENODEV; + ghid = kzalloc(sizeof(*ghid), GFP_KERNEL); if (!ghid) return -ENOMEM; - hid = hid_allocate_device(); - if (IS_ERR(hid)) { - ret = PTR_ERR(hid); + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + gb_hid_request_handler); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); goto err_free_ghid; } connection->private = ghid; ghid->connection = connection; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_connection_destroy; + } + ghid->hid = hid; - ret = gb_hid_init(ghid); + greybus_set_drvdata(bundle, ghid); + + ret = gb_connection_enable(connection); if (ret) goto err_destroy_hid; + ret = gb_hid_init(ghid); + if (ret) + goto err_connection_disable; + ret = hid_add_device(hid); if (ret) { hid_err(hid, "can't add hid device: %d\n", ret); - goto err_destroy_hid; + goto err_connection_disable; } return 0; +err_connection_disable: + gb_connection_disable(connection); err_destroy_hid: hid_destroy_device(hid); +err_connection_destroy: + gb_connection_destroy(connection); err_free_ghid: kfree(ghid); return ret; } -static void gb_hid_connection_exit(struct gb_connection *connection) +static void gb_hid_disconnect(struct gb_bundle *bundle) { - struct gb_hid *ghid = connection->private; + struct gb_hid *ghid = greybus_get_drvdata(bundle); + gb_connection_disable(ghid->connection); hid_destroy_device(ghid->hid); + gb_connection_destroy(ghid->connection); kfree(ghid); } -static struct gb_protocol hid_protocol = { - .name = "hid", - .id = GREYBUS_PROTOCOL_HID, - .major = GB_HID_VERSION_MAJOR, - .minor = GB_HID_VERSION_MINOR, - .connection_init = gb_hid_connection_init, - .connection_exit = gb_hid_connection_exit, - .request_recv = gb_hid_irq_handler, +static const struct greybus_bundle_id gb_hid_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_HID) }, + { } }; +MODULE_DEVICE_TABLE(greybus, gb_hid_id_table); -gb_protocol_driver(&hid_protocol); +static struct greybus_driver gb_hid_driver = { + .name = "hid", + .probe = gb_hid_probe, + .disconnect = gb_hid_disconnect, + .id_table = gb_hid_id_table, +}; +module_greybus_driver(gb_hid_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 18f1a2b043a3..5cfbd23c1b8d 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -236,7 +236,6 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_HID) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) }, -- cgit v1.2.3-59-g8ed1b From e0deb079edc9bd403acdb1ecada0879786cc0fe3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:24 +0100 Subject: greybus: vibrator: convert to bundle driver Convert the legacy vibrator protocol driver to a bundle driver. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/vibrator.c | 75 +++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 5cfbd23c1b8d..4a45a943470d 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -245,7 +245,6 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SENSOR) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_MGMT) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_DATA) }, diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 04737c80496d..8c0df99b9f8d 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -88,19 +88,42 @@ static struct class vibrator_class = { static DEFINE_IDA(minors); -static int gb_vibrator_connection_init(struct gb_connection *connection) +static int gb_vibrator_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) { + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; struct gb_vibrator_device *vib; struct device *dev; int retval; + if (bundle->num_cports != 1) + return -ENODEV; + + cport_desc = &bundle->cport_desc[0]; + if (cport_desc->protocol_id != GREYBUS_PROTOCOL_VIBRATOR) + return -ENODEV; + vib = kzalloc(sizeof(*vib), GFP_KERNEL); if (!vib) return -ENOMEM; - vib->connection = connection; + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + NULL); + if (IS_ERR(connection)) { + retval = PTR_ERR(connection); + goto err_free_vib; + } connection->private = vib; + vib->connection = connection; + + greybus_set_drvdata(bundle, vib); + + retval = gb_connection_enable(connection); + if (retval) + goto err_connection_destroy; + /* * For now we create a device in sysfs for the vibrator, but odds are * there is a "real" device somewhere in the kernel for this, but I @@ -109,9 +132,9 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); if (vib->minor < 0) { retval = vib->minor; - goto error; + goto err_connection_disable; } - dev = device_create(&vibrator_class, &connection->bundle->dev, + dev = device_create(&vibrator_class, &bundle->dev, MKDEV(0, 0), vib, "vibrator%d", vib->minor); if (IS_ERR(dev)) { retval = -EINVAL; @@ -136,34 +159,44 @@ static int gb_vibrator_connection_init(struct gb_connection *connection) err_ida_remove: ida_simple_remove(&minors, vib->minor); -error: +err_connection_disable: + gb_connection_disable(connection); +err_connection_destroy: + gb_connection_destroy(connection); +err_free_vib: kfree(vib); + return retval; } -static void gb_vibrator_connection_exit(struct gb_connection *connection) +static void gb_vibrator_disconnect(struct gb_bundle *bundle) { - struct gb_vibrator_device *vib = connection->private; + struct gb_vibrator_device *vib = greybus_get_drvdata(bundle); #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); #endif device_unregister(vib->dev); ida_simple_remove(&minors, vib->minor); + gb_connection_disable(vib->connection); + gb_connection_destroy(vib->connection); kfree(vib); } -static struct gb_protocol vibrator_protocol = { - .name = "vibrator", - .id = GREYBUS_PROTOCOL_VIBRATOR, - .major = GB_VIBRATOR_VERSION_MAJOR, - .minor = GB_VIBRATOR_VERSION_MINOR, - .connection_init = gb_vibrator_connection_init, - .connection_exit = gb_vibrator_connection_exit, - .request_recv = NULL, /* no incoming requests */ +static const struct greybus_bundle_id gb_vibrator_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) }, + { } +}; +MODULE_DEVICE_TABLE(greybus, gb_vibrator_id_table); + +static struct greybus_driver gb_vibrator_driver = { + .name = "vibrator", + .probe = gb_vibrator_probe, + .disconnect = gb_vibrator_disconnect, + .id_table = gb_vibrator_id_table, }; -static __init int protocol_init(void) +static __init int gb_vibrator_init(void) { int retval; @@ -171,7 +204,7 @@ static __init int protocol_init(void) if (retval) return retval; - retval = gb_protocol_register(&vibrator_protocol); + retval = greybus_register(&gb_vibrator_driver); if (retval) goto err_class_unregister; @@ -182,14 +215,14 @@ err_class_unregister: return retval; } -module_init(protocol_init); +module_init(gb_vibrator_init); -static __exit void protocol_exit(void) +static __exit void gb_vibrator_exit(void) { - gb_protocol_deregister(&vibrator_protocol); + greybus_deregister(&gb_vibrator_driver); class_unregister(&vibrator_class); ida_destroy(&minors); } -module_exit(protocol_exit); +module_exit(gb_vibrator_exit); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 8eff5109616f4fb1784aa311b7544ef29b8deae9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Jan 2016 17:34:26 +0100 Subject: greybus: firmware: abort if AP_READY fails Abort if the AP_READY request fails. Also update the comment suggesting that this operation was a temporary one. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 6d8fd0ecbc1f..ee239a913e0c 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -251,25 +251,23 @@ static int gb_firmware_connection_init(struct gb_connection *connection) firmware_es2_fixup_vid_pid(firmware); - /* - * Module's Bootrom needs a way to know (currently), when to start - * sending requests to the AP. The version request is sent before this - * routine is called, and if the module sends the request right after - * receiving version request, the connection->private field will be - * NULL. - * - * Fix this TEMPORARILY by sending an AP_READY request. - */ + /* Tell bootrom we're ready. */ ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_AP_READY, NULL, 0, NULL, 0); if (ret) { dev_err(&connection->bundle->dev, "failed to send AP READY: %d\n", ret); + goto err_free_firmware; } dev_dbg(&connection->bundle->dev, "%s: AP_READY sent\n", __func__); return 0; + +err_free_firmware: + kfree(firmware); + + return ret; } static void gb_firmware_connection_exit(struct gb_connection *connection) -- cgit v1.2.3-59-g8ed1b From a547deb5105b583138e74e882b1e2aae1f76dc9c Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Thu, 21 Jan 2016 22:33:13 +0530 Subject: greybus: audio_codec: update codec_name as per driver->name Originally, driver->name was not poluated from GB and thus manually set from audio_codec driver as a hack. This is no more required. Another patch already removes that hack. Now, with new driver->name as "legacy." codec is registered with different name. So, during DAI link registration as well it needs modification. Signed-off-by: Vaibhav Agarwal Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 5c02b6534ab2..1f39c9cd1aee 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -541,7 +541,8 @@ static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev, gbcodec->dev_id = dev_id; dev_set_drvdata(dev, gbcodec); gbcodec->dev = dev; - strlcpy(gbcodec->name, dev_name(dev), NAME_SIZE); + snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name, + dev_name(dev)); mutex_lock(&gb_codec_list_lock); list_add(&gbcodec->list, &gb_codec_list); -- cgit v1.2.3-59-g8ed1b From fd7c28ebf03d7c5c293ac07fd178640582c8c137 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 22 Jan 2016 14:42:34 -0800 Subject: greybus: lsgb: add lsgb shell script Horrid hack, but a start at making it easier for people to see what is currently connected without having to deal with poking around in sysfs directly. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/lsgb | 149 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100755 drivers/staging/greybus/lsgb diff --git a/drivers/staging/greybus/lsgb b/drivers/staging/greybus/lsgb new file mode 100755 index 000000000000..4fb30d26e940 --- /dev/null +++ b/drivers/staging/greybus/lsgb @@ -0,0 +1,149 @@ +#!/bin/sh +# +# 'ls greybus' +# +# Copyright 2016 Google 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, +# 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +############################################################################### + +# Horrid hack to just make it easier than poking around directly in sysfs +# Don't rely on any of these fields to be stable, the layout is going to +# change a lot over time. +# + +print_interface() { + local devname=$1 + local iid=`cat interface_id` + local vid=`cat vendor_id` + local pid=`cat product_id` + local version=`cat version` + local serial=`cat serial_number` + local vs=`cat vendor_string` + local ps=`cat product_string` + printf "Interface: %s %s:%s ver:%s \"%s\" \"%s\"\n"\ + ${iid} ${vid} ${pid} ${version} "${vs}" "${ps}" +} + +print_bundle() { + local devname=$1 + local id=`cat bundle_id` + local class=`cat bundle_class` + local class_type="unknown" + case ${class} in + "0x01" ) class_type="SVC" + ;; + "0x02" ) class_type="GPIO" + ;; + "0x03" ) class_type="I2C" + ;; + "0x04" ) class_type="UART" + ;; + "0x05" ) class_type="HID" + ;; + "0x06" ) class_type="USB" + ;; + "0x07" ) class_type="SDIO" + ;; + "0x08" ) class_type="Power Supply" + ;; + "0x09" ) class_type="PWM" + ;; + "0x0a" ) class_type="unknown" + ;; + "0x0b" ) class_type="SPI" + ;; + "0x0c" ) class_type="Display" + ;; + "0x0d" ) class_type="Camera" + ;; + "0x0e" ) class_type="Sensoe" + ;; + "0x0f" ) class_type="Lights" + ;; + "0x10" ) class_type="Vibrator" + ;; + "0x11" ) class_type="Loopback" + ;; + "0x12" ) class_type="Audio Management" + ;; + "0x13" ) class_type="Audio Data" + ;; + "0x14" ) class_type="SVC" + ;; + "0x15" ) class_type="Firmware" + ;; + "0xfe" ) class_type="Raw" + ;; + "0xff" ) class_type="Vendor" + ;; + esac + printf " Bundle: %s %s (%s)\n" ${id} ${class_type} ${class} +} + +print_svc() { + local devname=$1 + printf " SVC: ${devname}\n" +} + +print_host_device() { + local devname=$1 + printf " Host: ${devname}\n" +} + +print_unknown() { + local devname=$1 + + printf "Unknown device type: ${devname}\n" +} + + +print_device() { + local dev=$1 + + [ -d $dev ] || return + cd $dev + + local devname=`basename ${dev}` + local devtype=`cat uevent | grep DEVTYPE | cut -f 2 -d '='` + case ${devtype} in + greybus_interface ) print_interface ${devname} + ;; + greybus_bundle ) print_bundle ${devname} + ;; + greybus_svc ) print_svc ${devname} + ;; + greybus_host_device ) print_host_device ${devname} + ;; + * ) print_unknown ${devname} + ;; + esac +} + +for device in /sys/bus/greybus/devices/* +do + print_device $device +done -- cgit v1.2.3-59-g8ed1b From f3e6c0971af62c81d0a9f0b408ae0e97248e2976 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 22 Jan 2016 16:16:08 +0530 Subject: greybus: svc: Expose and retain VID/PID received from bootrom for ES2 ES2 chips doesn't have efuses for storing module's vendor_id and product_id and so we have hacked bootrom earlier using firmware protocol, so that VID/PID can be received for fetching firmware packages. Another requirement is to expose them to sysfs, so that modules can be identified properly. That can be easily solved by updating interface's VID/PID, when fetched using firmware protocol and later reusing them while the module switches its identity from bootrom to firmware. Do that only if the module is ES2 and the VID/PID sent during hotplug are both 0. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 22 ++++++++++++---------- drivers/staging/greybus/svc.c | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index ee239a913e0c..37895107e427 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -14,8 +14,6 @@ struct gb_firmware { struct gb_connection *connection; const struct firmware *fw; - u32 vendor_id; - u32 product_id; }; static void free_firmware(struct gb_firmware *firmware) @@ -31,7 +29,7 @@ static void free_firmware(struct gb_firmware *firmware) * This fetches VID/PID (over firmware protocol) for es2 chip only, when VID/PID * already sent during hotplug are 0. * - * Otherwise, we keep firmware->vendor_id/product_id same as what's passed + * Otherwise, we keep intf->vendor_id/product_id same as what's passed * during hotplug. */ static void firmware_es2_fixup_vid_pid(struct gb_firmware *firmware) @@ -59,11 +57,18 @@ static void firmware_es2_fixup_vid_pid(struct gb_firmware *firmware) return; } - firmware->vendor_id = le32_to_cpu(response.vendor_id); - firmware->product_id = le32_to_cpu(response.product_id); + /* + * NOTE: This is hacked, so that the same values of VID/PID can be used + * by next firmware level as well. The uevent for bootrom will still + * have VID/PID as 0, though after this point the sysfs files will start + * showing the updated values. But yeah, that's a bit racy as the same + * sysfs files would be showing 0 before this point. + */ + intf->vendor_id = le32_to_cpu(response.vendor_id); + intf->product_id = le32_to_cpu(response.product_id); dev_dbg(&connection->bundle->dev, "Firmware got vid (0x%x)/pid (0x%x)\n", - firmware->vendor_id, firmware->product_id); + intf->vendor_id, intf->product_id); } /* This returns path of the firmware blob on the disk */ @@ -86,7 +91,7 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) snprintf(firmware_name, sizeof(firmware_name), "ara_%08x_%08x_%08x_%08x_%02x.tftf", intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, - firmware->vendor_id, firmware->product_id, stage); + intf->vendor_id, intf->product_id, stage); // FIXME: // Turn to dev_dbg later after everyone has valid bootloaders with good @@ -246,9 +251,6 @@ static int gb_firmware_connection_init(struct gb_connection *connection) firmware->connection = connection; connection->private = firmware; - firmware->vendor_id = connection->intf->vendor_id; - firmware->product_id = connection->intf->product_id; - firmware_es2_fixup_vid_pid(firmware); /* Tell bootrom we're ready. */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index feadb624cfd4..85eb7eaae737 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -464,6 +464,8 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) struct gb_host_device *hd = connection->hd; struct gb_interface *intf; u8 intf_id, device_id; + u32 vendor_id = 0; + u32 product_id = 0; int ret; /* The request message size has already been verified. */ @@ -474,6 +476,14 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) intf = gb_interface_find(hd, intf_id); if (intf) { + /* + * For ES2, we need to maintain the same vendor/product ids we + * got from bootrom, otherwise userspace can't distinguish + * between modules. + */ + vendor_id = intf->vendor_id; + product_id = intf->product_id; + /* * We have received a hotplug request for an interface that * already exists. @@ -506,6 +516,20 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) intf->product_id = le32_to_cpu(request->data.ara_prod_id); intf->serial_number = le64_to_cpu(request->data.serial_number); + /* + * Use VID/PID specified at hotplug if: + * - Bridge ASIC chip isn't ES2 + * - Received non-zero Vendor/Product ids + * + * Otherwise, use the ids we received from bootrom. + */ + if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && + intf->ddbl1_product_id == ES2_DDBL1_PROD_ID && + intf->vendor_id == 0 && intf->product_id == 0) { + intf->vendor_id = vendor_id; + intf->product_id = product_id; + } + ret = gb_svc_read_and_clear_module_boot_status(intf); if (ret) { dev_err(&svc->dev, "failed to clear boot status of interface %u: %d\n", -- cgit v1.2.3-59-g8ed1b From 7c154711a6705bbc3036d8f64944e8882fe3fd84 Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos Date: Fri, 22 Jan 2016 17:59:44 +0200 Subject: greybus: camera: add semiplanar and planar formats This change adds missing planar and semiplanar formats from gb specification. Mbus to Gb format map: V4L2_MBUS_FMT_NV12_1x8 -> 0x12 V4L2_MBUS_FMT_NV21_1x8 -> 0x13 V4L2_MBUS_FMT_YV12_1x8 -> 0x16 V4L2_MBUS_FMT_YU12_1x8 -> 0x17 Change depends on: "media: add new mediabus format enums for ara camera" Signed-off-by: Gjorgji Rosikopulos Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 6f14848fa854..bcef3920d002 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -70,11 +70,19 @@ static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { .gb_format = 0x01, }, { - .mbus_code = V4L2_MBUS_FMT_YUYV8_1_5X8, + .mbus_code = V4L2_MBUS_FMT_NV12_1x8, + .gb_format = 0x12, + }, + { + .mbus_code = V4L2_MBUS_FMT_NV21_1x8, + .gb_format = 0x13, + }, + { + .mbus_code = V4L2_MBUS_FMT_YU12_1x8, .gb_format = 0x16, }, { - .mbus_code = V4L2_MBUS_FMT_YVYU8_1_5X8, + .mbus_code = V4L2_MBUS_FMT_YV12_1x8, .gb_format = 0x17, }, { -- cgit v1.2.3-59-g8ed1b From 0f65fb1ea268a7268ab97a92d172d26d960a5d92 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 22 Jan 2016 18:26:23 -0800 Subject: greybus: lsgb: Minor tweaks Make it executable on an Android system Change layout to be show heirachy a bit better. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/lsgb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/lsgb b/drivers/staging/greybus/lsgb index 4fb30d26e940..2d84169a7b83 100755 --- a/drivers/staging/greybus/lsgb +++ b/drivers/staging/greybus/lsgb @@ -1,4 +1,4 @@ -#!/bin/sh +#!/system/bin/sh # # 'ls greybus' # @@ -44,8 +44,8 @@ print_interface() { local serial=`cat serial_number` local vs=`cat vendor_string` local ps=`cat product_string` - printf "Interface: %s %s:%s ver:%s \"%s\" \"%s\"\n"\ - ${iid} ${vid} ${pid} ${version} "${vs}" "${ps}" + printf " Intf %02d %s:%s %s %s v%s\n" \ + ${iid} ${vid} ${pid} "${vs}" "${ps}" ${version} } print_bundle() { @@ -101,17 +101,19 @@ print_bundle() { "0xff" ) class_type="Vendor" ;; esac - printf " Bundle: %s %s (%s)\n" ${id} ${class_type} ${class} + printf " Bundle %02d Class %s (%s)\n" ${id} ${class} ${class_type} } print_svc() { local devname=$1 - printf " SVC: ${devname}\n" + local bus=`cat uevent | grep BUS | cut -f 2 -d '='` + printf " SVC %02d\n" ${bus} } print_host_device() { local devname=$1 - printf " Host: ${devname}\n" + local bus=`cat uevent | grep BUS | cut -f 2 -d '='` + printf "Bus %02d\n" ${bus} } print_unknown() { -- cgit v1.2.3-59-g8ed1b From ed7279ae31b0a0a97e832bdd0eb6edb31ec2736f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 Jan 2016 22:51:49 -0800 Subject: greybus: svc: add a "watchdog" to check the network health Now that we have a svc ping command, let's add a watchdog to call it every so often (1 second at the moment.) If it finds something went wrong, post a stern message to the kernel log and call: start unipro_reset to reset the whole greybus hardware subsystem. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/svc.c | 8 +++ drivers/staging/greybus/svc.h | 5 ++ drivers/staging/greybus/svc_watchdog.c | 109 +++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 drivers/staging/greybus/svc_watchdog.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1e24509bdb4d..1e45416a2796 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -8,6 +8,7 @@ greybus-y := core.o \ protocol.o \ control.o \ svc.o \ + svc_watchdog.o \ firmware.o \ operation.o \ legacy.o diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 85eb7eaae737..fc5747031721 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -436,6 +436,13 @@ static int gb_svc_hello(struct gb_operation *op) return ret; } + ret = gb_svc_watchdog_create(svc); + if (ret) { + dev_err(&svc->dev, "failed to create watchdog: %d\n", ret); + input_unregister_device(svc->input); + device_del(&svc->dev); + } + return 0; } @@ -963,6 +970,7 @@ void gb_svc_del(struct gb_svc *svc) * from the request handler. */ if (device_is_registered(&svc->dev)) { + gb_svc_watchdog_destroy(svc); input_unregister_device(svc->input); device_del(&svc->dev); } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index f3e8479b9438..0f81e97a2e88 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -16,6 +16,8 @@ enum gb_svc_state { GB_SVC_STATE_SVC_HELLO, }; +struct gb_svc_watchdog; + struct gb_svc { struct device dev; @@ -33,6 +35,7 @@ struct gb_svc { struct input_dev *input; char *input_phys; + struct gb_svc_watchdog *watchdog; }; #define to_gb_svc(d) container_of(d, struct gb_svc, d) @@ -56,6 +59,8 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, u8 rx_mode, u8 rx_gear, u8 rx_nlanes, u8 flags, u32 quirks); int gb_svc_ping(struct gb_svc *svc); +int gb_svc_watchdog_create(struct gb_svc *svc); +void gb_svc_watchdog_destroy(struct gb_svc *svc); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); diff --git a/drivers/staging/greybus/svc_watchdog.c b/drivers/staging/greybus/svc_watchdog.c new file mode 100644 index 000000000000..edb73efd53f7 --- /dev/null +++ b/drivers/staging/greybus/svc_watchdog.c @@ -0,0 +1,109 @@ +/* + * SVC Greybus "watchdog" driver. + * + * Copyright 2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include "greybus.h" + +#define SVC_WATCHDOG_PERIOD (2*HZ) + +struct gb_svc_watchdog { + struct delayed_work work; + struct gb_svc *svc; + bool finished; +}; + +static struct delayed_work reset_work; + +static void greybus_reset(struct work_struct *work) +{ + static char start_path[256] = "/system/bin/start"; + static char *envp[] = { + "HOME=/", + "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin", + NULL, + }; + static char *argv[] = { + start_path, + "unipro_reset", + NULL, + }; + + printk(KERN_ERR "svc_watchdog: calling \"%s %s\" to reset greybus network!\n", + argv[0], argv[1]); + call_usermodehelper(start_path, argv, envp, UMH_WAIT_EXEC); +} + +static void do_work(struct work_struct *work) +{ + struct gb_svc_watchdog *watchdog; + struct gb_svc *svc; + int retval; + + watchdog = container_of(work, struct gb_svc_watchdog, work.work); + svc = watchdog->svc; + + dev_dbg(&svc->dev, "%s: ping.\n", __func__); + retval = gb_svc_ping(svc); + if (retval) { + /* + * Something went really wrong, let's warn userspace and then + * pull the plug and reset the whole greybus network. + * We need to do this outside of this workqueue as we will be + * tearing down the svc device itself. So queue up + * yet-another-callback to do that. + */ + dev_err(&svc->dev, + "SVC ping has returned %d, something is wrong!!!\n", + retval); + dev_err(&svc->dev, "Resetting the greybus network, watch out!!!\n"); + + INIT_DELAYED_WORK(&reset_work, greybus_reset); + queue_delayed_work(system_wq, &reset_work, HZ/2); + return; + } + + /* resubmit our work to happen again, if we are still "alive" */ + if (!watchdog->finished) + queue_delayed_work(system_wq, &watchdog->work, + SVC_WATCHDOG_PERIOD); +} + +int gb_svc_watchdog_create(struct gb_svc *svc) +{ + struct gb_svc_watchdog *watchdog; + + if (svc->watchdog) + return 0; + + watchdog = kmalloc(sizeof(*watchdog), GFP_KERNEL); + if (!watchdog) + return -ENOMEM; + + watchdog->finished = false; + watchdog->svc = svc; + INIT_DELAYED_WORK(&watchdog->work, do_work); + svc->watchdog = watchdog; + + queue_delayed_work(system_wq, &watchdog->work, + SVC_WATCHDOG_PERIOD); + return 0; +} + +void gb_svc_watchdog_destroy(struct gb_svc *svc) +{ + struct gb_svc_watchdog *watchdog = svc->watchdog; + + if (!watchdog) + return; + + watchdog->finished = true; + cancel_delayed_work_sync(&watchdog->work); + svc->watchdog = NULL; + kfree(watchdog); +} -- cgit v1.2.3-59-g8ed1b From 539d6e11d266bceb597bc416060f9f2888bbd89f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 23 Jan 2016 17:36:00 -0800 Subject: greybus: svc: return proper error code on watchdog create error When the watchdog is not created properly, we need to propagate the error for this and not just return success. Reported-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index fc5747031721..9df3c7a4bb82 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -441,6 +441,7 @@ static int gb_svc_hello(struct gb_operation *op) dev_err(&svc->dev, "failed to create watchdog: %d\n", ret); input_unregister_device(svc->input); device_del(&svc->dev); + return ret; } return 0; -- cgit v1.2.3-59-g8ed1b From 4fbf69c71cdd3248745fd842481e946d1685f13c Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Sat, 23 Jan 2016 19:44:46 -0800 Subject: greybus: camera: Stream config change unipro speed Unipro network speed was increased at camera initialization time and never slowed down. This unnecessary drains power during the entire time camera module is plugged in. Increasing/decreasing unipro link speed before issuing stream configuration request to camera module prevents this from happening. Signed-off-by: Jacopo Mondi Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 110 +++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index bcef3920d002..c7359088e259 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -120,9 +120,12 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, unsigned int *flags, struct gb_camera_stream_config *streams) { + struct gb_interface *intf = gcam->connection->intf; + struct gb_svc *svc = gcam->connection->hd->svc; struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; struct ap_csi_config_request csi_cfg; + unsigned int nstreams = *num_streams; unsigned int i; size_t req_size; @@ -142,6 +145,57 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto done; } + /* + * Setup unipro link speed before actually issuing configuration + * to the camera module, to assure unipro network speed is set + * before CSI interfaces gets configured + */ + if (nstreams) { + ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_PWRM_RXTERMINATION | + GB_SVC_PWRM_TXTERMINATION, 0); + if (ret < 0) + goto done; + + ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_PWRM_RXTERMINATION | + GB_SVC_PWRM_TXTERMINATION, 0); + if (ret < 0) + goto done; + } else { + ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + 0, 0); + if (ret < 0) { + gcam_err(gcam, "can't take camera link to PWM-G1 auto: %d\n", + ret); + goto done; + } + + ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + 0, 0); + if (ret < 0) { + gcam_err(gcam, "can't take AP link to PWM-G1 auto: %d\n", + ret); + goto done; + } + } + req->num_streams = nstreams; req->flags = *flags; req->padding = 0; @@ -159,19 +213,19 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, GB_CAMERA_TYPE_CONFIGURE_STREAMS, req, req_size, resp, resp_size); if (ret < 0) - goto done; + goto set_unipro_slow_mode; if (resp->num_streams > nstreams) { gcam_dbg(gcam, "got #streams %u > request %u\n", resp->num_streams, nstreams); ret = -EIO; - goto done; + goto set_unipro_slow_mode; } if (resp->padding != 0) { gcam_dbg(gcam, "response padding != 0"); ret = -EIO; - goto done; + goto set_unipro_slow_mode; } *flags = resp->flags; @@ -190,7 +244,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) { gcam_dbg(gcam, "stream #%u padding != 0", i); ret = -EIO; - goto done; + goto set_unipro_slow_mode; } } @@ -219,6 +273,36 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, *num_streams = resp->num_streams; ret = 0; + kfree(req); + kfree(resp); + return ret; + +set_unipro_slow_mode: + ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + 0, 0); + if (ret < 0) { + gcam_err(gcam, "can't take camera link to PWM-G1 auto: %d\n", + ret); + goto done; + } + + gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + 0, 0); + if (ret < 0) + gcam_err(gcam, "can't take AP link to PWM-G1 auto: %d\n", + ret); + + done: kfree(req); kfree(resp); @@ -767,24 +851,6 @@ static int gb_camera_connection_init(struct gb_connection *connection) gcam->data_connected = true; - ret = gb_svc_intf_set_power_mode(svc, connection->intf->interface_id, - GB_SVC_UNIPRO_HS_SERIES_A, - GB_SVC_UNIPRO_FAST_MODE, 2, 2, - GB_SVC_UNIPRO_FAST_MODE, 2, 2, - GB_SVC_PWRM_RXTERMINATION | - GB_SVC_PWRM_TXTERMINATION, 0); - if (ret < 0) - goto error; - - ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, - GB_SVC_UNIPRO_HS_SERIES_A, - GB_SVC_UNIPRO_FAST_MODE, 2, 2, - GB_SVC_UNIPRO_FAST_MODE, 2, 2, - GB_SVC_PWRM_RXTERMINATION | - GB_SVC_PWRM_TXTERMINATION, 0); - if (ret < 0) - goto error; - ret = gb_camera_debugfs_init(gcam); if (ret < 0) goto error; -- cgit v1.2.3-59-g8ed1b From d8b16338df9bcef1fa0a047bd3e8d2e922dc5bb8 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Tue, 26 Jan 2016 07:23:29 +0530 Subject: greybus: arche-platform: Disable clock as part of driver remove As part of driver remove (cleanup) function, disable the clock for both SVC, APB1 & APB2. Testing Done: Tested on EVT1 platform with Connect=>disconnect=>connect iteration, almost close to 100 iterations have passed (demo branch). And also tested with kernel-only build. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 4 ++++ drivers/staging/greybus/arche-platform.c | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index d073543963a5..f2bad8df0cf7 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -220,6 +220,10 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, static void apb_ctrl_cleanup(struct arche_apb_ctrl_drvdata *apb) { + /* disable the clock */ + if (gpio_is_valid(apb->clk_en_gpio)) + gpio_set_value(apb->clk_en_gpio, 0); + if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0) regulator_disable(apb->vcore); diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index d12fa0e16a4b..b5596ec4409c 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -104,6 +104,7 @@ static void unexport_gpios(struct arche_platform_drvdata *arche_pdata) static void arche_platform_cleanup(struct arche_platform_drvdata *arche_pdata) { + clk_disable_unprepare(arche_pdata->svc_ref_clk); /* As part of exit, put APB back in reset state */ svc_reset_onoff(arche_pdata->svc_reset_gpio, arche_pdata->is_reset_act_hi); -- cgit v1.2.3-59-g8ed1b From 839ac5b989fbfcc260d23ea4be9e3c8840e1c4e1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 26 Jan 2016 08:57:50 -0800 Subject: greybus: svc: double the ping delay timeout Sometimes the ping response comes back _right_ after we timed out, as the svc got its act together and squeaked out the ack, yet we miss it and reset the whole bus. Double the delay to hopefully give the svc a little more of a chance to fix itself. Odds are, it's still in trouble, but we can just hold off resetting it for a bit more... Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 9df3c7a4bb82..b9e5b8565010 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -365,8 +365,9 @@ EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); int gb_svc_ping(struct gb_svc *svc) { - return gb_operation_sync(svc->connection, GB_SVC_TYPE_PING, - NULL, 0, NULL, 0); + return gb_operation_sync_timeout(svc->connection, GB_SVC_TYPE_PING, + NULL, 0, NULL, 0, + GB_OPERATION_TIMEOUT_DEFAULT * 2); } EXPORT_SYMBOL_GPL(gb_svc_ping); -- cgit v1.2.3-59-g8ed1b From 7030f9246b55b7c186bd4bd8217162874fe29002 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 25 Jan 2016 20:13:15 -0800 Subject: greybus: camera: only build module against msm kernel The camera driver currently does not build against anything other than the msm kernel, due to cross-dependancies, so enable that here so that we can build against other kernels without failing the build. Signed-off-by: Greg Kroah-Hartman Tested-by: Marti Bolivar Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1e45416a2796..870a7bb22d9e 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -51,7 +51,9 @@ obj-m += gb-arche.o ifeq ($(CONFIG_SND_SOC_DYNAMIC_DAILINK),y) obj-m += gb-audio-codec.o endif -obj-m += gb-camera.o +ifeq ($(CONFIG_ARCH_MSM8994),y) + obj-m += gb-camera.o +endif obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o obj-m += gb-audio-manager.o -- cgit v1.2.3-59-g8ed1b From d562853d3ecc31806412635adcc590b33621b0c0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 26 Jan 2016 15:17:08 -0800 Subject: greybus: svc watchdog: allow it to be enabled/disabled from userspace Sometimes you want to disable the SVC watchdog without having to patch your kernel, so provide a sysfs file to do this. This can let us do power measurements, and let the firmware developers not go crazy worrying that the kernel is going to reset their chips with no notice. Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs-bus-greybus | 8 +++ drivers/staging/greybus/svc.c | 32 ++++++++++++ drivers/staging/greybus/svc.h | 3 ++ drivers/staging/greybus/svc_watchdog.c | 60 ++++++++++++++++++---- 4 files changed, 94 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 1550b7f8d776..4493605186b0 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -163,3 +163,11 @@ KernelVersion: 4.XX Contact: Greg Kroah-Hartman Description: The version number of the firmware in the SVC device. + +What: /sys/bus/greybus/device/N-svc/watchdog +Date: October 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman +Description: + If the SVC watchdog is enabled or not. Writing 0 to this + file will disable the watchdog, writing 1 will enable it. diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b9e5b8565010..eeb251e28441 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -70,10 +70,42 @@ static ssize_t intf_eject_store(struct device *dev, } static DEVICE_ATTR_WO(intf_eject); +static ssize_t watchdog_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_svc *svc = to_gb_svc(dev); + + return sprintf(buf, "%s\n", + gb_svc_watchdog_enabled(svc) ? "enabled" : "disabled"); +} + +static ssize_t watchdog_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct gb_svc *svc = to_gb_svc(dev); + int retval; + bool user_request; + + retval = strtobool(buf, &user_request); + if (retval) + return retval; + + if (user_request) + retval = gb_svc_watchdog_enable(svc); + else + retval = gb_svc_watchdog_disable(svc); + if (retval) + return retval; + return len; +} +static DEVICE_ATTR_RW(watchdog); + static struct attribute *svc_attrs[] = { &dev_attr_endo_id.attr, &dev_attr_ap_intf_id.attr, &dev_attr_intf_eject.attr, + &dev_attr_watchdog.attr, NULL, }; ATTRIBUTE_GROUPS(svc); diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 0f81e97a2e88..b9fb93ea56be 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -61,6 +61,9 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, int gb_svc_ping(struct gb_svc *svc); int gb_svc_watchdog_create(struct gb_svc *svc); void gb_svc_watchdog_destroy(struct gb_svc *svc); +bool gb_svc_watchdog_enabled(struct gb_svc *svc); +int gb_svc_watchdog_enable(struct gb_svc *svc); +int gb_svc_watchdog_disable(struct gb_svc *svc); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); diff --git a/drivers/staging/greybus/svc_watchdog.c b/drivers/staging/greybus/svc_watchdog.c index edb73efd53f7..9bd68f4f4cf7 100644 --- a/drivers/staging/greybus/svc_watchdog.c +++ b/drivers/staging/greybus/svc_watchdog.c @@ -15,7 +15,7 @@ struct gb_svc_watchdog { struct delayed_work work; struct gb_svc *svc; - bool finished; + bool enabled; }; static struct delayed_work reset_work; @@ -65,11 +65,16 @@ static void do_work(struct work_struct *work) INIT_DELAYED_WORK(&reset_work, greybus_reset); queue_delayed_work(system_wq, &reset_work, HZ/2); - return; + + /* + * Disable ourselves, we don't want to trip again unless + * userspace wants us to. + */ + watchdog->enabled = false; } /* resubmit our work to happen again, if we are still "alive" */ - if (!watchdog->finished) + if (watchdog->enabled) queue_delayed_work(system_wq, &watchdog->work, SVC_WATCHDOG_PERIOD); } @@ -85,14 +90,12 @@ int gb_svc_watchdog_create(struct gb_svc *svc) if (!watchdog) return -ENOMEM; - watchdog->finished = false; + watchdog->enabled = false; watchdog->svc = svc; INIT_DELAYED_WORK(&watchdog->work, do_work); svc->watchdog = watchdog; - queue_delayed_work(system_wq, &watchdog->work, - SVC_WATCHDOG_PERIOD); - return 0; + return gb_svc_watchdog_enable(svc); } void gb_svc_watchdog_destroy(struct gb_svc *svc) @@ -102,8 +105,47 @@ void gb_svc_watchdog_destroy(struct gb_svc *svc) if (!watchdog) return; - watchdog->finished = true; - cancel_delayed_work_sync(&watchdog->work); + gb_svc_watchdog_disable(svc); svc->watchdog = NULL; kfree(watchdog); } + +bool gb_svc_watchdog_enabled(struct gb_svc *svc) +{ + if (!svc || !svc->watchdog) + return false; + return svc->watchdog->enabled; +} + +int gb_svc_watchdog_enable(struct gb_svc *svc) +{ + struct gb_svc_watchdog *watchdog; + + if (!svc->watchdog) + return -ENODEV; + + watchdog = svc->watchdog; + if (watchdog->enabled) + return 0; + + watchdog->enabled = true; + queue_delayed_work(system_wq, &watchdog->work, + SVC_WATCHDOG_PERIOD); + return 0; +} + +int gb_svc_watchdog_disable(struct gb_svc *svc) +{ + struct gb_svc_watchdog *watchdog; + + if (!svc->watchdog) + return -ENODEV; + + watchdog = svc->watchdog; + if (!watchdog->enabled) + return 0; + + watchdog->enabled = false; + cancel_delayed_work_sync(&watchdog->work); + return 0; +} -- cgit v1.2.3-59-g8ed1b From 5bee760890aff6dfef8b50c5e4167e9cd003ee7b Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Tue, 26 Jan 2016 21:26:02 +0100 Subject: greybus: loopback_test: make output to csv file a parameter option Its useful to get a CSV output on stdout for test frameworks to read and parse the results. However, a csv file is not always needed. Add the -z option to create/append a csv file only when the user asks for it. Signed-off-by: Axel Haslam Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/tools/loopback_test.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index 27c733d1c82e..e8d35bd95cc7 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -92,6 +92,7 @@ struct loopback_test { int async_timeout; int async_outstanding_operations; int us_wait; + int file_output; char test_name[MAX_STR_LEN]; char sysfs_prefix[MAX_SYSFS_PATH]; char debugfs_prefix[MAX_SYSFS_PATH]; @@ -202,6 +203,7 @@ void usage(void) " -o Async Timeout - Timeout in uSec for async operations\n" " -c Max number of outstanding operations for async operations\n" " -w Wait in uSec between operations\n" + " -z Enable output to a CSV file (incompatible with -p)\n" "Examples:\n" " Send 10000 transfers with a packet size of 128 bytes to all active connections\n" " loopback_test -t transfer -s 128 -i 10000 -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n" @@ -527,7 +529,7 @@ static int log_results(struct loopback_test *t) * append to the same CSV with datestamp - representing each test * dataset. */ - if (!t->porcelain) { + if (t->file_output && !t->porcelain) { snprintf(file_name, sizeof(file_name), "%s_%d_%d.csv", t->test_name, t->size, t->iteration_max); @@ -545,7 +547,7 @@ static int log_results(struct loopback_test *t) len = format_output(t, &t->devices[i].results, t->devices[i].name, data, sizeof(data), &tm); - if (!t->porcelain) { + if (t->file_output && !t->porcelain) { ret = write(fd, data, len); if (ret == -1) fprintf(stderr, "unable to write %d bytes to csv.\n", len); @@ -557,14 +559,14 @@ static int log_results(struct loopback_test *t) if (t->aggregate_output) { len = format_output(t, &t->aggregate_results, "aggregate", data, sizeof(data), &tm); - if (!t->porcelain) { + if (t->file_output && !t->porcelain) { ret = write(fd, data, len); if (ret == -1) fprintf(stderr, "unable to write %d bytes to csv.\n", len); } } - if (!t->porcelain) + if (t->file_output && !t->porcelain) close(fd); return 0; @@ -923,6 +925,8 @@ int main(int argc, char *argv[]) case 'w': t.us_wait = atoi(optarg); break; + case 'z': + t.file_output = 1; default: usage(); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 6e304f5963ce2136cf8fda2acfb87cfe942d2e71 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 27 Jan 2016 13:17:52 +0100 Subject: greybus: legacy: fix a null pointer dereference When gb_protocol_get() fails in legacy_connection_create(), we end up bailing-out before assigning lc->connection and lc->protocol. Calling legacy_connection_destroy() in that case results in a null pointer dereference. Check if lc->connection is not null before freeing it. Signed-off-by: Bartosz Golaszewski Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 4a45a943470d..838c70fe74c0 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -148,6 +148,9 @@ err_protocol_put: static void legacy_connection_destroy(struct legacy_connection *lc) { + if (!lc->connection) + return; + lc->connection->protocol = NULL; gb_connection_destroy(lc->connection); -- cgit v1.2.3-59-g8ed1b From 2df6396160b019d992241e2db8a6d5a15f654e69 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 27 Jan 2016 11:16:57 +0530 Subject: greybus: audio_codec: Free gccodec on codec probe failure We aren't freeing the codec, that we allocated before failing to probe the connection. Free it. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 1f39c9cd1aee..a7ccaaad5281 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -713,6 +713,7 @@ topology_error: kfree(topology); base_error: gbcodec->mgmt_connection = NULL; + gbaudio_free_codec(dev, gbcodec); return ret; } -- cgit v1.2.3-59-g8ed1b From 4b27be1223b048322398e04fcebef7f85c0dac0d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 27 Jan 2016 11:16:58 +0530 Subject: greybus: audio_codec: Free gccodec on dia probe failure We aren't freeing the codec, that we allocated before failing to probe the connection. Free it. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index a7ccaaad5281..5fef49552813 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -789,6 +789,7 @@ static int gbaudio_dai_probe(struct gb_connection *connection) int dev_id = connection->intf->interface_id; struct gbaudio_codec_info *gbcodec = dev_get_drvdata(dev); struct gb_audio_manager_module_descriptor desc; + int ret; dev_dbg(dev, "Add DAI device:%d:%s\n", dev_id, dev_name(dev)); @@ -800,8 +801,10 @@ static int gbaudio_dai_probe(struct gb_connection *connection) /* add/update dai_list*/ dai = gbaudio_add_dai(gbcodec, connection->intf_cport_id, connection, NULL); - if (!dai) - return -ENOMEM; + if (!dai) { + ret = -ENOMEM; + goto err_free_codec; + } /* update dai_added count */ mutex_lock(&gbcodec->lock); @@ -825,6 +828,10 @@ static int gbaudio_dai_probe(struct gb_connection *connection) mutex_unlock(&gbcodec->lock); return 0; + +err_free_codec: + gbaudio_free_codec(dev, gbcodec); + return ret; } static void gbaudio_dai_remove(struct gb_connection *connection) -- cgit v1.2.3-59-g8ed1b From 35e28794dcddf2eab1d53b9f3bf5a0eeee82e3c9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 27 Jan 2016 16:57:48 +0530 Subject: greybus: audio_codec: convert to bundle driver Convert the legacy audio management and data protocol drivers to a bundle driver. The Audio bundle driver can support a single management and any number of data cports, and so we expect multiple data cports to be present for the bundle during initialization. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 245 +++++++++++++++++++++++-------- drivers/staging/greybus/audio_codec.h | 7 + drivers/staging/greybus/audio_topology.c | 18 ++- drivers/staging/greybus/legacy.c | 2 - 4 files changed, 202 insertions(+), 70 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 5fef49552813..b2feba8cee14 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -15,9 +15,6 @@ #include "audio_apbridgea.h" #include "audio_manager.h" -#define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt" -#define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data" - static DEFINE_MUTEX(gb_codec_list_lock); static LIST_HEAD(gb_codec_list); @@ -31,8 +28,8 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, __u16 i2s_port, cportid; struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = - (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + struct gb_audio *audio = dev_get_drvdata(dai->dev); + struct gbaudio_codec_info *gb = audio->gbcodec; /* find the dai */ found = 0; @@ -69,8 +66,8 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, __u16 i2s_port, cportid; struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = - (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + struct gb_audio *audio = dev_get_drvdata(dai->dev); + struct gbaudio_codec_info *gb = audio->gbcodec; /* find the dai */ found = 0; @@ -126,8 +123,8 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, uint32_t format, rate; uint16_t data_cport; struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = - (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + struct gb_audio *audio = dev_get_drvdata(dai->dev); + struct gbaudio_codec_info *gb = audio->gbcodec; /* find the dai */ found = 0; @@ -203,8 +200,8 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, int ret, found; uint16_t data_cport; struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = - (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + struct gb_audio *audio = dev_get_drvdata(dai->dev); + struct gbaudio_codec_info *gb = audio->gbcodec; /* find the dai */ found = 0; @@ -280,8 +277,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, int ret, found; int tx, rx, start, stop; struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = - (struct gbaudio_codec_info *)dev_get_drvdata(dai->dev); + struct gb_audio *audio = dev_get_drvdata(dai->dev); + struct gbaudio_codec_info *gb = audio->gbcodec; /* find the dai */ found = 0; @@ -386,7 +383,8 @@ static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { int ret = 0; - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); + struct gbaudio_codec_info *gbcodec = audio->gbcodec; u8 *gbcodec_reg = gbcodec->reg; if (reg == SND_SOC_NOPM) @@ -406,7 +404,8 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec, { unsigned int val = 0; - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); + struct gbaudio_codec_info *gbcodec = audio->gbcodec; u8 *gbcodec_reg = gbcodec->reg; if (reg == SND_SOC_NOPM) @@ -524,6 +523,7 @@ static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev, int dev_id) { struct gbaudio_codec_info *gbcodec; + struct gb_audio *audio = dev_get_drvdata(dev); gbcodec = gbaudio_find_codec(dev, dev_id); if (gbcodec) @@ -539,7 +539,7 @@ static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev, INIT_LIST_HEAD(&gbcodec->codec_ctl_list); INIT_LIST_HEAD(&gbcodec->widget_ctl_list); gbcodec->dev_id = dev_id; - dev_set_drvdata(dev, gbcodec); + audio->gbcodec = gbcodec; gbcodec->dev = dev; snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name, dev_name(dev)); @@ -556,12 +556,14 @@ static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev, static void gbaudio_free_codec(struct device *dev, struct gbaudio_codec_info *gbcodec) { + struct gb_audio *audio = dev_get_drvdata(dev); + mutex_lock(&gb_codec_list_lock); if (!gbcodec->mgmt_connection && list_empty(&gbcodec->dai_list)) { list_del(&gbcodec->list); mutex_unlock(&gb_codec_list_lock); - dev_set_drvdata(dev, NULL); + audio->gbcodec = NULL; devm_kfree(dev, gbcodec); } else { mutex_unlock(&gb_codec_list_lock); @@ -633,12 +635,16 @@ static int gbaudio_codec_probe(struct gb_connection *connection) gbcodec->mgmt_connection = connection; + ret = gb_connection_enable(connection); + if (ret) + goto base_error; + /* fetch topology data */ ret = gb_audio_gb_get_topology(connection, &topology); if (ret) { dev_err(gbcodec->dev, "%d:Error while fetching topology\n", ret); - goto base_error; + goto err_connection_disable; } /* process topology data */ @@ -711,6 +717,8 @@ parse_error: gbcodec->topology = NULL; topology_error: kfree(topology); +err_connection_disable: + gb_connection_disable(connection); base_error: gbcodec->mgmt_connection = NULL; gbaudio_free_codec(dev, gbcodec); @@ -753,6 +761,7 @@ static void gbaudio_codec_remove(struct gb_connection *connection) dev->driver = NULL; gbaudio_tplg_release(gbcodec); kfree(gbcodec->topology); + gb_connection_disable(connection); gbcodec->mgmt_connection = NULL; mutex_lock(&gbcodec->lock); gbcodec->codec_registered = 0; @@ -760,7 +769,7 @@ static void gbaudio_codec_remove(struct gb_connection *connection) gbaudio_free_codec(dev, gbcodec); } -static int gbaudio_codec_report_event_recv(u8 type, struct gb_operation *op) +static int gbaudio_codec_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_audio_streaming_event_request *req = op->request->payload; @@ -772,22 +781,12 @@ static int gbaudio_codec_report_event_recv(u8 type, struct gb_operation *op) return 0; } -static struct gb_protocol gb_audio_mgmt_protocol = { - .name = GB_AUDIO_MGMT_DRIVER_NAME, - .id = GREYBUS_PROTOCOL_AUDIO_MGMT, - .major = 0, - .minor = 1, - .connection_init = gbaudio_codec_probe, - .connection_exit = gbaudio_codec_remove, - .request_recv = gbaudio_codec_report_event_recv, -}; - static int gbaudio_dai_probe(struct gb_connection *connection) { struct gbaudio_dai *dai; struct device *dev = &connection->bundle->dev; int dev_id = connection->intf->interface_id; - struct gbaudio_codec_info *gbcodec = dev_get_drvdata(dev); + struct gbaudio_codec_info *gbcodec; struct gb_audio_manager_module_descriptor desc; int ret; @@ -798,12 +797,16 @@ static int gbaudio_dai_probe(struct gb_connection *connection) if (!gbcodec) return -ENOMEM; + ret = gb_connection_enable(connection); + if (ret) + goto err_free_codec; + /* add/update dai_list*/ dai = gbaudio_add_dai(gbcodec, connection->intf_cport_id, connection, NULL); if (!dai) { ret = -ENOMEM; - goto err_free_codec; + goto err_connection_disable; } /* update dai_added count */ @@ -829,6 +832,8 @@ static int gbaudio_dai_probe(struct gb_connection *connection) return 0; +err_connection_disable: + gb_connection_disable(connection); err_free_codec: gbaudio_free_codec(dev, gbcodec); return ret; @@ -859,10 +864,11 @@ static void gbaudio_dai_remove(struct gb_connection *connection) gbcodec->dai_added--; mutex_unlock(&gbcodec->lock); + gb_connection_disable(connection); gbaudio_free_codec(dev, gbcodec); } -static int gbaudio_dai_report_event_recv(u8 type, struct gb_operation *op) +static int gbaudio_dai_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -871,50 +877,165 @@ static int gbaudio_dai_report_event_recv(u8 type, struct gb_operation *op) return 0; } -static struct gb_protocol gb_audio_data_protocol = { - .name = GB_AUDIO_DATA_DRIVER_NAME, - .id = GREYBUS_PROTOCOL_AUDIO_DATA, - .major = 0, - .minor = 1, - .connection_init = gbaudio_dai_probe, - .connection_exit = gbaudio_dai_remove, - .request_recv = gbaudio_dai_report_event_recv, -}; +static int gb_audio_add_mgmt_connection(struct gb_audio *audio, + struct greybus_descriptor_cport *cport_desc, + struct gb_bundle *bundle) +{ + struct gb_connection *connection; -/* - * This is the basic hook get things initialized and registered w/ gb - */ + /* Management Cport */ + if (audio->mgmt_connection) { + dev_err(&bundle->dev, + "Can't have multiple Management connections\n"); + return -ENODEV; + } + + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + gbaudio_codec_request_handler); + if (IS_ERR(connection)) + return PTR_ERR(connection); + + connection->private = audio; + audio->mgmt_connection = connection; + + return 0; +} + +static int gb_audio_add_data_connection(struct gb_audio *audio, + struct greybus_descriptor_cport *cport_desc, + struct gb_bundle *bundle, int index) +{ + struct gb_connection *connection; + + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + gbaudio_dai_request_handler); + if (IS_ERR(connection)) + return PTR_ERR(connection); + + connection->private = audio; + audio->data_connection[index] = connection; + + return 0; +} -static int __init gb_audio_protocol_init(void) +static int gb_audio_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) { - int err; + struct greybus_descriptor_cport *cport_desc; + struct gb_audio *audio; + int ret, i; + int count = bundle->num_cports - 1; + + /* There should be at least one Management and one Data cport */ + if (bundle->num_cports < 2) + return -ENODEV; - err = gb_protocol_register(&gb_audio_mgmt_protocol); - if (err) { - pr_err("Can't register i2s mgmt protocol driver: %d\n", -err); - return err; + /* + * There can be only one Management connection and any number of data + * connections. + */ + audio = kzalloc(sizeof(*audio) + + count * sizeof(*audio->data_connection), GFP_KERNEL); + if (!audio) + return -ENOMEM; + + audio->num_data_connections = count; + greybus_set_drvdata(bundle, audio); + + /* Create all connections */ + for (count = 0, i = 0; i < bundle->num_cports; i++) { + cport_desc = &bundle->cport_desc[i]; + + switch (cport_desc->protocol_id) { + case GREYBUS_PROTOCOL_AUDIO_MGMT: + ret = gb_audio_add_mgmt_connection(audio, cport_desc, + bundle); + if (ret) + goto destroy_connections; + break; + case GREYBUS_PROTOCOL_AUDIO_DATA: + ret = gb_audio_add_data_connection(audio, cport_desc, + bundle, count); + if (ret) + goto destroy_connections; + + count++; + break; + default: + dev_err(&bundle->dev, "Unsupported protocol: 0x%02x\n", + cport_desc->protocol_id); + ret = -ENODEV; + goto destroy_connections; + } } - err = gb_protocol_register(&gb_audio_data_protocol); - if (err) { - pr_err("Can't register Audio protocol driver: %d\n", -err); - goto err_unregister_audio_mgmt; + /* There must be a management cport */ + if (!audio->mgmt_connection) { + ret = -EINVAL; + dev_err(&bundle->dev, "Missing management connection\n"); + goto destroy_connections; + } + + /* Initialize management connection */ + ret = gbaudio_codec_probe(audio->mgmt_connection); + if (ret) + goto destroy_connections; + + /* Initialize data connections */ + for (i = 0; i < audio->num_data_connections; i++) { + ret = gbaudio_dai_probe(audio->data_connection[i]); + if (ret) + goto remove_dai; } return 0; -err_unregister_audio_mgmt: - gb_protocol_deregister(&gb_audio_mgmt_protocol); - return err; +remove_dai: + while (i--) + gbaudio_dai_remove(audio->data_connection[i]); + + gbaudio_codec_remove(audio->mgmt_connection); +destroy_connections: + while (count--) + gb_connection_destroy(audio->data_connection[count]); + + if (audio->mgmt_connection) + gb_connection_destroy(audio->mgmt_connection); + + kfree(audio); + + return ret; } -module_init(gb_audio_protocol_init); -static void __exit gb_audio_protocol_exit(void) +static void gb_audio_disconnect(struct gb_bundle *bundle) { - gb_protocol_deregister(&gb_audio_data_protocol); - gb_protocol_deregister(&gb_audio_mgmt_protocol); + struct gb_audio *audio = greybus_get_drvdata(bundle); + int i; + + for (i = audio->num_data_connections - 1; i >= 0; i--) { + gbaudio_dai_remove(audio->data_connection[i]); + gb_connection_destroy(audio->data_connection[i]); + } + + gbaudio_codec_remove(audio->mgmt_connection); + gb_connection_destroy(audio->mgmt_connection); + + kfree(audio); } -module_exit(gb_audio_protocol_exit); + +static const struct greybus_bundle_id gb_audio_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_MGMT) }, + { } +}; +MODULE_DEVICE_TABLE(greybus, gb_audio_id_table); + +static struct greybus_driver gb_audio_driver = { + .name = "gb-audio", + .probe = gb_audio_probe, + .disconnect = gb_audio_disconnect, + .id_table = gb_audio_id_table, +}; +module_greybus_driver(gb_audio_driver); MODULE_DESCRIPTION("Greybus Audio codec driver"); MODULE_AUTHOR("Vaibhav Agarwal "); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 0b587ef74d16..bba48a59bf4d 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -142,6 +142,13 @@ struct gbaudio_codec_info { struct mutex lock; }; +struct gb_audio { + struct gb_connection *mgmt_connection; + size_t num_data_connections; + struct gbaudio_codec_info *gbcodec; + struct gb_connection *data_connection[0]; +}; + struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec, int data_cport, struct gb_connection *connection, diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index b18574e91b5f..90d2148392ef 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -92,7 +92,8 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_info *info; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); + struct gbaudio_codec_info *gbcodec = audio->gbcodec; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -138,7 +139,8 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); + struct gbaudio_codec_info *gb = audio->gbcodec; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -185,7 +187,8 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); + struct gbaudio_codec_info *gb = audio->gbcodec; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -279,7 +282,8 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; - struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); + struct gbaudio_codec_info *gb = audio->gbcodec; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -313,7 +317,8 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; - struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); + struct gbaudio_codec_info *gb = audio->gbcodec; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -519,7 +524,8 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, int wid; int ret; struct snd_soc_codec *codec = w->codec; - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); + struct gbaudio_codec_info *gbcodec = audio->gbcodec; dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 838c70fe74c0..677a949cb0ec 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -249,8 +249,6 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SENSOR) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_MGMT) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_DATA) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SVC) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) }, -- cgit v1.2.3-59-g8ed1b From 3b710ec06e00ce041606678b25f9c82e95fde813 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 27 Jan 2016 11:17:00 +0530 Subject: greybus: audio: Rename Audio class and remove the unused one There should be a single class macro for Audio and two protocol macros. Rename class with value 0x12 as GREYBUS_CLASS_AUDIO and remove the other unused class GREYBUS_CLASS_AUDIO_DATA. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 2 +- drivers/staging/greybus/greybus_manifest.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index b2feba8cee14..b43b5432d7a6 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -1024,7 +1024,7 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) } static const struct greybus_bundle_id gb_audio_id_table[] = { - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_MGMT) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) }, { } }; MODULE_DEVICE_TABLE(greybus, gb_audio_id_table); diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 32602b1951b8..76649a649554 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -69,8 +69,8 @@ enum greybus_class_type { GREYBUS_CLASS_LIGHTS = 0x0f, GREYBUS_CLASS_VIBRATOR = 0x10, GREYBUS_CLASS_LOOPBACK = 0x11, - GREYBUS_CLASS_AUDIO_MGMT = 0x12, - GREYBUS_CLASS_AUDIO_DATA = 0x13, + GREYBUS_CLASS_AUDIO = 0x12, + /* 0x13 is unused */ GREYBUS_CLASS_SVC = 0x14, GREYBUS_CLASS_FIRMWARE = 0x15, /* ... */ -- cgit v1.2.3-59-g8ed1b From 24988d3a253303be63c460071dd99bd4674c0880 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 Jan 2016 12:43:29 +0100 Subject: greybus: hd: fix host-device-removal race Make sure to tear down the svc and flush any on-going hotplug processing before removing the remaining interfaces. This fixes crashes due to host-device removal racing with svc hotplug/unplug processing (e.g. at "UniPro restart"). Testing Done: Verified that this fixes crashes reproducible on SDB when unloading the host-device driver module while generating hotplug/unplug events. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hd.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index b11a63644235..d1aab29d3da4 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -135,9 +135,12 @@ EXPORT_SYMBOL_GPL(gb_hd_add); void gb_hd_del(struct gb_host_device *hd) { - gb_interfaces_remove(hd); - + /* + * Tear down the svc and flush any on-going hotplug processing before + * removing the remaining interfaces. + */ gb_svc_del(hd->svc); + gb_interfaces_remove(hd); device_del(&hd->dev); } -- cgit v1.2.3-59-g8ed1b From 057aad29f9dec26d49671c6744d070edb8594174 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Wed, 27 Jan 2016 16:40:58 -0800 Subject: greybus: arche-platform: ensure wake-detect pin is deasserted During DB3.5 bringup, it was noted that wake_detect signal was not properly generating SVC edge IRQ. To ensure signal goes from low to high correctly, let's bring signal low (regardless of default pin state). Testing Done: - Used for DB3.5/EVT1.5 hardware during bringup - Regression tested on DB3.1+ES2, DB3.1+ES3 Signed-off-by: Michael Scott Reviewed-by: Vaibhav Hiremath Tested-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index b5596ec4409c..4e49be837b0e 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -211,6 +211,8 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->wake_detect_gpio); goto exit; } + /* deassert wake detect */ + gpio_direction_output(arche_pdata->wake_detect_gpio, 0); arche_pdata->dev = &pdev->dev; INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); -- cgit v1.2.3-59-g8ed1b From 9e1aef82af47edd1697449ca8be5fd3c1c58b9bb Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Wed, 27 Jan 2016 16:40:59 -0800 Subject: greybus: arche-platform: bring SVC out of reset later in probe Move SVC deassert reset to after wake-detect pin has been pulled low in probe. Otherwise, SVC may trigger WAKE_OUT based on a default high signal. Testing Done: - Used for DB3.5/EVT1.5 hardware during bringup - Regression tested on DB3.1+ES2, DB3.1+ES3 Signed-off-by: Michael Scott Reviewed-by: Vaibhav Hiremath Tested-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 4e49be837b0e..b6fb90e9c7dc 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -191,10 +191,6 @@ static int arche_platform_probe(struct platform_device *pdev) platform_set_drvdata(pdev, arche_pdata); - /* bring SVC out of reset */ - svc_reset_onoff(arche_pdata->svc_reset_gpio, - !arche_pdata->is_reset_act_hi); - arche_pdata->num_apbs = of_get_child_count(np); dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); @@ -214,6 +210,10 @@ static int arche_platform_probe(struct platform_device *pdev) /* deassert wake detect */ gpio_direction_output(arche_pdata->wake_detect_gpio, 0); + /* bring SVC out of reset */ + svc_reset_onoff(arche_pdata->svc_reset_gpio, + !arche_pdata->is_reset_act_hi); + arche_pdata->dev = &pdev->dev; INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); -- cgit v1.2.3-59-g8ed1b From 1e83ee3321447679eb327fed90f366ef18411778 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Wed, 27 Jan 2016 16:41:00 -0800 Subject: greybus: arche-platform: assert wake-detect to complete WAKE_OUT event After SVC generates WAKE_OUT (pulling wake-detect pin low) and APB is brought out of reset, we need to assert wake-detect again to complete SVC WAKE_OUT logic. Testing Done: - Used for DB3.5/EVT1.5 hardware during bringup - Regression tested on DB3.1+ES2, DB3.1+ES3 Signed-off-by: Michael Scott Reviewed-by: Vaibhav Hiremath Tested-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index b6fb90e9c7dc..f18cec624f78 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -79,9 +79,11 @@ static void svc_delayed_work(struct work_struct *work) if (timeout >= 0) { ret = of_platform_populate(np, NULL, NULL, dev); - if (!ret) - /* Should we set wake_detect gpio to output again? */ + if (!ret) { + /* re-assert wake_detect to confirm SVC WAKE_OUT */ + gpio_direction_output(arche_pdata->wake_detect_gpio, 1); return; + } } /* FIXME: We may want to limit retries here */ -- cgit v1.2.3-59-g8ed1b From a3043d9e717ace89a34352e57d88c70584f9c768 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Wed, 27 Jan 2016 16:41:01 -0800 Subject: greybus: arche-platform: reduce wait between WAKE_OUT checks SVC WAKE_OUT loop is estimated 400ms during which wake_detect line is pulled low for AP detection. On AP side we have 500ms delay between checks. To avoid timing issues, reduce delay between checks and raise total # of checks so that overall time for sequence is the same. Testing Done: - Used for DB3.5/EVT1.5 hardware during bringup - Regression tested on DB3.1+ES2, DB3.1+ES3 Signed-off-by: Michael Scott Reviewed-by: Vaibhav Hiremath Tested-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index f18cec624f78..65c8f718dd86 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -52,7 +52,7 @@ static void svc_delayed_work(struct work_struct *work) container_of(work, struct arche_platform_drvdata, delayed_work.work); struct device *dev = arche_pdata->dev; struct device_node *np = dev->of_node; - int timeout = 10; + int timeout = 50; int ret; /* @@ -74,7 +74,7 @@ static void svc_delayed_work(struct work_struct *work) if (gpio_get_value(arche_pdata->wake_detect_gpio) == 0) break; - msleep(500); + msleep(100); } while(timeout--); if (timeout >= 0) { -- cgit v1.2.3-59-g8ed1b From c760442e1cafef30de20ea5b64074d70efa6eec2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 29 Jan 2016 15:42:30 +0100 Subject: greybus: legacy: remove unimplemented classes from id table Remove the unimplemented display, sensor, and svc classes from the device-id table. As Viresh noted the SVC protocol is special and having an SVC class doesn't really make sense at all. Either way, the SVC protocol is already implemented by core. Testing Done: Compiled Reported-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 677a949cb0ec..900521ab6931 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -244,12 +244,9 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_DISPLAY) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SENSOR) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SVC) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) }, { } -- cgit v1.2.3-59-g8ed1b From 8ec589b9796eebfa266d2b047ee2318541814e28 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 29 Jan 2016 15:42:31 +0100 Subject: greybus: firmware: convert to bundle driver Convert the legacy firmware protocol driver to a bundle driver. This also fixes a potential crash should a (malicious) module have sent an early request before the private data had been initialised. Note that the firmware protocol needs to support the version request indefinitely since it has been burnt into ROM. In order to avoid having to update current module-loading scripts, keep this driver internal to greybus core at least until modalias support is added. Note that there is no MODULE_DEVICE_TABLE defined for firmware as we cannot have two greybus tables in one module on ancient 3.10 kernels and that the legacy driver is currently also internal to core. This needs be added once the driver can be built as a module. Testing Done: Tested on DB3. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 9 +- drivers/staging/greybus/firmware.c | 131 ++++++++++++++++++++++++---- drivers/staging/greybus/firmware.h | 4 +- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/greybus_protocols.h | 11 +++ drivers/staging/greybus/legacy.c | 1 - 6 files changed, 130 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index b9303c0cd5e4..2fb95744e01c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -10,6 +10,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define CREATE_TRACE_POINTS +#include "firmware.h" #include "greybus.h" #include "greybus_trace.h" #include "legacy.h" @@ -243,9 +244,9 @@ static int __init gb_init(void) goto error_operation; } - retval = gb_firmware_protocol_init(); + retval = gb_firmware_init(); if (retval) { - pr_err("gb_firmware_protocol_init failed\n"); + pr_err("gb_firmware_init failed\n"); goto error_firmware; } @@ -258,7 +259,7 @@ static int __init gb_init(void) return 0; /* Success */ error_legacy: - gb_firmware_protocol_exit(); + gb_firmware_exit(); error_firmware: gb_operation_exit(); error_operation: @@ -275,7 +276,7 @@ module_init(gb_init); static void __exit gb_exit(void) { gb_legacy_exit(); - gb_firmware_protocol_exit(); + gb_firmware_exit(); gb_operation_exit(); gb_hd_exit(); bus_unregister(&greybus_bus_type); diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 37895107e427..17ce573b9162 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -9,11 +9,15 @@ #include +#include "firmware.h" #include "greybus.h" + struct gb_firmware { struct gb_connection *connection; const struct firmware *fw; + u8 protocol_major; + u8 protocol_minor; }; static void free_firmware(struct gb_firmware *firmware) @@ -223,8 +227,10 @@ static int gb_firmware_ready_to_boot(struct gb_operation *op) return 0; } -static int gb_firmware_request_recv(u8 type, struct gb_operation *op) +static int gb_firmware_request_handler(struct gb_operation *op) { + u8 type = op->type; + switch (type) { case GB_FIRMWARE_TYPE_FIRMWARE_SIZE: return gb_firmware_size_request(op); @@ -239,60 +245,147 @@ static int gb_firmware_request_recv(u8 type, struct gb_operation *op) } } -static int gb_firmware_connection_init(struct gb_connection *connection) +static int gb_firmware_get_version(struct gb_firmware *firmware) { + struct gb_bundle *bundle = firmware->connection->bundle; + struct gb_firmware_version_request request; + struct gb_firmware_version_response response; + int ret; + + request.major = GB_FIRMWARE_VERSION_MAJOR; + request.minor = GB_FIRMWARE_VERSION_MINOR; + + ret = gb_operation_sync(firmware->connection, + GB_FIRMWARE_TYPE_VERSION, + &request, sizeof(request), &response, + sizeof(response)); + if (ret) { + dev_err(&bundle->dev, + "failed to get protocol version: %d\n", + ret); + return ret; + } + + if (response.major > request.major) { + dev_err(&bundle->dev, + "unsupported major protocol version (%u > %u)\n", + response.major, request.major); + return -ENOTSUPP; + } + + firmware->protocol_major = response.major; + firmware->protocol_minor = response.minor; + + dev_dbg(&bundle->dev, "%s - %u.%u\n", __func__, response.major, + response.minor); + + return 0; +} + +static int gb_firmware_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; struct gb_firmware *firmware; int ret; + if (bundle->num_cports != 1) + return -ENODEV; + + cport_desc = &bundle->cport_desc[0]; + if (cport_desc->protocol_id != GREYBUS_PROTOCOL_FIRMWARE) + return -ENODEV; + firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) return -ENOMEM; - firmware->connection = connection; + connection = gb_connection_create(bundle, + le16_to_cpu(cport_desc->id), + gb_firmware_request_handler); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto err_free_firmware; + } + connection->private = firmware; + firmware->connection = connection; + + greybus_set_drvdata(bundle, firmware); + + ret = gb_connection_enable_tx(connection); + if (ret) + goto err_connection_destroy; + + ret = gb_firmware_get_version(firmware); + if (ret) + goto err_connection_disable; + firmware_es2_fixup_vid_pid(firmware); + ret = gb_connection_enable(connection); + if (ret) + goto err_connection_disable; + /* Tell bootrom we're ready. */ ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_AP_READY, NULL, 0, NULL, 0); if (ret) { dev_err(&connection->bundle->dev, "failed to send AP READY: %d\n", ret); - goto err_free_firmware; + goto err_connection_disable; } - dev_dbg(&connection->bundle->dev, "%s: AP_READY sent\n", __func__); + dev_dbg(&bundle->dev, "AP_READY sent\n"); return 0; +err_connection_disable: + gb_connection_disable(connection); +err_connection_destroy: + gb_connection_destroy(connection); err_free_firmware: kfree(firmware); return ret; } -static void gb_firmware_connection_exit(struct gb_connection *connection) +static void gb_firmware_disconnect(struct gb_bundle *bundle) { - struct gb_firmware *firmware = connection->private; + struct gb_firmware *firmware = greybus_get_drvdata(bundle); + + dev_dbg(&bundle->dev, "%s\n", __func__); + + gb_connection_disable(firmware->connection); /* Release firmware */ if (firmware->fw) free_firmware(firmware); - connection->private = NULL; + gb_connection_destroy(firmware->connection); kfree(firmware); - - dev_dbg(&connection->bundle->dev, "%s\n", __func__); } -static struct gb_protocol firmware_protocol = { - .name = "firmware", - .id = GREYBUS_PROTOCOL_FIRMWARE, - .major = GB_FIRMWARE_VERSION_MAJOR, - .minor = GB_FIRMWARE_VERSION_MINOR, - .connection_init = gb_firmware_connection_init, - .connection_exit = gb_firmware_connection_exit, - .request_recv = gb_firmware_request_recv, +static const struct greybus_bundle_id gb_firmware_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) }, + { } }; -gb_builtin_protocol_driver(firmware_protocol); + +static struct greybus_driver gb_firmware_driver = { + .name = "firmware", + .probe = gb_firmware_probe, + .disconnect = gb_firmware_disconnect, + .id_table = gb_firmware_id_table, +}; + +int gb_firmware_init(void) +{ + return greybus_register(&gb_firmware_driver); +} + +void gb_firmware_exit(void) +{ + greybus_deregister(&gb_firmware_driver); +} diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h index 548d297eec63..f25744c1648e 100644 --- a/drivers/staging/greybus/firmware.h +++ b/drivers/staging/greybus/firmware.h @@ -10,7 +10,7 @@ #ifndef __FIRMWARE_H #define __FIRMWARE_H -int gb_firmware_protocol_init(void); -void gb_firmware_protocol_exit(void); +int gb_firmware_init(void); +void gb_firmware_exit(void); #endif /* __FIRMWARE_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 27679468e997..2fc79faad28e 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -27,7 +27,6 @@ #include "manifest.h" #include "hd.h" #include "svc.h" -#include "firmware.h" #include "control.h" #include "interface.h" #include "bundle.h" diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 3f51fb5d6e8b..89db93282cac 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -203,6 +203,7 @@ struct gb_control_interface_version_response { #define GB_FIRMWARE_VERSION_MINOR 0x01 /* Greybus firmware request types */ +#define GB_FIRMWARE_TYPE_VERSION 0x01 #define GB_FIRMWARE_TYPE_FIRMWARE_SIZE 0x02 #define GB_FIRMWARE_TYPE_GET_FIRMWARE 0x03 #define GB_FIRMWARE_TYPE_READY_TO_BOOT 0x04 @@ -226,6 +227,16 @@ struct gb_control_interface_version_response { /* Max firmware data fetch size in bytes */ #define GB_FIRMWARE_FETCH_MAX 2000 +struct gb_firmware_version_request { + __u8 major; + __u8 minor; +} __packed; + +struct gb_firmware_version_response { + __u8 major; + __u8 minor; +} __packed; + /* Firmware protocol firmware size request/response */ struct gb_firmware_size_request { __u8 stage; diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 900521ab6931..057029dc3ee4 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -247,7 +247,6 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) }, { } }; -- cgit v1.2.3-59-g8ed1b From 3f830562deade2ab6163f07ee6829158cc070ee4 Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Fri, 29 Jan 2016 11:34:19 +0100 Subject: greybus: loopback_test: Decrease the max number of devices Its unrealistic to expect 50 loopback devices, and the mask parameter can hold up to "int" anyways so decrease to max number of devices to sane value. Signed-off-by: Axel Haslam Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/tools/loopback_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index e8d35bd95cc7..6178354fbb74 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -17,7 +17,7 @@ #include #include -#define MAX_NUM_DEVICES 50 +#define MAX_NUM_DEVICES 10 #define MAX_SYSFS_PATH 0x200 #define CSV_MAX_LINE 0x1000 #define SYSFS_MAX_INT 0x20 -- cgit v1.2.3-59-g8ed1b From 3c5de59473f88aeb12420f142d42801d4d389759 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 25 Jan 2016 16:52:17 -0800 Subject: greybus: audio_manager: use an 'ida' for the module id Every time we hotplug an audio module, we get a new audio module id. We should recycle them instead of just constantly incrementing the number so we don't see things like: [178016.832580] Created audio module #6124 in the kernel logs. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_manager.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c index 117676314daf..05af441be162 100644 --- a/drivers/staging/greybus/audio_manager.c +++ b/drivers/staging/greybus/audio_manager.c @@ -19,8 +19,7 @@ static struct kset *manager_kset; static LIST_HEAD(modules_list); static DEFINE_RWLOCK(modules_lock); - -static int current_module_id; +static DEFINE_IDA(module_id); /* helpers */ static struct gb_audio_manager_module *gb_audio_manager_get_locked(int id) @@ -43,12 +42,16 @@ int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc) { struct gb_audio_manager_module *module; unsigned long flags; + int id; int err; + id = ida_simple_get(&module_id, 0, 0, GFP_KERNEL); err = gb_audio_manager_module_create(&module, manager_kset, - current_module_id++, desc); - if (err) + id, desc); + if (err) { + ida_simple_remove(&module_id, id); return err; + } /* Add it to the list */ write_lock_irqsave(&modules_lock, flags); @@ -72,6 +75,7 @@ int gb_audio_manager_remove(int id) return -EINVAL; } + ida_simple_remove(&module_id, module->id); list_del(&module->list); kobject_put(&module->kobj); write_unlock_irqrestore(&modules_lock, flags); @@ -175,6 +179,7 @@ static void __exit manager_exit(void) { gb_audio_manager_remove_all(); kset_unregister(manager_kset); + ida_destroy(&module_id); } module_init(manager_init); -- cgit v1.2.3-59-g8ed1b From 1045451f8abbfe3628af9bc1ce53da4d57723626 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Jan 2016 15:50:47 +0530 Subject: greybus: audio: Fix sparse warnings greybus/audio_apbridgea.c:13:5: warning: symbol 'gb_audio_apbridgea_set_config' was not declared. Should it be static? greybus/audio_apbridgea.c:30:5: warning: symbol 'gb_audio_apbridgea_register_cport' was not declared. Should it be static? greybus/audio_apbridgea.c:44:5: warning: symbol 'gb_audio_apbridgea_unregister_cport' was not declared. Should it be static? greybus/audio_apbridgea.c:58:5: warning: symbol 'gb_audio_apbridgea_set_tx_data_size' was not declared. Should it be static? greybus/audio_apbridgea.c:72:5: warning: symbol 'gb_audio_apbridgea_get_tx_delay' was not declared. Should it be static? greybus/audio_apbridgea.c:80:5: warning: symbol 'gb_audio_apbridgea_start_tx' was not declared. Should it be static? greybus/audio_apbridgea.c:94:5: warning: symbol 'gb_audio_apbridgea_stop_tx' was not declared. Should it be static? greybus/audio_apbridgea.c:106:5: warning: symbol 'gb_audio_apbridgea_set_rx_data_size' was not declared. Should it be static? greybus/audio_apbridgea.c:120:5: warning: symbol 'gb_audio_apbridgea_get_rx_delay' was not declared. Should it be static? greybus/audio_apbridgea.c:128:5: warning: symbol 'gb_audio_apbridgea_start_rx' was not declared. Should it be static? greybus/audio_apbridgea.c:141:5: warning: symbol 'gb_audio_apbridgea_stop_rx' was not declared. Should it be static? greybus/audio_gb.c:14:5: warning: symbol 'gb_audio_gb_get_topology' was not declared. Should it be static? greybus/audio_gb.c:48:5: warning: symbol 'gb_audio_gb_get_control' was not declared. Should it be static? greybus/audio_gb.c:70:5: warning: symbol 'gb_audio_gb_set_control' was not declared. Should it be static? greybus/audio_gb.c:85:5: warning: symbol 'gb_audio_gb_enable_widget' was not declared. Should it be static? greybus/audio_gb.c:97:5: warning: symbol 'gb_audio_gb_disable_widget' was not declared. Should it be static? greybus/audio_gb.c:109:5: warning: symbol 'gb_audio_gb_get_pcm' was not declared. Should it be static? greybus/audio_gb.c:133:5: warning: symbol 'gb_audio_gb_set_pcm' was not declared. Should it be static? greybus/audio_gb.c:150:5: warning: symbol 'gb_audio_gb_set_tx_data_size' was not declared. Should it be static? greybus/audio_gb.c:163:5: warning: symbol 'gb_audio_gb_get_tx_delay' was not declared. Should it be static? greybus/audio_gb.c:183:5: warning: symbol 'gb_audio_gb_activate_tx' was not declared. Should it be static? greybus/audio_gb.c:195:5: warning: symbol 'gb_audio_gb_deactivate_tx' was not declared. Should it be static? greybus/audio_gb.c:207:5: warning: symbol 'gb_audio_gb_set_rx_data_size' was not declared. Should it be static? greybus/audio_gb.c:220:5: warning: symbol 'gb_audio_gb_get_rx_delay' was not declared. Should it be static? greybus/audio_gb.c:240:5: warning: symbol 'gb_audio_gb_activate_rx' was not declared. Should it be static? greybus/audio_gb.c:252:5: warning: symbol 'gb_audio_gb_deactivate_rx' was not declared. Should it be static? Fix them by including the header that declares the exported routines. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_apbridgea.c | 1 + drivers/staging/greybus/audio_gb.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/staging/greybus/audio_apbridgea.c b/drivers/staging/greybus/audio_apbridgea.c index 20238e68e83a..75c8c3ce4b71 100644 --- a/drivers/staging/greybus/audio_apbridgea.c +++ b/drivers/staging/greybus/audio_apbridgea.c @@ -9,6 +9,7 @@ #include "greybus.h" #include "greybus_protocols.h" #include "audio_apbridgea.h" +#include "audio_codec.h" int gb_audio_apbridgea_set_config(struct gb_connection *connection, __u16 i2s_port, __u32 format, __u32 rate, diff --git a/drivers/staging/greybus/audio_gb.c b/drivers/staging/greybus/audio_gb.c index 08bfdeb87a61..167683d74f1a 100644 --- a/drivers/staging/greybus/audio_gb.c +++ b/drivers/staging/greybus/audio_gb.c @@ -9,6 +9,7 @@ #include "greybus.h" #include "greybus_protocols.h" #include "operation.h" +#include "audio_codec.h" /* TODO: Split into separate calls */ int gb_audio_gb_get_topology(struct gb_connection *connection, -- cgit v1.2.3-59-g8ed1b From ee9627bce7561f772345a329e783149893cfbb55 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Jan 2016 15:50:49 +0530 Subject: greybus: control: Fix sparse warnings gb_control_get_version() is not used outside of the file and must be marked as static. Following sparse warnings are reported today: greybus/control.c:20:5: warning: symbol 'gb_control_get_version' was not declared. Should it be static? Fix it by marking gb_control_get_version() 'static'. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index fb0bb1c40d99..ba2754aa513c 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -17,7 +17,7 @@ #define GB_CONTROL_VERSION_MINOR 1 -int gb_control_get_version(struct gb_control *control) +static int gb_control_get_version(struct gb_control *control) { struct gb_interface *intf = control->connection->intf; struct gb_control_version_request request; -- cgit v1.2.3-59-g8ed1b From 68ba0a01ccc3e0aff3687ac2704c01a3849d5345 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Feb 2016 21:35:12 -0800 Subject: greybus: lsgb: remove it, it's in the gb-utils repo lsgb does not need to be in this repo, it gets installed as part of gb-utils now. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/lsgb | 151 ------------------------------------------- 1 file changed, 151 deletions(-) delete mode 100755 drivers/staging/greybus/lsgb diff --git a/drivers/staging/greybus/lsgb b/drivers/staging/greybus/lsgb deleted file mode 100755 index 2d84169a7b83..000000000000 --- a/drivers/staging/greybus/lsgb +++ /dev/null @@ -1,151 +0,0 @@ -#!/system/bin/sh -# -# 'ls greybus' -# -# Copyright 2016 Google 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, -# 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -############################################################################### - -# Horrid hack to just make it easier than poking around directly in sysfs -# Don't rely on any of these fields to be stable, the layout is going to -# change a lot over time. -# - -print_interface() { - local devname=$1 - local iid=`cat interface_id` - local vid=`cat vendor_id` - local pid=`cat product_id` - local version=`cat version` - local serial=`cat serial_number` - local vs=`cat vendor_string` - local ps=`cat product_string` - printf " Intf %02d %s:%s %s %s v%s\n" \ - ${iid} ${vid} ${pid} "${vs}" "${ps}" ${version} -} - -print_bundle() { - local devname=$1 - local id=`cat bundle_id` - local class=`cat bundle_class` - local class_type="unknown" - case ${class} in - "0x01" ) class_type="SVC" - ;; - "0x02" ) class_type="GPIO" - ;; - "0x03" ) class_type="I2C" - ;; - "0x04" ) class_type="UART" - ;; - "0x05" ) class_type="HID" - ;; - "0x06" ) class_type="USB" - ;; - "0x07" ) class_type="SDIO" - ;; - "0x08" ) class_type="Power Supply" - ;; - "0x09" ) class_type="PWM" - ;; - "0x0a" ) class_type="unknown" - ;; - "0x0b" ) class_type="SPI" - ;; - "0x0c" ) class_type="Display" - ;; - "0x0d" ) class_type="Camera" - ;; - "0x0e" ) class_type="Sensoe" - ;; - "0x0f" ) class_type="Lights" - ;; - "0x10" ) class_type="Vibrator" - ;; - "0x11" ) class_type="Loopback" - ;; - "0x12" ) class_type="Audio Management" - ;; - "0x13" ) class_type="Audio Data" - ;; - "0x14" ) class_type="SVC" - ;; - "0x15" ) class_type="Firmware" - ;; - "0xfe" ) class_type="Raw" - ;; - "0xff" ) class_type="Vendor" - ;; - esac - printf " Bundle %02d Class %s (%s)\n" ${id} ${class} ${class_type} -} - -print_svc() { - local devname=$1 - local bus=`cat uevent | grep BUS | cut -f 2 -d '='` - printf " SVC %02d\n" ${bus} -} - -print_host_device() { - local devname=$1 - local bus=`cat uevent | grep BUS | cut -f 2 -d '='` - printf "Bus %02d\n" ${bus} -} - -print_unknown() { - local devname=$1 - - printf "Unknown device type: ${devname}\n" -} - - -print_device() { - local dev=$1 - - [ -d $dev ] || return - cd $dev - - local devname=`basename ${dev}` - local devtype=`cat uevent | grep DEVTYPE | cut -f 2 -d '='` - case ${devtype} in - greybus_interface ) print_interface ${devname} - ;; - greybus_bundle ) print_bundle ${devname} - ;; - greybus_svc ) print_svc ${devname} - ;; - greybus_host_device ) print_host_device ${devname} - ;; - * ) print_unknown ${devname} - ;; - esac -} - -for device in /sys/bus/greybus/devices/* -do - print_device $device -done -- cgit v1.2.3-59-g8ed1b From 127c1fbd55939642365f26efd3121562629ec1b1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 28 Jan 2016 15:50:48 +0530 Subject: greybus: connection: Fix sparse warnings around locking The callers ensures that connection->lock is taken before calling few routines, but that isn't enough for sparse as it sees an unexpected unlock. greybus/connection.c:380:29: warning: context imbalance in 'gb_connection_cancel_operations' - unexpected unlock Fix that adding __must_lock() attribute to the function declaration. This also adds the attribute for gb_connection_flush_incoming_operations(), which isn't showing any sparse warnings with the current state of code, but with minor rearrangements of the code. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 77c2f672b405..4f5e2adfa8c4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -373,6 +373,7 @@ gb_connection_control_disconnected(struct gb_connection *connection) */ static void gb_connection_cancel_operations(struct gb_connection *connection, int errno) + __must_hold(&connection->lock) { struct gb_operation *operation; @@ -401,6 +402,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, static void gb_connection_flush_incoming_operations(struct gb_connection *connection, int errno) + __must_hold(&connection->lock) { struct gb_operation *operation; bool incoming; -- cgit v1.2.3-59-g8ed1b From 796fad441cb248c1eac88bfb3a5929bb1a10fabb Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Thu, 28 Jan 2016 21:15:39 +0530 Subject: greybus: audio: codec driver cleanup audio codec driver is now moved to bundle driver approach. This resolved many race conditions related to audio mgmt & data connection init/exit sequence. Thus, a lot of helper functions can now be safely removed. Signed-off-by: Vaibhav Agarwal Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 622 +++++++++++-------------------- drivers/staging/greybus/audio_codec.h | 21 +- drivers/staging/greybus/audio_topology.c | 99 +---- 3 files changed, 230 insertions(+), 512 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index b43b5432d7a6..ad28c10fa154 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -18,30 +18,38 @@ static DEFINE_MUTEX(gb_codec_list_lock); static LIST_HEAD(gb_codec_list); +struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, + int data_cport, const char *name) +{ + struct gbaudio_dai *dai; + + list_for_each_entry(dai, &gbcodec->dai_list, list) { + if (name && !strncmp(dai->name, name, NAME_SIZE)) + return dai; + if ((data_cport != -1) && (dai->data_cport == data_cport)) + return dai; + } + return NULL; +} + /* * codec DAI ops */ static int gbcodec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret, found; + int ret; __u16 i2s_port, cportid; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + mutex_lock(&gb->lock); + gb_dai = gbaudio_find_dai(gb, -1, dai->name); + if (!gb_dai) { dev_err(dai->dev, "%s: DAI not registered\n", dai->name); + mutex_unlock(&gb->lock); return -EINVAL; } @@ -54,7 +62,8 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, ret); if (!ret) - atomic_inc(&gb->users); + atomic_inc(&gb_dai->users); + mutex_unlock(&gb->lock); return ret; } @@ -62,28 +71,21 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, static void gbcodec_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret, found; + int ret; __u16 i2s_port, cportid; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + mutex_lock(&gb->lock); + gb_dai = gbaudio_find_dai(gb, -1, dai->name); + if (!gb_dai) { dev_err(dai->dev, "%s: DAI not registered\n", dai->name); - return; + goto func_exit; } - atomic_dec(&gb->users); + atomic_dec(&gb_dai->users); /* deactivate rx/tx */ cportid = gb_dai->connection->intf_cport_id; @@ -97,7 +99,7 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, break; default: dev_err(dai->dev, "Invalid stream type during shutdown\n"); - return; + goto func_exit; } if (ret) @@ -110,6 +112,8 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name, gb_dai->connection->hd_cport_id, ret); +func_exit: + mutex_unlock(&gb->lock); return; } @@ -118,26 +122,20 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai) { - int ret, found; + int ret; uint8_t sig_bits, channels; uint32_t format, rate; uint16_t data_cport; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + mutex_lock(&gb->lock); + gb_dai = gbaudio_find_dai(gb, -1, dai->name); + if (!gb_dai) { dev_err(dai->dev, "%s: DAI not registered\n", dai->name); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } /* @@ -147,21 +145,24 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, if (params_channels(hwparams) != 2) { dev_err(dai->dev, "Invalid channel count:%d\n", params_channels(hwparams)); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } channels = params_channels(hwparams); if (params_rate(hwparams) != 48000) { dev_err(dai->dev, "Invalid sampling rate:%d\n", params_rate(hwparams)); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } rate = GB_AUDIO_PCM_RATE_48000; if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) { dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams)); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } format = GB_AUDIO_PCM_FMT_S16_LE; @@ -176,7 +177,7 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, rate, channels, sig_bits); if (ret) { dev_err(dai->dev, "%d: Error during set_pcm\n", ret); - return ret; + goto func_exit; } /* @@ -190,31 +191,26 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, 6144000); if (ret) dev_err(dai->dev, "%d: Error during set_config\n", ret); - +func_exit: + mutex_unlock(&gb->lock); return ret; } static int gbcodec_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret, found; + int ret; uint16_t data_cport; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + mutex_lock(&gb->lock); + gb_dai = gbaudio_find_dai(gb, -1, dai->name); + if (!gb_dai) { dev_err(dai->dev, "%s: DAI not registered\n", dai->name); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } /* deactivate rx/tx */ @@ -228,7 +224,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, dev_err(dai->dev, "%d:Error during set_rx_data_size, cport:%d\n", ret, data_cport); - return ret; + goto func_exit; } ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0, 192); @@ -236,7 +232,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, dev_err(dai->dev, "%d:Error during apbridgea_set_rx_data_size\n", ret); - return ret; + goto func_exit; } ret = gb_audio_gb_activate_rx(gb->mgmt_connection, data_cport); break; @@ -247,7 +243,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, dev_err(dai->dev, "%d:Error during module set_tx_data_size, cport:%d\n", ret, data_cport); - return ret; + goto func_exit; } ret = gb_audio_apbridgea_set_tx_data_size(gb_dai->connection, 0, 192); @@ -255,43 +251,40 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, dev_err(dai->dev, "%d:Error during apbridgea set_tx_data_size, cport\n", ret); - return ret; + goto func_exit; } ret = gb_audio_gb_activate_tx(gb->mgmt_connection, data_cport); break; default: dev_err(dai->dev, "Invalid stream type %d during prepare\n", substream->stream); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } if (ret) dev_err(dai->dev, "%d: Error during activate stream\n", ret); +func_exit: + mutex_unlock(&gb->lock); return ret; } static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - int ret, found; + int ret; int tx, rx, start, stop; struct gbaudio_dai *gb_dai; - struct gb_audio *audio = dev_get_drvdata(dai->dev); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); /* find the dai */ - found = 0; - list_for_each_entry(gb_dai, &gb->dai_list, list) { - if (!strncmp(gb_dai->name, dai->name, NAME_SIZE)) { - found = 1; - break; - } - } - - if (!found) { + mutex_lock(&gb->lock); + gb_dai = gbaudio_find_dai(gb, -1, dai->name); + if (!gb_dai) { dev_err(dai->dev, "%s: DAI not registered\n", dai->name); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } tx = rx = start = stop = 0; @@ -306,7 +299,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, break; default: dev_err(dai->dev, "Invalid tigger cmd:%d\n", cmd); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } switch (substream->stream) { @@ -319,7 +313,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, default: dev_err(dai->dev, "Invalid stream type:%d\n", substream->stream); - return -EINVAL; + ret = -EINVAL; + goto func_exit; } if (start && tx) @@ -341,6 +336,8 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(dai->dev, "%d:Error during %s stream\n", ret, start ? "Start" : "Stop"); +func_exit: + mutex_unlock(&gb->lock); return ret; } @@ -383,8 +380,7 @@ static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { int ret = 0; - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gbcodec = audio->gbcodec; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); u8 *gbcodec_reg = gbcodec->reg; if (reg == SND_SOC_NOPM) @@ -404,8 +400,7 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec, { unsigned int val = 0; - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gbcodec = audio->gbcodec; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); u8 *gbcodec_reg = gbcodec->reg; if (reg == SND_SOC_NOPM) @@ -447,28 +442,15 @@ static struct snd_soc_dai_link gbaudio_dailink = { .be_id = 34, }; -static void gbaudio_remove_dailinks(struct gbaudio_codec_info *gbcodec) -{ - int i; - - for (i = 0; i < gbcodec->num_dai_links; i++) { - dev_dbg(gbcodec->dev, "Remove %s: DAI link\n", - gbcodec->dailink_name[i]); - devm_kfree(gbcodec->dev, gbcodec->dailink_name[i]); - gbcodec->dailink_name[i] = NULL; - } - gbcodec->num_dai_links = 0; -} - static int gbaudio_add_dailinks(struct gbaudio_codec_info *gbcodec) { int ret, i; char *dai_link_name; - struct snd_soc_dai_link *dai; + struct snd_soc_dai_link *dailink; struct device *dev = gbcodec->dev; - dai = &gbaudio_dailink; - dai->codec_name = gbcodec->name; + dailink = &gbaudio_dailink; + dailink->codec_name = gbcodec->name; /* FIXME * allocate memory for DAI links based on count. @@ -481,98 +463,20 @@ static int gbaudio_add_dailinks(struct gbaudio_codec_info *gbcodec) devm_kzalloc(dev, NAME_SIZE, GFP_KERNEL); snprintf(dai_link_name, NAME_SIZE, "GB %d.%d PRI_MI2S_RX", gbcodec->dev_id, i); - dai->name = dai_link_name; - dai->codec_dai_name = gbcodec->dais[i].name; + dailink->name = dai_link_name; + dailink->codec_dai_name = gbcodec->dais[i].name; } - ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", dai, 1); - if (ret) { + ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", dailink, 1); + if (ret) dev_err(dev, "%d:Error while adding DAI link\n", ret); - goto err_dai_link; - } return ret; - -err_dai_link: - gbcodec->num_dai_links = i; - gbaudio_remove_dailinks(gbcodec); - return ret; } /* * gb_snd management functions */ -static struct gbaudio_codec_info *gbaudio_find_codec(struct device *dev, - int dev_id) -{ - struct gbaudio_codec_info *tmp, *ret; - - mutex_lock(&gb_codec_list_lock); - list_for_each_entry_safe(ret, tmp, &gb_codec_list, list) { - dev_dbg(dev, "%d:device found\n", ret->dev_id); - if (ret->dev_id == dev_id) { - mutex_unlock(&gb_codec_list_lock); - return ret; - } - } - mutex_unlock(&gb_codec_list_lock); - return NULL; -} - -static struct gbaudio_codec_info *gbaudio_get_codec(struct device *dev, - int dev_id) -{ - struct gbaudio_codec_info *gbcodec; - struct gb_audio *audio = dev_get_drvdata(dev); - - gbcodec = gbaudio_find_codec(dev, dev_id); - if (gbcodec) - return gbcodec; - - gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL); - if (!gbcodec) - return NULL; - - mutex_init(&gbcodec->lock); - INIT_LIST_HEAD(&gbcodec->dai_list); - INIT_LIST_HEAD(&gbcodec->widget_list); - INIT_LIST_HEAD(&gbcodec->codec_ctl_list); - INIT_LIST_HEAD(&gbcodec->widget_ctl_list); - gbcodec->dev_id = dev_id; - audio->gbcodec = gbcodec; - gbcodec->dev = dev; - snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name, - dev_name(dev)); - - mutex_lock(&gb_codec_list_lock); - list_add(&gbcodec->list, &gb_codec_list); - mutex_unlock(&gb_codec_list_lock); - dev_dbg(dev, "%d:%s Added to codec list\n", gbcodec->dev_id, - gbcodec->name); - - return gbcodec; -} - -static void gbaudio_free_codec(struct device *dev, - struct gbaudio_codec_info *gbcodec) -{ - struct gb_audio *audio = dev_get_drvdata(dev); - - mutex_lock(&gb_codec_list_lock); - if (!gbcodec->mgmt_connection && - list_empty(&gbcodec->dai_list)) { - list_del(&gbcodec->list); - mutex_unlock(&gb_codec_list_lock); - audio->gbcodec = NULL; - devm_kfree(dev, gbcodec); - } else { - mutex_unlock(&gb_codec_list_lock); - } -} - -/* - * This is the basic hook get things initialized and registered w/ gb - */ /* XXX * since BE DAI path is not yet properly closed from above layer, @@ -593,7 +497,7 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) * In case of BE dailink, need to deactivate APBridge * manually */ - if (gbaudio_dailink.no_pcm && atomic_read(&gb->users)) { + if (atomic_read(&gb_dai->users)) { connection = gb_dai->connection; /* PB active */ ret = gb_audio_apbridgea_stop_tx(connection, 0); @@ -613,38 +517,34 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) if (ret) dev_info(dev, "%d:Failed during unregister cport\n", ret); - atomic_dec(&gb->users); + atomic_dec(&gb_dai->users); } } } -static int gbaudio_codec_probe(struct gb_connection *connection) +static int gbaudio_register_codec(struct gbaudio_codec_info *gbcodec) { int ret, i; - struct gbaudio_codec_info *gbcodec; + struct device *dev = gbcodec->dev; + struct gb_connection *connection = gbcodec->mgmt_connection; + /* + * FIXME: malloc for topology happens via audio_gb driver + * should be done within codec driver itself + */ struct gb_audio_topology *topology; - struct gb_audio_manager_module_descriptor desc; - struct device *dev = &connection->bundle->dev; - int dev_id = connection->intf->interface_id; - - dev_dbg(dev, "Add device:%d:%s\n", dev_id, dev_name(dev)); - /* get gbcodec data */ - gbcodec = gbaudio_get_codec(dev, dev_id); - if (!gbcodec) - return -ENOMEM; - - gbcodec->mgmt_connection = connection; ret = gb_connection_enable(connection); - if (ret) - goto base_error; + if (ret) { + dev_err(dev, "%d: Error while enabling mgmt connection\n", ret); + return ret; + } + gbcodec->dev_id = connection->intf->interface_id; /* fetch topology data */ ret = gb_audio_gb_get_topology(connection, &topology); if (ret) { - dev_err(gbcodec->dev, - "%d:Error while fetching topology\n", ret); - goto err_connection_disable; + dev_err(dev, "%d:Error while fetching topology\n", ret); + goto tplg_fetch_err; } /* process topology data */ @@ -652,7 +552,7 @@ static int gbaudio_codec_probe(struct gb_connection *connection) if (ret) { dev_err(dev, "%d:Error while parsing topology data\n", ret); - goto topology_error; + goto tplg_parse_err; } gbcodec->topology = topology; @@ -673,100 +573,39 @@ static int gbaudio_codec_probe(struct gb_connection *connection) gbcodec->dais, 1); if (ret) { dev_err(dev, "%d:Failed to register codec\n", ret); - goto parse_error; + goto codec_reg_err; } /* update DAI links in response to this codec */ ret = gbaudio_add_dailinks(gbcodec); if (ret) { dev_err(dev, "%d: Failed to add DAI links\n", ret); - goto codec_reg_error; + goto add_dailink_err; } - /* set registered flag */ - mutex_lock(&gbcodec->lock); - gbcodec->codec_registered = 1; - - /* codec cleanup related */ - atomic_set(&gbcodec->users, 0); - - /* inform above layer for uevent */ - if (!gbcodec->set_uevent && - (gbcodec->dai_added == gbcodec->num_dais)) { - dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); - /* prepare for the audio manager */ - strlcpy(desc.name, gbcodec->name, - GB_AUDIO_MANAGER_MODULE_NAME_LEN); /* todo */ - desc.slot = 1; /* todo */ - desc.vid = 2; /* todo */ - desc.pid = 3; /* todo */ - desc.cport = gbcodec->dev_id; - desc.devices = 0x2; /* todo */ - gbcodec->manager_id = gb_audio_manager_add(&desc); - gbcodec->set_uevent = 1; - } - mutex_unlock(&gbcodec->lock); - - return ret; + return 0; -codec_reg_error: +add_dailink_err: snd_soc_unregister_codec(dev); - dev->driver = NULL; -parse_error: +codec_reg_err: gbaudio_tplg_release(gbcodec); gbcodec->topology = NULL; -topology_error: +tplg_parse_err: kfree(topology); -err_connection_disable: - gb_connection_disable(connection); -base_error: - gbcodec->mgmt_connection = NULL; - gbaudio_free_codec(dev, gbcodec); +tplg_fetch_err: + gb_connection_disable(gbcodec->mgmt_connection); return ret; } -static void gbaudio_codec_remove(struct gb_connection *connection) +static void gbaudio_unregister_codec(struct gbaudio_codec_info *gbcodec) { - struct gbaudio_codec_info *gbcodec; - struct device *dev = &connection->bundle->dev; - int dev_id = connection->intf->interface_id; - - dev_dbg(dev, "Remove device:%d:%s\n", dev_id, dev_name(dev)); - - /* get gbcodec data */ - gbcodec = gbaudio_find_codec(dev, dev_id); - if (!gbcodec) - return; - - /* inform uevent to above layers */ - mutex_lock(&gbcodec->lock); - if (gbcodec->set_uevent) { - /* notify the audio manager */ - dev_dbg(dev, "Inform set_event:%d to above layer\n", 0); - gb_audio_manager_remove(gbcodec->manager_id); - gbcodec->set_uevent = 0; - } - mutex_unlock(&gbcodec->lock); - - if (atomic_read(&gbcodec->users)) { - dev_err(dev, "Cleanup Error: BE stream not yet closed\n"); - gb_audio_cleanup(gbcodec); - } - + gb_audio_cleanup(gbcodec); msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink, 1); - gbaudio_remove_dailinks(gbcodec); - - snd_soc_unregister_codec(dev); - dev->driver = NULL; + snd_soc_unregister_codec(gbcodec->dev); gbaudio_tplg_release(gbcodec); kfree(gbcodec->topology); - gb_connection_disable(connection); - gbcodec->mgmt_connection = NULL; - mutex_lock(&gbcodec->lock); - gbcodec->codec_registered = 0; - mutex_unlock(&gbcodec->lock); - gbaudio_free_codec(dev, gbcodec); + gb_connection_disable(gbcodec->mgmt_connection); } static int gbaudio_codec_request_handler(struct gb_operation *op) @@ -781,93 +620,6 @@ static int gbaudio_codec_request_handler(struct gb_operation *op) return 0; } -static int gbaudio_dai_probe(struct gb_connection *connection) -{ - struct gbaudio_dai *dai; - struct device *dev = &connection->bundle->dev; - int dev_id = connection->intf->interface_id; - struct gbaudio_codec_info *gbcodec; - struct gb_audio_manager_module_descriptor desc; - int ret; - - dev_dbg(dev, "Add DAI device:%d:%s\n", dev_id, dev_name(dev)); - - /* get gbcodec data */ - gbcodec = gbaudio_get_codec(dev, dev_id); - if (!gbcodec) - return -ENOMEM; - - ret = gb_connection_enable(connection); - if (ret) - goto err_free_codec; - - /* add/update dai_list*/ - dai = gbaudio_add_dai(gbcodec, connection->intf_cport_id, connection, - NULL); - if (!dai) { - ret = -ENOMEM; - goto err_connection_disable; - } - - /* update dai_added count */ - mutex_lock(&gbcodec->lock); - gbcodec->dai_added++; - - /* inform above layer for uevent */ - if (!gbcodec->set_uevent && gbcodec->codec_registered && - (gbcodec->dai_added == gbcodec->num_dais)) { - /* prepare for the audio manager */ - dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); - strlcpy(desc.name, gbcodec->name, - GB_AUDIO_MANAGER_MODULE_NAME_LEN); /* todo */ - desc.slot = 1; /* todo */ - desc.vid = 2; /* todo */ - desc.pid = 3; /* todo */ - desc.cport = gbcodec->dev_id; - desc.devices = 0x2; /* todo */ - gbcodec->manager_id = gb_audio_manager_add(&desc); - gbcodec->set_uevent = 1; - } - mutex_unlock(&gbcodec->lock); - - return 0; - -err_connection_disable: - gb_connection_disable(connection); -err_free_codec: - gbaudio_free_codec(dev, gbcodec); - return ret; -} - -static void gbaudio_dai_remove(struct gb_connection *connection) -{ - struct device *dev = &connection->bundle->dev; - int dev_id = connection->intf->interface_id; - struct gbaudio_codec_info *gbcodec; - - dev_dbg(dev, "Remove DAI device:%d:%s\n", dev_id, dev_name(dev)); - - /* get gbcodec data */ - gbcodec = gbaudio_find_codec(dev, dev_id); - if (!gbcodec) - return; - - /* inform uevent to above layers */ - mutex_lock(&gbcodec->lock); - if (gbcodec->set_uevent) { - /* notify the audio manager */ - dev_dbg(dev, "Inform set_event:%d to above layer\n", 0); - gb_audio_manager_remove(gbcodec->manager_id); - gbcodec->set_uevent = 0; - } - /* update dai_added count */ - gbcodec->dai_added--; - mutex_unlock(&gbcodec->lock); - - gb_connection_disable(connection); - gbaudio_free_codec(dev, gbcodec); -} - static int gbaudio_dai_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -877,14 +629,14 @@ static int gbaudio_dai_request_handler(struct gb_operation *op) return 0; } -static int gb_audio_add_mgmt_connection(struct gb_audio *audio, +static int gb_audio_add_mgmt_connection(struct gbaudio_codec_info *gbcodec, struct greybus_descriptor_cport *cport_desc, struct gb_bundle *bundle) { struct gb_connection *connection; /* Management Cport */ - if (audio->mgmt_connection) { + if (gbcodec->mgmt_connection) { dev_err(&bundle->dev, "Can't have multiple Management connections\n"); return -ENODEV; @@ -895,74 +647,99 @@ static int gb_audio_add_mgmt_connection(struct gb_audio *audio, if (IS_ERR(connection)) return PTR_ERR(connection); - connection->private = audio; - audio->mgmt_connection = connection; + connection->private = gbcodec; + gbcodec->mgmt_connection = connection; return 0; } -static int gb_audio_add_data_connection(struct gb_audio *audio, +static int gb_audio_add_data_connection(struct gbaudio_codec_info *gbcodec, struct greybus_descriptor_cport *cport_desc, - struct gb_bundle *bundle, int index) + struct gb_bundle *bundle) { struct gb_connection *connection; + struct gbaudio_dai *dai; + + dai = devm_kzalloc(gbcodec->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) { + dev_err(gbcodec->dev, "DAI Malloc failure\n"); + return -ENOMEM; + } connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), gbaudio_dai_request_handler); - if (IS_ERR(connection)) + if (IS_ERR(connection)) { + devm_kfree(gbcodec->dev, dai); return PTR_ERR(connection); + } - connection->private = audio; - audio->data_connection[index] = connection; + connection->private = gbcodec; + atomic_set(&dai->users, 0); + dai->data_cport = connection->intf_cport_id; + dai->connection = connection; + list_add(&dai->list, &gbcodec->dai_list); return 0; } +/* + * This is the basic hook get things initialized and registered w/ gb + */ static int gb_audio_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { + struct device *dev = &bundle->dev; + struct gbaudio_codec_info *gbcodec; struct greybus_descriptor_cport *cport_desc; - struct gb_audio *audio; + struct gb_audio_manager_module_descriptor desc; + struct gbaudio_dai *dai, *_dai; int ret, i; - int count = bundle->num_cports - 1; /* There should be at least one Management and one Data cport */ if (bundle->num_cports < 2) return -ENODEV; + mutex_lock(&gb_codec_list_lock); /* * There can be only one Management connection and any number of data * connections. */ - audio = kzalloc(sizeof(*audio) + - count * sizeof(*audio->data_connection), GFP_KERNEL); - if (!audio) + gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL); + if (!gbcodec) { + mutex_unlock(&gb_codec_list_lock); return -ENOMEM; + } - audio->num_data_connections = count; - greybus_set_drvdata(bundle, audio); + gbcodec->num_data_connections = bundle->num_cports - 1; + mutex_init(&gbcodec->lock); + INIT_LIST_HEAD(&gbcodec->dai_list); + INIT_LIST_HEAD(&gbcodec->widget_list); + INIT_LIST_HEAD(&gbcodec->codec_ctl_list); + INIT_LIST_HEAD(&gbcodec->widget_ctl_list); + gbcodec->dev = dev; + snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name, + dev_name(dev)); + greybus_set_drvdata(bundle, gbcodec); /* Create all connections */ - for (count = 0, i = 0; i < bundle->num_cports; i++) { + for (i = 0; i < bundle->num_cports; i++) { cport_desc = &bundle->cport_desc[i]; switch (cport_desc->protocol_id) { case GREYBUS_PROTOCOL_AUDIO_MGMT: - ret = gb_audio_add_mgmt_connection(audio, cport_desc, + ret = gb_audio_add_mgmt_connection(gbcodec, cport_desc, bundle); if (ret) goto destroy_connections; break; case GREYBUS_PROTOCOL_AUDIO_DATA: - ret = gb_audio_add_data_connection(audio, cport_desc, - bundle, count); + ret = gb_audio_add_data_connection(gbcodec, cport_desc, + bundle); if (ret) goto destroy_connections; - - count++; break; default: - dev_err(&bundle->dev, "Unsupported protocol: 0x%02x\n", + dev_err(dev, "Unsupported protocol: 0x%02x\n", cport_desc->protocol_id); ret = -ENODEV; goto destroy_connections; @@ -970,57 +747,88 @@ static int gb_audio_probe(struct gb_bundle *bundle, } /* There must be a management cport */ - if (!audio->mgmt_connection) { + if (!gbcodec->mgmt_connection) { ret = -EINVAL; - dev_err(&bundle->dev, "Missing management connection\n"); + dev_err(dev, "Missing management connection\n"); goto destroy_connections; } /* Initialize management connection */ - ret = gbaudio_codec_probe(audio->mgmt_connection); + ret = gbaudio_register_codec(gbcodec); if (ret) goto destroy_connections; /* Initialize data connections */ - for (i = 0; i < audio->num_data_connections; i++) { - ret = gbaudio_dai_probe(audio->data_connection[i]); + list_for_each_entry(dai, &gbcodec->dai_list, list) { + ret = gb_connection_enable(dai->connection); if (ret) goto remove_dai; } + /* inform above layer for uevent */ + dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); + /* prepare for the audio manager */ + strlcpy(desc.name, gbcodec->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN); + desc.slot = 1; /* todo */ + desc.vid = 2; /* todo */ + desc.pid = 3; /* todo */ + desc.cport = gbcodec->dev_id; + desc.devices = 0x2; /* todo */ + gbcodec->manager_id = gb_audio_manager_add(&desc); + + list_add(&gbcodec->list, &gb_codec_list); + dev_dbg(dev, "Add GB Audio device:%s\n", gbcodec->name); + mutex_unlock(&gb_codec_list_lock); + return 0; remove_dai: - while (i--) - gbaudio_dai_remove(audio->data_connection[i]); + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) + gb_connection_disable(dai->connection); - gbaudio_codec_remove(audio->mgmt_connection); + gbaudio_unregister_codec(gbcodec); destroy_connections: - while (count--) - gb_connection_destroy(audio->data_connection[count]); + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { + gb_connection_destroy(dai->connection); + list_del(&dai->list); + devm_kfree(dev, dai); + } - if (audio->mgmt_connection) - gb_connection_destroy(audio->mgmt_connection); + if (gbcodec->mgmt_connection) + gb_connection_destroy(gbcodec->mgmt_connection); - kfree(audio); + devm_kfree(dev, gbcodec); + mutex_unlock(&gb_codec_list_lock); return ret; } static void gb_audio_disconnect(struct gb_bundle *bundle) { - struct gb_audio *audio = greybus_get_drvdata(bundle); - int i; + struct gbaudio_codec_info *gbcodec = greybus_get_drvdata(bundle); + struct gbaudio_dai *dai, *_dai; - for (i = audio->num_data_connections - 1; i >= 0; i--) { - gbaudio_dai_remove(audio->data_connection[i]); - gb_connection_destroy(audio->data_connection[i]); - } + mutex_lock(&gb_codec_list_lock); + list_del(&gbcodec->list); + /* inform uevent to above layers */ + gb_audio_manager_remove(gbcodec->manager_id); - gbaudio_codec_remove(audio->mgmt_connection); - gb_connection_destroy(audio->mgmt_connection); + mutex_lock(&gbcodec->lock); + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) + gb_connection_disable(dai->connection); + gbaudio_unregister_codec(gbcodec); - kfree(audio); + list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { + gb_connection_destroy(dai->connection); + list_del(&dai->list); + devm_kfree(gbcodec->dev, dai); + } + gb_connection_destroy(gbcodec->mgmt_connection); + gbcodec->mgmt_connection = NULL; + mutex_unlock(&gbcodec->lock); + + devm_kfree(&bundle->dev, gbcodec); + mutex_unlock(&gb_codec_list_lock); } static const struct greybus_bundle_id gb_audio_id_table[] = { diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index bba48a59bf4d..56110913b70e 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -84,6 +84,8 @@ struct gbaudio_control { struct gbaudio_dai { __le16 data_cport; char name[NAME_SIZE]; + /* DAI users */ + atomic_t users; struct gb_connection *connection; struct list_head list; }; @@ -116,8 +118,9 @@ struct gbaudio_codec_info { char *dailink_name[MAX_DAIS]; int num_dai_links; - /* topology related */ struct gb_connection *mgmt_connection; + size_t num_data_connections; + /* topology related */ int num_dais; int num_kcontrols; int num_dapm_widgets; @@ -131,9 +134,6 @@ struct gbaudio_codec_info { struct snd_soc_dapm_route *routes; struct snd_soc_dai_driver *dais; - /* codec users */ - atomic_t users; - /* lists */ struct list_head dai_list; struct list_head widget_list; @@ -142,17 +142,8 @@ struct gbaudio_codec_info { struct mutex lock; }; -struct gb_audio { - struct gb_connection *mgmt_connection; - size_t num_data_connections; - struct gbaudio_codec_info *gbcodec; - struct gb_connection *data_connection[0]; -}; - -struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec, - int data_cport, - struct gb_connection *connection, - const char *name); +struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, + int data_cport, const char *name); int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, struct gb_audio_topology *tplg_data); void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec); diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 90d2148392ef..5fab393130a5 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -92,8 +92,7 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_info *info; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gbcodec = audio->gbcodec; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -139,8 +138,7 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -187,8 +185,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -282,8 +279,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -317,8 +313,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gb = audio->gbcodec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -524,8 +519,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, int wid; int ret; struct snd_soc_codec *codec = w->codec; - struct gb_audio *audio = snd_soc_codec_get_drvdata(codec); - struct gbaudio_codec_info *gbcodec = audio->gbcodec; + struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); @@ -819,9 +813,9 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, int i, ret; struct snd_soc_dai_driver *gb_dais; struct gb_audio_dai *curr; - struct gbaudio_dai *dai, *_dai; size_t size; char dai_name[NAME_SIZE]; + struct gbaudio_dai *dai; size = sizeof(struct snd_soc_dai_driver) * gbcodec->num_dais; gb_dais = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); @@ -839,10 +833,10 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, /* append dev_id to dai_name */ snprintf(dai_name, NAME_SIZE, "%s.%d", curr->name, gbcodec->dev_id); - dai = gbaudio_add_dai(gbcodec, curr->data_cport, NULL, - dai_name); + dai = gbaudio_find_dai(gbcodec, curr->data_cport, NULL); if (!dai) goto error; + strlcpy(dai->name, dai_name, NAME_SIZE); dev_dbg(gbcodec->dev, "%s:DAI added\n", dai->name); gb_dais[i].name = dai->name; curr++; @@ -852,10 +846,6 @@ static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, return 0; error: - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { - list_del(&dai->list); - devm_kfree(gbcodec->dev, dai); - } devm_kfree(gbcodec->dev, gb_dais); return ret; } @@ -948,68 +938,6 @@ static int gbaudio_tplg_process_header(struct gbaudio_codec_info *gbcodec, return 0; } -static struct gbaudio_dai *gbaudio_allocate_dai(struct gbaudio_codec_info *gb, - int data_cport, - struct gb_connection *connection, - const char *name) -{ - struct gbaudio_dai *dai; - - mutex_lock(&gb->lock); - dai = devm_kzalloc(gb->dev, sizeof(*dai), GFP_KERNEL); - if (!dai) { - dev_err(gb->dev, "%s:DAI Malloc failure\n", name); - mutex_unlock(&gb->lock); - return NULL; - } - - dai->data_cport = data_cport; - dai->connection = connection; - - /* update name */ - if (name) - strlcpy(dai->name, name, NAME_SIZE); - list_add(&dai->list, &gb->dai_list); - dev_dbg(gb->dev, "%d:%s: DAI added\n", data_cport, dai->name); - mutex_unlock(&gb->lock); - - return dai; -} - -struct gbaudio_dai *gbaudio_add_dai(struct gbaudio_codec_info *gbcodec, - int data_cport, - struct gb_connection *connection, - const char *name) -{ - struct gbaudio_dai *dai, *_dai; - - /* FIXME need to take care for multiple DAIs */ - mutex_lock(&gbcodec->lock); - if (list_empty(&gbcodec->dai_list)) { - mutex_unlock(&gbcodec->lock); - return gbaudio_allocate_dai(gbcodec, data_cport, connection, - name); - } - - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { - if (dai->data_cport == data_cport) { - if (connection) - dai->connection = connection; - - if (name) - strlcpy(dai->name, name, NAME_SIZE); - dev_dbg(gbcodec->dev, "%d:%s: DAI updated\n", - data_cport, dai->name); - mutex_unlock(&gbcodec->lock); - return dai; - } - } - - dev_err(gbcodec->dev, "%s:DAI not found\n", name); - mutex_unlock(&gbcodec->lock); - return NULL; -} - int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, struct gb_audio_topology *tplg_data) { @@ -1074,7 +1002,6 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec) { - struct gbaudio_dai *dai, *_dai; struct gbaudio_control *control, *_control; struct gbaudio_widget *widget, *_widget; @@ -1109,12 +1036,4 @@ void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec) /* release routes */ if (gbcodec->routes) devm_kfree(gbcodec->dev, gbcodec->routes); - - /* release DAIs */ - mutex_lock(&gbcodec->lock); - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { - list_del(&dai->list); - devm_kfree(gbcodec->dev, dai); - } - mutex_unlock(&gbcodec->lock); } -- cgit v1.2.3-59-g8ed1b From 3994e0b139c709047cdeb44b6c28cfb39f89f3f2 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Thu, 28 Jan 2016 21:15:40 +0530 Subject: greybus: audio: use variable 'is_connected' to maintain module state there is race condition between _disconnect() request & stop_trigger() in case of abrupt module removal. And sometimes this can lead to deadlock while acquiring codec_info->lock. To avoid such situation, atomic variable is used to maintain codec connected state. During dai operations (trigger, shutdown, etc.), 'is_connected' variable is validated to avoid unnecessary lock acquire in case module already removed. Signed-off-by: Vaibhav Agarwal Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 17 +++++++++++++++++ drivers/staging/greybus/audio_codec.h | 10 +++++++++- drivers/staging/greybus/audio_topology.c | 12 ++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index ad28c10fa154..c05ab4fb8754 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -44,6 +44,9 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -77,6 +80,9 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -129,6 +135,9 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -204,6 +213,9 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -278,6 +290,9 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -776,6 +791,7 @@ static int gb_audio_probe(struct gb_bundle *bundle, desc.devices = 0x2; /* todo */ gbcodec->manager_id = gb_audio_manager_add(&desc); + atomic_set(&gbcodec->is_connected, 1); list_add(&gbcodec->list, &gb_codec_list); dev_dbg(dev, "Add GB Audio device:%s\n", gbcodec->name); mutex_unlock(&gb_codec_list_lock); @@ -809,6 +825,7 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) struct gbaudio_dai *dai, *_dai; mutex_lock(&gb_codec_list_lock); + atomic_set(&gbcodec->is_connected, 0); list_del(&gbcodec->list); /* inform uevent to above layers */ gb_audio_manager_remove(gbcodec->manager_id); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 56110913b70e..4c19bd884488 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -108,6 +108,15 @@ struct gbaudio_codec_info { int manager_id; char name[NAME_SIZE]; + /* + * there can be a rece condition between gb_audio_disconnect() + * and dai->trigger from above ASoC layer. + * To avoid any deadlock over codec_info->lock, atomic variable + * is used. + */ + atomic_t is_connected; + struct mutex lock; + /* soc related data */ struct snd_soc_codec *codec; struct device *dev; @@ -139,7 +148,6 @@ struct gbaudio_codec_info { struct list_head widget_list; struct list_head codec_ctl_list; struct list_head widget_ctl_list; - struct mutex lock; }; struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 5fab393130a5..1651c14c87ba 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -140,6 +140,9 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -187,6 +190,9 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -281,6 +287,9 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = widget->codec; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -315,6 +324,9 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = widget->codec; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; -- cgit v1.2.3-59-g8ed1b From ec413566e819ad19be929ab38d2b5911cbf096c2 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 3 Feb 2016 12:53:40 +0100 Subject: greybus: audio_manager: add missing header There's a definition missing in audio_manager causing the kernel build to fail: CC [M] ./greybus/audio_manager.o ./greybus/audio_manager.c:22:8: warning: type defaults to 'int' in declaration of 'DEFINE_IDA' [-Wimplicit-int] error, forbidden warning: audio_manager.c:22 ./kernel/scripts/Makefile.build:308: recipe for target './greybus/audio_manager.o' failed Including linux/idr.h fixes the issue. Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_manager.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c index 05af441be162..bc5be4907c81 100644 --- a/drivers/staging/greybus/audio_manager.c +++ b/drivers/staging/greybus/audio_manager.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "audio_manager.h" #include "audio_manager_private.h" -- cgit v1.2.3-59-g8ed1b From d1a9c0560a4ffc05c31a042dff380ddf1ea721b0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Feb 2016 21:31:19 -0800 Subject: greybus: fix sparse warning in manifest.c The cport id field is a le16, so treat it as such when comparing it to something else. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 5ca36c9001be..4e3c4cab0fc4 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -256,7 +256,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) */ list_for_each_entry(tmp, &list, links) { desc_cport = tmp->data; - if (cport_id == desc_cport->id) { + if (cport_id == le16_to_cpu(desc_cport->id)) { dev_err(&bundle->dev, "duplicate CPort %u found\n", cport_id); -- cgit v1.2.3-59-g8ed1b From 6084653541ac5fb619c4338e6fa6a9d5d4a51713 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Fri, 5 Feb 2016 01:10:56 +0530 Subject: greybus: arche-apb-ctrl: Remove extra delay in APB reset With synchronization between SVC <=> AP over wake/detect line to bring APB's out of reset, we do not need any extra delays now. So remove it. Testing Done: Tested for 10 iterations on EVT1 Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index f2bad8df0cf7..4e5ce37ea37e 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -54,7 +54,6 @@ struct arche_apb_ctrl_drvdata { static inline void deassert_reset(unsigned int gpio) { gpio_set_value(gpio, 1); - msleep(500); } static inline void assert_reset(unsigned int gpio) -- cgit v1.2.3-59-g8ed1b From f121d79d8acb0852f42b42b111967a3ef265362b Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 5 Feb 2016 09:35:32 +0100 Subject: greybus: camera: Reset link speed on failed config Improve the management of unipro power mode changes in response to a configure_stream operation. When sending a "test only" request to camera module, do not change power mode to HS-G2 as no frame will be actually transmitted. When receiveing an "adjusted" configuration response, reset power mode to PWM-G1. Signed-off-by: Jacopo Mondi Reviewed-by: Gjorgji Rosikopulos Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index c7359088e259..90eef23bf654 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -150,7 +150,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, * to the camera module, to assure unipro network speed is set * before CSI interfaces gets configured */ - if (nstreams) { + if (nstreams && !(*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_FAST_MODE, 2, 2, @@ -168,7 +168,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, GB_SVC_PWRM_TXTERMINATION, 0); if (ret < 0) goto done; - } else { + } else if (nstreams == 0) { ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_SLOW_AUTO_MODE, @@ -228,8 +228,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto set_unipro_slow_mode; } - *flags = resp->flags; - for (i = 0; i < nstreams; ++i) { struct gb_camera_stream_config_response *cfg = &resp->config[i]; @@ -248,10 +246,16 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, } } + if (nstreams && resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) { + *flags = resp->flags; + *num_streams = resp->num_streams; + goto set_unipro_slow_mode; + } + memset(&csi_cfg, 0, sizeof(csi_cfg)); /* Configure the CSI transmitter. Hardcode the parameters for now. */ - if (nstreams && !(resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED)) { + if (nstreams) { csi_cfg.csi_id = 1; csi_cfg.clock_mode = 0; csi_cfg.num_lanes = 4; @@ -259,7 +263,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, ret = gb_hd_output(gcam->connection->hd, &csi_cfg, sizeof(csi_cfg), GB_APB_REQUEST_CSI_TX_CONTROL, false); - } else if (nstreams == 0) { + } else { csi_cfg.csi_id = 1; ret = gb_hd_output(gcam->connection->hd, &csi_cfg, sizeof(csi_cfg), @@ -270,6 +274,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, gcam_err(gcam, "failed to %s the CSI transmitter\n", nstreams ? "start" : "stop"); + *flags = resp->flags; *num_streams = resp->num_streams; ret = 0; -- cgit v1.2.3-59-g8ed1b From db81b76970a2e9f3ffa1ca66e7761f9f4b84efc6 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 5 Feb 2016 09:35:33 +0100 Subject: greybus: camera: Add missing return value Add missing return value assignement when changing unipro power mode to PWM-G1. Signed-off-by: Jacopo Mondi Reviewed-by: Gjorgji Rosikopulos Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 90eef23bf654..3f31a2fd7e5e 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -296,7 +296,7 @@ set_unipro_slow_mode: goto done; } - gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, + ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 1, 2, @@ -307,7 +307,6 @@ set_unipro_slow_mode: gcam_err(gcam, "can't take AP link to PWM-G1 auto: %d\n", ret); - done: kfree(req); kfree(resp); -- cgit v1.2.3-59-g8ed1b From 9d15134d067ecb52bf02136234fbd1d09e1706d8 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 4 Feb 2016 14:00:36 +0000 Subject: greybus: power_supply: rework get descriptors Rework the get property descriptors function to fix a memory handling error for the response structure. This could corrupt the stack and throw nonalignment PC or SP error: Internal error: SP or PC abort: 8a000000 1 PREEMPT SMP Modules linked in: gb_power_supply(O) gb_arche(O) gb_camera(O) gb_es2(O) gb_vibrator(O) gb_raw(O) g] CPU: 3 PID: 51 Comm: kworker/u16:2 Tainted: G W O 3.10.73-g8a6af60-00118-g599a5c1 #1 Workqueue: greybus1:svc gb_svc_connection_destroy [greybus] task: ffffffc0ba249580 ti: ffffffc0ba294000 task.ti: ffffffc0ba294000 PC is at gb_power_supply_connection_init+0x81/0x1dc [gb_power_supply] LR is at gb_power_supply_connection_init+0x81/0x1dc [gb_power_supply] pc : [] lr : [] pstate: 80000145 sp : ffffffc0ba297a00 x29: 32002e002a001100 x28: ffffffc042cb2c80 To fix this, allocate firstly the operation and handle request and response using operation payload. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 57 ++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 2dc193a081b0..37bea9c0cea8 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -249,39 +249,53 @@ static int gb_power_supply_description_get(struct gb_power_supply *gbpsy) static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy) { struct gb_connection *connection = get_conn_from_psy(gbpsy); - struct gb_power_supply_get_property_descriptors_request req; - struct gb_power_supply_get_property_descriptors_response resp; + struct gb_power_supply_get_property_descriptors_request *req; + struct gb_power_supply_get_property_descriptors_response *resp; + struct gb_operation *op; + u8 props_count = gbpsy->properties_count; int ret; int i; - if (gbpsy->properties_count == 0) + if (props_count == 0) return 0; - req.psy_id = gbpsy->id; + op = gb_operation_create(connection, + GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS, + sizeof(req), sizeof(*resp) + props_count * + sizeof(struct gb_power_supply_props_desc), + GFP_KERNEL); + if (!op) + return -ENOMEM; - ret = gb_operation_sync(connection, - GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS, - &req, sizeof(req), &resp, - sizeof(resp) + gbpsy->properties_count * - sizeof(struct gb_power_supply_props_desc)); + req = op->request->payload; + req->psy_id = gbpsy->id; + + ret = gb_operation_request_send_sync(op); if (ret < 0) - return ret; + goto out_put_operation; + + resp = op->response->payload; gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props), GFP_KERNEL); - if (!gbpsy->props) - return -ENOMEM; + if (!gbpsy->props) { + ret = -ENOMEM; + goto out_put_operation; + } + + gbpsy->props_raw = kcalloc(gbpsy->properties_count, + sizeof(*gbpsy->props_raw), GFP_KERNEL); + if (!gbpsy->props_raw) { + ret = -ENOMEM; + goto out_put_operation; + } - gbpsy->props_raw = kzalloc(gbpsy->properties_count * - sizeof(*gbpsy->props_raw), GFP_KERNEL); - if (!gbpsy->props_raw) - return -ENOMEM; /* Store available properties */ for (i = 0; i < gbpsy->properties_count; i++) { - gbpsy->props[i].prop = resp.props[i].property; - gbpsy->props_raw[i] = resp.props[i].property; - if (resp.props[i].is_writeable) + gbpsy->props[i].prop = resp->props[i].property; + gbpsy->props_raw[i] = resp->props[i].property; + if (resp->props[i].is_writeable) gbpsy->props[i].is_writeable = true; } @@ -291,7 +305,10 @@ static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy) */ _gb_power_supply_append_props(gbpsy); - return 0; +out_put_operation: + gb_operation_put(op); + + return ret; } static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy, -- cgit v1.2.3-59-g8ed1b From 9d4bb6c9183f1283158bbb00ebf65ec4cf18ee33 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Fri, 5 Feb 2016 13:50:37 +0000 Subject: greybus: uart: fix double free of tty port When inserting and removing a module with a UART protocol defined a double free of the tty_port would happen and that would generate a lot of kernel oops in different places related to memory corruption. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 1ba8476ce982..46cce8c82412 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -701,7 +701,6 @@ static void gb_uart_connection_exit(struct gb_connection *connection) /* FIXME - free transmit / receive buffers */ - tty_port_put(&gb_tty->port); tty_port_destroy(&gb_tty->port); kfree(gb_tty->buffer); kfree(gb_tty); -- cgit v1.2.3-59-g8ed1b From 0273038df61349868d368dd9254eb629425a3378 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Tue, 2 Feb 2016 14:23:16 +0000 Subject: greybus: spi: add device_type field to device config Add device_type field in device config operation to get the type of device and try to expose less the kernel internal over greybus. This include the spidev, spi-nor will fetch the correct nor id over jede and a modalias that will have the previous behavior (name will set the driver to be loaded). As at it, fix a trivial error path and return immediately. Tested: using gbsim and confirming that a spidev and mtd device were created. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 ++++ drivers/staging/greybus/kernel_ver.h | 8 ++++++++ drivers/staging/greybus/spi.c | 18 ++++++++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 89db93282cac..cd64ac84dad2 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -737,6 +737,10 @@ struct gb_spi_device_config_response { __le16 mode; __u8 bits_per_word; __le32 max_speed_hz; + __u8 device_type; +#define GB_SPI_SPI_DEV 0x00 +#define GB_SPI_SPI_NOR 0x01 +#define GB_SPI_SPI_MODALIAS 0x02 __u8 name[32]; } __packed; diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 1f8d6a1bb6da..18bf8dff0f86 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -305,4 +305,12 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev) #define PSY_HAVE_PUT #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +#define SPI_DEV_MODALIAS "spidev" +#define SPI_NOR_MODALIAS "spi-nor" +#else +#define SPI_DEV_MODALIAS "spidev" +#define SPI_NOR_MODALIAS "m25p80" +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index ad4a1d62587d..c00492cc632e 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -285,6 +285,7 @@ static int gb_spi_setup_device(struct gb_spi *spi, u8 cs) struct spi_board_info spi_board = { {0} }; struct spi_device *spidev; int ret; + u8 dev_type; request.chip_select = cs; @@ -294,7 +295,20 @@ static int gb_spi_setup_device(struct gb_spi *spi, u8 cs) if (ret < 0) return ret; - memcpy(spi_board.modalias, response.name, sizeof(spi_board.modalias)); + dev_type = response.device_type; + + if (dev_type == GB_SPI_SPI_DEV) + strlcpy(spi_board.modalias, SPI_DEV_MODALIAS, + sizeof(spi_board.modalias)); + else if (dev_type == GB_SPI_SPI_NOR) + strlcpy(spi_board.modalias, SPI_NOR_MODALIAS, + sizeof(spi_board.modalias)); + else if (dev_type == GB_SPI_SPI_MODALIAS) + memcpy(spi_board.modalias, response.name, + sizeof(spi_board.modalias)); + else + return -EINVAL; + spi_board.mode = le16_to_cpu(response.mode); spi_board.bus_num = master->bus_num; spi_board.chip_select = cs; @@ -302,7 +316,7 @@ static int gb_spi_setup_device(struct gb_spi *spi, u8 cs) spidev = spi_new_device(master, &spi_board); if (!spidev) - ret = -EINVAL; + return -EINVAL; return 0; } -- cgit v1.2.3-59-g8ed1b From f6d6f5bd29f76721d7ac5c64426a1f45203c622a Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Tue, 9 Feb 2016 22:09:16 +0530 Subject: greybus: Revert "arche-apb-ctrl: Remove extra delay in APB reset" We are seeing failures on DB3.1 board, and Axel root-caused it to this commit, so revert it as of now. This reverts commit 942627227684c187b727ba5fb581bc2d886b6708. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 4e5ce37ea37e..f2bad8df0cf7 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -54,6 +54,7 @@ struct arche_apb_ctrl_drvdata { static inline void deassert_reset(unsigned int gpio) { gpio_set_value(gpio, 1); + msleep(500); } static inline void assert_reset(unsigned int gpio) -- cgit v1.2.3-59-g8ed1b From d5a265648aa830383bc2c3e750ffe1bea0cd4309 Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Mon, 8 Feb 2016 17:08:46 -0800 Subject: greybus: interface: clear upper 16-bits of version_id and product_id Current userspace looks through the sysfs interface entries for matching vendor_id and product_id any time an interface is opened by module developers. The upper 16-bits of ES3 vendor_id and product_id contain a reverse mask of the lower 16-bits. This additional information is never used and should be removed so that every consumer of these sysfs entries doesn't have to perform the same bit clearing logic. Signed-off-by: Michael Scott Reviewed-by: Alex Elder Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 9c05d8142d8b..6c815db112b6 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -23,12 +23,33 @@ static DEVICE_ATTR_RO(field) gb_interface_attr(ddbl1_manufacturer_id, "0x%08x"); gb_interface_attr(ddbl1_product_id, "0x%08x"); gb_interface_attr(interface_id, "%u"); -gb_interface_attr(vendor_id, "0x%08x"); -gb_interface_attr(product_id, "0x%08x"); gb_interface_attr(vendor_string, "%s"); gb_interface_attr(product_string, "%s"); gb_interface_attr(serial_number, "0x%016llx"); +static ssize_t vendor_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + + /* clear the upper 16-bits to keep userspace "simple" */ + return scnprintf(buf, PAGE_SIZE, "0x%04x\n", + (0x0000FFFF & intf->vendor_id)); +} +static DEVICE_ATTR_RO(vendor_id); + +static ssize_t product_id_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + + /* clear the upper 16-bits to keep userspace "simple" */ + return scnprintf(buf, PAGE_SIZE, "0x%04x\n", + (0x0000FFFF & intf->product_id)); +} +static DEVICE_ATTR_RO(product_id); + static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) { -- cgit v1.2.3-59-g8ed1b From a0b5542df26b6d2fccb611c05ea1cb266456b60c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 11 Feb 2016 13:52:46 +0100 Subject: greybus: manifest: add interface-device prefix to messages Use dev_err and friends so that we can tell which interface (and module) a manifest-parsing error messages was for. Testing Done: Tested on DB3.5 with the generic bridge firmware on APB2. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 41 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 4e3c4cab0fc4..74e3be997023 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -7,8 +7,6 @@ * Released under the GPLv2 only. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include "greybus.h" static const char *get_descriptor_type_string(u8 type) @@ -107,14 +105,15 @@ static int identify_descriptor(struct gb_interface *intf, size_t expected_size; if (size < sizeof(*desc_header)) { - pr_err("manifest too small (%zu < %zu)\n", + dev_err(&intf->dev, "manifest too small (%zu < %zu)\n", size, sizeof(*desc_header)); return -EINVAL; /* Must at least have header */ } desc_size = le16_to_cpu(desc_header->size); if (desc_size > size) { - pr_err("descriptor too big (%zu > %zu)\n", desc_size, size); + dev_err(&intf->dev, "descriptor too big (%zu > %zu)\n", + desc_size, size); return -EINVAL; } @@ -140,22 +139,23 @@ static int identify_descriptor(struct gb_interface *intf, break; case GREYBUS_TYPE_INVALID: default: - pr_err("invalid descriptor type (%u)\n", desc_header->type); + dev_err(&intf->dev, "invalid descriptor type (%u)\n", + desc_header->type); return -EINVAL; } if (desc_size < expected_size) { - pr_err("%s descriptor too small (%zu < %zu)\n", - get_descriptor_type_string(desc_header->type), - desc_size, expected_size); + dev_err(&intf->dev, "%s descriptor too small (%zu < %zu)\n", + get_descriptor_type_string(desc_header->type), + desc_size, expected_size); return -EINVAL; } /* Descriptor bigger than what we expect */ if (desc_size > expected_size) { - pr_warn("%s descriptor size mismatch (want %zu got %zu)\n", - get_descriptor_type_string(desc_header->type), - expected_size, desc_size); + dev_warn(&intf->dev, "%s descriptor size mismatch (want %zu got %zu)\n", + get_descriptor_type_string(desc_header->type), + expected_size, desc_size); } descriptor = kzalloc(sizeof(*descriptor), GFP_KERNEL); @@ -454,7 +454,8 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) /* we have to have at _least_ the manifest header */ if (size < sizeof(*header)) { - pr_err("short manifest (%zu < %zu)\n", size, sizeof(*header)); + dev_err(&intf->dev, "short manifest (%zu < %zu)\n", + size, sizeof(*header)); return false; } @@ -463,16 +464,16 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) header = &manifest->header; manifest_size = le16_to_cpu(header->size); if (manifest_size != size) { - pr_err("manifest size mismatch (%zu != %u)\n", - size, manifest_size); + dev_err(&intf->dev, "manifest size mismatch (%zu != %u)\n", + size, manifest_size); return false; } /* Validate major/minor number */ if (header->version_major > GREYBUS_VERSION_MAJOR) { - pr_err("manifest version too new (%u.%u > %u.%u)\n", - header->version_major, header->version_minor, - GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); + dev_err(&intf->dev, "manifest version too new (%u.%u > %u.%u)\n", + header->version_major, header->version_minor, + GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); return false; } @@ -498,8 +499,8 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) interface_desc = descriptor; } if (found != 1) { - pr_err("manifest must have 1 interface descriptor (%u found)\n", - found); + dev_err(&intf->dev, "manifest must have 1 interface descriptor (%u found)\n", + found); result = false; goto out; } @@ -512,7 +513,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) * don't know what newer format manifests might leave. */ if (result && !list_empty(&intf->manifest_descs)) - pr_info("excess descriptors in interface manifest\n"); + dev_info(&intf->dev, "excess descriptors in interface manifest\n"); out: release_manifest_descriptors(intf); -- cgit v1.2.3-59-g8ed1b From b427572ebff3abba436a9ae45db73c78fa3ddcf8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 11 Feb 2016 13:52:47 +0100 Subject: greybus: core: add device prefix to error messages Use dev_err and dev_warn where appropriate and remove now unused pr_fmt defines. Testing Done: Tested on DB3.5 with the generic bridge firmware on APB2. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/debugfs.c | 2 -- drivers/staging/greybus/hd.c | 4 +--- drivers/staging/greybus/operation.c | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c index 725565de5d43..a9d4d3da99a0 100644 --- a/drivers/staging/greybus/debugfs.c +++ b/drivers/staging/greybus/debugfs.c @@ -7,8 +7,6 @@ * Released under the GPLv2 only. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include "greybus.h" diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index d1aab29d3da4..147a92d14cfc 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -7,8 +7,6 @@ * Released under the GPLv2 only. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include @@ -55,7 +53,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, * so that we don't have to every time we make them. */ if ((!driver->message_send) || (!driver->message_cancel)) { - pr_err("Must implement all gb_hd_driver callbacks!\n"); + dev_err(parent, "mandatory hd-callbacks missing\n"); return ERR_PTR(-EINVAL); } diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d6b3d1f22b28..bd79f81da020 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -327,7 +327,7 @@ gb_operation_message_alloc(struct gb_host_device *hd, u8 type, size_t message_size = payload_size + sizeof(*header); if (message_size > hd->buffer_size_max) { - pr_warn("requested message size too big (%zu > %zu)\n", + dev_warn(&hd->dev, "requested message size too big (%zu > %zu)\n", message_size, hd->buffer_size_max); return NULL; } -- cgit v1.2.3-59-g8ed1b From 6d6fb2549f23d5d7d3494574b49e35df8e737645 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 11 Feb 2016 13:52:48 +0100 Subject: greybus: i2c: add bundle-device prefix to error messages Replace all pr_err with dev_err so we can tell what device (and driver) a message was for. Testing Done: Compiled Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/i2c.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 5a5af36570c8..ec74e870d76e 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -52,6 +52,7 @@ static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) { + struct device *dev = &gb_i2c_dev->connection->bundle->dev; struct gb_i2c_timeout_request request; int ret; @@ -59,7 +60,7 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_TIMEOUT, &request, sizeof(request), NULL, 0); if (ret) - pr_err("timeout operation failed (%d)\n", ret); + dev_err(dev, "timeout operation failed (%d)\n", ret); else gb_i2c_dev->timeout_msec = msec; @@ -69,6 +70,7 @@ static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, u8 retries) { + struct device *dev = &gb_i2c_dev->connection->bundle->dev; struct gb_i2c_retries_request request; int ret; @@ -76,7 +78,7 @@ static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_RETRIES, &request, sizeof(request), NULL, 0); if (ret) - pr_err("retries operation failed (%d)\n", ret); + dev_err(dev, "retries operation failed (%d)\n", ret); else gb_i2c_dev->retries = retries; @@ -201,6 +203,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, struct i2c_msg *msgs, u32 msg_count) { struct gb_connection *connection = gb_i2c_dev->connection; + struct device *dev = &connection->bundle->dev; struct gb_operation *operation; int ret; @@ -216,7 +219,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, gb_i2c_decode_response(msgs, msg_count, response); ret = msg_count; } else if (!gb_i2c_expected_transfer_error(ret)) { - pr_err("transfer operation failed (%d)\n", ret); + dev_err(dev, "transfer operation failed (%d)\n", ret); } gb_operation_put(operation); -- cgit v1.2.3-59-g8ed1b From 8923c5b59ffd18b8a31b18dd02332fdfe801f7c9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 11 Feb 2016 13:52:49 +0100 Subject: greybus: loopback: add missing pr_fmt Add missing pr_fmt so we can at least tell what module the sole remaining pr_err was from. Testing Done: Tested on DB3.5 with the generic bridge firmware on APB2. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index c35d22733a36..3f32dee1ec93 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -6,6 +6,9 @@ * * Released under the GPLv2 only. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include -- cgit v1.2.3-59-g8ed1b From 5ef545fb8c22ab605f764ea467859572797dbc09 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 11 Feb 2016 13:52:50 +0100 Subject: greybus: spi: add bundle-device prefix to error messages Replace all pr_err with dev_err so we can tell what device (and driver) a message was for. Testing Done: Compiled Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/spi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index c00492cc632e..7689af0f63a9 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -218,7 +218,8 @@ static int gb_spi_transfer_one_message(struct spi_master *master, if (response) gb_spi_decode_response(msg, response); } else { - pr_err("transfer operation failed (%d)\n", ret); + dev_err(&connection->bundle->dev, + "transfer operation failed: %d\n", ret); } gb_operation_put(operation); -- cgit v1.2.3-59-g8ed1b From 8d46ec49819599d33ee5cffd43b19bf21f9472b8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 10 Feb 2016 12:31:03 +0100 Subject: greybus: bundle: remove private data field Remove the private data field from the bundle structure as it is no longer needed. Bundle drivers can use the driver data field in the bundle device. Update the only current user to use the connection private data until it has been converted to a bundle driver. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.h | 1 - drivers/staging/greybus/loopback.c | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 48fb3fd76817..18b125e8169b 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -29,7 +29,6 @@ struct gb_bundle { u8 *state; struct list_head links; /* interface->bundles */ - void *private; }; #define to_gb_bundle(d) container_of(d, struct gb_bundle, dev) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 3f32dee1ec93..66b3fcaca9b0 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -1121,7 +1121,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) gb->file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, gb, &gb_loopback_debugfs_latency_ops); gb->connection = connection; - connection->bundle->private = gb; + connection->private = gb; gb->id = ida_simple_get(&loopback_ida, 0, 0, GFP_KERNEL); if (gb->id < 0) { @@ -1177,7 +1177,6 @@ out_dev: ida_simple_remove(&loopback_ida, gb->id); out_ida: debugfs_remove(gb->file); - connection->bundle->private = NULL; out_kzalloc: kfree(gb); @@ -1186,13 +1185,12 @@ out_kzalloc: static void gb_loopback_connection_exit(struct gb_connection *connection) { - struct gb_loopback *gb = connection->bundle->private; + struct gb_loopback *gb = connection->private; unsigned long flags; if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); - connection->bundle->private = NULL; kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); gb_connection_latency_tag_disable(connection); -- cgit v1.2.3-59-g8ed1b From 397d34152423d2ddbff3e48495ef988cbb07776b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 10 Feb 2016 11:08:29 +0100 Subject: greybus: raw: fix memory leak on disconnect Make sure the class device is freed as well as deregistered on disconnect. Also deregister the class device before character device as the former depends on the latter. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index fa5025dd17a3..a6e795996053 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -203,9 +203,9 @@ static void gb_raw_connection_exit(struct gb_connection *connection) struct raw_data *temp; // FIXME - handle removing a connection when the char device node is open. + device_destroy(raw_class, raw->dev); cdev_del(&raw->cdev); ida_simple_remove(&minors, MINOR(raw->dev)); - device_del(raw->device); mutex_lock(&raw->list_lock); list_for_each_entry_safe(raw_data, temp, &raw->list, entry) { list_del(&raw_data->entry); -- cgit v1.2.3-59-g8ed1b From a821adb47d28e22f96716b0ab8c4a8d5f7d3dadb Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:02 +0530 Subject: greybus: arche-platform: make apb_state common to both platform drivers Make 'enum apb_state' common to both platform drivers, so that both drivers can make use of same state and user will have unified control configuration across devices (SVC, APB1 and APB2) Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 11 +++-------- drivers/staging/greybus/arche_platform.h | 6 ++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index f2bad8df0cf7..42267fa121f5 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -21,11 +21,6 @@ #include #include "arche_platform.h" -enum apb_state { - APB_STATE_OFF, - APB_STATE_ACTIVE, - APB_STATE_STANDBY, -}; struct arche_apb_ctrl_drvdata { /* Control GPIO signals to and from AP <=> AP Bridges */ @@ -36,7 +31,7 @@ struct arche_apb_ctrl_drvdata { int wake_out_gpio; int pwrdn_gpio; - enum apb_state state; + enum arche_platform_state state; struct regulator *vcore; struct regulator *vio; @@ -232,7 +227,7 @@ static void apb_ctrl_cleanup(struct arche_apb_ctrl_drvdata *apb) /* As part of exit, put APB back in reset state */ assert_reset(apb->resetn_gpio); - apb->state = APB_STATE_OFF; + apb->state = ARCHE_PLATFORM_STATE_OFF; /* TODO: May have to send an event to SVC about this exit */ } @@ -262,7 +257,7 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) /* deassert reset to APB : Active-low signal */ deassert_reset(apb->resetn_gpio); - apb->state = APB_STATE_ACTIVE; + apb->state = ARCHE_PLATFORM_STATE_ACTIVE; platform_set_drvdata(pdev, apb); diff --git a/drivers/staging/greybus/arche_platform.h b/drivers/staging/greybus/arche_platform.h index 22a968a9197e..33c4bb8bca93 100644 --- a/drivers/staging/greybus/arche_platform.h +++ b/drivers/staging/greybus/arche_platform.h @@ -10,6 +10,12 @@ #ifndef __ARCHE_PLATFORM_H #define __ARCHE_PLATFORM_H +enum arche_platform_state { + ARCHE_PLATFORM_STATE_OFF, + ARCHE_PLATFORM_STATE_ACTIVE, + ARCHE_PLATFORM_STATE_STANDBY, +}; + int arche_apb_ctrl_probe(struct platform_device *pdev); int arche_apb_ctrl_remove(struct platform_device *pdev); extern const struct dev_pm_ops arche_apb_ctrl_pm_ops; -- cgit v1.2.3-59-g8ed1b From 758ca99de9961036675048e1487713e801b184db Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:03 +0530 Subject: greybus: arche-platform: Introduce coldboot_seq fn This is preparation for support for different operational states (defined by arche_platform_state) in the driver, to enable user/developer to dynamically configure the state. arche_platform_coldboot_seq() fn will be responsible for rebooting SVC device. Testing Done: Tested on EVT1.2 and DB3.5 platform Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 49 ++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 65c8f718dd86..f3674301a2e6 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -104,6 +104,32 @@ static void unexport_gpios(struct arche_platform_drvdata *arche_pdata) gpio_unexport(arche_pdata->svc_sysboot_gpio); } +static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) +{ + int ret; + + dev_info(arche_pdata->dev, "Booting from cold boot state\n"); + + svc_reset_onoff(arche_pdata->svc_reset_gpio, + arche_pdata->is_reset_act_hi); + + gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); + usleep_range(100, 200); + + ret = clk_prepare_enable(arche_pdata->svc_ref_clk); + if (ret) { + dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", + ret); + return ret; + } + + /* bring SVC out of reset */ + svc_reset_onoff(arche_pdata->svc_reset_gpio, + !arche_pdata->is_reset_act_hi); + + return 0; +} + static void arche_platform_cleanup(struct arche_platform_drvdata *arche_pdata) { clk_disable_unprepare(arche_pdata->svc_ref_clk); @@ -185,11 +211,6 @@ static int arche_platform_probe(struct platform_device *pdev) dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); return ret; } - ret = clk_prepare_enable(arche_pdata->svc_ref_clk); - if (ret) { - dev_err(dev, "failed to enable svc_ref_clk: %d\n", ret); - return ret; - } platform_set_drvdata(pdev, arche_pdata); @@ -200,34 +221,32 @@ static int arche_platform_probe(struct platform_device *pdev) if (arche_pdata->wake_detect_gpio < 0) { dev_err(dev, "failed to get wake detect gpio\n"); ret = arche_pdata->wake_detect_gpio; - goto exit; + return ret; } ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); if (ret) { dev_err(dev, "Failed requesting wake_detect gpio %d\n", arche_pdata->wake_detect_gpio); - goto exit; + return ret; } /* deassert wake detect */ gpio_direction_output(arche_pdata->wake_detect_gpio, 0); - /* bring SVC out of reset */ - svc_reset_onoff(arche_pdata->svc_reset_gpio, - !arche_pdata->is_reset_act_hi); - arche_pdata->dev = &pdev->dev; INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); + ret = arche_platform_coldboot_seq(arche_pdata); + if (ret) { + dev_err(dev, "Failed to cold boot svc %d\n", ret); + return ret; + } + export_gpios(arche_pdata); dev_info(dev, "Device registered successfully\n"); return 0; - -exit: - arche_platform_cleanup(arche_pdata); - return ret; } static int arche_remove_child(struct device *dev, void *unused) -- cgit v1.2.3-59-g8ed1b From 49e6e04bb44e4f3bf2c6d3c2c096257da2cb54ff Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 01:15:11 +0530 Subject: greybus: arche-platform: Cancel delayed_work in driver remove fn This is a bug fix, where as part of cleanup, delayed work was not canceled in driver remove function. So fix it. Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index f3674301a2e6..3b7974dad12a 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -262,6 +262,7 @@ static int arche_platform_remove(struct platform_device *pdev) { struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); + cancel_delayed_work_sync(&arche_pdata->delayed_work); device_for_each_child(&pdev->dev, NULL, arche_remove_child); arche_platform_cleanup(arche_pdata); platform_set_drvdata(pdev, NULL); -- cgit v1.2.3-59-g8ed1b From 5993e2bfd848538b1f3e8e3de6412f76e600c69f Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:04 +0530 Subject: greybus: arche-platform: Rename cleanup fn to poweroff_seq In sync with operational states, rename arche_platform_cleanup() fn to arche_platform_poweroff_seq() fn. Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 3b7974dad12a..48ab1eb24d63 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -130,7 +130,7 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat return 0; } -static void arche_platform_cleanup(struct arche_platform_drvdata *arche_pdata) +static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) { clk_disable_unprepare(arche_pdata->svc_ref_clk); /* As part of exit, put APB back in reset state */ @@ -264,7 +264,7 @@ static int arche_platform_remove(struct platform_device *pdev) cancel_delayed_work_sync(&arche_pdata->delayed_work); device_for_each_child(&pdev->dev, NULL, arche_remove_child); - arche_platform_cleanup(arche_pdata); + arche_platform_poweroff_seq(arche_pdata); platform_set_drvdata(pdev, NULL); unexport_gpios(arche_pdata); -- cgit v1.2.3-59-g8ed1b From e74d04a5810233a95cb7a556165817d378b89e44 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:05 +0530 Subject: greybus: arche-platform: Add state variable to driver data This is preparation for operational state support. So in order to provide operational state transitions, driver needs to maintain state. So add 'enum arche_platform_state' variable to 'struct arche_platform_drvdata' Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 48ab1eb24d63..1336fc4194c7 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -26,6 +26,8 @@ struct arche_platform_drvdata { int svc_sysboot_gpio; int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ + enum arche_platform_state state; + unsigned int svc_refclk_req; struct clk *svc_ref_clk; @@ -127,6 +129,8 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat svc_reset_onoff(arche_pdata->svc_reset_gpio, !arche_pdata->is_reset_act_hi); + arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE; + return 0; } @@ -136,6 +140,8 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda /* As part of exit, put APB back in reset state */ svc_reset_onoff(arche_pdata->svc_reset_gpio, arche_pdata->is_reset_act_hi); + + arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; } static int arche_platform_probe(struct platform_device *pdev) @@ -168,6 +174,7 @@ static int arche_platform_probe(struct platform_device *pdev) dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); return ret; } + arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, "svc,sysboot-gpio", 0); -- cgit v1.2.3-59-g8ed1b From b4c95fca6a9dd8ac2242a451e53781d5313b8c1d Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:06 +0530 Subject: greybus: arche-platform: Pull wake/detect low in poweroff When SVC goes down, make sure that wake/detect is also pulled low. Note that this is not the criteria for SVC poweroff, but it is required to have right state on wake/detect line before powering off. And on next boot, the boot-sequence code would take care of handshaking with SVC. Tested-on: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 1336fc4194c7..b2f3919f7cb3 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -136,6 +136,10 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) { + /* Send disconnect/detach event to SVC */ + gpio_set_value(arche_pdata->wake_detect_gpio, 0); + usleep_range(100, 200); + clk_disable_unprepare(arche_pdata->svc_ref_clk); /* As part of exit, put APB back in reset state */ svc_reset_onoff(arche_pdata->svc_reset_gpio, -- cgit v1.2.3-59-g8ed1b From 2923c58eb5acb019e6781dc05662b896e14339f6 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:07 +0530 Subject: greybus: arche_platform: Add sysfs to allow user to change state This patch introduces sysfs interface for the user space to enable operational state change of the driver. Currently, driver supports, 'off', 'active' and 'standby' Note that, driver doesn't do anything for standby state as of today. To see the current state # cat /sys/devices/arche_platform.*/state And to change the state # echo [off/active/standby] > /sys/devices/arche_platform.*/state Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index b2f3919f7cb3..1dd2b08225c4 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -148,6 +148,55 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; } +static ssize_t state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); + int ret = 0; + + if (sysfs_streq(buf, "off")) { + if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) + return count; + + arche_platform_poweroff_seq(arche_pdata); + } else if (sysfs_streq(buf, "active")) { + if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) + return count; + + ret = arche_platform_coldboot_seq(arche_pdata); + } else if (sysfs_streq(buf, "standby")) { + if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) + return count; + + dev_warn(arche_pdata->dev, "standby state not supported\n"); + } else { + dev_err(arche_pdata->dev, "unknown state\n"); + ret = -EINVAL; + } + + return ret ? ret : count; +} + +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); + + switch (arche_pdata->state) { + case ARCHE_PLATFORM_STATE_OFF: + return sprintf(buf, "off\n"); + case ARCHE_PLATFORM_STATE_ACTIVE: + return sprintf(buf, "active\n"); + case ARCHE_PLATFORM_STATE_STANDBY: + return sprintf(buf, "standby\n"); + default: + return sprintf(buf, "unknown state\n"); + } +} + +static DEVICE_ATTR_RW(state); + static int arche_platform_probe(struct platform_device *pdev) { struct arche_platform_drvdata *arche_pdata; @@ -248,6 +297,12 @@ static int arche_platform_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); + ret = device_create_file(dev, &dev_attr_state); + if (ret) { + dev_err(dev, "failed to create state file in sysfs\n"); + return ret; + } + ret = arche_platform_coldboot_seq(arche_pdata); if (ret) { dev_err(dev, "Failed to cold boot svc %d\n", ret); @@ -273,6 +328,7 @@ static int arche_platform_remove(struct platform_device *pdev) { struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); + device_remove_file(&pdev->dev, &dev_attr_state); cancel_delayed_work_sync(&arche_pdata->delayed_work); device_for_each_child(&pdev->dev, NULL, arche_remove_child); arche_platform_poweroff_seq(arche_pdata); -- cgit v1.2.3-59-g8ed1b From 7691fed20e7301bd89b04d38f88262c83279817a Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:08 +0530 Subject: greybus: arche-platform: Introduce FW_FLASHING state Introduce FW_FLASHING state to arche-platform driver, to enable user space to flash/upgrade SVC firmware. Command to enter into flashing state: # echo fw_flashing > /sys/devices/arche_platform.*/state Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 27 +++++++++++++++++++++++++++ drivers/staging/greybus/arche_platform.h | 1 + 2 files changed, 28 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 1dd2b08225c4..3e6432f0f1bc 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -134,6 +134,23 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat return 0; } +static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) +{ + dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); + + svc_reset_onoff(arche_pdata->svc_reset_gpio, + arche_pdata->is_reset_act_hi); + + gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); + + usleep_range(100, 200); + svc_reset_onoff(arche_pdata->svc_reset_gpio, + !arche_pdata->is_reset_act_hi); + + arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING; + +} + static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) { /* Send disconnect/detach event to SVC */ @@ -170,6 +187,14 @@ static ssize_t state_store(struct device *dev, return count; dev_warn(arche_pdata->dev, "standby state not supported\n"); + } else if (sysfs_streq(buf, "fw_flashing")) { + if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) + return count; + + /* First we want to make sure we power off everything + * and then enter FW flashing state */ + arche_platform_poweroff_seq(arche_pdata); + arche_platform_fw_flashing_seq(arche_pdata); } else { dev_err(arche_pdata->dev, "unknown state\n"); ret = -EINVAL; @@ -190,6 +215,8 @@ static ssize_t state_show(struct device *dev, return sprintf(buf, "active\n"); case ARCHE_PLATFORM_STATE_STANDBY: return sprintf(buf, "standby\n"); + case ARCHE_PLATFORM_STATE_FW_FLASHING: + return sprintf(buf, "fw_flashing\n"); default: return sprintf(buf, "unknown state\n"); } diff --git a/drivers/staging/greybus/arche_platform.h b/drivers/staging/greybus/arche_platform.h index 33c4bb8bca93..27deeb7cd157 100644 --- a/drivers/staging/greybus/arche_platform.h +++ b/drivers/staging/greybus/arche_platform.h @@ -14,6 +14,7 @@ enum arche_platform_state { ARCHE_PLATFORM_STATE_OFF, ARCHE_PLATFORM_STATE_ACTIVE, ARCHE_PLATFORM_STATE_STANDBY, + ARCHE_PLATFORM_STATE_FW_FLASHING, }; int arche_apb_ctrl_probe(struct platform_device *pdev); -- cgit v1.2.3-59-g8ed1b From 6ceed512e68ea8a82cc320630e24656f03c50424 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:09 +0530 Subject: greybus: arche-apb-ctrl: Re-arrange init sequence In order to allow APB2 disable by default on boot/init, and also provide interface to user to enable it later on, re-arrange the init_seq fn. The idea here is, the apb_ctrl_init_seq() fn will be renamed to apb_ctrl_coldboot_seq(), and should not try to claim any resources. All the resource claim should happen in apb_ctrl_get_devtree_data() fn. And also uses devm_gpio_request_one() fn wherever possible. Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 96 +++++++++++++------------------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 42267fa121f5..772015393221 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -78,29 +78,7 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, int ret; /* Hold APB in reset state */ - ret = devm_gpio_request(dev, apb->resetn_gpio, "apb-reset"); - if (ret) { - dev_err(dev, "Failed requesting reset gpio %d\n", - apb->resetn_gpio); - return ret; - } - ret = gpio_direction_output(apb->resetn_gpio, 0); - if (ret) { - dev_err(dev, "failed to set reset gpio dir:%d\n", ret); - return ret; - } - - ret = devm_gpio_request(dev, apb->pwroff_gpio, "pwroff_n"); - if (ret) { - dev_err(dev, "Failed requesting pwroff_n gpio %d\n", - apb->pwroff_gpio); - return ret; - } - ret = gpio_direction_input(apb->pwroff_gpio); - if (ret) { - dev_err(dev, "failed to set pwroff gpio dir:%d\n", ret); - return ret; - } + assert_reset(apb->resetn_gpio); /* Enable power to APB */ if (!IS_ERR(apb->vcore)) { @@ -115,45 +93,24 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, ret = regulator_enable(apb->vio); if (ret) { dev_err(dev, "failed to enable IO regulator\n"); - goto out_vcore_disable; + return ret; } } - ret = devm_gpio_request_one(dev, apb->boot_ret_gpio, - GPIOF_OUT_INIT_LOW, "boot retention"); - if (ret) { - dev_err(dev, "Failed requesting bootret gpio %d\n", - apb->boot_ret_gpio); - goto out_vio_disable; - } gpio_set_value(apb->boot_ret_gpio, 0); /* On DB3 clock was not mandatory */ - if (gpio_is_valid(apb->clk_en_gpio)) { - ret = devm_gpio_request(dev, apb->clk_en_gpio, "apb_clk_en"); - if (ret) { - dev_warn(dev, "Failed requesting APB clock en gpio %d\n", - apb->clk_en_gpio); - } else { - ret = gpio_direction_output(apb->clk_en_gpio, 1); - if (ret) - dev_warn(dev, "failed to set APB clock en gpio dir:%d\n", - ret); - } - } + if (gpio_is_valid(apb->clk_en_gpio)) + gpio_set_value(apb->clk_en_gpio, 1); usleep_range(100, 200); - return 0; + /* deassert reset to APB : Active-low signal */ + deassert_reset(apb->resetn_gpio); -out_vio_disable: - if (!IS_ERR(apb->vio)) - regulator_disable(apb->vio); -out_vcore_disable: - if (!IS_ERR(apb->vcore)) - regulator_disable(apb->vcore); + apb->state = ARCHE_PLATFORM_STATE_ACTIVE; - return ret; + return 0; } static int apb_ctrl_get_devtree_data(struct platform_device *pdev, @@ -161,18 +118,33 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; + int ret; apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0); if (apb->resetn_gpio < 0) { dev_err(dev, "failed to get reset gpio\n"); return apb->resetn_gpio; } + ret = devm_gpio_request_one(dev, apb->resetn_gpio, + GPIOF_OUT_INIT_LOW, "apb-reset"); + if (ret) { + dev_err(dev, "Failed requesting reset gpio %d\n", + apb->resetn_gpio); + return ret; + } apb->boot_ret_gpio = of_get_named_gpio(np, "boot-ret-gpios", 0); if (apb->boot_ret_gpio < 0) { dev_err(dev, "failed to get boot retention gpio\n"); return apb->boot_ret_gpio; } + ret = devm_gpio_request_one(dev, apb->boot_ret_gpio, + GPIOF_OUT_INIT_LOW, "boot retention"); + if (ret) { + dev_err(dev, "Failed requesting bootret gpio %d\n", + apb->boot_ret_gpio); + return ret; + } /* It's not mandatory to support power management interface */ apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0); @@ -180,11 +152,27 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, dev_err(dev, "failed to get power off gpio\n"); return apb->pwroff_gpio; } + ret = devm_gpio_request_one(dev, apb->pwroff_gpio, + GPIOF_IN, "pwroff_n"); + if (ret) { + dev_err(dev, "Failed requesting pwroff_n gpio %d\n", + apb->pwroff_gpio); + return ret; + } /* Do not make clock mandatory as of now (for DB3) */ apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0); - if (apb->clk_en_gpio < 0) + if (apb->clk_en_gpio < 0) { dev_warn(dev, "failed to get clock en gpio\n"); + } else if (gpio_is_valid(apb->clk_en_gpio)) { + ret = devm_gpio_request_one(dev, apb->clk_en_gpio, + GPIOF_OUT_INIT_LOW, "apb_clk_en"); + if (ret) { + dev_warn(dev, "Failed requesting APB clock en gpio %d\n", + apb->clk_en_gpio); + return ret; + } + } apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0); if (apb->pwrdn_gpio < 0) @@ -255,10 +243,6 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) return ret; } - /* deassert reset to APB : Active-low signal */ - deassert_reset(apb->resetn_gpio); - apb->state = ARCHE_PLATFORM_STATE_ACTIVE; - platform_set_drvdata(pdev, apb); export_gpios(apb); -- cgit v1.2.3-59-g8ed1b From 5667ab17999a16c2bb1a5cdcaf183d8710aeb717 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:10 +0530 Subject: greybus: arche-apb-ctrl: Rename init_seq and cleanup fn apb_ctrl_coldboot_seq() and apb_ctrl_poweroff_seq() is appropriate name as per spec and implementation, so rename it. Also move apb_ctrl_poweroff_seq() fn above, to group it with other _seq functions. Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 44 ++++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 772015393221..9b7d61fc9267 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -71,7 +71,7 @@ static void unexport_gpios(struct arche_apb_ctrl_drvdata *apb) /* * Note: Please do not modify the below sequence, as it is as per the spec */ -static int apb_ctrl_init_seq(struct platform_device *pdev, +static int apb_ctrl_coldboot_seq(struct platform_device *pdev, struct arche_apb_ctrl_drvdata *apb) { struct device *dev = &pdev->dev; @@ -113,6 +113,25 @@ static int apb_ctrl_init_seq(struct platform_device *pdev, return 0; } +static void apb_ctrl_poweroff_seq(struct arche_apb_ctrl_drvdata *apb) +{ + /* disable the clock */ + if (gpio_is_valid(apb->clk_en_gpio)) + gpio_set_value(apb->clk_en_gpio, 0); + + if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0) + regulator_disable(apb->vcore); + + if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0) + regulator_disable(apb->vio); + + /* As part of exit, put APB back in reset state */ + assert_reset(apb->resetn_gpio); + apb->state = ARCHE_PLATFORM_STATE_OFF; + + /* TODO: May have to send an event to SVC about this exit */ +} + static int apb_ctrl_get_devtree_data(struct platform_device *pdev, struct arche_apb_ctrl_drvdata *apb) { @@ -201,25 +220,6 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, return 0; } -static void apb_ctrl_cleanup(struct arche_apb_ctrl_drvdata *apb) -{ - /* disable the clock */ - if (gpio_is_valid(apb->clk_en_gpio)) - gpio_set_value(apb->clk_en_gpio, 0); - - if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0) - regulator_disable(apb->vcore); - - if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0) - regulator_disable(apb->vio); - - /* As part of exit, put APB back in reset state */ - assert_reset(apb->resetn_gpio); - apb->state = ARCHE_PLATFORM_STATE_OFF; - - /* TODO: May have to send an event to SVC about this exit */ -} - int arche_apb_ctrl_probe(struct platform_device *pdev) { int ret; @@ -236,7 +236,7 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) return ret; } - ret = apb_ctrl_init_seq(pdev, apb); + ret = apb_ctrl_coldboot_seq(pdev, apb); if (ret) { dev_err(dev, "failed to set init state of control signal %d\n", ret); @@ -255,7 +255,7 @@ int arche_apb_ctrl_remove(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); - apb_ctrl_cleanup(apb); + apb_ctrl_poweroff_seq(apb); platform_set_drvdata(pdev, NULL); unexport_gpios(apb); -- cgit v1.2.3-59-g8ed1b From f2222a41db55f89bfafad03bd04ffdfb317b2edb Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:11 +0530 Subject: greybus: arche-apb-ctrl: Set default state value to OFF Explicitly set default apb->state value to ARCHE_PLATFORM_STATE_OFF in probe() fn. Testing Done: Tested on EVT1.2 and DB3.5 platform Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 9b7d61fc9267..1d3c112e974a 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -236,6 +236,9 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) return ret; } + /* Initially set APB to OFF state */ + apb->state = ARCHE_PLATFORM_STATE_OFF; + ret = apb_ctrl_coldboot_seq(pdev, apb); if (ret) { dev_err(dev, "failed to set init state of control signal %d\n", -- cgit v1.2.3-59-g8ed1b From 6961e0466575608639e91ddbcc62d9385d45a198 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:12 +0530 Subject: greybus: arche-apb-ctrl: Pass only platform_device to _seq fns Pass only pointer to platform_device to _seq fns and fetch handle to arche_apb_ctrl_drvdata from platform_device. This is preparation for support for dynamic switching between operational states for the device, where these functions will be called from parent driver. Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 1d3c112e974a..8f6f0962421e 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -71,10 +71,10 @@ static void unexport_gpios(struct arche_apb_ctrl_drvdata *apb) /* * Note: Please do not modify the below sequence, as it is as per the spec */ -static int apb_ctrl_coldboot_seq(struct platform_device *pdev, - struct arche_apb_ctrl_drvdata *apb) +static int apb_ctrl_coldboot_seq(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); int ret; /* Hold APB in reset state */ @@ -113,8 +113,10 @@ static int apb_ctrl_coldboot_seq(struct platform_device *pdev, return 0; } -static void apb_ctrl_poweroff_seq(struct arche_apb_ctrl_drvdata *apb) +static void apb_ctrl_poweroff_seq(struct platform_device *pdev) { + struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); + /* disable the clock */ if (gpio_is_valid(apb->clk_en_gpio)) gpio_set_value(apb->clk_en_gpio, 0); @@ -239,15 +241,16 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) /* Initially set APB to OFF state */ apb->state = ARCHE_PLATFORM_STATE_OFF; - ret = apb_ctrl_coldboot_seq(pdev, apb); + platform_set_drvdata(pdev, apb); + + ret = apb_ctrl_coldboot_seq(pdev); if (ret) { dev_err(dev, "failed to set init state of control signal %d\n", ret); + platform_set_drvdata(pdev, NULL); return ret; } - platform_set_drvdata(pdev, apb); - export_gpios(apb); dev_info(&pdev->dev, "Device registered successfully\n"); @@ -258,7 +261,7 @@ int arche_apb_ctrl_remove(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); - apb_ctrl_poweroff_seq(apb); + apb_ctrl_poweroff_seq(pdev); platform_set_drvdata(pdev, NULL); unexport_gpios(apb); -- cgit v1.2.3-59-g8ed1b From 33d76291073dd9408055a23cd96a23c375944377 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:13 +0530 Subject: greybus: arche-apb-ctrl: Add sysfs to allow user to change state This patch introduces sysfs interface for the user space to enable state change of the driver. Driver supports below operational states, - off - active - standby - fw_flashing To see the current state i # cat /sys/devices/arche_platform.*/apb*/state And to change the state # echo [off/active/standby/fw_flashing] > /sys/devices/arche_platform.*/apb*/state Testing Done: Tested on EVT1.2 and DB3.5 platform Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 115 +++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 8f6f0962421e..e4fd34ddf465 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -113,6 +113,52 @@ static int apb_ctrl_coldboot_seq(struct platform_device *pdev) return 0; } +static int apb_ctrl_fw_flashing_seq(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); + int ret; + + ret = regulator_enable(apb->vcore); + if (ret) { + dev_err(dev, "failed to enable core regulator\n"); + return ret; + } + + ret = regulator_enable(apb->vio); + if (ret) { + dev_err(dev, "failed to enable IO regulator\n"); + return ret; + } + + /* for flashing device should be in reset state */ + assert_reset(apb->resetn_gpio); + apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING; + + return 0; +} + +static int apb_ctrl_standby_boot_seq(struct platform_device *pdev) +{ + struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); + + /* If it is in OFF state, then we do not want to change the state */ + if (apb->state == ARCHE_PLATFORM_STATE_OFF) + return 0; + + /* + * As per WDM spec, do nothing + * + * Pasted from WDM spec, + * - A falling edge on POWEROFF_L is detected (a) + * - WDM enters standby mode, but no output signals are changed + * */ + + /* TODO: POWEROFF_L is input to WDM module */ + apb->state = ARCHE_PLATFORM_STATE_STANDBY; + return 0; +} + static void apb_ctrl_poweroff_seq(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); @@ -134,6 +180,66 @@ static void apb_ctrl_poweroff_seq(struct platform_device *pdev) /* TODO: May have to send an event to SVC about this exit */ } +static ssize_t state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); + int ret = 0; + + if (sysfs_streq(buf, "off")) { + if (apb->state == ARCHE_PLATFORM_STATE_OFF) + return count; + + apb_ctrl_poweroff_seq(pdev); + } else if (sysfs_streq(buf, "active")) { + if (apb->state == ARCHE_PLATFORM_STATE_ACTIVE) + return count; + + apb_ctrl_poweroff_seq(pdev); + ret = apb_ctrl_coldboot_seq(pdev); + } else if (sysfs_streq(buf, "standby")) { + if (apb->state == ARCHE_PLATFORM_STATE_STANDBY) + return count; + + ret = apb_ctrl_standby_boot_seq(pdev); + } else if (sysfs_streq(buf, "fw_flashing")) { + if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING) + return count; + + /* First we want to make sure we power off everything + * and then enter FW flashing state */ + apb_ctrl_poweroff_seq(pdev); + ret = apb_ctrl_fw_flashing_seq(pdev); + } else { + dev_err(dev, "unknown state\n"); + ret = -EINVAL; + } + + return ret ? ret : count; +} + +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); + + switch (apb->state) { + case ARCHE_PLATFORM_STATE_OFF: + return sprintf(buf, "off\n"); + case ARCHE_PLATFORM_STATE_ACTIVE: + return sprintf(buf, "active\n"); + case ARCHE_PLATFORM_STATE_STANDBY: + return sprintf(buf, "standby\n"); + case ARCHE_PLATFORM_STATE_FW_FLASHING: + return sprintf(buf, "fw_flashing\n"); + default: + return sprintf(buf, "unknown state\n"); + } +} + +static DEVICE_ATTR_RW(state); + static int apb_ctrl_get_devtree_data(struct platform_device *pdev, struct arche_apb_ctrl_drvdata *apb) { @@ -243,10 +349,18 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) platform_set_drvdata(pdev, apb); + /* Create sysfs interface to allow user to change state dynamically */ + ret = device_create_file(dev, &dev_attr_state); + if (ret) { + dev_err(dev, "failed to create state file in sysfs\n"); + return ret; + } + ret = apb_ctrl_coldboot_seq(pdev); if (ret) { dev_err(dev, "failed to set init state of control signal %d\n", ret); + device_remove_file(dev, &dev_attr_state); platform_set_drvdata(pdev, NULL); return ret; } @@ -261,6 +375,7 @@ int arche_apb_ctrl_remove(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); + device_remove_file(&pdev->dev, &dev_attr_state); apb_ctrl_poweroff_seq(pdev); platform_set_drvdata(pdev, NULL); unexport_gpios(apb); -- cgit v1.2.3-59-g8ed1b From af3aae10f16f05acba27294bc1ae234f3cb61a61 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:14 +0530 Subject: greybus: arche-apb-ctrl: Introduce ara,init-disable property for APB New DT property "ara,init-disable" will allow user to disable APB1 or APB2 during boot and enable it only when needed through command prompt via sysfs interface. - To disable APB2 during boot, specify "ara,init-disable" property in respective APB node. - How to check the state # cat /sys/devices/arche_platform.*/apb*/state It should be 'off', if 'ara,init-disable' enabled in DT. - During runtime if user/developer desired to enable APB2 (strictly and only for development purpose) then respective APB can be enabled through, # echo active > /sys/devices/arche_platform.*/apb*/state Note: - If APB device is in 'off,disabled' state, then no state transitions are permitted. - User/developer must first activate APB device # echo active > /sys/devices/arche_platform.*/apb*/state This will clear the 'init-disable' flag and allow state transition from here onwards. Note that, 'off,disabled' is only indicative state and is only applicable during init/boot. Testing Done: Tested on EVT1.2 and DB3.5 platform Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index e4fd34ddf465..04ba8365f7bf 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -32,6 +32,7 @@ struct arche_apb_ctrl_drvdata { int pwrdn_gpio; enum arche_platform_state state; + bool init_disabled; struct regulator *vcore; struct regulator *vio; @@ -77,6 +78,9 @@ static int apb_ctrl_coldboot_seq(struct platform_device *pdev) struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); int ret; + if (apb->init_disabled) + return 0; + /* Hold APB in reset state */ assert_reset(apb->resetn_gpio); @@ -119,6 +123,9 @@ static int apb_ctrl_fw_flashing_seq(struct platform_device *pdev) struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); int ret; + if (apb->init_disabled) + return 0; + ret = regulator_enable(apb->vcore); if (ret) { dev_err(dev, "failed to enable core regulator\n"); @@ -142,6 +149,9 @@ static int apb_ctrl_standby_boot_seq(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); + if (apb->init_disabled) + return 0; + /* If it is in OFF state, then we do not want to change the state */ if (apb->state == ARCHE_PLATFORM_STATE_OFF) return 0; @@ -163,6 +173,9 @@ static void apb_ctrl_poweroff_seq(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); + if (apb->init_disabled) + return; + /* disable the clock */ if (gpio_is_valid(apb->clk_en_gpio)) gpio_set_value(apb->clk_en_gpio, 0); @@ -186,6 +199,7 @@ static ssize_t state_store(struct device *dev, struct platform_device *pdev = to_platform_device(dev); struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); int ret = 0; + bool is_disabled; if (sysfs_streq(buf, "off")) { if (apb->state == ARCHE_PLATFORM_STATE_OFF) @@ -197,7 +211,11 @@ static ssize_t state_store(struct device *dev, return count; apb_ctrl_poweroff_seq(pdev); + is_disabled = apb->init_disabled; + apb->init_disabled = false; ret = apb_ctrl_coldboot_seq(pdev); + if (ret) + apb->init_disabled = is_disabled; } else if (sysfs_streq(buf, "standby")) { if (apb->state == ARCHE_PLATFORM_STATE_STANDBY) return count; @@ -226,7 +244,8 @@ static ssize_t state_show(struct device *dev, switch (apb->state) { case ARCHE_PLATFORM_STATE_OFF: - return sprintf(buf, "off\n"); + return sprintf(buf, "off%s\n", + apb->init_disabled ? ",disabled" : ""); case ARCHE_PLATFORM_STATE_ACTIVE: return sprintf(buf, "active\n"); case ARCHE_PLATFORM_STATE_STANDBY: @@ -346,6 +365,9 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) /* Initially set APB to OFF state */ apb->state = ARCHE_PLATFORM_STATE_OFF; + /* Check whether device needs to be enabled on boot */ + if (of_property_read_bool(pdev->dev.of_node, "ara,init-disable")) + apb->init_disabled = true; platform_set_drvdata(pdev, apb); -- cgit v1.2.3-59-g8ed1b From 80a057aa6c170403261f8d1d2ce435d3668848f6 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:15 +0530 Subject: greybus: arche-apb-ctrl: Rename internal operational state fns This is preparation for enabling export set of operational fns to parent driver. So it is important to differentiate internal ops function against externally accessed (from parent). Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 04ba8365f7bf..50d27a626ec6 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -72,7 +72,7 @@ static void unexport_gpios(struct arche_apb_ctrl_drvdata *apb) /* * Note: Please do not modify the below sequence, as it is as per the spec */ -static int apb_ctrl_coldboot_seq(struct platform_device *pdev) +static int coldboot_seq(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); @@ -117,7 +117,7 @@ static int apb_ctrl_coldboot_seq(struct platform_device *pdev) return 0; } -static int apb_ctrl_fw_flashing_seq(struct platform_device *pdev) +static int fw_flashing_seq(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); @@ -145,7 +145,7 @@ static int apb_ctrl_fw_flashing_seq(struct platform_device *pdev) return 0; } -static int apb_ctrl_standby_boot_seq(struct platform_device *pdev) +static int standby_boot_seq(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); @@ -169,7 +169,7 @@ static int apb_ctrl_standby_boot_seq(struct platform_device *pdev) return 0; } -static void apb_ctrl_poweroff_seq(struct platform_device *pdev) +static void poweroff_seq(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); @@ -205,30 +205,30 @@ static ssize_t state_store(struct device *dev, if (apb->state == ARCHE_PLATFORM_STATE_OFF) return count; - apb_ctrl_poweroff_seq(pdev); + poweroff_seq(pdev); } else if (sysfs_streq(buf, "active")) { if (apb->state == ARCHE_PLATFORM_STATE_ACTIVE) return count; - apb_ctrl_poweroff_seq(pdev); + poweroff_seq(pdev); is_disabled = apb->init_disabled; apb->init_disabled = false; - ret = apb_ctrl_coldboot_seq(pdev); + ret = coldboot_seq(pdev); if (ret) apb->init_disabled = is_disabled; } else if (sysfs_streq(buf, "standby")) { if (apb->state == ARCHE_PLATFORM_STATE_STANDBY) return count; - ret = apb_ctrl_standby_boot_seq(pdev); + ret = standby_boot_seq(pdev); } else if (sysfs_streq(buf, "fw_flashing")) { if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING) return count; /* First we want to make sure we power off everything * and then enter FW flashing state */ - apb_ctrl_poweroff_seq(pdev); - ret = apb_ctrl_fw_flashing_seq(pdev); + poweroff_seq(pdev); + ret = fw_flashing_seq(pdev); } else { dev_err(dev, "unknown state\n"); ret = -EINVAL; @@ -378,7 +378,7 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) return ret; } - ret = apb_ctrl_coldboot_seq(pdev); + ret = coldboot_seq(pdev); if (ret) { dev_err(dev, "failed to set init state of control signal %d\n", ret); @@ -398,7 +398,7 @@ int arche_apb_ctrl_remove(struct platform_device *pdev) struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); device_remove_file(&pdev->dev, &dev_attr_state); - apb_ctrl_poweroff_seq(pdev); + poweroff_seq(pdev); platform_set_drvdata(pdev, NULL); unexport_gpios(apb); -- cgit v1.2.3-59-g8ed1b From 65fd5a5018c8c5b7ddf14dffa75474b3a9040851 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:16 +0530 Subject: greybus: arche-apb-ctrl: Export operational fns from driver In order to use single wake/detect line for both APB's we need to have access to APB operational functions to parent/SVC driver. So export coldboot, standby_boot, fw_flashing and poweroff operation functions from the driver. Testing Done: Tested on EVT1.2 and DB3.5 platform Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 20 ++++++++++++++++++++ drivers/staging/greybus/arche_platform.h | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 50d27a626ec6..e1df998ca877 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -193,6 +193,26 @@ static void poweroff_seq(struct platform_device *pdev) /* TODO: May have to send an event to SVC about this exit */ } +int apb_ctrl_coldboot(struct device *dev) +{ + return coldboot_seq(to_platform_device(dev)); +} + +int apb_ctrl_fw_flashing(struct device *dev) +{ + return fw_flashing_seq(to_platform_device(dev)); +} + +int apb_ctrl_standby_boot(struct device *dev) +{ + return standby_boot_seq(to_platform_device(dev)); +} + +void apb_ctrl_poweroff(struct device *dev) +{ + poweroff_seq(to_platform_device(dev)); +} + static ssize_t state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { diff --git a/drivers/staging/greybus/arche_platform.h b/drivers/staging/greybus/arche_platform.h index 27deeb7cd157..700c548d68db 100644 --- a/drivers/staging/greybus/arche_platform.h +++ b/drivers/staging/greybus/arche_platform.h @@ -17,8 +17,17 @@ enum arche_platform_state { ARCHE_PLATFORM_STATE_FW_FLASHING, }; + int arche_apb_ctrl_probe(struct platform_device *pdev); int arche_apb_ctrl_remove(struct platform_device *pdev); + +/* Operational states for the APB device */ +int apb_ctrl_coldboot(struct device *dev); +int apb_ctrl_fw_flashing(struct device *dev); +int apb_ctrl_standby_boot(struct device *dev); +void apb_ctrl_poweroff(struct device *dev); + + extern const struct dev_pm_ops arche_apb_ctrl_pm_ops; #endif /* __ARCHE_PLATFORM_H */ -- cgit v1.2.3-59-g8ed1b From fd60ac585607979e37b64ecec8afb898f9ad6a85 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:17 +0530 Subject: greybus: arche-platform: Fix boot, poweroff and fw_flashing seq with APBs Now SVC driver has an access to APBs operational functions (coldboot, standby_boot, fw_flashing and poweroff), SVC driver can control APB's as per below rules, - If SVC goes down (poweroff state), it will also power off APBs and vice a versa for all operational states. - On boot, SVC will probe/populate APB device, but will not coldboot it. APBs will coldboot only after handshaking with SVC over wake/detect line. Note that, both APBs share same wake/detect line. So from user/developer perspective, it is highly recommended that they should use arche-platform interfaces, instead of individual apb interface, # echo [off/active/standby/fw_flashing] > /sys/devices/arche_platform.*/state Note: 'standby' mode is not supported as of now. Testing Done: Testd on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 77 +++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 3e6432f0f1bc..037e14206059 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -45,6 +45,37 @@ static inline void svc_reset_onoff(unsigned int gpio, bool onoff) gpio_set_value(gpio, onoff); } +static int apb_cold_boot(struct device *dev, void *data) +{ + int ret; + + ret = apb_ctrl_coldboot(dev); + if (ret) + dev_warn(dev, "failed to coldboot\n"); + + /*Child nodes are independent, so do not exit coldboot operation */ + return 0; +} + +static int apb_fw_flashing_state(struct device *dev, void *data) +{ + int ret; + + ret = apb_ctrl_fw_flashing(dev); + if (ret) + dev_warn(dev, "failed to switch to fw flashing state\n"); + + /*Child nodes are independent, so do not exit coldboot operation */ + return 0; +} + +static int apb_poweroff(struct device *dev, void *data) +{ + apb_ctrl_poweroff(dev); + + return 0; +} + /** * svc_delayed_work - Time to give SVC to boot. */ @@ -52,10 +83,7 @@ static void svc_delayed_work(struct work_struct *work) { struct arche_platform_drvdata *arche_pdata = container_of(work, struct arche_platform_drvdata, delayed_work.work); - struct device *dev = arche_pdata->dev; - struct device_node *np = dev->of_node; int timeout = 50; - int ret; /* * 1. SVC and AP boot independently, with AP<-->SVC wake/detect pin @@ -79,18 +107,20 @@ static void svc_delayed_work(struct work_struct *work) msleep(100); } while(timeout--); - if (timeout >= 0) { - ret = of_platform_populate(np, NULL, NULL, dev); - if (!ret) { - /* re-assert wake_detect to confirm SVC WAKE_OUT */ - gpio_direction_output(arche_pdata->wake_detect_gpio, 1); - return; - } + if (timeout < 0) { + /* FIXME: We may want to limit retries here */ + dev_warn(arche_pdata->dev, + "Timed out on wake/detect, rescheduling handshake\n"); + gpio_direction_output(arche_pdata->wake_detect_gpio, 0); + schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); + return; } - /* FIXME: We may want to limit retries here */ - gpio_direction_output(arche_pdata->wake_detect_gpio, 0); - schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); + /* Bring APB out of reset: cold boot sequence */ + device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); + + /* re-assert wake_detect to confirm SVC WAKE_OUT */ + gpio_direction_output(arche_pdata->wake_detect_gpio, 1); } /* Export gpio's to user space */ @@ -176,12 +206,17 @@ static ssize_t state_store(struct device *dev, if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) return count; + /* If SVC goes down, bring down APB's as well */ + device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); + arche_platform_poweroff_seq(arche_pdata); } else if (sysfs_streq(buf, "active")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) return count; ret = arche_platform_coldboot_seq(arche_pdata); + /* Give enough time for SVC to boot and then handshake with SVC */ + schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); } else if (sysfs_streq(buf, "standby")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) return count; @@ -193,8 +228,13 @@ static ssize_t state_store(struct device *dev, /* First we want to make sure we power off everything * and then enter FW flashing state */ + device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); + arche_platform_poweroff_seq(arche_pdata); + arche_platform_fw_flashing_seq(arche_pdata); + + device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); } else { dev_err(arche_pdata->dev, "unknown state\n"); ret = -EINVAL; @@ -321,8 +361,6 @@ static int arche_platform_probe(struct platform_device *pdev) gpio_direction_output(arche_pdata->wake_detect_gpio, 0); arche_pdata->dev = &pdev->dev; - INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); - schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); ret = device_create_file(dev, &dev_attr_state); if (ret) { @@ -336,6 +374,15 @@ static int arche_platform_probe(struct platform_device *pdev) return ret; } + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to populate child nodes %d\n", ret); + return ret; + } + + INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); + schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); + export_gpios(arche_pdata); dev_info(dev, "Device registered successfully\n"); -- cgit v1.2.3-59-g8ed1b From b03249390806c48921e1a538673276ebfcb645b7 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:18 +0530 Subject: greybus: arche-platform: Do not export any gpios With addition of operational state in driver, user/developer can switch to FW flashing state through sysfs. So no need to export any gpios to userspace now. Testing Done: Tested on EVT1.2 and DB3.5 platform Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 037e14206059..1fd806f6580d 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -123,19 +123,6 @@ static void svc_delayed_work(struct work_struct *work) gpio_direction_output(arche_pdata->wake_detect_gpio, 1); } -/* Export gpio's to user space */ -static void export_gpios(struct arche_platform_drvdata *arche_pdata) -{ - gpio_export(arche_pdata->svc_reset_gpio, false); - gpio_export(arche_pdata->svc_sysboot_gpio, false); -} - -static void unexport_gpios(struct arche_platform_drvdata *arche_pdata) -{ - gpio_unexport(arche_pdata->svc_reset_gpio); - gpio_unexport(arche_pdata->svc_sysboot_gpio); -} - static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) { int ret; @@ -383,8 +370,6 @@ static int arche_platform_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); - export_gpios(arche_pdata); - dev_info(dev, "Device registered successfully\n"); return 0; } @@ -407,7 +392,6 @@ static int arche_platform_remove(struct platform_device *pdev) device_for_each_child(&pdev->dev, NULL, arche_remove_child); arche_platform_poweroff_seq(arche_pdata); platform_set_drvdata(pdev, NULL); - unexport_gpios(arche_pdata); return 0; } -- cgit v1.2.3-59-g8ed1b From bc8eadd46c05280f7ddcf2ab7cf97d1cd15a578c Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:19 +0530 Subject: greybus: arche-apb-ctrl: Do not export any gpios With addition of operational state in driver, user/developer can switch to FW flashing state through sysfs. So no need to export any gpios to userspace now. Testing Done: Tested on EVT1.2 and DB3.5 platform Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index e1df998ca877..b888da376b21 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -58,17 +58,6 @@ static inline void assert_reset(unsigned int gpio) gpio_set_value(gpio, 0); } -/* Export gpio's to user space */ -static void export_gpios(struct arche_apb_ctrl_drvdata *apb) -{ - gpio_export(apb->resetn_gpio, false); -} - -static void unexport_gpios(struct arche_apb_ctrl_drvdata *apb) -{ - gpio_unexport(apb->resetn_gpio); -} - /* * Note: Please do not modify the below sequence, as it is as per the spec */ @@ -407,20 +396,15 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) return ret; } - export_gpios(apb); - dev_info(&pdev->dev, "Device registered successfully\n"); return 0; } int arche_apb_ctrl_remove(struct platform_device *pdev) { - struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); - device_remove_file(&pdev->dev, &dev_attr_state); poweroff_seq(pdev); platform_set_drvdata(pdev, NULL); - unexport_gpios(apb); return 0; } -- cgit v1.2.3-59-g8ed1b From ad4d3f95c9a25590da8b2b71dbbad838b80bf7dc Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Sat, 13 Feb 2016 02:04:20 +0530 Subject: greybus: arche-platform: Control usb3613 from arche-platform USB3613 hub driver exports control function, which allows caller to switch the mode of operation of hub device. As we know that, we have dependency between HUB3613 and APB's where, HUB supposed to enter into HUB only after APB's brought out of reset. Until now, we had all userspace driver sequences to control this, but now since we are moving all resource management strictly to the driver, it makes sense (even though it looks hacky) to enable control of hub3613 from arche-platform driver. Note that, there is another discussion where, the hub.connect IO pin may get interfaced with MSM gpio, at that time, we can get rid of this hack and replace it with gpio control. Testing Done: Tested on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 1fd806f6580d..591cf9dc5071 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -19,6 +19,8 @@ #include #include "arche_platform.h" +#include + struct arche_platform_drvdata { /* Control GPIO signals to and from AP <=> SVC */ int svc_reset_gpio; @@ -121,6 +123,10 @@ static void svc_delayed_work(struct work_struct *work) /* re-assert wake_detect to confirm SVC WAKE_OUT */ gpio_direction_output(arche_pdata->wake_detect_gpio, 1); + + /* Enable HUB3613 into HUB mode. */ + if (usb3613_hub_mode_ctrl(true)) + dev_warn(arche_pdata->dev, "failed to control hub device\n"); } static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) @@ -197,6 +203,11 @@ static ssize_t state_store(struct device *dev, device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); arche_platform_poweroff_seq(arche_pdata); + + ret = usb3613_hub_mode_ctrl(false); + if (ret) + dev_warn(arche_pdata->dev, "failed to control hub device\n"); + /* TODO: Should we do anything more here ?? */ } else if (sysfs_streq(buf, "active")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) return count; @@ -219,6 +230,11 @@ static ssize_t state_store(struct device *dev, arche_platform_poweroff_seq(arche_pdata); + ret = usb3613_hub_mode_ctrl(false); + if (ret) + dev_warn(arche_pdata->dev, "failed to control hub device\n"); + /* TODO: Should we do anything more here ?? */ + arche_platform_fw_flashing_seq(arche_pdata); device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); @@ -393,6 +409,9 @@ static int arche_platform_remove(struct platform_device *pdev) arche_platform_poweroff_seq(arche_pdata); platform_set_drvdata(pdev, NULL); + if (usb3613_hub_mode_ctrl(false)) + dev_warn(arche_pdata->dev, "failed to control hub device\n"); + /* TODO: Should we do anything more here ?? */ return 0; } -- cgit v1.2.3-59-g8ed1b From 7d0493d191abc0bb20c0746fe687b7e8ba8a696a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 15 Feb 2016 13:58:21 -0800 Subject: greybus: only build arche platform driver if usb3613 is enabled The build is broken if you try to build the arche platform driver without the usb3163 driver enabled at the same time, so specify that dependency in the greybus Makefile. Testing done: built inside and outside of the build system against many different kernel versions. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 870a7bb22d9e..6e276d82fdec 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -47,7 +47,9 @@ obj-m += gb-light.o obj-m += gb-hid.o obj-m += gb-raw.o obj-m += gb-es2.o -obj-m += gb-arche.o +ifeq ($(CONFIG_USB_HSIC_USB3613),y) + obj-m += gb-arche.o +endif ifeq ($(CONFIG_SND_SOC_DYNAMIC_DAILINK),y) obj-m += gb-audio-codec.o endif -- cgit v1.2.3-59-g8ed1b From c463593c3dbc8d4dcb132538b0116256fa0cc455 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Feb 2016 16:08:26 +0530 Subject: greybus: raw: Use consistent label names in connection_init() Some of the labels are named based on what they are going to undo, while others are based on where we failed in connection_init(). Follow only the first type of naming. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/raw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index a6e795996053..d2e0281e86c5 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -174,21 +174,21 @@ static int gb_raw_connection_init(struct gb_connection *connection) cdev_init(&raw->cdev, &raw_fops); retval = cdev_add(&raw->cdev, raw->dev, 1); if (retval) - goto error_cdev; + goto error_remove_ida; raw->device = device_create(raw_class, &connection->bundle->dev, raw->dev, raw, "gb!raw%d", minor); if (IS_ERR(raw->device)) { retval = PTR_ERR(raw->device); - goto error_device; + goto error_del_cdev; } return 0; -error_device: +error_del_cdev: cdev_del(&raw->cdev); -error_cdev: +error_remove_ida: ida_simple_remove(&minors, minor); error_free: -- cgit v1.2.3-59-g8ed1b From 2554eda5756a37118ab310bd02de78491303ab5f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Feb 2016 16:08:27 +0530 Subject: greybus: raw: Don't use (possibly) uninitialized raw->device in gb_raw_receive() If an incoming request comes on the connection, before the driver has allocated its raw->device in gb_raw_connection_init(), then it might result in a crash while printing error messages. Fix that by using bundle->dev for printing error messages. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/raw.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index d2e0281e86c5..ed17ba3ca0b3 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -57,17 +57,17 @@ static DEFINE_IDA(minors); static int receive_data(struct gb_raw *raw, u32 len, u8 *data) { struct raw_data *raw_data; + struct device *dev = &raw->connection->bundle->dev; int retval = 0; if (len > MAX_PACKET_SIZE) { - dev_err(raw->device, "Too big of a data packet, rejected\n"); + dev_err(dev, "Too big of a data packet, rejected\n"); return -EINVAL; } mutex_lock(&raw->list_lock); if ((raw->list_data + len) > MAX_DATA_SIZE) { - dev_err(raw->device, - "Too much data in receive buffer, now dropping packets\n"); + dev_err(dev, "Too much data in receive buffer, now dropping packets\n"); retval = -EINVAL; goto exit; } @@ -91,32 +91,31 @@ exit: static int gb_raw_receive(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; + struct device *dev = &connection->bundle->dev; struct gb_raw *raw = connection->private; struct gb_raw_send_request *receive; u32 len; if (type != GB_RAW_TYPE_SEND) { - dev_err(raw->device, "unknown request type %d\n", type); + dev_err(dev, "unknown request type %d\n", type); return -EINVAL; } /* Verify size of payload */ if (op->request->payload_size < sizeof(*receive)) { - dev_err(raw->device, "raw receive request too small (%zu < %zu)\n", + dev_err(dev, "raw receive request too small (%zu < %zu)\n", op->request->payload_size, sizeof(*receive)); return -EINVAL; } receive = op->request->payload; len = le32_to_cpu(receive->len); if (len != (int)(op->request->payload_size - sizeof(__le32))) { - dev_err(raw->device, - "raw receive request wrong size %d vs %d\n", - len, + dev_err(dev, "raw receive request wrong size %d vs %d\n", len, (int)(op->request->payload_size - sizeof(__le32))); return -EINVAL; } if (len == 0) { - dev_err(raw->device, "raw receive request of 0 bytes?\n"); + dev_err(dev, "raw receive request of 0 bytes?\n"); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 512cc327625651260b2ba885b63cf2cf14ff54d1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 15 Feb 2016 10:47:27 +0530 Subject: greybus: raw: convert to bundle driver Convert the legacy raw protocol driver to a bundle driver. This also fixes a potential crash should a (malicious) module have sent an early request before the private data had been initialised. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/raw.c | 82 ++++++++++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 057029dc3ee4..23ea45879d25 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -247,7 +247,6 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) }, { } }; MODULE_DEVICE_TABLE(greybus, legacy_id_table); diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index ed17ba3ca0b3..338139e815af 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -88,16 +88,16 @@ exit: return retval; } -static int gb_raw_receive(u8 type, struct gb_operation *op) +static int gb_raw_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct device *dev = &connection->bundle->dev; - struct gb_raw *raw = connection->private; + struct gb_raw *raw = greybus_get_drvdata(connection->bundle); struct gb_raw_send_request *receive; u32 len; - if (type != GB_RAW_TYPE_SEND) { - dev_err(dev, "unknown request type %d\n", type); + if (op->type != GB_RAW_TYPE_SEND) { + dev_err(dev, "unknown request type %d\n", op->type); return -EINVAL; } @@ -147,34 +147,56 @@ static int gb_raw_send(struct gb_raw *raw, u32 len, const char __user *data) return retval; } -static int gb_raw_connection_init(struct gb_connection *connection) +static int gb_raw_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) { + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; struct gb_raw *raw; int retval; int minor; + if (bundle->num_cports != 1) + return -ENODEV; + + cport_desc = &bundle->cport_desc[0]; + if (cport_desc->protocol_id != GREYBUS_PROTOCOL_RAW) + return -ENODEV; + raw = kzalloc(sizeof(*raw), GFP_KERNEL); if (!raw) return -ENOMEM; - raw->connection = connection; - connection->private = raw; + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + gb_raw_request_handler); + if (IS_ERR(connection)) { + retval = PTR_ERR(connection); + goto error_free; + } INIT_LIST_HEAD(&raw->list); mutex_init(&raw->list_lock); + raw->connection = connection; + greybus_set_drvdata(bundle, raw); + minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); if (minor < 0) { retval = minor; - goto error_free; + goto error_connection_destroy; } raw->dev = MKDEV(raw_major, minor); cdev_init(&raw->cdev, &raw_fops); - retval = cdev_add(&raw->cdev, raw->dev, 1); + + retval = gb_connection_enable(connection); if (retval) goto error_remove_ida; + retval = cdev_add(&raw->cdev, raw->dev, 1); + if (retval) + goto error_connection_disable; + raw->device = device_create(raw_class, &connection->bundle->dev, raw->dev, raw, "gb!raw%d", minor); if (IS_ERR(raw->device)) { @@ -187,24 +209,34 @@ static int gb_raw_connection_init(struct gb_connection *connection) error_del_cdev: cdev_del(&raw->cdev); +error_connection_disable: + gb_connection_disable(connection); + error_remove_ida: ida_simple_remove(&minors, minor); +error_connection_destroy: + gb_connection_destroy(connection); + error_free: kfree(raw); return retval; } -static void gb_raw_connection_exit(struct gb_connection *connection) +static void gb_raw_disconnect(struct gb_bundle *bundle) { - struct gb_raw *raw = connection->private; + struct gb_raw *raw = greybus_get_drvdata(bundle); + struct gb_connection *connection = raw->connection; struct raw_data *raw_data; struct raw_data *temp; // FIXME - handle removing a connection when the char device node is open. device_destroy(raw_class, raw->dev); cdev_del(&raw->cdev); + gb_connection_disable(connection); ida_simple_remove(&minors, MINOR(raw->dev)); + gb_connection_destroy(connection); + mutex_lock(&raw->list_lock); list_for_each_entry_safe(raw_data, temp, &raw->list, entry) { list_del(&raw_data->entry); @@ -215,16 +247,6 @@ static void gb_raw_connection_exit(struct gb_connection *connection) kfree(raw); } -static struct gb_protocol raw_protocol = { - .name = "raw", - .id = GREYBUS_PROTOCOL_RAW, - .major = GB_RAW_VERSION_MAJOR, - .minor = GB_RAW_VERSION_MINOR, - .connection_init = gb_raw_connection_init, - .connection_exit = gb_raw_connection_exit, - .request_recv = gb_raw_receive, -}; - /* * Character device node interfaces. * @@ -302,6 +324,19 @@ static const struct file_operations raw_fops = { .llseek = noop_llseek, }; +static const struct greybus_bundle_id gb_raw_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) }, + { } +}; +MODULE_DEVICE_TABLE(greybus, gb_raw_id_table); + +static struct greybus_driver gb_raw_driver = { + .name = "raw", + .probe = gb_raw_probe, + .disconnect = gb_raw_disconnect, + .id_table = gb_raw_id_table, +}; + static int raw_init(void) { dev_t dev; @@ -317,10 +352,9 @@ static int raw_init(void) if (retval < 0) goto error_chrdev; - raw_major = MAJOR(dev); - retval = gb_protocol_register(&raw_protocol); + retval = greybus_register(&gb_raw_driver); if (retval) goto error_gb; @@ -337,7 +371,7 @@ module_init(raw_init); static void __exit raw_exit(void) { - gb_protocol_deregister(&raw_protocol); + greybus_deregister(&gb_raw_driver); unregister_chrdev_region(MKDEV(raw_major, 0), NUM_MINORS); class_destroy(raw_class); ida_destroy(&minors); -- cgit v1.2.3-59-g8ed1b From e82a11dcbd37f0b5596d08636859b372713d4460 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Feb 2016 16:08:29 +0530 Subject: greybus: loopback: convert to bundle driver Convert the legacy loopback protocol driver to a bundle driver. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/loopback.c | 86 +++++++++++++++++++++++++++----------- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 23ea45879d25..63597f7ac52a 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -246,7 +246,6 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) }, { } }; MODULE_DEVICE_TABLE(greybus, legacy_id_table); diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 66b3fcaca9b0..f3ae2e9fc1bb 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -777,7 +777,7 @@ static int gb_loopback_async_ping(struct gb_loopback *gb) NULL, 0, 0, NULL); } -static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) +static int gb_loopback_request_handler(struct gb_operation *operation) { struct gb_connection *connection = operation->connection; struct gb_loopback_transfer_request *request; @@ -786,7 +786,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) size_t len; /* By convention, the AP initiates the version operation */ - switch (type) { + switch (operation->type) { case GB_REQUEST_TYPE_PROTOCOL_VERSION: dev_err(dev, "module-initiated version operation\n"); return -EINVAL; @@ -820,7 +820,7 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation) return 0; default: - dev_err(dev, "unsupported request: %u\n", type); + dev_err(dev, "unsupported request: %u\n", operation->type); return -EINVAL; } } @@ -1083,18 +1083,38 @@ static void gb_loopback_insert_id(struct gb_loopback *gb) #define DEBUGFS_NAMELEN 32 -static int gb_loopback_connection_init(struct gb_connection *connection) +static int gb_loopback_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) { + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; struct gb_loopback *gb; struct device *dev; int retval; char name[DEBUGFS_NAMELEN]; unsigned long flags; + if (bundle->num_cports != 1) + return -ENODEV; + + cport_desc = &bundle->cport_desc[0]; + if (cport_desc->protocol_id != GREYBUS_PROTOCOL_LOOPBACK) + return -ENODEV; + gb = kzalloc(sizeof(*gb), GFP_KERNEL); if (!gb) return -ENOMEM; + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + gb_loopback_request_handler); + if (IS_ERR(connection)) { + retval = PTR_ERR(connection); + goto out_kzalloc; + } + + gb->connection = connection; + greybus_set_drvdata(bundle, gb); + init_waitqueue_head(&gb->wq); init_waitqueue_head(&gb->wq_completion); atomic_set(&gb->outstanding_operations, 0); @@ -1110,7 +1130,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) if (gb_dev.size_max <= sizeof(struct gb_loopback_transfer_request)) { retval = -EINVAL; - goto out_kzalloc; + goto out_connection_destroy; } gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request); } @@ -1120,22 +1140,24 @@ static int gb_loopback_connection_init(struct gb_connection *connection) dev_name(&connection->bundle->dev)); gb->file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, gb, &gb_loopback_debugfs_latency_ops); - gb->connection = connection; - connection->private = gb; gb->id = ida_simple_get(&loopback_ida, 0, 0, GFP_KERNEL); if (gb->id < 0) { retval = gb->id; - goto out_ida; + goto out_debugfs_remove; } + retval = gb_connection_enable(connection); + if (retval) + goto out_ida_remove; + dev = device_create_with_groups(&loopback_class, &connection->bundle->dev, MKDEV(0, 0), gb, loopback_groups, "gb_loopback%d", gb->id); if (IS_ERR(dev)) { retval = PTR_ERR(dev); - goto out_dev; + goto out_connection_disable; } gb->dev = dev; @@ -1173,28 +1195,40 @@ out_kfifo0: kfifo_free(&gb->kfifo_lat); out_conn: device_unregister(dev); -out_dev: +out_connection_disable: + gb_connection_disable(connection); +out_ida_remove: ida_simple_remove(&loopback_ida, gb->id); -out_ida: +out_debugfs_remove: debugfs_remove(gb->file); +out_connection_destroy: + gb_connection_destroy(connection); out_kzalloc: kfree(gb); return retval; } -static void gb_loopback_connection_exit(struct gb_connection *connection) +static void gb_loopback_disconnect(struct gb_bundle *bundle) { - struct gb_loopback *gb = connection->private; + struct gb_loopback *gb = greybus_get_drvdata(bundle); unsigned long flags; + gb_connection_disable(gb->connection); + if (!IS_ERR_OR_NULL(gb->task)) kthread_stop(gb->task); kfifo_free(&gb->kfifo_lat); kfifo_free(&gb->kfifo_ts); - gb_connection_latency_tag_disable(connection); + gb_connection_latency_tag_disable(gb->connection); debugfs_remove(gb->file); + + /* + * FIXME: gb_loopback_async_wait_all() is redundant now, as connection + * is disabled at the beginning and so we can't have any more + * incoming/outgoing requests. + */ gb_loopback_async_wait_all(gb); spin_lock_irqsave(&gb_dev.lock, flags); @@ -1205,17 +1239,21 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) device_unregister(gb->dev); ida_simple_remove(&loopback_ida, gb->id); + gb_connection_destroy(gb->connection); kfree(gb); } -static struct gb_protocol loopback_protocol = { - .name = "loopback", - .id = GREYBUS_PROTOCOL_LOOPBACK, - .major = GB_LOOPBACK_VERSION_MAJOR, - .minor = GB_LOOPBACK_VERSION_MINOR, - .connection_init = gb_loopback_connection_init, - .connection_exit = gb_loopback_connection_exit, - .request_recv = gb_loopback_request_recv, +static const struct greybus_bundle_id gb_loopback_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) }, + { } +}; +MODULE_DEVICE_TABLE(greybus, gb_loopback_id_table); + +static struct greybus_driver gb_loopback_driver = { + .name = "loopback", + .probe = gb_loopback_probe, + .disconnect = gb_loopback_disconnect, + .id_table = gb_loopback_id_table, }; static int loopback_init(void) @@ -1231,7 +1269,7 @@ static int loopback_init(void) if (retval) goto err; - retval = gb_protocol_register(&loopback_protocol); + retval = greybus_register(&gb_loopback_driver); if (retval) goto err_unregister; @@ -1248,7 +1286,7 @@ module_init(loopback_init); static void __exit loopback_exit(void) { debugfs_remove_recursive(gb_dev.root); - gb_protocol_deregister(&loopback_protocol); + greybus_deregister(&gb_loopback_driver); class_unregister(&loopback_class); ida_destroy(&loopback_ida); } -- cgit v1.2.3-59-g8ed1b From 6ce0eed783bd6b507fb029323d30d0e9a9d38da2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Feb 2016 16:08:30 +0530 Subject: greybus: lights: Break light setup into two parts This breaks the light setup routine into two parts, the first one allocates all the necessary resources and the second on registers lights to the required frameworks. This is required to enable only TX on the connection, until we have allocated all the resources, otherwise the request handler might get called for partially initialized structures. Signed-off-by: Viresh Kumar Reviewed-by: Rui Miguel Silva Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 46 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 3488bbd58198..c1ad6b11bc16 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -983,6 +983,14 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) return ret; } + return 0; +} + +static int gb_lights_light_register(struct gb_light *light) +{ + int ret; + int i; + /* * Then, if everything went ok in getting configurations, we register * the classdev, flash classdev and v4l2 subsystem, if a flash device is @@ -1089,7 +1097,7 @@ static int gb_lights_get_count(struct gb_lights *glights) return 0; } -static int gb_lights_setup(struct gb_lights *glights) +static int gb_lights_create_all(struct gb_lights *glights) { struct gb_connection *connection = glights->connection; int ret; @@ -1121,11 +1129,32 @@ out: return ret; } +static int gb_lights_register_all(struct gb_lights *glights) +{ + struct gb_connection *connection = glights->connection; + int ret = 0; + int i; + + mutex_lock(&glights->lights_lock); + for (i = 0; i < glights->lights_count; i++) { + ret = gb_lights_light_register(&glights->lights[i]); + if (ret < 0) { + dev_err(&connection->bundle->dev, + "Fail to enable lights device\n"); + break; + } + } + + mutex_unlock(&glights->lights_lock); + return ret; +} + static int gb_lights_event_recv(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; struct device *dev = &connection->bundle->dev; struct gb_lights *glights = connection->private; + struct gb_light *light; struct gb_message *request; struct gb_lights_event_request *payload; int ret = 0; @@ -1157,11 +1186,15 @@ static int gb_lights_event_recv(u8 type, struct gb_operation *op) event = payload->event; if (event & GB_LIGHTS_LIGHT_CONFIG) { + light = &glights->lights[light_id]; + mutex_lock(&glights->lights_lock); - gb_lights_light_release(&glights->lights[light_id]); + gb_lights_light_release(light); ret = gb_lights_light_config(glights, light_id); + if (!ret) + ret = gb_lights_light_register(light); if (ret < 0) - gb_lights_light_release(&glights->lights[light_id]); + gb_lights_light_release(light); mutex_unlock(&glights->lights_lock); } @@ -1186,7 +1219,12 @@ static int gb_lights_connection_init(struct gb_connection *connection) * Setup all the lights devices over this connection, if anything goes * wrong tear down all lights */ - ret = gb_lights_setup(glights); + ret = gb_lights_create_all(glights); + if (ret < 0) + goto out; + + /* Enable & register lights */ + ret = gb_lights_register_all(glights); if (ret < 0) goto out; -- cgit v1.2.3-59-g8ed1b From 69564dfeacf0728b60e3b2c85b357bc63213116c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Feb 2016 16:08:31 +0530 Subject: greybus: lights: convert to bundle driver Convert the legacy lights protocol driver to a bundle driver. This also fixes a potential crash should a (malicious) module have sent an early request before the private data had been initialised. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/light.c | 72 ++++++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 63597f7ac52a..2188de94a590 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -245,7 +245,6 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, { } }; MODULE_DEVICE_TABLE(greybus, legacy_id_table); diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index c1ad6b11bc16..70fcade451dc 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1149,7 +1149,7 @@ static int gb_lights_register_all(struct gb_lights *glights) return ret; } -static int gb_lights_event_recv(u8 type, struct gb_operation *op) +static int gb_lights_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct device *dev = &connection->bundle->dev; @@ -1161,8 +1161,8 @@ static int gb_lights_event_recv(u8 type, struct gb_operation *op) u8 light_id; u8 event; - if (type != GB_LIGHTS_TYPE_EVENT) { - dev_err(dev, "Unsupported unsolicited event: %u\n", type); + if (op->type != GB_LIGHTS_TYPE_EVENT) { + dev_err(dev, "Unsupported unsolicited event: %u\n", op->type); return -EINVAL; } @@ -1201,57 +1201,95 @@ static int gb_lights_event_recv(u8 type, struct gb_operation *op) return ret; } -static int gb_lights_connection_init(struct gb_connection *connection) +static int gb_lights_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) { + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; struct gb_lights *glights; int ret; + if (bundle->num_cports != 1) + return -ENODEV; + + cport_desc = &bundle->cport_desc[0]; + if (cport_desc->protocol_id != GREYBUS_PROTOCOL_LIGHTS) + return -ENODEV; + glights = kzalloc(sizeof(*glights), GFP_KERNEL); if (!glights) return -ENOMEM; + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + gb_lights_request_handler); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto out; + } + glights->connection = connection; connection->private = glights; mutex_init(&glights->lights_lock); + greybus_set_drvdata(bundle, glights); + + /* We aren't ready to receive an incoming request yet */ + ret = gb_connection_enable_tx(connection); + if (ret) + goto error_connection_destroy; + /* * Setup all the lights devices over this connection, if anything goes * wrong tear down all lights */ ret = gb_lights_create_all(glights); if (ret < 0) - goto out; + goto error_connection_disable; + + /* We are ready to receive an incoming request now, enable RX as well */ + ret = gb_connection_enable(connection); + if (ret) + goto error_connection_disable; /* Enable & register lights */ ret = gb_lights_register_all(glights); if (ret < 0) - goto out; + goto error_connection_disable; return 0; +error_connection_disable: + gb_connection_disable(connection); +error_connection_destroy: + gb_connection_destroy(connection); out: gb_lights_release(glights); return ret; } -static void gb_lights_connection_exit(struct gb_connection *connection) +static void gb_lights_disconnect(struct gb_bundle *bundle) { - struct gb_lights *glights = connection->private; + struct gb_lights *glights = greybus_get_drvdata(bundle); + + gb_connection_disable(glights->connection); + gb_connection_destroy(glights->connection); gb_lights_release(glights); } -static struct gb_protocol lights_protocol = { - .name = "lights", - .id = GREYBUS_PROTOCOL_LIGHTS, - .major = GB_LIGHTS_VERSION_MAJOR, - .minor = GB_LIGHTS_VERSION_MINOR, - .connection_init = gb_lights_connection_init, - .connection_exit = gb_lights_connection_exit, - .request_recv = gb_lights_event_recv, +static const struct greybus_bundle_id gb_lights_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, + { } }; +MODULE_DEVICE_TABLE(greybus, gb_lights_id_table); -gb_protocol_driver(&lights_protocol); +static struct greybus_driver gb_lights_driver = { + .name = "lights", + .probe = gb_lights_probe, + .disconnect = gb_lights_disconnect, + .id_table = gb_lights_id_table, +}; +module_greybus_driver(gb_lights_driver); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 7e9fba8df7606bcace9d22394f59c2047dd55091 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Feb 2016 16:08:32 +0530 Subject: greybus: power_supply: Break supply setup into two parts This breaks the power supply setup routine into two parts, the first one allocates all the necessary resources and the second on registers supplies to the required frameworks. This is required to enable only TX on the connection, until we have allocated all the resources, otherwise the request handler might get called for partially initialized structures. Signed-off-by: Viresh Kumar Reviewed-by: Rui Miguel Silva Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/power_supply.c | 57 ++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 37bea9c0cea8..c50b30d1a632 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -624,31 +624,33 @@ static int gb_power_supply_config(struct gb_power_supplies *supplies, int id) ret = gb_power_supply_description_get(gbpsy); if (ret < 0) - goto out; + return ret; ret = gb_power_supply_prop_descriptors_get(gbpsy); if (ret < 0) - goto out; + return ret; /* guarantee that we have an unique name, before register */ - ret = __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name, - sizeof(gbpsy->name)); - if (ret < 0) - goto out; + return __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name, + sizeof(gbpsy->name)); +} + +static int gb_power_supply_enable(struct gb_power_supply *gbpsy) +{ + int ret; ret = gb_power_supply_register(gbpsy); if (ret < 0) - goto out; + return ret; gbpsy->update_interval = update_interval_init; INIT_DELAYED_WORK(&gbpsy->work, gb_power_supply_work); schedule_delayed_work(&gbpsy->work, 0); -out: - /* if everything went fine just mark it for release code to know */ - if (ret == 0) - gbpsy->registered = true; - return ret; + /* everything went fine, mark it for release code to know */ + gbpsy->registered = true; + + return 0; } static int gb_power_supplies_setup(struct gb_power_supplies *supplies) @@ -685,6 +687,27 @@ out: return ret; } +static int gb_power_supplies_register(struct gb_power_supplies *supplies) +{ + struct gb_connection *connection = supplies->connection; + int ret = 0; + int i; + + mutex_lock(&supplies->supplies_lock); + + for (i = 0; i < supplies->supplies_count; i++) { + ret = gb_power_supply_enable(&supplies->supply[i]); + if (ret < 0) { + dev_err(&connection->bundle->dev, + "Fail to enable supplies devices\n"); + break; + } + } + + mutex_unlock(&supplies->supplies_lock); + return ret; +} + static int gb_power_supply_event_recv(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -758,8 +781,16 @@ static int gb_power_supply_connection_init(struct gb_connection *connection) ret = gb_power_supplies_setup(supplies); if (ret < 0) - _gb_power_supplies_release(supplies); + goto out; + + ret = gb_power_supplies_register(supplies); + if (ret < 0) + goto out; + + return 0; +out: + _gb_power_supplies_release(supplies); return ret; } -- cgit v1.2.3-59-g8ed1b From 68b1309be689ec4e93d2b48e09ccaaa8abf5b0b6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Feb 2016 16:08:33 +0530 Subject: greybus: power_supply: convert to bundle driver Convert the legacy power_supply protocol driver to a bundle driver. This also fixes a potential crash should a (malicious) module have sent an early request before the private data had been initialised. Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/power_supply.c | 72 ++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 2188de94a590..58b277979a60 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -241,7 +241,6 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index c50b30d1a632..0467537bf4f6 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -708,7 +708,7 @@ static int gb_power_supplies_register(struct gb_power_supplies *supplies) return ret; } -static int gb_power_supply_event_recv(u8 type, struct gb_operation *op) +static int gb_supplies_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_power_supplies *supplies = connection->private; @@ -719,9 +719,9 @@ static int gb_power_supply_event_recv(u8 type, struct gb_operation *op) u8 event; int ret = 0; - if (type != GB_POWER_SUPPLY_TYPE_EVENT) { + if (op->type != GB_POWER_SUPPLY_TYPE_EVENT) { dev_err(&connection->bundle->dev, - "Unsupported unsolicited event: %u\n", type); + "Unsupported unsolicited event: %u\n", op->type); return -EINVAL; } @@ -765,52 +765,90 @@ out_unlock: return ret; } -static int gb_power_supply_connection_init(struct gb_connection *connection) +static int gb_power_supply_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) { + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; struct gb_power_supplies *supplies; int ret; + if (bundle->num_cports != 1) + return -ENODEV; + + cport_desc = &bundle->cport_desc[0]; + if (cport_desc->protocol_id != GREYBUS_PROTOCOL_POWER_SUPPLY) + return -ENODEV; + supplies = kzalloc(sizeof(*supplies), GFP_KERNEL); if (!supplies) return -ENOMEM; + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + gb_supplies_request_handler); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto out; + } + supplies->connection = connection; connection->private = supplies; mutex_init(&supplies->supplies_lock); + greybus_set_drvdata(bundle, supplies); + + /* We aren't ready to receive an incoming request yet */ + ret = gb_connection_enable_tx(connection); + if (ret) + goto error_connection_destroy; + ret = gb_power_supplies_setup(supplies); if (ret < 0) - goto out; + goto error_connection_disable; + + /* We are ready to receive an incoming request now, enable RX as well */ + ret = gb_connection_enable(connection); + if (ret) + goto error_connection_disable; ret = gb_power_supplies_register(supplies); if (ret < 0) - goto out; + goto error_connection_disable; return 0; +error_connection_disable: + gb_connection_disable(connection); +error_connection_destroy: + gb_connection_destroy(connection); out: _gb_power_supplies_release(supplies); return ret; } -static void gb_power_supply_connection_exit(struct gb_connection *connection) +static void gb_power_supply_disconnect(struct gb_bundle *bundle) { - struct gb_power_supplies *supplies = connection->private; + struct gb_power_supplies *supplies = greybus_get_drvdata(bundle); + + gb_connection_disable(supplies->connection); + gb_connection_destroy(supplies->connection); _gb_power_supplies_release(supplies); } -static struct gb_protocol power_supply_protocol = { - .name = "power_supply", - .id = GREYBUS_PROTOCOL_POWER_SUPPLY, - .major = GB_POWER_SUPPLY_VERSION_MAJOR, - .minor = GB_POWER_SUPPLY_VERSION_MINOR, - .connection_init = gb_power_supply_connection_init, - .connection_exit = gb_power_supply_connection_exit, - .request_recv = gb_power_supply_event_recv, +static const struct greybus_bundle_id gb_power_supply_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) }, + { } }; +MODULE_DEVICE_TABLE(greybus, gb_power_supply_id_table); -gb_protocol_driver(&power_supply_protocol); +static struct greybus_driver gb_power_supply_driver = { + .name = "power_supply", + .probe = gb_power_supply_probe, + .disconnect = gb_power_supply_disconnect, + .id_table = gb_power_supply_id_table, +}; +module_greybus_driver(gb_power_supply_driver); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From c161c0fc2f18ab8c299d6cb716d6264427e38867 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 14 Feb 2016 02:33:04 +0200 Subject: greybus: camera: Factorize link power mode configuration code into a function Avoid duplicating the same code block multiple times. Signed-off-by: Laurent Pinchart Tested-by: Jacopo Mondi Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 116 ++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 64 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 3f31a2fd7e5e..8383770910c5 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -107,6 +107,54 @@ static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { * Camera Protocol Operations */ +static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, + bool hs) +{ + struct gb_svc *svc = gcam->connection->hd->svc; + int ret; + + if (hs) + ret = gb_svc_intf_set_power_mode(svc, intf_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_PWRM_RXTERMINATION | + GB_SVC_PWRM_TXTERMINATION, 0); + else + ret = gb_svc_intf_set_power_mode(svc, intf_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 1, 2, + 0, 0); + + return ret; +} + +static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) +{ + struct gb_interface *intf = gcam->connection->intf; + struct gb_svc *svc = gcam->connection->hd->svc; + int ret; + + ret = gb_camera_set_intf_power_mode(gcam, intf->interface_id, hs); + if (ret < 0) { + gcam_err(gcam, "failed to set module interface to %s (%d)\n", + hs ? "HS" : "PWM", ret); + return ret; + } + + ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs); + if (ret < 0) { + gcam_err(gcam, "failed to set AP interface to %s (%d)\n", + hs ? "HS" : "PWM", ret); + return ret; + } + + return 0; +} + struct ap_csi_config_request { __u8 csi_id; __u8 clock_mode; @@ -120,8 +168,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, unsigned int *flags, struct gb_camera_stream_config *streams) { - struct gb_interface *intf = gcam->connection->intf; - struct gb_svc *svc = gcam->connection->hd->svc; struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; struct ap_csi_config_request csi_cfg; @@ -151,49 +197,13 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, * before CSI interfaces gets configured */ if (nstreams && !(*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { - ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, - GB_SVC_UNIPRO_HS_SERIES_A, - GB_SVC_UNIPRO_FAST_MODE, 2, 2, - GB_SVC_UNIPRO_FAST_MODE, 2, 2, - GB_SVC_PWRM_RXTERMINATION | - GB_SVC_PWRM_TXTERMINATION, 0); - if (ret < 0) - goto done; - - ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, - GB_SVC_UNIPRO_HS_SERIES_A, - GB_SVC_UNIPRO_FAST_MODE, 2, 2, - GB_SVC_UNIPRO_FAST_MODE, 2, 2, - GB_SVC_PWRM_RXTERMINATION | - GB_SVC_PWRM_TXTERMINATION, 0); + ret = gb_camera_set_power_mode(gcam, true); if (ret < 0) goto done; } else if (nstreams == 0) { - ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, - GB_SVC_UNIPRO_HS_SERIES_A, - GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, - GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, - 0, 0); - if (ret < 0) { - gcam_err(gcam, "can't take camera link to PWM-G1 auto: %d\n", - ret); - goto done; - } - - ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, - GB_SVC_UNIPRO_HS_SERIES_A, - GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, - GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, - 0, 0); - if (ret < 0) { - gcam_err(gcam, "can't take AP link to PWM-G1 auto: %d\n", - ret); + ret = gb_camera_set_power_mode(gcam, false); + if (ret < 0) goto done; - } } req->num_streams = nstreams; @@ -283,29 +293,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, return ret; set_unipro_slow_mode: - ret = gb_svc_intf_set_power_mode(svc, intf->interface_id, - GB_SVC_UNIPRO_HS_SERIES_A, - GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, - GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, - 0, 0); - if (ret < 0) { - gcam_err(gcam, "can't take camera link to PWM-G1 auto: %d\n", - ret); - goto done; - } - - ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, - GB_SVC_UNIPRO_HS_SERIES_A, - GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, - GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, - 0, 0); - if (ret < 0) - gcam_err(gcam, "can't take AP link to PWM-G1 auto: %d\n", - ret); + ret = gb_camera_set_power_mode(gcam, false); done: kfree(req); -- cgit v1.2.3-59-g8ed1b From b573b0e65616c1ae5c5dd2c14fd62d3ded3d5466 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 14 Feb 2016 02:33:05 +0200 Subject: greybus: camera: Clean up when AP link power mode configuration failed Restore the module link power mode to the previous state in that case. Signed-off-by: Laurent Pinchart Reviewed-by: Gjorgji Rosikopulos Tested-by: Jacopo Mondi Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 8383770910c5..25dbf690e1fc 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -147,6 +147,7 @@ static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs); if (ret < 0) { + gb_camera_set_intf_power_mode(gcam, intf->interface_id, !hs); gcam_err(gcam, "failed to set AP interface to %s (%d)\n", hs ? "HS" : "PWM", ret); return ret; -- cgit v1.2.3-59-g8ed1b From 66c3607076e7e801ab20077dc2d0ed45693eea10 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 14 Feb 2016 02:33:06 +0200 Subject: greybus: camera: Set power mode after configuring streams There's no need to set the power mode before configuring streams, doing it after simplifies code. Signed-off-by: Laurent Pinchart Reviewed-by: Gjorgji Rosikopulos Tested-by: Jacopo Mondi Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 43 +++++++++++++++------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 25dbf690e1fc..7eef6eceec6c 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -192,21 +192,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto done; } - /* - * Setup unipro link speed before actually issuing configuration - * to the camera module, to assure unipro network speed is set - * before CSI interfaces gets configured - */ - if (nstreams && !(*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { - ret = gb_camera_set_power_mode(gcam, true); - if (ret < 0) - goto done; - } else if (nstreams == 0) { - ret = gb_camera_set_power_mode(gcam, false); - if (ret < 0) - goto done; - } - req->num_streams = nstreams; req->flags = *flags; req->padding = 0; @@ -224,19 +209,19 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, GB_CAMERA_TYPE_CONFIGURE_STREAMS, req, req_size, resp, resp_size); if (ret < 0) - goto set_unipro_slow_mode; + goto done; if (resp->num_streams > nstreams) { gcam_dbg(gcam, "got #streams %u > request %u\n", resp->num_streams, nstreams); ret = -EIO; - goto set_unipro_slow_mode; + goto done; } if (resp->padding != 0) { gcam_dbg(gcam, "response padding != 0"); ret = -EIO; - goto set_unipro_slow_mode; + goto done; } for (i = 0; i < nstreams; ++i) { @@ -253,14 +238,25 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) { gcam_dbg(gcam, "stream #%u padding != 0", i); ret = -EIO; - goto set_unipro_slow_mode; + goto done; } } if (nstreams && resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) { *flags = resp->flags; *num_streams = resp->num_streams; - goto set_unipro_slow_mode; + goto done; + } + + /* Setup unipro link speed. */ + if (nstreams && !(*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { + ret = gb_camera_set_power_mode(gcam, true); + if (ret < 0) + goto done; + } else if (nstreams == 0) { + ret = gb_camera_set_power_mode(gcam, false); + if (ret < 0) + goto done; } memset(&csi_cfg, 0, sizeof(csi_cfg)); @@ -289,13 +285,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, *num_streams = resp->num_streams; ret = 0; - kfree(req); - kfree(resp); - return ret; - -set_unipro_slow_mode: - ret = gb_camera_set_power_mode(gcam, false); - done: kfree(req); kfree(resp); -- cgit v1.2.3-59-g8ed1b From 640924d2172da9b6f8a0c9372c3ea3b83f68e2f8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 14 Feb 2016 02:33:07 +0200 Subject: greybus: camera: Don't configure CSI TX in test only mode When the GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY flag is set by the caller the configure streams operation should only test the requested settings without modifying the hardware state. This applies for both the module, the UniPro links power modes and the AP bridge settings. Return early when the flag is set to avoid modifying the AP bridge CSI TX settings. Signed-off-by: Laurent Pinchart Reviewed-by: Gjorgji Rosikopulos Tested-by: Jacopo Mondi Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 7eef6eceec6c..bb8bc175dc0a 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -242,26 +242,21 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, } } - if (nstreams && resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) { + if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) || + (*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { *flags = resp->flags; *num_streams = resp->num_streams; goto done; } /* Setup unipro link speed. */ - if (nstreams && !(*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { - ret = gb_camera_set_power_mode(gcam, true); - if (ret < 0) - goto done; - } else if (nstreams == 0) { - ret = gb_camera_set_power_mode(gcam, false); - if (ret < 0) - goto done; - } + ret = gb_camera_set_power_mode(gcam, nstreams != 0); + if (ret < 0) + goto done; + /* Configure the CSI transmitter. Hardcode the parameters for now. */ memset(&csi_cfg, 0, sizeof(csi_cfg)); - /* Configure the CSI transmitter. Hardcode the parameters for now. */ if (nstreams) { csi_cfg.csi_id = 1; csi_cfg.clock_mode = 0; -- cgit v1.2.3-59-g8ed1b From 4a7908cb71875991cabb2194c1c6b05cb97187ae Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 12 Feb 2016 21:48:03 +0530 Subject: greybus: manifest: Parse cports (within a bundle) in the order from manifest blob The order in which cports (of a bundle) are present in the manifest blob is important for gbsim, as it allocates hd_cport_id's for them sequentially. For example, if there are two cports (1 and 2, in order 1->2) present in a bundle in the manifest blob, then gbsim allocates hd_cport_id X and X+1 for them. This is done on the assumption that kernel will do the same. Though it shouldn't have had any such assumptions since the beginning. But with a recent patch that sequence is changed, and it broke the assumption gbsim had. While parsing the manifest blob, the cports within a bundle are now moved to another list using list_move() and then they are picked one by one from the HEAD of the list. list_move() first deletes the node and then adds it to HEAD as it uses list_add() and not list_add_tail(). And that reverses the order in which the cports were present in the original list. And because of this, the messages destined for cport 1 are delivered to cport 2 and the ones for cport 2 are delivered to cport 1. In order to get gbsim working with greybus, keep the cport list in the order in which they were present in manifest, by replacing list_move() with list_move_tail(). Its a trivial patch and shouldn't have any side effects on the working of greybus with nuttx. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/manifest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 74e3be997023..070afc619f84 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -263,7 +263,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) goto exit; } } - list_move(&desc->links, &list); + list_move_tail(&desc->links, &list); count++; } -- cgit v1.2.3-59-g8ed1b From ee97e24ac63510c5836ac71ce88c9f61e265ee84 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Wed, 17 Feb 2016 01:27:52 +0530 Subject: greybus: arche-apb-ctrl: Do not coldboot APBs in probe Since parent driver (SVC) is controlling APBs directly, we do not need to bringup APBs in its own probe. Testing Done: Tested on EVT1.2. Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index b888da376b21..3086306f04fb 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -387,15 +387,6 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) return ret; } - ret = coldboot_seq(pdev); - if (ret) { - dev_err(dev, "failed to set init state of control signal %d\n", - ret); - device_remove_file(dev, &dev_attr_state); - platform_set_drvdata(pdev, NULL); - return ret; - } - dev_info(&pdev->dev, "Device registered successfully\n"); return 0; } -- cgit v1.2.3-59-g8ed1b From b59281ac077414d879e66798f8e44eb976967814 Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Wed, 10 Feb 2016 14:19:29 +0100 Subject: greybus: loopback: Add reserved fields to transfer request All loopback transfer operations should have an identical header format in order to facilitate bandwidth and data movement analysis. Suggested-by: Bryan O'Donoghue Signed-off-by: Axel Haslam Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index cd64ac84dad2..6b192924a566 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1053,8 +1053,14 @@ struct gb_uart_serial_state_request { #define GB_LOOPBACK_TYPE_TRANSFER 0x03 #define GB_LOOPBACK_TYPE_SINK 0x04 +/* + * Loopback request/response header format should be identical + * to simplify bandwidth and data movement analysis. + */ struct gb_loopback_transfer_request { __le32 len; + __le32 reserved0; + __le32 reserved1; __u8 data[0]; } __packed; -- cgit v1.2.3-59-g8ed1b From 478ce7203c2cf3f241f11587717336a60d391269 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 17 Feb 2016 19:30:40 +0100 Subject: greybus: es2: fix cport-count error handling Make sure to check for short transfers when retrieving the bridge cport count. Also clear the request buffer when allocating it. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index e39bd58e3a5c..5ade51e83745 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -861,7 +861,7 @@ static int apb_get_cport_count(struct usb_device *udev) int retval; __le16 *cport_count; - cport_count = kmalloc(sizeof(*cport_count), GFP_KERNEL); + cport_count = kzalloc(sizeof(*cport_count), GFP_KERNEL); if (!cport_count) return -ENOMEM; @@ -870,9 +870,13 @@ static int apb_get_cport_count(struct usb_device *udev) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, cport_count, sizeof(*cport_count), ES2_TIMEOUT); - if (retval < 0) { + if (retval != sizeof(*cport_count)) { dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", retval); + + if (retval >= 0) + retval = -EIO; + goto out; } -- cgit v1.2.3-59-g8ed1b From c53b0b27d57cc1dff50080c911b48b2af7728dc4 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Wed, 17 Feb 2016 18:21:06 +0100 Subject: greybus: gb_loopback: Fix throughput calculations Throughput and requests per second calculations are broken for asynchronous request. Instead of calculate the throughput for each iteration, calculate it once at the end of the test. Signed-off-by: Alexandre Bailon Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 57 ++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index f3ae2e9fc1bb..c022edbfb135 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -81,6 +81,7 @@ struct gb_loopback { atomic_t outstanding_operations; /* Per connection stats */ + struct timeval ts; struct gb_loopback_stats latency; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; @@ -520,7 +521,6 @@ static void gb_loopback_async_operation_callback(struct gb_operation *operation) gb_loopback_push_latency_ts(gb, &op_async->ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&op_async->ts, &te); - gb_loopback_calculate_stats(gb); } if (op_async->pending) { @@ -529,6 +529,7 @@ static void gb_loopback_async_operation_callback(struct gb_operation *operation) del_timer_sync(&op_async->timer); gb_loopback_async_operation_put(op_async); } + gb_loopback_calculate_stats(gb); mutex_unlock(&gb->mutex); dev_dbg(&gb->connection->bundle->dev, "complete operation %d\n", @@ -846,6 +847,7 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) /* Should be initialized at least once per transaction set */ gb->apbridge_latency_ts = 0; gb->gpbridge_latency_ts = 0; + memset(&gb->ts, 0, sizeof(struct timeval)); } static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) @@ -860,15 +862,15 @@ static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency) { - u32 req = USEC_PER_SEC; + u64 req = gb->requests_completed * USEC_PER_SEC; do_div(req, latency); - gb_loopback_update_stats(&gb->requests_per_second, req); + gb_loopback_update_stats(&gb->requests_per_second, (u32)req); } static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) { - u32 throughput; + u64 throughput; u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2; switch (gb->type) { @@ -887,14 +889,13 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) return; } - /* Calculate bytes per second */ - throughput = USEC_PER_SEC; + aggregate_size *= gb->requests_completed; + throughput = aggregate_size * USEC_PER_SEC; do_div(throughput, latency); - throughput *= aggregate_size; - gb_loopback_update_stats(&gb->throughput, throughput); + gb_loopback_update_stats(&gb->throughput, (u32)throughput); } -static void gb_loopback_calculate_stats(struct gb_loopback *gb) +static void gb_loopback_calculate_latency_stats(struct gb_loopback *gb) { u32 lat; @@ -907,10 +908,6 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) /* Raw latency log on a per thread basis */ kfifo_in(&gb->kfifo_lat, (unsigned char *)&lat, sizeof(lat)); - /* Log throughput and requests using latency as benchmark */ - gb_loopback_throughput_update(gb, lat); - gb_loopback_requests_update(gb, lat); - /* Log the firmware supplied latency values */ gb_loopback_update_stats(&gb->apbridge_unipro_latency, gb->apbridge_latency_ts); @@ -918,6 +915,27 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) gb->gpbridge_latency_ts); } +static void gb_loopback_calculate_stats(struct gb_loopback *gb) +{ + u64 nlat; + u32 lat; + struct timeval te; + + gb_loopback_calculate_latency_stats(gb); + + if (gb->iteration_count == gb->iteration_max) { + do_gettimeofday(&te); + nlat = gb_loopback_calc_latency(&gb->ts, &te); + lat = gb_loopback_nsec_to_usec_latency(nlat); + + gb_loopback_throughput_update(gb, lat); + gb_loopback_requests_update(gb, lat); + + memset(&gb->ts, 0, sizeof(struct timeval)); + gb->type = 0; + } +} + static void gb_loopback_async_wait_to_send(struct gb_loopback *gb) { if (!(gb->async && gb->outstanding_operations_max)) @@ -955,8 +973,10 @@ static int gb_loopback_fn(void *data) /* Optionally terminate */ if (send_count == gb->iteration_max) { - gb->type = 0; - send_count = 0; + if (!gb->async) + send_count = 0; + else if (gb->iteration_count == gb->iteration_max) + send_count = 0; mutex_unlock(&gb->mutex); continue; } @@ -965,10 +985,15 @@ static int gb_loopback_fn(void *data) type = gb->type; mutex_unlock(&gb->mutex); + if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) + do_gettimeofday(&gb->ts); + /* Else operations to perform */ if (gb->async) { if (type == GB_LOOPBACK_TYPE_PING) { error = gb_loopback_async_ping(gb); + if (!error) + gb->requests_completed++; gb_loopback_calculate_stats(gb); } else if (type == GB_LOOPBACK_TYPE_TRANSFER) { error = gb_loopback_async_transfer(gb, size); @@ -989,6 +1014,8 @@ static int gb_loopback_fn(void *data) if (error) gb->error++; + else + gb->requests_completed++; gb->iteration_count++; gb_loopback_calculate_stats(gb); } -- cgit v1.2.3-59-g8ed1b From 492331435465977407e43865c19e34918cd90a66 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 17 Feb 2016 14:32:51 +0000 Subject: greybus: lights: remove unnecessary checks We do not need to check for channels and lights as they can never be NULL as a big memory array elements. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 70fcade451dc..6c5b9994457c 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1026,9 +1026,6 @@ static void gb_lights_channel_free(struct gb_channel *channel) static void gb_lights_channel_release(struct gb_channel *channel) { - if (!channel) - return; - channel->releasing = true; gb_lights_channel_unregister(channel); @@ -1041,8 +1038,6 @@ static void gb_lights_light_release(struct gb_light *light) int i; int count; - if (!light) - return; count = light->channels_count; -- cgit v1.2.3-59-g8ed1b From 137f717942c9cff82484ee90647881d9c524178f Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 17 Feb 2016 14:32:52 +0000 Subject: greybus: lights: remove has_flash on failure If register to v4l2 fails just mark the light as not having flash so in release we do not try to unregister. Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 6c5b9994457c..4c46d149cb7d 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1004,8 +1004,10 @@ static int gb_lights_light_register(struct gb_light *light) if (light->has_flash) { ret = gb_lights_light_v4l2_register(light); - if (ret < 0) + if (ret < 0) { + light->has_flash = false; return ret; + } } return 0; -- cgit v1.2.3-59-g8ed1b From c6ad27a98ce5f9718c7235cf02de2055897f56d5 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 17 Feb 2016 14:32:53 +0000 Subject: greybus: lights: fix check for configured lights The validation for a complete configured light is wrong and it is reworked to make sure that only when the light is ready, will handle request events. Signed-off-by: Rui Miguel Silva Reported-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 4c46d149cb7d..17877f7e2ee4 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -55,6 +55,7 @@ struct gb_light { u8 channels_count; struct gb_channel *channels; bool has_flash; + bool ready; #ifdef V4L2_HAVE_FLASH struct v4l2_flash *v4l2_flash; #endif @@ -1002,6 +1003,8 @@ static int gb_lights_light_register(struct gb_light *light) return ret; } + light->ready = true; + if (light->has_flash) { ret = gb_lights_light_v4l2_register(light); if (ret < 0) { @@ -1040,6 +1043,7 @@ static void gb_lights_light_release(struct gb_light *light) int i; int count; + light->ready = false; count = light->channels_count; @@ -1174,7 +1178,8 @@ static int gb_lights_request_handler(struct gb_operation *op) payload = request->payload; light_id = payload->light_id; - if (light_id >= glights->lights_count || !&glights->lights[light_id]) { + if (light_id >= glights->lights_count || !glights->lights || + !glights->lights[light_id].ready) { dev_err(dev, "Event received for unconfigured light id: %d\n", light_id); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From b19df7b9950cd8c1212dcc775dffcdaace391db3 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 16 Feb 2016 00:27:27 +0530 Subject: greybus: audio: Enable support for multiple codec modules Update params, sequence in response to changes in msm8994 helper APIs Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 107 ++++++++++++---------------------- 1 file changed, 36 insertions(+), 71 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index c05ab4fb8754..cabe289b9324 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -430,65 +430,6 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec, return val; } -static struct snd_soc_codec_driver soc_codec_dev_gbcodec = { - .probe = gbcodec_probe, - .remove = gbcodec_remove, - - .read = gbcodec_read, - .write = gbcodec_write, - - .reg_cache_size = GBCODEC_REG_COUNT, - .reg_cache_default = gbcodec_reg_defaults, - .reg_word_size = 1, - - .idle_bias_off = true, - .ignore_pmdown_time = 1, -}; - -/* - * GB codec DAI link related - */ -static struct snd_soc_dai_link gbaudio_dailink = { - .name = "PRI_MI2S_RX", - .stream_name = "Primary MI2S Playback", - .platform_name = "msm-pcm-routing", - .cpu_dai_name = "msm-dai-q6-mi2s.0", - .no_pcm = 1, - .be_id = 34, -}; - -static int gbaudio_add_dailinks(struct gbaudio_codec_info *gbcodec) -{ - int ret, i; - char *dai_link_name; - struct snd_soc_dai_link *dailink; - struct device *dev = gbcodec->dev; - - dailink = &gbaudio_dailink; - dailink->codec_name = gbcodec->name; - - /* FIXME - * allocate memory for DAI links based on count. - * currently num_dai_links=1, so using static struct - */ - gbcodec->num_dai_links = 1; - - for (i = 0; i < gbcodec->num_dai_links; i++) { - gbcodec->dailink_name[i] = dai_link_name = - devm_kzalloc(dev, NAME_SIZE, GFP_KERNEL); - snprintf(dai_link_name, NAME_SIZE, "GB %d.%d PRI_MI2S_RX", - gbcodec->dev_id, i); - dailink->name = dai_link_name; - dailink->codec_dai_name = gbcodec->dais[i].name; - } - - ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", dailink, 1); - if (ret) - dev_err(dev, "%d:Error while adding DAI link\n", ret); - - return ret; -} - /* * gb_snd management functions */ @@ -542,12 +483,21 @@ static int gbaudio_register_codec(struct gbaudio_codec_info *gbcodec) int ret, i; struct device *dev = gbcodec->dev; struct gb_connection *connection = gbcodec->mgmt_connection; + struct snd_soc_codec_driver *soc_codec_dev_gbcodec; /* * FIXME: malloc for topology happens via audio_gb driver * should be done within codec driver itself */ struct gb_audio_topology *topology; + soc_codec_dev_gbcodec = devm_kzalloc(gbcodec->dev, + sizeof(*soc_codec_dev_gbcodec), + GFP_KERNEL); + if (!soc_codec_dev_gbcodec) { + dev_err(gbcodec->dev, "Malloc failed for codec_driver\n"); + return -ENOMEM; + } + ret = gb_connection_enable(connection); if (ret) { dev_err(dev, "%d: Error while enabling mgmt connection\n", ret); @@ -572,19 +522,32 @@ static int gbaudio_register_codec(struct gbaudio_codec_info *gbcodec) gbcodec->topology = topology; /* update codec info */ - soc_codec_dev_gbcodec.controls = gbcodec->kctls; - soc_codec_dev_gbcodec.num_controls = gbcodec->num_kcontrols; - soc_codec_dev_gbcodec.dapm_widgets = gbcodec->widgets; - soc_codec_dev_gbcodec.num_dapm_widgets = gbcodec->num_dapm_widgets; - soc_codec_dev_gbcodec.dapm_routes = gbcodec->routes; - soc_codec_dev_gbcodec.num_dapm_routes = gbcodec->num_dapm_routes; + soc_codec_dev_gbcodec->probe = gbcodec_probe, + soc_codec_dev_gbcodec->remove = gbcodec_remove, + + soc_codec_dev_gbcodec->read = gbcodec_read, + soc_codec_dev_gbcodec->write = gbcodec_write, + + soc_codec_dev_gbcodec->reg_cache_size = GBCODEC_REG_COUNT, + soc_codec_dev_gbcodec->reg_cache_default = gbcodec_reg_defaults, + soc_codec_dev_gbcodec->reg_word_size = 1, + + soc_codec_dev_gbcodec->idle_bias_off = true, + soc_codec_dev_gbcodec->ignore_pmdown_time = 1, + + soc_codec_dev_gbcodec->controls = gbcodec->kctls; + soc_codec_dev_gbcodec->num_controls = gbcodec->num_kcontrols; + soc_codec_dev_gbcodec->dapm_widgets = gbcodec->widgets; + soc_codec_dev_gbcodec->num_dapm_widgets = gbcodec->num_dapm_widgets; + soc_codec_dev_gbcodec->dapm_routes = gbcodec->routes; + soc_codec_dev_gbcodec->num_dapm_routes = gbcodec->num_dapm_routes; /* update DAI info */ for (i = 0; i < gbcodec->num_dais; i++) gbcodec->dais[i].ops = &gbcodec_dai_ops; /* register codec */ - ret = snd_soc_register_codec(dev, &soc_codec_dev_gbcodec, + ret = snd_soc_register_codec(dev, soc_codec_dev_gbcodec, gbcodec->dais, 1); if (ret) { dev_err(dev, "%d:Failed to register codec\n", ret); @@ -592,11 +555,13 @@ static int gbaudio_register_codec(struct gbaudio_codec_info *gbcodec) } /* update DAI links in response to this codec */ - ret = gbaudio_add_dailinks(gbcodec); + ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", gbcodec->name, + gbcodec->dais[0].name, 1); if (ret) { dev_err(dev, "%d: Failed to add DAI links\n", ret); goto add_dailink_err; } + gbcodec->num_dai_links = 1; return 0; @@ -615,8 +580,8 @@ tplg_fetch_err: static void gbaudio_unregister_codec(struct gbaudio_codec_info *gbcodec) { gb_audio_cleanup(gbcodec); - msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", &gbaudio_dailink, - 1); + msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", gbcodec->name, + gbcodec->dais[0].name, 1); snd_soc_unregister_codec(gbcodec->dev); gbaudio_tplg_release(gbcodec); kfree(gbcodec->topology); @@ -792,7 +757,7 @@ static int gb_audio_probe(struct gb_bundle *bundle, gbcodec->manager_id = gb_audio_manager_add(&desc); atomic_set(&gbcodec->is_connected, 1); - list_add(&gbcodec->list, &gb_codec_list); + list_add_tail(&gbcodec->list, &gb_codec_list); dev_dbg(dev, "Add GB Audio device:%s\n", gbcodec->name); mutex_unlock(&gb_codec_list_lock); @@ -826,7 +791,6 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) mutex_lock(&gb_codec_list_lock); atomic_set(&gbcodec->is_connected, 0); - list_del(&gbcodec->list); /* inform uevent to above layers */ gb_audio_manager_remove(gbcodec->manager_id); @@ -842,6 +806,7 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) } gb_connection_destroy(gbcodec->mgmt_connection); gbcodec->mgmt_connection = NULL; + list_del(&gbcodec->list); mutex_unlock(&gbcodec->lock); devm_kfree(&bundle->dev, gbcodec); -- cgit v1.2.3-59-g8ed1b From 29386f058a758f5ef6e8a522101fcbfd0ef07a19 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 16 Feb 2016 00:27:28 +0530 Subject: greybus: audio: schedule workqueue to perform codec cleanup on module removal In response to codec module removal, user space is reported about the event. In response to this, ALSA layer will update DAPM route and cleanup DAPM states. As a fallback mechanism, kernel can cleanup the DAPM state for codec module. But, this would cause immediate playback (first trial) to fail, since DSP is still in inconsistent state. To avoid such situation, a workqueue is scheduled for codec cleanup with timeout=50ms. Thus, normally it is expected from above layers to update routes and perform cleanup. However, fallback mechanism still holds good after 50ms. Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 36 ++++++++++++++++++++++++++++------- drivers/staging/greybus/audio_codec.h | 1 + 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index cabe289b9324..25b1042d3c77 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -80,11 +80,7 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); - if (!atomic_read(&gb->is_connected)) - return; - /* find the dai */ - mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); if (!gb_dai) { dev_err(dai->dev, "%s: DAI not registered\n", dai->name); @@ -93,6 +89,13 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, atomic_dec(&gb_dai->users); + if (!atomic_read(&gb->is_connected)) { + if (!atomic_read(&gb_dai->users)) + wake_up_interruptible(&gb_dai->wait_queue); + return; + } + + mutex_lock(&gb->lock); /* deactivate rx/tx */ cportid = gb_dai->connection->intf_cport_id; @@ -121,6 +124,11 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, func_exit: mutex_unlock(&gb->lock); + /* + if (!atomic_read(&gb_dai->users)) + wake_up_interruptible(&gb_dai->wait_queue); + */ + return; } @@ -290,8 +298,11 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); - if (!atomic_read(&gb->is_connected)) + if (!atomic_read(&gb->is_connected)) { + if (cmd == SNDRV_PCM_TRIGGER_STOP) + return 0; return -ENODEV; + } /* find the dai */ mutex_lock(&gb->lock); @@ -443,9 +454,10 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec, */ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) { - int cportid, ret; + int cportid, ret, timeout_result; struct gbaudio_dai *gb_dai; struct gb_connection *connection; + long timeout = msecs_to_jiffies(50); /* 50ms */ struct device *dev = gb->dev; list_for_each_entry(gb_dai, &gb->dai_list, list) { @@ -454,6 +466,16 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) * manually */ if (atomic_read(&gb_dai->users)) { + /* schedule a wait event */ + timeout_result = + wait_event_interruptible_timeout( + gb_dai->wait_queue, + !atomic_read(&gb_dai->users), + timeout); + if (!timeout_result) + dev_warn(dev, "%s:DAI still in use.\n", + gb_dai->name); + connection = gb_dai->connection; /* PB active */ ret = gb_audio_apbridgea_stop_tx(connection, 0); @@ -473,7 +495,6 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) if (ret) dev_info(dev, "%d:Failed during unregister cport\n", ret); - atomic_dec(&gb_dai->users); } } } @@ -655,6 +676,7 @@ static int gb_audio_add_data_connection(struct gbaudio_codec_info *gbcodec, connection->private = gbcodec; atomic_set(&dai->users, 0); + init_waitqueue_head(&dai->wait_queue); dai->data_cport = connection->intf_cport_id; dai->connection = connection; list_add(&dai->list, &gbcodec->dai_list); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 4c19bd884488..fc60c36aa040 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -88,6 +88,7 @@ struct gbaudio_dai { atomic_t users; struct gb_connection *connection; struct list_head list; + wait_queue_head_t wait_queue; }; struct gbaudio_codec_info { -- cgit v1.2.3-59-g8ed1b From b07868bda2fb61b784dc260dd075d84ac994d599 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 16 Feb 2016 22:16:33 +0530 Subject: greybus: audio: return success for stop trigger if device removed In case GB codec module is already removed, no action is required at the HW level. Thus, report SUCCESS to above layer. Reporting error to above layer will cause repeated trials and won't allow to update DPCM connections. Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 25b1042d3c77..d820116dd196 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -362,6 +362,10 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(dai->dev, "%d:Error during %s stream\n", ret, start ? "Start" : "Stop"); + /* in case device removed, return 0 for stop trigger */ + if (stop && (ret == -ENODEV)) + ret = 0; + func_exit: mutex_unlock(&gb->lock); return ret; -- cgit v1.2.3-59-g8ed1b From 2422d36696f36ea92f17bb34a129a74f9b173891 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 17 Feb 2016 16:30:38 -0800 Subject: greybus: Revert "gb_loopback: Fix throughput calculations" This reverts commit 9b9b046af237f5674c2f7ca991dc62332b2d4041 Bryan wants more feedback first. Reported-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 57 ++++++++++---------------------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index c022edbfb135..f3ae2e9fc1bb 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -81,7 +81,6 @@ struct gb_loopback { atomic_t outstanding_operations; /* Per connection stats */ - struct timeval ts; struct gb_loopback_stats latency; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; @@ -521,6 +520,7 @@ static void gb_loopback_async_operation_callback(struct gb_operation *operation) gb_loopback_push_latency_ts(gb, &op_async->ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&op_async->ts, &te); + gb_loopback_calculate_stats(gb); } if (op_async->pending) { @@ -529,7 +529,6 @@ static void gb_loopback_async_operation_callback(struct gb_operation *operation) del_timer_sync(&op_async->timer); gb_loopback_async_operation_put(op_async); } - gb_loopback_calculate_stats(gb); mutex_unlock(&gb->mutex); dev_dbg(&gb->connection->bundle->dev, "complete operation %d\n", @@ -847,7 +846,6 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) /* Should be initialized at least once per transaction set */ gb->apbridge_latency_ts = 0; gb->gpbridge_latency_ts = 0; - memset(&gb->ts, 0, sizeof(struct timeval)); } static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) @@ -862,15 +860,15 @@ static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency) { - u64 req = gb->requests_completed * USEC_PER_SEC; + u32 req = USEC_PER_SEC; do_div(req, latency); - gb_loopback_update_stats(&gb->requests_per_second, (u32)req); + gb_loopback_update_stats(&gb->requests_per_second, req); } static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) { - u64 throughput; + u32 throughput; u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2; switch (gb->type) { @@ -889,13 +887,14 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) return; } - aggregate_size *= gb->requests_completed; - throughput = aggregate_size * USEC_PER_SEC; + /* Calculate bytes per second */ + throughput = USEC_PER_SEC; do_div(throughput, latency); - gb_loopback_update_stats(&gb->throughput, (u32)throughput); + throughput *= aggregate_size; + gb_loopback_update_stats(&gb->throughput, throughput); } -static void gb_loopback_calculate_latency_stats(struct gb_loopback *gb) +static void gb_loopback_calculate_stats(struct gb_loopback *gb) { u32 lat; @@ -908,6 +907,10 @@ static void gb_loopback_calculate_latency_stats(struct gb_loopback *gb) /* Raw latency log on a per thread basis */ kfifo_in(&gb->kfifo_lat, (unsigned char *)&lat, sizeof(lat)); + /* Log throughput and requests using latency as benchmark */ + gb_loopback_throughput_update(gb, lat); + gb_loopback_requests_update(gb, lat); + /* Log the firmware supplied latency values */ gb_loopback_update_stats(&gb->apbridge_unipro_latency, gb->apbridge_latency_ts); @@ -915,27 +918,6 @@ static void gb_loopback_calculate_latency_stats(struct gb_loopback *gb) gb->gpbridge_latency_ts); } -static void gb_loopback_calculate_stats(struct gb_loopback *gb) -{ - u64 nlat; - u32 lat; - struct timeval te; - - gb_loopback_calculate_latency_stats(gb); - - if (gb->iteration_count == gb->iteration_max) { - do_gettimeofday(&te); - nlat = gb_loopback_calc_latency(&gb->ts, &te); - lat = gb_loopback_nsec_to_usec_latency(nlat); - - gb_loopback_throughput_update(gb, lat); - gb_loopback_requests_update(gb, lat); - - memset(&gb->ts, 0, sizeof(struct timeval)); - gb->type = 0; - } -} - static void gb_loopback_async_wait_to_send(struct gb_loopback *gb) { if (!(gb->async && gb->outstanding_operations_max)) @@ -973,10 +955,8 @@ static int gb_loopback_fn(void *data) /* Optionally terminate */ if (send_count == gb->iteration_max) { - if (!gb->async) - send_count = 0; - else if (gb->iteration_count == gb->iteration_max) - send_count = 0; + gb->type = 0; + send_count = 0; mutex_unlock(&gb->mutex); continue; } @@ -985,15 +965,10 @@ static int gb_loopback_fn(void *data) type = gb->type; mutex_unlock(&gb->mutex); - if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) - do_gettimeofday(&gb->ts); - /* Else operations to perform */ if (gb->async) { if (type == GB_LOOPBACK_TYPE_PING) { error = gb_loopback_async_ping(gb); - if (!error) - gb->requests_completed++; gb_loopback_calculate_stats(gb); } else if (type == GB_LOOPBACK_TYPE_TRANSFER) { error = gb_loopback_async_transfer(gb, size); @@ -1014,8 +989,6 @@ static int gb_loopback_fn(void *data) if (error) gb->error++; - else - gb->requests_completed++; gb->iteration_count++; gb_loopback_calculate_stats(gb); } -- cgit v1.2.3-59-g8ed1b From 907d1e16ffe4ea50a9bdf1c644fd48ce6a126e4f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 19 Feb 2016 15:57:46 +0530 Subject: greybus: interface: Add print messages on interface initialization/removal It might be of interest (to developers at least) to know when an interface is getting created or removed from the system. Interface creation message can further contain basic information about the interface, like its vid/pid and mfg/prod ids. Now, the interface is created by gb_interface_create(), which doesn't register the intf->dev to the kernel and so the print message is rather added to gb_interface_init() where we register the device with the kernel. A similar message is added to gb_interface_remove() only when the interface was earlier initialized. And this is how the output looks on real insertion/removal of the module: greybus 1-1: Interface added: VID=0x00000001, PID=0x00000001 greybus 1-1: DDBL1 Manufacturer=0x00000001, Product=0x00000001 ... greybus 1-1: Interface removed Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 6c815db112b6..5e49bc826fe7 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -174,8 +174,10 @@ void gb_interface_remove(struct gb_interface *intf) list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); - if (device_is_registered(&intf->dev)) + if (device_is_registered(&intf->dev)) { device_del(&intf->dev); + dev_info(&intf->dev, "Interface removed\n"); + } gb_control_disable(intf->control); @@ -260,6 +262,11 @@ int gb_interface_init(struct gb_interface *intf, u8 device_id) goto free_manifest; } + dev_info(&intf->dev, "Interface added: VID=0x%08x, PID=0x%08x\n", + intf->vendor_id, intf->product_id); + dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n", + intf->ddbl1_manufacturer_id, intf->ddbl1_product_id); + list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) { ret = gb_bundle_add(bundle); if (ret) { -- cgit v1.2.3-59-g8ed1b From 737df280a73b9e9d3d24cd8e81637b0496f06dde Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Feb 2016 18:50:53 +0100 Subject: greybus: Documentation: remove svc unique_id attribute Remove unimplemented svc unique_id attribute from the documentation. This attribute made more sense when we thought we'd have an AP-module, unlike now when the AP and SVC are both part of the same frame. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 7 ------- .../staging/greybus/Documentation/sysfs/greybus1/1-svc/unique_id | 0 .../staging/greybus/Documentation/sysfs/greybus2/2-svc/unique_id | 0 3 files changed, 7 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/unique_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/unique_id diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 4493605186b0..17b1a2919ef9 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -150,13 +150,6 @@ Description: Write the number of the interface that you wish to forcibly eject from the system. -What: /sys/bus/greybus/device/N-svc/unique_id -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman -Description: - The unique ID, or serial number, of the SVC device - What: /sys/bus/greybus/device/N-svc/version Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/unique_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/unique_id deleted file mode 100644 index e69de29bb2d1..000000000000 -- cgit v1.2.3-59-g8ed1b From 7f29aded453e0392391b831c196583c274ec2cfd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 22 Feb 2016 18:14:46 -0800 Subject: greybus: uart: properly calculate max buffer size We forgot to count the size of the uart send data message header when calculating the maximum size of the buffer that the uart driver could send in one chunk. This fixes the math and makes the variable a size_t to match the return value of the call to gb_operation_get_payload_size_max(); Reported-by: Axel Haslam Signed-off-by: Greg Kroah-Hartman Tested-by: Axel Haslam Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 46cce8c82412..52cc9d581ca4 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -43,7 +43,7 @@ struct gb_tty_line_coding { struct gb_tty { struct tty_port port; void *buffer; - u32 buffer_payload_max; + size_t buffer_payload_max; struct gb_connection *connection; u16 cport_id; unsigned int minor; @@ -608,11 +608,8 @@ static int gb_uart_connection_init(struct gb_connection *connection) } gb_tty->buffer_payload_max = - gb_operation_get_payload_size_max(connection); - if (!gb_tty->buffer_payload_max) { - retval = -EINVAL; - goto error_payload; - } + gb_operation_get_payload_size_max(connection) - + sizeof(struct gb_uart_send_data_request); gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); if (!gb_tty->buffer) { -- cgit v1.2.3-59-g8ed1b From 446091c999388f9365e8d206f70b0c1a860212a0 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 23 Feb 2016 11:22:48 +0100 Subject: greybus: camera: Add CSI configuration parameters Add CSI configuration parameters to the configure_stream operation response. Currently, only the total number of lines in a second is used to configure the the AP-Bridge CSI transmitter, all other parameters (number of CSI data lanes, and CSI bus clock frequency) are kept hard-coded for two reasons: 1) We need to configure the CSI receiver on AP side accordingly to these settings, before sending them to APB1 CSI transmitter. 2) We cannot use the camera module provided parameters as-is, but use those information to compute the required bandwidth on the CSI bus, and configure the # of CSI data lanes, and the CSI bus clock speed in a way that satisfies that bandwidth requirement. Signed-off-by: Jacopo Mondi Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 11 ++++++++++- drivers/staging/greybus/greybus_protocols.h | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index bb8bc175dc0a..3f205cbcb21b 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -162,6 +162,7 @@ struct ap_csi_config_request { __u8 num_lanes; __u8 padding; __le32 bus_freq; + __le32 lines_per_second; } __packed; static int gb_camera_configure_streams(struct gb_camera *gcam, @@ -254,7 +255,14 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, if (ret < 0) goto done; - /* Configure the CSI transmitter. Hardcode the parameters for now. */ + /* + * Configure the APB1 CSI transmitter using the lines count reported by + * the camera module, but with hard-coded bus frequency and lanes number. + * + * TODO: use the clocking and size informations reported by camera module + * to compute the required CSI bandwidth, and configure the CSI receiver + * on AP side, and the CSI transmitter on APB1 side accordingly. + */ memset(&csi_cfg, 0, sizeof(csi_cfg)); if (nstreams) { @@ -262,6 +270,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, csi_cfg.clock_mode = 0; csi_cfg.num_lanes = 4; csi_cfg.bus_freq = cpu_to_le32(960000000); + csi_cfg.lines_per_second = resp->lines_per_second; ret = gb_hd_output(gcam->connection->hd, &csi_cfg, sizeof(csi_cfg), GB_APB_REQUEST_CSI_TX_CONTROL, false); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 6b192924a566..1a90dc2d9c3a 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1276,7 +1276,10 @@ struct gb_camera_configure_streams_response { __u8 num_streams; __u8 flags; #define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED 0x01 - __le16 padding; + __u8 num_lanes; + __u8 padding; + __le32 bus_freq; + __le32 lines_per_second; struct gb_camera_stream_config_response config[0]; } __packed; -- cgit v1.2.3-59-g8ed1b From 27e18d8c6967f554b374a713c6bd21bcc985389b Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 23 Feb 2016 11:22:49 +0100 Subject: greybus: camera: Rename clock_mode to flags Rename the 'clock_mode' parameter to a more generic 'flags' in the csi bus configuration structure. Define flags value for continuous clock mode. Signed-off-by: Jacopo Mondi Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 3f205cbcb21b..444e218e15ab 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -158,7 +158,8 @@ static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) struct ap_csi_config_request { __u8 csi_id; - __u8 clock_mode; + __u8 flags; +#define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01 __u8 num_lanes; __u8 padding; __le32 bus_freq; @@ -267,7 +268,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, if (nstreams) { csi_cfg.csi_id = 1; - csi_cfg.clock_mode = 0; + csi_cfg.flags = 0; csi_cfg.num_lanes = 4; csi_cfg.bus_freq = cpu_to_le32(960000000); csi_cfg.lines_per_second = resp->lines_per_second; -- cgit v1.2.3-59-g8ed1b From 599159b6877e665b086d6e3092203c651b8a3952 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Mon, 22 Feb 2016 17:27:24 +0530 Subject: greybus: arche-platform: Return immediately if in same state from state change fns Have a check inside all individual operational state change functions to check whether device is in same state, and if yes, then return immediately. Testing Done: Tested on DB3.5 platform Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 591cf9dc5071..2dc11fdc5b88 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -133,6 +133,9 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat { int ret; + if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) + return 0; + dev_info(arche_pdata->dev, "Booting from cold boot state\n"); svc_reset_onoff(arche_pdata->svc_reset_gpio, @@ -159,6 +162,9 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) { + if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) + return; + dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); svc_reset_onoff(arche_pdata->svc_reset_gpio, @@ -176,6 +182,9 @@ static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) { + if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) + return; + /* Send disconnect/detach event to SVC */ gpio_set_value(arche_pdata->wake_detect_gpio, 0); usleep_range(100, 200); -- cgit v1.2.3-59-g8ed1b From 25847ee7c9517f91323f9139713ebdc94c865a2e Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Mon, 22 Feb 2016 17:27:25 +0530 Subject: greybus: arche-platform: Avoid doing same thing again in poweroff fn If user switches from fw_flashing => off mode, then we do not need to do same things again, for example, clk_disable and wake/detect event, as while switching to fw_flashing, driver makes sure that device goes to off state and then brings back in fw_flashing state. Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 2dc11fdc5b88..efeafb373cf1 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -185,11 +185,15 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) return; - /* Send disconnect/detach event to SVC */ - gpio_set_value(arche_pdata->wake_detect_gpio, 0); - usleep_range(100, 200); + /* If in fw_flashing mode, then no need to repeate things again */ + if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { + /* Send disconnect/detach event to SVC */ + gpio_set_value(arche_pdata->wake_detect_gpio, 0); + usleep_range(100, 200); + + clk_disable_unprepare(arche_pdata->svc_ref_clk); + } - clk_disable_unprepare(arche_pdata->svc_ref_clk); /* As part of exit, put APB back in reset state */ svc_reset_onoff(arche_pdata->svc_reset_gpio, arche_pdata->is_reset_act_hi); -- cgit v1.2.3-59-g8ed1b From c5e7cbaf3ab8842278a9162dfaf04464ce21f0e4 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Mon, 22 Feb 2016 17:27:26 +0530 Subject: greybus: arche-apb-ctrl: Return immediately if in same state from state change fns Have a check inside all individual operational state change functions to check whether device is in same state, and if yes, then return immediately. Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 3086306f04fb..c2f0776f04f4 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -67,7 +67,8 @@ static int coldboot_seq(struct platform_device *pdev) struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); int ret; - if (apb->init_disabled) + if (apb->init_disabled || + apb->state == ARCHE_PLATFORM_STATE_ACTIVE) return 0; /* Hold APB in reset state */ @@ -112,7 +113,8 @@ static int fw_flashing_seq(struct platform_device *pdev) struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); int ret; - if (apb->init_disabled) + if (apb->init_disabled || + apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING) return 0; ret = regulator_enable(apb->vcore); @@ -141,8 +143,9 @@ static int standby_boot_seq(struct platform_device *pdev) if (apb->init_disabled) return 0; - /* If it is in OFF state, then we do not want to change the state */ - if (apb->state == ARCHE_PLATFORM_STATE_OFF) + /* Even if it is in OFF state, then we do not want to change the state */ + if (apb->state == ARCHE_PLATFORM_STATE_STANDBY || + apb->state == ARCHE_PLATFORM_STATE_OFF) return 0; /* @@ -162,7 +165,7 @@ static void poweroff_seq(struct platform_device *pdev) { struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); - if (apb->init_disabled) + if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF) return; /* disable the clock */ -- cgit v1.2.3-59-g8ed1b From 9ed5e1ba33d12dfcc693c87779f39b91c16c15b9 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Tue, 23 Feb 2016 18:46:08 +0100 Subject: greybus: connection: add api to {en,dis}able unipro fct flow In order to support mailbox-free control cport init on the bridges the AP must be able to enable/disable the flow of unipro fct tokens. Add a new API that will enable or disable on APBA the flow of fct tokens. Reviewed-by: Johan Hovold Signed-off-by: Fabien Parent Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 28 ++++++++++++++++++++++++++++ drivers/staging/greybus/hd.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 4f5e2adfa8c4..4ae7153ff863 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -274,6 +274,34 @@ static void gb_connection_hd_cport_disable(struct gb_connection *connection) hd->driver->cport_disable(hd, connection->hd_cport_id); } +static int gb_connection_hd_fct_flow_enable(struct gb_connection *connection) +{ + struct gb_host_device *hd = connection->hd; + int ret; + + if (!hd->driver->fct_flow_enable) + return 0; + + ret = hd->driver->fct_flow_enable(hd, connection->hd_cport_id); + if (ret) { + dev_err(&hd->dev, "%s: failed to enable FCT flow: %d\n", + connection->name, ret); + return ret; + } + + return 0; +} + +static void gb_connection_hd_fct_flow_disable(struct gb_connection *connection) +{ + struct gb_host_device *hd = connection->hd; + + if (!hd->driver->fct_flow_disable) + return; + + hd->driver->fct_flow_disable(hd, connection->hd_cport_id); +} + /* * Request the SVC to create a connection from AP's cport to interface's * cport. diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index e11359b145e6..eaddfc9befd6 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -25,6 +25,8 @@ struct gb_hd_driver { int (*latency_tag_disable)(struct gb_host_device *hd, u16 cport_id); int (*output)(struct gb_host_device *hd, void *req, u16 size, u8 cmd, bool async); + int (*fct_flow_enable)(struct gb_host_device *hd, u16 cport_id); + int (*fct_flow_disable)(struct gb_host_device *hd, u16 cport_id); }; struct gb_host_device { -- cgit v1.2.3-59-g8ed1b From 48a173030545472e4d0aa1b432b389ef59bf5cf0 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Tue, 23 Feb 2016 18:46:09 +0100 Subject: greybus: apba: add fct flow usb control requests Add control requests to enable/disable the flow of unipro FCT tokens Reviewed-by: Johan Hovold Signed-off-by: Fabien Parent Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 1a90dc2d9c3a..f28391279555 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -195,6 +195,9 @@ struct gb_control_interface_version_response { /* request to control the CSI transmitter */ #define GB_APB_REQUEST_AUDIO_CONTROL 0x09 +/* vendor requests to enable/disable FCT tokens flow */ +#define GB_APB_REQUEST_FCT_FLOW_EN 0x0b +#define GB_APB_REQUEST_FCT_FLOW_DIS 0x0c /* Firmware Protocol */ -- cgit v1.2.3-59-g8ed1b From e70055b3fb62c7ff1c23d8d2076e4c5b4caf39a1 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Tue, 23 Feb 2016 18:46:10 +0100 Subject: greybus: es2: implement the fct flow control requests Implement the control requests enabling/disabling the flow of FCT on APBA. Reviewed-by: Johan Hovold Signed-off-by: Fabien Parent Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/es2.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 5ade51e83745..e920563351ff 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -574,6 +574,41 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) return retval; } +static int fct_flow_enable(struct gb_host_device *hd, u16 cport_id) +{ + int retval; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + GB_APB_REQUEST_FCT_FLOW_EN, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, cport_id, 0, NULL, + 0, ES2_TIMEOUT); + if (retval < 0) + dev_err(&udev->dev, "Cannot enable FCT flow for cport %u: %d\n", + cport_id, retval); + return retval; +} + +static int fct_flow_disable(struct gb_host_device *hd, u16 cport_id) +{ + int retval; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + GB_APB_REQUEST_FCT_FLOW_DIS, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, cport_id, 0, NULL, + 0, ES2_TIMEOUT); + if (retval < 0) + dev_err(&udev->dev, + "Cannot disable FCT flow for cport %u: %d\n", + cport_id, retval); + return retval; +} + static struct gb_hd_driver es2_driver = { .hd_priv_size = sizeof(struct es2_ap_dev), .message_send = message_send, @@ -582,6 +617,8 @@ static struct gb_hd_driver es2_driver = { .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, .output = output, + .fct_flow_enable = fct_flow_enable, + .fct_flow_disable = fct_flow_disable, }; /* Common function to report consistent warnings based on URB status */ -- cgit v1.2.3-59-g8ed1b From 71f6c3231c1dce6980f97b7bc7ed7ac32faac550 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Tue, 23 Feb 2016 18:46:11 +0100 Subject: greybus: connection: {en,dis}able fct flow in connection management The AP must enable the FCT flow of APBA once it has received the response from the AP that the connection between APBA and a module has been setted up. Disable the flow of FCT tokens when destroying connections. Signed-off-by: Fabien Parent Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 4ae7153ff863..34e26dc6e249 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -314,7 +314,7 @@ gb_connection_svc_connection_create(struct gb_connection *connection) int ret; if (gb_connection_is_static(connection)) - return 0; + return gb_connection_hd_fct_flow_enable(connection); intf = connection->intf; ret = gb_svc_connection_create(hd->svc, @@ -330,12 +330,23 @@ gb_connection_svc_connection_create(struct gb_connection *connection) return ret; } + ret = gb_connection_hd_fct_flow_enable(connection); + if (ret) { + gb_svc_connection_destroy(hd->svc, hd->svc->ap_intf_id, + connection->hd_cport_id, + intf->interface_id, + connection->intf_cport_id); + return ret; + } + return 0; } static void gb_connection_svc_connection_destroy(struct gb_connection *connection) { + gb_connection_hd_fct_flow_disable(connection); + if (gb_connection_is_static(connection)) return; -- cgit v1.2.3-59-g8ed1b From 066f950c65c206c1a1a2f72f2c80134177ea3999 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 24 Feb 2016 16:11:49 +0100 Subject: greybus: uart: add max-payload sanity check Let's be well behaved and add a sanity check on the maximum greybus payload size to avoid underflow on the calculated buffer size. Reviewed-by: Rui Miguel Silva Signed-off-by: Johan Hovold Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 52cc9d581ca4..60617cb69a5a 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -587,6 +587,7 @@ static void gb_tty_exit(void); static int gb_uart_connection_init(struct gb_connection *connection) { + size_t max_payload; struct gb_tty *gb_tty; struct device *tty_dev; int retval; @@ -607,8 +608,13 @@ static int gb_uart_connection_init(struct gb_connection *connection) goto error_alloc; } - gb_tty->buffer_payload_max = - gb_operation_get_payload_size_max(connection) - + max_payload = gb_operation_get_payload_size_max(connection); + if (max_payload < sizeof(struct gb_uart_send_data_request)) { + retval = -EINVAL; + goto error_payload; + } + + gb_tty->buffer_payload_max = max_payload - sizeof(struct gb_uart_send_data_request); gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); -- cgit v1.2.3-59-g8ed1b From cb7f00ba5f581ca2f0848dd2ed77f1b9d793c648 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 24 Feb 2016 16:11:50 +0100 Subject: greybus: uart: add missing serial-state sanity check Add dedicated serial-state request handler and add the missing sanity check on the incoming request. Reviewed-by: Rui Miguel Silva Signed-off-by: Johan Hovold Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 60617cb69a5a..c09a76bee269 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -103,13 +103,32 @@ static int gb_uart_receive_data(struct gb_tty *gb_tty, return 0; } -static int gb_uart_request_recv(u8 type, struct gb_operation *op) +static int gb_uart_serial_state_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_tty *gb_tty = connection->private; struct gb_message *request = op->request; struct gb_uart_serial_state_request *serial_state; - int ret = 0; + + if (request->payload_size < sizeof(*serial_state)) { + dev_err(&connection->bundle->dev, + "short serial-state event received (%zu < %zu)\n", + request->payload_size, sizeof(*serial_state)); + return -EINVAL; + } + + serial_state = request->payload; + gb_tty->ctrlin = serial_state->control; + + return 0; +} + +static int gb_uart_request_recv(u8 type, struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_tty *gb_tty = connection->private; + struct gb_message *request = op->request; + int ret; switch (type) { case GB_UART_TYPE_RECEIVE_DATA: @@ -117,8 +136,7 @@ static int gb_uart_request_recv(u8 type, struct gb_operation *op) request->payload); break; case GB_UART_TYPE_SERIAL_STATE: - serial_state = request->payload; - gb_tty->ctrlin = serial_state->control; + ret = gb_uart_serial_state_handler(op); break; default: dev_err(&connection->bundle->dev, -- cgit v1.2.3-59-g8ed1b From c5f338c4a040d7d6eefc560f3ee5cdb6ede03b74 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 24 Feb 2016 16:11:51 +0100 Subject: greybus: uart: fix incomplete receive-data sanity checks Fix incomplete receive-data sanity checks. The payload size was never verified before parsing the uart header and neither was the uart-header data size verified against the actual payload size, something which could lead to information leaks when passing data beyond the payload buffer to the tty layer. Also remove the incorrect check against the maximum (tx-buffer) payload size. Reviewed-by: Rui Miguel Silva Signed-off-by: Johan Hovold Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/uart.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index c09a76bee269..0685cdcb510d 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -65,18 +65,36 @@ static DEFINE_IDR(tty_minors); static DEFINE_MUTEX(table_lock); static atomic_t reference_count = ATOMIC_INIT(0); -static int gb_uart_receive_data(struct gb_tty *gb_tty, - struct gb_connection *connection, - struct gb_uart_recv_data_request *receive_data) +static int gb_uart_receive_data_handler(struct gb_operation *op) { + struct gb_connection *connection = op->connection; + struct gb_tty *gb_tty = connection->private; struct tty_port *port = &gb_tty->port; + struct gb_message *request = op->request; + struct gb_uart_recv_data_request *receive_data; u16 recv_data_size; int count; unsigned long tty_flags = TTY_NORMAL; - count = gb_tty->buffer_payload_max - sizeof(*receive_data); + if (request->payload_size < sizeof(*receive_data)) { + dev_err(&connection->bundle->dev, + "short receive-data request received (%zu < %zu)\n", + request->payload_size, sizeof(*receive_data)); + return -EINVAL; + } + + receive_data = op->request->payload; recv_data_size = le16_to_cpu(receive_data->size); - if (!recv_data_size || recv_data_size > count) + + if (recv_data_size != request->payload_size - sizeof(*receive_data)) { + dev_err(&connection->bundle->dev, + "malformed receive-data request received (%u != %zu)\n", + recv_data_size, + request->payload_size - sizeof(*receive_data)); + return -EINVAL; + } + + if (!recv_data_size) return -EINVAL; if (receive_data->flags) { @@ -126,14 +144,11 @@ static int gb_uart_serial_state_handler(struct gb_operation *op) static int gb_uart_request_recv(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_tty *gb_tty = connection->private; - struct gb_message *request = op->request; int ret; switch (type) { case GB_UART_TYPE_RECEIVE_DATA: - ret = gb_uart_receive_data(gb_tty, connection, - request->payload); + ret = gb_uart_receive_data_handler(op); break; case GB_UART_TYPE_SERIAL_STATE: ret = gb_uart_serial_state_handler(op); -- cgit v1.2.3-59-g8ed1b From 6743a6fd963aba000b128fd0b5c420a8f4b0dcde Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 02:27:36 +0530 Subject: greybus: arche-platform: Fix exit path in probe fn If SVC coldboot fails or if of_platform_populate() fn fails, then state of device needs to be reverted. Importantly, if of_platform_populate() fails, then poweroff the SVC. Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index efeafb373cf1..c99a375bbafb 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -387,13 +387,13 @@ static int arche_platform_probe(struct platform_device *pdev) ret = arche_platform_coldboot_seq(arche_pdata); if (ret) { dev_err(dev, "Failed to cold boot svc %d\n", ret); - return ret; + goto err_coldboot; } ret = of_platform_populate(np, NULL, NULL, dev); if (ret) { dev_err(dev, "failed to populate child nodes %d\n", ret); - return ret; + goto err_populate; } INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); @@ -401,6 +401,12 @@ static int arche_platform_probe(struct platform_device *pdev) dev_info(dev, "Device registered successfully\n"); return 0; + +err_populate: + arche_platform_poweroff_seq(arche_pdata); +err_coldboot: + device_remove_file(&pdev->dev, &dev_attr_state); + return ret; } static int arche_remove_child(struct device *dev, void *unused) -- cgit v1.2.3-59-g8ed1b From 7a6396d9ce81564b09d91586b29fdd02e6814c28 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Feb 2016 22:51:23 -0800 Subject: greybus: gpio: use bundle device for error messages Use the bundle device directly in gpio error messages instead of the gpio device, as they are the same pointer. This will make future gpio api changes much easier to handle. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 47 ++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 5830dc9b87f3..ec375c0475d9 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -77,6 +77,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, u8 which) { + struct device *dev = &ggc->connection->bundle->dev; struct gb_gpio_deactivate_request request; int ret; @@ -84,8 +85,7 @@ static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DEACTIVATE, &request, sizeof(request), NULL, 0); if (ret) { - dev_err(ggc->chip.dev, "failed to deactivate gpio %u\n", - which); + dev_err(dev, "failed to deactivate gpio %u\n", which); return; } @@ -95,6 +95,7 @@ static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, u8 which) { + struct device *dev = &ggc->connection->bundle->dev; struct gb_gpio_get_direction_request request; struct gb_gpio_get_direction_response response; int ret; @@ -109,8 +110,7 @@ static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, direction = response.direction; if (direction && direction != 1) { - dev_warn(ggc->chip.dev, - "gpio %u direction was %u (should be 0 or 1)\n", + dev_warn(dev, "gpio %u direction was %u (should be 0 or 1)\n", which, direction); } ggc->lines[which].direction = direction ? 1 : 0; @@ -149,6 +149,7 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, u8 which) { + struct device *dev = &ggc->connection->bundle->dev; struct gb_gpio_get_value_request request; struct gb_gpio_get_value_response response; int ret; @@ -159,15 +160,13 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(ggc->chip.dev, "failed to get value of gpio %u\n", - which); + dev_err(dev, "failed to get value of gpio %u\n", which); return ret; } value = response.value; if (value && value != 1) { - dev_warn(ggc->chip.dev, - "gpio %u value was %u (should be 0 or 1)\n", + dev_warn(dev, "gpio %u value was %u (should be 0 or 1)\n", which, value); } ggc->lines[which].value = value ? 1 : 0; @@ -177,12 +176,13 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, static void gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, u8 which, bool value_high) { + struct device *dev = &ggc->connection->bundle->dev; struct gb_gpio_set_value_request request; int ret; if (ggc->lines[which].direction == 1) { - dev_warn(ggc->chip.dev, - "refusing to set value of input gpio %u\n", which); + dev_warn(dev, "refusing to set value of input gpio %u\n", + which); return; } @@ -191,8 +191,7 @@ static void gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, &request, sizeof(request), NULL, 0); if (ret) { - dev_err(ggc->chip.dev, "failed to set value of gpio %u\n", - which); + dev_err(dev, "failed to set value of gpio %u\n", which); return; } @@ -216,6 +215,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, static void _gb_gpio_irq_mask(struct gb_gpio_controller *ggc, u8 hwirq) { + struct device *dev = &ggc->connection->bundle->dev; struct gb_gpio_irq_mask_request request; int ret; @@ -224,11 +224,12 @@ static void _gb_gpio_irq_mask(struct gb_gpio_controller *ggc, u8 hwirq) GB_GPIO_TYPE_IRQ_MASK, &request, sizeof(request), NULL, 0); if (ret) - dev_err(ggc->chip.dev, "failed to mask irq: %d\n", ret); + dev_err(dev, "failed to mask irq: %d\n", ret); } static void _gb_gpio_irq_unmask(struct gb_gpio_controller *ggc, u8 hwirq) { + struct device *dev = &ggc->connection->bundle->dev; struct gb_gpio_irq_unmask_request request; int ret; @@ -237,12 +238,13 @@ static void _gb_gpio_irq_unmask(struct gb_gpio_controller *ggc, u8 hwirq) GB_GPIO_TYPE_IRQ_UNMASK, &request, sizeof(request), NULL, 0); if (ret) - dev_err(ggc->chip.dev, "failed to unmask irq: %d\n", ret); + dev_err(dev, "failed to unmask irq: %d\n", ret); } static void _gb_gpio_irq_set_type(struct gb_gpio_controller *ggc, u8 hwirq, u8 type) { + struct device *dev = &ggc->connection->bundle->dev; struct gb_gpio_irq_type_request request; int ret; @@ -253,7 +255,7 @@ static void _gb_gpio_irq_set_type(struct gb_gpio_controller *ggc, GB_GPIO_TYPE_IRQ_TYPE, &request, sizeof(request), NULL, 0); if (ret) - dev_err(ggc->chip.dev, "failed to set irq type: %d\n", ret); + dev_err(dev, "failed to set irq type: %d\n", ret); } static void gb_gpio_irq_mask(struct irq_data *d) @@ -281,6 +283,7 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_chip *chip = irq_data_to_gpio_chip(d); struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); struct gb_gpio_line *line = &ggc->lines[d->hwirq]; + struct device *dev = &ggc->connection->bundle->dev; u8 irq_type; switch (type) { @@ -303,7 +306,7 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) irq_type = GB_GPIO_IRQ_TYPE_LEVEL_HIGH; break; default: - dev_err(chip->dev, "unsupported irq type: %u\n", type); + dev_err(dev, "unsupported irq type: %u\n", type); return -EINVAL; } @@ -346,6 +349,7 @@ static void gb_gpio_irq_bus_sync_unlock(struct irq_data *d) static int gb_gpio_request_recv(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; + struct device *dev = &connection->bundle->dev; struct gb_gpio_controller *ggc = connection->private; struct gb_message *request; struct gb_gpio_irq_event_request *event; @@ -353,33 +357,32 @@ static int gb_gpio_request_recv(u8 type, struct gb_operation *op) struct irq_desc *desc; if (type != GB_GPIO_TYPE_IRQ_EVENT) { - dev_err(&connection->bundle->dev, - "unsupported unsolicited request: %u\n", type); + dev_err(dev, "unsupported unsolicited request: %u\n", type); return -EINVAL; } request = op->request; if (request->payload_size < sizeof(*event)) { - dev_err(ggc->chip.dev, "short event received (%zu < %zu)\n", + dev_err(dev, "short event received (%zu < %zu)\n", request->payload_size, sizeof(*event)); return -EINVAL; } event = request->payload; if (event->which > ggc->line_max) { - dev_err(ggc->chip.dev, "invalid hw irq: %d\n", event->which); + dev_err(dev, "invalid hw irq: %d\n", event->which); return -EINVAL; } irq = irq_find_mapping(ggc->irqdomain, event->which); if (!irq) { - dev_err(ggc->chip.dev, "failed to find IRQ\n"); + dev_err(dev, "failed to find IRQ\n"); return -EINVAL; } desc = irq_to_desc(irq); if (!desc) { - dev_err(ggc->chip.dev, "failed to look up irq\n"); + dev_err(dev, "failed to look up irq\n"); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From bb80c76448b2463a1ecfe622df4f7b3c5fb39cdf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Feb 2016 22:51:45 -0800 Subject: greybus: gpio: handle api changes for 4.5 kernel release In kernel version 4.5, struct gpio_chip renamed the field 'dev' to 'parent' so handle this properly. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gpio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index ec375c0475d9..e7dd99444ea2 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -651,7 +651,11 @@ static int gb_gpio_connection_init(struct gb_connection *connection) gpio = &ggc->chip; gpio->label = "greybus_gpio"; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + gpio->parent = &connection->bundle->dev; +#else gpio->dev = &connection->bundle->dev; +#endif gpio->owner = THIS_MODULE; gpio->request = gb_gpio_request; -- cgit v1.2.3-59-g8ed1b From 8886c44f87cbee41fd4ac1cfdf1ef6a7cb4583f5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Feb 2016 18:50:54 +0100 Subject: greybus: Documentation/sysfs: add module devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce module devices and rename interface and bundle devices. Greybus module devices correspond to physical modules and have one or more interfaces. Modules have an id that is identical to the id of their primary interface, which in turn is the interface with lowest numbered id. The module name is constructed from the bus and module id: - Interfaces and bundles are consequently renamed as -. and -.. respectively. As before, interface ids (and therefore in a sense now also module ids) correspond to physical interface positions on the frame. Modules have the following attributes: eject module_id num_interfaces where module_id is the id of the module and num_interface the number of interfaces the module has. Note that the interface ids of a module's interfaces are expected to be , , ..., . Writing a non-zero argument to eject cleanly shuts down and unregisters all of the module interfaces before ejecting the module. The example sysfs tree now looks as follows with the second bus (APBridgeA) left out: greybus1/ ├── 1-2 │   ├── 1-2.2 │   │   ├── 1-2.2.1 │   │   │   ├── bundle_class │   │   │   ├── bundle_id │   │   │   └── state │   │   ├── 1-2.2.2 │   │   │   ├── bundle_class │   │   │   ├── bundle_id │   │   │   └── state │   │   ├── ddbl1_manufacturer_id │   │   ├── ddbl1_product_id │   │   ├── interface_id │   │   ├── product_id │   │   ├── serial_number │   │   ├── unique_id │   │   └── vendor_id │   ├── eject │   ├── module_id │   └── num_interfaces ├── 1-4 │   ├── 1-4.4 │   │   ├── 1-4.4.2 │   │   │   ├── bundle_class │   │   │   ├── bundle_id │   │   │   ├── gpbridge0 │   │   │   │   ├── gpio │   │   │   │   │   └── gpiochip490 │   │   │   │   └── i2c-4 │   │   │   └── state │   │   ├── ddbl1_manufacturer_id │   │   ├── ddbl1_product_id │   │   ├── interface_id │   │   ├── product_id │   │   ├── serial_number │   │   ├── unique_id │   │   └── vendor_id │   ├── eject │   ├── module_id │   └── num_interfaces └── 1-svc ├── ap_intf_id ├── eject └── endo_id where greybus1 is a bus; 1-svc the svc; 1-2, and 1-4 are modules; 1-2.2 and 1-4.4 are (primary) interfaces; and 1-2.2.1, 1-2.2.2, and 1-4.4.2 are bundles. Note that the svc eject attribute may eventually be renamed force_eject. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/state | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id | 1 + .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id | 1 + .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id | 1 - .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id | 1 + .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/state | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id | 0 .../greybus/Documentation/sysfs/greybus1/1-2/ddbl1_manufacturer_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_product_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/product_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/vendor_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_class | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id | 1 - .../sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore | 1 - .../Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/state | 0 .../greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_class | 0 .../greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id | 1 + .../greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore | 1 + .../sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore | 1 + .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/state | 0 .../greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_manufacturer_id | 0 .../greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id | 1 + .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/vendor_id | 0 .../greybus/Documentation/sysfs/greybus1/1-4/ddbl1_manufacturer_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_product_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/product_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/vendor_id | 0 .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_class | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/state | 0 .../greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class | 0 .../greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id | 1 + .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state | 0 .../greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id | 0 .../greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id | 0 .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id | 1 + .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id | 0 .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id | 0 .../greybus/Documentation/sysfs/greybus2/2-3/ddbl1_manufacturer_id | 0 .../staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_product_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces | 1 + drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/product_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/vendor_id | 0 79 files changed, 15 insertions(+), 9 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/unique_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_manufacturer_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/unique_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_manufacturer_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/serial_number create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/unique_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_manufacturer_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/unique_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/unique_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_manufacturer_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/unique_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/vendor_id diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/bundle_id +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.1/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/bundle_id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/unique_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_manufacturer_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/ddbl1_product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/interface_id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/unique_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/vendor_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/bundle_id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/gpio/gpiochip490/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/gpbridge0/i2c-4/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.2/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_manufacturer_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id new file mode 100644 index 000000000000..b8626c4cff28 --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id @@ -0,0 +1 @@ +4 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/unique_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/vendor_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_manufacturer_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/ddbl1_product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/eject new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id deleted file mode 100644 index b8626c4cff28..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/interface_id +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id new file mode 100644 index 000000000000..b8626c4cff28 --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id @@ -0,0 +1 @@ +4 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/unique_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/vendor_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/bundle_id +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.1/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id new file mode 100644 index 000000000000..00750edc07d6 --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id @@ -0,0 +1 @@ +3 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/unique_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_manufacturer_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/ddbl1_product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id deleted file mode 100644 index 00750edc07d6..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/interface_id +++ /dev/null @@ -1 +0,0 @@ -3 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id new file mode 100644 index 000000000000..00750edc07d6 --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id @@ -0,0 +1 @@ +3 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/unique_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/vendor_id deleted file mode 100644 index e69de29bb2d1..000000000000 -- cgit v1.2.3-59-g8ed1b From bd93d2a819c5e6081920f5dc775fc3361dbca5f7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Feb 2016 18:50:55 +0100 Subject: greybus: Documentation/sysfs: move module 1-4 to position 5 Move example module 1-4 to position 5, effectively renaming it 1-5. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- .../greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_class | 0 .../greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id | 1 - .../greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore | 1 - .../sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore | 1 - .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/state | 0 .../greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_manufacturer_id | 0 .../greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id | 1 - .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/vendor_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces | 1 - .../greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class | 0 .../greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id | 1 + .../greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore | 1 + .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore | 1 + .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state | 0 .../greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id | 0 .../greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id | 1 + .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces | 1 + 30 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/state delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_manufacturer_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/unique_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/unique_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/bundle_id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/gpio/gpiochip490/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/gpbridge0/i2c-4/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/1-4.4.2/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_manufacturer_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/ddbl1_product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id deleted file mode 100644 index b8626c4cff28..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/interface_id +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/unique_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/1-4.4/vendor_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/eject deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id deleted file mode 100644 index b8626c4cff28..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/module_id +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-4/num_interfaces +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id @@ -0,0 +1 @@ +2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id new file mode 100644 index 000000000000..7ed6ff82de6b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id @@ -0,0 +1 @@ +5 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/unique_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id new file mode 100644 index 000000000000..7ed6ff82de6b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id @@ -0,0 +1 @@ +5 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces @@ -0,0 +1 @@ +1 -- cgit v1.2.3-59-g8ed1b From c8733c513df38d5e5b3a19fe54be0f5821bfda2a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Feb 2016 18:50:56 +0100 Subject: greybus: Documentation/sysfs: make 1-5 a 2x2 module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make example module 1-5 a 2x2 module by adding a second, dummy interface. This is both an example of how a 2x2 module would be represented and also suggests what a dummy interface may look like. A 2x2 module has two child interface devices and a num_interfaces value of two. In this example, the secondary interface 1-5.6, is a dummy interface and therefore lacks the normal identifying attributes (e.g. UniPro DDBL1 and Ara ids). We may eventually add an interface_type attribute to facilitate distinguishing various interface types (there may be more than two). In the following tree, the bundle attributes and child devices have been left out: greybus1/ ├── 1-2 │   ├── 1-2.2 │   │   ├── 1-2.2.1 │   │   ├── 1-2.2.2 │   │   ├── ddbl1_manufacturer_id │   │   ├── ddbl1_product_id │   │   ├── interface_id │   │   ├── product_id │   │   ├── serial_number │   │   ├── unique_id │   │   └── vendor_id │   ├── eject │   ├── module_id │   └── num_interfaces ├── 1-5 │   ├── 1-5.5 │   │   ├── 1-5.5.2 │   │   ├── ddbl1_manufacturer_id │   │   ├── ddbl1_product_id │   │   ├── interface_id │   │   ├── product_id │   │   ├── serial_number │   │   ├── unique_id │   │   └── vendor_id │   ├── 1-5.6 │   │   └── interface_id │   ├── eject │   ├── module_id │   └── num_interfaces └── 1-svc In this example there are two modules: 1-2 is a 1x2 module with one interface, and 1-5 is a 2x2 module with two interfaces of which the second (1-5.6) is a dummy interface. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id new file mode 100644 index 000000000000..1e8b31496214 --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id @@ -0,0 +1 @@ +6 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces index d00491fd7e5b..0cfbf08886fc 100644 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces @@ -1 +1 @@ -1 +2 -- cgit v1.2.3-59-g8ed1b From 037a4028be8d6551dc226fc48c0c16063774e649 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 04:37:33 +0530 Subject: greybus: arche-apb-ctrl: Remove extra delay in APB reset With synchronization between SVC <=> AP over wake/detect line to bring APB's out of reset, we do not need any extra delays now. So remove it. Testing Done: Tested for DB3.5 and EVT1.2 platform. Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Tested-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index c2f0776f04f4..55806bdf5898 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -50,7 +50,6 @@ struct arche_apb_ctrl_drvdata { static inline void deassert_reset(unsigned int gpio) { gpio_set_value(gpio, 1); - msleep(500); } static inline void assert_reset(unsigned int gpio) -- cgit v1.2.3-59-g8ed1b From db5a3bca56584c0ae62dbe0b280333c75a813b0a Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 04:37:34 +0530 Subject: greybus: arche-platform: Convert delayed work to do only hub3613 configuration This is preparation of interrupt handling support, where APB coldboot and wake/detect handling will be handled as response to wake/detect interrupt. Due to slower I2C write operations in HUB configuration, it is important to separate HUB configuration, and probably delay it after APB is cold booted. Note that delayed work will be scheduled from interrupt handler, in following patches. To satisfy build (and bisect), remove apb_cold_boot() fn, which will be added back in the patch where it gets used again. Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Tested-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 64 ++++---------------------------- 1 file changed, 8 insertions(+), 56 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index c99a375bbafb..50991a601731 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -47,18 +47,6 @@ static inline void svc_reset_onoff(unsigned int gpio, bool onoff) gpio_set_value(gpio, onoff); } -static int apb_cold_boot(struct device *dev, void *data) -{ - int ret; - - ret = apb_ctrl_coldboot(dev); - if (ret) - dev_warn(dev, "failed to coldboot\n"); - - /*Child nodes are independent, so do not exit coldboot operation */ - return 0; -} - static int apb_fw_flashing_state(struct device *dev, void *data) { int ret; @@ -79,50 +67,17 @@ static int apb_poweroff(struct device *dev, void *data) } /** - * svc_delayed_work - Time to give SVC to boot. + * hub_conf_delayed_work - Configures USB3613 device to HUB mode + * + * The idea here is to split the APB coldboot operation with slow HUB configuration, + * so that driver response to wake/detect event can be met. + * So expectation is, once code reaches here, means initial unipro linkup + * between APB<->Switch was successful, so now just take it to AP. */ -static void svc_delayed_work(struct work_struct *work) +static void hub_conf_delayed_work(struct work_struct *work) { struct arche_platform_drvdata *arche_pdata = container_of(work, struct arche_platform_drvdata, delayed_work.work); - int timeout = 50; - - /* - * 1. SVC and AP boot independently, with AP<-->SVC wake/detect pin - * deasserted (LOW in this case) - * 2.1. SVC allows 360 milliseconds to elapse after switch boots to work - * around bug described in ENG-330. - * 2.2. AP asserts wake/detect pin (HIGH) (this can proceed in parallel with 2.1) - * 3. SVC detects assertion of wake/detect pin, and sends "wake out" signal to AP - * 4. AP receives "wake out" signal, takes AP Bridges through their power - * on reset sequence as defined in the bridge ASIC reference manuals - * 5. AP takes USB3613 through its power on reset sequence - * 6. AP enumerates AP Bridges - */ - gpio_set_value(arche_pdata->wake_detect_gpio, 1); - gpio_direction_input(arche_pdata->wake_detect_gpio); - do { - /* Read the wake_detect GPIO, for WAKE_OUT event from SVC */ - if (gpio_get_value(arche_pdata->wake_detect_gpio) == 0) - break; - - msleep(100); - } while(timeout--); - - if (timeout < 0) { - /* FIXME: We may want to limit retries here */ - dev_warn(arche_pdata->dev, - "Timed out on wake/detect, rescheduling handshake\n"); - gpio_direction_output(arche_pdata->wake_detect_gpio, 0); - schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); - return; - } - - /* Bring APB out of reset: cold boot sequence */ - device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); - - /* re-assert wake_detect to confirm SVC WAKE_OUT */ - gpio_direction_output(arche_pdata->wake_detect_gpio, 1); /* Enable HUB3613 into HUB mode. */ if (usb3613_hub_mode_ctrl(true)) @@ -226,8 +181,6 @@ static ssize_t state_store(struct device *dev, return count; ret = arche_platform_coldboot_seq(arche_pdata); - /* Give enough time for SVC to boot and then handshake with SVC */ - schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); } else if (sysfs_streq(buf, "standby")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) return count; @@ -396,8 +349,7 @@ static int arche_platform_probe(struct platform_device *pdev) goto err_populate; } - INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); - schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); + INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work); dev_info(dev, "Device registered successfully\n"); return 0; -- cgit v1.2.3-59-g8ed1b From 685353c12ea33e99d1daba5b3721b9033cdbdb87 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 04:37:35 +0530 Subject: greybus: arche-platform: Add wake detect state based on functionality If driver needs to process wake/detect events from SVC, by enabling interrupt support on wake/detect event, it becomes easier to maintain state of wake/detect line based on functionality. Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Tested-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 50991a601731..dcc3844854c2 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -21,6 +21,15 @@ #include +enum svc_wakedetect_state { + WD_STATE_IDLE, /* Default state = pulled high/low */ + WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ + WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */ + WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ + WD_STATE_COLDBOOT_START, /* Cold boot process started */ + WD_STATE_STANDBYBOOT_START, /* Not used */ +}; + struct arche_platform_drvdata { /* Control GPIO signals to and from AP <=> SVC */ int svc_reset_gpio; @@ -39,6 +48,8 @@ struct arche_platform_drvdata { int num_apbs; struct delayed_work delayed_work; + enum svc_wakedetect_state wake_detect_state; + struct device *dev; }; @@ -145,6 +156,7 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda /* Send disconnect/detach event to SVC */ gpio_set_value(arche_pdata->wake_detect_gpio, 0); usleep_range(100, 200); + arche_pdata->wake_detect_state = WD_STATE_IDLE; clk_disable_unprepare(arche_pdata->svc_ref_clk); } @@ -328,6 +340,7 @@ static int arche_platform_probe(struct platform_device *pdev) } /* deassert wake detect */ gpio_direction_output(arche_pdata->wake_detect_gpio, 0); + arche_pdata->wake_detect_state = WD_STATE_IDLE; arche_pdata->dev = &pdev->dev; -- cgit v1.2.3-59-g8ed1b From f760bbfb5c10952739fd0d39869cdd6a43844037 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 04:37:36 +0530 Subject: greybus: arche-platform: Enable interrupt support on wake/detect line This patch enabled interrupt support on events received over wake/detect line. The driver follows below state machine, Default: wake/detect line is high (WD_STATE_IDLE) On Falling edge: SVC initiates boot (either cold/standby). On ES3, > 30msec = coldboot, else standby boot. Driver moves to WD_STATE_BOOT_INIT On rising edge (> 30msec): SVC expects APB to coldboot Driver wakes irq thread which kicks off APB coldboot (WD_STATE_COLDBOOT_TRIG) On rising edge (< 30msec): Driver ignores it, do nothing. After coldboot of APB, HUB configuration work is scheduled after 2 sec, allowing enough time for APB<->SVC/Switch to linkup (in multiple iterations) Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Tested-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 120 +++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index dcc3844854c2..83db892a8a5d 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -17,10 +17,15 @@ #include #include #include +#include +#include +#include #include "arche_platform.h" #include +#define WD_COLDBOOT_PULSE_WIDTH_MS 30 + enum svc_wakedetect_state { WD_STATE_IDLE, /* Default state = pulled high/low */ WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ @@ -49,6 +54,9 @@ struct arche_platform_drvdata { struct delayed_work delayed_work; enum svc_wakedetect_state wake_detect_state; + int wake_detect_irq; + spinlock_t lock; + unsigned long wake_detect_start; struct device *dev; }; @@ -58,6 +66,18 @@ static inline void svc_reset_onoff(unsigned int gpio, bool onoff) gpio_set_value(gpio, onoff); } +static int apb_cold_boot(struct device *dev, void *data) +{ + int ret; + + ret = apb_ctrl_coldboot(dev); + if (ret) + dev_warn(dev, "failed to coldboot\n"); + + /*Child nodes are independent, so do not exit coldboot operation */ + return 0; +} + static int apb_fw_flashing_state(struct device *dev, void *data) { int ret; @@ -95,6 +115,86 @@ static void hub_conf_delayed_work(struct work_struct *work) dev_warn(arche_pdata->dev, "failed to control hub device\n"); } +static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) +{ + struct arche_platform_drvdata *arche_pdata = devid; + unsigned long flags; + + spin_lock_irqsave(&arche_pdata->lock, flags); + if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { + /* Something is wrong */ + spin_unlock_irqrestore(&arche_pdata->lock, flags); + return IRQ_HANDLED; + } + + arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START; + spin_unlock_irqrestore(&arche_pdata->lock, flags); + + /* Bring APB out of reset: cold boot sequence */ + device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); + + spin_lock_irqsave(&arche_pdata->lock, flags); + /* USB HUB configuration */ + schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); + arche_pdata->wake_detect_state = WD_STATE_IDLE; + spin_unlock_irqrestore(&arche_pdata->lock, flags); + + return IRQ_HANDLED; +} + +static irqreturn_t arche_platform_wd_irq(int irq, void *devid) +{ + struct arche_platform_drvdata *arche_pdata = devid; + unsigned long flags; + + spin_lock_irqsave(&arche_pdata->lock, flags); + + if (gpio_get_value(arche_pdata->wake_detect_gpio)) { + /* wake/detect rising */ + + /* + * If wake/detect line goes high after low, within less than + * 30msec, then standby boot sequence is initiated, which is not + * supported/implemented as of now. So ignore it. + */ + if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { + if (time_before(jiffies, + arche_pdata->wake_detect_start + + msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { + /* No harm with cancellation, even if not pending */ + cancel_delayed_work(&arche_pdata->delayed_work); + arche_pdata->wake_detect_state = WD_STATE_IDLE; + } else { + /* Check we are not in middle of irq thread already */ + if (arche_pdata->wake_detect_state != + WD_STATE_COLDBOOT_START) { + arche_pdata->wake_detect_state = + WD_STATE_COLDBOOT_TRIG; + spin_unlock_irqrestore(&arche_pdata->lock, flags); + return IRQ_WAKE_THREAD; + } + } + } + } else { + /* wake/detect falling */ + if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { + arche_pdata->wake_detect_start = jiffies; + /* No harm with cancellation even if it is not pending*/ + cancel_delayed_work(&arche_pdata->delayed_work); + /* + * In the begining, when wake/detect goes low (first time), we assume + * it is meant for coldboot and set the flag. If wake/detect line stays low + * beyond 30msec, then it is coldboot else fallback to standby boot. + */ + arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT; + } + } + + spin_unlock_irqrestore(&arche_pdata->lock, flags); + + return IRQ_HANDLED; +} + static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) { int ret; @@ -148,6 +248,8 @@ static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) { + unsigned long flags; + if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) return; @@ -156,7 +258,9 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda /* Send disconnect/detach event to SVC */ gpio_set_value(arche_pdata->wake_detect_gpio, 0); usleep_range(100, 200); + spin_lock_irqsave(&arche_pdata->lock, flags); arche_pdata->wake_detect_state = WD_STATE_IDLE; + spin_unlock_irqrestore(&arche_pdata->lock, flags); clk_disable_unprepare(arche_pdata->svc_ref_clk); } @@ -344,6 +448,22 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->dev = &pdev->dev; + spin_lock_init(&arche_pdata->lock); + arche_pdata->wake_detect_irq = + gpio_to_irq(arche_pdata->wake_detect_gpio); + + ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, + arche_platform_wd_irq, + arche_platform_wd_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, + dev_name(dev), arche_pdata); + if (ret) { + dev_err(dev, "failed to request wake detect IRQ %d\n", ret); + return ret; + } + /* Enable it only after sending wake/detect event */ + disable_irq(arche_pdata->wake_detect_irq); + ret = device_create_file(dev, &dev_attr_state); if (ret) { dev_err(dev, "failed to create state file in sysfs\n"); -- cgit v1.2.3-59-g8ed1b From 16fe18ca9ef0a3d806d33121a3a653138a1a6854 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 04:37:37 +0530 Subject: greybus: arche-platform: Assert wake/detect after SVC reset without delay Since now driver supports interrupt based mechanism to read events from SVC over wake/detect line, no need to delay wake/detect assertion. We can assert wake/detect after SVC reset deassertion, so during boot itself SVC will start sending wake_out pulses. Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Tested-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 83db892a8a5d..87526b2bf0a9 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -115,6 +115,16 @@ static void hub_conf_delayed_work(struct work_struct *work) dev_warn(arche_pdata->dev, "failed to control hub device\n"); } +static void assert_wakedetect(struct arche_platform_drvdata *arche_pdata) +{ + /* Assert wake/detect = Detect event from AP */ + gpio_direction_output(arche_pdata->wake_detect_gpio, 1); + + /* Enable interrupt here, to read event back from SVC */ + gpio_direction_input(arche_pdata->wake_detect_gpio); + enable_irq(arche_pdata->wake_detect_irq); +} + static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) { struct arche_platform_drvdata *arche_pdata = devid; @@ -297,6 +307,8 @@ static ssize_t state_store(struct device *dev, return count; ret = arche_platform_coldboot_seq(arche_pdata); + + assert_wakedetect(arche_pdata); } else if (sysfs_streq(buf, "standby")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) return count; @@ -482,6 +494,7 @@ static int arche_platform_probe(struct platform_device *pdev) goto err_populate; } + assert_wakedetect(arche_pdata); INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work); dev_info(dev, "Device registered successfully\n"); -- cgit v1.2.3-59-g8ed1b From 0786212d6c1514545f33554b7403ea10d8061eb7 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 04:37:38 +0530 Subject: greybus: arche-platform: Set direction of wake/detect gpio in poweroff fn With support of interrupt based mechanism, gpio is not longer set to output mode, so gpio_set_value won't work. So use gpio_direction_output() fn in poweroff(), while setting value on wake/detect line. Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Tested-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 87526b2bf0a9..5b393eca7bba 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -266,7 +266,7 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda /* If in fw_flashing mode, then no need to repeate things again */ if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { /* Send disconnect/detach event to SVC */ - gpio_set_value(arche_pdata->wake_detect_gpio, 0); + gpio_direction_output(arche_pdata->wake_detect_gpio, 0); usleep_range(100, 200); spin_lock_irqsave(&arche_pdata->lock, flags); arche_pdata->wake_detect_state = WD_STATE_IDLE; -- cgit v1.2.3-59-g8ed1b From d2320b2dbf95216e9aedf69345862f934ae55138 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 04:37:39 +0530 Subject: greybus: arche-platform: Disable wake/detect interrupt in poweroff In poweroff() fn, we are shutting down SVC, so disable interrupt as well. Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Reviewed-by: Michael Scott Tested-by: Michael Scott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 5b393eca7bba..21a84389bd3a 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -265,6 +265,7 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda /* If in fw_flashing mode, then no need to repeate things again */ if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { + disable_irq(arche_pdata->wake_detect_irq); /* Send disconnect/detach event to SVC */ gpio_direction_output(arche_pdata->wake_detect_gpio, 0); usleep_range(100, 200); -- cgit v1.2.3-59-g8ed1b From 34804efb0c25d189bd9aa6e495a3bf5d778a0299 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Feb 2016 14:40:23 +0100 Subject: greybus: operation: add temporary pointer to response handler As a preparatory clean up, add a temporary variable to point to the response message header. Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index bd79f81da020..9de548d1a05c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -858,6 +858,7 @@ static void gb_connection_recv_request(struct gb_connection *connection, static void gb_connection_recv_response(struct gb_connection *connection, u16 operation_id, u8 result, void *data, size_t size) { + struct gb_operation_msg_hdr *header; struct gb_operation *operation; struct gb_message *message; int errno = gb_operation_status_map(result); @@ -872,11 +873,12 @@ static void gb_connection_recv_response(struct gb_connection *connection, } message = operation->response; - message_size = sizeof(*message->header) + message->payload_size; + header = message->header; + message_size = sizeof(*header) + message->payload_size; if (!errno && size != message_size) { dev_err(&connection->hd->dev, "%s: malformed response 0x%02x received (%zu != %zu)\n", - connection->name, message->header->type, size, + connection->name, header->type, size, message_size); errno = -EMSGSIZE; } @@ -884,11 +886,11 @@ static void gb_connection_recv_response(struct gb_connection *connection, /* We must ignore the payload if a bad status is returned */ if (errno) - size = sizeof(*message->header); + size = sizeof(*header); /* The rest will be handled in work queue context */ if (gb_operation_result_set(operation, errno)) { - memcpy(message->header, data, size); + memcpy(header, data, size); queue_work(gb_operation_completion_wq, &operation->work); } -- cgit v1.2.3-59-g8ed1b From 7e43e337a524eb5ceab2cda9bbf882c35074bcc2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 25 Feb 2016 14:40:24 +0100 Subject: greybus: operation: add support for short responses Add support for operations with short responses. So far we have assumed that the initiator of an operation always knows the exact size of the expected response. This is however not always the case and we've worked around this limitation in a couple of places by, for example, first requesting the size of a resource before fetching the actual data. To avoid such workarounds and simplify our protocols, add a short-response flag that can be set when allocating an operation. When this flag is set on an operation, core will accept a response that is shorter than the size of the (pre-allocated) response payload buffer. For now, we update the response-message payload_size field to reflect the actual length of the response received. Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/operation.c | 35 +++++++++++++++++++++++++---------- drivers/staging/greybus/operation.h | 28 ++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 9de548d1a05c..5818d7c92c4e 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -530,20 +530,25 @@ err_cache: * invalid operation type for all protocols, and this is enforced * here. */ -struct gb_operation *gb_operation_create(struct gb_connection *connection, - u8 type, size_t request_size, - size_t response_size, - gfp_t gfp) +struct gb_operation * +gb_operation_create_flags(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size, unsigned long flags, + gfp_t gfp) { if (WARN_ON_ONCE(type == GB_OPERATION_TYPE_INVALID)) return NULL; if (WARN_ON_ONCE(type & GB_MESSAGE_TYPE_RESPONSE)) type &= ~GB_MESSAGE_TYPE_RESPONSE; + if (WARN_ON_ONCE(flags & ~GB_OPERATION_FLAG_USER_MASK)) + flags &= GB_OPERATION_FLAG_USER_MASK; + return gb_operation_create_common(connection, type, - request_size, response_size, 0, gfp); + request_size, response_size, + flags, gfp); } -EXPORT_SYMBOL_GPL(gb_operation_create); +EXPORT_SYMBOL_GPL(gb_operation_create_flags); size_t gb_operation_get_payload_size_max(struct gb_connection *connection) { @@ -875,12 +880,22 @@ static void gb_connection_recv_response(struct gb_connection *connection, message = operation->response; header = message->header; message_size = sizeof(*header) + message->payload_size; - if (!errno && size != message_size) { + if (!errno && size > message_size) { dev_err(&connection->hd->dev, - "%s: malformed response 0x%02x received (%zu != %zu)\n", - connection->name, header->type, size, - message_size); + "%s: malformed response 0x%02x received (%zu > %zu)\n", + connection->name, header->type, + size, message_size); errno = -EMSGSIZE; + } else if (!errno && size < message_size) { + if (gb_operation_short_response_allowed(operation)) { + message->payload_size = size - sizeof(*header); + } else { + dev_err(&connection->hd->dev, + "%s: short response 0x%02x received (%zu < %zu)\n", + connection->name, header->type, + size, message_size); + errno = -EMSGSIZE; + } } trace_gb_message_recv_response(operation->response); diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index c3f7ce71b089..38e5303a511f 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -65,6 +65,9 @@ struct gb_message { #define GB_OPERATION_FLAG_INCOMING BIT(0) #define GB_OPERATION_FLAG_UNIDIRECTIONAL BIT(1) +#define GB_OPERATION_FLAG_SHORT_RESPONSE BIT(2) + +#define GB_OPERATION_FLAG_USER_MASK GB_OPERATION_FLAG_SHORT_RESPONSE /* * A Greybus operation is a remote procedure call performed over a @@ -119,16 +122,33 @@ gb_operation_is_unidirectional(struct gb_operation *operation) return operation->flags & GB_OPERATION_FLAG_UNIDIRECTIONAL; } +static inline bool +gb_operation_short_response_allowed(struct gb_operation *operation) +{ + return operation->flags & GB_OPERATION_FLAG_SHORT_RESPONSE; +} + void gb_connection_recv(struct gb_connection *connection, void *data, size_t size); int gb_operation_result(struct gb_operation *operation); size_t gb_operation_get_payload_size_max(struct gb_connection *connection); -struct gb_operation *gb_operation_create(struct gb_connection *connection, - u8 type, size_t request_size, - size_t response_size, - gfp_t gfp); +struct gb_operation * +gb_operation_create_flags(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size, unsigned long flags, + gfp_t gfp); + +static inline struct gb_operation * +gb_operation_create(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size, gfp_t gfp) +{ + return gb_operation_create_flags(connection, type, request_size, + response_size, 0, gfp); +} + void gb_operation_get(struct gb_operation *operation); void gb_operation_put(struct gb_operation *operation); -- cgit v1.2.3-59-g8ed1b From ff788de0b41b960d7b4a650e167cfcc0d3f63644 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 16:45:44 +0530 Subject: greybus: arche-platform: Make sure APB power cycles on coldboot On first wake/detect pulse, everything works fine, as APB would be in poweroff state initially. But on subsequent wake/detect pulses, where APB is already in active state, internal function just returns doing nothing, as it finds that device is already in active state. So the solution would be to make sure that, whenever execution reaches to coldboot, make sure we power cycle it. Power off first, before powering on. Interrupt handler takes care of ignoring < 30msec pulses, so we should be safe here to power cycle APB. Testing Done: Testd on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 21a84389bd3a..f44c34314127 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -140,6 +140,8 @@ static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START; spin_unlock_irqrestore(&arche_pdata->lock, flags); + /* It should complete power cycle, so first make sure it is poweroff */ + device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); /* Bring APB out of reset: cold boot sequence */ device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); -- cgit v1.2.3-59-g8ed1b From e915ce48bae606b79150a4d799a30037604a93ed Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Thu, 25 Feb 2016 16:45:45 +0530 Subject: greybus: arche-platform: Disable HUB3613 only in APB poweroff fn HUB3613 configuration, either disable (Standby mode) or enable (HUB mode) is related to APB. So it makes perfect sense to put both of them together in one function. HUB3613 enable happens only at one place, in hub_conf_delayed_work() fn, but disable is initiated from multiple places. Move all calls to usb3613_hub_mode_ctrl(false) to apb_poweroff(). Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-platform.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index f44c34314127..64dd8a1ebe37 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -94,6 +94,10 @@ static int apb_poweroff(struct device *dev, void *data) { apb_ctrl_poweroff(dev); + /* Enable HUB3613 into HUB mode. */ + if (usb3613_hub_mode_ctrl(false)) + dev_warn(dev, "failed to control hub device\n"); + return 0; } @@ -142,6 +146,7 @@ static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) /* It should complete power cycle, so first make sure it is poweroff */ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); + /* Bring APB out of reset: cold boot sequence */ device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); @@ -301,10 +306,6 @@ static ssize_t state_store(struct device *dev, arche_platform_poweroff_seq(arche_pdata); - ret = usb3613_hub_mode_ctrl(false); - if (ret) - dev_warn(arche_pdata->dev, "failed to control hub device\n"); - /* TODO: Should we do anything more here ?? */ } else if (sysfs_streq(buf, "active")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) return count; @@ -327,11 +328,6 @@ static ssize_t state_store(struct device *dev, arche_platform_poweroff_seq(arche_pdata); - ret = usb3613_hub_mode_ctrl(false); - if (ret) - dev_warn(arche_pdata->dev, "failed to control hub device\n"); - /* TODO: Should we do anything more here ?? */ - arche_platform_fw_flashing_seq(arche_pdata); device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); -- cgit v1.2.3-59-g8ed1b From bf9deb29e2b3c5dbc97ce559325f4faa0bad5022 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Thu, 25 Feb 2016 18:05:09 +0000 Subject: greybus: lights: remove unnecessary check lights can never be NULL at that point since lights_count must be different than zero, and we need only to validate the light_id. Reported-by: Johan Hovold Signed-off-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/light.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 17877f7e2ee4..47d4ac4533bc 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1178,7 +1178,7 @@ static int gb_lights_request_handler(struct gb_operation *op) payload = request->payload; light_id = payload->light_id; - if (light_id >= glights->lights_count || !glights->lights || + if (light_id >= glights->lights_count || !glights->lights[light_id].ready) { dev_err(dev, "Event received for unconfigured light id: %d\n", light_id); -- cgit v1.2.3-59-g8ed1b From d97bbf3ed6211c01bd7df77d405dee2e9846cd68 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Thu, 25 Feb 2016 18:19:13 +0100 Subject: greybus: loopback: Fix calculations error for ping transfers For the async ping transfer, statistics are counted twice, once after the after the gb_loopback_async_operation() and once in the callback. Only keep the one in the callback. Signed-off-by: Alexandre Bailon Reported-by: Johan Hovold Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index f3ae2e9fc1bb..b40c95eb8801 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -969,7 +969,6 @@ static int gb_loopback_fn(void *data) if (gb->async) { if (type == GB_LOOPBACK_TYPE_PING) { error = gb_loopback_async_ping(gb); - gb_loopback_calculate_stats(gb); } else if (type == GB_LOOPBACK_TYPE_TRANSFER) { error = gb_loopback_async_transfer(gb, size); } else if (type == GB_LOOPBACK_TYPE_SINK) { -- cgit v1.2.3-59-g8ed1b From d9048d8c96bfe2d18cdf7205b3de97aa344283a7 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Thu, 25 Feb 2016 18:19:14 +0100 Subject: greybus: loopback: Fix incoherency in calculations in the case of error Currently, in case the case of error, statistics are updated for asynchronous but not for an asynchronous operation. Do not update the statistics in the case of error. Signed-off-by: Alexandre Bailon Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index b40c95eb8801..a1f878b24e74 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -398,10 +398,8 @@ static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, do_gettimeofday(&ts); operation = gb_operation_create(gb->connection, type, request_size, response_size, GFP_KERNEL); - if (!operation) { - ret = -ENOMEM; - goto error; - } + if (!operation) + return -ENOMEM; if (request_size) memcpy(operation->request->payload, request, request_size); @@ -410,6 +408,7 @@ static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, if (ret) { dev_err(&gb->connection->bundle->dev, "synchronous operation failed: %d\n", ret); + goto out_put_operation; } else { if (response_size == operation->response->payload_size) { memcpy(response, operation->response->payload, @@ -419,18 +418,20 @@ static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, "response size %zu expected %d\n", operation->response->payload_size, response_size); + ret = -EINVAL; + goto out_put_operation; } } - gb_operation_put(operation); - -error: do_gettimeofday(&te); /* Calculate the total time the message took */ gb_loopback_push_latency_ts(gb, &ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); +out_put_operation: + gb_operation_put(operation); + return ret; } @@ -988,8 +989,9 @@ static int gb_loopback_fn(void *data) if (error) gb->error++; + else + gb_loopback_calculate_stats(gb); gb->iteration_count++; - gb_loopback_calculate_stats(gb); } send_count++; if (us_wait) -- cgit v1.2.3-59-g8ed1b From ab81bb9c68d9fb899b79fa622fedf294e66fa53d Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Thu, 25 Feb 2016 18:19:15 +0100 Subject: greybus: loopback: Fix throughput calculations Throughput and requests per second calculations are broken for asynchronous request. Instead of calculate the throughput for each iteration, calculate it once at the end of the test. In addition, update every seconds the min and the max for throughput and requests per second. Signed-off-by: Alexandre Bailon Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 81 +++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index a1f878b24e74..85d3e35e6a5f 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -81,6 +81,7 @@ struct gb_loopback { atomic_t outstanding_operations; /* Per connection stats */ + struct timeval ts; struct gb_loopback_stats latency; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; @@ -351,7 +352,7 @@ static struct attribute *loopback_attrs[] = { }; ATTRIBUTE_GROUPS(loopback); -static void gb_loopback_calculate_stats(struct gb_loopback *gb); +static void gb_loopback_calculate_stats(struct gb_loopback *gb, bool error); static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs) { @@ -517,11 +518,9 @@ static void gb_loopback_async_operation_callback(struct gb_operation *operation) if (err) { gb->error++; } else { - gb->requests_completed++; gb_loopback_push_latency_ts(gb, &op_async->ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&op_async->ts, &te); - gb_loopback_calculate_stats(gb); } if (op_async->pending) { @@ -529,6 +528,7 @@ static void gb_loopback_async_operation_callback(struct gb_operation *operation) op_async->pending = false; del_timer_sync(&op_async->timer); gb_loopback_async_operation_put(op_async); + gb_loopback_calculate_stats(gb, err); } mutex_unlock(&gb->mutex); @@ -555,6 +555,7 @@ static void gb_loopback_async_operation_work(struct work_struct *work) gb->iteration_count++; op_async->pending = false; gb_loopback_async_operation_put(op_async); + gb_loopback_calculate_stats(gb, true); } mutex_unlock(&gb->mutex); @@ -847,6 +848,7 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) /* Should be initialized at least once per transaction set */ gb->apbridge_latency_ts = 0; gb->gpbridge_latency_ts = 0; + memset(&gb->ts, 0, sizeof(struct timeval)); } static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) @@ -859,18 +861,29 @@ static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) stats->count++; } +static void gb_loopback_update_stats_window(struct gb_loopback_stats *stats, + u64 val, u32 count) +{ + stats->sum += val; + stats->count += count; + + do_div(val, count); + if (stats->min > val) + stats->min = val; + if (stats->max < val) + stats->max = val; +} + static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency) { - u32 req = USEC_PER_SEC; + u64 req = gb->requests_completed * USEC_PER_SEC; - do_div(req, latency); - gb_loopback_update_stats(&gb->requests_per_second, req); + gb_loopback_update_stats_window(&gb->requests_per_second, req, latency); } static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) { - u32 throughput; - u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2; + u64 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2; switch (gb->type) { case GB_LOOPBACK_TYPE_PING: @@ -888,14 +901,13 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) return; } - /* Calculate bytes per second */ - throughput = USEC_PER_SEC; - do_div(throughput, latency); - throughput *= aggregate_size; - gb_loopback_update_stats(&gb->throughput, throughput); + aggregate_size *= gb->requests_completed; + aggregate_size *= USEC_PER_SEC; + gb_loopback_update_stats_window(&gb->throughput, aggregate_size, + latency); } -static void gb_loopback_calculate_stats(struct gb_loopback *gb) +static void gb_loopback_calculate_latency_stats(struct gb_loopback *gb) { u32 lat; @@ -908,10 +920,6 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) /* Raw latency log on a per thread basis */ kfifo_in(&gb->kfifo_lat, (unsigned char *)&lat, sizeof(lat)); - /* Log throughput and requests using latency as benchmark */ - gb_loopback_throughput_update(gb, lat); - gb_loopback_requests_update(gb, lat); - /* Log the firmware supplied latency values */ gb_loopback_update_stats(&gb->apbridge_unipro_latency, gb->apbridge_latency_ts); @@ -919,6 +927,32 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) gb->gpbridge_latency_ts); } +static void gb_loopback_calculate_stats(struct gb_loopback *gb, bool error) +{ + u64 nlat; + u32 lat; + struct timeval te; + + if (!error) { + gb->requests_completed++; + gb_loopback_calculate_latency_stats(gb); + } + + do_gettimeofday(&te); + nlat = gb_loopback_calc_latency(&gb->ts, &te); + if (nlat >= NSEC_PER_SEC || gb->iteration_count == gb->iteration_max) { + lat = gb_loopback_nsec_to_usec_latency(nlat); + + gb_loopback_throughput_update(gb, lat); + gb_loopback_requests_update(gb, lat); + + if (gb->iteration_count != gb->iteration_max) { + gb->ts = te; + gb->requests_completed = 0; + } + } +} + static void gb_loopback_async_wait_to_send(struct gb_loopback *gb) { if (!(gb->async && gb->outstanding_operations_max)) @@ -956,14 +990,18 @@ static int gb_loopback_fn(void *data) /* Optionally terminate */ if (send_count == gb->iteration_max) { - gb->type = 0; - send_count = 0; + if (gb->iteration_count == gb->iteration_max) { + gb->type = 0; + send_count = 0; + } mutex_unlock(&gb->mutex); continue; } size = gb->size; us_wait = gb->us_wait; type = gb->type; + if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) + do_gettimeofday(&gb->ts); mutex_unlock(&gb->mutex); /* Else operations to perform */ @@ -989,9 +1027,8 @@ static int gb_loopback_fn(void *data) if (error) gb->error++; - else - gb_loopback_calculate_stats(gb); gb->iteration_count++; + gb_loopback_calculate_stats(gb, !!error); } send_count++; if (us_wait) -- cgit v1.2.3-59-g8ed1b From fb37f137b78f186275b15e8ce1e5a26b51a6e6ad Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Thu, 25 Feb 2016 18:19:16 +0100 Subject: greybus: loopback: Fix averaging Currently, we are adding 0.5 to the average to round the average. But we are using the remainder to calculate the decimal, so we do not need to round the average. In addition, use a u64 type for the remainder to avoid overflow that might happen when stats->sum value is too big, usually for requests per seconds and the throughput. Signed-off-by: Alexandre Bailon Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 85d3e35e6a5f..9f0da577d842 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -157,14 +157,15 @@ static ssize_t name##_avg_show(struct device *dev, \ { \ struct gb_loopback_stats *stats; \ struct gb_loopback *gb; \ - u64 avg; \ - u32 count, rem; \ + u64 avg, rem; \ + u32 count; \ gb = dev_get_drvdata(dev); \ stats = &gb->name; \ count = stats->count ? stats->count : 1; \ - avg = stats->sum + count / 2; /* round closest */ \ + avg = stats->sum; \ rem = do_div(avg, count); \ - return sprintf(buf, "%llu.%06u\n", avg, 1000000 * rem / count); \ + rem = 1000000 * rem / count; \ + return sprintf(buf, "%llu.%06u\n", avg, (u32)rem); \ } \ static DEVICE_ATTR_RO(name##_avg) -- cgit v1.2.3-59-g8ed1b From 611924dd72594200ac55957b4e68b0a65bab143b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 26 Feb 2016 21:54:38 -0800 Subject: greybus: expose full 32 bits of vid/pid to userspace Now that userspace is ready for all 32 bits of the vid/pid, take off our mask and send the full values. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/core.c | 5 ++--- drivers/staging/greybus/interface.c | 25 ++----------------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 2fb95744e01c..ca7469ee8994 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -112,9 +112,8 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) if (intf) { if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) return -ENOMEM; - if (add_uevent_var(env, "GREYBUS_ID=%04x/%04x", - (u16)(intf->vendor_id & 0xffff), - (u16)(intf->product_id & 0xffff))) + if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x", + intf->vendor_id, intf->product_id)) return -ENOMEM; } diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 5e49bc826fe7..2f3966f319bc 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -23,33 +23,12 @@ static DEVICE_ATTR_RO(field) gb_interface_attr(ddbl1_manufacturer_id, "0x%08x"); gb_interface_attr(ddbl1_product_id, "0x%08x"); gb_interface_attr(interface_id, "%u"); +gb_interface_attr(vendor_id, "0x%08x"); +gb_interface_attr(product_id, "0x%08x"); gb_interface_attr(vendor_string, "%s"); gb_interface_attr(product_string, "%s"); gb_interface_attr(serial_number, "0x%016llx"); -static ssize_t vendor_id_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct gb_interface *intf = to_gb_interface(dev); - - /* clear the upper 16-bits to keep userspace "simple" */ - return scnprintf(buf, PAGE_SIZE, "0x%04x\n", - (0x0000FFFF & intf->vendor_id)); -} -static DEVICE_ATTR_RO(vendor_id); - -static ssize_t product_id_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct gb_interface *intf = to_gb_interface(dev); - - /* clear the upper 16-bits to keep userspace "simple" */ - return scnprintf(buf, PAGE_SIZE, "0x%04x\n", - (0x0000FFFF & intf->product_id)); -} -static DEVICE_ATTR_RO(product_id); - static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) { -- cgit v1.2.3-59-g8ed1b From 4a8e519902e73c833fb57f69bc194c2274dcdc30 Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Fri, 26 Feb 2016 17:04:36 -0700 Subject: greybus: audio: Register CPorts for specific directions Currently, it is assumed that all audio data CPorts registered on APB1 are used for transmitting audio data. That may not always be true like when a microphone is connected but no speakers. Also, the current special protocol lacks a way to tell APB1 whether the CPort being registered is for transmitting, receiving, or both. Fix by adding a 'direction' field to the register and unregister CPort requests and define bits indicating which direction (or both) audio data will go on that CPort. Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_apbridgea.c | 8 ++++++-- drivers/staging/greybus/audio_apbridgea.h | 5 +++++ drivers/staging/greybus/audio_codec.c | 9 ++++++--- drivers/staging/greybus/audio_codec.h | 6 ++++-- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/audio_apbridgea.c b/drivers/staging/greybus/audio_apbridgea.c index 75c8c3ce4b71..bed087d3894b 100644 --- a/drivers/staging/greybus/audio_apbridgea.c +++ b/drivers/staging/greybus/audio_apbridgea.c @@ -29,13 +29,15 @@ int gb_audio_apbridgea_set_config(struct gb_connection *connection, EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_config); int gb_audio_apbridgea_register_cport(struct gb_connection *connection, - __u16 i2s_port, __u16 cportid) + __u16 i2s_port, __u16 cportid, + __u8 direction) { struct audio_apbridgea_register_cport_request req; req.hdr.type = AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT; req.hdr.i2s_port = cpu_to_le16(i2s_port); req.cport = cpu_to_le16(cportid); + req.direction = direction; return gb_hd_output(connection->hd, &req, sizeof(req), GB_APB_REQUEST_AUDIO_CONTROL, true); @@ -43,13 +45,15 @@ int gb_audio_apbridgea_register_cport(struct gb_connection *connection, EXPORT_SYMBOL_GPL(gb_audio_apbridgea_register_cport); int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, - __u16 i2s_port, __u16 cportid) + __u16 i2s_port, __u16 cportid, + __u8 direction) { struct audio_apbridgea_unregister_cport_request req; req.hdr.type = AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT; req.hdr.i2s_port = cpu_to_le16(i2s_port); req.cport = cpu_to_le16(cportid); + req.direction = direction; return gb_hd_output(connection->hd, &req, sizeof(req), GB_APB_REQUEST_AUDIO_CONTROL, true); diff --git a/drivers/staging/greybus/audio_apbridgea.h b/drivers/staging/greybus/audio_apbridgea.h index d1d56b7c1a75..c543e399ca04 100644 --- a/drivers/staging/greybus/audio_apbridgea.h +++ b/drivers/staging/greybus/audio_apbridgea.h @@ -75,6 +75,9 @@ #define AUDIO_APBRIDGEA_PCM_RATE_176400 BIT(11) #define AUDIO_APBRIDGEA_PCM_RATE_192000 BIT(12) +#define AUDIO_APBRIDGEA_DIRECTION_TX BIT(0) +#define AUDIO_APBRIDGEA_DIRECTION_RX BIT(1) + /* The I2S port is passed in the 'index' parameter of the USB request */ /* The CPort is passed in the 'value' parameter of the USB request */ @@ -94,11 +97,13 @@ struct audio_apbridgea_set_config_request { struct audio_apbridgea_register_cport_request { struct audio_apbridgea_hdr hdr; __le16 cport; + __u8 direction; } __packed; struct audio_apbridgea_unregister_cport_request { struct audio_apbridgea_hdr hdr; __le16 cport; + __u8 direction; } __packed; struct audio_apbridgea_set_tx_data_size_request { diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index d820116dd196..5e29694139f7 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -60,7 +60,8 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, i2s_port = 0; /* fixed for now */ cportid = gb_dai->connection->hd_cport_id; ret = gb_audio_apbridgea_register_cport(gb_dai->connection, i2s_port, - cportid); + cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid, ret); @@ -117,7 +118,8 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, /* un register cport */ i2s_port = 0; /* fixed for now */ ret = gb_audio_apbridgea_unregister_cport(gb_dai->connection, i2s_port, - gb_dai->connection->hd_cport_id); + gb_dai->connection->hd_cport_id, + AUDIO_APBRIDGEA_DIRECTION_TX); dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name, gb_dai->connection->hd_cport_id, ret); @@ -495,7 +497,8 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) ret); cportid = connection->hd_cport_id; ret = gb_audio_apbridgea_unregister_cport(connection, 0, - cportid); + cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); if (ret) dev_info(dev, "%d:Failed during unregister cport\n", ret); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index fc60c36aa040..06312031af35 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -198,9 +198,11 @@ extern int gb_audio_apbridgea_set_config(struct gb_connection *connection, __u16 i2s_port, __u32 format, __u32 rate, __u32 mclk_freq); extern int gb_audio_apbridgea_register_cport(struct gb_connection *connection, - __u16 i2s_port, __u16 cportid); + __u16 i2s_port, __u16 cportid, + __u8 direction); extern int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, - __u16 i2s_port, __u16 cportid); + __u16 i2s_port, __u16 cportid, + __u8 direction); extern int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, __u16 i2s_port, __u16 size); extern int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, -- cgit v1.2.3-59-g8ed1b From b22b7104d5930ed4aae2c8494b8f95f20a85d66b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 29 Feb 2016 12:19:50 +0530 Subject: greybus: hid: Don't disable connection-tx before destroying hid-device hid_destroy_device() can potentially call callbacks defined in struct hid_ll_driver, which may initiate few greybus operations. And so connection (tx) should be kept enabled until the hid-device isn't destroyed. Reported-by: Jiss Kuruvila Reported-by: Laurent Pinchart Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 4db337cf7acc..601ee6fc81d5 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -490,8 +490,8 @@ static void gb_hid_disconnect(struct gb_bundle *bundle) { struct gb_hid *ghid = greybus_get_drvdata(bundle); - gb_connection_disable(ghid->connection); hid_destroy_device(ghid->hid); + gb_connection_disable(ghid->connection); gb_connection_destroy(ghid->connection); kfree(ghid); } -- cgit v1.2.3-59-g8ed1b From 9b22f155beb4e5a71f86710034b478ec43af9c43 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 26 Feb 2016 16:44:19 +0530 Subject: greybus: hid: Replace WARN_ON() with dev_err() WARN_ON() is a bit harsh here, as we just failed to power-off the HID device while it is getting removed. Replace it with dev_err(). Signed-off-by: Viresh Kumar Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 601ee6fc81d5..6ef151f61ffc 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -345,6 +345,7 @@ static int gb_hid_open(struct hid_device *hid) static void gb_hid_close(struct hid_device *hid) { struct gb_hid *ghid = hid->driver_data; + int ret; /* * Protecting hid->open to make sure we don't restart data acquistion @@ -355,7 +356,10 @@ static void gb_hid_close(struct hid_device *hid) clear_bit(GB_HID_STARTED, &ghid->flags); /* Save some power */ - WARN_ON(gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF)); + ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF); + if (ret) + dev_err(&ghid->connection->bundle->dev, + "failed to power off (%d)\n", ret); } mutex_unlock(&gb_hid_open_mutex); } -- cgit v1.2.3-59-g8ed1b From 39c2787b120a6e69e065b255d4db97ee60d73bf8 Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Fri, 26 Feb 2016 11:39:48 +0100 Subject: greybus: Notify user space only when the test finished. Currently, user space is notified for every message sent, but this is not really needed and does not work in the async case where all messages are sent from the start. Instead, notify userspace only when all the transfers are complete. This allows userspace to wait in a poll loop and wakeup only when the test is finished. Also, don't use the bundle kobj to send the notification it is the loopback device that contains the loopback attributes. Signed-off-by: Axel Haslam Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 9f0da577d842..9014c2b51ae0 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -986,14 +986,14 @@ static int gb_loopback_fn(void *data) break; mutex_lock(&gb->mutex); - sysfs_notify(&gb->connection->bundle->dev.kobj, - NULL, "iteration_count"); /* Optionally terminate */ if (send_count == gb->iteration_max) { if (gb->iteration_count == gb->iteration_max) { gb->type = 0; send_count = 0; + sysfs_notify(&gb->dev->kobj, NULL, + "iteration_count"); } mutex_unlock(&gb->mutex); continue; -- cgit v1.2.3-59-g8ed1b From 9250c0ee2626d371753027b1b5ede697676de6d1 Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Fri, 26 Feb 2016 11:39:49 +0100 Subject: greybus: Loopback_test: use poll instead of inotify Inotify does not handle sysfs events, so use poll instead. The loopback kernel driver will send a notification when the test is complete. So, open a poll file descriptor for every enabled device, and after starting the test, wait for an event from each device. After all events are received, read the total number of iterations and make sure the test is complete. Also, add missing stdint header which was included in inotify. Signed-off-by: Axel Haslam Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/tools/loopback_test.c | 139 +++++++++++++------------- 1 file changed, 70 insertions(+), 69 deletions(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index 6178354fbb74..0abe62390b1b 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -11,7 +11,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -22,8 +23,7 @@ #define CSV_MAX_LINE 0x1000 #define SYSFS_MAX_INT 0x20 #define MAX_STR_LEN 255 -#define MAX_TIMEOUT_COUNT 5 -#define TIMEOUT_SEC 1 +#define DEFAULT_POLL_TIMEOUT_SEC 30 #define DEFAULT_ASYNC_TIMEOUT 200000 struct dict { @@ -71,7 +71,6 @@ struct loopback_device { char name[MAX_SYSFS_PATH]; char sysfs_entry[MAX_SYSFS_PATH]; char debugfs_entry[MAX_SYSFS_PATH]; - int inotify_wd; struct loopback_results results; }; @@ -86,19 +85,22 @@ struct loopback_test { int aggregate_output; int test_id; int device_count; - int inotify_fd; int list_devices; int use_async; int async_timeout; + int poll_timeout; int async_outstanding_operations; int us_wait; int file_output; + int poll_count; char test_name[MAX_STR_LEN]; char sysfs_prefix[MAX_SYSFS_PATH]; char debugfs_prefix[MAX_SYSFS_PATH]; struct loopback_device devices[MAX_NUM_DEVICES]; struct loopback_results aggregate_results; + struct pollfd fds[MAX_NUM_DEVICES]; }; + struct loopback_test t; /* Helper macros to calculate the aggregate results for all devices */ @@ -201,6 +203,7 @@ void usage(void) " -l list found loopback devices and exit\n" " -x Async - Enable async transfers\n" " -o Async Timeout - Timeout in uSec for async operations\n" + " -O Poll loop time out in seconds(max time a test is expected to last, default: 30sec)\n" " -c Max number of outstanding operations for async operations\n" " -w Wait in uSec between operations\n" " -z Enable output to a CSV file (incompatible with -p)\n" @@ -638,58 +641,51 @@ baddir: return ret; } - -static int register_for_notification(struct loopback_test *t) +static int open_poll_files(struct loopback_test *t) { - char buf[MAX_SYSFS_PATH]; + struct loopback_device *dev; + char buf[MAX_STR_LEN]; + char dummy; + int fds_idx = 0; int i; - t->inotify_fd = inotify_init(); - if (t->inotify_fd < 0) { - fprintf(stderr, "inotify_init fail %s\n", strerror(errno)); - abort(); - } - for (i = 0; i < t->device_count; i++) { + dev = &t->devices[i]; + if (!device_enabled(t, i)) continue; - snprintf(buf, sizeof(buf), "%s%s", t->devices[i].sysfs_entry, - "iteration_count"); - - t->devices[i].inotify_wd = inotify_add_watch(t->inotify_fd, - buf, IN_MODIFY); - if (t->devices[i].inotify_wd < 0) { - fprintf(stderr, "inotify_add_watch %s fail %s\n", - buf, strerror(errno)); - close(t->inotify_fd); - abort(); + snprintf(buf, sizeof(buf), "%s%s", dev->sysfs_entry, "iteration_count"); + t->fds[fds_idx].fd = open(buf, O_RDONLY); + if (t->fds[fds_idx].fd < 0) { + fprintf(stderr, "Error opening poll file!\n"); + goto err; } + read(t->fds[fds_idx].fd, &dummy, 1); + t->fds[fds_idx].events = POLLERR|POLLPRI; + t->fds[fds_idx].revents = 0; + fds_idx++; } + t->poll_count = fds_idx; + return 0; + +err: + for (i = 0; i < fds_idx; i++) + close(t->fds[fds_idx].fd); + + return -1; } -static int unregister_for_notification(struct loopback_test *t) +static int close_poll_files(struct loopback_test *t) { int i; - int ret = 0; - - for (i = 0; i < t->device_count; i++) { - if (!device_enabled(t, i)) - continue; - - ret = inotify_rm_watch(t->inotify_fd, t->devices[i].inotify_wd); - if (ret) { - fprintf(stderr, "inotify_rm_watch error.\n"); - return ret; - } - } + for (i = 0; i < t->poll_count; i++) + close(t->fds[i].fd); - close(t->inotify_fd); return 0; } - static int is_complete(struct loopback_test *t) { int iteration_count; @@ -712,39 +708,38 @@ static int is_complete(struct loopback_test *t) static int wait_for_complete(struct loopback_test *t) { - int remaining_timeouts = MAX_TIMEOUT_COUNT; - char buf[MAX_SYSFS_PATH]; - struct timeval timeout; - fd_set read_fds; + int number_of_events = 0; + char dummy; int ret; + int i; while (1) { - /* Wait for change */ - timeout.tv_sec = TIMEOUT_SEC; - timeout.tv_usec = 0; - FD_ZERO(&read_fds); - FD_SET(t->inotify_fd, &read_fds); - ret = select(FD_SETSIZE, &read_fds, NULL, NULL, &timeout); - if (ret < 0) { - fprintf(stderr, "Select error.\n"); + ret = poll(t->fds, t->poll_count, DEFAULT_POLL_TIMEOUT_SEC * 1000); + if (ret == 0) { + fprintf(stderr, "Poll timmed out!\n"); return -1; } - /* timeout - test may be finished.*/ - if (!FD_ISSET(t->inotify_fd, &read_fds)) { - remaining_timeouts--; - - if (is_complete(t)) - return 0; + if (ret < 0) { + fprintf(stderr, "Poll Error!\n"); + return -1; + } - if (!remaining_timeouts) { - fprintf(stderr, "Too many timeouts\n"); - return -1; + for (i = 0; i < t->poll_count; i++) { + if (t->fds[i].revents & POLLPRI) { + /* Dummy read to clear the event */ + read(t->fds[i].fd, &dummy, 1); + number_of_events++; } - } else { - /* read to clear the event */ - ret = read(t->inotify_fd, buf, sizeof(buf)); } + + if (number_of_events == t->poll_count) + break; + } + + if (!is_complete(t)) { + fprintf(stderr, "Iteration count did not finish!\n"); + return -1; } return 0; @@ -820,17 +815,17 @@ void loopback_run(struct loopback_test *t) prepare_devices(t); - ret = register_for_notification(t); + ret = open_poll_files(t); if (ret) goto err; start(t); - sleep(1); - - wait_for_complete(t); + ret = wait_for_complete(t); + close_poll_files(t); + if (ret) + goto err; - unregister_for_notification(t); get_results(t); @@ -875,7 +870,7 @@ int main(int argc, char *argv[]) memset(&t, 0, sizeof(t)); while ((o = getopt(argc, argv, - "t:s:i:S:D:m:v::d::r::p::a::l::x::o:c:w:")) != -1) { + "t:s:i:S:D:m:v::d::r::p::a::l::x::o:c:w:O:")) != -1) { switch (o) { case 't': snprintf(t.test_name, MAX_STR_LEN, "%s", optarg); @@ -919,6 +914,9 @@ int main(int argc, char *argv[]) case 'o': t.async_timeout = atoi(optarg); break; + case 'O': + t.poll_timeout = atoi(optarg); + break; case 'c': t.async_outstanding_operations = atoi(optarg); break; @@ -957,6 +955,9 @@ int main(int argc, char *argv[]) if (t.async_timeout == 0) t.async_timeout = DEFAULT_ASYNC_TIMEOUT; + if (t.poll_timeout == 0) + t.poll_timeout = DEFAULT_POLL_TIMEOUT_SEC; + loopback_run(&t); return 0; -- cgit v1.2.3-59-g8ed1b From 5bbe14b7acc2a00f51b23812ffc596577d94e80b Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Mon, 29 Feb 2016 15:31:02 -0700 Subject: greybus: audio: Split start and stop APBridgeA requests Provide finer-grained control of the audio streaming on APB1 by splitting the transmit/receive start and stop requests into prepare, start, stop, and shutdown. CC: Vaibhav Agarwal Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_apbridgea.c | 52 +++++++++++++++++++++++++++++++ drivers/staging/greybus/audio_apbridgea.h | 32 +++++++++++++++---- drivers/staging/greybus/audio_codec.c | 32 +++++++++++++++---- drivers/staging/greybus/audio_codec.h | 8 +++++ 4 files changed, 112 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/audio_apbridgea.c b/drivers/staging/greybus/audio_apbridgea.c index bed087d3894b..a6e089d2d1a8 100644 --- a/drivers/staging/greybus/audio_apbridgea.c +++ b/drivers/staging/greybus/audio_apbridgea.c @@ -82,6 +82,19 @@ int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, } EXPORT_SYMBOL_GPL(gb_audio_apbridgea_get_tx_delay); +int gb_audio_apbridgea_prepare_tx(struct gb_connection *connection, + __u16 i2s_port) +{ + struct audio_apbridgea_prepare_tx_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_PREPARE_TX; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_prepare_tx); + int gb_audio_apbridgea_start_tx(struct gb_connection *connection, __u16 i2s_port, __u64 timestamp) { @@ -108,6 +121,19 @@ int gb_audio_apbridgea_stop_tx(struct gb_connection *connection, __u16 i2s_port) } EXPORT_SYMBOL_GPL(gb_audio_apbridgea_stop_tx); +int gb_audio_apbridgea_shutdown_tx(struct gb_connection *connection, + __u16 i2s_port) +{ + struct audio_apbridgea_shutdown_tx_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_SHUTDOWN_TX; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_shutdown_tx); + int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, __u16 i2s_port, __u16 size) { @@ -130,6 +156,19 @@ int gb_audio_apbridgea_get_rx_delay(struct gb_connection *connection, } EXPORT_SYMBOL_GPL(gb_audio_apbridgea_get_rx_delay); +int gb_audio_apbridgea_prepare_rx(struct gb_connection *connection, + __u16 i2s_port) +{ + struct audio_apbridgea_prepare_rx_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_PREPARE_RX; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_prepare_rx); + int gb_audio_apbridgea_start_rx(struct gb_connection *connection, __u16 i2s_port) { @@ -155,6 +194,19 @@ int gb_audio_apbridgea_stop_rx(struct gb_connection *connection, __u16 i2s_port) } EXPORT_SYMBOL_GPL(gb_audio_apbridgea_stop_rx); +int gb_audio_apbridgea_shutdown_rx(struct gb_connection *connection, + __u16 i2s_port) +{ + struct audio_apbridgea_shutdown_rx_request req; + + req.hdr.type = AUDIO_APBRIDGEA_TYPE_SHUTDOWN_RX; + req.hdr.i2s_port = cpu_to_le16(i2s_port); + + return gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); +} +EXPORT_SYMBOL_GPL(gb_audio_apbridgea_shutdown_rx); + MODULE_LICENSE("GPL v2"); MODULE_ALIAS("greybus:audio-apbridgea"); MODULE_DESCRIPTION("Greybus Special APBridgeA Audio Protocol library"); diff --git a/drivers/staging/greybus/audio_apbridgea.h b/drivers/staging/greybus/audio_apbridgea.h index c543e399ca04..a48f815bc56b 100644 --- a/drivers/staging/greybus/audio_apbridgea.h +++ b/drivers/staging/greybus/audio_apbridgea.h @@ -48,12 +48,16 @@ #define AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT 0x03 #define AUDIO_APBRIDGEA_TYPE_SET_TX_DATA_SIZE 0x04 #define AUDIO_APBRIDGEA_TYPE_GET_TX_DELAY 0x05 -#define AUDIO_APBRIDGEA_TYPE_START_TX 0x06 -#define AUDIO_APBRIDGEA_TYPE_STOP_TX 0x07 -#define AUDIO_APBRIDGEA_TYPE_SET_RX_DATA_SIZE 0x08 -#define AUDIO_APBRIDGEA_TYPE_GET_RX_DELAY 0x09 -#define AUDIO_APBRIDGEA_TYPE_START_RX 0x0a -#define AUDIO_APBRIDGEA_TYPE_STOP_RX 0x0b +#define AUDIO_APBRIDGEA_TYPE_PREPARE_TX 0x06 +#define AUDIO_APBRIDGEA_TYPE_START_TX 0x07 +#define AUDIO_APBRIDGEA_TYPE_STOP_TX 0x08 +#define AUDIO_APBRIDGEA_TYPE_SHUTDOWN_TX 0x09 +#define AUDIO_APBRIDGEA_TYPE_SET_RX_DATA_SIZE 0x0a +#define AUDIO_APBRIDGEA_TYPE_GET_RX_DELAY 0x0b +#define AUDIO_APBRIDGEA_TYPE_PREPARE_RX 0x0c +#define AUDIO_APBRIDGEA_TYPE_START_RX 0x0d +#define AUDIO_APBRIDGEA_TYPE_STOP_RX 0x0e +#define AUDIO_APBRIDGEA_TYPE_SHUTDOWN_RX 0x0f #define AUDIO_APBRIDGEA_PCM_FMT_8 BIT(0) #define AUDIO_APBRIDGEA_PCM_FMT_16 BIT(1) @@ -120,6 +124,10 @@ struct audio_apbridgea_get_tx_delay_response { __le16 delay; } __packed; +struct audio_apbridgea_prepare_tx_request { + struct audio_apbridgea_hdr hdr; +} __packed; + struct audio_apbridgea_start_tx_request { struct audio_apbridgea_hdr hdr; __le64 timestamp; @@ -129,6 +137,10 @@ struct audio_apbridgea_stop_tx_request { struct audio_apbridgea_hdr hdr; } __packed; +struct audio_apbridgea_shutdown_tx_request { + struct audio_apbridgea_hdr hdr; +} __packed; + struct audio_apbridgea_set_rx_data_size_request { struct audio_apbridgea_hdr hdr; __le16 size; @@ -143,6 +155,10 @@ struct audio_apbridgea_get_rx_delay_response { __le16 delay; } __packed; +struct audio_apbridgea_prepare_rx_request { + struct audio_apbridgea_hdr hdr; +} __packed; + struct audio_apbridgea_start_rx_request { struct audio_apbridgea_hdr hdr; } __packed; @@ -151,4 +167,8 @@ struct audio_apbridgea_stop_rx_request { struct audio_apbridgea_hdr hdr; } __packed; +struct audio_apbridgea_shutdown_rx_request { + struct audio_apbridgea_hdr hdr; +} __packed; + #endif /*__AUDIO_APBRIDGEA_H */ diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 5e29694139f7..d7cae772dbf5 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -345,17 +345,33 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, goto func_exit; } - if (start && tx) - ret = gb_audio_apbridgea_start_tx(gb_dai->connection, 0, 0); + if (start && tx) { + ret = gb_audio_apbridgea_prepare_tx(gb_dai->connection, 0); + if (!ret) + ret = gb_audio_apbridgea_start_tx(gb_dai->connection, 0, + 0); + } - else if (start && rx) - ret = gb_audio_apbridgea_start_rx(gb_dai->connection, 0); + else if (start && rx) { + ret = gb_audio_apbridgea_prepare_rx(gb_dai->connection, 0); + if (!ret) + ret = gb_audio_apbridgea_start_rx(gb_dai->connection, + 0); + } - else if (stop && tx) + else if (stop && tx) { ret = gb_audio_apbridgea_stop_tx(gb_dai->connection, 0); + if (!ret) + ret = gb_audio_apbridgea_shutdown_tx(gb_dai->connection, + 0); + } - else if (stop && rx) + else if (stop && rx) { ret = gb_audio_apbridgea_stop_rx(gb_dai->connection, 0); + if (!ret) + ret = gb_audio_apbridgea_shutdown_rx(gb_dai->connection, + 0); + } else ret = -EINVAL; @@ -488,6 +504,10 @@ static void gb_audio_cleanup(struct gbaudio_codec_info *gb) if (ret) dev_info(dev, "%d:Failed during APBridge stop_tx\n", ret); + ret = gb_audio_apbridgea_shutdown_tx(connection, 0); + if (ret) + dev_info(dev, "%d:Failed during APBridge shutdown_tx\n", + ret); cportid = connection->intf_cport_id; ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection, cportid); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 06312031af35..9197adcbc32e 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -207,17 +207,25 @@ extern int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, __u16 i2s_port, __u16 size); extern int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, __u16 i2s_port, __u32 *delay); +extern int gb_audio_apbridgea_prepare_tx(struct gb_connection *connection, + __u16 i2s_port); extern int gb_audio_apbridgea_start_tx(struct gb_connection *connection, __u16 i2s_port, __u64 timestamp); extern int gb_audio_apbridgea_stop_tx(struct gb_connection *connection, __u16 i2s_port); +extern int gb_audio_apbridgea_shutdown_tx(struct gb_connection *connection, + __u16 i2s_port); extern int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, __u16 i2s_port, __u16 size); extern int gb_audio_apbridgea_get_rx_delay(struct gb_connection *connection, __u16 i2s_port, __u32 *delay); +extern int gb_audio_apbridgea_prepare_rx(struct gb_connection *connection, + __u16 i2s_port); extern int gb_audio_apbridgea_start_rx(struct gb_connection *connection, __u16 i2s_port); extern int gb_audio_apbridgea_stop_rx(struct gb_connection *connection, __u16 i2s_port); +extern int gb_audio_apbridgea_shutdown_rx(struct gb_connection *connection, + __u16 i2s_port); #endif /* __LINUX_GBAUDIO_CODEC_H */ -- cgit v1.2.3-59-g8ed1b From 41993cd54fdc5c774cc4881e18a21e3e4a2544cd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 3 Mar 2016 15:29:39 +0100 Subject: greybus: loopback: fix double error count Make sure not count errors during asynchronous tests twice (first in the timeout handler then again in the completion handler) to avoid obviously broken error stats such as: $ loopback_test -i 1000 -t transfer -p -o 200000 -c 64 -x -s 2000 1970-1-1 1:3:35 test: transfer path: gb_loopback0 size: 2000 iterations: 1000 errors: 1998 async: Enabled requests per-sec: min=0, max=0, average=0.310556, jitter=0 ap-throughput B/s: min=0 max=4026 average=1254.647461 jitter=4026 ap-latency usec: min=12803 max=12803 average=12803.000000 jitter=0 apbridge-latency usec: min=89 max=89 average=89.000000 jitter=0 gpbridge-latency usec: min=294 max=294 average=294.000000 jitter=0 where we supposedly have more errors than iterations (operations initiated). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 9014c2b51ae0..ba6e12a3648c 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -516,15 +516,15 @@ static void gb_loopback_async_operation_callback(struct gb_operation *operation) err = true; } - if (err) { - gb->error++; - } else { + if (!err) { gb_loopback_push_latency_ts(gb, &op_async->ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&op_async->ts, &te); } if (op_async->pending) { + if (err) + gb->error++; gb->iteration_count++; op_async->pending = false; del_timer_sync(&op_async->timer); -- cgit v1.2.3-59-g8ed1b From c7733b6167750a42da81133189e9cca33ce7584f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 2 Mar 2016 18:00:51 +0100 Subject: greybus: raw: use hexadecimal notation for request types Use hexadecimal notation for request types in log messages. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index 338139e815af..729d25811568 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -97,7 +97,7 @@ static int gb_raw_request_handler(struct gb_operation *op) u32 len; if (op->type != GB_RAW_TYPE_SEND) { - dev_err(dev, "unknown request type %d\n", op->type); + dev_err(dev, "unknown request type 0x%02x\n", op->type); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From e494b580ea23a98f2f3b3507157c2d573240486c Mon Sep 17 00:00:00 2001 From: Michael Mogenson Date: Thu, 3 Mar 2016 16:23:11 -0500 Subject: greybus: remove gb_i2c_timeout_operation Set timeout operation was removed from the Greybus specification. Remove gb_i2c_timeout_operation and all other no longer necessary code bits from the Greybus kernel code. Signed-off-by: Michael Mogenson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 7 ------- drivers/staging/greybus/i2c.c | 31 ++++------------------------- 2 files changed, 4 insertions(+), 34 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index f28391279555..f1b5cf6d0b87 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -495,23 +495,16 @@ struct gb_hid_input_report_request { /* Greybus i2c request types */ #define GB_I2C_TYPE_FUNCTIONALITY 0x02 -#define GB_I2C_TYPE_TIMEOUT 0x03 #define GB_I2C_TYPE_RETRIES 0x04 #define GB_I2C_TYPE_TRANSFER 0x05 #define GB_I2C_RETRIES_DEFAULT 3 -#define GB_I2C_TIMEOUT_DEFAULT 1000 /* milliseconds */ /* functionality request has no payload */ struct gb_i2c_functionality_response { __le32 functionality; } __packed; -struct gb_i2c_timeout_request { - __le16 msec; -} __packed; -/* timeout response has no payload */ - struct gb_i2c_retries_request { __u8 retries; } __packed; diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index ec74e870d76e..d9d483dc1c85 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -18,7 +18,6 @@ struct gb_i2c_device { struct gb_connection *connection; u32 functionality; - u16 timeout_msec; u8 retries; struct i2c_adapter adapter; @@ -50,23 +49,6 @@ static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) return 0; } -static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec) -{ - struct device *dev = &gb_i2c_dev->connection->bundle->dev; - struct gb_i2c_timeout_request request; - int ret; - - request.msec = cpu_to_le16(msec); - ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_TIMEOUT, - &request, sizeof(request), NULL, 0); - if (ret) - dev_err(dev, "timeout operation failed (%d)\n", ret); - else - gb_i2c_dev->timeout_msec = msec; - - return ret; -} - static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, u8 retries) { @@ -267,8 +249,8 @@ static const struct i2c_algorithm gb_i2c_algorithm = { /* * Do initial setup of the i2c device. This includes verifying we * can support it (based on the protocol version it advertises). - * If that's OK, we get and cached its functionality bits, and - * set up the retry count and timeout. + * If that's OK, we get and cached its functionality bits and + * set up the retry count. * * Note: gb_i2c_dev->connection is assumed to have been valid. */ @@ -281,12 +263,8 @@ static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) if (ret) return ret; - /* Set up our default retry count and timeout */ - ret = gb_i2c_retries_operation(gb_i2c_dev, GB_I2C_RETRIES_DEFAULT); - if (ret) - return ret; - - return gb_i2c_timeout_operation(gb_i2c_dev, GB_I2C_TIMEOUT_DEFAULT); + /* Set up our default retry count */ + return gb_i2c_retries_operation(gb_i2c_dev, GB_I2C_RETRIES_DEFAULT); } static int gb_i2c_connection_init(struct gb_connection *connection) @@ -312,7 +290,6 @@ static int gb_i2c_connection_init(struct gb_connection *connection) adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adapter->algo = &gb_i2c_algorithm; /* adapter->algo_data = what? */ - adapter->timeout = gb_i2c_dev->timeout_msec * HZ / 1000; adapter->retries = gb_i2c_dev->retries; adapter->dev.parent = &connection->bundle->dev; -- cgit v1.2.3-59-g8ed1b From 3522a09fa7df45b49c27997b419a20d9dcff8919 Mon Sep 17 00:00:00 2001 From: Michael Mogenson Date: Thu, 3 Mar 2016 16:23:12 -0500 Subject: greybus: remove gb_i2c_retries_operation Set retries operation was removed from the Greybus specification. Remove gb_i2c_retries_operation and all other no longer necessary code bits from the Greybus kernel code. Signed-off-by: Michael Mogenson Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 8 ------- drivers/staging/greybus/i2c.c | 33 ++--------------------------- 2 files changed, 2 insertions(+), 39 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index f1b5cf6d0b87..b60c7dc0e6dc 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -495,21 +495,13 @@ struct gb_hid_input_report_request { /* Greybus i2c request types */ #define GB_I2C_TYPE_FUNCTIONALITY 0x02 -#define GB_I2C_TYPE_RETRIES 0x04 #define GB_I2C_TYPE_TRANSFER 0x05 -#define GB_I2C_RETRIES_DEFAULT 3 - /* functionality request has no payload */ struct gb_i2c_functionality_response { __le32 functionality; } __packed; -struct gb_i2c_retries_request { - __u8 retries; -} __packed; -/* retries response has no payload */ - /* * Outgoing data immediately follows the op count and ops array. * The data for each write (master -> slave) op in the array is sent diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index d9d483dc1c85..cb68b455f833 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -18,7 +18,6 @@ struct gb_i2c_device { struct gb_connection *connection; u32 functionality; - u8 retries; struct i2c_adapter adapter; }; @@ -49,25 +48,6 @@ static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) return 0; } -static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev, - u8 retries) -{ - struct device *dev = &gb_i2c_dev->connection->bundle->dev; - struct gb_i2c_retries_request request; - int ret; - - request.retries = retries; - ret = gb_operation_sync(gb_i2c_dev->connection, GB_I2C_TYPE_RETRIES, - &request, sizeof(request), NULL, 0); - if (ret) - dev_err(dev, "retries operation failed (%d)\n", ret); - else - gb_i2c_dev->retries = retries; - - return ret; -} - - /* * Map Linux i2c_msg flags into Greybus i2c transfer op flags. */ @@ -249,22 +229,14 @@ static const struct i2c_algorithm gb_i2c_algorithm = { /* * Do initial setup of the i2c device. This includes verifying we * can support it (based on the protocol version it advertises). - * If that's OK, we get and cached its functionality bits and - * set up the retry count. + * If that's OK, we get and cached its functionality bits. * * Note: gb_i2c_dev->connection is assumed to have been valid. */ static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) { - int ret; - /* Assume the functionality never changes, just get it once */ - ret = gb_i2c_functionality_operation(gb_i2c_dev); - if (ret) - return ret; - - /* Set up our default retry count */ - return gb_i2c_retries_operation(gb_i2c_dev, GB_I2C_RETRIES_DEFAULT); + return gb_i2c_functionality_operation(gb_i2c_dev); } static int gb_i2c_connection_init(struct gb_connection *connection) @@ -290,7 +262,6 @@ static int gb_i2c_connection_init(struct gb_connection *connection) adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adapter->algo = &gb_i2c_algorithm; /* adapter->algo_data = what? */ - adapter->retries = gb_i2c_dev->retries; adapter->dev.parent = &connection->bundle->dev; snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); -- cgit v1.2.3-59-g8ed1b From c7b07265046b5db56778dc8c2cfc9056413ec5ba Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 28 Feb 2016 14:42:54 -0800 Subject: greybus: gpbridge.h: move protocol init/exit prototypes Create gpbridge.h for the gpbridge-specific function prototypes, the rest of the greybus drivers don't care about them. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Johan Hovold --- drivers/staging/greybus/gpbridge.c | 2 +- drivers/staging/greybus/gpbridge.h | 34 ++++++++++++++++++++++++++++++++++ drivers/staging/greybus/gpio.c | 1 + drivers/staging/greybus/i2c.c | 1 + drivers/staging/greybus/protocol.h | 26 -------------------------- drivers/staging/greybus/pwm.c | 1 + drivers/staging/greybus/sdio.c | 1 + drivers/staging/greybus/spi.c | 1 + drivers/staging/greybus/uart.c | 1 + drivers/staging/greybus/usb.c | 1 + 10 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 drivers/staging/greybus/gpbridge.h diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 29f0984f7213..c82ded14846d 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -17,7 +17,7 @@ #include #include "greybus.h" - +#include "gpbridge.h" static int __init gpbridge_init(void) { diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h new file mode 100644 index 000000000000..50ee87b8f737 --- /dev/null +++ b/drivers/staging/greybus/gpbridge.h @@ -0,0 +1,34 @@ +/* + * Greybus GPBridge phy driver + * + * Copyright 2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __GPBRIDGE_H +#define __GPBRIDGE_H + +extern int gb_gpio_protocol_init(void); +extern void gb_gpio_protocol_exit(void); + +extern int gb_pwm_protocol_init(void); +extern void gb_pwm_protocol_exit(void); + +extern int gb_uart_protocol_init(void); +extern void gb_uart_protocol_exit(void); + +extern int gb_sdio_protocol_init(void); +extern void gb_sdio_protocol_exit(void); + +extern int gb_usb_protocol_init(void); +extern void gb_usb_protocol_exit(void); + +extern int gb_i2c_protocol_init(void); +extern void gb_i2c_protocol_exit(void); + +extern int gb_spi_protocol_init(void); +extern void gb_spi_protocol_exit(void); + +#endif /* __GPBRIDGE_H */ + diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index e7dd99444ea2..7b2cb5d81e54 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -16,6 +16,7 @@ #include #include "greybus.h" +#include "gpbridge.h" struct gb_gpio_line { /* The following has to be an array of line_max entries */ diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index cb68b455f833..4b96f69318bd 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -13,6 +13,7 @@ #include #include "greybus.h" +#include "gpbridge.h" struct gb_i2c_device { struct gb_connection *connection; diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 26c59efd9ed8..38fe14cf46ce 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -48,32 +48,6 @@ int gb_protocol_get_version(struct gb_connection *connection); void gb_protocol_put(struct gb_protocol *protocol); -/* - * These are defined in their respective protocol source files. - * Declared here for now. They could be added via modules, or maybe - * just use initcalls (which level?). - */ -extern int gb_gpio_protocol_init(void); -extern void gb_gpio_protocol_exit(void); - -extern int gb_pwm_protocol_init(void); -extern void gb_pwm_protocol_exit(void); - -extern int gb_uart_protocol_init(void); -extern void gb_uart_protocol_exit(void); - -extern int gb_sdio_protocol_init(void); -extern void gb_sdio_protocol_exit(void); - -extern int gb_usb_protocol_init(void); -extern void gb_usb_protocol_exit(void); - -extern int gb_i2c_protocol_init(void); -extern void gb_i2c_protocol_exit(void); - -extern int gb_spi_protocol_init(void); -extern void gb_spi_protocol_exit(void); - /* __protocol: Pointer to struct gb_protocol */ #define gb_protocol_driver(__protocol) \ static int __init protocol_init(void) \ diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index c3a3a9dfc6b4..018d5c229070 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -13,6 +13,7 @@ #include #include "greybus.h" +#include "gpbridge.h" struct gb_pwm_chip { struct gb_connection *connection; diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index da9093738d4a..9a20f0e0363d 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -15,6 +15,7 @@ #include #include "greybus.h" +#include "gpbridge.h" struct gb_sdio_host { struct gb_connection *connection; diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 7689af0f63a9..35714533a1ac 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -14,6 +14,7 @@ #include #include "greybus.h" +#include "gpbridge.h" struct gb_spi { struct gb_connection *connection; diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 0685cdcb510d..d169c55b4552 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -29,6 +29,7 @@ #include #include "greybus.h" +#include "gpbridge.h" #define GB_NUM_MINORS 16 /* 16 is is more than enough */ #define GB_NAME "ttyGB" diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 6647868b4960..0cfc00f39b46 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -14,6 +14,7 @@ #include #include "greybus.h" +#include "gpbridge.h" /* Version of the Greybus USB protocol we support */ #define GB_USB_VERSION_MAJOR 0x00 -- cgit v1.2.3-59-g8ed1b From d6fefbe19327f4137360d7ab8af78f1d4d9b909e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 28 Feb 2016 14:13:27 -0800 Subject: greybus: manifest: reserve the Bridged PHY class This reserves the bridged phy class number to be used later on. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Reviewed-by: Johan Hovold --- drivers/staging/greybus/gpbridge.c | 12 ++++++++++++ drivers/staging/greybus/greybus_manifest.h | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index c82ded14846d..9be936cb422f 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -81,4 +81,16 @@ static void __exit gpbridge_exit(void) } module_exit(gpbridge_exit); +/* + * One large list of all classes we support in the gpbridge.ko module. + * + * Due to limitations in older kernels, the different phy .c files can not + * contain their own MODULE_DEVICE_TABLE(), so put them all here for now. + */ +static const struct greybus_bundle_id bridged_phy_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, + { }, +}; +MODULE_DEVICE_TABLE(greybus, bridged_phy_id_table); + MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 76649a649554..b9504c723a75 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -61,7 +61,7 @@ enum greybus_class_type { GREYBUS_CLASS_SDIO = 0x07, GREYBUS_CLASS_POWER_SUPPLY = 0x08, GREYBUS_CLASS_PWM = 0x09, - /* 0x0a is unused */ + GREYBUS_CLASS_BRIDGED_PHY = 0x0a, GREYBUS_CLASS_SPI = 0x0b, GREYBUS_CLASS_DISPLAY = 0x0c, GREYBUS_CLASS_CAMERA = 0x0d, -- cgit v1.2.3-59-g8ed1b From 177d4a4d34bc81e220cc2ef4056df3da25ad6994 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 2 Mar 2016 16:51:08 +0000 Subject: greybus: timesync: Add Control and SVC TimeSync command/response data definitions This patch adds the protocol command/response definitions for the SVC and Control protocols to the greybus_protocols definition header consistent with the greybus-spec. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b60c7dc0e6dc..c0a7863f8e1d 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -170,6 +170,20 @@ struct gb_control_interface_version_response { __le16 minor; } __packed; +#define GB_TIMESYNC_MAX_STROBES 0x04 + +struct gb_control_timesync_enable_request { + __u8 count; + __u64 frame_time; + __u32 strobe_delay; + __u32 refclk; +} __packed; +/* timesync enable response has no payload */ + +struct gb_control_timesync_authoritative_request { + __u64 frame_time[GB_TIMESYNC_MAX_STROBES]; +} __packed; +/* timesync authoritative response has no payload */ /* APBridge protocol */ @@ -900,6 +914,20 @@ struct gb_svc_route_destroy_request { } __packed; /* route destroy response has no payload */ +struct gb_svc_timesync_enable_request { + __u8 count; + __u64 frame_time; + __u32 strobe_delay; + __u32 strobe_mask; + __u32 refclk; +} __packed; +/* timesync enable response has no payload */ + +/* timesync authoritative request has no payload */ +struct gb_svc_timesync_authoritative_response { + __u64 frame_time[GB_TIMESYNC_MAX_STROBES]; +}; + #define GB_SVC_UNIPRO_FAST_MODE 0x01 #define GB_SVC_UNIPRO_SLOW_MODE 0x02 #define GB_SVC_UNIPRO_FAST_AUTO_MODE 0x04 -- cgit v1.2.3-59-g8ed1b From 0bd39ca4898fb554bcba28c82f425cc6c88180b9 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 2 Mar 2016 16:51:09 +0000 Subject: greybus: timesync: Add Control and SVC protocol TimeSync operation definitions This patch adds the protocol command extenions for SVC and Control protocols to the greybus_protocols definition header consistent with the greybus-spec. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 36 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c0a7863f8e1d..8fd91f1b10a1 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -122,6 +122,9 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_GET_MANIFEST 0x04 #define GB_CONTROL_TYPE_CONNECTED 0x05 #define GB_CONTROL_TYPE_DISCONNECTED 0x06 +#define GB_CONTROL_TYPE_TIMESYNC_ENABLE 0x07 +#define GB_CONTROL_TYPE_TIMESYNC_DISABLE 0x08 +#define GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE 0x09 #define GB_CONTROL_TYPE_INTERFACE_VERSION 0x0a #define GB_CONTROL_TYPE_BUNDLE_VERSION 0x0b @@ -785,21 +788,24 @@ struct gb_spi_transfer_response { #define GB_SVC_VERSION_MINOR 0x01 /* Greybus SVC request types */ -#define GB_SVC_TYPE_SVC_HELLO 0x02 -#define GB_SVC_TYPE_INTF_DEVICE_ID 0x03 -#define GB_SVC_TYPE_INTF_HOTPLUG 0x04 -#define GB_SVC_TYPE_INTF_HOT_UNPLUG 0x05 -#define GB_SVC_TYPE_INTF_RESET 0x06 -#define GB_SVC_TYPE_CONN_CREATE 0x07 -#define GB_SVC_TYPE_CONN_DESTROY 0x08 -#define GB_SVC_TYPE_DME_PEER_GET 0x09 -#define GB_SVC_TYPE_DME_PEER_SET 0x0a -#define GB_SVC_TYPE_ROUTE_CREATE 0x0b -#define GB_SVC_TYPE_ROUTE_DESTROY 0x0c -#define GB_SVC_TYPE_INTF_SET_PWRM 0x10 -#define GB_SVC_TYPE_INTF_EJECT 0x11 -#define GB_SVC_TYPE_KEY_EVENT 0x12 -#define GB_SVC_TYPE_PING 0x13 +#define GB_SVC_TYPE_SVC_HELLO 0x02 +#define GB_SVC_TYPE_INTF_DEVICE_ID 0x03 +#define GB_SVC_TYPE_INTF_HOTPLUG 0x04 +#define GB_SVC_TYPE_INTF_HOT_UNPLUG 0x05 +#define GB_SVC_TYPE_INTF_RESET 0x06 +#define GB_SVC_TYPE_CONN_CREATE 0x07 +#define GB_SVC_TYPE_CONN_DESTROY 0x08 +#define GB_SVC_TYPE_DME_PEER_GET 0x09 +#define GB_SVC_TYPE_DME_PEER_SET 0x0a +#define GB_SVC_TYPE_ROUTE_CREATE 0x0b +#define GB_SVC_TYPE_ROUTE_DESTROY 0x0c +#define GB_SVC_TYPE_TIMESYNC_ENABLE 0x0d +#define GB_SVC_TYPE_TIMESYNC_DISABLE 0x0e +#define GB_SVC_TYPE_TIMESYNC_AUTHORITATIVE 0x0f +#define GB_SVC_TYPE_INTF_SET_PWRM 0x10 +#define GB_SVC_TYPE_INTF_EJECT 0x11 +#define GB_SVC_TYPE_KEY_EVENT 0x12 +#define GB_SVC_TYPE_PING 0x13 /* * SVC version request/response has the same payload as -- cgit v1.2.3-59-g8ed1b From 8fd9466b8c324bc87c5db58a46c33731d1ea50bf Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 2 Mar 2016 16:51:10 +0000 Subject: greybus: control: Add TimeSync control commands Simple addition of the TimeSync commands defined in the specification. Note for the case of timesync_authoritative we're passing the request structure directly so as not to have to pass eight parameters into the function. Adds: - control.c::timesync_enable(u8 count, u64 frame_time, u32 strobe_delay, u32 refclk) Informs an Interface to expect count TimeSync strobe pulses with strobe_delay milliseconds delay between each strobe. Once enabled an Interface may not enter a low-power mode which will result in the reference timer used to track time switching off. - control.c::timesync_disable(void) Commands an Interface to immediately halt TimeSync logic. This will allow an Interface to transition into low-power modes where the reference time being used for TimeSync may switch off. - control.c::timesync_authoritative(u64 *frame_time, u8 count) Used by the AP Module to inform an Interface of the authoritative TimeSync clock-master time at each strobe pulse. Down-stream clock slaves shall adjust their local frame-time appropriately based on the diseminated authoritative frame-time. Signed-off-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/control.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/control.h | 5 +++++ 2 files changed, 41 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index ba2754aa513c..bac412ef72ab 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -241,3 +241,39 @@ void gb_control_destroy(struct gb_control *control) gb_connection_destroy(control->connection); kfree(control); } + +int gb_control_timesync_enable(struct gb_control *control, u8 count, + u64 frame_time, u32 strobe_delay, u32 refclk) +{ + struct gb_control_timesync_enable_request request; + + request.count = count; + request.frame_time = cpu_to_le64(frame_time); + request.strobe_delay = cpu_to_le32(strobe_delay); + request.refclk = cpu_to_le32(refclk); + return gb_operation_sync(control->connection, + GB_CONTROL_TYPE_TIMESYNC_ENABLE, &request, + sizeof(request), NULL, 0); +} + +int gb_control_timesync_disable(struct gb_control *control) +{ + return gb_operation_sync(control->connection, + GB_CONTROL_TYPE_TIMESYNC_DISABLE, NULL, 0, + NULL, 0); +} + +int gb_control_timesync_authoritative(struct gb_control *control, + u64 *frame_time, u8 count) +{ + struct gb_control_timesync_authoritative_request request; + int i; + + for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) + request.frame_time[i] = frame_time[i]; + + return gb_operation_sync(control->connection, + GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE, + &request, sizeof(request), + NULL, 0); +} diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index d31e7c6d866a..949f1a3c433b 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -31,5 +31,10 @@ int gb_control_get_manifest_size_operation(struct gb_interface *intf); int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, size_t size); int gb_control_get_interface_version_operation(struct gb_interface *intf); +int gb_control_timesync_enable(struct gb_control *control, u8 count, + u64 frame_time, u32 strobe_delay, u32 refclk); +int gb_control_timesync_disable(struct gb_control *control); +int gb_control_timesync_authoritative(struct gb_control *control, + u64 *frame_time, u8 count); #endif /* __CONTROL_H */ -- cgit v1.2.3-59-g8ed1b From cb033188d8a783be55d03017954b8e7453799a5d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 3 Mar 2016 13:34:36 +0100 Subject: greybus: connection: add connection-flag interface Add interface for associating a flag bitmask with a connection when creating it. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 24 ++++++++++++++++++++---- drivers/staging/greybus/connection.h | 4 ++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 34e26dc6e249..eaceafc4f8dd 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -119,6 +119,7 @@ static void gb_connection_init_name(struct gb_connection *connection) * @bundle: remote-interface bundle (may be NULL) * @cport_id: remote-interface cport id, or 0 for static connections * @handler: request handler (may be NULL) + * @flags: connection flags * * Create a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -137,7 +138,8 @@ static struct gb_connection * _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, struct gb_interface *intf, struct gb_bundle *bundle, int cport_id, - gb_request_handler_t handler) + gb_request_handler_t handler, + unsigned long flags) { struct gb_connection *connection; struct ida *id_map = &hd->cport_id_map; @@ -180,6 +182,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, connection->intf = intf; connection->bundle = bundle; connection->handler = handler; + connection->flags = flags; connection->state = GB_CONNECTION_STATE_DISABLED; atomic_set(&connection->op_cycle, 0); @@ -226,13 +229,14 @@ struct gb_connection * gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id, gb_request_handler_t handler) { - return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, handler); + return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, handler, + 0); } struct gb_connection * gb_connection_create_control(struct gb_interface *intf) { - return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL); + return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL, 0); } struct gb_connection * @@ -242,10 +246,22 @@ gb_connection_create(struct gb_bundle *bundle, u16 cport_id, struct gb_interface *intf = bundle->intf; return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, - handler); + handler, 0); } EXPORT_SYMBOL_GPL(gb_connection_create); +struct gb_connection * +gb_connection_create_flags(struct gb_bundle *bundle, u16 cport_id, + gb_request_handler_t handler, + unsigned long flags) +{ + struct gb_interface *intf = bundle->intf; + + return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, + handler, flags); +} +EXPORT_SYMBOL_GPL(gb_connection_create_flags); + static int gb_connection_hd_cport_enable(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 24b7d6f47671..d5ac1459332a 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -36,6 +36,7 @@ struct gb_connection { struct list_head bundle_links; gb_request_handler_t handler; + unsigned long flags; struct gb_protocol *protocol; u8 module_major; @@ -59,6 +60,9 @@ struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, struct gb_connection *gb_connection_create_control(struct gb_interface *intf); struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, gb_request_handler_t handler); +struct gb_connection * gb_connection_create_flags(struct gb_bundle *bundle, + u16 cport_id, gb_request_handler_t handler, + unsigned long flags); void gb_connection_destroy(struct gb_connection *connection); static inline bool gb_connection_is_static(struct gb_connection *connection) -- cgit v1.2.3-59-g8ed1b From 34145b608d4198d7c89bfeffded6bc71012b6d79 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 3 Mar 2016 13:34:37 +0100 Subject: greybus: svc: clean up CPortFlags handling Clean up CPortFlags handling and explicitly disable CSD when E2EFC is enabled (CSD_n is ignored when E2EFC is set). Note that the bootrom requires E2EFC, CSD, and CSV to all be disabled. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index eeb251e28441..65c6d8ee1c51 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -291,21 +291,14 @@ int gb_svc_connection_create(struct gb_svc *svc, request.cport1_id = cpu_to_le16(cport1_id); request.intf2_id = intf2_id; request.cport2_id = cpu_to_le16(cport2_id); - /* - * XXX: fix connections paramaters to TC0 and all CPort flags - * for now. - */ - request.tc = 0; + request.tc = 0; /* TC0 */ - /* - * We need to skip setting E2EFC and other flags to the connection - * create request, for all cports, on an interface that need to boot - * over unipro, i.e. interfaces required to download firmware. - */ + /* The ES2/ES3 bootrom requires E2EFC, CSD and CSV to be disabled. */ + request.flags = CPORT_FLAGS_CSV_N; if (boot_over_unipro) - request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_CSD_N; + request.flags |= CPORT_FLAGS_CSD_N; else - request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC; + request.flags |= CPORT_FLAGS_CSD_N | CPORT_FLAGS_E2EFC; return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); -- cgit v1.2.3-59-g8ed1b From 27f25c17ad7fa2b24a24a4e617077dec20a026ce Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 3 Mar 2016 13:34:38 +0100 Subject: greybus: connection: generalise CPortFlags handling Generalise the svc connection-create helper to accept a cport-flags argument and handle the flags in the connection code instead. Note that the camera driver currently manages its data connection directly. We keep E2EFC enabled for now even though it will soon need to be disabled due to some pending firmware updates. Reviewed-by: Viresh Kumar Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 6 +++++- drivers/staging/greybus/connection.c | 13 ++++++++++++- drivers/staging/greybus/svc.c | 14 ++------------ drivers/staging/greybus/svc.h | 6 +++++- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 444e218e15ab..3ca585f08b85 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -817,6 +817,7 @@ static int gb_camera_connection_init(struct gb_connection *connection) { struct gb_svc *svc = connection->hd->svc; struct gb_camera *gcam; + u8 cport_flags; int ret; gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); @@ -830,9 +831,12 @@ static int gb_camera_connection_init(struct gb_connection *connection) * Create the data connection between camera module CDSI0 and APB CDS1. * The CPort IDs are hardcoded by the ES2 bridges. */ + /* FIXME: remove E2EFC */ + cport_flags = GB_SVC_CPORT_FLAG_E2EFC | GB_SVC_CPORT_FLAG_CSD_N | + GB_SVC_CPORT_FLAG_CSV_N; ret = gb_svc_connection_create(svc, connection->intf->interface_id, ES2_APB_CDSI0_CPORT, svc->ap_intf_id, - ES2_APB_CDSI1_CPORT, false); + ES2_APB_CDSI1_CPORT, cport_flags); if (ret < 0) goto error; diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index eaceafc4f8dd..01d31f66b28e 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -327,18 +327,29 @@ gb_connection_svc_connection_create(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; struct gb_interface *intf; + u8 cport_flags; int ret; if (gb_connection_is_static(connection)) return gb_connection_hd_fct_flow_enable(connection); intf = connection->intf; + + /* The ES2/ES3 bootrom requires E2EFC, CSD and CSV to be disabled. */ + cport_flags = GB_SVC_CPORT_FLAG_CSV_N; + if (intf->boot_over_unipro) { + cport_flags |= GB_SVC_CPORT_FLAG_CSD_N; + } else { + cport_flags |= GB_SVC_CPORT_FLAG_CSD_N | + GB_SVC_CPORT_FLAG_E2EFC; + } + ret = gb_svc_connection_create(hd->svc, hd->svc->ap_intf_id, connection->hd_cport_id, intf->interface_id, connection->intf_cport_id, - intf->boot_over_unipro); + cport_flags); if (ret) { dev_err(&connection->hd->dev, "%s: failed to create svc connection: %d\n", diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 65c6d8ee1c51..69c37d4ca30c 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -12,10 +12,6 @@ #include "greybus.h" -#define CPORT_FLAGS_E2EFC BIT(0) -#define CPORT_FLAGS_CSD_N BIT(1) -#define CPORT_FLAGS_CSV_N BIT(2) - #define SVC_KEY_ARA_BUTTON KEY_A struct gb_svc_deferred_request { @@ -283,7 +279,7 @@ static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf) int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id, - bool boot_over_unipro) + u8 cport_flags) { struct gb_svc_conn_create_request request; @@ -292,13 +288,7 @@ int gb_svc_connection_create(struct gb_svc *svc, request.intf2_id = intf2_id; request.cport2_id = cpu_to_le16(cport2_id); request.tc = 0; /* TC0 */ - - /* The ES2/ES3 bootrom requires E2EFC, CSD and CSV to be disabled. */ - request.flags = CPORT_FLAGS_CSV_N; - if (boot_over_unipro) - request.flags |= CPORT_FLAGS_CSD_N; - else - request.flags |= CPORT_FLAGS_CSD_N | CPORT_FLAGS_E2EFC; + request.flags = cport_flags; return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index b9fb93ea56be..0436f49ef8fa 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -10,6 +10,10 @@ #ifndef __SVC_H #define __SVC_H +#define GB_SVC_CPORT_FLAG_E2EFC BIT(0) +#define GB_SVC_CPORT_FLAG_CSD_N BIT(1) +#define GB_SVC_CPORT_FLAG_CSV_N BIT(2) + enum gb_svc_state { GB_SVC_STATE_RESET, GB_SVC_STATE_PROTOCOL_VERSION, @@ -46,7 +50,7 @@ void gb_svc_put(struct gb_svc *svc); int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id, bool boot_over_unipro); + u8 intf2_id, u16 cport2_id, u8 cport_flags); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id); -- cgit v1.2.3-59-g8ed1b From 64a6d1388432704c9205fee04be8e6df45fc563b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 3 Mar 2016 13:34:39 +0100 Subject: greybus: connection: add CSD connection flag Add CSD connection flag that can be specified when allocating a connection to enable Controlled Segment Dropping in favour of E2EFC which is enabled by default. Note that most connections are expected to have E2EFC enabled. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/connection.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 01d31f66b28e..96a8aa3285ff 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -339,7 +339,7 @@ gb_connection_svc_connection_create(struct gb_connection *connection) cport_flags = GB_SVC_CPORT_FLAG_CSV_N; if (intf->boot_over_unipro) { cport_flags |= GB_SVC_CPORT_FLAG_CSD_N; - } else { + } else if (gb_connection_e2efc_enabled(connection)) { cport_flags |= GB_SVC_CPORT_FLAG_CSD_N | GB_SVC_CPORT_FLAG_E2EFC; } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index d5ac1459332a..6197d45cb16a 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -13,6 +13,8 @@ #include #include +#define GB_CONNECTION_FLAG_CSD BIT(0) + enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, GB_CONNECTION_STATE_DISABLED = 1, @@ -81,4 +83,9 @@ void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, void gb_connection_latency_tag_enable(struct gb_connection *connection); void gb_connection_latency_tag_disable(struct gb_connection *connection); +static inline bool gb_connection_e2efc_enabled(struct gb_connection *connection) +{ + return !(connection->flags & GB_CONNECTION_FLAG_CSD); +} + #endif /* __CONNECTION_H */ -- cgit v1.2.3-59-g8ed1b From aa2a5459aa167814f847afb05c262c1f29dc9c6a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 3 Mar 2016 13:34:40 +0100 Subject: greybus: hd: rename CPort-features callbacks Rename the CPort-features callbacks, that are not just used to enable FCT flow, to the more descriptive cport_features_enable/disable. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 22 ++++++++++++---------- drivers/staging/greybus/es2.c | 16 ++++++++-------- drivers/staging/greybus/greybus_protocols.h | 6 +++--- drivers/staging/greybus/hd.h | 4 ++-- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 96a8aa3285ff..64fa20a06da0 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -290,17 +290,18 @@ static void gb_connection_hd_cport_disable(struct gb_connection *connection) hd->driver->cport_disable(hd, connection->hd_cport_id); } -static int gb_connection_hd_fct_flow_enable(struct gb_connection *connection) +static int +gb_connection_hd_cport_features_enable(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; int ret; - if (!hd->driver->fct_flow_enable) + if (!hd->driver->cport_features_enable) return 0; - ret = hd->driver->fct_flow_enable(hd, connection->hd_cport_id); + ret = hd->driver->cport_features_enable(hd, connection->hd_cport_id); if (ret) { - dev_err(&hd->dev, "%s: failed to enable FCT flow: %d\n", + dev_err(&hd->dev, "%s: failed to enable CPort features: %d\n", connection->name, ret); return ret; } @@ -308,14 +309,15 @@ static int gb_connection_hd_fct_flow_enable(struct gb_connection *connection) return 0; } -static void gb_connection_hd_fct_flow_disable(struct gb_connection *connection) +static void +gb_connection_hd_cport_features_disable(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; - if (!hd->driver->fct_flow_disable) + if (!hd->driver->cport_features_disable) return; - hd->driver->fct_flow_disable(hd, connection->hd_cport_id); + hd->driver->cport_features_disable(hd, connection->hd_cport_id); } /* @@ -331,7 +333,7 @@ gb_connection_svc_connection_create(struct gb_connection *connection) int ret; if (gb_connection_is_static(connection)) - return gb_connection_hd_fct_flow_enable(connection); + return gb_connection_hd_cport_features_enable(connection); intf = connection->intf; @@ -357,7 +359,7 @@ gb_connection_svc_connection_create(struct gb_connection *connection) return ret; } - ret = gb_connection_hd_fct_flow_enable(connection); + ret = gb_connection_hd_cport_features_enable(connection); if (ret) { gb_svc_connection_destroy(hd->svc, hd->svc->ap_intf_id, connection->hd_cport_id, @@ -372,7 +374,7 @@ gb_connection_svc_connection_create(struct gb_connection *connection) static void gb_connection_svc_connection_destroy(struct gb_connection *connection) { - gb_connection_hd_fct_flow_disable(connection); + gb_connection_hd_cport_features_disable(connection); if (gb_connection_is_static(connection)) return; diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index e920563351ff..239adf7a3749 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -574,37 +574,37 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) return retval; } -static int fct_flow_enable(struct gb_host_device *hd, u16 cport_id) +static int cport_features_enable(struct gb_host_device *hd, u16 cport_id) { int retval; struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - GB_APB_REQUEST_FCT_FLOW_EN, + GB_APB_REQUEST_CPORT_FEAT_EN, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, 0, ES2_TIMEOUT); if (retval < 0) - dev_err(&udev->dev, "Cannot enable FCT flow for cport %u: %d\n", + dev_err(&udev->dev, "Cannot enable CPort features for cport %u: %d\n", cport_id, retval); return retval; } -static int fct_flow_disable(struct gb_host_device *hd, u16 cport_id) +static int cport_features_disable(struct gb_host_device *hd, u16 cport_id) { int retval; struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - GB_APB_REQUEST_FCT_FLOW_DIS, + GB_APB_REQUEST_CPORT_FEAT_DIS, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, 0, ES2_TIMEOUT); if (retval < 0) dev_err(&udev->dev, - "Cannot disable FCT flow for cport %u: %d\n", + "Cannot disable CPort features for cport %u: %d\n", cport_id, retval); return retval; } @@ -617,8 +617,8 @@ static struct gb_hd_driver es2_driver = { .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, .output = output, - .fct_flow_enable = fct_flow_enable, - .fct_flow_disable = fct_flow_disable, + .cport_features_enable = cport_features_enable, + .cport_features_disable = cport_features_disable, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 8fd91f1b10a1..27acbdd39571 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -212,9 +212,9 @@ struct gb_control_timesync_authoritative_request { /* request to control the CSI transmitter */ #define GB_APB_REQUEST_AUDIO_CONTROL 0x09 -/* vendor requests to enable/disable FCT tokens flow */ -#define GB_APB_REQUEST_FCT_FLOW_EN 0x0b -#define GB_APB_REQUEST_FCT_FLOW_DIS 0x0c +/* vendor requests to enable/disable CPort features */ +#define GB_APB_REQUEST_CPORT_FEAT_EN 0x0b +#define GB_APB_REQUEST_CPORT_FEAT_DIS 0x0c /* Firmware Protocol */ diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index eaddfc9befd6..fe6c086a8e3d 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -25,8 +25,8 @@ struct gb_hd_driver { int (*latency_tag_disable)(struct gb_host_device *hd, u16 cport_id); int (*output)(struct gb_host_device *hd, void *req, u16 size, u8 cmd, bool async); - int (*fct_flow_enable)(struct gb_host_device *hd, u16 cport_id); - int (*fct_flow_disable)(struct gb_host_device *hd, u16 cport_id); + int (*cport_features_enable)(struct gb_host_device *hd, u16 cport_id); + int (*cport_features_disable)(struct gb_host_device *hd, u16 cport_id); }; struct gb_host_device { -- cgit v1.2.3-59-g8ed1b From 53f965065a5eb067621e4734fee9719942976a86 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Fri, 4 Mar 2016 17:32:20 +0100 Subject: greybus: camera: disable E2EFC on CSI connection Following Toshiba's recommendation we shouldn't use E2EFC on a CSI connection. Disable E2EFC on the CSI connection. Signed-off-by: Fabien Parent Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 3ca585f08b85..d499ffdb786b 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -831,9 +831,7 @@ static int gb_camera_connection_init(struct gb_connection *connection) * Create the data connection between camera module CDSI0 and APB CDS1. * The CPort IDs are hardcoded by the ES2 bridges. */ - /* FIXME: remove E2EFC */ - cport_flags = GB_SVC_CPORT_FLAG_E2EFC | GB_SVC_CPORT_FLAG_CSD_N | - GB_SVC_CPORT_FLAG_CSV_N; + cport_flags = GB_SVC_CPORT_FLAG_CSD_N | GB_SVC_CPORT_FLAG_CSV_N; ret = gb_svc_connection_create(svc, connection->intf->interface_id, ES2_APB_CDSI0_CPORT, svc->ap_intf_id, ES2_APB_CDSI1_CPORT, cport_flags); -- cgit v1.2.3-59-g8ed1b From 9fa3a9b8cb7e04b600166049424d01b428a251ad Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 4 Mar 2016 18:40:02 -0800 Subject: greybus: properly annotate struct gb_control_timesync_enable_request A patch from created struct gb_control_timesync_enable_request, but forgot to properly annotate that the fields are little-endian. The code is correct in treating them this way, so there isn't a bug, but sparse complains. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 27acbdd39571..524c64987b42 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -177,9 +177,9 @@ struct gb_control_interface_version_response { struct gb_control_timesync_enable_request { __u8 count; - __u64 frame_time; - __u32 strobe_delay; - __u32 refclk; + __le64 frame_time; + __le32 strobe_delay; + __le32 refclk; } __packed; /* timesync enable response has no payload */ -- cgit v1.2.3-59-g8ed1b From 7fe9301422444a1fca71bb8c4c0673fb44eb0f2d Mon Sep 17 00:00:00 2001 From: David Lin Date: Mon, 7 Mar 2016 21:52:54 -0800 Subject: greybus: arche-platform: fix incorrect gpio variable type GPIO number obtained from of_get_named_gpio() should be signed to allow error handling. Testing Done: Built & booted on EVT1.5 Signed-off-by: David Lin Reviewed-by: Vaibhav Hiremath Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/arche-apb-ctrl.c | 2 +- drivers/staging/greybus/arche-platform.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 55806bdf5898..3a092a5b7fb8 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -37,7 +37,7 @@ struct arche_apb_ctrl_drvdata { struct regulator *vcore; struct regulator *vio; - unsigned int clk_en_gpio; + int clk_en_gpio; struct clk *clk; struct pinctrl *pinctrl; diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 64dd8a1ebe37..3293661d876a 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -44,7 +44,7 @@ struct arche_platform_drvdata { enum arche_platform_state state; - unsigned int svc_refclk_req; + int svc_refclk_req; struct clk *svc_ref_clk; struct pinctrl *pinctrl; -- cgit v1.2.3-59-g8ed1b From 4b82dd7be3d3ea62c634bda6bd01666912bc7e79 Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Tue, 8 Mar 2016 10:03:40 +0100 Subject: greybus: loopback_test: Use timeout argument Patch "c3b0a32 Loopback_test: use poll instead of inotify" added a optional argument for the user to specify a timeout value, but did not use this parameter in the actual poll function. The default of 30 seconds is always used. Fix this by actually using the the poll_timeout parameter so the user can run long tests. Signed-off-by: Axel Haslam Reviewed-by: Patrick Titiano Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/tools/loopback_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index 0abe62390b1b..ab40bcf8ca0a 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -714,7 +714,7 @@ static int wait_for_complete(struct loopback_test *t) int i; while (1) { - ret = poll(t->fds, t->poll_count, DEFAULT_POLL_TIMEOUT_SEC * 1000); + ret = poll(t->fds, t->poll_count, t->poll_timeout * 1000); if (ret == 0) { fprintf(stderr, "Poll timmed out!\n"); return -1; -- cgit v1.2.3-59-g8ed1b From 9230e298cf49761abbf5893125fc20ad286cff5b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 8 Mar 2016 16:50:43 +0100 Subject: greybus: greybus_manifest: remove unused AP class and protocol Mark the AP Bundle-class and protocol ids as unused. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Reviewed-by: Jeffrey Carlyle Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index b9504c723a75..86ebc0abe601 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -23,7 +23,7 @@ enum greybus_descriptor_type { enum greybus_protocol { GREYBUS_PROTOCOL_CONTROL = 0x00, - GREYBUS_PROTOCOL_AP = 0x01, + /* 0x01 is unused */ GREYBUS_PROTOCOL_GPIO = 0x02, GREYBUS_PROTOCOL_I2C = 0x03, GREYBUS_PROTOCOL_UART = 0x04, @@ -52,7 +52,7 @@ enum greybus_protocol { enum greybus_class_type { GREYBUS_CLASS_CONTROL = 0x00, - GREYBUS_CLASS_AP = 0x01, + /* 0x01 is unused */ GREYBUS_CLASS_GPIO = 0x02, GREYBUS_CLASS_I2C = 0x03, GREYBUS_CLASS_UART = 0x04, -- cgit v1.2.3-59-g8ed1b From a23aa56452a8c705c7da62e9eda4c2fafd837829 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 16:13:55 +0100 Subject: greybus: greybus_manifest: remove unused SVC class Mark the SVC Bundle-class id as unused. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_manifest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 86ebc0abe601..12b6e74ee345 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -71,7 +71,7 @@ enum greybus_class_type { GREYBUS_CLASS_LOOPBACK = 0x11, GREYBUS_CLASS_AUDIO = 0x12, /* 0x13 is unused */ - GREYBUS_CLASS_SVC = 0x14, + /* 0x14 is unused */ GREYBUS_CLASS_FIRMWARE = 0x15, /* ... */ GREYBUS_CLASS_RAW = 0xfe, -- cgit v1.2.3-59-g8ed1b From 89ec14ceaedf887fb666f09e1b768afdc32cb291 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Tue, 8 Mar 2016 16:37:36 +0100 Subject: greybus: loopback: Fix warning on 32-bit build gb_loopback_ro_avg_attr() is using "/" to divide two 64-bit integer, causing a reference to __aeabi_uldivmod() that is not availalbe on 32-bit. Instead, use do_div(). Signed-off-by: Alexandre Bailon Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ba6e12a3648c..75bff56b48c3 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -164,7 +164,8 @@ static ssize_t name##_avg_show(struct device *dev, \ count = stats->count ? stats->count : 1; \ avg = stats->sum; \ rem = do_div(avg, count); \ - rem = 1000000 * rem / count; \ + rem *= 1000000; \ + do_div(rem, count); \ return sprintf(buf, "%llu.%06u\n", avg, (u32)rem); \ } \ static DEVICE_ATTR_RO(name##_avg) -- cgit v1.2.3-59-g8ed1b From 58a527afff2b27bc912dbb7111d3f23d4f28105b Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Tue, 8 Mar 2016 16:37:37 +0100 Subject: greybus: loopback: round closest the sixth decimal The original round was removed becaused it was rounding the integer whereas we had decimals. Round the sixth decimal. Signed-off-by: Alexandre Bailon Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 75bff56b48c3..9f1cd9d6cd54 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -162,7 +162,7 @@ static ssize_t name##_avg_show(struct device *dev, \ gb = dev_get_drvdata(dev); \ stats = &gb->name; \ count = stats->count ? stats->count : 1; \ - avg = stats->sum; \ + avg = stats->sum + count / 2000000; /* round closest */ \ rem = do_div(avg, count); \ rem *= 1000000; \ do_div(rem, count); \ -- cgit v1.2.3-59-g8ed1b From 01480ba336982d49e2208636e38c0a80b732eba8 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Tue, 8 Mar 2016 17:40:16 +0100 Subject: greybus: loopback: Fix broken synchonous test loopback driver use the send_count variable to know the test progress. The test may be stopped or change but this variable is never cleaned. Such situation may break the next run. Signed-off-by: Alexandre Bailon Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 9f1cd9d6cd54..6c62706bb94c 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -107,6 +107,8 @@ struct gb_loopback { u64 elapsed_nsecs; u32 apbridge_latency_ts; u32 gpbridge_latency_ts; + + u32 send_count; }; static struct class loopback_class = { @@ -247,6 +249,7 @@ static void gb_loopback_check_attr(struct gb_loopback *gb) gb->requests_timedout = 0; gb->requests_completed = 0; gb->iteration_count = 0; + gb->send_count = 0; gb->error = 0; if (kfifo_depth < gb->iteration_max) { @@ -971,7 +974,7 @@ static int gb_loopback_fn(void *data) int us_wait = 0; int type; u32 size; - u32 send_count = 0; + struct gb_loopback *gb = data; while (1) { @@ -989,10 +992,10 @@ static int gb_loopback_fn(void *data) mutex_lock(&gb->mutex); /* Optionally terminate */ - if (send_count == gb->iteration_max) { + if (gb->send_count == gb->iteration_max) { if (gb->iteration_count == gb->iteration_max) { gb->type = 0; - send_count = 0; + gb->send_count = 0; sysfs_notify(&gb->dev->kobj, NULL, "iteration_count"); } @@ -1032,7 +1035,7 @@ static int gb_loopback_fn(void *data) gb->iteration_count++; gb_loopback_calculate_stats(gb, !!error); } - send_count++; + gb->send_count++; if (us_wait) udelay(us_wait); } -- cgit v1.2.3-59-g8ed1b From 83a124e2c57afee205284f8d5263252a4b8bbfac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:31 +0100 Subject: greybus: interface: remove unused function prototype Remove unused gb_interface_destroy() prototype. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 4168a5752808..6a198801523b 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -61,7 +61,6 @@ struct gb_interface *gb_interface_find(struct gb_host_device *hd, struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id); -void gb_interface_destroy(struct gb_interface *intf); int gb_interface_init(struct gb_interface *intf, u8 device_id); void gb_interface_remove(struct gb_interface *intf); void gb_interfaces_remove(struct gb_host_device *hd); -- cgit v1.2.3-59-g8ed1b From 52bbd5b3a04816cdd65605c53dd033d3d987cfee Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:32 +0100 Subject: greybus: interface: remove unused drvdata helpers Remove the unused interface drvdata helpers along with some dubious comments about public and private definitions. Greybus drivers bind to bundles and should be using the greybus_set_drvdata and greybus_get_drvdata helpers. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 6a198801523b..ebc51fc5c364 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -10,7 +10,6 @@ #ifndef __INTERFACE_H #define __INTERFACE_H -/* Greybus "public" definitions" */ struct gb_interface { struct device dev; struct gb_control *control; @@ -43,19 +42,6 @@ struct gb_interface { }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) -static inline void gb_interface_set_drvdata(struct gb_interface *intf, - void *data) -{ - dev_set_drvdata(&intf->dev, data); -} - -static inline void *gb_interface_get_drvdata(struct gb_interface *intf) -{ - return dev_get_drvdata(&intf->dev); -} - -/* Greybus "private" definitions */ - struct gb_interface *gb_interface_find(struct gb_host_device *hd, u8 interface_id); -- cgit v1.2.3-59-g8ed1b From 60269b958ea127a46be9caf0deff931384c4dd2f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:33 +0100 Subject: greybus: interface: add comment about early control-connection disable Add comment about why the control connection is disabled early when the interface is already gone. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 2f3966f319bc..5c5c4221e531 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -147,6 +147,10 @@ void gb_interface_remove(struct gb_interface *intf) struct gb_bundle *bundle; struct gb_bundle *next; + /* + * Disable the control-connection early to avoid operation timeouts + * when the interface is already gone. + */ if (intf->disconnected) gb_control_disable(intf->control); -- cgit v1.2.3-59-g8ed1b From bb2533a9742f19bfe6b87b64d54e846674e467d1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:34 +0100 Subject: greybus: svc: refactor interface-route creation Add interface-route-create helper to allocate an interface device id and setup the route. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 4 +- drivers/staging/greybus/interface.h | 2 +- drivers/staging/greybus/svc.c | 106 +++++++++++++++++++++--------------- 3 files changed, 64 insertions(+), 48 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 5c5c4221e531..63dd2854ea70 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -186,14 +186,12 @@ void gb_interfaces_remove(struct gb_host_device *hd) * Finally initialize all the bundles to set routes via SVC and initialize all * connections. */ -int gb_interface_init(struct gb_interface *intf, u8 device_id) +int gb_interface_init(struct gb_interface *intf) { struct gb_bundle *bundle, *tmp; int ret, size; void *manifest; - intf->device_id = device_id; - /* Establish control connection */ ret = gb_control_enable(intf->control); if (ret) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index ebc51fc5c364..8d48cfa378e1 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -47,7 +47,7 @@ struct gb_interface *gb_interface_find(struct gb_host_device *hd, struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id); -int gb_interface_init(struct gb_interface *intf, u8 device_id); +int gb_interface_init(struct gb_interface *intf); void gb_interface_remove(struct gb_interface *intf); void gb_interfaces_remove(struct gb_host_device *hd); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 69c37d4ca30c..4ed108b124f8 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -463,6 +463,61 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } +static int gb_svc_interface_route_create(struct gb_svc *svc, + struct gb_interface *intf) +{ + u8 intf_id = intf->interface_id; + u8 device_id; + int ret; + + /* + * Create a device id for the interface: + * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC + * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP + * + * XXX Do we need to allocate device ID for SVC or the AP here? And what + * XXX about an AP with multiple interface blocks? + */ + ret = ida_simple_get(&svc->device_id_map, + GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); + if (ret < 0) { + dev_err(&svc->dev, "failed to allocate device id for interface %u: %d\n", + intf_id, ret); + return ret; + } + device_id = ret; + + ret = gb_svc_intf_device_id(svc, intf_id, device_id); + if (ret) { + dev_err(&svc->dev, "failed to set device id %u for interface %u: %d\n", + device_id, intf_id, ret); + goto err_ida_remove; + } + + /* Create a two-way route between the AP and the new interface. */ + ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_DEVICE_ID_AP, + intf_id, device_id); + if (ret) { + dev_err(&svc->dev, "failed to create route to interface %u (device id %u): %d\n", + intf_id, device_id, ret); + goto err_svc_id_free; + } + + intf->device_id = device_id; + + return 0; + +err_svc_id_free: + /* + * XXX Should we tell SVC that this id doesn't belong to interface + * XXX anymore. + */ +err_ida_remove: + ida_simple_remove(&svc->device_id_map, device_id); + + return ret; +} + static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) { u8 intf_id = intf->interface_id; @@ -487,7 +542,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) struct gb_svc *svc = connection->private; struct gb_host_device *hd = connection->hd; struct gb_interface *intf; - u8 intf_id, device_id; + u8 intf_id; u32 vendor_id = 0; u32 product_id = 0; int ret; @@ -561,45 +616,14 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) goto destroy_interface; } - /* - * Create a device id for the interface: - * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC - * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP - * - * XXX Do we need to allocate device ID for SVC or the AP here? And what - * XXX about an AP with multiple interface blocks? - */ - device_id = ida_simple_get(&svc->device_id_map, - GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); - if (device_id < 0) { - ret = device_id; - dev_err(&svc->dev, "failed to allocate device id for interface %u: %d\n", - intf_id, ret); + ret = gb_svc_interface_route_create(svc, intf); + if (ret) goto destroy_interface; - } - - ret = gb_svc_intf_device_id(svc, intf_id, device_id); - if (ret) { - dev_err(&svc->dev, "failed to set device id %u for interface %u: %d\n", - device_id, intf_id, ret); - goto ida_put; - } - /* - * Create a two-way route between the AP and the new interface - */ - ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_DEVICE_ID_AP, - intf_id, device_id); + ret = gb_interface_init(intf); if (ret) { - dev_err(&svc->dev, "failed to create route to interface %u (device id %u): %d\n", - intf_id, device_id, ret); - goto svc_id_free; - } - - ret = gb_interface_init(intf, device_id); - if (ret) { - dev_err(&svc->dev, "failed to initialize interface %u (device id %u): %d\n", - intf_id, device_id, ret); + dev_err(&svc->dev, "failed to initialize interface %u: %d\n", + intf_id, ret); goto destroy_route; } @@ -607,13 +631,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) destroy_route: gb_svc_route_destroy(svc, svc->ap_intf_id, intf_id); -svc_id_free: - /* - * XXX Should we tell SVC that this id doesn't belong to interface - * XXX anymore. - */ -ida_put: - ida_simple_remove(&svc->device_id_map, device_id); + ida_simple_remove(&svc->device_id_map, intf->device_id); destroy_interface: gb_interface_remove(intf); } -- cgit v1.2.3-59-g8ed1b From 39495a2bea4d523c4d36f28f3739caa8d3a404c4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:35 +0100 Subject: greybus: svc: refactor interface-route destruction Add interface-route-destroy helper to tear down the route and release the interface device id. Note that we currently need to grab a reference to the interface to prevent it from being deallocated before tearing down the route. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 4ed108b124f8..efa418a1916c 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -518,21 +518,23 @@ err_ida_remove: return ret; } -static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) +static void gb_svc_interface_route_destroy(struct gb_svc *svc, + struct gb_interface *intf) { - u8 intf_id = intf->interface_id; - u8 device_id = intf->device_id; + gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id); + ida_simple_remove(&svc->device_id_map, intf->device_id); +} +static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) +{ intf->disconnected = true; - gb_interface_remove(intf); + get_device(&intf->dev); - /* - * Destroy the two-way route between the AP and the interface. - */ - gb_svc_route_destroy(svc, svc->ap_intf_id, intf_id); + gb_interface_remove(intf); + gb_svc_interface_route_destroy(svc, intf); - ida_simple_remove(&svc->device_id_map, device_id); + put_device(&intf->dev); } static void gb_svc_process_intf_hotplug(struct gb_operation *operation) @@ -630,8 +632,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) return; destroy_route: - gb_svc_route_destroy(svc, svc->ap_intf_id, intf_id); - ida_simple_remove(&svc->device_id_map, intf->device_id); + gb_svc_interface_route_destroy(svc, intf); destroy_interface: gb_interface_remove(intf); } -- cgit v1.2.3-59-g8ed1b From 7a137fb290df63f24242fc4996decd0b339ba3b1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:36 +0100 Subject: greybus: interface: separate initialisation and registration Separate interface initialisation from registration of the interface and its bundles. This is a step towards registering also interfaces that failed to initialise (e.g. a dummy interface). Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 31 +++++++++++++++++-------------- drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/svc.c | 4 ++++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 63dd2854ea70..a32e564851d8 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -179,16 +179,12 @@ void gb_interfaces_remove(struct gb_host_device *hd) gb_interface_remove(intf); } -/** - * gb_interface_init - * - * Create connection for control CPort and then request/parse manifest. - * Finally initialize all the bundles to set routes via SVC and initialize all - * connections. +/* + * Intialise an interface by enabling the control connection and fetching the + * manifest and other information over it. */ int gb_interface_init(struct gb_interface *intf) { - struct gb_bundle *bundle, *tmp; int ret, size; void *manifest; @@ -236,11 +232,22 @@ int gb_interface_init(struct gb_interface *intf) if (ret) goto free_manifest; - /* Register the interface and its bundles. */ +free_manifest: + kfree(manifest); + + return ret; +} + +/* Register an interface and its bundles. */ +int gb_interface_add(struct gb_interface *intf) +{ + struct gb_bundle *bundle, *tmp; + int ret; + ret = device_add(&intf->dev); if (ret) { dev_err(&intf->dev, "failed to register interface: %d\n", ret); - goto free_manifest; + return ret; } dev_info(&intf->dev, "Interface added: VID=0x%08x, PID=0x%08x\n", @@ -256,9 +263,5 @@ int gb_interface_init(struct gb_interface *intf) } } - ret = 0; - -free_manifest: - kfree(manifest); - return ret; + return 0; } diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 8d48cfa378e1..5238804e236b 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -48,6 +48,7 @@ struct gb_interface *gb_interface_find(struct gb_host_device *hd, struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id); int gb_interface_init(struct gb_interface *intf); +int gb_interface_add(struct gb_interface *intf); void gb_interface_remove(struct gb_interface *intf); void gb_interfaces_remove(struct gb_host_device *hd); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index efa418a1916c..0a6c0393e20e 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -629,6 +629,10 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) goto destroy_route; } + ret = gb_interface_add(intf); + if (ret) + goto destroy_route; + return; destroy_route: -- cgit v1.2.3-59-g8ed1b From a77660a75a3f79c71064dc6848a2975731ca5c00 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:37 +0100 Subject: greybus: interface: free bundles on initialisation errors Immediately free any created bundle structures on interface initialisation errors. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index a32e564851d8..fe4efe1bbdd5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -185,6 +185,7 @@ void gb_interfaces_remove(struct gb_host_device *hd) */ int gb_interface_init(struct gb_interface *intf) { + struct gb_bundle *bundle, *tmp; int ret, size; void *manifest; @@ -211,7 +212,7 @@ int gb_interface_init(struct gb_interface *intf) ret = gb_control_get_manifest_operation(intf, manifest, size); if (ret) { dev_err(&intf->dev, "failed to get manifest: %d\n", ret); - goto free_manifest; + goto err_free_manifest; } /* @@ -221,18 +222,25 @@ int gb_interface_init(struct gb_interface *intf) if (!gb_manifest_parse(intf, manifest, size)) { dev_err(&intf->dev, "failed to parse manifest\n"); ret = -EINVAL; - goto free_manifest; + goto err_destroy_bundles; } ret = gb_control_get_interface_version_operation(intf); if (ret) - goto free_manifest; + goto err_destroy_bundles; ret = gb_control_get_bundle_versions(intf->control); if (ret) - goto free_manifest; + goto err_destroy_bundles; + + kfree(manifest); -free_manifest: + return 0; + +err_destroy_bundles: + list_for_each_entry_safe(bundle, tmp, &intf->bundles, links) + gb_bundle_destroy(bundle); +err_free_manifest: kfree(manifest); return ret; -- cgit v1.2.3-59-g8ed1b From 986d6911083150a15b723b33f9aaaf5744b84a84 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:38 +0100 Subject: greybus: svc: always register interfaces at hotplug Always register interfaces at hotplug regardless of whether initialisation succeeded or not. Even if a module failed to initialise we want it to have a representation while it is physically present. Note that the vendor and product-string attribute will read as "(null)" for now on an interface that failed (early) initialisation. Also note that the switch route is kept until the interface is finally removed also on initialisation errors. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 0a6c0393e20e..572ed0e23fe7 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -521,8 +521,12 @@ err_ida_remove: static void gb_svc_interface_route_destroy(struct gb_svc *svc, struct gb_interface *intf) { + if (intf->device_id == GB_DEVICE_ID_BAD) + return; + gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id); ida_simple_remove(&svc->device_id_map, intf->device_id); + intf->device_id = GB_DEVICE_ID_BAD; } static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) @@ -615,30 +619,22 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) if (ret) { dev_err(&svc->dev, "failed to clear boot status of interface %u: %d\n", intf_id, ret); - goto destroy_interface; + goto out_interface_add; } ret = gb_svc_interface_route_create(svc, intf); if (ret) - goto destroy_interface; + goto out_interface_add; ret = gb_interface_init(intf); if (ret) { dev_err(&svc->dev, "failed to initialize interface %u: %d\n", intf_id, ret); - goto destroy_route; + goto out_interface_add; } - ret = gb_interface_add(intf); - if (ret) - goto destroy_route; - - return; - -destroy_route: - gb_svc_interface_route_destroy(svc, intf); -destroy_interface: - gb_interface_remove(intf); +out_interface_add: + gb_interface_add(intf); } static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) -- cgit v1.2.3-59-g8ed1b From 11548c8327000302cfaaf7bcac02a3ccf14c2db1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:39 +0100 Subject: greybus: interface: disable control connection on initialisation errors Disable the control connection immediately on any errors during interface initialisation as there's no need to keep it around for an interface in an error state. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index fe4efe1bbdd5..2654fa8a1f0b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -198,15 +198,20 @@ int gb_interface_init(struct gb_interface *intf) size = gb_control_get_manifest_size_operation(intf); if (size <= 0) { dev_err(&intf->dev, "failed to get manifest size: %d\n", size); + if (size) - return size; + ret = size; else - return -EINVAL; + ret = -EINVAL; + + goto err_disable_control; } manifest = kmalloc(size, GFP_KERNEL); - if (!manifest) - return -ENOMEM; + if (!manifest) { + ret = -ENOMEM; + goto err_disable_control; + } /* Get manifest using control protocol on CPort */ ret = gb_control_get_manifest_operation(intf, manifest, size); @@ -242,6 +247,8 @@ err_destroy_bundles: gb_bundle_destroy(bundle); err_free_manifest: kfree(manifest); +err_disable_control: + gb_control_disable(intf->control); return ret; } -- cgit v1.2.3-59-g8ed1b From 64acb6611f3c17251347870e73ddc106fed807cb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:40 +0100 Subject: greybus: interface: remove useless spinlock Remove useless global interface spinlock that appeared to protect the host-device interface list, but really did not as we are doing lock-less look-ups by relying on the single-threaded SVC workqueue. Document the locking assumptions. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 2654fa8a1f0b..4671f4092a17 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -54,9 +54,6 @@ static struct attribute *interface_attrs[] = { ATTRIBUTE_GROUPS(interface); -/* XXX This could be per-host device */ -static DEFINE_SPINLOCK(gb_interfaces_lock); - // FIXME, odds are you don't want to call this function, rework the caller to // not need it please. struct gb_interface *gb_interface_find(struct gb_host_device *hd, @@ -100,6 +97,9 @@ struct device_type greybus_interface_type = { * * Returns a pointer to the new interfce or a null pointer if a * failure occurs due to memory exhaustion. + * + * Locking: Caller ensures serialisation with gb_interface_remove and + * gb_interface_find. */ struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id) @@ -132,9 +132,7 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, return NULL; } - spin_lock_irq(&gb_interfaces_lock); list_add(&intf->links, &hd->interfaces); - spin_unlock_irq(&gb_interfaces_lock); return intf; } @@ -164,9 +162,7 @@ void gb_interface_remove(struct gb_interface *intf) gb_control_disable(intf->control); - spin_lock_irq(&gb_interfaces_lock); list_del(&intf->links); - spin_unlock_irq(&gb_interfaces_lock); put_device(&intf->dev); } -- cgit v1.2.3-59-g8ed1b From 66d674cfafb2be4002265e6081a1580d0ea461f7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:41 +0100 Subject: greybus: interface: move interface-removal helper Move helper to remove all interfaces of a host-device to the svc code and call it when removing the svc device as this needs to be coordinated with flushing the SVC work queue. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Reviewed-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hd.c | 1 - drivers/staging/greybus/interface.c | 8 -------- drivers/staging/greybus/interface.h | 1 - drivers/staging/greybus/svc.c | 10 ++++++++++ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 147a92d14cfc..2c13860e4a05 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -138,7 +138,6 @@ void gb_hd_del(struct gb_host_device *hd) * removing the remaining interfaces. */ gb_svc_del(hd->svc); - gb_interfaces_remove(hd); device_del(&hd->dev); } diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 4671f4092a17..ebf008161120 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -167,14 +167,6 @@ void gb_interface_remove(struct gb_interface *intf) put_device(&intf->dev); } -void gb_interfaces_remove(struct gb_host_device *hd) -{ - struct gb_interface *intf, *temp; - - list_for_each_entry_safe(intf, temp, &hd->interfaces, links) - gb_interface_remove(intf); -} - /* * Intialise an interface by enabling the control connection and fetching the * manifest and other information over it. diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 5238804e236b..7fc7d29257d9 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -50,6 +50,5 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, int gb_interface_init(struct gb_interface *intf); int gb_interface_add(struct gb_interface *intf); void gb_interface_remove(struct gb_interface *intf); -void gb_interfaces_remove(struct gb_host_device *hd); #endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 572ed0e23fe7..446cb25b49c0 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -997,6 +997,14 @@ int gb_svc_add(struct gb_svc *svc) return 0; } +static void gb_svc_remove_interfaces(struct gb_svc *svc) +{ + struct gb_interface *intf, *tmp; + + list_for_each_entry_safe(intf, tmp, &svc->hd->interfaces, links) + gb_interface_remove(intf); +} + void gb_svc_del(struct gb_svc *svc) { gb_connection_disable(svc->connection); @@ -1012,6 +1020,8 @@ void gb_svc_del(struct gb_svc *svc) } flush_workqueue(svc->wq); + + gb_svc_remove_interfaces(svc); } void gb_svc_put(struct gb_svc *svc) -- cgit v1.2.3-59-g8ed1b From 35580af0bd620056506ed51fff024fc4ade66f67 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:42 +0100 Subject: greybus: interface: rename initialisation function Rename the interface-initialisation function gb_interface_enable(), which is more descriptive. Signed-off-by: Johan Hovold Reviewed-by: Viresh Kumar Reviewed-by: Jeffrey Carlyle Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 4 ++-- drivers/staging/greybus/interface.h | 2 +- drivers/staging/greybus/svc.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index ebf008161120..072e7c5a72e6 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -168,10 +168,10 @@ void gb_interface_remove(struct gb_interface *intf) } /* - * Intialise an interface by enabling the control connection and fetching the + * Enable an interface by enabling its control connection and fetching the * manifest and other information over it. */ -int gb_interface_init(struct gb_interface *intf) +int gb_interface_enable(struct gb_interface *intf) { struct gb_bundle *bundle, *tmp; int ret, size; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 7fc7d29257d9..4b69e9ee9070 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -47,7 +47,7 @@ struct gb_interface *gb_interface_find(struct gb_host_device *hd, struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id); -int gb_interface_init(struct gb_interface *intf); +int gb_interface_enable(struct gb_interface *intf); int gb_interface_add(struct gb_interface *intf); void gb_interface_remove(struct gb_interface *intf); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 446cb25b49c0..631a76965b53 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -626,9 +626,9 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) if (ret) goto out_interface_add; - ret = gb_interface_init(intf); + ret = gb_interface_enable(intf); if (ret) { - dev_err(&svc->dev, "failed to initialize interface %u: %d\n", + dev_err(&svc->dev, "failed to enable interface %u: %d\n", intf_id, ret); goto out_interface_add; } -- cgit v1.2.3-59-g8ed1b From 629c0d007de962bb4edca7b959cebebb29746e90 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:43 +0100 Subject: greybus: interface: separate disabling from removal Separate interface disable from interface removal. Disabling an interface means tearing down its control connection and destroying (i.e. deregistering and releasing) its bundles, while removing it means deregistering and releasing the interface itself. This is needed to implement controlled module removal, where the module interfaces are disabled before being physically ejected. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 62 +++++++++++++++++++------------------ drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/svc.c | 11 +++---- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 072e7c5a72e6..44226c4c48ec 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -137,36 +137,6 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, return intf; } -/* - * Tear down a previously set up interface. - */ -void gb_interface_remove(struct gb_interface *intf) -{ - struct gb_bundle *bundle; - struct gb_bundle *next; - - /* - * Disable the control-connection early to avoid operation timeouts - * when the interface is already gone. - */ - if (intf->disconnected) - gb_control_disable(intf->control); - - list_for_each_entry_safe(bundle, next, &intf->bundles, links) - gb_bundle_destroy(bundle); - - if (device_is_registered(&intf->dev)) { - device_del(&intf->dev); - dev_info(&intf->dev, "Interface removed\n"); - } - - gb_control_disable(intf->control); - - list_del(&intf->links); - - put_device(&intf->dev); -} - /* * Enable an interface by enabling its control connection and fetching the * manifest and other information over it. @@ -241,6 +211,25 @@ err_disable_control: return ret; } +/* Disable an interface and destroy its bundles. */ +void gb_interface_disable(struct gb_interface *intf) +{ + struct gb_bundle *bundle; + struct gb_bundle *next; + + /* + * Disable the control-connection early to avoid operation timeouts + * when the interface is already gone. + */ + if (intf->disconnected) + gb_control_disable(intf->control); + + list_for_each_entry_safe(bundle, next, &intf->bundles, links) + gb_bundle_destroy(bundle); + + gb_control_disable(intf->control); +} + /* Register an interface and its bundles. */ int gb_interface_add(struct gb_interface *intf) { @@ -268,3 +257,16 @@ int gb_interface_add(struct gb_interface *intf) return 0; } + +/* Deregister an interface and drop its reference. */ +void gb_interface_remove(struct gb_interface *intf) +{ + if (device_is_registered(&intf->dev)) { + device_del(&intf->dev); + dev_info(&intf->dev, "Interface removed\n"); + } + + list_del(&intf->links); + + put_device(&intf->dev); +} diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 4b69e9ee9070..d4c55abae258 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -48,6 +48,7 @@ struct gb_interface *gb_interface_find(struct gb_host_device *hd, struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id); int gb_interface_enable(struct gb_interface *intf); +void gb_interface_disable(struct gb_interface *intf); int gb_interface_add(struct gb_interface *intf); void gb_interface_remove(struct gb_interface *intf); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 631a76965b53..480a0712018d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -533,12 +533,9 @@ static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) { intf->disconnected = true; - get_device(&intf->dev); - - gb_interface_remove(intf); + gb_interface_disable(intf); gb_svc_interface_route_destroy(svc, intf); - - put_device(&intf->dev); + gb_interface_remove(intf); } static void gb_svc_process_intf_hotplug(struct gb_operation *operation) @@ -1001,8 +998,10 @@ static void gb_svc_remove_interfaces(struct gb_svc *svc) { struct gb_interface *intf, *tmp; - list_for_each_entry_safe(intf, tmp, &svc->hd->interfaces, links) + list_for_each_entry_safe(intf, tmp, &svc->hd->interfaces, links) { + gb_interface_disable(intf); gb_interface_remove(intf); + } } void gb_svc_del(struct gb_svc *svc) -- cgit v1.2.3-59-g8ed1b From c80514a39a407caa5565450063ac1af42381636a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:44 +0100 Subject: greybus: svc: unexport eject helper Do no export the interface-eject helper, which is only supposed to be used by core. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 480a0712018d..71b38794a95c 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -143,7 +143,6 @@ int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) sizeof(request), NULL, 0, GB_SVC_EJECT_TIME); } -EXPORT_SYMBOL_GPL(gb_svc_intf_eject); int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value) -- cgit v1.2.3-59-g8ed1b From e676ccd7134ae9e73d2f82446cef011a12f30e9b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:45 +0100 Subject: greybus: svc: print an error message on failed eject attempts Print an error message when the SVC fails to eject an interface. Signed-off-by: Johan Hovold Reviewed-by: Jeffrey Carlyle Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 71b38794a95c..b9ef77097d36 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -131,6 +131,7 @@ EXPORT_SYMBOL_GPL(gb_svc_intf_reset); int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) { struct gb_svc_intf_eject_request request; + int ret; request.intf_id = intf_id; @@ -138,10 +139,16 @@ int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) * The pulse width for module release in svc is long so we need to * increase the timeout so the operation will not return to soon. */ - return gb_operation_sync_timeout(svc->connection, - GB_SVC_TYPE_INTF_EJECT, &request, - sizeof(request), NULL, 0, - GB_SVC_EJECT_TIME); + ret = gb_operation_sync_timeout(svc->connection, + GB_SVC_TYPE_INTF_EJECT, &request, + sizeof(request), NULL, 0, + GB_SVC_EJECT_TIME); + if (ret) { + dev_err(&svc->dev, "failed to eject interface %u\n", intf_id); + return ret; + } + + return 0; } int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, -- cgit v1.2.3-59-g8ed1b From d18da86b51c135100e297a9833bcdc586e68a84b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 9 Mar 2016 12:20:46 +0100 Subject: greybus: greybus_protocols: remove svc-eject timeout define The SVC eject timeout is implementation specific and does not belong in the protocol header so move it to the svc module. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 1 - drivers/staging/greybus/svc.c | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 524c64987b42..9c628cc5f8e6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -847,7 +847,6 @@ struct gb_svc_intf_reset_request { } __packed; /* interface reset response has no payload */ -#define GB_SVC_EJECT_TIME 9000 struct gb_svc_intf_eject_request { __u8 intf_id; } __packed; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b9ef77097d36..ae1911ce24ef 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -14,6 +14,8 @@ #define SVC_KEY_ARA_BUTTON KEY_A +#define SVC_INTF_EJECT_TIMEOUT 9000 + struct gb_svc_deferred_request { struct work_struct work; struct gb_operation *operation; @@ -142,7 +144,7 @@ int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) ret = gb_operation_sync_timeout(svc->connection, GB_SVC_TYPE_INTF_EJECT, &request, sizeof(request), NULL, 0, - GB_SVC_EJECT_TIME); + SVC_INTF_EJECT_TIMEOUT); if (ret) { dev_err(&svc->dev, "failed to eject interface %u\n", intf_id); return ret; -- cgit v1.2.3-59-g8ed1b From 1dc8d3d7c568d9a80fa20570a764da57677f1050 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon Date: Fri, 11 Mar 2016 17:23:37 +0100 Subject: greybus: loopback: Fix broken loopback min values Currently, when a loopback test completely fail, loopback will return 4294967295 for every min value. Return 0 instead of 4294967295 in such case. Signed-off-by: Alexandre Bailon Reviewed-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 6c62706bb94c..9b732a866455 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -148,6 +148,9 @@ static ssize_t name##_##field##_show(struct device *dev, \ char *buf) \ { \ struct gb_loopback *gb = dev_get_drvdata(dev); \ + /* Report 0 for min and max if no transfer successed */ \ + if (!gb->requests_completed) \ + return sprintf(buf, "0\n"); \ return sprintf(buf, "%"#type"\n", gb->name.field); \ } \ static DEVICE_ATTR_RO(name##_##field) -- cgit v1.2.3-59-g8ed1b From fece9c87cb80aceec7dd0205a77c37ff7ba831fe Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 16 Mar 2016 11:29:59 +0000 Subject: greybus: Ensure gb->mutex is held when adding timer Currently in loopback on the async path we issue an operation and then add a timer to time-out that operation should it fail to complete. Looking at a backtrace given in its feasible op_async->pending can be true and del_timer() can run before add_timer() has run. In the callback handler we already hold gb->mutex. This patch fixes that potential race by ensuring we hold gb->mutex both when we are adding and when we are removing the relevant timer. Signed-off-by: Bryan O'Donoghue Reported-and-tested-by: Axel Haslam Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/loopback.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 9b732a866455..5e009e1955ab 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -626,6 +626,7 @@ static int gb_loopback_async_operation(struct gb_loopback *gb, int type, do_gettimeofday(&op_async->ts); op_async->pending = true; atomic_inc(&gb->outstanding_operations); + mutex_lock(&gb->mutex); ret = gb_operation_request_send(operation, gb_loopback_async_operation_callback, GFP_KERNEL); @@ -637,9 +638,11 @@ static int gb_loopback_async_operation(struct gb_loopback *gb, int type, op_async->timer.data = (unsigned long)operation->id; add_timer(&op_async->timer); - return ret; + goto done; error: gb_loopback_async_operation_put(op_async); +done: + mutex_unlock(&gb->mutex); return ret; } -- cgit v1.2.3-59-g8ed1b From 3b90040de82a43ee0538d36b32c9fa4cbbab59c6 Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Fri, 11 Mar 2016 13:19:30 +0100 Subject: greybus: loopback_test: handle SIGINT signal Adding a default timeout may not be representative of every usecase for gb_loopback. Also, tests may continue to run on the driver in case of a timeout. To avoid adding a default timeout, handle SIGINT so that when the user presses ctrl-c the test are stoped. The user can still specify a timeout value with the -O option. Signed-off-by: Axel Haslam Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/tools/loopback_test.c | 53 ++++++++++++++++++++------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index ab40bcf8ca0a..d8ff1bc0878a 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -17,13 +17,13 @@ #include #include #include +#include #define MAX_NUM_DEVICES 10 #define MAX_SYSFS_PATH 0x200 #define CSV_MAX_LINE 0x1000 #define SYSFS_MAX_INT 0x20 #define MAX_STR_LEN 255 -#define DEFAULT_POLL_TIMEOUT_SEC 30 #define DEFAULT_ASYNC_TIMEOUT 200000 struct dict { @@ -88,7 +88,6 @@ struct loopback_test { int list_devices; int use_async; int async_timeout; - int poll_timeout; int async_outstanding_operations; int us_wait; int file_output; @@ -96,6 +95,7 @@ struct loopback_test { char test_name[MAX_STR_LEN]; char sysfs_prefix[MAX_SYSFS_PATH]; char debugfs_prefix[MAX_SYSFS_PATH]; + struct timespec poll_timeout; struct loopback_device devices[MAX_NUM_DEVICES]; struct loopback_results aggregate_results; struct pollfd fds[MAX_NUM_DEVICES]; @@ -706,22 +706,51 @@ static int is_complete(struct loopback_test *t) return 1; } +static void stop_tests(struct loopback_test *t) +{ + int i; + + for (i = 0; i < t->device_count; i++) { + if (!device_enabled(t, i)) + continue; + write_sysfs_val(t->devices[i].sysfs_entry, "type", 0); + } +} + +static void handler(int sig) { /* do nothing */ } + static int wait_for_complete(struct loopback_test *t) { int number_of_events = 0; char dummy; int ret; int i; + struct timespec *ts = NULL; + struct sigaction sa; + sigset_t mask_old, mask; + + sigemptyset(&mask); + sigemptyset(&mask_old); + sigaddset(&mask, SIGINT); + sigprocmask(SIG_BLOCK, &mask, &mask_old); + + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGINT, &sa, NULL) == -1) { + fprintf(stderr, "sigaction error\n"); + return -1; + } + + if (t->poll_timeout.tv_sec != 0) + ts = &t->poll_timeout; while (1) { - ret = poll(t->fds, t->poll_count, t->poll_timeout * 1000); - if (ret == 0) { - fprintf(stderr, "Poll timmed out!\n"); - return -1; - } - if (ret < 0) { - fprintf(stderr, "Poll Error!\n"); + ret = ppoll(t->fds, t->poll_count, ts, &mask_old); + if (ret <= 0) { + stop_tests(t); + fprintf(stderr, "Poll exit with errno %d\n", errno); return -1; } @@ -861,6 +890,7 @@ static int sanity_check(struct loopback_test *t) return 0; } + int main(int argc, char *argv[]) { int o, ret; @@ -915,7 +945,7 @@ int main(int argc, char *argv[]) t.async_timeout = atoi(optarg); break; case 'O': - t.poll_timeout = atoi(optarg); + t.poll_timeout.tv_sec = atoi(optarg); break; case 'c': t.async_outstanding_operations = atoi(optarg); @@ -955,9 +985,6 @@ int main(int argc, char *argv[]) if (t.async_timeout == 0) t.async_timeout = DEFAULT_ASYNC_TIMEOUT; - if (t.poll_timeout == 0) - t.poll_timeout = DEFAULT_POLL_TIMEOUT_SEC; - loopback_run(&t); return 0; -- cgit v1.2.3-59-g8ed1b From f8811c7630912617d1bc0e0b18e3221fec26f9cf Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Tue, 15 Mar 2016 12:28:38 -0700 Subject: greybus: power_supply: reverse version check for new psy API Reversing the kernel version check for new power supply APIs will easily allow us to use older kernels with backported power supply APIs by defining "CORE_OWNS_PSY_STRUCT" in power supply core header Testing Done: - Build tested with arche kernel with backported power supply APIs - Build tested also with current arche kernel to make sure we build with 3.10 kernels Signed-off-by: Sandeep Patil Reviewed-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/kernel_ver.h | 4 ++-- drivers/staging/greybus/power_supply.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 18bf8dff0f86..f5e62ed3d9f5 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -17,9 +17,9 @@ #include #include -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) /* Commit: 297d716 power_supply: Change ownership from driver to core */ -#define DRIVER_OWNS_PSY_STRUCT +#define CORE_OWNS_PSY_STRUCT #endif #ifndef __ATTR_WO diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 0467537bf4f6..ec8fb1b84024 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -26,7 +26,7 @@ struct gb_power_supply_prop { struct gb_power_supply { u8 id; bool registered; -#ifdef DRIVER_OWNS_PSY_STRUCT +#ifndef CORE_OWNS_PSY_STRUCT struct power_supply psy; #define to_gb_power_supply(x) container_of(x, struct gb_power_supply, psy) #else @@ -126,7 +126,7 @@ static void next_interval(struct gb_power_supply *gbpsy) gbpsy->update_interval = update_interval_max; } -#ifdef DRIVER_OWNS_PSY_STRUCT +#ifndef CORE_OWNS_PSY_STRUCT static void __gb_power_supply_changed(struct gb_power_supply *gbpsy) { power_supply_changed(&gbpsy->psy); @@ -513,7 +513,7 @@ static int property_is_writeable(struct power_supply *b, } -#ifdef DRIVER_OWNS_PSY_STRUCT +#ifndef CORE_OWNS_PSY_STRUCT static int gb_power_supply_register(struct gb_power_supply *gbpsy) { struct gb_connection *connection = get_conn_from_psy(gbpsy); @@ -569,7 +569,7 @@ static void _gb_power_supply_release(struct gb_power_supply *gbpsy) gbpsy->update_interval = 0; cancel_delayed_work_sync(&gbpsy->work); -#ifdef DRIVER_OWNS_PSY_STRUCT +#ifndef CORE_OWNS_PSY_STRUCT if (gbpsy->registered) power_supply_unregister(&gbpsy->psy); #else -- cgit v1.2.3-59-g8ed1b From 48b15a9b119d945933bfed6204d51e071d35328b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 17 Mar 2016 11:02:02 +0100 Subject: greybus: camera: Implement the capabilities operation The operation queries the camera module for its capabilities. The debugfs interface just prints a hex dump of the binary message. Signed-off-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Reviewed-by: Gjorgji Rosikopulos Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index d499ffdb786b..6042c681b8b6 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -156,6 +156,33 @@ static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) return 0; } +static int gb_camera_capabilities(struct gb_camera *gcam, + u8 *capabilities, size_t *size) +{ + struct gb_operation *op; + int ret; + + op = gb_operation_create_flags(gcam->connection, + GB_CAMERA_TYPE_CAPABILITIES, 0, *size, + GB_OPERATION_FLAG_SHORT_RESPONSE, + GFP_KERNEL); + if (!op) + return -ENOMEM; + + ret = gb_operation_request_send_sync(op); + if (ret) { + gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret); + goto done; + } + + memcpy(capabilities, op->response->payload, op->response->payload_size); + *size = op->response->payload_size; + +done: + gb_operation_put(op); + return ret; +} + struct ap_csi_config_request { __u8 csi_id; __u8 flags; @@ -478,10 +505,41 @@ static int gb_camera_register_intf_ops(struct gb_camera *gcam) /* ----------------------------------------------------------------------------- * DebugFS */ + static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, char *buf, size_t len) { - return len; + struct gb_camera_debugfs_buffer *buffer = + &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES]; + size_t size = 1024; + unsigned int i; + u8 *caps; + int ret; + + caps = kmalloc(size, GFP_KERNEL); + if (!caps) + return -ENOMEM; + + ret = gb_camera_capabilities(gcam, caps, &size); + if (ret < 0) + goto done; + + /* + * hex_dump_to_buffer() doesn't return the number of bytes dumped prior + * to v4.0, we need our own implementation :-( + */ + buffer->length = 0; + + for (i = 0; i < size; i += 16) { + unsigned int nbytes = min_t(unsigned int, size - i, 16); + + buffer->length += sprintf(buffer->data + buffer->length, + "%*ph\n", nbytes, caps + i); + } + +done: + kfree(caps); + return ret; } static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, -- cgit v1.2.3-59-g8ed1b From a883b0eb434c6ad0237b559b1b5c8cee89ef6350 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 17 Mar 2016 11:02:03 +0100 Subject: greybus: camera: Register capabilities operation Register the greybus camera driver capabilities operation to the ara_camera subdevice Signed-off-by: Jacopo Mondi Reviewed-by: Gjorgji Rosikopulos Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 6042c681b8b6..e862659a5ccc 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -421,6 +421,19 @@ static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt) return mbus_to_gbus_format[0].mbus_code; } +static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len) +{ + struct gb_camera *gcam = priv; + size_t capabilities_len = len; + int ret; + + ret = gb_camera_capabilities(gcam, data, &capabilities_len); + if (ret) + return ret; + + return capabilities_len; +} + static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, unsigned int *flags, struct gb_camera_stream *streams) { @@ -492,6 +505,7 @@ static int gb_camera_op_flush(void *priv, u32 *request_id) } struct gb_camera_ops gb_cam_ops = { + .capabilities = gb_camera_op_capabilities, .configure_streams = gb_camera_op_configure_streams, .capture = gb_camera_op_capture, .flush = gb_camera_op_flush, -- cgit v1.2.3-59-g8ed1b From c3d77f71308e38ef98909c317c57d906f4d51cb9 Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos Date: Mon, 14 Mar 2016 18:44:53 +0200 Subject: greybus: camera: Improve module registration mechanism Registering more then one module at same time was not possible with previous implementation. Also unregistering of the module was missing leading to many instability issues when camera module is ejected when camera is still active. Signed-off-by: Gjorgji Rosikopulos Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 25 +++++++++++++++++-------- drivers/staging/greybus/gb-camera.h | 15 ++++++++++++--- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index e862659a5ccc..722f2b4fe54d 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -38,6 +38,7 @@ struct gb_camera_debugfs_buffer { * @connection: the greybus connection for camera control * @data_connected: whether the data connection has been established * @debugfs: debugfs entries for camera protocol operations testing + * @module: Greybus camera module registered to HOST processor. */ struct gb_camera { struct gb_connection *connection; @@ -47,6 +48,8 @@ struct gb_camera { struct dentry *root; struct gb_camera_debugfs_buffer *buffers; } debugfs; + + struct gb_camera_module module; }; struct gb_camera_stream_config { @@ -504,16 +507,20 @@ static int gb_camera_op_flush(void *priv, u32 *request_id) return gb_camera_flush(priv, request_id); } -struct gb_camera_ops gb_cam_ops = { - .capabilities = gb_camera_op_capabilities, - .configure_streams = gb_camera_op_configure_streams, - .capture = gb_camera_op_capture, - .flush = gb_camera_op_flush, -}; - static int gb_camera_register_intf_ops(struct gb_camera *gcam) { - return gb_camera_register(&gb_cam_ops, gcam); + gcam->module.priv = gcam; + gcam->module.ops.capabilities = gb_camera_op_capabilities; + gcam->module.ops.configure_streams = gb_camera_op_configure_streams; + gcam->module.ops.capture = gb_camera_op_capture; + gcam->module.ops.flush = gb_camera_op_flush; + + return gb_camera_register(&gcam->module); +} + +static int gb_camera_unregister_intf_ops(struct gb_camera *gcam) +{ + return gb_camera_unregister(&gcam->module); } /* ----------------------------------------------------------------------------- @@ -931,6 +938,8 @@ static void gb_camera_connection_exit(struct gb_connection *connection) { struct gb_camera *gcam = connection->private; + gb_camera_unregister_intf_ops(gcam); + gb_camera_cleanup(gcam); } diff --git a/drivers/staging/greybus/gb-camera.h b/drivers/staging/greybus/gb-camera.h index 50af0573737b..0a48a16b675e 100644 --- a/drivers/staging/greybus/gb-camera.h +++ b/drivers/staging/greybus/gb-camera.h @@ -34,9 +34,18 @@ struct gb_camera_ops { int (*flush)(void *priv, u32 *request_id); }; -#define gb_camera_call(f, p, op, args...) \ - (((f)->op) ? (f)->op(p, ##args) : -ENOIOCTLCMD) +struct gb_camera_module { + void *priv; + struct gb_camera_ops ops; -int gb_camera_register(struct gb_camera_ops *ops, void *priv); + struct list_head list; /* Global list */ +}; + +#define gb_camera_call(f, op, args...) \ + ((!(f) ? -ENODEV : ((f)->ops.op) ? \ + (f)->ops.op((f)->priv, ##args) : -ENOIOCTLCMD)) + +int gb_camera_register(struct gb_camera_module *module); +int gb_camera_unregister(struct gb_camera_module *module); #endif /* __GB_CAMERA_H */ -- cgit v1.2.3-59-g8ed1b From 309520ec93f7190c0c3d22d613825a0835a8600f Mon Sep 17 00:00:00 2001 From: Mark Greer Date: Thu, 17 Mar 2016 10:32:37 -0700 Subject: greybus: audio: Use CSD instead of E2EFC for audio data connections There is no reason to use end-to-end flow control for Greybus audio data connections so disable it and enable Controlled Segment Dropping (CSD). Testing Done: Played music using audio modules on an EVT1.5. CC: Vaibhav Agarwal CC: Johan Hovold Signed-off-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index d7cae772dbf5..025dd53507b7 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -694,8 +694,10 @@ static int gb_audio_add_data_connection(struct gbaudio_codec_info *gbcodec, return -ENOMEM; } - connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), - gbaudio_dai_request_handler); + connection = gb_connection_create_flags(bundle, + le16_to_cpu(cport_desc->id), + gbaudio_dai_request_handler, + GB_CONNECTION_FLAG_CSD); if (IS_ERR(connection)) { devm_kfree(gbcodec->dev, dai); return PTR_ERR(connection); -- cgit v1.2.3-59-g8ed1b From 30b442b385c199b3f8e1df26fd6f0c07b61b32a2 Mon Sep 17 00:00:00 2001 From: David Lin Date: Fri, 18 Mar 2016 18:30:50 -0700 Subject: greybus: greybus_trace: Fix broken greybus ftrace Enabling greybus ftrace event causes null pointer access due to that gb_message to SVC has no Bundle. Fix it by handling this in the trace header. Testing Done: $ echo 1 > /d/tracing/event/greybus/enable [002] ...1 54.504426: gb_message_send: greybus:1-svc op=0023 if_id=0 hd_id=0 l=0 [002] ...1 54.504461: gb_host_device_send: greybus:greybus1 if_id=0 l=8 Signed-off-by: David Lin Reviewed-by: Bryan O'Donoghue Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_trace.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 96c515113b98..6f3e10164e3c 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -17,6 +17,11 @@ struct gb_message; struct gb_host_device; +#define gb_bundle_name(message) \ + (message->operation->connection->bundle ? \ + dev_name(&message->operation->connection->bundle->dev) : \ + dev_name(&message->operation->connection->hd->svc->dev)) + DECLARE_EVENT_CLASS(gb_message, TP_PROTO(struct gb_message *message), @@ -24,7 +29,7 @@ DECLARE_EVENT_CLASS(gb_message, TP_ARGS(message), TP_STRUCT__entry( - __string(name, dev_name(&message->operation->connection->bundle->dev)) + __string(name, gb_bundle_name(message)) __field(u16, op_id) __field(u16, intf_cport_id) __field(u16, hd_cport_id) @@ -32,7 +37,7 @@ DECLARE_EVENT_CLASS(gb_message, ), TP_fast_assign( - __assign_str(name, dev_name(&message->operation->connection->bundle->dev)) + __assign_str(name, gb_bundle_name(message)) __entry->op_id = message->operation->id; __entry->intf_cport_id = message->operation->connection->intf_cport_id; -- cgit v1.2.3-59-g8ed1b From 418f3dab841f85e24beb6e30858cafce5cf1f87c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Mar 2016 14:30:16 -0400 Subject: greybus: connection: add functions to get/set private data Add gb_connection_get_data() and gb_connection_set_data() to get and set the private data of a connection, instead of "open coding" it everywhere. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 6197d45cb16a..443d27bc3e35 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -88,4 +88,15 @@ static inline bool gb_connection_e2efc_enabled(struct gb_connection *connection) return !(connection->flags & GB_CONNECTION_FLAG_CSD); } +static inline void *gb_connection_get_data(struct gb_connection *connection) +{ + return connection->private; +} + +static inline void gb_connection_set_data(struct gb_connection *connection, + void *data) +{ + connection->private = data; +} + #endif /* __CONNECTION_H */ -- cgit v1.2.3-59-g8ed1b From 0ec306324423444d3ee0222708ef9de7f5586b93 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Mar 2016 14:30:35 -0400 Subject: greybus: convert drivers to use connection->private set/get This converts all drivers to use the gb_connection_get_data() and gb_connection_set_data() functions to make it a bit more explicit as to what is going on. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 4 ++-- drivers/staging/greybus/camera.c | 6 +++--- drivers/staging/greybus/control.c | 2 +- drivers/staging/greybus/firmware.c | 12 +++++------- drivers/staging/greybus/gpio.c | 6 +++--- drivers/staging/greybus/hid.c | 4 ++-- drivers/staging/greybus/i2c.c | 4 ++-- drivers/staging/greybus/light.c | 4 ++-- drivers/staging/greybus/power_supply.c | 4 ++-- drivers/staging/greybus/pwm.c | 5 ++--- drivers/staging/greybus/sdio.c | 13 ++++++------- drivers/staging/greybus/spi.c | 6 +++--- drivers/staging/greybus/svc.c | 24 ++++++++++++------------ drivers/staging/greybus/uart.c | 12 ++++++------ drivers/staging/greybus/usb.c | 4 ++-- drivers/staging/greybus/vibrator.c | 2 +- 16 files changed, 54 insertions(+), 58 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 025dd53507b7..30b381ab8a1f 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -675,7 +675,7 @@ static int gb_audio_add_mgmt_connection(struct gbaudio_codec_info *gbcodec, if (IS_ERR(connection)) return PTR_ERR(connection); - connection->private = gbcodec; + gb_connection_set_data(connection, gbcodec); gbcodec->mgmt_connection = connection; return 0; @@ -703,7 +703,7 @@ static int gb_audio_add_data_connection(struct gbaudio_codec_info *gbcodec, return PTR_ERR(connection); } - connection->private = gbcodec; + gb_connection_set_data(connection, gbcodec); atomic_set(&dai->users, 0); init_waitqueue_head(&dai->wait_queue); dai->data_cport = connection->intf_cport_id; diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 722f2b4fe54d..a871b0f33733 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -374,7 +374,7 @@ static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) static int gb_camera_event_recv(u8 type, struct gb_operation *op) { - struct gb_camera *gcam = op->connection->private; + struct gb_camera *gcam = gb_connection_get_data(op->connection); struct gb_camera_metadata_request *payload; struct gb_message *request; @@ -904,7 +904,7 @@ static int gb_camera_connection_init(struct gb_connection *connection) return -ENOMEM; gcam->connection = connection; - connection->private = gcam; + gb_connection_set_data(connection, gcam); /* * Create the data connection between camera module CDSI0 and APB CDS1. @@ -936,7 +936,7 @@ error: static void gb_camera_connection_exit(struct gb_connection *connection) { - struct gb_camera *gcam = connection->private; + struct gb_camera *gcam = gb_connection_get_data(connection); gb_camera_unregister_intf_ops(gcam); diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index bac412ef72ab..83be255bb534 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -195,7 +195,7 @@ struct gb_control *gb_control_create(struct gb_interface *intf) return NULL; } - control->connection->private = control; + gb_connection_set_data(control->connection, control); return control; } diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index 17ce573b9162..b1188b23ffe5 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -115,11 +115,10 @@ static int download_firmware(struct gb_firmware *firmware, u8 stage) static int gb_firmware_size_request(struct gb_operation *op) { - struct gb_connection *connection = op->connection; - struct gb_firmware *firmware = connection->private; + struct gb_firmware *firmware = gb_connection_get_data(op->connection); struct gb_firmware_size_request *size_request = op->request->payload; struct gb_firmware_size_response *size_response; - struct device *dev = &connection->bundle->dev; + struct device *dev = &op->connection->bundle->dev; int ret; if (op->request->payload_size != sizeof(*size_request)) { @@ -153,12 +152,11 @@ static int gb_firmware_size_request(struct gb_operation *op) static int gb_firmware_get_firmware(struct gb_operation *op) { - struct gb_connection *connection = op->connection; - struct gb_firmware *firmware = connection->private; + struct gb_firmware *firmware = gb_connection_get_data(op->connection); const struct firmware *fw = firmware->fw; struct gb_firmware_get_firmware_request *firmware_request; struct gb_firmware_get_firmware_response *firmware_response; - struct device *dev = &connection->bundle->dev; + struct device *dev = &op->connection->bundle->dev; unsigned int offset, size; if (op->request->payload_size != sizeof(*firmware_request)) { @@ -309,7 +307,7 @@ static int gb_firmware_probe(struct gb_bundle *bundle, goto err_free_firmware; } - connection->private = firmware; + gb_connection_set_data(connection, firmware); firmware->connection = connection; diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 7b2cb5d81e54..440ff44c8524 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -351,7 +351,7 @@ static int gb_gpio_request_recv(u8 type, struct gb_operation *op) { struct gb_connection *connection = op->connection; struct device *dev = &connection->bundle->dev; - struct gb_gpio_controller *ggc = connection->private; + struct gb_gpio_controller *ggc = gb_connection_get_data(connection); struct gb_message *request; struct gb_gpio_irq_event_request *event; int irq; @@ -633,7 +633,7 @@ static int gb_gpio_connection_init(struct gb_connection *connection) if (!ggc) return -ENOMEM; ggc->connection = connection; - connection->private = ggc; + gb_connection_set_data(connection, ggc); ret = gb_gpio_controller_setup(ggc); if (ret) @@ -700,7 +700,7 @@ err_free_controller: static void gb_gpio_connection_exit(struct gb_connection *connection) { - struct gb_gpio_controller *ggc = connection->private; + struct gb_gpio_controller *ggc = gb_connection_get_data(connection); if (!ggc) return; diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 6ef151f61ffc..fd4a7e096450 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -100,7 +100,7 @@ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, static int gb_hid_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_hid *ghid = connection->private; + struct gb_hid *ghid = gb_connection_get_data(connection); struct gb_hid_input_report_request *request = op->request->payload; if (op->type != GB_HID_TYPE_IRQ_EVENT) { @@ -449,7 +449,7 @@ static int gb_hid_probe(struct gb_bundle *bundle, goto err_free_ghid; } - connection->private = ghid; + gb_connection_set_data(connection, ghid); ghid->connection = connection; hid = hid_allocate_device(); diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 4b96f69318bd..73b85815d1eb 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -251,7 +251,7 @@ static int gb_i2c_connection_init(struct gb_connection *connection) return -ENOMEM; gb_i2c_dev->connection = connection; /* refcount? */ - connection->private = gb_i2c_dev; + gb_connection_set_data(connection, gb_i2c_dev); ret = gb_i2c_device_setup(gb_i2c_dev); if (ret) @@ -282,7 +282,7 @@ out_err: static void gb_i2c_connection_exit(struct gb_connection *connection) { - struct gb_i2c_device *gb_i2c_dev = connection->private; + struct gb_i2c_device *gb_i2c_dev = gb_connection_get_data(connection); i2c_del_adapter(&gb_i2c_dev->adapter); /* kref_put(gb_i2c_dev->connection) */ diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 47d4ac4533bc..8b71ed3df318 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1154,7 +1154,7 @@ static int gb_lights_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct device *dev = &connection->bundle->dev; - struct gb_lights *glights = connection->private; + struct gb_lights *glights = gb_connection_get_data(connection); struct gb_light *light; struct gb_message *request; struct gb_lights_event_request *payload; @@ -1230,7 +1230,7 @@ static int gb_lights_probe(struct gb_bundle *bundle, } glights->connection = connection; - connection->private = glights; + gb_connection_set_data(connection, glights); mutex_init(&glights->lights_lock); diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index ec8fb1b84024..9cae396c6115 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -711,7 +711,7 @@ static int gb_power_supplies_register(struct gb_power_supplies *supplies) static int gb_supplies_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_power_supplies *supplies = connection->private; + struct gb_power_supplies *supplies = gb_connection_get_data(connection); struct gb_power_supply *gbpsy; struct gb_message *request; struct gb_power_supply_event_request *payload; @@ -792,7 +792,7 @@ static int gb_power_supply_probe(struct gb_bundle *bundle, } supplies->connection = connection; - connection->private = supplies; + gb_connection_set_data(connection, supplies); mutex_init(&supplies->supplies_lock); diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 018d5c229070..176301a49865 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -188,7 +188,7 @@ static int gb_pwm_connection_init(struct gb_connection *connection) if (!pwmc) return -ENOMEM; pwmc->connection = connection; - connection->private = pwmc; + gb_connection_set_data(connection, pwmc); /* Query number of pwms present */ ret = gb_pwm_count_operation(pwmc); @@ -218,8 +218,7 @@ out_err: static void gb_pwm_connection_exit(struct gb_connection *connection) { - struct gb_pwm_chip *pwmc = connection->private; - + struct gb_pwm_chip *pwmc = gb_connection_get_data(connection); if (!pwmc) return; diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 9a20f0e0363d..d4cbcb972e94 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -201,15 +201,14 @@ static int _gb_sdio_process_events(struct gb_sdio_host *host, u8 event) static int gb_sdio_event_recv(u8 type, struct gb_operation *op) { - struct gb_connection *connection = op->connection; - struct gb_sdio_host *host = connection->private; + struct gb_sdio_host *host = gb_connection_get_data(op->connection); struct gb_message *request; struct gb_sdio_event_request *payload; int ret = 0; u8 event; if (type != GB_SDIO_TYPE_EVENT) { - dev_err(&connection->bundle->dev, + dev_err(mmc_dev(host->mmc), "unsupported unsolicited event: %u\n", type); return -EINVAL; } @@ -723,7 +722,7 @@ static int gb_sdio_connection_init(struct gb_connection *connection) host->removed = true; host->connection = connection; - connection->private = host; + gb_connection_set_data(connection, host); ret = gb_sdio_get_caps(host); if (ret < 0) @@ -767,7 +766,7 @@ free_work: free_buffer: kfree(host->xfer_buffer); free_mmc: - connection->private = NULL; + gb_connection_set_data(connection, NULL); mmc_free_host(mmc); return ret; @@ -776,7 +775,7 @@ free_mmc: static void gb_sdio_connection_exit(struct gb_connection *connection) { struct mmc_host *mmc; - struct gb_sdio_host *host = connection->private; + struct gb_sdio_host *host = gb_connection_get_data(connection); if (!host) return; @@ -784,7 +783,7 @@ static void gb_sdio_connection_exit(struct gb_connection *connection) mutex_lock(&host->lock); host->removed = true; mmc = host->mmc; - connection->private = NULL; + gb_connection_set_data(connection, NULL); mutex_unlock(&host->lock); flush_workqueue(host->mrq_workqueue); diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 35714533a1ac..12e8cd8b1655 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -28,7 +28,7 @@ struct gb_spi { static struct spi_master *get_master_from_spi(struct gb_spi *spi) { - return spi->connection->private; + return gb_connection_get_data(spi->connection); } static int tx_header_fit_operation(u32 tx_size, u32 count, size_t data_max) @@ -339,7 +339,7 @@ static int gb_spi_connection_init(struct gb_connection *connection) spi = spi_master_get_devdata(master); spi->connection = connection; - connection->private = master; + gb_connection_set_data(connection, master); /* get master configuration */ ret = gb_spi_get_master_config(spi); @@ -382,7 +382,7 @@ out_put_master: static void gb_spi_connection_exit(struct gb_connection *connection) { - struct spi_master *master = connection->private; + struct spi_master *master = gb_connection_get_data(connection); spi_unregister_master(master); } diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ae1911ce24ef..6a8da0dd9f33 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -397,7 +397,7 @@ EXPORT_SYMBOL_GPL(gb_svc_ping); static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_svc *svc = connection->private; + struct gb_svc *svc = gb_connection_get_data(connection); struct gb_protocol_version_request *request; struct gb_protocol_version_response *response; @@ -432,7 +432,7 @@ static int gb_svc_version_request(struct gb_operation *op) static int gb_svc_hello(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_svc *svc = connection->private; + struct gb_svc *svc = gb_connection_get_data(connection); struct gb_svc_hello_request *hello_request; int ret; @@ -550,7 +550,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) { struct gb_svc_intf_hotplug_request *request; struct gb_connection *connection = operation->connection; - struct gb_svc *svc = connection->private; + struct gb_svc *svc = gb_connection_get_data(connection); struct gb_host_device *hd = connection->hd; struct gb_interface *intf; u8 intf_id; @@ -644,7 +644,7 @@ out_interface_add: static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) { - struct gb_svc *svc = operation->connection->private; + struct gb_svc *svc = gb_connection_get_data(operation->connection); struct gb_svc_intf_hot_unplug_request *request; struct gb_host_device *hd = operation->connection->hd; struct gb_interface *intf; @@ -675,7 +675,7 @@ static void gb_svc_process_deferred_request(struct work_struct *work) dr = container_of(work, struct gb_svc_deferred_request, work); operation = dr->operation; - svc = operation->connection->private; + svc = gb_connection_get_data(operation->connection); type = operation->request->header->type; switch (type) { @@ -695,7 +695,7 @@ static void gb_svc_process_deferred_request(struct work_struct *work) static int gb_svc_queue_deferred_request(struct gb_operation *operation) { - struct gb_svc *svc = operation->connection->private; + struct gb_svc *svc = gb_connection_get_data(operation->connection); struct gb_svc_deferred_request *dr; dr = kmalloc(sizeof(*dr), GFP_KERNEL); @@ -723,7 +723,7 @@ static int gb_svc_queue_deferred_request(struct gb_operation *operation) */ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) { - struct gb_svc *svc = op->connection->private; + struct gb_svc *svc = gb_connection_get_data(op->connection); struct gb_svc_intf_hotplug_request *request; if (op->request->payload_size < sizeof(*request)) { @@ -741,7 +741,7 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) { - struct gb_svc *svc = op->connection->private; + struct gb_svc *svc = gb_connection_get_data(op->connection); struct gb_svc_intf_hot_unplug_request *request; if (op->request->payload_size < sizeof(*request)) { @@ -759,7 +759,7 @@ static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) static int gb_svc_intf_reset_recv(struct gb_operation *op) { - struct gb_svc *svc = op->connection->private; + struct gb_svc *svc = gb_connection_get_data(op->connection); struct gb_message *request = op->request; struct gb_svc_intf_reset_request *reset; u8 intf_id; @@ -794,7 +794,7 @@ static int gb_svc_key_code_map(struct gb_svc *svc, u16 key_code, u16 *code) static int gb_svc_key_event_recv(struct gb_operation *op) { - struct gb_svc *svc = op->connection->private; + struct gb_svc *svc = gb_connection_get_data(op->connection); struct gb_message *request = op->request; struct gb_svc_key_event_request *key; u16 code; @@ -828,7 +828,7 @@ static int gb_svc_key_event_recv(struct gb_operation *op) static int gb_svc_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_svc *svc = connection->private; + struct gb_svc *svc = gb_connection_get_data(connection); u8 type = op->type; int ret = 0; @@ -975,7 +975,7 @@ struct gb_svc *gb_svc_create(struct gb_host_device *hd) goto err_free_input; } - svc->connection->private = svc; + gb_connection_set_data(svc->connection, svc); return svc; diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index d169c55b4552..c580fe06f554 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -69,7 +69,7 @@ static atomic_t reference_count = ATOMIC_INIT(0); static int gb_uart_receive_data_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_tty *gb_tty = connection->private; + struct gb_tty *gb_tty = gb_connection_get_data(connection); struct tty_port *port = &gb_tty->port; struct gb_message *request = op->request; struct gb_uart_recv_data_request *receive_data; @@ -125,7 +125,7 @@ static int gb_uart_receive_data_handler(struct gb_operation *op) static int gb_uart_serial_state_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_tty *gb_tty = connection->private; + struct gb_tty *gb_tty = gb_connection_get_data(connection); struct gb_message *request = op->request; struct gb_uart_serial_state_request *serial_state; @@ -658,7 +658,7 @@ static int gb_uart_connection_init(struct gb_connection *connection) } gb_tty->connection = connection; - connection->private = gb_tty; + gb_connection_set_data(connection, gb_tty); minor = alloc_minor(gb_tty); if (minor < 0) { @@ -702,7 +702,7 @@ error: tty_port_destroy(&gb_tty->port); release_minor(gb_tty); error_minor: - connection->private = NULL; + gb_connection_set_data(connection, NULL); kfree(gb_tty->buffer); error_payload: kfree(gb_tty); @@ -714,7 +714,7 @@ error_alloc: static void gb_uart_connection_exit(struct gb_connection *connection) { - struct gb_tty *gb_tty = connection->private; + struct gb_tty *gb_tty = gb_connection_get_data(connection); struct tty_struct *tty; if (!gb_tty) @@ -724,7 +724,7 @@ static void gb_uart_connection_exit(struct gb_connection *connection) gb_tty->disconnected = true; wake_up_all(&gb_tty->wioctl); - connection->private = NULL; + gb_connection_set_data(connection, NULL); mutex_unlock(&gb_tty->mutex); tty = tty_port_tty_get(&gb_tty->port); diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 0cfc00f39b46..25a6c7e5e5ba 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -176,7 +176,7 @@ static int gb_usb_connection_init(struct gb_connection *connection) gb_usb_dev = to_gb_usb_device(hcd); gb_usb_dev->connection = connection; - connection->private = gb_usb_dev; + gb_connection_set_data(connection, gb_usb_dev); hcd->has_tt = 1; @@ -206,7 +206,7 @@ err_put_hcd: static void gb_usb_connection_exit(struct gb_connection *connection) { - struct gb_usb_device *gb_usb_dev = connection->private; + struct gb_usb_device *gb_usb_dev = gb_connection_get_data(connection); struct usb_hcd *hcd = gb_usb_device_to_hcd(gb_usb_dev); usb_remove_hcd(hcd); diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 8c0df99b9f8d..5afcb2784a32 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -114,7 +114,7 @@ static int gb_vibrator_probe(struct gb_bundle *bundle, retval = PTR_ERR(connection); goto err_free_vib; } - connection->private = vib; + gb_connection_set_data(connection, vib); vib->connection = connection; -- cgit v1.2.3-59-g8ed1b From 6dd67645f22cfeb55a32e9a08c92deb297d06935 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 29 Mar 2016 23:31:41 +0530 Subject: greybus: audio: Use single codec driver registration We have single I2S port via APB1 for communication with all audio modules. Thus, we should register single codec driver and manage all individual audio modules internally within this driver. Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 1413 +++++++++++++++++------------- drivers/staging/greybus/audio_codec.h | 96 +- drivers/staging/greybus/audio_topology.c | 408 ++++----- 3 files changed, 1046 insertions(+), 871 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 30b381ab8a1f..ab24ec8c5ab8 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -1,7 +1,7 @@ /* - * Greybus audio driver - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. + * audio codec driver + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. * * Released under the GPLv2 only. */ @@ -9,128 +9,382 @@ #include #include #include -#include #include "audio_codec.h" #include "audio_apbridgea.h" #include "audio_manager.h" -static DEFINE_MUTEX(gb_codec_list_lock); -static LIST_HEAD(gb_codec_list); +static struct gbaudio_codec_info *gbcodec; -struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, - int data_cport, const char *name) +struct gbaudio_data_connection *find_data(struct gbaudio_module_info *module, + const char *name) { - struct gbaudio_dai *dai; + struct gbaudio_data_connection *data; - list_for_each_entry(dai, &gbcodec->dai_list, list) { - if (name && !strncmp(dai->name, name, NAME_SIZE)) - return dai; - if ((data_cport != -1) && (dai->data_cport == data_cport)) - return dai; + list_for_each_entry(data, &module->data_list, list) { + if (name && !strncmp(data->name, name, NAME_SIZE)) + return data; } return NULL; } -/* - * codec DAI ops - */ -static int gbcodec_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int find_stream(const char *name) { - int ret; - __u16 i2s_port, cportid; + int stream = 0; + + if (strnstr(name, "SPK Amp", NAME_SIZE)) + stream |= GB_PLAYBACK; + + return stream; +} + +static int gbaudio_module_disable(struct gbaudio_codec_info *codec, + struct gbaudio_module_info *module, + int dir) +{ + int ret = 0; + uint16_t data_cport, cportid, i2s_port; + int codec_state, module_state; + struct gbaudio_data_connection *data; + const char *dai_name; + + mutex_lock(&codec->lock); + + codec_state = codec->stream[dir].state; + if (codec_state == GBAUDIO_CODEC_SHUTDOWN) { + mutex_unlock(&codec->lock); + return 0; + } + + dai_name = codec->stream[dir].dai_name; - struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + mutex_lock(&module->lock); + module_state = module->ctrlstate[dir]; + if (module_state == GBAUDIO_CODEC_SHUTDOWN) { + dev_dbg(codec->dev, "%s: module already configured\n", + module->name); + goto func_exit; + } - if (!atomic_read(&gb->is_connected)) + /* find the dai */ + data = find_data(module, dai_name); + if (!data) { + dev_err(codec->dev, "%s:%s DATA connection missing\n", + dai_name, module->name); + mutex_unlock(&module->lock); + mutex_unlock(&codec->lock); return -ENODEV; + } + if (codec_state > GBAUDIO_CODEC_HWPARAMS) { + data_cport = data->connection->intf_cport_id; + ret = gb_audio_gb_deactivate_tx(module->mgmt_connection, + data_cport); + if (ret) { + dev_err(codec->dev, "deactivate_tx for %s failed:%d\n", + module->name, ret); + goto func_exit; + } + dev_dbg(codec->dev, "Dynamic deactivate %s:%d DAI\n", dai_name, + data_cport); + } + if (codec_state > GBAUDIO_CODEC_SHUTDOWN) { + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); + if (ret) { + dev_err(codec->dev, "unregister_cport for %s failed:%d\n", + module->name, ret); + goto func_exit; + } + dev_dbg(codec->dev, "Dynamic Unregister %s:%d DAI\n", dai_name, + cportid); + } + module->ctrlstate[dir] = GBAUDIO_CODEC_SHUTDOWN; + +func_exit: + mutex_unlock(&module->lock); + mutex_unlock(&codec->lock); + return ret; +} + +static int gbaudio_module_enable(struct gbaudio_codec_info *codec, + struct gbaudio_module_info *module, int dir) +{ + int ret = 0; + __u16 i2s_port, cportid; + int codec_state, module_state; + uint16_t data_cport; + uint8_t sig_bits, channels; + uint32_t format, rate; + struct gbaudio_data_connection *data; + const char *dai_name; + + mutex_lock(&codec->lock); + + codec_state = codec->stream[dir].state; + if (codec_state == GBAUDIO_CODEC_SHUTDOWN) { + mutex_unlock(&codec->lock); + return 0; + } + + dai_name = codec->stream[dir].dai_name; + format = codec->stream[dir].format; + channels = codec->stream[dir].channels; + rate = codec->stream[dir].rate; + sig_bits = codec->stream[dir].sig_bits; + + mutex_lock(&module->lock); + module_state = module->ctrlstate[dir]; + if (module_state == codec_state) { + dev_dbg(codec->dev, "%s: module already configured\n", + module->name); + goto func_exit; + } /* find the dai */ - mutex_lock(&gb->lock); - gb_dai = gbaudio_find_dai(gb, -1, dai->name); - if (!gb_dai) { - dev_err(dai->dev, "%s: DAI not registered\n", dai->name); - mutex_unlock(&gb->lock); - return -EINVAL; + data = find_data(module, dai_name); + if (!data) { + dev_err(codec->dev, "%s:%s DATA connection missing\n", + dai_name, module->name); + mutex_unlock(&module->lock); + mutex_unlock(&codec->lock); + return -ENODEV; } /* register cport */ - i2s_port = 0; /* fixed for now */ - cportid = gb_dai->connection->hd_cport_id; - ret = gb_audio_apbridgea_register_cport(gb_dai->connection, i2s_port, - cportid, + if (module_state < codec_state) { + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_register_cport(data->connection, + i2s_port, cportid, AUDIO_APBRIDGEA_DIRECTION_TX); - dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid, - ret); + if (ret) { + dev_err(codec->dev, "reg_cport for %s\n", module->name); + goto func_exit; + } + module_state = GBAUDIO_CODEC_STARTUP; + dev_dbg(codec->dev, "Dynamic Register %s:%d DAI\n", dai_name, + cportid); + } + + /* hw_params */ + if (module_state < codec_state) { + data_cport = data->connection->intf_cport_id; + ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, + format, rate, channels, sig_bits); + if (ret) { + dev_err(codec->dev, "set_pcm for %s\n", module->name); + goto func_exit; + } + module_state = GBAUDIO_CODEC_HWPARAMS; + dev_dbg(codec->dev, "Dynamic hw_params %s:%d DAI\n", dai_name, + data_cport); + } - if (!ret) - atomic_inc(&gb_dai->users); - mutex_unlock(&gb->lock); + /* prepare */ + if (module_state < codec_state) { + data_cport = data->connection->intf_cport_id; + ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection, + data_cport, 192); + if (ret) { + dev_err(codec->dev, "set_tx_data_size for %s\n", + module->name); + goto func_exit; + } + ret = gb_audio_gb_activate_tx(module->mgmt_connection, + data_cport); + if (ret) { + dev_err(codec->dev, "activate_tx for %s\n", + module->name); + goto func_exit; + } + module_state = GBAUDIO_CODEC_PREPARE; + dev_dbg(codec->dev, "Dynamic prepare %s:%d DAI\n", dai_name, + data_cport); + } +func_exit: + module->ctrlstate[dir] = module_state; + mutex_unlock(&module->lock); + mutex_unlock(&codec->lock); return ret; } -static void gbcodec_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +int gbaudio_module_update(struct gbaudio_codec_info *codec, + const char *w_name, + struct gbaudio_module_info *module, int enable) { - int ret; - __u16 i2s_port, cportid; + int stream, ret = 0; + int pb_state; - struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + dev_dbg(module->dev, "Module update %s sequence\n", + enable ? "Enable":"Disable"); - /* find the dai */ - gb_dai = gbaudio_find_dai(gb, -1, dai->name); - if (!gb_dai) { - dev_err(dai->dev, "%s: DAI not registered\n", dai->name); - goto func_exit; + stream = find_stream(w_name); + if (!stream) { + dev_dbg(codec->dev, "No action required for %s\n", w_name); + return 0; } - atomic_dec(&gb_dai->users); + /* check if playback active */ + pb_state = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].state; + if ((stream & GB_PLAYBACK) && (pb_state > GBAUDIO_CODEC_SHUTDOWN)) { + if (enable) + ret = gbaudio_module_enable(codec, module, + SNDRV_PCM_STREAM_PLAYBACK); + else + ret = gbaudio_module_disable(codec, module, + SNDRV_PCM_STREAM_PLAYBACK); + } - if (!atomic_read(&gb->is_connected)) { - if (!atomic_read(&gb_dai->users)) - wake_up_interruptible(&gb_dai->wait_queue); - return; + /* TBD + * check if capture active + * cap_state = codec->stream[SNDRV_PCM_STREAM_CAPTURE].state; + * if ((stream & GB_CAPTURE) && (cap_state > GBAUDIO_CODEC_SHUTDOWN)) + * + */ + + return ret; +} +EXPORT_SYMBOL(gbaudio_module_update); + +/* + * codec DAI ops + */ +static int gbcodec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + __u16 i2s_port, cportid; + int state; + struct gbaudio_data_connection *data; + struct gbaudio_module_info *module; + struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); + + mutex_lock(&codec->lock); + + if (list_empty(&codec->module_list)) { + dev_err(codec->dev, "No codec module available\n"); + mutex_unlock(&codec->lock); + return -ENODEV; } - mutex_lock(&gb->lock); - /* deactivate rx/tx */ - cportid = gb_dai->connection->intf_cport_id; + state = codec->stream[substream->stream].state; + list_for_each_entry(module, &codec->module_list, list) { + mutex_lock(&module->lock); + if (!module->is_connected) { + mutex_unlock(&module->lock); + continue; + } - switch (substream->stream) { - case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_gb_deactivate_rx(gb->mgmt_connection, cportid); - break; - case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection, cportid); - break; - default: - dev_err(dai->dev, "Invalid stream type during shutdown\n"); - goto func_exit; + /* find the dai */ + data = find_data(module, dai->name); + if (!data) { + dev_err(dai->dev, "%s:%s DATA connection missing\n", + dai->name, module->name); + mutex_unlock(&module->lock); + continue; + } + + /* register cport */ + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_register_cport(data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); + dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, + cportid, ret); + state = GBAUDIO_CODEC_STARTUP; + module->ctrlstate[substream->stream] = state; + dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); + mutex_unlock(&module->lock); } + codec->stream[substream->stream].state = state; + codec->stream[substream->stream].dai_name = dai->name; + mutex_unlock(&codec->lock); - if (ret) - dev_err(dai->dev, "%d:Error during deactivate\n", ret); + return ret; +} - /* un register cport */ - i2s_port = 0; /* fixed for now */ - ret = gb_audio_apbridgea_unregister_cport(gb_dai->connection, i2s_port, - gb_dai->connection->hd_cport_id, - AUDIO_APBRIDGEA_DIRECTION_TX); +static void gbcodec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + __u16 i2s_port, cportid; + int state, module_state; + struct gbaudio_module_info *module; + struct gbaudio_data_connection *data; + struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); - dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name, - gb_dai->connection->hd_cport_id, ret); -func_exit: - mutex_unlock(&gb->lock); + mutex_lock(&codec->lock); - /* - if (!atomic_read(&gb_dai->users)) - wake_up_interruptible(&gb_dai->wait_queue); - */ + if (list_empty(&codec->module_list)) { + dev_err(codec->dev, "No codec module available\n"); + mutex_unlock(&codec->lock); + return; + } + + state = codec->stream[substream->stream].state; + list_for_each_entry(module, &codec->module_list, list) { + mutex_lock(&module->lock); + if (!module->is_connected) { + dev_err(dai->dev, "%s:%s module not connected\n", + __func__, module->name); + mutex_unlock(&module->lock); + continue; + } + module_state = module->ctrlstate[substream->stream]; + if (module_state == GBAUDIO_CODEC_SHUTDOWN) { + dev_dbg(codec->dev, "%s: module already configured\n", + module->name); + mutex_unlock(&module->lock); + continue; + } + + /* find the dai */ + data = find_data(module, dai->name); + if (!data) { + dev_err(dai->dev, "%s:%s DATA connection missing\n", + dai->name, module->name); + mutex_unlock(&module->lock); + continue; + } + /* deactivate */ + cportid = data->connection->intf_cport_id; + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_gb_deactivate_rx(module->mgmt_connection, + cportid); + /* unregister cport */ + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport( + data->connection, i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_gb_deactivate_tx(module->mgmt_connection, + cportid); + /* unregister cport */ + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport( + data->connection, i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); + break; + } + dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name, + cportid, ret); + state = GBAUDIO_CODEC_SHUTDOWN; + module->ctrlstate[substream->stream] = state; + dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); + mutex_unlock(&module->lock); + } + codec->stream[substream->stream].state = state; + codec->stream[substream->stream].dai_name = NULL; + mutex_unlock(&codec->lock); return; } @@ -142,19 +396,17 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, uint8_t sig_bits, channels; uint32_t format, rate; uint16_t data_cport; - struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + struct gbaudio_module_info *module; + struct gbaudio_data_connection *data; + int state; + struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); - if (!atomic_read(&gb->is_connected)) - return -ENODEV; + mutex_lock(&codec->lock); - /* find the dai */ - mutex_lock(&gb->lock); - gb_dai = gbaudio_find_dai(gb, -1, dai->name); - if (!gb_dai) { - dev_err(dai->dev, "%s: DAI not registered\n", dai->name); - ret = -EINVAL; - goto func_exit; + if (list_empty(&codec->module_list)) { + dev_err(codec->dev, "No codec module available\n"); + mutex_unlock(&codec->lock); + return -ENODEV; } /* @@ -164,54 +416,86 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, if (params_channels(hwparams) != 2) { dev_err(dai->dev, "Invalid channel count:%d\n", params_channels(hwparams)); - ret = -EINVAL; - goto func_exit; + mutex_unlock(&codec->lock); + return -EINVAL; } channels = params_channels(hwparams); if (params_rate(hwparams) != 48000) { dev_err(dai->dev, "Invalid sampling rate:%d\n", params_rate(hwparams)); - ret = -EINVAL; - goto func_exit; + mutex_unlock(&codec->lock); + return -EINVAL; } rate = GB_AUDIO_PCM_RATE_48000; if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) { dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams)); - ret = -EINVAL; - goto func_exit; + mutex_unlock(&codec->lock); + return -EINVAL; } format = GB_AUDIO_PCM_FMT_S16_LE; - data_cport = gb_dai->connection->intf_cport_id; - /* XXX check impact of sig_bit - * it should not change ideally - */ + state = codec->stream[substream->stream].state; + list_for_each_entry(module, &codec->module_list, list) { + mutex_lock(&module->lock); + if (!module->is_connected) { + dev_err(dai->dev, "%s:%s module not connected\n", + __func__, module->name); + ret = -ENODEV; + mutex_unlock(&module->lock); + continue; + } - dev_dbg(dai->dev, "cport:%d, rate:%d, channel %d, format %d, sig_bits:%d\n", - data_cport, rate, channels, format, sig_bits); - ret = gb_audio_gb_set_pcm(gb->mgmt_connection, data_cport, format, - rate, channels, sig_bits); - if (ret) { - dev_err(dai->dev, "%d: Error during set_pcm\n", ret); - goto func_exit; + /* find the data connection */ + data = find_data(module, dai->name); + if (!data) { + dev_err(dai->dev, "%s:%s DATA connection missing\n", + dai->name, module->name); + mutex_unlock(&module->lock); + continue; + } + + data_cport = data->connection->intf_cport_id; + /* XXX check impact of sig_bit + * it should not change ideally + */ + dev_dbg(dai->dev, + "cport:%d, rate:%d, channel %d, format %d, sig_bits:%d\n", + data_cport, rate, channels, format, sig_bits); + ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, + format, rate, channels, sig_bits); + if (ret) { + dev_err(dai->dev, "%d: Error during set_pcm\n", ret); + mutex_unlock(&module->lock); + goto func_exit; + } + if (state < GBAUDIO_CODEC_HWPARAMS) { + ret = gb_audio_apbridgea_set_config(data->connection, 0, + AUDIO_APBRIDGEA_PCM_FMT_16, + AUDIO_APBRIDGEA_PCM_RATE_48000, + 6144000); + if (ret) { + dev_err(dai->dev, + "%d: Error during set_config\n", ret); + mutex_unlock(&module->lock); + goto func_exit; + } + } + state = GBAUDIO_CODEC_HWPARAMS; + module->ctrlstate[substream->stream] = state; + dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); + mutex_unlock(&module->lock); } + codec->stream[substream->stream].state = state; + codec->stream[substream->stream].format = format; + codec->stream[substream->stream].rate = rate; + codec->stream[substream->stream].channels = channels; + codec->stream[substream->stream].sig_bits = sig_bits; - /* - * XXX need to check if - * set config is always required - * check for mclk_freq as well - */ - ret = gb_audio_apbridgea_set_config(gb_dai->connection, 0, - AUDIO_APBRIDGEA_PCM_FMT_16, - AUDIO_APBRIDGEA_PCM_RATE_48000, - 6144000); - if (ret) - dev_err(dai->dev, "%d: Error during set_config\n", ret); func_exit: - mutex_unlock(&gb->lock); + mutex_unlock(&codec->lock); return ret; } @@ -220,76 +504,110 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, { int ret; uint16_t data_cport; - struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + struct gbaudio_data_connection *data; + struct gbaudio_module_info *module; + int state; + struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); - if (!atomic_read(&gb->is_connected)) - return -ENODEV; + mutex_lock(&codec->lock); - /* find the dai */ - mutex_lock(&gb->lock); - gb_dai = gbaudio_find_dai(gb, -1, dai->name); - if (!gb_dai) { - dev_err(dai->dev, "%s: DAI not registered\n", dai->name); - ret = -EINVAL; - goto func_exit; + if (list_empty(&codec->module_list)) { + dev_err(codec->dev, "No codec module available\n"); + mutex_unlock(&codec->lock); + return -ENODEV; } - /* deactivate rx/tx */ - data_cport = gb_dai->connection->intf_cport_id; + state = codec->stream[substream->stream].state; + list_for_each_entry(module, &codec->module_list, list) { + mutex_lock(&module->lock); + if (!module->is_connected) { + mutex_unlock(&module->lock); + continue; + } - switch (substream->stream) { - case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_gb_set_rx_data_size(gb->mgmt_connection, - data_cport, 192); - if (ret) { - dev_err(dai->dev, - "%d:Error during set_rx_data_size, cport:%d\n", - ret, data_cport); - goto func_exit; + /* find the dai */ + data = find_data(module, dai->name); + if (!data) { + dev_err(dai->dev, "%s:%s DATA connection missing\n", + dai->name, module->name); + mutex_unlock(&module->lock); + continue; } - ret = gb_audio_apbridgea_set_rx_data_size(gb_dai->connection, 0, - 192); - if (ret) { - dev_err(dai->dev, + /* deactivate rx/tx */ + data_cport = data->connection->intf_cport_id; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_gb_set_rx_data_size( + module->mgmt_connection, + data_cport, 192); + if (ret) { + dev_err(dai->dev, + "%d:Error during set_rx_data_size, cport:%d\n", + ret, data_cport); + mutex_unlock(&module->lock); + goto func_exit; + } + if (state < GBAUDIO_CODEC_PREPARE) { + ret = gb_audio_apbridgea_set_rx_data_size( + data->connection, 0, + 192); + if (ret) { + dev_err(dai->dev, "%d:Error during apbridgea_set_rx_data_size\n", ret); - goto func_exit; - } - ret = gb_audio_gb_activate_rx(gb->mgmt_connection, data_cport); - break; - case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_gb_set_tx_data_size(gb->mgmt_connection, - data_cport, 192); - if (ret) { - dev_err(dai->dev, - "%d:Error during module set_tx_data_size, cport:%d\n", - ret, data_cport); - goto func_exit; - } - ret = gb_audio_apbridgea_set_tx_data_size(gb_dai->connection, 0, - 192); - if (ret) { - dev_err(dai->dev, - "%d:Error during apbridgea set_tx_data_size, cport\n", - ret); - goto func_exit; + mutex_unlock(&module->lock); + goto func_exit; + } + } + ret = gb_audio_gb_activate_rx(module->mgmt_connection, + data_cport); + if (ret) + dev_err(dai->dev, + "%s:Error during activate stream,%d\n", + module->name, ret); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_gb_set_tx_data_size( + module->mgmt_connection, + data_cport, 192); + if (ret) { + dev_err(dai->dev, + "%d:Error during module set_tx_data_size, cport:%d\n", + ret, data_cport); + mutex_unlock(&module->lock); + goto func_exit; + } + if (state < GBAUDIO_CODEC_PREPARE) { + ret = gb_audio_apbridgea_set_tx_data_size( + data->connection, 0, + 192); + if (ret) { + dev_err(dai->dev, + "%d:Error during apbridgea set_tx_data_size, cport\n", + ret); + mutex_unlock(&module->lock); + goto func_exit; + } + } + ret = gb_audio_gb_activate_tx(module->mgmt_connection, + data_cport); + if (ret) + dev_err(dai->dev, + "%s:Error during activate stream,%d\n", + module->name, ret); + break; } - ret = gb_audio_gb_activate_tx(gb->mgmt_connection, data_cport); - break; - default: - dev_err(dai->dev, "Invalid stream type %d during prepare\n", - substream->stream); - ret = -EINVAL; - goto func_exit; + state = GBAUDIO_CODEC_PREPARE; + module->ctrlstate[substream->stream] = state; + dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); + mutex_unlock(&module->lock); } - - if (ret) - dev_err(dai->dev, "%d: Error during activate stream\n", ret); + codec->stream[substream->stream].state = state; func_exit: - mutex_unlock(&gb->lock); - return ret; + mutex_unlock(&codec->lock); + return 0; } static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, @@ -297,24 +615,19 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, { int ret; int tx, rx, start, stop; - struct gbaudio_dai *gb_dai; - struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); - - if (!atomic_read(&gb->is_connected)) { + struct gbaudio_data_connection *data; + struct gbaudio_module_info *module; + struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); + + mutex_lock(&codec->lock); + if (list_empty(&codec->module_list)) { + dev_err(codec->dev, "No codec module available\n"); + mutex_unlock(&codec->lock); if (cmd == SNDRV_PCM_TRIGGER_STOP) return 0; return -ENODEV; } - /* find the dai */ - mutex_lock(&gb->lock); - gb_dai = gbaudio_find_dai(gb, -1, dai->name); - if (!gb_dai) { - dev_err(dai->dev, "%s: DAI not registered\n", dai->name); - ret = -EINVAL; - goto func_exit; - } - tx = rx = start = stop = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -345,47 +658,60 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, goto func_exit; } + list_for_each_entry(module, &codec->module_list, list) { + mutex_lock(&module->lock); + if (!module->is_connected) { + mutex_unlock(&module->lock); + continue; + } + + /* find the dai */ + data = find_data(module, dai->name); + if (data) + break; + } + if (!data) { + dev_err(dai->dev, "%s:%s DATA connection missing\n", + dai->name, module->name); + ret = -ENODEV; + mutex_unlock(&module->lock); + goto func_exit; + } if (start && tx) { - ret = gb_audio_apbridgea_prepare_tx(gb_dai->connection, 0); + ret = gb_audio_apbridgea_prepare_tx(data->connection, + 0); if (!ret) - ret = gb_audio_apbridgea_start_tx(gb_dai->connection, 0, - 0); - } - - else if (start && rx) { - ret = gb_audio_apbridgea_prepare_rx(gb_dai->connection, 0); + ret = gb_audio_apbridgea_start_tx(data->connection, + 0, 0); + codec->stream[substream->stream].state = GBAUDIO_CODEC_START; + } else if (start && rx) { + ret = gb_audio_apbridgea_prepare_rx(data->connection, + 0); if (!ret) - ret = gb_audio_apbridgea_start_rx(gb_dai->connection, + ret = gb_audio_apbridgea_start_rx(data->connection, 0); - } - - else if (stop && tx) { - ret = gb_audio_apbridgea_stop_tx(gb_dai->connection, 0); + codec->stream[substream->stream].state = GBAUDIO_CODEC_START; + } else if (stop && tx) { + ret = gb_audio_apbridgea_stop_tx(data->connection, 0); if (!ret) - ret = gb_audio_apbridgea_shutdown_tx(gb_dai->connection, + ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0); - } - - else if (stop && rx) { - ret = gb_audio_apbridgea_stop_rx(gb_dai->connection, 0); + codec->stream[substream->stream].state = GBAUDIO_CODEC_STOP; + } else if (stop && rx) { + ret = gb_audio_apbridgea_stop_rx(data->connection, 0); if (!ret) - ret = gb_audio_apbridgea_shutdown_rx(gb_dai->connection, + ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0); - } - - else + codec->stream[substream->stream].state = GBAUDIO_CODEC_STOP; + } else ret = -EINVAL; - if (ret) - dev_err(dai->dev, "%d:Error during %s stream\n", ret, - start ? "Start" : "Stop"); - - /* in case device removed, return 0 for stop trigger */ - if (stop && (ret == -ENODEV)) - ret = 0; + dev_err(dai->dev, "%s:Error during %s stream:%d\n", + module->name, start ? "Start" : "Stop", ret); + mutex_unlock(&module->lock); func_exit: - mutex_unlock(&gb->lock); + mutex_unlock(&codec->lock); return ret; } @@ -409,476 +735,325 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { .digital_mute = gbcodec_digital_mute, }; -/* - * codec driver ops - */ -static int gbcodec_probe(struct snd_soc_codec *codec) +int gbaudio_register_module(struct gbaudio_module_info *module) { - /* Empty function for now */ - return 0; -} + int ret; + struct snd_soc_codec *codec; -static int gbcodec_remove(struct snd_soc_codec *codec) -{ - /* Empty function for now */ - return 0; -} + if (!gbcodec) { + dev_err(module->dev, "GB Codec not yet probed\n"); + return -EAGAIN; + } -static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - int ret = 0; - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); - u8 *gbcodec_reg = gbcodec->reg; + codec = gbcodec->codec; + mutex_lock(&gbcodec->lock); - if (reg == SND_SOC_NOPM) - return 0; + if (module->num_dais) { + dev_err(gbcodec->dev, + "%d:DAIs not supported via gbcodec driver\n", + module->num_dais); + mutex_unlock(&gbcodec->lock); + return -EINVAL; + } - if (reg >= GBCODEC_REG_COUNT) - return 0; + if (module->dapm_widgets) + snd_soc_dapm_new_controls(&codec->dapm, module->dapm_widgets, + module->num_dapm_widgets); + if (module->controls) + snd_soc_add_codec_controls(codec, module->controls, + module->num_controls); + if (module->dapm_routes) + snd_soc_dapm_add_routes(&codec->dapm, module->dapm_routes, + module->num_dapm_routes); + + /* card already instantiated, create widgets here only */ + if (codec->card->instantiated) { + ret = snd_soc_dapm_new_widgets(&codec->dapm); + if (!ret) + snd_soc_dapm_link_dai_widgets_component(codec->card, + &codec->dapm); + } - gbcodec_reg[reg] = value; - dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value); + list_add(&module->list, &gbcodec->module_list); + dev_dbg(codec->dev, "Registered %s module\n", module->name); - return ret; + mutex_unlock(&gbcodec->lock); + return 0; } +EXPORT_SYMBOL(gbaudio_register_module); -static unsigned int gbcodec_read(struct snd_soc_codec *codec, - unsigned int reg) +void gbaudio_codec_cleanup(struct gbaudio_module_info *module) { - unsigned int val = 0; - - struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); - u8 *gbcodec_reg = gbcodec->reg; - - if (reg == SND_SOC_NOPM) - return 0; - - if (reg >= GBCODEC_REG_COUNT) - return 0; - - val = gbcodec_reg[reg]; - dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val); - - return val; -} + struct gbaudio_data_connection *data; + int pb_state = gbcodec->stream[0].state; + int cap_state = gbcodec->stream[1].state; + int ret; + uint16_t i2s_port, cportid; -/* - * gb_snd management functions - */ + /* locks already acquired */ + if (!pb_state && !cap_state) + return; -/* XXX - * since BE DAI path is not yet properly closed from above layer, - * dsp dai.mi2s_dai_data.status_mask is still set to STATUS_PORT_STARTED - * this causes immediate playback/capture to fail in case relevant mixer - * control is not turned OFF - * user need to try once again after failure to recover DSP state. - */ -static void gb_audio_cleanup(struct gbaudio_codec_info *gb) -{ - int cportid, ret, timeout_result; - struct gbaudio_dai *gb_dai; - struct gb_connection *connection; - long timeout = msecs_to_jiffies(50); /* 50ms */ - struct device *dev = gb->dev; - - list_for_each_entry(gb_dai, &gb->dai_list, list) { - /* - * In case of BE dailink, need to deactivate APBridge - * manually - */ - if (atomic_read(&gb_dai->users)) { - /* schedule a wait event */ - timeout_result = - wait_event_interruptible_timeout( - gb_dai->wait_queue, - !atomic_read(&gb_dai->users), - timeout); - if (!timeout_result) - dev_warn(dev, "%s:DAI still in use.\n", - gb_dai->name); - - connection = gb_dai->connection; - /* PB active */ - ret = gb_audio_apbridgea_stop_tx(connection, 0); - if (ret) - dev_info(dev, "%d:Failed during APBridge stop_tx\n", - ret); - ret = gb_audio_apbridgea_shutdown_tx(connection, 0); - if (ret) - dev_info(dev, "%d:Failed during APBridge shutdown_tx\n", - ret); - cportid = connection->intf_cport_id; - ret = gb_audio_gb_deactivate_tx(gb->mgmt_connection, - cportid); - if (ret) - dev_info(dev, - "%d:Failed during deactivate_tx\n", - ret); - cportid = connection->hd_cport_id; - ret = gb_audio_apbridgea_unregister_cport(connection, 0, - cportid, + if (pb_state == GBAUDIO_CODEC_START) { + /* cleanup PB path, only APBridge specific */ + data = find_data(module, gbcodec->stream[0].dai_name); + if (!data) { + dev_err(gbcodec->dev, "%s: Missing data pointer\n", + __func__); + return; + } + ret = gb_audio_apbridgea_stop_tx(data->connection, 0); + if (ret) + return; + ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0); + if (ret) + return; + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(data->connection, + i2s_port, cportid, AUDIO_APBRIDGEA_DIRECTION_TX); - if (ret) - dev_info(dev, "%d:Failed during unregister cport\n", - ret); + gbcodec->stream[0].state = GBAUDIO_CODEC_SHUTDOWN; + } + + if (cap_state == GBAUDIO_CODEC_START) { + /* cleanup CAP path, only APBridge specific */ + data = find_data(module, gbcodec->stream[1].dai_name); + if (!data) { + dev_err(gbcodec->dev, "%s: Missing data pointer\n", + __func__); + return; } + ret = gb_audio_apbridgea_stop_rx(data->connection, 0); + if (ret) + return; + ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0); + if (ret) + return; + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); + gbcodec->stream[1].state = GBAUDIO_CODEC_SHUTDOWN; } } -static int gbaudio_register_codec(struct gbaudio_codec_info *gbcodec) +void gbaudio_unregister_module(struct gbaudio_module_info *module) { - int ret, i; - struct device *dev = gbcodec->dev; - struct gb_connection *connection = gbcodec->mgmt_connection; - struct snd_soc_codec_driver *soc_codec_dev_gbcodec; - /* - * FIXME: malloc for topology happens via audio_gb driver - * should be done within codec driver itself - */ - struct gb_audio_topology *topology; + struct snd_soc_codec *codec = gbcodec->codec; + struct snd_card *card = codec->card->snd_card; - soc_codec_dev_gbcodec = devm_kzalloc(gbcodec->dev, - sizeof(*soc_codec_dev_gbcodec), - GFP_KERNEL); - if (!soc_codec_dev_gbcodec) { - dev_err(gbcodec->dev, "Malloc failed for codec_driver\n"); - return -ENOMEM; - } + dev_dbg(codec->dev, "Unregister %s module\n", module->name); - ret = gb_connection_enable(connection); - if (ret) { - dev_err(dev, "%d: Error while enabling mgmt connection\n", ret); - return ret; - } + /* complete widget processing, if ongoing */ + snd_soc_dapm_sync(&codec->dapm); - gbcodec->dev_id = connection->intf->interface_id; - /* fetch topology data */ - ret = gb_audio_gb_get_topology(connection, &topology); - if (ret) { - dev_err(dev, "%d:Error while fetching topology\n", ret); - goto tplg_fetch_err; - } + down_write(&card->controls_rwsem); + mutex_lock(&gbcodec->lock); + dev_dbg(codec->dev, "Process Unregister %s module\n", module->name); + mutex_lock(&module->lock); - /* process topology data */ - ret = gbaudio_tplg_parse_data(gbcodec, topology); - if (ret) { - dev_err(dev, "%d:Error while parsing topology data\n", - ret); - goto tplg_parse_err; + if (list_is_last(&module->list, &gbcodec->module_list)) { + dev_dbg(codec->dev, "Last module removed, cleanup APBridge\n"); + gbaudio_codec_cleanup(module); } - gbcodec->topology = topology; - - /* update codec info */ - soc_codec_dev_gbcodec->probe = gbcodec_probe, - soc_codec_dev_gbcodec->remove = gbcodec_remove, - - soc_codec_dev_gbcodec->read = gbcodec_read, - soc_codec_dev_gbcodec->write = gbcodec_write, - - soc_codec_dev_gbcodec->reg_cache_size = GBCODEC_REG_COUNT, - soc_codec_dev_gbcodec->reg_cache_default = gbcodec_reg_defaults, - soc_codec_dev_gbcodec->reg_word_size = 1, - soc_codec_dev_gbcodec->idle_bias_off = true, - soc_codec_dev_gbcodec->ignore_pmdown_time = 1, - - soc_codec_dev_gbcodec->controls = gbcodec->kctls; - soc_codec_dev_gbcodec->num_controls = gbcodec->num_kcontrols; - soc_codec_dev_gbcodec->dapm_widgets = gbcodec->widgets; - soc_codec_dev_gbcodec->num_dapm_widgets = gbcodec->num_dapm_widgets; - soc_codec_dev_gbcodec->dapm_routes = gbcodec->routes; - soc_codec_dev_gbcodec->num_dapm_routes = gbcodec->num_dapm_routes; - - /* update DAI info */ - for (i = 0; i < gbcodec->num_dais; i++) - gbcodec->dais[i].ops = &gbcodec_dai_ops; - - /* register codec */ - ret = snd_soc_register_codec(dev, soc_codec_dev_gbcodec, - gbcodec->dais, 1); - if (ret) { - dev_err(dev, "%d:Failed to register codec\n", ret); - goto codec_reg_err; + module->is_connected = 0; + if (module->dapm_routes) { + dev_dbg(codec->dev, "Removing %d routes\n", + module->num_dapm_routes); + snd_soc_dapm_del_routes(&codec->dapm, module->dapm_routes, + module->num_dapm_routes); } - - /* update DAI links in response to this codec */ - ret = msm8994_add_dailink("msm8994-tomtom-mtp-snd-card", gbcodec->name, - gbcodec->dais[0].name, 1); - if (ret) { - dev_err(dev, "%d: Failed to add DAI links\n", ret); - goto add_dailink_err; + if (module->controls) { + dev_dbg(codec->dev, "Removing %d controls\n", + module->num_controls); + soc_remove_codec_controls(codec, module->controls, + module->num_controls); + } + if (module->dapm_widgets) { + dev_dbg(codec->dev, "Removing %d widgets\n", + module->num_dapm_widgets); + snd_soc_dapm_free_controls(&codec->dapm, module->dapm_widgets, + module->num_dapm_widgets); } - gbcodec->num_dai_links = 1; - return 0; + mutex_unlock(&module->lock); -add_dailink_err: - snd_soc_unregister_codec(dev); -codec_reg_err: - gbaudio_tplg_release(gbcodec); - gbcodec->topology = NULL; -tplg_parse_err: - kfree(topology); -tplg_fetch_err: - gb_connection_disable(gbcodec->mgmt_connection); - return ret; -} + list_del(&module->list); + dev_dbg(codec->dev, "Unregistered %s module\n", module->name); -static void gbaudio_unregister_codec(struct gbaudio_codec_info *gbcodec) -{ - gb_audio_cleanup(gbcodec); - msm8994_remove_dailink("msm8994-tomtom-mtp-snd-card", gbcodec->name, - gbcodec->dais[0].name, 1); - snd_soc_unregister_codec(gbcodec->dev); - gbaudio_tplg_release(gbcodec); - kfree(gbcodec->topology); - gb_connection_disable(gbcodec->mgmt_connection); + mutex_unlock(&gbcodec->lock); + up_write(&card->controls_rwsem); } +EXPORT_SYMBOL(gbaudio_unregister_module); -static int gbaudio_codec_request_handler(struct gb_operation *op) +/* + * codec driver ops + */ +static int gbcodec_probe(struct snd_soc_codec *codec) { - struct gb_connection *connection = op->connection; - struct gb_audio_streaming_event_request *req = op->request->payload; + struct gbaudio_codec_info *info; + + info = devm_kzalloc(codec->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; - dev_warn(&connection->bundle->dev, - "Audio Event received: cport: %u, event: %u\n", - req->data_cport, req->event); + info->dev = codec->dev; + INIT_LIST_HEAD(&info->module_list); + mutex_init(&info->lock); + info->codec = codec; + snd_soc_codec_set_drvdata(codec, info); + gbcodec = info; + /* Empty function for now */ return 0; } -static int gbaudio_dai_request_handler(struct gb_operation *op) +static int gbcodec_remove(struct snd_soc_codec *codec) { - struct gb_connection *connection = op->connection; - - dev_warn(&connection->bundle->dev, "Audio Event received\n"); - + /* Empty function for now */ return 0; } -static int gb_audio_add_mgmt_connection(struct gbaudio_codec_info *gbcodec, - struct greybus_descriptor_cport *cport_desc, - struct gb_bundle *bundle) +static u8 gbcodec_reg[GBCODEC_REG_COUNT] = { + [GBCODEC_CTL_REG] = GBCODEC_CTL_REG_DEFAULT, + [GBCODEC_MUTE_REG] = GBCODEC_MUTE_REG_DEFAULT, + [GBCODEC_PB_LVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT, + [GBCODEC_PB_RVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT, + [GBCODEC_CAP_LVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT, + [GBCODEC_CAP_RVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT, + [GBCODEC_APB1_MUX_REG] = GBCODEC_APB1_MUX_REG_DEFAULT, + [GBCODEC_APB2_MUX_REG] = GBCODEC_APB2_MUX_REG_DEFAULT, +}; + +static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) { - struct gb_connection *connection; + int ret = 0; - /* Management Cport */ - if (gbcodec->mgmt_connection) { - dev_err(&bundle->dev, - "Can't have multiple Management connections\n"); - return -ENODEV; - } + if (reg == SND_SOC_NOPM) + return 0; - connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), - gbaudio_codec_request_handler); - if (IS_ERR(connection)) - return PTR_ERR(connection); + BUG_ON(reg >= GBCODEC_REG_COUNT); + return 0; - gb_connection_set_data(connection, gbcodec); - gbcodec->mgmt_connection = connection; + gbcodec_reg[reg] = value; + dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value); - return 0; + return ret; } -static int gb_audio_add_data_connection(struct gbaudio_codec_info *gbcodec, - struct greybus_descriptor_cport *cport_desc, - struct gb_bundle *bundle) +static unsigned int gbcodec_read(struct snd_soc_codec *codec, + unsigned int reg) { - struct gb_connection *connection; - struct gbaudio_dai *dai; + unsigned int val = 0; - dai = devm_kzalloc(gbcodec->dev, sizeof(*dai), GFP_KERNEL); - if (!dai) { - dev_err(gbcodec->dev, "DAI Malloc failure\n"); - return -ENOMEM; - } + if (reg == SND_SOC_NOPM) + return 0; - connection = gb_connection_create_flags(bundle, - le16_to_cpu(cport_desc->id), - gbaudio_dai_request_handler, - GB_CONNECTION_FLAG_CSD); - if (IS_ERR(connection)) { - devm_kfree(gbcodec->dev, dai); - return PTR_ERR(connection); - } + BUG_ON(reg >= GBCODEC_REG_COUNT); - gb_connection_set_data(connection, gbcodec); - atomic_set(&dai->users, 0); - init_waitqueue_head(&dai->wait_queue); - dai->data_cport = connection->intf_cport_id; - dai->connection = connection; - list_add(&dai->list, &gbcodec->dai_list); + val = gbcodec_reg[reg]; + dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val); - return 0; + return val; } -/* - * This is the basic hook get things initialized and registered w/ gb - */ -static int gb_audio_probe(struct gb_bundle *bundle, - const struct greybus_bundle_id *id) -{ - struct device *dev = &bundle->dev; - struct gbaudio_codec_info *gbcodec; - struct greybus_descriptor_cport *cport_desc; - struct gb_audio_manager_module_descriptor desc; - struct gbaudio_dai *dai, *_dai; - int ret, i; - - /* There should be at least one Management and one Data cport */ - if (bundle->num_cports < 2) - return -ENODEV; +static struct snd_soc_dai_driver gbaudio_dai[] = { + { + .name = "greybus-apb1", + .id = 0, + .playback = { + .stream_name = "GB Audio Playback", + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FORMAT_S16_LE, + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .capture = { + .stream_name = "GB Audio Capture", + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FORMAT_S16_LE, + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &gbcodec_dai_ops, + }, +}; - mutex_lock(&gb_codec_list_lock); - /* - * There can be only one Management connection and any number of data - * connections. - */ - gbcodec = devm_kzalloc(dev, sizeof(*gbcodec), GFP_KERNEL); - if (!gbcodec) { - mutex_unlock(&gb_codec_list_lock); - return -ENOMEM; - } +static struct snd_soc_codec_driver soc_codec_dev_gbaudio = { + .probe = gbcodec_probe, + .remove = gbcodec_remove, - gbcodec->num_data_connections = bundle->num_cports - 1; - mutex_init(&gbcodec->lock); - INIT_LIST_HEAD(&gbcodec->dai_list); - INIT_LIST_HEAD(&gbcodec->widget_list); - INIT_LIST_HEAD(&gbcodec->codec_ctl_list); - INIT_LIST_HEAD(&gbcodec->widget_ctl_list); - gbcodec->dev = dev; - snprintf(gbcodec->name, NAME_SIZE, "%s.%s", dev->driver->name, - dev_name(dev)); - greybus_set_drvdata(bundle, gbcodec); - - /* Create all connections */ - for (i = 0; i < bundle->num_cports; i++) { - cport_desc = &bundle->cport_desc[i]; - - switch (cport_desc->protocol_id) { - case GREYBUS_PROTOCOL_AUDIO_MGMT: - ret = gb_audio_add_mgmt_connection(gbcodec, cport_desc, - bundle); - if (ret) - goto destroy_connections; - break; - case GREYBUS_PROTOCOL_AUDIO_DATA: - ret = gb_audio_add_data_connection(gbcodec, cport_desc, - bundle); - if (ret) - goto destroy_connections; - break; - default: - dev_err(dev, "Unsupported protocol: 0x%02x\n", - cport_desc->protocol_id); - ret = -ENODEV; - goto destroy_connections; - } - } + .read = gbcodec_read, + .write = gbcodec_write, - /* There must be a management cport */ - if (!gbcodec->mgmt_connection) { - ret = -EINVAL; - dev_err(dev, "Missing management connection\n"); - goto destroy_connections; - } + .reg_cache_size = GBCODEC_REG_COUNT, + .reg_cache_default = gbcodec_reg_defaults, + .reg_word_size = 1, - /* Initialize management connection */ - ret = gbaudio_register_codec(gbcodec); - if (ret) - goto destroy_connections; - - /* Initialize data connections */ - list_for_each_entry(dai, &gbcodec->dai_list, list) { - ret = gb_connection_enable(dai->connection); - if (ret) - goto remove_dai; - } - - /* inform above layer for uevent */ - dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); - /* prepare for the audio manager */ - strlcpy(desc.name, gbcodec->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN); - desc.slot = 1; /* todo */ - desc.vid = 2; /* todo */ - desc.pid = 3; /* todo */ - desc.cport = gbcodec->dev_id; - desc.devices = 0x2; /* todo */ - gbcodec->manager_id = gb_audio_manager_add(&desc); + .idle_bias_off = true, + .ignore_pmdown_time = 1, +}; - atomic_set(&gbcodec->is_connected, 1); - list_add_tail(&gbcodec->list, &gb_codec_list); - dev_dbg(dev, "Add GB Audio device:%s\n", gbcodec->name); - mutex_unlock(&gb_codec_list_lock); +#ifdef CONFIG_PM +static int gbaudio_codec_suspend(struct device *dev) +{ + dev_dbg(dev, "%s: suspend\n", __func__); return 0; - -remove_dai: - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) - gb_connection_disable(dai->connection); - - gbaudio_unregister_codec(gbcodec); -destroy_connections: - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { - gb_connection_destroy(dai->connection); - list_del(&dai->list); - devm_kfree(dev, dai); - } - - if (gbcodec->mgmt_connection) - gb_connection_destroy(gbcodec->mgmt_connection); - - devm_kfree(dev, gbcodec); - mutex_unlock(&gb_codec_list_lock); - - return ret; } -static void gb_audio_disconnect(struct gb_bundle *bundle) +static int gbaudio_codec_resume(struct device *dev) { - struct gbaudio_codec_info *gbcodec = greybus_get_drvdata(bundle); - struct gbaudio_dai *dai, *_dai; + dev_dbg(dev, "%s: resume\n", __func__); + return 0; +} - mutex_lock(&gb_codec_list_lock); - atomic_set(&gbcodec->is_connected, 0); - /* inform uevent to above layers */ - gb_audio_manager_remove(gbcodec->manager_id); +static const struct dev_pm_ops gbaudio_codec_pm_ops = { + .suspend = gbaudio_codec_suspend, + .resume = gbaudio_codec_resume, +}; +#endif - mutex_lock(&gbcodec->lock); - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) - gb_connection_disable(dai->connection); - gbaudio_unregister_codec(gbcodec); - - list_for_each_entry_safe(dai, _dai, &gbcodec->dai_list, list) { - gb_connection_destroy(dai->connection); - list_del(&dai->list); - devm_kfree(gbcodec->dev, dai); - } - gb_connection_destroy(gbcodec->mgmt_connection); - gbcodec->mgmt_connection = NULL; - list_del(&gbcodec->list); - mutex_unlock(&gbcodec->lock); +static int gbaudio_codec_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbaudio, + gbaudio_dai, ARRAY_SIZE(gbaudio_dai)); +} - devm_kfree(&bundle->dev, gbcodec); - mutex_unlock(&gb_codec_list_lock); +static int gbaudio_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; } -static const struct greybus_bundle_id gb_audio_id_table[] = { - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) }, - { } +static const struct of_device_id greybus_asoc_machine_of_match[] = { + { .compatible = "qcom,ara-codec", }, + {}, }; -MODULE_DEVICE_TABLE(greybus, gb_audio_id_table); -static struct greybus_driver gb_audio_driver = { - .name = "gb-audio", - .probe = gb_audio_probe, - .disconnect = gb_audio_disconnect, - .id_table = gb_audio_id_table, +static struct platform_driver gbaudio_codec_driver = { + .driver = { + .name = "gb-codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &gbaudio_codec_pm_ops, +#endif + .of_match_table = greybus_asoc_machine_of_match, + }, + .probe = gbaudio_codec_probe, + .remove = gbaudio_codec_remove, }; -module_greybus_driver(gb_audio_driver); +module_platform_driver(gbaudio_codec_driver); -MODULE_DESCRIPTION("Greybus Audio codec driver"); +MODULE_DESCRIPTION("Greybus codec driver"); MODULE_AUTHOR("Vaibhav Agarwal "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:gbaudio-codec"); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 9197adcbc32e..a2697dd62949 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -68,6 +68,31 @@ static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { GBCODEC_APB2_MUX_REG_DEFAULT, }; +enum gbaudio_codec_state { + GBAUDIO_CODEC_SHUTDOWN = 0, + GBAUDIO_CODEC_STARTUP, + GBAUDIO_CODEC_HWPARAMS, + GBAUDIO_CODEC_PREPARE, + GBAUDIO_CODEC_START, + GBAUDIO_CODEC_STOP, +}; + +struct gbaudio_stream { + const char *dai_name; + int state; + uint8_t sig_bits, channels; + uint32_t format, rate; +}; + +struct gbaudio_codec_info { + struct device *dev; + struct snd_soc_codec *codec; + struct list_head module_list; + struct gbaudio_stream stream[2]; /* PB/CAP */ + struct mutex lock; + u8 reg[GBCODEC_REG_COUNT]; +}; + struct gbaudio_widget { __u8 id; const char *name; @@ -81,81 +106,80 @@ struct gbaudio_control { struct list_head list; }; -struct gbaudio_dai { +struct gbaudio_data_connection { __le16 data_cport; + int cport_configured; char name[NAME_SIZE]; - /* DAI users */ - atomic_t users; struct gb_connection *connection; struct list_head list; - wait_queue_head_t wait_queue; }; -struct gbaudio_codec_info { +/* stream direction */ +#define GB_PLAYBACK BIT(0) +#define GB_CAPTURE BIT(1) + +enum gbaudio_module_state { + GBAUDIO_MODULE_OFF = 0, + GBAUDIO_MODULE_ON, +}; + +struct gbaudio_module_info { /* module info */ + struct device *dev; int dev_id; /* check if it should be bundle_id/hd_cport_id */ int vid; int pid; int slot; int type; - int dai_added; - int codec_registered; int set_uevent; char vstr[NAME_SIZE]; char pstr[NAME_SIZE]; struct list_head list; - struct gb_audio_topology *topology; /* need to share this info to above user space */ int manager_id; char name[NAME_SIZE]; - /* - * there can be a rece condition between gb_audio_disconnect() - * and dai->trigger from above ASoC layer. - * To avoid any deadlock over codec_info->lock, atomic variable - * is used. - */ - atomic_t is_connected; + /* used by codec_ops */ struct mutex lock; + int is_connected; + int ctrlstate[2]; /* PB/CAP */ - /* soc related data */ - struct snd_soc_codec *codec; - struct device *dev; - u8 reg[GBCODEC_REG_COUNT]; - - /* dai_link related */ - char card_name[NAME_SIZE]; - char *dailink_name[MAX_DAIS]; - int num_dai_links; - + /* connection info */ struct gb_connection *mgmt_connection; size_t num_data_connections; + struct list_head data_list; + /* topology related */ int num_dais; - int num_kcontrols; + int num_controls; int num_dapm_widgets; int num_dapm_routes; unsigned long dai_offset; unsigned long widget_offset; unsigned long control_offset; unsigned long route_offset; - struct snd_kcontrol_new *kctls; - struct snd_soc_dapm_widget *widgets; - struct snd_soc_dapm_route *routes; + struct snd_kcontrol_new *controls; + struct snd_soc_dapm_widget *dapm_widgets; + struct snd_soc_dapm_route *dapm_routes; struct snd_soc_dai_driver *dais; - /* lists */ - struct list_head dai_list; struct list_head widget_list; - struct list_head codec_ctl_list; + struct list_head ctl_list; struct list_head widget_ctl_list; + + struct gb_audio_topology *topology; }; -struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, - int data_cport, const char *name); -int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, +int gbaudio_tplg_parse_data(struct gbaudio_module_info *module, struct gb_audio_topology *tplg_data); -void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec); +void gbaudio_tplg_release(struct gbaudio_module_info *module); + +int gbaudio_module_update(struct gbaudio_codec_info *codec, + const char *w_name, + struct gbaudio_module_info *module, + int enable); +int gbaudio_register_module(struct gbaudio_module_info *module); +void gbaudio_unregister_module(struct gbaudio_module_info *module); /* protocol related */ extern int gb_audio_gb_get_topology(struct gb_connection *connection, diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 1651c14c87ba..4901348a2ada 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -25,7 +25,32 @@ struct gbaudio_ctl_pvt { struct gb_audio_ctl_elem_info *info; }; -static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec, +static struct gbaudio_module_info *find_gb_module( + struct gbaudio_codec_info *codec, + char const *name) +{ + int dev_id, ret; + char begin[NAME_SIZE]; + struct gbaudio_module_info *module; + + if (!name) + return NULL; + + ret = sscanf(name, "%s %d", begin, &dev_id); + dev_dbg(codec->dev, "%s:Find module#%d\n", __func__, dev_id); + + mutex_lock(&codec->lock); + list_for_each_entry(module, &codec->module_list, list) { + if (module->dev_id == dev_id) { + mutex_unlock(&codec->lock); + return module; + } + } + mutex_unlock(&codec->lock); + return NULL; +} + +static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, __u8 control_id, __u8 index) { struct gbaudio_control *control; @@ -33,14 +58,14 @@ static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec, if (control_id == GBAUDIO_INVALID_ID) return NULL; - list_for_each_entry(control, &gbcodec->codec_ctl_list, list) { + list_for_each_entry(control, &module->ctl_list, list) { if (control->id == control_id) { if (index == GBAUDIO_INVALID_ID) return control->name; return control->texts[index]; } } - list_for_each_entry(control, &gbcodec->widget_ctl_list, list) { + list_for_each_entry(control, &module->widget_ctl_list, list) { if (control->id == control_id) { if (index == GBAUDIO_INVALID_ID) return control->name; @@ -50,34 +75,23 @@ static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec, return NULL; } -static int gbaudio_map_widgetname(struct gbaudio_codec_info *gbcodec, +static int gbaudio_map_widgetname(struct gbaudio_module_info *module, const char *name) { struct gbaudio_widget *widget; - char widget_name[NAME_SIZE]; - char prefix_name[NAME_SIZE]; - - snprintf(prefix_name, NAME_SIZE, "GB %d ", gbcodec->dev_id); - if (strncmp(name, prefix_name, strlen(prefix_name))) - return -EINVAL; - - strlcpy(widget_name, name+strlen(prefix_name), NAME_SIZE); - dev_dbg(gbcodec->dev, "widget_name:%s, truncated widget_name:%s\n", - name, widget_name); - - list_for_each_entry(widget, &gbcodec->widget_list, list) { - if (!strncmp(widget->name, widget_name, NAME_SIZE)) + list_for_each_entry(widget, &module->widget_list, list) { + if (!strncmp(widget->name, name, NAME_SIZE)) return widget->id; } return -EINVAL; } -static const char *gbaudio_map_widgetid(struct gbaudio_codec_info *gbcodec, +static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module, __u8 widget_id) { struct gbaudio_widget *widget; - list_for_each_entry(widget, &gbcodec->widget_list, list) { + list_for_each_entry(widget, &module->widget_list, list) { if (widget->id == widget_id) return widget->name; } @@ -91,14 +105,16 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, const char *name; struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_info *info; + struct gbaudio_module_info *module; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; if (!info) { - dev_err(gbcodec->dev, "NULL info for %s\n", uinfo->id.name); + dev_err(module->dev, "NULL info for %s\n", uinfo->id.name); return -EINVAL; } @@ -118,7 +134,10 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = max; if (uinfo->value.enumerated.item > max - 1) uinfo->value.enumerated.item = max - 1; - name = gbaudio_map_controlid(gbcodec, data->ctl_id, + module = find_gb_module(gbcodec, kcontrol->id.name); + if (!module) + return -EINVAL; + name = gbaudio_map_controlid(module, data->ctl_id, uinfo->value.enumerated.item); strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE); break; @@ -137,16 +156,19 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, struct gb_audio_ctl_elem_info *info; struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); - if (!atomic_read(&gb->is_connected)) - return -ENODEV; + dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; - ret = gb_audio_gb_get_control(gb->mgmt_connection, data->ctl_id, + ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); if (ret) { dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, @@ -187,11 +209,14 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, struct gb_audio_ctl_elem_info *info; struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); - if (!atomic_read(&gb->is_connected)) - return -ENODEV; + dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -223,7 +248,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, if (ret) return ret; - ret = gb_audio_gb_set_control(gb->mgmt_connection, data->ctl_id, + ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); if (ret) { dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, @@ -250,7 +275,11 @@ static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol, int platform_max, platform_min; struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_info *info; + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; + dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -282,13 +311,16 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, struct gb_audio_ctl_elem_info *info; struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); - if (!atomic_read(&gb->is_connected)) - return -ENODEV; + dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -298,7 +330,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, "GB: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); - ret = gb_audio_gb_get_control(gb->mgmt_connection, data->ctl_id, + ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); if (ret) { dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, @@ -319,13 +351,16 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, struct gb_audio_ctl_elem_info *info; struct gbaudio_ctl_pvt *data; struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); - if (!atomic_read(&gb->is_connected)) - return -ENODEV; + dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -352,7 +387,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, } gbvalue.value.integer_value[0] = ucontrol->value.integer.value[0]; - ret = gb_audio_gb_set_control(gb->mgmt_connection, + ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); if (ret) { @@ -420,7 +455,7 @@ static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w) return ret; } -static int gbaudio_tplg_create_kcontrol(struct gbaudio_codec_info *gb, +static int gbaudio_tplg_create_kcontrol(struct gbaudio_module_info *gb, struct snd_kcontrol_new *kctl, struct gb_audio_control *ctl) { @@ -452,23 +487,23 @@ static int gbaudio_tplg_create_kcontrol(struct gbaudio_codec_info *gb, static const char * const gbtexts[] = {"Stereo", "Left", "Right"}; static const SOC_ENUM_SINGLE_DECL( - gbcodec_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbtexts); + module_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbtexts); static const SOC_ENUM_SINGLE_DECL( - gbcodec_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbtexts); + module_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbtexts); -static int gbaudio_tplg_create_enum_ctl(struct gbaudio_codec_info *gb, +static int gbaudio_tplg_create_enum_ctl(struct gbaudio_module_info *gb, struct snd_kcontrol_new *kctl, struct gb_audio_control *ctl) { switch (ctl->id) { case 8: *kctl = (struct snd_kcontrol_new) - SOC_DAPM_ENUM(ctl->name, gbcodec_apb1_rx_enum); + SOC_DAPM_ENUM(ctl->name, module_apb1_rx_enum); break; case 9: *kctl = (struct snd_kcontrol_new) - SOC_DAPM_ENUM(ctl->name, gbcodec_mic_enum); + SOC_DAPM_ENUM(ctl->name, module_mic_enum); break; default: return -EINVAL; @@ -477,7 +512,7 @@ static int gbaudio_tplg_create_enum_ctl(struct gbaudio_codec_info *gb, return 0; } -static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_codec_info *gb, +static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_module_info *gb, struct snd_kcontrol_new *kctl, struct gb_audio_control *ctl) { @@ -498,7 +533,7 @@ static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_codec_info *gb, return 0; } -static int gbaudio_tplg_create_wcontrol(struct gbaudio_codec_info *gb, +static int gbaudio_tplg_create_wcontrol(struct gbaudio_module_info *gb, struct snd_kcontrol_new *kctl, struct gb_audio_control *ctl) { @@ -532,11 +567,17 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, int ret; struct snd_soc_codec *codec = w->codec; struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); + struct gbaudio_module_info *module; dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + /* Find relevant module */ + module = find_gb_module(gbcodec, w->name); + if (!module) + return -EINVAL; + /* map name to widget id */ - wid = gbaudio_map_widgetname(gbcodec, w->name); + wid = gbaudio_map_widgetname(module, w->name); if (wid < 0) { dev_err(codec->dev, "Invalid widget name:%s\n", w->name); return -EINVAL; @@ -544,10 +585,16 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - ret = gb_audio_gb_enable_widget(gbcodec->mgmt_connection, wid); + ret = gb_audio_gb_enable_widget(module->mgmt_connection, wid); + if (!ret) + ret = gbaudio_module_update(gbcodec, w->name, module, + 1); break; case SND_SOC_DAPM_POST_PMD: - ret = gb_audio_gb_disable_widget(gbcodec->mgmt_connection, wid); + ret = gb_audio_gb_disable_widget(module->mgmt_connection, wid); + if (!ret) + ret = gbaudio_module_update(gbcodec, w->name, module, + 0); break; } if (ret) @@ -556,7 +603,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, return ret; } -static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, +static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, struct snd_soc_dapm_widget *dw, struct gb_audio_widget *w) { @@ -565,10 +612,11 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, struct gb_audio_control *curr; struct gbaudio_control *control, *_control; size_t size; + char temp_name[NAME_SIZE]; ret = gbaudio_validate_kcontrol_count(w); if (ret) { - dev_err(gbcodec->dev, "Inavlid kcontrol count=%d for %s\n", + dev_err(module->dev, "Inavlid kcontrol count=%d for %s\n", w->ncontrols, w->name); return ret; } @@ -576,7 +624,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, /* allocate memory for kcontrol */ if (w->ncontrols) { size = sizeof(struct snd_kcontrol_new) * w->ncontrols; - widget_kctls = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); + widget_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL); if (!widget_kctls) return -ENOMEM; } @@ -584,15 +632,15 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, /* create relevant kcontrols */ for (i = 0; i < w->ncontrols; i++) { curr = &w->ctl[i]; - ret = gbaudio_tplg_create_wcontrol(gbcodec, &widget_kctls[i], + ret = gbaudio_tplg_create_wcontrol(module, &widget_kctls[i], curr); if (ret) { - dev_err(gbcodec->dev, + dev_err(module->dev, "%s:%d type widget_ctl not supported\n", curr->name, curr->iface); goto error; } - control = devm_kzalloc(gbcodec->dev, + control = devm_kzalloc(module->dev, sizeof(struct gbaudio_control), GFP_KERNEL); if (!control) { @@ -604,11 +652,15 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) control->texts = (const char * const *) curr->info.value.enumerated.names; - list_add(&control->list, &gbcodec->widget_ctl_list); - dev_dbg(gbcodec->dev, "%s: control of type %d created\n", + list_add(&control->list, &module->widget_ctl_list); + dev_dbg(module->dev, "%s: control of type %d created\n", widget_kctls[i].name, widget_kctls[i].iface); } + /* Prefix dev_id to widget control_name */ + strlcpy(temp_name, w->name, NAME_SIZE); + snprintf(w->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name); + switch (w->type) { case snd_soc_dapm_spk: *dw = (struct snd_soc_dapm_widget) @@ -677,45 +729,19 @@ static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, goto error; } - dev_dbg(gbcodec->dev, "%s: widget of type %d created\n", dw->name, + dev_dbg(module->dev, "%s: widget of type %d created\n", dw->name, dw->id); return 0; error: - list_for_each_entry_safe(control, _control, &gbcodec->widget_ctl_list, + list_for_each_entry_safe(control, _control, &module->widget_ctl_list, list) { list_del(&control->list); - devm_kfree(gbcodec->dev, control); + devm_kfree(module->dev, control); } return ret; } -static int gbaudio_tplg_create_dai(struct gbaudio_codec_info *gbcodec, - struct snd_soc_dai_driver *gb_dai, - struct gb_audio_dai *dai) -{ - /* - * do not update name here, - * append dev_id before assigning it here - */ - - gb_dai->playback.stream_name = dai->playback.stream_name; - gb_dai->playback.channels_min = dai->playback.chan_min; - gb_dai->playback.channels_max = dai->playback.chan_max; - gb_dai->playback.formats = dai->playback.formats; - gb_dai->playback.rates = dai->playback.rates; - gb_dai->playback.sig_bits = dai->playback.sig_bits; - - gb_dai->capture.stream_name = dai->capture.stream_name; - gb_dai->capture.channels_min = dai->capture.chan_min; - gb_dai->capture.channels_max = dai->capture.chan_max; - gb_dai->capture.formats = dai->capture.formats; - gb_dai->capture.rates = dai->capture.rates; - gb_dai->capture.sig_bits = dai->capture.sig_bits; - - return 0; -} - -static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec, +static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module, struct gb_audio_control *controls) { int i, ret; @@ -723,22 +749,23 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec, struct gb_audio_control *curr; struct gbaudio_control *control, *_control; size_t size; + char temp_name[NAME_SIZE]; - size = sizeof(struct snd_kcontrol_new) * gbcodec->num_kcontrols; - dapm_kctls = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); + size = sizeof(struct snd_kcontrol_new) * module->num_controls; + dapm_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL); if (!dapm_kctls) return -ENOMEM; curr = controls; - for (i = 0; i < gbcodec->num_kcontrols; i++) { - ret = gbaudio_tplg_create_kcontrol(gbcodec, &dapm_kctls[i], + for (i = 0; i < module->num_controls; i++) { + ret = gbaudio_tplg_create_kcontrol(module, &dapm_kctls[i], curr); if (ret) { - dev_err(gbcodec->dev, "%s:%d type not supported\n", + dev_err(module->dev, "%s:%d type not supported\n", curr->name, curr->iface); goto error; } - control = devm_kzalloc(gbcodec->dev, sizeof(struct + control = devm_kzalloc(module->dev, sizeof(struct gbaudio_control), GFP_KERNEL); if (!control) { @@ -746,29 +773,33 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec, goto error; } control->id = curr->id; + /* Prefix dev_id to widget_name */ + strlcpy(temp_name, curr->name, NAME_SIZE); + snprintf(curr->name, NAME_SIZE, "GB %d %s", module->dev_id, + temp_name); control->name = curr->name; if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) control->texts = (const char * const *) curr->info.value.enumerated.names; - list_add(&control->list, &gbcodec->codec_ctl_list); - dev_dbg(gbcodec->dev, "%d:%s created of type %d\n", curr->id, + list_add(&control->list, &module->ctl_list); + dev_dbg(module->dev, "%d:%s created of type %d\n", curr->id, curr->name, curr->info.type); curr++; } - gbcodec->kctls = dapm_kctls; + module->controls = dapm_kctls; return 0; error: - list_for_each_entry_safe(control, _control, &gbcodec->codec_ctl_list, + list_for_each_entry_safe(control, _control, &module->ctl_list, list) { list_del(&control->list); - devm_kfree(gbcodec->dev, control); + devm_kfree(module->dev, control); } - devm_kfree(gbcodec->dev, dapm_kctls); + devm_kfree(module->dev, dapm_kctls); return ret; } -static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec, +static int gbaudio_tplg_process_widgets(struct gbaudio_module_info *module, struct gb_audio_widget *widgets) { int i, ret, ncontrols; @@ -777,21 +808,21 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec, struct gbaudio_widget *widget, *_widget; size_t size; - size = sizeof(struct snd_soc_dapm_widget) * gbcodec->num_dapm_widgets; - dapm_widgets = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); + size = sizeof(struct snd_soc_dapm_widget) * module->num_dapm_widgets; + dapm_widgets = devm_kzalloc(module->dev, size, GFP_KERNEL); if (!dapm_widgets) return -ENOMEM; curr = widgets; - for (i = 0; i < gbcodec->num_dapm_widgets; i++) { - ret = gbaudio_tplg_create_widget(gbcodec, &dapm_widgets[i], + for (i = 0; i < module->num_dapm_widgets; i++) { + ret = gbaudio_tplg_create_widget(module, &dapm_widgets[i], curr); if (ret) { - dev_err(gbcodec->dev, "%s:%d type not supported\n", + dev_err(module->dev, "%s:%d type not supported\n", curr->name, curr->type); goto error; } - widget = devm_kzalloc(gbcodec->dev, sizeof(struct + widget = devm_kzalloc(module->dev, sizeof(struct gbaudio_widget), GFP_KERNEL); if (!widget) { @@ -800,69 +831,26 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec, } widget->id = curr->id; widget->name = curr->name; - list_add(&widget->list, &gbcodec->widget_list); + list_add(&widget->list, &module->widget_list); ncontrols = curr->ncontrols; curr++; curr += ncontrols * sizeof(struct gb_audio_control); } - gbcodec->widgets = dapm_widgets; + module->dapm_widgets = dapm_widgets; return 0; error: - list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list, + list_for_each_entry_safe(widget, _widget, &module->widget_list, list) { list_del(&widget->list); - devm_kfree(gbcodec->dev, widget); - } - devm_kfree(gbcodec->dev, dapm_widgets); - return ret; -} - -static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, - struct gb_audio_dai *dais) -{ - int i, ret; - struct snd_soc_dai_driver *gb_dais; - struct gb_audio_dai *curr; - size_t size; - char dai_name[NAME_SIZE]; - struct gbaudio_dai *dai; - - size = sizeof(struct snd_soc_dai_driver) * gbcodec->num_dais; - gb_dais = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); - if (!gb_dais) - return -ENOMEM; - - curr = dais; - for (i = 0; i < gbcodec->num_dais; i++) { - ret = gbaudio_tplg_create_dai(gbcodec, &gb_dais[i], curr); - if (ret) { - dev_err(gbcodec->dev, "%s failed to create\n", - curr->name); - goto error; - } - /* append dev_id to dai_name */ - snprintf(dai_name, NAME_SIZE, "%s.%d", curr->name, - gbcodec->dev_id); - dai = gbaudio_find_dai(gbcodec, curr->data_cport, NULL); - if (!dai) - goto error; - strlcpy(dai->name, dai_name, NAME_SIZE); - dev_dbg(gbcodec->dev, "%s:DAI added\n", dai->name); - gb_dais[i].name = dai->name; - curr++; + devm_kfree(module->dev, widget); } - gbcodec->dais = gb_dais; - - return 0; - -error: - devm_kfree(gbcodec->dev, gb_dais); + devm_kfree(module->dev, dapm_widgets); return ret; } -static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec, +static int gbaudio_tplg_process_routes(struct gbaudio_module_info *module, struct gb_audio_route *routes) { int i, ret; @@ -870,47 +858,47 @@ static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec, struct gb_audio_route *curr; size_t size; - size = sizeof(struct snd_soc_dapm_route) * gbcodec->num_dapm_routes; - dapm_routes = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); + size = sizeof(struct snd_soc_dapm_route) * module->num_dapm_routes; + dapm_routes = devm_kzalloc(module->dev, size, GFP_KERNEL); if (!dapm_routes) return -ENOMEM; - gbcodec->routes = dapm_routes; + module->dapm_routes = dapm_routes; curr = routes; - for (i = 0; i < gbcodec->num_dapm_routes; i++) { + for (i = 0; i < module->num_dapm_routes; i++) { dapm_routes->sink = - gbaudio_map_widgetid(gbcodec, curr->destination_id); + gbaudio_map_widgetid(module, curr->destination_id); if (!dapm_routes->sink) { - dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid sink\n", + dev_err(module->dev, "%d:%d:%d:%d - Invalid sink\n", curr->source_id, curr->destination_id, curr->control_id, curr->index); ret = -EINVAL; goto error; } dapm_routes->source = - gbaudio_map_widgetid(gbcodec, curr->source_id); + gbaudio_map_widgetid(module, curr->source_id); if (!dapm_routes->source) { - dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid source\n", + dev_err(module->dev, "%d:%d:%d:%d - Invalid source\n", curr->source_id, curr->destination_id, curr->control_id, curr->index); ret = -EINVAL; goto error; } dapm_routes->control = - gbaudio_map_controlid(gbcodec, + gbaudio_map_controlid(module, curr->control_id, curr->index); if ((curr->control_id != GBAUDIO_INVALID_ID) && !dapm_routes->control) { - dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid control\n", + dev_err(module->dev, "%d:%d:%d:%d - Invalid control\n", curr->source_id, curr->destination_id, curr->control_id, curr->index); ret = -EINVAL; goto error; } - dev_dbg(gbcodec->dev, "Route {%s, %s, %s}\n", dapm_routes->sink, + dev_dbg(module->dev, "Route {%s, %s, %s}\n", dapm_routes->sink, (dapm_routes->control) ? dapm_routes->control:"NULL", dapm_routes->source); dapm_routes++; @@ -920,41 +908,39 @@ static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec, return 0; error: - devm_kfree(gbcodec->dev, dapm_routes); + devm_kfree(module->dev, dapm_routes); return ret; } -static int gbaudio_tplg_process_header(struct gbaudio_codec_info *gbcodec, +static int gbaudio_tplg_process_header(struct gbaudio_module_info *module, struct gb_audio_topology *tplg_data) { /* fetch no. of kcontrols, widgets & routes */ - gbcodec->num_dais = tplg_data->num_dais; - gbcodec->num_kcontrols = tplg_data->num_controls; - gbcodec->num_dapm_widgets = tplg_data->num_widgets; - gbcodec->num_dapm_routes = tplg_data->num_routes; + module->num_controls = tplg_data->num_controls; + module->num_dapm_widgets = tplg_data->num_widgets; + module->num_dapm_routes = tplg_data->num_routes; /* update block offset */ - gbcodec->dai_offset = (unsigned long)&tplg_data->data; - gbcodec->control_offset = gbcodec->dai_offset + tplg_data->size_dais; - gbcodec->widget_offset = gbcodec->control_offset + + module->dai_offset = (unsigned long)&tplg_data->data; + module->control_offset = module->dai_offset + tplg_data->size_dais; + module->widget_offset = module->control_offset + tplg_data->size_controls; - gbcodec->route_offset = gbcodec->widget_offset + + module->route_offset = module->widget_offset + tplg_data->size_widgets; - dev_dbg(gbcodec->dev, "DAI offset is 0x%lx\n", gbcodec->dai_offset); - dev_dbg(gbcodec->dev, "control offset is %lx\n", - gbcodec->control_offset); - dev_dbg(gbcodec->dev, "widget offset is %lx\n", gbcodec->widget_offset); - dev_dbg(gbcodec->dev, "route offset is %lx\n", gbcodec->route_offset); + dev_dbg(module->dev, "DAI offset is 0x%lx\n", module->dai_offset); + dev_dbg(module->dev, "control offset is %lx\n", + module->control_offset); + dev_dbg(module->dev, "widget offset is %lx\n", module->widget_offset); + dev_dbg(module->dev, "route offset is %lx\n", module->route_offset); return 0; } -int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, +int gbaudio_tplg_parse_data(struct gbaudio_module_info *module, struct gb_audio_topology *tplg_data) { int ret; - struct gb_audio_dai *dais; struct gb_audio_control *controls; struct gb_audio_widget *widgets; struct gb_audio_route *routes; @@ -962,90 +948,80 @@ int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, if (!tplg_data) return -EINVAL; - ret = gbaudio_tplg_process_header(gbcodec, tplg_data); + ret = gbaudio_tplg_process_header(module, tplg_data); if (ret) { - dev_err(gbcodec->dev, "%d: Error in parsing topology header\n", + dev_err(module->dev, "%d: Error in parsing topology header\n", ret); return ret; } /* process control */ - controls = (struct gb_audio_control *)gbcodec->control_offset; - ret = gbaudio_tplg_process_kcontrols(gbcodec, controls); + controls = (struct gb_audio_control *)module->control_offset; + ret = gbaudio_tplg_process_kcontrols(module, controls); if (ret) { - dev_err(gbcodec->dev, + dev_err(module->dev, "%d: Error in parsing controls data\n", ret); return ret; } - dev_dbg(gbcodec->dev, "Control parsing finished\n"); - - /* process DAI */ - dais = (struct gb_audio_dai *)gbcodec->dai_offset; - ret = gbaudio_tplg_process_dais(gbcodec, dais); - if (ret) { - dev_err(gbcodec->dev, - "%d: Error in parsing DAIs data\n", ret); - return ret; - } - dev_dbg(gbcodec->dev, "DAI parsing finished\n"); + dev_dbg(module->dev, "Control parsing finished\n"); /* process widgets */ - widgets = (struct gb_audio_widget *)gbcodec->widget_offset; - ret = gbaudio_tplg_process_widgets(gbcodec, widgets); + widgets = (struct gb_audio_widget *)module->widget_offset; + ret = gbaudio_tplg_process_widgets(module, widgets); if (ret) { - dev_err(gbcodec->dev, + dev_err(module->dev, "%d: Error in parsing widgets data\n", ret); return ret; } - dev_dbg(gbcodec->dev, "Widget parsing finished\n"); + dev_dbg(module->dev, "Widget parsing finished\n"); /* process route */ - routes = (struct gb_audio_route *)gbcodec->route_offset; - ret = gbaudio_tplg_process_routes(gbcodec, routes); + routes = (struct gb_audio_route *)module->route_offset; + ret = gbaudio_tplg_process_routes(module, routes); if (ret) { - dev_err(gbcodec->dev, + dev_err(module->dev, "%d: Error in parsing routes data\n", ret); return ret; } - dev_dbg(gbcodec->dev, "Route parsing finished\n"); + dev_dbg(module->dev, "Route parsing finished\n"); return ret; } -void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec) +void gbaudio_tplg_release(struct gbaudio_module_info *module) { struct gbaudio_control *control, *_control; struct gbaudio_widget *widget, *_widget; - if (!gbcodec->topology) + if (!module->topology) return; /* release kcontrols */ - list_for_each_entry_safe(control, _control, &gbcodec->codec_ctl_list, + list_for_each_entry_safe(control, _control, &module->ctl_list, list) { list_del(&control->list); - devm_kfree(gbcodec->dev, control); + devm_kfree(module->dev, control); } - if (gbcodec->kctls) - devm_kfree(gbcodec->dev, gbcodec->kctls); + if (module->controls) + devm_kfree(module->dev, module->controls); /* release widget controls */ - list_for_each_entry_safe(control, _control, &gbcodec->widget_ctl_list, + list_for_each_entry_safe(control, _control, &module->widget_ctl_list, list) { list_del(&control->list); - devm_kfree(gbcodec->dev, control); + devm_kfree(module->dev, control); } /* release widgets */ - list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list, + list_for_each_entry_safe(widget, _widget, &module->widget_list, list) { list_del(&widget->list); - devm_kfree(gbcodec->dev, widget); + devm_kfree(module->dev, widget); } - if (gbcodec->widgets) - devm_kfree(gbcodec->dev, gbcodec->widgets); + if (module->dapm_widgets) + devm_kfree(module->dev, module->dapm_widgets); /* release routes */ - if (gbcodec->routes) - devm_kfree(gbcodec->dev, gbcodec->routes); + if (module->dapm_routes) + devm_kfree(module->dev, module->dapm_routes); } -- cgit v1.2.3-59-g8ed1b From 5793227cb037e4fddf1dc869b0e0abcc22646f6b Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 29 Mar 2016 23:31:42 +0530 Subject: greybus: audio: Add module specific driver Use seperate driver to process GB Audio modules plugged-in. It'll use helper function register_module to attach itself to gbaudio-codec driver. Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 4 +- drivers/staging/greybus/audio_module.c | 309 +++++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/greybus/audio_module.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 6e276d82fdec..ec92891a5773 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -31,7 +31,8 @@ gb-raw-y := raw.o gb-hid-y := hid.o gb-es2-y := es2.o gb-arche-y := arche-platform.o arche-apb-ctrl.o -gb-audio-codec-y := audio_codec.o audio_topology.o +gb-audio-module-y := audio_module.o audio_topology.o +gb-audio-codec-y := audio_codec.o gb-audio-gb-y := audio_gb.o gb-audio-apbridgea-y := audio_apbridgea.o gb-audio-manager-y += audio_manager.o @@ -52,6 +53,7 @@ ifeq ($(CONFIG_USB_HSIC_USB3613),y) endif ifeq ($(CONFIG_SND_SOC_DYNAMIC_DAILINK),y) obj-m += gb-audio-codec.o +obj-m += gb-audio-module.o endif ifeq ($(CONFIG_ARCH_MSM8994),y) obj-m += gb-camera.o diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c new file mode 100644 index 000000000000..94fdadc67c18 --- /dev/null +++ b/drivers/staging/greybus/audio_module.c @@ -0,0 +1,309 @@ +/* + * Greybus audio driver + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include +#include +#include +#include +#include + +#include "audio_codec.h" +#include "audio_apbridgea.h" +#include "audio_manager.h" + +static DEFINE_MUTEX(gb_codec_list_lock); +static LIST_HEAD(gb_codec_list); + +/* + * gb_snd management functions + */ + +static int gbaudio_codec_request_handler(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_audio_streaming_event_request *req = op->request->payload; + + dev_warn(&connection->bundle->dev, + "Audio Event received: cport: %u, event: %u\n", + req->data_cport, req->event); + + return 0; +} + +static int gbaudio_data_connection_request_handler(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + + dev_warn(&connection->bundle->dev, "Audio Event received\n"); + + return 0; +} + +static int gb_audio_add_mgmt_connection(struct gbaudio_module_info *gbmodule, + struct greybus_descriptor_cport *cport_desc, + struct gb_bundle *bundle) +{ + struct gb_connection *connection; + + /* Management Cport */ + if (gbmodule->mgmt_connection) { + dev_err(&bundle->dev, + "Can't have multiple Management connections\n"); + return -ENODEV; + } + + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + gbaudio_codec_request_handler); + if (IS_ERR(connection)) + return PTR_ERR(connection); + + connection->private = gbmodule; + gbmodule->mgmt_connection = connection; + + return 0; +} + +static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule, + struct greybus_descriptor_cport *cport_desc, + struct gb_bundle *bundle) +{ + struct gb_connection *connection; + struct gbaudio_data_connection *dai; + + dai = devm_kzalloc(gbmodule->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) { + dev_err(gbmodule->dev, "DAI Malloc failure\n"); + return -ENOMEM; + } + + connection = gb_connection_create_flags(bundle, + le16_to_cpu(cport_desc->id), + gbaudio_data_connection_request_handler, + GB_CONNECTION_FLAG_CSD); + if (IS_ERR(connection)) { + devm_kfree(gbmodule->dev, dai); + return PTR_ERR(connection); + } + + connection->private = gbmodule; + /* dai->name should be same as codec->dai_name */ + strlcpy(dai->name, "greybus-apb1", NAME_SIZE); + dai->data_cport = connection->intf_cport_id; + dai->connection = connection; + list_add(&dai->list, &gbmodule->data_list); + + return 0; +} + +/* + * This is the basic hook get things initialized and registered w/ gb + */ + +static int gb_audio_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + struct device *dev = &bundle->dev; + struct gbaudio_module_info *gbmodule; + struct greybus_descriptor_cport *cport_desc; + struct gb_audio_manager_module_descriptor desc; + struct gbaudio_data_connection *dai, *_dai; + int ret, i; + struct gb_audio_topology *topology; + + + /* There should be at least one Management and one Data cport */ + if (bundle->num_cports < 2) + return -ENODEV; + + mutex_lock(&gb_codec_list_lock); + /* + * There can be only one Management connection and any number of data + * connections. + */ + gbmodule = devm_kzalloc(dev, sizeof(*gbmodule), GFP_KERNEL); + if (!gbmodule) { + mutex_unlock(&gb_codec_list_lock); + return -ENOMEM; + } + + gbmodule->num_data_connections = bundle->num_cports - 1; + mutex_init(&gbmodule->lock); + INIT_LIST_HEAD(&gbmodule->data_list); + INIT_LIST_HEAD(&gbmodule->widget_list); + INIT_LIST_HEAD(&gbmodule->ctl_list); + INIT_LIST_HEAD(&gbmodule->widget_ctl_list); + gbmodule->dev = dev; + snprintf(gbmodule->name, NAME_SIZE, "%s.%s", dev->driver->name, + dev_name(dev)); + greybus_set_drvdata(bundle, gbmodule); + + /* Create all connections */ + for (i = 0; i < bundle->num_cports; i++) { + cport_desc = &bundle->cport_desc[i]; + + switch (cport_desc->protocol_id) { + case GREYBUS_PROTOCOL_AUDIO_MGMT: + ret = gb_audio_add_mgmt_connection(gbmodule, cport_desc, + bundle); + if (ret) + goto destroy_connections; + break; + case GREYBUS_PROTOCOL_AUDIO_DATA: + ret = gb_audio_add_data_connection(gbmodule, cport_desc, + bundle); + if (ret) + goto destroy_connections; + break; + default: + dev_err(dev, "Unsupported protocol: 0x%02x\n", + cport_desc->protocol_id); + ret = -ENODEV; + goto destroy_connections; + } + } + + /* There must be a management cport */ + if (!gbmodule->mgmt_connection) { + ret = -EINVAL; + dev_err(dev, "Missing management connection\n"); + goto destroy_connections; + } + + /* Initialize management connection */ + ret = gb_connection_enable(gbmodule->mgmt_connection); + if (ret) { + dev_err(dev, "%d: Error while enabling mgmt connection\n", ret); + goto destroy_connections; + } + gbmodule->dev_id = gbmodule->mgmt_connection->intf->interface_id; + + /* + * FIXME: malloc for topology happens via audio_gb driver + * should be done within codec driver itself + */ + ret = gb_audio_gb_get_topology(gbmodule->mgmt_connection, &topology); + if (ret) { + dev_err(dev, "%d:Error while fetching topology\n", ret); + goto disable_connection; + } + + /* process topology data */ + ret = gbaudio_tplg_parse_data(gbmodule, topology); + if (ret) { + dev_err(dev, "%d:Error while parsing topology data\n", + ret); + goto free_topology; + } + gbmodule->topology = topology; + + /* register module with gbcodec */ + ret = gbaudio_register_module(gbmodule); + if (ret) + goto release_topology; + + /* Initialize data connections */ + list_for_each_entry(dai, &gbmodule->data_list, list) { + ret = gb_connection_enable(dai->connection); + if (ret) + goto disable_data_connection; + } + gbmodule->is_connected = 1; + + /* inform above layer for uevent */ + dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); + /* prepare for the audio manager */ + strlcpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN); + desc.slot = 1; /* todo */ + desc.vid = 2; /* todo */ + desc.pid = 3; /* todo */ + desc.cport = gbmodule->dev_id; + desc.devices = 0x2; /* todo */ + gbmodule->manager_id = gb_audio_manager_add(&desc); + + dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name); + mutex_unlock(&gb_codec_list_lock); + + return 0; + +disable_data_connection: + list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) + gb_connection_disable(dai->connection); + gbaudio_unregister_module(gbmodule); + +release_topology: + gbaudio_tplg_release(gbmodule); + gbmodule->topology = NULL; + +free_topology: + kfree(topology); + +disable_connection: + gb_connection_disable(gbmodule->mgmt_connection); + +destroy_connections: + list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) { + gb_connection_destroy(dai->connection); + list_del(&dai->list); + devm_kfree(dev, dai); + } + + if (gbmodule->mgmt_connection) + gb_connection_destroy(gbmodule->mgmt_connection); + + devm_kfree(dev, gbmodule); + mutex_unlock(&gb_codec_list_lock); + + return ret; +} + +static void gb_audio_disconnect(struct gb_bundle *bundle) +{ + struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); + struct gbaudio_data_connection *dai, *_dai; + + mutex_lock(&gb_codec_list_lock); + + gbaudio_unregister_module(gbmodule); + + /* inform uevent to above layers */ + gb_audio_manager_remove(gbmodule->manager_id); + + gbaudio_tplg_release(gbmodule); + gbmodule->topology = NULL; + kfree(gbmodule->topology); + gb_connection_disable(gbmodule->mgmt_connection); + list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) { + gb_connection_disable(dai->connection); + gb_connection_destroy(dai->connection); + list_del(&dai->list); + devm_kfree(gbmodule->dev, dai); + } + gb_connection_destroy(gbmodule->mgmt_connection); + gbmodule->mgmt_connection = NULL; + + devm_kfree(&bundle->dev, gbmodule); + mutex_unlock(&gb_codec_list_lock); +} + +static const struct greybus_bundle_id gb_audio_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) }, + { } +}; +MODULE_DEVICE_TABLE(greybus, gb_audio_id_table); + +static struct greybus_driver gb_audio_driver = { + .name = "gb-audio", + .probe = gb_audio_probe, + .disconnect = gb_audio_disconnect, + .id_table = gb_audio_id_table, +}; +module_greybus_driver(gb_audio_driver); + +MODULE_DESCRIPTION("Greybus Audio module driver"); +MODULE_AUTHOR("Vaibhav Agarwal "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gbaudio-module"); -- cgit v1.2.3-59-g8ed1b From d764212f73cf270f10b041d8e26a8eb1ee7d4909 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 29 Mar 2016 23:31:43 +0530 Subject: greybus: audio: fix to resolve multiple audio module playback issue Cleanup APBridge sequence only in case of last module plugged-out. For other modules, unregister cportid is sufficient. Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index ab24ec8c5ab8..803bab20cf0c 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -794,6 +794,7 @@ void gbaudio_codec_cleanup(struct gbaudio_module_info *module) if (!pb_state && !cap_state) return; + dev_dbg(gbcodec->dev, "%s: removed, cleanup APBridge\n", module->name); if (pb_state == GBAUDIO_CODEC_START) { /* cleanup PB path, only APBridge specific */ data = find_data(module, gbcodec->stream[0].dai_name); @@ -802,12 +803,16 @@ void gbaudio_codec_cleanup(struct gbaudio_module_info *module) __func__); return; } - ret = gb_audio_apbridgea_stop_tx(data->connection, 0); - if (ret) - return; - ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0); - if (ret) - return; + + if (list_is_singular(&gbcodec->module_list)) { + ret = gb_audio_apbridgea_stop_tx(data->connection, 0); + if (ret) + return; + ret = gb_audio_apbridgea_shutdown_tx(data->connection, + 0); + if (ret) + return; + } i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; ret = gb_audio_apbridgea_unregister_cport(data->connection, @@ -824,12 +829,15 @@ void gbaudio_codec_cleanup(struct gbaudio_module_info *module) __func__); return; } - ret = gb_audio_apbridgea_stop_rx(data->connection, 0); - if (ret) - return; - ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0); - if (ret) - return; + if (list_is_singular(&gbcodec->module_list)) { + ret = gb_audio_apbridgea_stop_rx(data->connection, 0); + if (ret) + return; + ret = gb_audio_apbridgea_shutdown_rx(data->connection, + 0); + if (ret) + return; + } i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; ret = gb_audio_apbridgea_unregister_cport(data->connection, @@ -854,10 +862,7 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) dev_dbg(codec->dev, "Process Unregister %s module\n", module->name); mutex_lock(&module->lock); - if (list_is_last(&module->list, &gbcodec->module_list)) { - dev_dbg(codec->dev, "Last module removed, cleanup APBridge\n"); - gbaudio_codec_cleanup(module); - } + gbaudio_codec_cleanup(module); module->is_connected = 0; if (module->dapm_routes) { -- cgit v1.2.3-59-g8ed1b From b77e3e5658921537df925c03fd358c4818be6f52 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 30 Mar 2016 16:53:17 -0400 Subject: greybus: core: fix two container-of macros Fix two greybus container-of macros that used the pointer name for the member. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/hd.h | 2 +- drivers/staging/greybus/svc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index fe6c086a8e3d..b481dd03bd73 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -48,7 +48,7 @@ struct gb_host_device { /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); }; -#define to_gb_host_device(d) container_of(d, struct gb_host_device, d) +#define to_gb_host_device(d) container_of(d, struct gb_host_device, dev) struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, struct device *parent, diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 0436f49ef8fa..2f23bc081f82 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -41,7 +41,7 @@ struct gb_svc { char *input_phys; struct gb_svc_watchdog *watchdog; }; -#define to_gb_svc(d) container_of(d, struct gb_svc, d) +#define to_gb_svc(d) container_of(d, struct gb_svc, dev) struct gb_svc *gb_svc_create(struct gb_host_device *hd); int gb_svc_add(struct gb_svc *svc); -- cgit v1.2.3-59-g8ed1b From 1b4c4e98eb9dcf0dbe4d940c4485e81de64b0325 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:55:56 -0400 Subject: greybus: greybus_protocols: align DME-attribute values Align the DME-attribute values in the protocol header. Signed-off-by: Johan Hovold Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9c628cc5f8e6..863bcb9e78e4 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -893,9 +893,9 @@ struct gb_svc_dme_peer_set_response { } __packed; /* Attributes for peer get/set operations */ -#define DME_ATTR_SELECTOR_INDEX 0 +#define DME_ATTR_SELECTOR_INDEX 0 /* FIXME: remove ES2 support and DME_ATTR_T_TST_SRC_INCREMENT */ -#define DME_ATTR_T_TST_SRC_INCREMENT 0x4083 +#define DME_ATTR_T_TST_SRC_INCREMENT 0x4083 #define DME_ATTR_ES3_INIT_STATUS 0x6101 /* Return value from init-status attributes listed above */ -- cgit v1.2.3-59-g8ed1b From b5eebbf6a1f15273ce100ea14c9339d26c44e546 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:55:57 -0400 Subject: greybus: svc: remove bogus interface-reset helper Remove unused, bogus interface-reset helper. The interface-reset operation is initiated by the SVC, not the AP. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 11 ----------- drivers/staging/greybus/svc.h | 1 - 2 files changed, 12 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 6a8da0dd9f33..ca1d4826ca9c 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -119,17 +119,6 @@ static int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) &request, sizeof(request), NULL, 0); } -int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id) -{ - struct gb_svc_intf_reset_request request; - - request.intf_id = intf_id; - - return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET, - &request, sizeof(request), NULL, 0); -} -EXPORT_SYMBOL_GPL(gb_svc_intf_reset); - int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) { struct gb_svc_intf_eject_request request; diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 2f23bc081f82..519d2f39aa02 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -48,7 +48,6 @@ int gb_svc_add(struct gb_svc *svc); void gb_svc_del(struct gb_svc *svc); void gb_svc_put(struct gb_svc *svc); -int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id, u8 cport_flags); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, -- cgit v1.2.3-59-g8ed1b From 601df8ff3ad2f88bdece80e71986b462da9c9b2e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:55:58 -0400 Subject: greybus: greybus_protocols: remove bogus comment about svc bundle The SVC connection is special and does not belong to neither an interface or a bundle. Remove the unused SVC bundle-id define. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 863bcb9e78e4..31e772a61f48 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -59,11 +59,6 @@ #define GB_DEVICE_ID_AP 1 #define GB_DEVICE_ID_MODULES_START 2 -/* - * Bundle/cport for control/svc cport: The same bundle/cport is shared by both - * CONTROL and SVC protocols for communication between AP and SVC. - */ -#define GB_SVC_BUNDLE_ID 0 #define GB_SVC_CPORT_ID 0 #define GB_CONTROL_BUNDLE_ID 0 #define GB_CONTROL_CPORT_ID 0 -- cgit v1.2.3-59-g8ed1b From 9ba486e3198eab8cd254c5089cc9f56b1d5b8d4c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:55:59 -0400 Subject: greybus: svc: indent CPort flag defines properly Use tabs to indent CPort-flag values. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 519d2f39aa02..904b2dd918cc 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -10,9 +10,9 @@ #ifndef __SVC_H #define __SVC_H -#define GB_SVC_CPORT_FLAG_E2EFC BIT(0) -#define GB_SVC_CPORT_FLAG_CSD_N BIT(1) -#define GB_SVC_CPORT_FLAG_CSV_N BIT(2) +#define GB_SVC_CPORT_FLAG_E2EFC BIT(0) +#define GB_SVC_CPORT_FLAG_CSD_N BIT(1) +#define GB_SVC_CPORT_FLAG_CSV_N BIT(2) enum gb_svc_state { GB_SVC_STATE_RESET, -- cgit v1.2.3-59-g8ed1b From c2d80906fbe529ee4b1963bf83d84e53e02e44c2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:00 -0400 Subject: greybus: interface: move boot-status clearing to interface enable Reading and clearing the boot status of an interface is an interface operation and belongs in the interface code. As part of the reworked interface boot sequence, we also want to do this when enabling (enumerating) a Greybus interface. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 69 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/svc.c | 70 ------------------------------------- 2 files changed, 69 insertions(+), 70 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 44226c4c48ec..deb5b2641f4a 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -9,6 +9,69 @@ #include "greybus.h" + +/* + * T_TstSrcIncrement is written by the module on ES2 as a stand-in for boot + * status attribute ES3_INIT_STATUS. AP needs to read and clear it, after + * reading a non-zero value from it. + * + * FIXME: This is module-hardware dependent and needs to be extended for every + * type of module we want to support. + */ +static int gb_interface_read_and_clear_boot_status(struct gb_interface *intf) +{ + struct gb_host_device *hd = intf->hd; + int ret; + u32 value; + u16 attr; + u8 init_status; + + /* + * Check if the module is ES2 or ES3, and choose attr number + * appropriately. + * FIXME: Remove ES2 support from the kernel entirely. + */ + if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && + intf->ddbl1_product_id == ES2_DDBL1_PROD_ID) + attr = DME_ATTR_T_TST_SRC_INCREMENT; + else + attr = DME_ATTR_ES3_INIT_STATUS; + + /* Read and clear boot status in ES3_INIT_STATUS */ + ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr, + DME_ATTR_SELECTOR_INDEX, &value); + if (ret) + return ret; + + /* + * A nonzero boot status indicates the module has finished + * booting. Clear it. + */ + if (!value) { + dev_err(&intf->dev, "Module not ready yet\n"); + return -ENODEV; + } + + /* + * Check if the module needs to boot from UniPro. + * For ES2: We need to check lowest 8 bits of 'value'. + * For ES3: We need to check highest 8 bits out of 32 of 'value'. + * FIXME: Remove ES2 support from the kernel entirely. + */ + if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && + intf->ddbl1_product_id == ES2_DDBL1_PROD_ID) + init_status = value; + else + init_status = value >> 24; + + if (init_status == DME_DIS_UNIPRO_BOOT_STARTED || + init_status == DME_DIS_FALLBACK_UNIPRO_BOOT_STARTED) + intf->boot_over_unipro = true; + + return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr, + DME_ATTR_SELECTOR_INDEX, 0); +} + /* interface sysfs attributes */ #define gb_interface_attr(field, type) \ static ssize_t field##_show(struct device *dev, \ @@ -147,6 +210,12 @@ int gb_interface_enable(struct gb_interface *intf) int ret, size; void *manifest; + ret = gb_interface_read_and_clear_boot_status(intf); + if (ret) { + dev_err(&intf->dev, "failed to clear boot status: %d\n", ret); + return ret; + } + /* Establish control connection */ ret = gb_control_enable(intf->control); if (ret) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ca1d4826ca9c..ca2f34e3e1aa 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -210,69 +210,6 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, } EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set); -/* - * T_TstSrcIncrement is written by the module on ES2 as a stand-in for boot - * status attribute ES3_INIT_STATUS. AP needs to read and clear it, after - * reading a non-zero value from it. - * - * FIXME: This is module-hardware dependent and needs to be extended for every - * type of module we want to support. - */ -static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf) -{ - struct gb_host_device *hd = intf->hd; - int ret; - u32 value; - u16 attr; - u8 init_status; - - /* - * Check if the module is ES2 or ES3, and choose attr number - * appropriately. - * FIXME: Remove ES2 support from the kernel entirely. - */ - if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && - intf->ddbl1_product_id == ES2_DDBL1_PROD_ID) - attr = DME_ATTR_T_TST_SRC_INCREMENT; - else - attr = DME_ATTR_ES3_INIT_STATUS; - - /* Read and clear boot status in ES3_INIT_STATUS */ - ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr, - DME_ATTR_SELECTOR_INDEX, &value); - - if (ret) - return ret; - - /* - * A nonzero boot status indicates the module has finished - * booting. Clear it. - */ - if (!value) { - dev_err(&intf->dev, "Module not ready yet\n"); - return -ENODEV; - } - - /* - * Check if the module needs to boot from UniPro. - * For ES2: We need to check lowest 8 bits of 'value'. - * For ES3: We need to check highest 8 bits out of 32 of 'value'. - * FIXME: Remove ES2 support from the kernel entirely. - */ - if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && - intf->ddbl1_product_id == ES2_DDBL1_PROD_ID) - init_status = value; - else - init_status = value >> 24; - - if (init_status == DME_DIS_UNIPRO_BOOT_STARTED || - init_status == DME_DIS_FALLBACK_UNIPRO_BOOT_STARTED) - intf->boot_over_unipro = true; - - return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr, - DME_ATTR_SELECTOR_INDEX, 0); -} - int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id, @@ -609,13 +546,6 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) intf->product_id = product_id; } - ret = gb_svc_read_and_clear_module_boot_status(intf); - if (ret) { - dev_err(&svc->dev, "failed to clear boot status of interface %u: %d\n", - intf_id, ret); - goto out_interface_add; - } - ret = gb_svc_interface_route_create(svc, intf); if (ret) goto out_interface_add; -- cgit v1.2.3-59-g8ed1b From e12811eff7f9948d6cce661eef4a5910d747f194 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:01 -0400 Subject: greybus: interface: fix es2 boot-status mask The ES2 boot status is stored in the least significant byte. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index deb5b2641f4a..3b18f611f66d 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -60,7 +60,7 @@ static int gb_interface_read_and_clear_boot_status(struct gb_interface *intf) */ if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && intf->ddbl1_product_id == ES2_DDBL1_PROD_ID) - init_status = value; + init_status = value & 0xff; else init_status = value >> 24; -- cgit v1.2.3-59-g8ed1b From 133e366bbee53c76596df5da4bf6b32d0d9cb364 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:02 -0400 Subject: greybus: interface: clean up and rename init-status helper Clean up and rename the interface-init-status helper. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 42 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 3b18f611f66d..569403c9d142 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -11,55 +11,58 @@ /* - * T_TstSrcIncrement is written by the module on ES2 as a stand-in for boot - * status attribute ES3_INIT_STATUS. AP needs to read and clear it, after - * reading a non-zero value from it. + * T_TstSrcIncrement is written by the module on ES2 as a stand-in for the + * init-status attribute ES3_INIT_STATUS. The AP needs to read and clear it + * after reading a non-zero value from it. * * FIXME: This is module-hardware dependent and needs to be extended for every * type of module we want to support. */ -static int gb_interface_read_and_clear_boot_status(struct gb_interface *intf) +static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) { struct gb_host_device *hd = intf->hd; int ret; u32 value; u16 attr; u8 init_status; + bool es2_bridge; + + es2_bridge = intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && + intf->ddbl1_product_id == ES2_DDBL1_PROD_ID; /* - * Check if the module is ES2 or ES3, and choose attr number - * appropriately. - * FIXME: Remove ES2 support from the kernel entirely. + * ES2 bridges use T_TstSrcIncrement for the init status. + * + * FIXME: Remove ES2 support */ - if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && - intf->ddbl1_product_id == ES2_DDBL1_PROD_ID) + if (es2_bridge) attr = DME_ATTR_T_TST_SRC_INCREMENT; else attr = DME_ATTR_ES3_INIT_STATUS; - /* Read and clear boot status in ES3_INIT_STATUS */ ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr, DME_ATTR_SELECTOR_INDEX, &value); if (ret) return ret; /* - * A nonzero boot status indicates the module has finished - * booting. Clear it. + * A nonzero init status indicates the module has finished + * initializing. */ if (!value) { - dev_err(&intf->dev, "Module not ready yet\n"); + dev_err(&intf->dev, "invalid init status\n"); return -ENODEV; } /* - * Check if the module needs to boot from UniPro. + * Check if the interface needs to boot over UniPro. + * * For ES2: We need to check lowest 8 bits of 'value'. * For ES3: We need to check highest 8 bits out of 32 of 'value'. - * FIXME: Remove ES2 support from the kernel entirely. + * + * FIXME: Remove ES2 support */ - if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && - intf->ddbl1_product_id == ES2_DDBL1_PROD_ID) + if (es2_bridge) init_status = value & 0xff; else init_status = value >> 24; @@ -68,6 +71,7 @@ static int gb_interface_read_and_clear_boot_status(struct gb_interface *intf) init_status == DME_DIS_FALLBACK_UNIPRO_BOOT_STARTED) intf->boot_over_unipro = true; + /* Clear the init status. */ return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr, DME_ATTR_SELECTOR_INDEX, 0); } @@ -210,9 +214,9 @@ int gb_interface_enable(struct gb_interface *intf) int ret, size; void *manifest; - ret = gb_interface_read_and_clear_boot_status(intf); + ret = gb_interface_read_and_clear_init_status(intf); if (ret) { - dev_err(&intf->dev, "failed to clear boot status: %d\n", ret); + dev_err(&intf->dev, "failed to clear init status: %d\n", ret); return ret; } -- cgit v1.2.3-59-g8ed1b From ec199ccdd2a3c215f5088052377f08a5855f560e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:03 -0400 Subject: greybus: interface: clean up ES3-bootroom-quirk handling Clean up handling of the ES3-bootrom quirks by adding an interface quirk-flags field that is set appropriately when we detect that the ES3 bootrom is running. Note that we need to reserve the DME_DIS_UNIPRO_BOOT_STARTED and DME_DIS_FALLBACK_UNIPRO_BOOT_STARTED status values for the ES3 bootrom, which does not support any CPort features (unlike later boot stages). Add a BOOTROM infix to the defines to make this more clear. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/connection.c | 7 +++++-- drivers/staging/greybus/control.c | 3 +-- drivers/staging/greybus/greybus_protocols.h | 10 +++++----- drivers/staging/greybus/interface.c | 17 +++++++++++++---- drivers/staging/greybus/interface.h | 7 +++++-- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 64fa20a06da0..1880f8f6065f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -337,9 +337,12 @@ gb_connection_svc_connection_create(struct gb_connection *connection) intf = connection->intf; - /* The ES2/ES3 bootrom requires E2EFC, CSD and CSV to be disabled. */ + /* + * Enable either E2EFC or CSD, unless the interface does not support + * any CPort features. + */ cport_flags = GB_SVC_CPORT_FLAG_CSV_N; - if (intf->boot_over_unipro) { + if (intf->quirks & GB_INTERFACE_QUIRK_NO_CPORT_FEATURES) { cport_flags |= GB_SVC_CPORT_FLAG_CSD_N; } else if (gb_connection_e2efc_enabled(connection)) { cport_flags |= GB_SVC_CPORT_FLAG_CSD_N | diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 83be255bb534..8475f1577325 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -155,8 +155,7 @@ int gb_control_get_interface_version_operation(struct gb_interface *intf) struct gb_connection *connection = intf->control->connection; int ret; - /* The ES3 bootrom fails to boot if this request it sent to it */ - if (intf->boot_over_unipro) + if (intf->quirks & GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION) return 0; ret = gb_operation_sync(connection, GB_CONTROL_TYPE_INTERFACE_VERSION, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 31e772a61f48..8370cfead6f2 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -894,11 +894,11 @@ struct gb_svc_dme_peer_set_response { #define DME_ATTR_ES3_INIT_STATUS 0x6101 /* Return value from init-status attributes listed above */ -#define DME_DIS_SPI_BOOT_STARTED 0x02 -#define DME_DIS_TRUSTED_SPI_BOOT_FINISHED 0x03 -#define DME_DIS_UNTRUSTED_SPI_BOOT_FINISHED 0x04 -#define DME_DIS_UNIPRO_BOOT_STARTED 0x06 -#define DME_DIS_FALLBACK_UNIPRO_BOOT_STARTED 0x09 +#define DME_DIS_SPI_BOOT_STARTED 0x02 +#define DME_DIS_TRUSTED_SPI_BOOT_FINISHED 0x03 +#define DME_DIS_UNTRUSTED_SPI_BOOT_FINISHED 0x04 +#define DME_DIS_BOOTROM_UNIPRO_BOOT_STARTED 0x06 +#define DME_DIS_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED 0x09 struct gb_svc_route_create_request { __u8 intf1_id; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 569403c9d142..a13b221131bc 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -55,7 +55,7 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) } /* - * Check if the interface needs to boot over UniPro. + * Extract the init status. * * For ES2: We need to check lowest 8 bits of 'value'. * For ES3: We need to check highest 8 bits out of 32 of 'value'. @@ -67,9 +67,18 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) else init_status = value >> 24; - if (init_status == DME_DIS_UNIPRO_BOOT_STARTED || - init_status == DME_DIS_FALLBACK_UNIPRO_BOOT_STARTED) - intf->boot_over_unipro = true; + /* + * Check if the interface is executing the quirky ES3 bootrom that + * requires E2EFC, CSD and CSV to be disabled and that does not + * support the interface-version request. + */ + switch (init_status) { + case DME_DIS_BOOTROM_UNIPRO_BOOT_STARTED: + case DME_DIS_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: + intf->quirks |= GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; + intf->quirks |= GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION; + break; + } /* Clear the init status. */ return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr, diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index d4c55abae258..96caabc8a73f 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -10,6 +10,9 @@ #ifndef __INTERFACE_H #define __INTERFACE_H +#define GB_INTERFACE_QUIRK_NO_CPORT_FEATURES BIT(0) +#define GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION BIT(1) + struct gb_interface { struct device dev; struct gb_control *control; @@ -36,8 +39,8 @@ struct gb_interface { struct gb_host_device *hd; - /* The interface needs to boot over unipro */ - bool boot_over_unipro; + unsigned long quirks; + bool disconnected; }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) -- cgit v1.2.3-59-g8ed1b From 4d5f6218886e5d363cf41af5c5a51045f2722e50 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:04 -0400 Subject: greybus: interface: move route creation to interface activation Creating and destroying a route to an interface is arguably an interface operation and belongs with the interface code. Add new interface_activate and interface_deactivate helpers that will be used to activate and deactivate an interface in the new interface boot sequence. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 81 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/interface.h | 2 + drivers/staging/greybus/svc.c | 81 +++++-------------------------------- drivers/staging/greybus/svc.h | 4 ++ 4 files changed, 96 insertions(+), 72 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index a13b221131bc..48f64fbe9248 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -10,6 +10,71 @@ #include "greybus.h" +static int gb_interface_route_create(struct gb_interface *intf) +{ + struct gb_svc *svc = intf->hd->svc; + u8 intf_id = intf->interface_id; + u8 device_id; + int ret; + + /* + * Create a device id for the interface: + * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC + * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP + * + * XXX Do we need to allocate device ID for SVC or the AP here? And what + * XXX about an AP with multiple interface blocks? + */ + ret = ida_simple_get(&svc->device_id_map, + GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); + if (ret < 0) { + dev_err(&intf->dev, "failed to allocate device id: %d\n", ret); + return ret; + } + device_id = ret; + + ret = gb_svc_intf_device_id(svc, intf_id, device_id); + if (ret) { + dev_err(&intf->dev, "failed to set device id %u: %d\n", + device_id, ret); + goto err_ida_remove; + } + + /* Create a two-way route between the AP and the new interface. */ + ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_DEVICE_ID_AP, + intf_id, device_id); + if (ret) { + dev_err(&intf->dev, "failed to create route: %d\n", ret); + goto err_svc_id_free; + } + + intf->device_id = device_id; + + return 0; + +err_svc_id_free: + /* + * XXX Should we tell SVC that this id doesn't belong to interface + * XXX anymore. + */ +err_ida_remove: + ida_simple_remove(&svc->device_id_map, device_id); + + return ret; +} + +static void gb_interface_route_destroy(struct gb_interface *intf) +{ + struct gb_svc *svc = intf->hd->svc; + + if (intf->device_id == GB_DEVICE_ID_BAD) + return; + + gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id); + ida_simple_remove(&svc->device_id_map, intf->device_id); + intf->device_id = GB_DEVICE_ID_BAD; +} + /* * T_TstSrcIncrement is written by the module on ES2 as a stand-in for the * init-status attribute ES3_INIT_STATUS. The AP needs to read and clear it @@ -213,6 +278,22 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, return intf; } +int gb_interface_activate(struct gb_interface *intf) +{ + int ret; + + ret = gb_interface_route_create(intf); + if (ret) + return ret; + + return 0; +} + +void gb_interface_deactivate(struct gb_interface *intf) +{ + gb_interface_route_destroy(intf); +} + /* * Enable an interface by enabling its control connection and fetching the * manifest and other information over it. diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 96caabc8a73f..5dfaea51eb9f 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -50,6 +50,8 @@ struct gb_interface *gb_interface_find(struct gb_host_device *hd, struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id); +int gb_interface_activate(struct gb_interface *intf); +void gb_interface_deactivate(struct gb_interface *intf); int gb_interface_enable(struct gb_interface *intf); void gb_interface_disable(struct gb_interface *intf); int gb_interface_add(struct gb_interface *intf); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ca2f34e3e1aa..5c517d7e65f0 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -108,7 +108,7 @@ static struct attribute *svc_attrs[] = { }; ATTRIBUTE_GROUPS(svc); -static int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) +int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) { struct gb_svc_intf_device_id_request request; @@ -251,7 +251,7 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); /* Creates bi-directional routes between the devices */ -static int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, +int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, u8 intf2_id, u8 dev2_id) { struct gb_svc_route_create_request request; @@ -266,7 +266,7 @@ static int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, } /* Destroys bi-directional routes between the devices */ -static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) +void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) { struct gb_svc_route_destroy_request request; int ret; @@ -397,78 +397,12 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } -static int gb_svc_interface_route_create(struct gb_svc *svc, - struct gb_interface *intf) -{ - u8 intf_id = intf->interface_id; - u8 device_id; - int ret; - - /* - * Create a device id for the interface: - * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC - * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP - * - * XXX Do we need to allocate device ID for SVC or the AP here? And what - * XXX about an AP with multiple interface blocks? - */ - ret = ida_simple_get(&svc->device_id_map, - GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); - if (ret < 0) { - dev_err(&svc->dev, "failed to allocate device id for interface %u: %d\n", - intf_id, ret); - return ret; - } - device_id = ret; - - ret = gb_svc_intf_device_id(svc, intf_id, device_id); - if (ret) { - dev_err(&svc->dev, "failed to set device id %u for interface %u: %d\n", - device_id, intf_id, ret); - goto err_ida_remove; - } - - /* Create a two-way route between the AP and the new interface. */ - ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_DEVICE_ID_AP, - intf_id, device_id); - if (ret) { - dev_err(&svc->dev, "failed to create route to interface %u (device id %u): %d\n", - intf_id, device_id, ret); - goto err_svc_id_free; - } - - intf->device_id = device_id; - - return 0; - -err_svc_id_free: - /* - * XXX Should we tell SVC that this id doesn't belong to interface - * XXX anymore. - */ -err_ida_remove: - ida_simple_remove(&svc->device_id_map, device_id); - - return ret; -} - -static void gb_svc_interface_route_destroy(struct gb_svc *svc, - struct gb_interface *intf) -{ - if (intf->device_id == GB_DEVICE_ID_BAD) - return; - - gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id); - ida_simple_remove(&svc->device_id_map, intf->device_id); - intf->device_id = GB_DEVICE_ID_BAD; -} - static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) { intf->disconnected = true; gb_interface_disable(intf); - gb_svc_interface_route_destroy(svc, intf); + gb_interface_deactivate(intf); gb_interface_remove(intf); } @@ -546,9 +480,12 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) intf->product_id = product_id; } - ret = gb_svc_interface_route_create(svc, intf); - if (ret) + ret = gb_interface_activate(intf); + if (ret) { + dev_err(&svc->dev, "failed to activate interface %u: %d\n", + intf_id, ret); goto out_interface_add; + } ret = gb_interface_enable(intf); if (ret) { diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 904b2dd918cc..8950baff9aef 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -48,6 +48,10 @@ int gb_svc_add(struct gb_svc *svc); void gb_svc_del(struct gb_svc *svc); void gb_svc_put(struct gb_svc *svc); +int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); +int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, + u8 intf2_id, u8 dev2_id); +void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id, u8 cport_flags); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, -- cgit v1.2.3-59-g8ed1b From 984c9d38ae370993f28b36c8be9923b1de39f5e7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:05 -0400 Subject: greybus: interface: deactivate interface on enumeration failure Deactivate an interface immediately on enumeration failure. Note that an interface is always registered. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5c517d7e65f0..96d3d55f54ed 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -484,17 +484,23 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) if (ret) { dev_err(&svc->dev, "failed to activate interface %u: %d\n", intf_id, ret); - goto out_interface_add; + goto err_interface_add; } ret = gb_interface_enable(intf); if (ret) { dev_err(&svc->dev, "failed to enable interface %u: %d\n", intf_id, ret); - goto out_interface_add; + goto err_interface_deactivate; } -out_interface_add: + gb_interface_add(intf); + + return; + +err_interface_deactivate: + gb_interface_deactivate(intf); +err_interface_add: gb_interface_add(intf); } -- cgit v1.2.3-59-g8ed1b From e9f2f688d1a9434f615eec7180f14f0c52941bb9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:06 -0400 Subject: greybus: interface: clean up device-id handling Clean up the device id-handling and make sure we never allocate invalid device ids due to a missing upper bound. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/bundle.h | 2 -- drivers/staging/greybus/greybus_protocols.h | 9 +++++---- drivers/staging/greybus/interface.c | 25 +++++++++++-------------- drivers/staging/greybus/interface.h | 2 +- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 18b125e8169b..2dc61ab7495d 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -32,8 +32,6 @@ struct gb_bundle { }; #define to_gb_bundle(d) container_of(d, struct gb_bundle, dev) -#define GB_DEVICE_ID_BAD 0xff - /* Greybus "private" definitions" */ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, u8 class); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 8370cfead6f2..2e126e671bd6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -54,10 +54,11 @@ /* Fixed IDs for control/svc protocols */ -/* Device ID of SVC and AP */ -#define GB_DEVICE_ID_SVC 0 -#define GB_DEVICE_ID_AP 1 -#define GB_DEVICE_ID_MODULES_START 2 +/* SVC switch-port device ids */ +#define GB_SVC_DEVICE_ID_SVC 0 +#define GB_SVC_DEVICE_ID_AP 1 +#define GB_SVC_DEVICE_ID_MIN 2 +#define GB_SVC_DEVICE_ID_MAX 31 #define GB_SVC_CPORT_ID 0 #define GB_CONTROL_BUNDLE_ID 0 diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 48f64fbe9248..7e7bcdafa6e2 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -10,6 +10,9 @@ #include "greybus.h" +#define GB_INTERFACE_DEVICE_ID_BAD 0xff + + static int gb_interface_route_create(struct gb_interface *intf) { struct gb_svc *svc = intf->hd->svc; @@ -17,16 +20,10 @@ static int gb_interface_route_create(struct gb_interface *intf) u8 device_id; int ret; - /* - * Create a device id for the interface: - * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC - * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP - * - * XXX Do we need to allocate device ID for SVC or the AP here? And what - * XXX about an AP with multiple interface blocks? - */ + /* Allocate an interface device id. */ ret = ida_simple_get(&svc->device_id_map, - GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL); + GB_SVC_DEVICE_ID_MIN, GB_SVC_DEVICE_ID_MAX + 1, + GFP_KERNEL); if (ret < 0) { dev_err(&intf->dev, "failed to allocate device id: %d\n", ret); return ret; @@ -40,8 +37,8 @@ static int gb_interface_route_create(struct gb_interface *intf) goto err_ida_remove; } - /* Create a two-way route between the AP and the new interface. */ - ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_DEVICE_ID_AP, + /* FIXME: Hard-coded AP device id. */ + ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_SVC_DEVICE_ID_AP, intf_id, device_id); if (ret) { dev_err(&intf->dev, "failed to create route: %d\n", ret); @@ -67,12 +64,12 @@ static void gb_interface_route_destroy(struct gb_interface *intf) { struct gb_svc *svc = intf->hd->svc; - if (intf->device_id == GB_DEVICE_ID_BAD) + if (intf->device_id == GB_INTERFACE_DEVICE_ID_BAD) return; gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id); ida_simple_remove(&svc->device_id_map, intf->device_id); - intf->device_id = GB_DEVICE_ID_BAD; + intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; } /* @@ -257,7 +254,7 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, INIT_LIST_HEAD(&intf->manifest_descs); /* Invalid device id to start with */ - intf->device_id = GB_DEVICE_ID_BAD; + intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; intf->dev.parent = &hd->dev; intf->dev.bus = &greybus_bus_type; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 5dfaea51eb9f..8b6fcfe90883 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -21,7 +21,7 @@ struct gb_interface { struct list_head links; /* gb_host_device->interfaces */ struct list_head manifest_descs; u8 interface_id; /* Physical location within the Endo */ - u8 device_id; /* Device id allocated for the interface block by the SVC */ + u8 device_id; /* Information taken from the manifest descriptor */ char *vendor_string; -- cgit v1.2.3-59-g8ed1b From af1471e7a903c18e98adf49fc9e2e444915b48c8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:07 -0400 Subject: greybus: greybus_protocols: rename NULL DME selector index Add NULL suffix to the don't-care DME selector index. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 2 +- drivers/staging/greybus/interface.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 2e126e671bd6..0a7427ef6431 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -889,7 +889,7 @@ struct gb_svc_dme_peer_set_response { } __packed; /* Attributes for peer get/set operations */ -#define DME_ATTR_SELECTOR_INDEX 0 +#define DME_ATTR_SELECTOR_INDEX_NULL 0 /* FIXME: remove ES2 support and DME_ATTR_T_TST_SRC_INCREMENT */ #define DME_ATTR_T_TST_SRC_INCREMENT 0x4083 #define DME_ATTR_ES3_INIT_STATUS 0x6101 diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 7e7bcdafa6e2..823debb40272 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -103,7 +103,7 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) attr = DME_ATTR_ES3_INIT_STATUS; ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr, - DME_ATTR_SELECTOR_INDEX, &value); + DME_ATTR_SELECTOR_INDEX_NULL, &value); if (ret) return ret; @@ -144,7 +144,7 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) /* Clear the init status. */ return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr, - DME_ATTR_SELECTOR_INDEX, 0); + DME_ATTR_SELECTOR_INDEX_NULL, 0); } /* interface sysfs attributes */ -- cgit v1.2.3-59-g8ed1b From 153ff7e76ddb2fd5ab3d790c0139154dfd5464f1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:08 -0400 Subject: greybus: interface: read DME attributes at activation Read the DDBL1 and Ara DME attributes when activating an interface. These values are currently provided by the SVC in the intf_hotplug request, which is about to go away. Note that there are currently no standard Ara VID and PID attributes and that Toshiba uses attributes from the reserved space in ES3. For now, we therefore refuse to enumerate any non-Toshiba bridges. Also note that the Ara serial number is currently not supported. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 72 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/interface.h | 1 - drivers/staging/greybus/svc.c | 18 ++++------ 3 files changed, 78 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 823debb40272..c7793a93c802 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -12,6 +12,74 @@ #define GB_INTERFACE_DEVICE_ID_BAD 0xff +/* DME attributes */ +#define DME_DDBL1_MANUFACTURERID 0x5003 +#define DME_DDBL1_PRODUCTID 0x5004 + +#define DME_TOSHIBA_ARA_VID 0x6000 +#define DME_TOSHIBA_ARA_PID 0x6001 + +/* DDBL1 Manufacturer and Product ids */ +#define TOSHIBA_DMID 0x0126 +#define TOSHIBA_ES2_BRIDGE_DPID 0x1000 +#define TOSHIBA_ES3_APBRIDGE_DPID 0x1001 +#define TOSHIBA_ES3_GPBRIDGE_DPID 0x1002 + + +static int gb_interface_dme_attr_get(struct gb_interface *intf, + u16 attr, u32 *val) +{ + return gb_svc_dme_peer_get(intf->hd->svc, intf->interface_id, + attr, DME_ATTR_SELECTOR_INDEX_NULL, + val); +} + +static int gb_interface_read_ara_dme(struct gb_interface *intf) +{ + int ret; + + /* + * Unless this is a Toshiba bridge, bail out until we have defined + * standard Ara attributes. + */ + if (intf->ddbl1_manufacturer_id != TOSHIBA_DMID) { + dev_err(&intf->dev, "unknown manufacturer %08x\n", + intf->ddbl1_manufacturer_id); + return -ENODEV; + } + + ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_ARA_VID, + &intf->vendor_id); + if (ret) + return ret; + + ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_ARA_PID, + &intf->product_id); + if (ret) + return ret; + + /* FIXME: serial number not implemented */ + intf->serial_number = 0; + + return 0; +} + +static int gb_interface_read_dme(struct gb_interface *intf) +{ + int ret; + + ret = gb_interface_dme_attr_get(intf, DME_DDBL1_MANUFACTURERID, + &intf->ddbl1_manufacturer_id); + if (ret) + return ret; + + ret = gb_interface_dme_attr_get(intf, DME_DDBL1_PRODUCTID, + &intf->ddbl1_product_id); + if (ret) + return ret; + + return gb_interface_read_ara_dme(intf); +} static int gb_interface_route_create(struct gb_interface *intf) { @@ -279,6 +347,10 @@ int gb_interface_activate(struct gb_interface *intf) { int ret; + ret = gb_interface_read_dme(intf); + if (ret) + return ret; + ret = gb_interface_route_create(intf); if (ret) return ret; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 8b6fcfe90883..1a6ce5ce6e6c 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -27,7 +27,6 @@ struct gb_interface { char *vendor_string; char *product_string; - /* Information taken from the hotplug event */ u32 ddbl1_manufacturer_id; u32 ddbl1_product_id; u32 vendor_id; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 96d3d55f54ed..0659815248f9 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -460,11 +460,12 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) return; } - intf->ddbl1_manufacturer_id = le32_to_cpu(request->data.ddbl1_mfr_id); - intf->ddbl1_product_id = le32_to_cpu(request->data.ddbl1_prod_id); - intf->vendor_id = le32_to_cpu(request->data.ara_vend_id); - intf->product_id = le32_to_cpu(request->data.ara_prod_id); - intf->serial_number = le64_to_cpu(request->data.serial_number); + ret = gb_interface_activate(intf); + if (ret) { + dev_err(&svc->dev, "failed to activate interface %u: %d\n", + intf_id, ret); + goto err_interface_add; + } /* * Use VID/PID specified at hotplug if: @@ -480,13 +481,6 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) intf->product_id = product_id; } - ret = gb_interface_activate(intf); - if (ret) { - dev_err(&svc->dev, "failed to activate interface %u: %d\n", - intf_id, ret); - goto err_interface_add; - } - ret = gb_interface_enable(intf); if (ret) { dev_err(&svc->dev, "failed to enable interface %u: %d\n", -- cgit v1.2.3-59-g8ed1b From 7d7acc06a0febf9f08649df52aa267b8f74b0de9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:09 -0400 Subject: greybus: interface: add ES2 init-status quirk flag Add ES2 init-status quirk flag instead of checking MID/PID directly. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 13 +++++++------ drivers/staging/greybus/interface.h | 1 + 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c7793a93c802..767bf87b2dbd 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -78,6 +78,11 @@ static int gb_interface_read_dme(struct gb_interface *intf) if (ret) return ret; + if (intf->ddbl1_manufacturer_id == TOSHIBA_DMID && + intf->ddbl1_product_id == TOSHIBA_ES2_BRIDGE_DPID) { + intf->quirks |= GB_INTERFACE_QUIRK_NO_INIT_STATUS; + } + return gb_interface_read_ara_dme(intf); } @@ -155,17 +160,13 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) u32 value; u16 attr; u8 init_status; - bool es2_bridge; - - es2_bridge = intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && - intf->ddbl1_product_id == ES2_DDBL1_PROD_ID; /* * ES2 bridges use T_TstSrcIncrement for the init status. * * FIXME: Remove ES2 support */ - if (es2_bridge) + if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS) attr = DME_ATTR_T_TST_SRC_INCREMENT; else attr = DME_ATTR_ES3_INIT_STATUS; @@ -192,7 +193,7 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) * * FIXME: Remove ES2 support */ - if (es2_bridge) + if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS) init_status = value & 0xff; else init_status = value >> 24; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 1a6ce5ce6e6c..94eb36e36975 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -12,6 +12,7 @@ #define GB_INTERFACE_QUIRK_NO_CPORT_FEATURES BIT(0) #define GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION BIT(1) +#define GB_INTERFACE_QUIRK_NO_INIT_STATUS BIT(2) struct gb_interface { struct device dev; -- cgit v1.2.3-59-g8ed1b From ac72cfbe6937f00c1fb334001c89ea26254ef430 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:10 -0400 Subject: greybus: interface: clean up ES2 VID/PID hack Clean up the ES2 VID/PID hack using a new quirk flag. Note that the hack is now used if and only if the interface is a Toshiba ES2 bridge (and not if the attributes read zero). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/firmware.c | 9 +-------- drivers/staging/greybus/greybus_protocols.h | 4 ---- drivers/staging/greybus/interface.c | 1 + drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/svc.c | 16 ++++------------ 5 files changed, 7 insertions(+), 24 deletions(-) diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c index b1188b23ffe5..b1e373c16440 100644 --- a/drivers/staging/greybus/firmware.c +++ b/drivers/staging/greybus/firmware.c @@ -43,14 +43,7 @@ static void firmware_es2_fixup_vid_pid(struct gb_firmware *firmware) struct gb_interface *intf = connection->bundle->intf; int ret; - /* - * Use VID/PID specified at hotplug if: - * - Bridge ASIC chip isn't ES2 - * - Received non-zero Vendor/Product ids - */ - if (intf->ddbl1_manufacturer_id != ES2_DDBL1_MFR_ID || - intf->ddbl1_product_id != ES2_DDBL1_PROD_ID || - intf->vendor_id != 0 || intf->product_id != 0) + if (!(intf->quirks & GB_INTERFACE_QUIRK_NO_ARA_IDS)) return; ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_GET_VID_PID, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 0a7427ef6431..bb7b01f82ea0 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -226,10 +226,6 @@ struct gb_control_timesync_authoritative_request { #define GB_FIRMWARE_TYPE_AP_READY 0x05 /* Request with no-payload */ #define GB_FIRMWARE_TYPE_GET_VID_PID 0x06 /* Request with no-payload */ -/* FIXME: remove all ES2-specific identifiers from the kernel */ -#define ES2_DDBL1_MFR_ID 0x00000126 -#define ES2_DDBL1_PROD_ID 0x00001000 - /* Greybus firmware boot stages */ #define GB_FIRMWARE_BOOT_STAGE_ONE 0x01 /* Reserved for the boot ROM */ #define GB_FIRMWARE_BOOT_STAGE_TWO 0x02 /* Firmware package to be loaded by the boot ROM */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 767bf87b2dbd..bf29831bcd21 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -80,6 +80,7 @@ static int gb_interface_read_dme(struct gb_interface *intf) if (intf->ddbl1_manufacturer_id == TOSHIBA_DMID && intf->ddbl1_product_id == TOSHIBA_ES2_BRIDGE_DPID) { + intf->quirks |= GB_INTERFACE_QUIRK_NO_ARA_IDS; intf->quirks |= GB_INTERFACE_QUIRK_NO_INIT_STATUS; } diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 94eb36e36975..15c687f5dbc1 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -13,6 +13,7 @@ #define GB_INTERFACE_QUIRK_NO_CPORT_FEATURES BIT(0) #define GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION BIT(1) #define GB_INTERFACE_QUIRK_NO_INIT_STATUS BIT(2) +#define GB_INTERFACE_QUIRK_NO_ARA_IDS BIT(3) struct gb_interface { struct device dev; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 0659815248f9..a19e575de029 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -426,11 +426,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) intf = gb_interface_find(hd, intf_id); if (intf) { - /* - * For ES2, we need to maintain the same vendor/product ids we - * got from bootrom, otherwise userspace can't distinguish - * between modules. - */ + /* HACK: Save Ara VID/PID for ES2 hack below */ vendor_id = intf->vendor_id; product_id = intf->product_id; @@ -468,15 +464,11 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) } /* - * Use VID/PID specified at hotplug if: - * - Bridge ASIC chip isn't ES2 - * - Received non-zero Vendor/Product ids + * HACK: Use Ara VID/PID from earlier boot stage. * - * Otherwise, use the ids we received from bootrom. + * FIXME: remove quirk with ES2 support */ - if (intf->ddbl1_manufacturer_id == ES2_DDBL1_MFR_ID && - intf->ddbl1_product_id == ES2_DDBL1_PROD_ID && - intf->vendor_id == 0 && intf->product_id == 0) { + if (intf->quirks & GB_INTERFACE_QUIRK_NO_ARA_IDS) { intf->vendor_id = vendor_id; intf->product_id = product_id; } -- cgit v1.2.3-59-g8ed1b From 50ad4163a54cb373012b330d078e4ad17b6e6c95 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:11 -0400 Subject: greybus: interface: clean up DME attribute handling Move all DME defines to the interface code and rename them using common prefixes (e.g. DME_T and DME_TOSHIBA). The DDB L1 attributes are defined by MIPI and the Ara attributes are currently Toshiba specific so move them all out of the Greybus protocol header. Also rename the Greybus init-status values using a GB_INIT prefix. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/greybus_protocols.h | 18 ++++++------------ drivers/staging/greybus/interface.c | 26 ++++++++++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index bb7b01f82ea0..06888e029473 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -884,18 +884,12 @@ struct gb_svc_dme_peer_set_response { __le16 result_code; } __packed; -/* Attributes for peer get/set operations */ -#define DME_ATTR_SELECTOR_INDEX_NULL 0 -/* FIXME: remove ES2 support and DME_ATTR_T_TST_SRC_INCREMENT */ -#define DME_ATTR_T_TST_SRC_INCREMENT 0x4083 -#define DME_ATTR_ES3_INIT_STATUS 0x6101 - -/* Return value from init-status attributes listed above */ -#define DME_DIS_SPI_BOOT_STARTED 0x02 -#define DME_DIS_TRUSTED_SPI_BOOT_FINISHED 0x03 -#define DME_DIS_UNTRUSTED_SPI_BOOT_FINISHED 0x04 -#define DME_DIS_BOOTROM_UNIPRO_BOOT_STARTED 0x06 -#define DME_DIS_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED 0x09 +/* Greybus init-status values, currently retrieved using DME peer gets. */ +#define GB_INIT_SPI_BOOT_STARTED 0x02 +#define GB_INIT_TRUSTED_SPI_BOOT_FINISHED 0x03 +#define GB_INIT_UNTRUSTED_SPI_BOOT_FINISHED 0x04 +#define GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED 0x06 +#define GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED 0x09 struct gb_svc_route_create_request { __u8 intf1_id; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index bf29831bcd21..d0542e5a9ac2 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -12,12 +12,19 @@ #define GB_INTERFACE_DEVICE_ID_BAD 0xff +/* Don't-care selector index */ +#define DME_SELECTOR_INDEX_NULL 0 + /* DME attributes */ +/* FIXME: remove ES2 support and DME_T_TST_SRC_INCREMENT */ +#define DME_T_TST_SRC_INCREMENT 0x4083 + #define DME_DDBL1_MANUFACTURERID 0x5003 #define DME_DDBL1_PRODUCTID 0x5004 #define DME_TOSHIBA_ARA_VID 0x6000 #define DME_TOSHIBA_ARA_PID 0x6001 +#define DME_TOSHIBA_ARA_INIT_STATUS 0x6101 /* DDBL1 Manufacturer and Product ids */ #define TOSHIBA_DMID 0x0126 @@ -30,8 +37,7 @@ static int gb_interface_dme_attr_get(struct gb_interface *intf, u16 attr, u32 *val) { return gb_svc_dme_peer_get(intf->hd->svc, intf->interface_id, - attr, DME_ATTR_SELECTOR_INDEX_NULL, - val); + attr, DME_SELECTOR_INDEX_NULL, val); } static int gb_interface_read_ara_dme(struct gb_interface *intf) @@ -148,8 +154,8 @@ static void gb_interface_route_destroy(struct gb_interface *intf) /* * T_TstSrcIncrement is written by the module on ES2 as a stand-in for the - * init-status attribute ES3_INIT_STATUS. The AP needs to read and clear it - * after reading a non-zero value from it. + * init-status attribute DME_TOSHIBA_INIT_STATUS. The AP needs to read and + * clear it after reading a non-zero value from it. * * FIXME: This is module-hardware dependent and needs to be extended for every * type of module we want to support. @@ -168,12 +174,12 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) * FIXME: Remove ES2 support */ if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS) - attr = DME_ATTR_T_TST_SRC_INCREMENT; + attr = DME_T_TST_SRC_INCREMENT; else - attr = DME_ATTR_ES3_INIT_STATUS; + attr = DME_TOSHIBA_ARA_INIT_STATUS; ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr, - DME_ATTR_SELECTOR_INDEX_NULL, &value); + DME_SELECTOR_INDEX_NULL, &value); if (ret) return ret; @@ -205,8 +211,8 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) * support the interface-version request. */ switch (init_status) { - case DME_DIS_BOOTROM_UNIPRO_BOOT_STARTED: - case DME_DIS_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: + case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED: + case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: intf->quirks |= GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; intf->quirks |= GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION; break; @@ -214,7 +220,7 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) /* Clear the init status. */ return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr, - DME_ATTR_SELECTOR_INDEX_NULL, 0); + DME_SELECTOR_INDEX_NULL, 0); } /* interface sysfs attributes */ -- cgit v1.2.3-59-g8ed1b From a7be84613acc4f732f3aa9ab28da7e2e3a6bab0b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Mar 2016 18:56:12 -0400 Subject: greybus: interface: add Ara serial-number support Add support for reading the Ara serial-number attributes. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/interface.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index d0542e5a9ac2..27dbd79d2e19 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -24,6 +24,8 @@ #define DME_TOSHIBA_ARA_VID 0x6000 #define DME_TOSHIBA_ARA_PID 0x6001 +#define DME_TOSHIBA_ARA_SN0 0x6002 +#define DME_TOSHIBA_ARA_SN1 0x6003 #define DME_TOSHIBA_ARA_INIT_STATUS 0x6101 /* DDBL1 Manufacturer and Product ids */ @@ -42,6 +44,7 @@ static int gb_interface_dme_attr_get(struct gb_interface *intf, static int gb_interface_read_ara_dme(struct gb_interface *intf) { + u32 sn0, sn1; int ret; /* @@ -64,8 +67,15 @@ static int gb_interface_read_ara_dme(struct gb_interface *intf) if (ret) return ret; - /* FIXME: serial number not implemented */ - intf->serial_number = 0; + ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_ARA_SN0, &sn0); + if (ret) + return ret; + + ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_ARA_SN1, &sn1); + if (ret) + return ret; + + intf->serial_number = (u64)sn1 << 32 | sn0; return 0; } -- cgit v1.2.3-59-g8ed1b From 1472ec67f734d9707d4758fddd4787113fe0b0b2 Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos Date: Thu, 31 Mar 2016 14:12:45 +0300 Subject: greybus: camera: Use pointer for gb camera module ops No need to duplicate module ops on every registration. NOTE: Change should be along merged with: "msm: camera: Change gb_camera_module ops to pointer" Signed-off-by: Gjorgji Rosikopulos Reviewed-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/camera.c | 21 ++++++++++++++------- drivers/staging/greybus/gb-camera.h | 6 +++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index a871b0f33733..2de91d59a54e 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -498,23 +498,30 @@ static int gb_camera_op_capture(void *priv, u32 request_id, unsigned int streams, unsigned int num_frames, size_t settings_size, const void *settings) { - return gb_camera_capture(priv, request_id, streams, num_frames, + struct gb_camera *gcam = priv; + + return gb_camera_capture(gcam, request_id, streams, num_frames, settings_size, settings); } static int gb_camera_op_flush(void *priv, u32 *request_id) { - return gb_camera_flush(priv, request_id); + struct gb_camera *gcam = priv; + + return gb_camera_flush(gcam, request_id); } +static const struct gb_camera_ops gb_cam_ops = { + .capabilities = gb_camera_op_capabilities, + .configure_streams = gb_camera_op_configure_streams, + .capture = gb_camera_op_capture, + .flush = gb_camera_op_flush, +}; + static int gb_camera_register_intf_ops(struct gb_camera *gcam) { gcam->module.priv = gcam; - gcam->module.ops.capabilities = gb_camera_op_capabilities; - gcam->module.ops.configure_streams = gb_camera_op_configure_streams; - gcam->module.ops.capture = gb_camera_op_capture; - gcam->module.ops.flush = gb_camera_op_flush; - + gcam->module.ops = &gb_cam_ops; return gb_camera_register(&gcam->module); } diff --git a/drivers/staging/greybus/gb-camera.h b/drivers/staging/greybus/gb-camera.h index 0a48a16b675e..273b4fa6dd4f 100644 --- a/drivers/staging/greybus/gb-camera.h +++ b/drivers/staging/greybus/gb-camera.h @@ -36,14 +36,14 @@ struct gb_camera_ops { struct gb_camera_module { void *priv; - struct gb_camera_ops ops; + const struct gb_camera_ops *ops; struct list_head list; /* Global list */ }; #define gb_camera_call(f, op, args...) \ - ((!(f) ? -ENODEV : ((f)->ops.op) ? \ - (f)->ops.op((f)->priv, ##args) : -ENOIOCTLCMD)) + (!(f) ? -ENODEV : (((f)->ops->op) ? \ + (f)->ops->op((f)->priv, ##args) : -ENOIOCTLCMD)) int gb_camera_register(struct gb_camera_module *module); int gb_camera_unregister(struct gb_camera_module *module); -- cgit v1.2.3-59-g8ed1b From 6da549ec851117293a07e64f97170f7dc9d55578 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 29 Mar 2016 16:32:35 +0530 Subject: greybus: audio: Fix widget pointer update during control parsing widget pointer was incorrectly modfied while parsing kcontrol Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 4901348a2ada..9e36fb27faf8 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -834,7 +834,7 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_module_info *module, list_add(&widget->list, &module->widget_list); ncontrols = curr->ncontrols; curr++; - curr += ncontrols * sizeof(struct gb_audio_control); + curr = (void *)curr + ncontrols*sizeof(struct gb_audio_control); } module->dapm_widgets = dapm_widgets; -- cgit v1.2.3-59-g8ed1b From 64a7e2cceb75ccabaec713944a95511605751b29 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 29 Mar 2016 16:32:36 +0530 Subject: greybus: audio: Added jack support to audio module Register jack with ASoC sound card in case audio module populates it via codec FW. Currently, only a single jack with 4 buttons can be registered for each module. Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 98 +++++++++++++++++++++ drivers/staging/greybus/audio_codec.h | 15 ++++ drivers/staging/greybus/audio_module.c | 142 +++++++++++++++++++++++++++++-- drivers/staging/greybus/audio_topology.c | 1 + 4 files changed, 251 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 803bab20cf0c..66a954806cf5 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "audio_codec.h" #include "audio_apbridgea.h" @@ -735,10 +736,80 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { .digital_mute = gbcodec_digital_mute, }; +static int gbaudio_init_jack(struct gbaudio_module_info *module, + struct snd_soc_codec *codec) +{ + int ret; + + if (!module->num_jacks) + return 0; + + /* register jack(s) in case any */ + if (module->num_jacks > 1) { + dev_err(module->dev, "Currently supports max=1 jack\n"); + return -EINVAL; + } + + snprintf(module->jack_name, NAME_SIZE, "GB %d Headset Jack", + module->dev_id); + ret = snd_soc_jack_new(codec, module->jack_name, GBCODEC_JACK_MASK, + &module->headset_jack); + if (ret) { + dev_err(module->dev, "Failed to create new jack\n"); + return ret; + } + + snprintf(module->button_name, NAME_SIZE, "GB %d Button Jack", + module->dev_id); + ret = snd_soc_jack_new(codec, module->button_name, + GBCODEC_JACK_BUTTON_MASK, &module->button_jack); + if (ret) { + dev_err(module->dev, "Failed to create button jack\n"); + return ret; + } + + ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_0, + KEY_MEDIA); + if (ret) { + dev_err(module->dev, "Failed to set BTN_0\n"); + return ret; + } + + ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_1, + KEY_VOICECOMMAND); + if (ret) { + dev_err(module->dev, "Failed to set BTN_1\n"); + return ret; + } + + ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_2, + KEY_VOLUMEUP); + if (ret) { + dev_err(module->dev, "Failed to set BTN_2\n"); + return ret; + } + + ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_3, + KEY_VOLUMEDOWN); + if (ret) { + dev_err(module->dev, "Failed to set BTN_0\n"); + return ret; + } + + /* FIXME + * verify if this is really required + set_bit(INPUT_PROP_NO_DUMMY_RELEASE, + module->button_jack.jack->input_dev->propbit); + */ + + return 0; +} + int gbaudio_register_module(struct gbaudio_module_info *module) { int ret; struct snd_soc_codec *codec; + struct snd_soc_jack *jack = NULL; if (!gbcodec) { dev_err(module->dev, "GB Codec not yet probed\n"); @@ -756,6 +827,12 @@ int gbaudio_register_module(struct gbaudio_module_info *module) return -EINVAL; } + ret = gbaudio_init_jack(module, codec); + if (ret) { + mutex_unlock(&gbcodec->lock); + return ret; + } + if (module->dapm_widgets) snd_soc_dapm_new_controls(&codec->dapm, module->dapm_widgets, module->num_dapm_widgets); @@ -774,6 +851,15 @@ int gbaudio_register_module(struct gbaudio_module_info *module) &codec->dapm); } +#ifdef CONFIG_SND_JACK + /* register jack devices for this module from codec->jack_list */ + list_for_each_entry(jack, &codec->jack_list, list) { + if ((jack == &module->headset_jack) + || (jack == &module->button_jack)) + snd_device_register(codec->card->snd_card, jack->jack); + } +#endif + list_add(&module->list, &gbcodec->module_list); dev_dbg(codec->dev, "Registered %s module\n", module->name); @@ -851,6 +937,7 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) { struct snd_soc_codec *codec = gbcodec->codec; struct snd_card *card = codec->card->snd_card; + struct snd_soc_jack *jack, *next_j; dev_dbg(codec->dev, "Unregister %s module\n", module->name); @@ -862,6 +949,17 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) dev_dbg(codec->dev, "Process Unregister %s module\n", module->name); mutex_lock(&module->lock); +#ifdef CONFIG_SND_JACK + /* free jack devices for this module from codec->jack_list */ + list_for_each_entry_safe(jack, next_j, &codec->jack_list, list) { + if ((jack == &module->headset_jack) + || (jack == &module->button_jack)) { + snd_device_free(codec->card->snd_card, jack->jack); + list_del(&jack->list); + } + } +#endif + gbaudio_codec_cleanup(module); module->is_connected = 0; diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index a2697dd62949..165b3595dae9 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -10,6 +10,7 @@ #define __LINUX_GBAUDIO_CODEC_H #include +#include #include "greybus.h" #include "greybus_protocols.h" @@ -57,6 +58,11 @@ enum gbcodec_reg_index { #define GBCODEC_APB1_MUX_REG_DEFAULT 0x00 #define GBCODEC_APB2_MUX_REG_DEFAULT 0x00 +#define GBCODEC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \ + SND_JACK_LINEIN | SND_JACK_UNSUPPORTED) +#define GBCODEC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { GBCODEC_CTL_REG_DEFAULT, GBCODEC_MUTE_REG_DEFAULT, @@ -139,6 +145,15 @@ struct gbaudio_module_info { int manager_id; char name[NAME_SIZE]; + /* jack related */ + char jack_name[NAME_SIZE]; + char button_name[NAME_SIZE]; + int num_jacks; + int jack_type; + int button_status; + struct snd_soc_jack headset_jack; + struct snd_soc_jack button_jack; + /* used by codec_ops */ struct mutex lock; int is_connected; diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 94fdadc67c18..9039aa63e040 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -22,18 +22,150 @@ static LIST_HEAD(gb_codec_list); * gb_snd management functions */ -static int gbaudio_codec_request_handler(struct gb_operation *op) +static int gbaudio_request_jack(struct gbaudio_module_info *module, + struct gb_audio_jack_event_request *req) { - struct gb_connection *connection = op->connection; - struct gb_audio_streaming_event_request *req = op->request->payload; + int report, button_status; + + dev_warn(module->dev, "Jack Event received: type: %u, event: %u\n", + req->widget_type, req->event); + + mutex_lock(&module->lock); + if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) { + module->jack_type = 0; + button_status = module->button_status; + module->button_status = 0; + mutex_unlock(&module->lock); + if (button_status) + snd_soc_jack_report(&module->button_jack, 0, + GBCODEC_JACK_BUTTON_MASK); + snd_soc_jack_report(&module->headset_jack, 0, + GBCODEC_JACK_MASK); + return 0; + } + + report &= ~GBCODEC_JACK_MASK; + /* currently supports Headphone, Headset & Lineout only */ + if (req->widget_type && GB_AUDIO_WIDGET_TYPE_HP) + report |= SND_JACK_HEADPHONE & GBCODEC_JACK_MASK; + + if (req->widget_type && GB_AUDIO_WIDGET_TYPE_MIC) + report = SND_JACK_MICROPHONE & GBCODEC_JACK_MASK; + + if (req->widget_type && GB_AUDIO_WIDGET_TYPE_LINE) + report = (report & GBCODEC_JACK_MASK) | + SND_JACK_LINEOUT | SND_JACK_LINEIN; + + if (module->jack_type) + dev_warn(module->dev, "Modifying jack from %d to %d\n", + module->jack_type, report); + + module->jack_type = report; + mutex_unlock(&module->lock); + snd_soc_jack_report(&module->headset_jack, report, GBCODEC_JACK_MASK); + + return 0; +} + +static int gbaudio_request_button(struct gbaudio_module_info *module, + struct gb_audio_button_event_request *req) +{ + int soc_button_id, report; + + dev_warn(module->dev, "Button Event received: id: %u, event: %u\n", + req->button_id, req->event); + + /* currently supports 4 buttons only */ + mutex_lock(&module->lock); + if (!module->jack_type) { + dev_err(module->dev, "Jack not present. Bogus event!!\n"); + mutex_unlock(&module->lock); + return -EINVAL; + } + + report = module->button_status & GBCODEC_JACK_BUTTON_MASK; + + switch (req->button_id) { + case 1: + soc_button_id = SND_JACK_BTN_0; + break; + + case 2: + soc_button_id = SND_JACK_BTN_1; + break; - dev_warn(&connection->bundle->dev, - "Audio Event received: cport: %u, event: %u\n", + case 3: + soc_button_id = SND_JACK_BTN_2; + break; + + case 4: + soc_button_id = SND_JACK_BTN_3; + break; + default: + dev_err(module->dev, "Invalid button request received\n"); + return -EINVAL; + } + + if (req->event == GB_AUDIO_BUTTON_EVENT_PRESS) + report = report | soc_button_id; + else + report = report & ~soc_button_id; + + module->button_status = report; + + mutex_unlock(&module->lock); + + snd_soc_jack_report(&module->button_jack, report, + GBCODEC_JACK_BUTTON_MASK); + + return 0; +} + +static int gbaudio_request_stream(struct gbaudio_module_info *module, + struct gb_audio_streaming_event_request *req) +{ + dev_warn(module->dev, "Audio Event received: cport: %u, event: %u\n", req->data_cport, req->event); return 0; } +static int gbaudio_codec_request_handler(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gbaudio_module_info *module = + greybus_get_drvdata(connection->bundle); + struct gb_operation_msg_hdr *header = op->request->header; + struct gb_audio_streaming_event_request *stream_req; + struct gb_audio_jack_event_request *jack_req; + struct gb_audio_button_event_request *button_req; + int ret; + + switch (header->type) { + case GB_AUDIO_TYPE_STREAMING_EVENT: + stream_req = op->request->payload; + ret = gbaudio_request_stream(module, stream_req); + break; + + case GB_AUDIO_TYPE_JACK_EVENT: + jack_req = op->request->payload; + ret = gbaudio_request_jack(module, jack_req); + break; + + case GB_AUDIO_TYPE_BUTTON_EVENT: + button_req = op->request->payload; + ret = gbaudio_request_button(module, button_req); + break; + + default: + dev_err(&connection->bundle->dev, + "Invalid Audio Event received\n"); + return -EINVAL; + } + + return ret; +} + static int gbaudio_data_connection_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 9e36fb27faf8..79161c1b74ce 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -669,6 +669,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, case snd_soc_dapm_hp: *dw = (struct snd_soc_dapm_widget) SND_SOC_DAPM_HP(w->name, gbcodec_event_hp); + module->num_jacks++; break; case snd_soc_dapm_mic: *dw = (struct snd_soc_dapm_widget) -- cgit v1.2.3-59-g8ed1b From 094c4302c11889683af54525221ca68f64c1a358 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal Date: Tue, 29 Mar 2016 16:32:37 +0530 Subject: greybus: audio: Add I2S_RX path related settings Capture path related settings during startup, perpare & hwparams were earlier missing. Signed-off-by: Vaibhav Agarwal Reviewed-by: Mark Greer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/audio_codec.c | 144 +++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 30 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 66a954806cf5..2c6142d9817a 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -72,16 +72,27 @@ static int gbaudio_module_disable(struct gbaudio_codec_info *codec, if (!data) { dev_err(codec->dev, "%s:%s DATA connection missing\n", dai_name, module->name); - mutex_unlock(&module->lock); - mutex_unlock(&codec->lock); - return -ENODEV; + ret = -ENODEV; + goto func_exit; } if (codec_state > GBAUDIO_CODEC_HWPARAMS) { data_cport = data->connection->intf_cport_id; - ret = gb_audio_gb_deactivate_tx(module->mgmt_connection, + switch(dir) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_gb_deactivate_rx( + module->mgmt_connection, + data_cport); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_gb_deactivate_tx( + module->mgmt_connection, data_cport); + break; + default: + ret = -EINVAL; + } if (ret) { - dev_err(codec->dev, "deactivate_tx for %s failed:%d\n", + dev_err(codec->dev, "deactivate for %s failed:%d\n", module->name, ret); goto func_exit; } @@ -90,9 +101,22 @@ static int gbaudio_module_disable(struct gbaudio_codec_info *codec, } if (codec_state > GBAUDIO_CODEC_SHUTDOWN) { cportid = data->connection->hd_cport_id; - ret = gb_audio_apbridgea_unregister_cport(data->connection, + switch(dir) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_apbridgea_unregister_cport( + data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_apbridgea_unregister_cport( + data->connection, i2s_port, cportid, AUDIO_APBRIDGEA_DIRECTION_TX); + break; + default: + ret = -EINVAL; + } if (ret) { dev_err(codec->dev, "unregister_cport for %s failed:%d\n", module->name, ret); @@ -148,18 +172,30 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, if (!data) { dev_err(codec->dev, "%s:%s DATA connection missing\n", dai_name, module->name); - mutex_unlock(&module->lock); - mutex_unlock(&codec->lock); - return -ENODEV; + ret = -ENODEV; + goto func_exit; } /* register cport */ if (module_state < codec_state) { i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; - ret = gb_audio_apbridgea_register_cport(data->connection, + switch(dir) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_apbridgea_register_cport( + data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_apbridgea_register_cport( + data->connection, i2s_port, cportid, AUDIO_APBRIDGEA_DIRECTION_TX); + break; + default: + ret = -EINVAL; + } if (ret) { dev_err(codec->dev, "reg_cport for %s\n", module->name); goto func_exit; @@ -186,18 +222,46 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, /* prepare */ if (module_state < codec_state) { data_cport = data->connection->intf_cport_id; - ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection, - data_cport, 192); - if (ret) { - dev_err(codec->dev, "set_tx_data_size for %s\n", - module->name); - goto func_exit; - } - ret = gb_audio_gb_activate_tx(module->mgmt_connection, - data_cport); - if (ret) { - dev_err(codec->dev, "activate_tx for %s\n", - module->name); + switch(dir) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_gb_set_rx_data_size( + module->mgmt_connection, + data_cport, 192); + if (ret) { + dev_err(codec->dev, + "set_rx_data_size for %s\n", + module->name); + goto func_exit; + } + ret = gb_audio_gb_activate_rx(module->mgmt_connection, + data_cport); + if (ret) { + dev_err(codec->dev, "activate_rx for %s\n", + module->name); + goto func_exit; + } + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_gb_set_tx_data_size( + module->mgmt_connection, + data_cport, 192); + if (ret) { + dev_err(codec->dev, + "set_tx_data_size for %s\n", + module->name); + goto func_exit; + } + ret = gb_audio_gb_activate_tx(module->mgmt_connection, + data_cport); + if (ret) { + dev_err(codec->dev, "activate_tx for %s\n", + module->name); + goto func_exit; + } + break; + default: + dev_err(codec->dev, "Inavlid stream direction\n"); + ret = -EINVAL; goto func_exit; } module_state = GBAUDIO_CODEC_PREPARE; @@ -217,7 +281,7 @@ int gbaudio_module_update(struct gbaudio_codec_info *codec, struct gbaudio_module_info *module, int enable) { int stream, ret = 0; - int pb_state; + int pb_state, cap_state; dev_dbg(module->dev, "Module update %s sequence\n", enable ? "Enable":"Disable"); @@ -239,12 +303,16 @@ int gbaudio_module_update(struct gbaudio_codec_info *codec, SNDRV_PCM_STREAM_PLAYBACK); } - /* TBD - * check if capture active - * cap_state = codec->stream[SNDRV_PCM_STREAM_CAPTURE].state; - * if ((stream & GB_CAPTURE) && (cap_state > GBAUDIO_CODEC_SHUTDOWN)) - * - */ + /* check if capture active */ + cap_state = codec->stream[SNDRV_PCM_STREAM_CAPTURE].state; + if ((stream & GB_CAPTURE) && (cap_state > GBAUDIO_CODEC_SHUTDOWN)) { + if (enable) + ret = gbaudio_module_enable(codec, module, + SNDRV_PCM_STREAM_CAPTURE); + else + ret = gbaudio_module_disable(codec, module, + SNDRV_PCM_STREAM_CAPTURE); + } return ret; } @@ -291,9 +359,25 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, /* register cport */ i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; - ret = gb_audio_apbridgea_register_cport(data->connection, + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_apbridgea_register_cport( + data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_apbridgea_register_cport( + data->connection, i2s_port, cportid, AUDIO_APBRIDGEA_DIRECTION_TX); + break; + default: + dev_err(dai->dev, "Inavlid stream\n"); + mutex_unlock(&module->lock); + mutex_unlock(&codec->lock); + return -EINVAL; + } dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, cportid, ret); state = GBAUDIO_CODEC_STARTUP; -- cgit v1.2.3-59-g8ed1b From a4d11cee6fe9591a78ac82c8b91cf6592d0ba8cf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 31 Mar 2016 22:26:39 -0700 Subject: greybus: trivial: add checkpatch.pl to the tree For those who are stuck using old kernel trees, let's include the latest version of checkpatch.pl into our tree to help prevent coding style mistakes from creeping in. Also add spelling.txt to catch spelling errors in comments. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/checkpatch.pl | 5993 +++++++++++++++++++++++++++++++++ drivers/staging/greybus/spelling.txt | 1072 ++++++ 2 files changed, 7065 insertions(+) create mode 100755 drivers/staging/greybus/checkpatch.pl create mode 100644 drivers/staging/greybus/spelling.txt diff --git a/drivers/staging/greybus/checkpatch.pl b/drivers/staging/greybus/checkpatch.pl new file mode 100755 index 000000000000..d574d13ba963 --- /dev/null +++ b/drivers/staging/greybus/checkpatch.pl @@ -0,0 +1,5993 @@ +#!/usr/bin/perl -w +# (c) 2001, Dave Jones. (the file handling bit) +# (c) 2005, Joel Schopp (the ugly bit) +# (c) 2007,2008, Andy Whitcroft (new conditions, test suite) +# (c) 2008-2010 Andy Whitcroft +# Licensed under the terms of the GNU GPL License version 2 + +use strict; +use POSIX; +use File::Basename; +use Cwd 'abs_path'; +use Term::ANSIColor qw(:constants); + +my $P = $0; +my $D = dirname(abs_path($P)); + +my $V = '0.32'; + +use Getopt::Long qw(:config no_auto_abbrev); + +my $quiet = 0; +my $tree = 1; +my $chk_signoff = 1; +my $chk_patch = 1; +my $tst_only; +my $emacs = 0; +my $terse = 0; +my $showfile = 0; +my $file = 0; +my $check = 0; +my $check_orig = 0; +my $summary = 1; +my $mailback = 0; +my $summary_file = 0; +my $show_types = 0; +my $fix = 0; +my $fix_inplace = 0; +my $root; +my %debug; +my %camelcase = (); +my %use_type = (); +my @use = (); +my %ignore_type = (); +my @ignore = (); +my $help = 0; +my $configuration_file = ".checkpatch.conf"; +my $max_line_length = 80; +my $ignore_perl_version = 0; +my $minimum_perl_version = 5.10.0; +my $min_conf_desc_length = 4; +my $spelling_file = "$D/spelling.txt"; +my $codespell = 0; +my $codespellfile = "/usr/share/codespell/dictionary.txt"; +my $color = 1; + +sub help { + my ($exitcode) = @_; + + print << "EOM"; +Usage: $P [OPTION]... [FILE]... +Version: $V + +Options: + -q, --quiet quiet + --no-tree run without a kernel tree + --no-signoff do not check for 'Signed-off-by' line + --patch treat FILE as patchfile (default) + --emacs emacs compile window format + --terse one line per report + --showfile emit diffed file position, not input file position + -f, --file treat FILE as regular source file + --subjective, --strict enable more subjective tests + --types TYPE(,TYPE2...) show only these comma separated message types + --ignore TYPE(,TYPE2...) ignore various comma separated message types + --max-line-length=n set the maximum line length, if exceeded, warn + --min-conf-desc-length=n set the min description length, if shorter, warn + --show-types show the message "types" in the output + --root=PATH PATH to the kernel tree root + --no-summary suppress the per-file summary + --mailback only produce a report in case of warnings/errors + --summary-file include the filename in summary + --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of + 'values', 'possible', 'type', and 'attr' (default + is all off) + --test-only=WORD report only warnings/errors containing WORD + literally + --fix EXPERIMENTAL - may create horrible results + If correctable single-line errors exist, create + ".EXPERIMENTAL-checkpatch-fixes" + with potential errors corrected to the preferred + checkpatch style + --fix-inplace EXPERIMENTAL - may create horrible results + Is the same as --fix, but overwrites the input + file. It's your fault if there's no backup or git + --ignore-perl-version override checking of perl version. expect + runtime errors. + --codespell Use the codespell dictionary for spelling/typos + (default:/usr/share/codespell/dictionary.txt) + --codespellfile Use this codespell dictionary + --color Use colors when output is STDOUT (default: on) + -h, --help, --version display this help and exit + +When FILE is - read standard input. +EOM + + exit($exitcode); +} + +my $conf = which_conf($configuration_file); +if (-f $conf) { + my @conf_args; + open(my $conffile, '<', "$conf") + or warn "$P: Can't find a readable $configuration_file file $!\n"; + + while (<$conffile>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + $line =~ s/\s+/ /g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + + my @words = split(" ", $line); + foreach my $word (@words) { + last if ($word =~ m/^#/); + push (@conf_args, $word); + } + } + close($conffile); + unshift(@ARGV, @conf_args) if @conf_args; +} + +GetOptions( + 'q|quiet+' => \$quiet, + 'tree!' => \$tree, + 'signoff!' => \$chk_signoff, + 'patch!' => \$chk_patch, + 'emacs!' => \$emacs, + 'terse!' => \$terse, + 'showfile!' => \$showfile, + 'f|file!' => \$file, + 'subjective!' => \$check, + 'strict!' => \$check, + 'ignore=s' => \@ignore, + 'types=s' => \@use, + 'show-types!' => \$show_types, + 'max-line-length=i' => \$max_line_length, + 'min-conf-desc-length=i' => \$min_conf_desc_length, + 'root=s' => \$root, + 'summary!' => \$summary, + 'mailback!' => \$mailback, + 'summary-file!' => \$summary_file, + 'fix!' => \$fix, + 'fix-inplace!' => \$fix_inplace, + 'ignore-perl-version!' => \$ignore_perl_version, + 'debug=s' => \%debug, + 'test-only=s' => \$tst_only, + 'codespell!' => \$codespell, + 'codespellfile=s' => \$codespellfile, + 'color!' => \$color, + 'h|help' => \$help, + 'version' => \$help +) or help(1); + +help(0) if ($help); + +$fix = 1 if ($fix_inplace); +$check_orig = $check; + +my $exit = 0; + +if ($^V && $^V lt $minimum_perl_version) { + printf "$P: requires at least perl version %vd\n", $minimum_perl_version; + if (!$ignore_perl_version) { + exit(1); + } +} + +if ($#ARGV < 0) { + print "$P: no input files\n"; + exit(1); +} + +sub hash_save_array_words { + my ($hashRef, $arrayRef) = @_; + + my @array = split(/,/, join(',', @$arrayRef)); + foreach my $word (@array) { + $word =~ s/\s*\n?$//g; + $word =~ s/^\s*//g; + $word =~ s/\s+/ /g; + $word =~ tr/[a-z]/[A-Z]/; + + next if ($word =~ m/^\s*#/); + next if ($word =~ m/^\s*$/); + + $hashRef->{$word}++; + } +} + +sub hash_show_words { + my ($hashRef, $prefix) = @_; + + if (keys %$hashRef) { + print "\nNOTE: $prefix message types:"; + foreach my $word (sort keys %$hashRef) { + print " $word"; + } + print "\n"; + } +} + +hash_save_array_words(\%ignore_type, \@ignore); +hash_save_array_words(\%use_type, \@use); + +my $dbg_values = 0; +my $dbg_possible = 0; +my $dbg_type = 0; +my $dbg_attr = 0; +for my $key (keys %debug) { + ## no critic + eval "\${dbg_$key} = '$debug{$key}';"; + die "$@" if ($@); +} + +my $rpt_cleaners = 0; + +if ($terse) { + $emacs = 1; + $quiet++; +} + +if ($tree) { + if (defined $root) { + if (!top_of_kernel_tree($root)) { + die "$P: $root: --root does not point at a valid tree\n"; + } + } else { + if (top_of_kernel_tree('.')) { + $root = '.'; + } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && + top_of_kernel_tree($1)) { + $root = $1; + } + } + + if (!defined $root) { + print "Must be run from the top-level dir. of a kernel tree\n"; + exit(2); + } +} + +my $emitted_corrupt = 0; + +our $Ident = qr{ + [A-Za-z_][A-Za-z\d_]* + (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* + }x; +our $Storage = qr{extern|static|asmlinkage}; +our $Sparse = qr{ + __user| + __kernel| + __force| + __iomem| + __pmem| + __must_check| + __init_refok| + __kprobes| + __ref| + __rcu| + __private + }x; +our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; +our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; +our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; +our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; +our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; + +# Notes to $Attribute: +# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check +our $Attribute = qr{ + const| + __percpu| + __nocast| + __safe| + __bitwise__| + __packed__| + __packed2__| + __naked| + __maybe_unused| + __always_unused| + __noreturn| + __used| + __cold| + __pure| + __noclone| + __deprecated| + __read_mostly| + __kprobes| + $InitAttribute| + ____cacheline_aligned| + ____cacheline_aligned_in_smp| + ____cacheline_internodealigned_in_smp| + __weak + }x; +our $Modifier; +our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; +our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; +our $Lval = qr{$Ident(?:$Member)*}; + +our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; +our $Binary = qr{(?i)0b[01]+$Int_type?}; +our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; +our $Int = qr{[0-9]+$Int_type?}; +our $Octal = qr{0[0-7]+$Int_type?}; +our $String = qr{"[X\t]*"}; +our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; +our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; +our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; +our $Float = qr{$Float_hex|$Float_dec|$Float_int}; +our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; +our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; +our $Compare = qr{<=|>=|==|!=|<|(?}; +our $Arithmetic = qr{\+|-|\*|\/|%}; +our $Operators = qr{ + <=|>=|==|!=| + =>|->|<<|>>|<|>|!|~| + &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic + }x; + +our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; + +our $BasicType; +our $NonptrType; +our $NonptrTypeMisordered; +our $NonptrTypeWithAttr; +our $Type; +our $TypeMisordered; +our $Declare; +our $DeclareMisordered; + +our $NON_ASCII_UTF8 = qr{ + [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 +}x; + +our $UTF8 = qr{ + [\x09\x0A\x0D\x20-\x7E] # ASCII + | $NON_ASCII_UTF8 +}x; + +our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; +our $typeOtherOSTypedefs = qr{(?x: + u_(?:char|short|int|long) | # bsd + u(?:nchar|short|int|long) # sysv +)}; +our $typeKernelTypedefs = qr{(?x: + (?:__)?(?:u|s|be|le)(?:8|16|32|64)| + atomic_t +)}; +our $typeTypedefs = qr{(?x: + $typeC99Typedefs\b| + $typeOtherOSTypedefs\b| + $typeKernelTypedefs\b +)}; + +our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; + +our $logFunctions = qr{(?x: + printk(?:_ratelimited|_once|)| + (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| + WARN(?:_RATELIMIT|_ONCE|)| + panic| + MODULE_[A-Z_]+| + seq_vprintf|seq_printf|seq_puts +)}; + +our $signature_tags = qr{(?xi: + Signed-off-by:| + Acked-by:| + Tested-by:| + Reviewed-by:| + Reported-by:| + Suggested-by:| + To:| + Cc: +)}; + +our @typeListMisordered = ( + qr{char\s+(?:un)?signed}, + qr{int\s+(?:(?:un)?signed\s+)?short\s}, + qr{int\s+short(?:\s+(?:un)?signed)}, + qr{short\s+int(?:\s+(?:un)?signed)}, + qr{(?:un)?signed\s+int\s+short}, + qr{short\s+(?:un)?signed}, + qr{long\s+int\s+(?:un)?signed}, + qr{int\s+long\s+(?:un)?signed}, + qr{long\s+(?:un)?signed\s+int}, + qr{int\s+(?:un)?signed\s+long}, + qr{int\s+(?:un)?signed}, + qr{int\s+long\s+long\s+(?:un)?signed}, + qr{long\s+long\s+int\s+(?:un)?signed}, + qr{long\s+long\s+(?:un)?signed\s+int}, + qr{long\s+long\s+(?:un)?signed}, + qr{long\s+(?:un)?signed}, +); + +our @typeList = ( + qr{void}, + qr{(?:(?:un)?signed\s+)?char}, + qr{(?:(?:un)?signed\s+)?short\s+int}, + qr{(?:(?:un)?signed\s+)?short}, + qr{(?:(?:un)?signed\s+)?int}, + qr{(?:(?:un)?signed\s+)?long\s+int}, + qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, + qr{(?:(?:un)?signed\s+)?long\s+long}, + qr{(?:(?:un)?signed\s+)?long}, + qr{(?:un)?signed}, + qr{float}, + qr{double}, + qr{bool}, + qr{struct\s+$Ident}, + qr{union\s+$Ident}, + qr{enum\s+$Ident}, + qr{${Ident}_t}, + qr{${Ident}_handler}, + qr{${Ident}_handler_fn}, + @typeListMisordered, +); + +our $C90_int_types = qr{(?x: + long\s+long\s+int\s+(?:un)?signed| + long\s+long\s+(?:un)?signed\s+int| + long\s+long\s+(?:un)?signed| + (?:(?:un)?signed\s+)?long\s+long\s+int| + (?:(?:un)?signed\s+)?long\s+long| + int\s+long\s+long\s+(?:un)?signed| + int\s+(?:(?:un)?signed\s+)?long\s+long| + + long\s+int\s+(?:un)?signed| + long\s+(?:un)?signed\s+int| + long\s+(?:un)?signed| + (?:(?:un)?signed\s+)?long\s+int| + (?:(?:un)?signed\s+)?long| + int\s+long\s+(?:un)?signed| + int\s+(?:(?:un)?signed\s+)?long| + + int\s+(?:un)?signed| + (?:(?:un)?signed\s+)?int +)}; + +our @typeListFile = (); +our @typeListWithAttr = ( + @typeList, + qr{struct\s+$InitAttribute\s+$Ident}, + qr{union\s+$InitAttribute\s+$Ident}, +); + +our @modifierList = ( + qr{fastcall}, +); +our @modifierListFile = (); + +our @mode_permission_funcs = ( + ["module_param", 3], + ["module_param_(?:array|named|string)", 4], + ["module_param_array_named", 5], + ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], + ["proc_create(?:_data|)", 2], + ["(?:CLASS|DEVICE|SENSOR)_ATTR", 2], +); + +#Create a search pattern for all these functions to speed up a loop below +our $mode_perms_search = ""; +foreach my $entry (@mode_permission_funcs) { + $mode_perms_search .= '|' if ($mode_perms_search ne ""); + $mode_perms_search .= $entry->[0]; +} + +our $mode_perms_world_writable = qr{ + S_IWUGO | + S_IWOTH | + S_IRWXUGO | + S_IALLUGO | + 0[0-7][0-7][2367] +}x; + +our $allowed_asm_includes = qr{(?x: + irq| + memory| + time| + reboot +)}; +# memory.h: ARM has a custom one + +# Load common spelling mistakes and build regular expression list. +my $misspellings; +my %spelling_fix; + +if (open(my $spelling, '<', $spelling_file)) { + while (<$spelling>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + + my ($suspect, $fix) = split(/\|\|/, $line); + + $spelling_fix{$suspect} = $fix; + } + close($spelling); +} else { + warn "No typos will be found - file '$spelling_file': $!\n"; +} + +if ($codespell) { + if (open(my $spelling, '<', $codespellfile)) { + while (<$spelling>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + next if ($line =~ m/, disabled/i); + + $line =~ s/,.*$//; + + my ($suspect, $fix) = split(/->/, $line); + + $spelling_fix{$suspect} = $fix; + } + close($spelling); + } else { + warn "No codespell typos will be found - file '$codespellfile': $!\n"; + } +} + +$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; + +sub build_types { + my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; + my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; + my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; + my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; + $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + $BasicType = qr{ + (?:$typeTypedefs\b)| + (?:${all}\b) + }x; + $NonptrType = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\([^\)]*\)| + (?:$typeTypedefs\b)| + (?:${all}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $NonptrTypeMisordered = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:${Misordered}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $NonptrTypeWithAttr = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\([^\)]*\)| + (?:$typeTypedefs\b)| + (?:${allWithAttr}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $Type = qr{ + $NonptrType + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? + (?:\s+$Inline|\s+$Modifier)* + }x; + $TypeMisordered = qr{ + $NonptrTypeMisordered + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? + (?:\s+$Inline|\s+$Modifier)* + }x; + $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; + $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; +} +build_types(); + +our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; + +# Using $balanced_parens, $LvalOrFunc, or $FuncArg +# requires at least perl version v5.10.0 +# Any use must be runtime checked with $^V + +our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; +our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; +our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; + +our $declaration_macros = qr{(?x: + (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| + (?:$Storage\s+)?LIST_HEAD\s*\(| + (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\( +)}; + +sub deparenthesize { + my ($string) = @_; + return "" if (!defined($string)); + + while ($string =~ /^\s*\(.*\)\s*$/) { + $string =~ s@^\s*\(\s*@@; + $string =~ s@\s*\)\s*$@@; + } + + $string =~ s@\s+@ @g; + + return $string; +} + +sub seed_camelcase_file { + my ($file) = @_; + + return if (!(-f $file)); + + local $/; + + open(my $include_file, '<', "$file") + or warn "$P: Can't read '$file' $!\n"; + my $text = <$include_file>; + close($include_file); + + my @lines = split('\n', $text); + + foreach my $line (@lines) { + next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); + if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { + $camelcase{$1} = 1; + } + } +} + +my $camelcase_seeded = 0; +sub seed_camelcase_includes { + return if ($camelcase_seeded); + + my $files; + my $camelcase_cache = ""; + my @include_files = (); + + $camelcase_seeded = 1; + + if (-e ".git") { + my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; + chomp $git_last_include_commit; + $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; + } else { + my $last_mod_date = 0; + $files = `find $root/include -name "*.h"`; + @include_files = split('\n', $files); + foreach my $file (@include_files) { + my $date = POSIX::strftime("%Y%m%d%H%M", + localtime((stat $file)[9])); + $last_mod_date = $date if ($last_mod_date < $date); + } + $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; + } + + if ($camelcase_cache ne "" && -f $camelcase_cache) { + open(my $camelcase_file, '<', "$camelcase_cache") + or warn "$P: Can't read '$camelcase_cache' $!\n"; + while (<$camelcase_file>) { + chomp; + $camelcase{$_} = 1; + } + close($camelcase_file); + + return; + } + + if (-e ".git") { + $files = `git ls-files "include/*.h"`; + @include_files = split('\n', $files); + } + + foreach my $file (@include_files) { + seed_camelcase_file($file); + } + + if ($camelcase_cache ne "") { + unlink glob ".checkpatch-camelcase.*"; + open(my $camelcase_file, '>', "$camelcase_cache") + or warn "$P: Can't write '$camelcase_cache' $!\n"; + foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { + print $camelcase_file ("$_\n"); + } + close($camelcase_file); + } +} + +sub git_commit_info { + my ($commit, $id, $desc) = @_; + + return ($id, $desc) if ((which("git") eq "") || !(-e ".git")); + + my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`; + $output =~ s/^\s*//gm; + my @lines = split("\n", $output); + + return ($id, $desc) if ($#lines < 0); + + if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) { +# Maybe one day convert this block of bash into something that returns +# all matching commit ids, but it's very slow... +# +# echo "checking commits $1..." +# git rev-list --remotes | grep -i "^$1" | +# while read line ; do +# git log --format='%H %s' -1 $line | +# echo "commit $(cut -c 1-12,41-)" +# done + } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { + } else { + $id = substr($lines[0], 0, 12); + $desc = substr($lines[0], 41); + } + + return ($id, $desc); +} + +$chk_signoff = 0 if ($file); + +my @rawlines = (); +my @lines = (); +my @fixed = (); +my @fixed_inserted = (); +my @fixed_deleted = (); +my $fixlinenr = -1; + +my $vname; +for my $filename (@ARGV) { + my $FILE; + if ($file) { + open($FILE, '-|', "diff -u /dev/null $filename") || + die "$P: $filename: diff failed - $!\n"; + } elsif ($filename eq '-') { + open($FILE, '<&STDIN'); + } else { + open($FILE, '<', "$filename") || + die "$P: $filename: open failed - $!\n"; + } + if ($filename eq '-') { + $vname = 'Your patch'; + } else { + $vname = $filename; + } + while (<$FILE>) { + chomp; + push(@rawlines, $_); + } + close($FILE); + + if ($#ARGV > 0 && $quiet == 0) { + print '-' x length($vname) . "\n"; + print "$vname\n"; + print '-' x length($vname) . "\n"; + } + + if (!process($filename)) { + $exit = 1; + } + @rawlines = (); + @lines = (); + @fixed = (); + @fixed_inserted = (); + @fixed_deleted = (); + $fixlinenr = -1; + @modifierListFile = (); + @typeListFile = (); + build_types(); +} + +if (!$quiet) { + hash_show_words(\%use_type, "Used"); + hash_show_words(\%ignore_type, "Ignored"); + + if ($^V lt 5.10.0) { + print << "EOM" + +NOTE: perl $^V is not modern enough to detect all possible issues. + An upgrade to at least perl v5.10.0 is suggested. +EOM + } + if ($exit) { + print << "EOM" + +NOTE: If any of the errors are false positives, please report + them to the maintainer, see CHECKPATCH in MAINTAINERS. +EOM + } +} + +exit($exit); + +sub top_of_kernel_tree { + my ($root) = @_; + + my @tree_check = ( + "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", + "README", "Documentation", "arch", "include", "drivers", + "fs", "init", "ipc", "kernel", "lib", "scripts", + ); + + foreach my $check (@tree_check) { + if (! -e $root . '/' . $check) { + return 0; + } + } + return 1; +} + +sub parse_email { + my ($formatted_email) = @_; + + my $name = ""; + my $address = ""; + my $comment = ""; + + if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { + $name = $1; + $address = $2; + $comment = $3 if defined $3; + } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { + $address = $1; + $comment = $2 if defined $2; + } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { + $address = $1; + $comment = $2 if defined $2; + $formatted_email =~ s/$address.*$//; + $name = $formatted_email; + $name = trim($name); + $name =~ s/^\"|\"$//g; + # If there's a name left after stripping spaces and + # leading quotes, and the address doesn't have both + # leading and trailing angle brackets, the address + # is invalid. ie: + # "joe smith joe@smith.com" bad + # "joe smith ]+>$/) { + $name = ""; + $address = ""; + $comment = ""; + } + } + + $name = trim($name); + $name =~ s/^\"|\"$//g; + $address = trim($address); + $address =~ s/^\<|\>$//g; + + if ($name =~ /[^\w \-]/i) { ##has "must quote" chars + $name =~ s/(?"; + } + + return $formatted_email; +} + +sub which { + my ($bin) = @_; + + foreach my $path (split(/:/, $ENV{PATH})) { + if (-e "$path/$bin") { + return "$path/$bin"; + } + } + + return ""; +} + +sub which_conf { + my ($conf) = @_; + + foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { + if (-e "$path/$conf") { + return "$path/$conf"; + } + } + + return ""; +} + +sub expand_tabs { + my ($str) = @_; + + my $res = ''; + my $n = 0; + for my $c (split(//, $str)) { + if ($c eq "\t") { + $res .= ' '; + $n++; + for (; ($n % 8) != 0; $n++) { + $res .= ' '; + } + next; + } + $res .= $c; + $n++; + } + + return $res; +} +sub copy_spacing { + (my $res = shift) =~ tr/\t/ /c; + return $res; +} + +sub line_stats { + my ($line) = @_; + + # Drop the diff line leader and expand tabs + $line =~ s/^.//; + $line = expand_tabs($line); + + # Pick the indent from the front of the line. + my ($white) = ($line =~ /^(\s*)/); + + return (length($line), length($white)); +} + +my $sanitise_quote = ''; + +sub sanitise_line_reset { + my ($in_comment) = @_; + + if ($in_comment) { + $sanitise_quote = '*/'; + } else { + $sanitise_quote = ''; + } +} +sub sanitise_line { + my ($line) = @_; + + my $res = ''; + my $l = ''; + + my $qlen = 0; + my $off = 0; + my $c; + + # Always copy over the diff marker. + $res = substr($line, 0, 1); + + for ($off = 1; $off < length($line); $off++) { + $c = substr($line, $off, 1); + + # Comments we are wacking completly including the begin + # and end, all to $;. + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { + $sanitise_quote = '*/'; + + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { + $sanitise_quote = ''; + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { + $sanitise_quote = '//'; + + substr($res, $off, 2, $sanitise_quote); + $off++; + next; + } + + # A \ in a string means ignore the next character. + if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && + $c eq "\\") { + substr($res, $off, 2, 'XX'); + $off++; + next; + } + # Regular quotes. + if ($c eq "'" || $c eq '"') { + if ($sanitise_quote eq '') { + $sanitise_quote = $c; + + substr($res, $off, 1, $c); + next; + } elsif ($sanitise_quote eq $c) { + $sanitise_quote = ''; + } + } + + #print "c<$c> SQ<$sanitise_quote>\n"; + if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { + substr($res, $off, 1, 'X'); + } else { + substr($res, $off, 1, $c); + } + } + + if ($sanitise_quote eq '//') { + $sanitise_quote = ''; + } + + # The pathname on a #include may be surrounded by '<' and '>'. + if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { + my $clean = 'X' x length($1); + $res =~ s@\<.*\>@<$clean>@; + + # The whole of a #error is a string. + } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { + my $clean = 'X' x length($1); + $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; + } + + return $res; +} + +sub get_quoted_string { + my ($line, $rawline) = @_; + + return "" if ($line !~ m/($String)/g); + return substr($rawline, $-[0], $+[0] - $-[0]); +} + +sub ctx_statement_block { + my ($linenr, $remain, $off) = @_; + my $line = $linenr - 1; + my $blk = ''; + my $soff = $off; + my $coff = $off - 1; + my $coff_set = 0; + + my $loff = 0; + + my $type = ''; + my $level = 0; + my @stack = (); + my $p; + my $c; + my $len = 0; + + my $remainder; + while (1) { + @stack = (['', 0]) if ($#stack == -1); + + #warn "CSB: blk<$blk> remain<$remain>\n"; + # If we are about to drop off the end, pull in more + # context. + if ($off >= $len) { + for (; $remain > 0; $line++) { + last if (!defined $lines[$line]); + next if ($lines[$line] =~ /^-/); + $remain--; + $loff = $len; + $blk .= $lines[$line] . "\n"; + $len = length($blk); + $line++; + last; + } + # Bail if there is no further context. + #warn "CSB: blk<$blk> off<$off> len<$len>\n"; + if ($off >= $len) { + last; + } + if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { + $level++; + $type = '#'; + } + } + $p = $c; + $c = substr($blk, $off, 1); + $remainder = substr($blk, $off); + + #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; + + # Handle nested #if/#else. + if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, [ $type, $level ]); + } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { + ($type, $level) = @{$stack[$#stack - 1]}; + } elsif ($remainder =~ /^#\s*endif\b/) { + ($type, $level) = @{pop(@stack)}; + } + + # Statement ends at the ';' or a close '}' at the + # outermost level. + if ($level == 0 && $c eq ';') { + last; + } + + # An else is really a conditional as long as its not else if + if ($level == 0 && $coff_set == 0 && + (!defined($p) || $p =~ /(?:\s|\}|\+)/) && + $remainder =~ /^(else)(?:\s|{)/ && + $remainder !~ /^else\s+if\b/) { + $coff = $off + length($1) - 1; + $coff_set = 1; + #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; + #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; + } + + if (($type eq '' || $type eq '(') && $c eq '(') { + $level++; + $type = '('; + } + if ($type eq '(' && $c eq ')') { + $level--; + $type = ($level != 0)? '(' : ''; + + if ($level == 0 && $coff < $soff) { + $coff = $off; + $coff_set = 1; + #warn "CSB: mark coff<$coff>\n"; + } + } + if (($type eq '' || $type eq '{') && $c eq '{') { + $level++; + $type = '{'; + } + if ($type eq '{' && $c eq '}') { + $level--; + $type = ($level != 0)? '{' : ''; + + if ($level == 0) { + if (substr($blk, $off + 1, 1) eq ';') { + $off++; + } + last; + } + } + # Preprocessor commands end at the newline unless escaped. + if ($type eq '#' && $c eq "\n" && $p ne "\\") { + $level--; + $type = ''; + $off++; + last; + } + $off++; + } + # We are truly at the end, so shuffle to the next line. + if ($off == $len) { + $loff = $len + 1; + $line++; + $remain--; + } + + my $statement = substr($blk, $soff, $off - $soff + 1); + my $condition = substr($blk, $soff, $coff - $soff + 1); + + #warn "STATEMENT<$statement>\n"; + #warn "CONDITION<$condition>\n"; + + #print "coff<$coff> soff<$off> loff<$loff>\n"; + + return ($statement, $condition, + $line, $remain + 1, $off - $loff + 1, $level); +} + +sub statement_lines { + my ($stmt) = @_; + + # Strip the diff line prefixes and rip blank lines at start and end. + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_rawlines { + my ($stmt) = @_; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_block_size { + my ($stmt) = @_; + + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*{//; + $stmt =~ s/}\s*$//; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + my @stmt_statements = ($stmt =~ /;/g); + + my $stmt_lines = $#stmt_lines + 2; + my $stmt_statements = $#stmt_statements + 1; + + if ($stmt_lines > $stmt_statements) { + return $stmt_lines; + } else { + return $stmt_statements; + } +} + +sub ctx_statement_full { + my ($linenr, $remain, $off) = @_; + my ($statement, $condition, $level); + + my (@chunks); + + # Grab the first conditional/block pair. + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "F: c<$condition> s<$statement> remain<$remain>\n"; + push(@chunks, [ $condition, $statement ]); + if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { + return ($level, $linenr, @chunks); + } + + # Pull in the following conditional/block pairs and see if they + # could continue the statement. + for (;;) { + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "C: c<$condition> s<$statement> remain<$remain>\n"; + last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); + #print "C: push\n"; + push(@chunks, [ $condition, $statement ]); + } + + return ($level, $linenr, @chunks); +} + +sub ctx_block_get { + my ($linenr, $remain, $outer, $open, $close, $off) = @_; + my $line; + my $start = $linenr - 1; + my $blk = ''; + my @o; + my @c; + my @res = (); + + my $level = 0; + my @stack = ($level); + for ($line = $start; $remain > 0; $line++) { + next if ($rawlines[$line] =~ /^-/); + $remain--; + + $blk .= $rawlines[$line]; + + # Handle nested #if/#else. + if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, $level); + } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { + $level = $stack[$#stack - 1]; + } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { + $level = pop(@stack); + } + + foreach my $c (split(//, $lines[$line])) { + ##print "C<$c>L<$level><$open$close>O<$off>\n"; + if ($off > 0) { + $off--; + next; + } + + if ($c eq $close && $level > 0) { + $level--; + last if ($level == 0); + } elsif ($c eq $open) { + $level++; + } + } + + if (!$outer || $level <= 1) { + push(@res, $rawlines[$line]); + } + + last if ($level == 0); + } + + return ($level, @res); +} +sub ctx_block_outer { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); + return @r; +} +sub ctx_block { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); + return @r; +} +sub ctx_statement { + my ($linenr, $remain, $off) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); + return @r; +} +sub ctx_block_level { + my ($linenr, $remain) = @_; + + return ctx_block_get($linenr, $remain, 0, '{', '}', 0); +} +sub ctx_statement_level { + my ($linenr, $remain, $off) = @_; + + return ctx_block_get($linenr, $remain, 0, '(', ')', $off); +} + +sub ctx_locate_comment { + my ($first_line, $end_line) = @_; + + # Catch a comment on the end of the line itself. + my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); + return $current_comment if (defined $current_comment); + + # Look through the context and try and figure out if there is a + # comment. + my $in_comment = 0; + $current_comment = ''; + for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { + my $line = $rawlines[$linenr - 1]; + #warn " $line\n"; + if ($linenr == $first_line and $line =~ m@^.\s*\*@) { + $in_comment = 1; + } + if ($line =~ m@/\*@) { + $in_comment = 1; + } + if (!$in_comment && $current_comment ne '') { + $current_comment = ''; + } + $current_comment .= $line . "\n" if ($in_comment); + if ($line =~ m@\*/@) { + $in_comment = 0; + } + } + + chomp($current_comment); + return($current_comment); +} +sub ctx_has_comment { + my ($first_line, $end_line) = @_; + my $cmt = ctx_locate_comment($first_line, $end_line); + + ##print "LINE: $rawlines[$end_line - 1 ]\n"; + ##print "CMMT: $cmt\n"; + + return ($cmt ne ''); +} + +sub raw_line { + my ($linenr, $cnt) = @_; + + my $offset = $linenr - 1; + $cnt++; + + my $line; + while ($cnt) { + $line = $rawlines[$offset++]; + next if (defined($line) && $line =~ /^-/); + $cnt--; + } + + return $line; +} + +sub cat_vet { + my ($vet) = @_; + my ($res, $coded); + + $res = ''; + while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { + $res .= $1; + if ($2 ne '') { + $coded = sprintf("^%c", unpack('C', $2) + 64); + $res .= $coded; + } + } + $res =~ s/$/\$/; + + return $res; +} + +my $av_preprocessor = 0; +my $av_pending; +my @av_paren_type; +my $av_pend_colon; + +sub annotate_reset { + $av_preprocessor = 0; + $av_pending = '_'; + @av_paren_type = ('E'); + $av_pend_colon = 'O'; +} + +sub annotate_values { + my ($stream, $type) = @_; + + my $res; + my $var = '_' x length($stream); + my $cur = $stream; + + print "$stream\n" if ($dbg_values > 1); + + while (length($cur)) { + @av_paren_type = ('E') if ($#av_paren_type < 0); + print " <" . join('', @av_paren_type) . + "> <$type> <$av_pending>" if ($dbg_values > 1); + if ($cur =~ /^(\s+)/o) { + print "WS($1)\n" if ($dbg_values > 1); + if ($1 =~ /\n/ && $av_preprocessor) { + $type = pop(@av_paren_type); + $av_preprocessor = 0; + } + + } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { + print "CAST($1)\n" if ($dbg_values > 1); + push(@av_paren_type, $type); + $type = 'c'; + + } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { + print "DECLARE($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^($Modifier)\s*/) { + print "MODIFIER($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { + print "DEFINE($1,$2)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + if ($2 ne '') { + $av_pending = 'N'; + } + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { + print "UNDEF($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + + } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { + print "PRE_START($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { + print "PRE_RESTART($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $av_paren_type[$#av_paren_type]); + + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:endif))/o) { + print "PRE_END($1)\n" if ($dbg_values > 1); + + $av_preprocessor = 1; + + # Assume all arms of the conditional end as this + # one does, and continue as if the #endif was not here. + pop(@av_paren_type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\\\n)/o) { + print "PRECONT($1)\n" if ($dbg_values > 1); + + } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { + print "ATTR($1)\n" if ($dbg_values > 1); + $av_pending = $type; + $type = 'N'; + + } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { + print "SIZEOF($1)\n" if ($dbg_values > 1); + if (defined $2) { + $av_pending = 'V'; + } + $type = 'N'; + + } elsif ($cur =~ /^(if|while|for)\b/o) { + print "COND($1)\n" if ($dbg_values > 1); + $av_pending = 'E'; + $type = 'N'; + + } elsif ($cur =~/^(case)/o) { + print "CASE($1)\n" if ($dbg_values > 1); + $av_pend_colon = 'C'; + $type = 'N'; + + } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { + print "KEYWORD($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(\()/o) { + print "PAREN('$1')\n" if ($dbg_values > 1); + push(@av_paren_type, $av_pending); + $av_pending = '_'; + $type = 'N'; + + } elsif ($cur =~ /^(\))/o) { + my $new_type = pop(@av_paren_type); + if ($new_type ne '_') { + $type = $new_type; + print "PAREN('$1') -> $type\n" + if ($dbg_values > 1); + } else { + print "PAREN('$1')\n" if ($dbg_values > 1); + } + + } elsif ($cur =~ /^($Ident)\s*\(/o) { + print "FUNC($1)\n" if ($dbg_values > 1); + $type = 'V'; + $av_pending = 'V'; + + } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { + if (defined $2 && $type eq 'C' || $type eq 'T') { + $av_pend_colon = 'B'; + } elsif ($type eq 'E') { + $av_pend_colon = 'L'; + } + print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Ident|$Constant)/o) { + print "IDENT($1)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Assignment)/o) { + print "ASSIGN($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~/^(;|{|})/) { + print "END($1)\n" if ($dbg_values > 1); + $type = 'E'; + $av_pend_colon = 'O'; + + } elsif ($cur =~/^(,)/) { + print "COMMA($1)\n" if ($dbg_values > 1); + $type = 'C'; + + } elsif ($cur =~ /^(\?)/o) { + print "QUESTION($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(:)/o) { + print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); + + substr($var, length($res), 1, $av_pend_colon); + if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { + $type = 'E'; + } else { + $type = 'N'; + } + $av_pend_colon = 'O'; + + } elsif ($cur =~ /^(\[)/o) { + print "CLOSE($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { + my $variant; + + print "OPV($1)\n" if ($dbg_values > 1); + if ($type eq 'V') { + $variant = 'B'; + } else { + $variant = 'U'; + } + + substr($var, length($res), 1, $variant); + $type = 'N'; + + } elsif ($cur =~ /^($Operators)/o) { + print "OP($1)\n" if ($dbg_values > 1); + if ($1 ne '++' && $1 ne '--') { + $type = 'N'; + } + + } elsif ($cur =~ /(^.)/o) { + print "C($1)\n" if ($dbg_values > 1); + } + if (defined $1) { + $cur = substr($cur, length($1)); + $res .= $type x length($1); + } + } + + return ($res, $var); +} + +sub possible { + my ($possible, $line) = @_; + my $notPermitted = qr{(?: + ^(?: + $Modifier| + $Storage| + $Type| + DEFINE_\S+ + )$| + ^(?: + goto| + return| + case| + else| + asm|__asm__| + do| + \#| + \#\#| + )(?:\s|$)| + ^(?:typedef|struct|enum)\b + )}x; + warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); + if ($possible !~ $notPermitted) { + # Check for modifiers. + $possible =~ s/\s*$Storage\s*//g; + $possible =~ s/\s*$Sparse\s*//g; + if ($possible =~ /^\s*$/) { + + } elsif ($possible =~ /\s/) { + $possible =~ s/\s*$Type\s*//g; + for my $modifier (split(' ', $possible)) { + if ($modifier !~ $notPermitted) { + warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); + push(@modifierListFile, $modifier); + } + } + + } else { + warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); + push(@typeListFile, $possible); + } + build_types(); + } else { + warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); + } +} + +my $prefix = ''; + +sub show_type { + my ($type) = @_; + + return defined $use_type{$type} if (scalar keys %use_type > 0); + + return !defined $ignore_type{$type}; +} + +sub report { + my ($level, $type, $msg) = @_; + + if (!show_type($type) || + (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { + return 0; + } + my $output = ''; + if (-t STDOUT && $color) { + if ($level eq 'ERROR') { + $output .= RED; + } elsif ($level eq 'WARNING') { + $output .= YELLOW; + } else { + $output .= GREEN; + } + } + $output .= $prefix . $level . ':'; + if ($show_types) { + $output .= BLUE if (-t STDOUT && $color); + $output .= "$type:"; + } + $output .= RESET if (-t STDOUT && $color); + $output .= ' ' . $msg . "\n"; + + if ($showfile) { + my @lines = split("\n", $output, -1); + splice(@lines, 1, 1); + $output = join("\n", @lines); + } + $output = (split('\n', $output))[0] . "\n" if ($terse); + + push(our @report, $output); + + return 1; +} + +sub report_dump { + our @report; +} + +sub fixup_current_range { + my ($lineRef, $offset, $length) = @_; + + if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { + my $o = $1; + my $l = $2; + my $no = $o + $offset; + my $nl = $l + $length; + $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; + } +} + +sub fix_inserted_deleted_lines { + my ($linesRef, $insertedRef, $deletedRef) = @_; + + my $range_last_linenr = 0; + my $delta_offset = 0; + + my $old_linenr = 0; + my $new_linenr = 0; + + my $next_insert = 0; + my $next_delete = 0; + + my @lines = (); + + my $inserted = @{$insertedRef}[$next_insert++]; + my $deleted = @{$deletedRef}[$next_delete++]; + + foreach my $old_line (@{$linesRef}) { + my $save_line = 1; + my $line = $old_line; #don't modify the array + if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename + $delta_offset = 0; + } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk + $range_last_linenr = $new_linenr; + fixup_current_range(\$line, $delta_offset, 0); + } + + while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { + $deleted = @{$deletedRef}[$next_delete++]; + $save_line = 0; + fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); + } + + while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { + push(@lines, ${$inserted}{'LINE'}); + $inserted = @{$insertedRef}[$next_insert++]; + $new_linenr++; + fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); + } + + if ($save_line) { + push(@lines, $line); + $new_linenr++; + } + + $old_linenr++; + } + + return @lines; +} + +sub fix_insert_line { + my ($linenr, $line) = @_; + + my $inserted = { + LINENR => $linenr, + LINE => $line, + }; + push(@fixed_inserted, $inserted); +} + +sub fix_delete_line { + my ($linenr, $line) = @_; + + my $deleted = { + LINENR => $linenr, + LINE => $line, + }; + + push(@fixed_deleted, $deleted); +} + +sub ERROR { + my ($type, $msg) = @_; + + if (report("ERROR", $type, $msg)) { + our $clean = 0; + our $cnt_error++; + return 1; + } + return 0; +} +sub WARN { + my ($type, $msg) = @_; + + if (report("WARNING", $type, $msg)) { + our $clean = 0; + our $cnt_warn++; + return 1; + } + return 0; +} +sub CHK { + my ($type, $msg) = @_; + + if ($check && report("CHECK", $type, $msg)) { + our $clean = 0; + our $cnt_chk++; + return 1; + } + return 0; +} + +sub check_absolute_file { + my ($absolute, $herecurr) = @_; + my $file = $absolute; + + ##print "absolute<$absolute>\n"; + + # See if any suffix of this path is a path within the tree. + while ($file =~ s@^[^/]*/@@) { + if (-f "$root/$file") { + ##print "file<$file>\n"; + last; + } + } + if (! -f _) { + return 0; + } + + # It is, so see if the prefix is acceptable. + my $prefix = $absolute; + substr($prefix, -length($file)) = ''; + + ##print "prefix<$prefix>\n"; + if ($prefix ne ".../") { + WARN("USE_RELATIVE_PATH", + "use relative pathname instead of absolute in changelog text\n" . $herecurr); + } +} + +sub trim { + my ($string) = @_; + + $string =~ s/^\s+|\s+$//g; + + return $string; +} + +sub ltrim { + my ($string) = @_; + + $string =~ s/^\s+//; + + return $string; +} + +sub rtrim { + my ($string) = @_; + + $string =~ s/\s+$//; + + return $string; +} + +sub string_find_replace { + my ($string, $find, $replace) = @_; + + $string =~ s/$find/$replace/g; + + return $string; +} + +sub tabify { + my ($leading) = @_; + + my $source_indent = 8; + my $max_spaces_before_tab = $source_indent - 1; + my $spaces_to_tab = " " x $source_indent; + + #convert leading spaces to tabs + 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; + #Remove spaces before a tab + 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; + + return "$leading"; +} + +sub pos_last_openparen { + my ($line) = @_; + + my $pos = 0; + + my $opens = $line =~ tr/\(/\(/; + my $closes = $line =~ tr/\)/\)/; + + my $last_openparen = 0; + + if (($opens == 0) || ($closes >= $opens)) { + return -1; + } + + my $len = length($line); + + for ($pos = 0; $pos < $len; $pos++) { + my $string = substr($line, $pos); + if ($string =~ /^($FuncArg|$balanced_parens)/) { + $pos += length($1) - 1; + } elsif (substr($line, $pos, 1) eq '(') { + $last_openparen = $pos; + } elsif (index($string, '(') == -1) { + last; + } + } + + return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; +} + +sub process { + my $filename = shift; + + my $linenr=0; + my $prevline=""; + my $prevrawline=""; + my $stashline=""; + my $stashrawline=""; + + my $length; + my $indent; + my $previndent=0; + my $stashindent=0; + + our $clean = 1; + my $signoff = 0; + my $is_patch = 0; + my $in_header_lines = $file ? 0 : 1; + my $in_commit_log = 0; #Scanning lines before patch + my $commit_log_possible_stack_dump = 0; + my $commit_log_long_line = 0; + my $commit_log_has_diff = 0; + my $reported_maintainer_file = 0; + my $non_utf8_charset = 0; + + my $last_blank_line = 0; + my $last_coalesced_string_linenr = -1; + + our @report = (); + our $cnt_lines = 0; + our $cnt_error = 0; + our $cnt_warn = 0; + our $cnt_chk = 0; + + # Trace the real file/line as we go. + my $realfile = ''; + my $realline = 0; + my $realcnt = 0; + my $here = ''; + my $in_comment = 0; + my $comment_edge = 0; + my $first_line = 0; + my $p1_prefix = ''; + + my $prev_values = 'E'; + + # suppression flags + my %suppress_ifbraces; + my %suppress_whiletrailers; + my %suppress_export; + my $suppress_statement = 0; + + my %signatures = (); + + # Pre-scan the patch sanitizing the lines. + # Pre-scan the patch looking for any __setup documentation. + # + my @setup_docs = (); + my $setup_docs = 0; + + my $camelcase_file_seeded = 0; + + sanitise_line_reset(); + my $line; + foreach my $rawline (@rawlines) { + $linenr++; + $line = $rawline; + + push(@fixed, $rawline) if ($fix); + + if ($rawline=~/^\+\+\+\s+(\S+)/) { + $setup_docs = 0; + if ($1 =~ m@Documentation/kernel-parameters.txt$@) { + $setup_docs = 1; + } + #next; + } + if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + $in_comment = 0; + + # Guestimate if this is a continuing comment. Run + # the context looking for a comment "edge". If this + # edge is a close comment then we must be in a comment + # at context start. + my $edge; + my $cnt = $realcnt; + for (my $ln = $linenr + 1; $cnt > 0; $ln++) { + next if (defined $rawlines[$ln - 1] && + $rawlines[$ln - 1] =~ /^-/); + $cnt--; + #print "RAW<$rawlines[$ln - 1]>\n"; + last if (!defined $rawlines[$ln - 1]); + if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && + $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { + ($edge) = $1; + last; + } + } + if (defined $edge && $edge eq '*/') { + $in_comment = 1; + } + + # Guestimate if this is a continuing comment. If this + # is the start of a diff block and this line starts + # ' *' then it is very likely a comment. + if (!defined $edge && + $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) + { + $in_comment = 1; + } + + ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; + sanitise_line_reset($in_comment); + + } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { + # Standardise the strings and chars within the input to + # simplify matching -- only bother with positive lines. + $line = sanitise_line($rawline); + } + push(@lines, $line); + + if ($realcnt > 1) { + $realcnt-- if ($line =~ /^(?:\+| |$)/); + } else { + $realcnt = 0; + } + + #print "==>$rawline\n"; + #print "-->$line\n"; + + if ($setup_docs && $line =~ /^\+/) { + push(@setup_docs, $line); + } + } + + $prefix = ''; + + $realcnt = 0; + $linenr = 0; + $fixlinenr = -1; + foreach my $line (@lines) { + $linenr++; + $fixlinenr++; + my $sline = $line; #copy of $line + $sline =~ s/$;/ /g; #with comments as spaces + + my $rawline = $rawlines[$linenr - 1]; + +#extract the line range in the file after the patch is applied + if (!$in_commit_log && + $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $is_patch = 1; + $first_line = $linenr + 1; + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + annotate_reset(); + $prev_values = 'E'; + + %suppress_ifbraces = (); + %suppress_whiletrailers = (); + %suppress_export = (); + $suppress_statement = 0; + next; + +# track the line number as we move through the hunk, note that +# new versions of GNU diff omit the leading space on completely +# blank context lines so we need to count that too. + } elsif ($line =~ /^( |\+|$)/) { + $realline++; + $realcnt-- if ($realcnt != 0); + + # Measure the line length and indent. + ($length, $indent) = line_stats($rawline); + + # Track the previous line. + ($prevline, $stashline) = ($stashline, $line); + ($previndent, $stashindent) = ($stashindent, $indent); + ($prevrawline, $stashrawline) = ($stashrawline, $rawline); + + #warn "line<$line>\n"; + + } elsif ($realcnt == 1) { + $realcnt--; + } + + my $hunk_line = ($realcnt != 0); + + $here = "#$linenr: " if (!$file); + $here = "#$realline: " if ($file); + + my $found_file = 0; + # extract the filename as it passes + if ($line =~ /^diff --git.*?(\S+)$/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@ if (!$file); + $in_commit_log = 0; + $found_file = 1; + } elsif ($line =~ /^\+\+\+\s+(\S+)/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@ if (!$file); + $in_commit_log = 0; + + $p1_prefix = $1; + if (!$file && $tree && $p1_prefix ne '' && + -e "$root/$p1_prefix") { + WARN("PATCH_PREFIX", + "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); + } + + if ($realfile =~ m@^include/asm/@) { + ERROR("MODIFIED_INCLUDE_ASM", + "do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); + } + $found_file = 1; + } + +#make up the handle for any error we report on this line + if ($showfile) { + $prefix = "$realfile:$realline: " + } elsif ($emacs) { + if ($file) { + $prefix = "$filename:$realline: "; + } else { + $prefix = "$filename:$linenr: "; + } + } + + if ($found_file) { + if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { + $check = 1; + } else { + $check = $check_orig; + } + next; + } + + $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); + + my $hereline = "$here\n$rawline\n"; + my $herecurr = "$here\n$rawline\n"; + my $hereprev = "$here\n$prevrawline\n$rawline\n"; + + $cnt_lines++ if ($realcnt != 0); + +# Check if the commit log has what seems like a diff which can confuse patch + if ($in_commit_log && !$commit_log_has_diff && + (($line =~ m@^\s+diff\b.*a/[\w/]+@ && + $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) || + $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || + $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { + ERROR("DIFF_IN_COMMIT_MSG", + "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); + $commit_log_has_diff = 1; + } + +# Check for incorrect file permissions + if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { + my $permhere = $here . "FILE: $realfile\n"; + if ($realfile !~ m@scripts/@ && + $realfile !~ /\.(py|pl|awk|sh)$/) { + ERROR("EXECUTE_PERMISSIONS", + "do not set execute permissions for source files\n" . $permhere); + } + } + +# Check the patch for a signoff: + if ($line =~ /^\s*signed-off-by:/i) { + $signoff++; + $in_commit_log = 0; + } + +# Check if MAINTAINERS is being updated. If so, there's probably no need to +# emit the "does MAINTAINERS need updating?" message on file add/move/delete + if ($line =~ /^\s*MAINTAINERS\s*\|/) { + $reported_maintainer_file = 1; + } + +# Check signature styles + if (!$in_header_lines && + $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { + my $space_before = $1; + my $sign_off = $2; + my $space_after = $3; + my $email = $4; + my $ucfirst_sign_off = ucfirst(lc($sign_off)); + + if ($sign_off !~ /$signature_tags/) { + WARN("BAD_SIGN_OFF", + "Non-standard signature: $sign_off\n" . $herecurr); + } + if (defined $space_before && $space_before ne "") { + if (WARN("BAD_SIGN_OFF", + "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + } + if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { + if (WARN("BAD_SIGN_OFF", + "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + + } + if (!defined $space_after || $space_after ne " ") { + if (WARN("BAD_SIGN_OFF", + "Use a single space after $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + } + + my ($email_name, $email_address, $comment) = parse_email($email); + my $suggested_email = format_email(($email_name, $email_address)); + if ($suggested_email eq "") { + ERROR("BAD_SIGN_OFF", + "Unrecognized email address: '$email'\n" . $herecurr); + } else { + my $dequoted = $suggested_email; + $dequoted =~ s/^"//; + $dequoted =~ s/" $comment" ne $email && + "$suggested_email$comment" ne $email) { + WARN("BAD_SIGN_OFF", + "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); + } + } + +# Check for duplicate signatures + my $sig_nospace = $line; + $sig_nospace =~ s/\s//g; + $sig_nospace = lc($sig_nospace); + if (defined $signatures{$sig_nospace}) { + WARN("BAD_SIGN_OFF", + "Duplicate signature\n" . $herecurr); + } else { + $signatures{$sig_nospace} = 1; + } + } + +# Check email subject for common tools that don't need to be mentioned + if ($in_header_lines && + $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { + WARN("EMAIL_SUBJECT", + "A patch subject line should describe the change not the tool that found it\n" . $herecurr); + } + +# Check for old stable address + if ($line =~ /^\s*cc:\s*.*?.*$/i) { + ERROR("STABLE_ADDRESS", + "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr); + } + +# Check for unwanted Gerrit info + if ($in_commit_log && $line =~ /^\s*change-id:/i) { + ERROR("GERRIT_CHANGE_ID", + "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); + } + +# Check if the commit log is in a possible stack dump + if ($in_commit_log && !$commit_log_possible_stack_dump && + ($line =~ /^\s*(?:WARNING:|BUG:)/ || + $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || + # timestamp + $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) { + # stack dump address + $commit_log_possible_stack_dump = 1; + } + +# Check for line lengths > 75 in commit log, warn once + if ($in_commit_log && !$commit_log_long_line && + length($line) > 75 && + !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || + # file delta changes + $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || + # filename then : + $line =~ /^\s*(?:Fixes:|Link:)/i || + # A Fixes: or Link: line + $commit_log_possible_stack_dump)) { + WARN("COMMIT_LOG_LONG_LINE", + "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); + $commit_log_long_line = 1; + } + +# Reset possible stack dump if a blank line is found + if ($in_commit_log && $commit_log_possible_stack_dump && + $line =~ /^\s*$/) { + $commit_log_possible_stack_dump = 0; + } + +# Check for git id commit length and improperly formed commit descriptions + if ($in_commit_log && !$commit_log_possible_stack_dump && + ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || + ($line =~ /\b[0-9a-f]{12,40}\b/i && + $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && + $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { + my $init_char = "c"; + my $orig_commit = ""; + my $short = 1; + my $long = 0; + my $case = 1; + my $space = 1; + my $hasdesc = 0; + my $hasparens = 0; + my $id = '0123456789ab'; + my $orig_desc = "commit description"; + my $description = ""; + + if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { + $init_char = $1; + $orig_commit = lc($2); + } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) { + $orig_commit = lc($1); + } + + $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i); + $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i); + $space = 0 if ($line =~ /\bcommit [0-9a-f]/i); + $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); + if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) { + $orig_desc = $1; + $hasparens = 1; + } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i && + defined $rawlines[$linenr] && + $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) { + $orig_desc = $1; + $hasparens = 1; + } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i && + defined $rawlines[$linenr] && + $rawlines[$linenr] =~ /^\s*[^"]+"\)/) { + $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i; + $orig_desc = $1; + $rawlines[$linenr] =~ /^\s*([^"]+)"\)/; + $orig_desc .= " " . $1; + $hasparens = 1; + } + + ($id, $description) = git_commit_info($orig_commit, + $id, $orig_desc); + + if ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens) { + ERROR("GIT_COMMIT_ID", + "Please use git commit description style 'commit <12+ chars of sha1> (\"\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); + } + } + +# Check for added, moved or deleted files + if (!$reported_maintainer_file && !$in_commit_log && + ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || + $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || + ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && + (defined($1) || defined($2))))) { + $reported_maintainer_file = 1; + WARN("FILE_PATH_CHANGES", + "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); + } + +# Check for wrappage within a valid hunk of the file + if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { + ERROR("CORRUPTED_PATCH", + "patch seems to be corrupt (line wrapped?)\n" . + $herecurr) if (!$emitted_corrupt++); + } + +# Check for absolute kernel paths. + if ($tree) { + while ($line =~ m{(?:^|\s)(/\S*)}g) { + my $file = $1; + + if ($file =~ m{^(.*?)(?::\d+)+:?$} && + check_absolute_file($1, $herecurr)) { + # + } else { + check_absolute_file($file, $herecurr); + } + } + } + +# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php + if (($realfile =~ /^$/ || $line =~ /^\+/) && + $rawline !~ m/^$UTF8*$/) { + my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); + + my $blank = copy_spacing($rawline); + my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; + my $hereptr = "$hereline$ptr\n"; + + CHK("INVALID_UTF8", + "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); + } + +# Check if it's the start of a commit log +# (not a header line and we haven't seen the patch filename) + if ($in_header_lines && $realfile =~ /^$/ && + !($rawline =~ /^\s+\S/ || + $rawline =~ /^(commit\b|from\b|[\w-]+:).*$/i)) { + $in_header_lines = 0; + $in_commit_log = 1; + } + +# Check if there is UTF-8 in a commit log when a mail header has explicitly +# declined it, i.e defined some charset where it is missing. + if ($in_header_lines && + $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && + $1 !~ /utf-8/i) { + $non_utf8_charset = 1; + } + + if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && + $rawline =~ /$NON_ASCII_UTF8/) { + WARN("UTF8_BEFORE_PATCH", + "8-bit UTF-8 used in possible commit log\n" . $herecurr); + } + +# Check for various typo / spelling mistakes + if (defined($misspellings) && + ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { + while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) { + my $typo = $1; + my $typo_fix = $spelling_fix{lc($typo)}; + $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); + $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); + my $msg_type = \&WARN; + $msg_type = \&CHK if ($file); + if (&{$msg_type}("TYPO_SPELLING", + "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; + } + } + } + +# ignore non-hunk lines and lines being removed + next if (!$hunk_line || $line =~ /^-/); + +#trailing whitespace + if ($line =~ /^\+.*\015/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (ERROR("DOS_LINE_ENDINGS", + "DOS line endings\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/[\s\015]+$//; + } + } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (ERROR("TRAILING_WHITESPACE", + "trailing whitespace\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+$//; + } + + $rpt_cleaners = 1; + } + +# Check for FSF mailing addresses. + if ($rawline =~ /\bwrite to the Free/i || + $rawline =~ /\b59\s+Temple\s+Pl/i || + $rawline =~ /\b51\s+Franklin\s+St/i) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + my $msg_type = \&ERROR; + $msg_type = \&CHK if ($file); + &{$msg_type}("FSF_MAILING_ADDRESS", + "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) + } + +# check for Kconfig help text having a real description +# Only applies when adding the entry originally, after that we do not have +# sufficient context to determine whether it is indeed long enough. + if ($realfile =~ /Kconfig/ && + $line =~ /^\+\s*config\s+/) { + my $length = 0; + my $cnt = $realcnt; + my $ln = $linenr + 1; + my $f; + my $is_start = 0; + my $is_end = 0; + for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) { + $f = $lines[$ln - 1]; + $cnt-- if ($lines[$ln - 1] !~ /^-/); + $is_end = $lines[$ln - 1] =~ /^\+/; + + next if ($f =~ /^-/); + last if (!$file && $f =~ /^\@\@/); + + if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { + $is_start = 1; + } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { + $length = -1; + } + + $f =~ s/^.//; + $f =~ s/#.*//; + $f =~ s/^\s+//; + next if ($f =~ /^$/); + if ($f =~ /^\s*config\s/) { + $is_end = 1; + last; + } + $length++; + } + if ($is_start && $is_end && $length < $min_conf_desc_length) { + WARN("CONFIG_DESCRIPTION", + "please write a paragraph that describes the config symbol fully\n" . $herecurr); + } + #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; + } + +# discourage the addition of CONFIG_EXPERIMENTAL in Kconfig. + if ($realfile =~ /Kconfig/ && + $line =~ /.\s*depends on\s+.*\bEXPERIMENTAL\b/) { + WARN("CONFIG_EXPERIMENTAL", + "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); + } + +# discourage the use of boolean for type definition attributes of Kconfig options + if ($realfile =~ /Kconfig/ && + $line =~ /^\+\s*\bboolean\b/) { + WARN("CONFIG_TYPE_BOOLEAN", + "Use of boolean is deprecated, please use bool instead.\n" . $herecurr); + } + + if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && + ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { + my $flag = $1; + my $replacement = { + 'EXTRA_AFLAGS' => 'asflags-y', + 'EXTRA_CFLAGS' => 'ccflags-y', + 'EXTRA_CPPFLAGS' => 'cppflags-y', + 'EXTRA_LDFLAGS' => 'ldflags-y', + }; + + WARN("DEPRECATED_VARIABLE", + "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); + } + +# check for DT compatible documentation + if (defined $root && + (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || + ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { + + my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; + + my $dt_path = $root . "/Documentation/devicetree/bindings/"; + my $vp_file = $dt_path . "vendor-prefixes.txt"; + + foreach my $compat (@compats) { + my $compat2 = $compat; + $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/; + my $compat3 = $compat; + $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/; + `grep -Erq "$compat|$compat2|$compat3" $dt_path`; + if ( $? >> 8 ) { + WARN("UNDOCUMENTED_DT_STRING", + "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); + } + + next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; + my $vendor = $1; + `grep -Eq "^$vendor\\b" $vp_file`; + if ( $? >> 8 ) { + WARN("UNDOCUMENTED_DT_STRING", + "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); + } + } + } + +# check we are in a valid source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c|s|S|pl|sh|dtsi|dts)$/); + +# line length limit (with some exclusions) +# +# There are a few types of lines that may extend beyond $max_line_length: +# logging functions like pr_info that end in a string +# lines with a single string +# #defines that are a single string +# +# There are 3 different line length message types: +# LONG_LINE_COMMENT a comment starts before but extends beyond $max_linelength +# LONG_LINE_STRING a string starts before but extends beyond $max_line_length +# LONG_LINE all other lines longer than $max_line_length +# +# if LONG_LINE is ignored, the other 2 types are also ignored +# + + if ($line =~ /^\+/ && $length > $max_line_length) { + my $msg_type = "LONG_LINE"; + + # Check the allowed long line types first + + # logging functions that end in a string that starts + # before $max_line_length + if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = ""; + + # lines with only strings (w/ possible termination) + # #defines with only strings + } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || + $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { + $msg_type = ""; + + # Otherwise set the alternate message types + + # a comment starts before $max_line_length + } elsif ($line =~ /($;[\s$;]*)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = "LONG_LINE_COMMENT" + + # a quoted string starts before $max_line_length + } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = "LONG_LINE_STRING" + } + + if ($msg_type ne "" && + (show_type("LONG_LINE") || show_type($msg_type))) { + WARN($msg_type, + "line over $max_line_length characters\n" . $herecurr); + } + } + +# check for adding lines without a newline. + if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { + WARN("MISSING_EOF_NEWLINE", + "adding a line without newline at end of file\n" . $herecurr); + } + +# Blackfin: use hi/lo macros + if ($realfile =~ m@arch/blackfin/.*\.S$@) { + if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("LO_MACRO", + "use the LO() macro, not (... & 0xFFFF)\n" . $herevet); + } + if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("HI_MACRO", + "use the HI() macro, not (... >> 16)\n" . $herevet); + } + } + +# check we are in a valid source file C or perl if not then ignore this hunk + next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); + +# at the beginning of a line any tabs must come first and anything +# more than 8 must use tabs. + if ($rawline =~ /^\+\s* \t\s*\S/ || + $rawline =~ /^\+\s* \s*/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + $rpt_cleaners = 1; + if (ERROR("CODE_INDENT", + "code indent should use tabs where possible\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } + } + +# check for space before tabs. + if ($rawline =~ /^\+/ && $rawline =~ / \t/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (WARN("SPACE_BEFORE_TAB", + "please, no space before tabs\n" . $herevet) && + $fix) { + while ($fixed[$fixlinenr] =~ + s/(^\+.*) {8,8}\t/$1\t\t/) {} + while ($fixed[$fixlinenr] =~ + s/(^\+.*) +\t/$1\t/) {} + } + } + +# check for && or || at the start of a line + if ($rawline =~ /^\+\s*(&&|\|\|)/) { + CHK("LOGICAL_CONTINUATIONS", + "Logical continuations should be on the previous line\n" . $hereprev); + } + +# check multi-line statement indentation matches previous line + if ($^V && $^V ge 5.10.0 && + $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { + $prevline =~ /^\+(\t*)(.*)$/; + my $oldindent = $1; + my $rest = $2; + + my $pos = pos_last_openparen($rest); + if ($pos >= 0) { + $line =~ /^(\+| )([ \t]*)/; + my $newindent = $2; + + my $goodtabindent = $oldindent . + "\t" x ($pos / 8) . + " " x ($pos % 8); + my $goodspaceindent = $oldindent . " " x $pos; + + if ($newindent ne $goodtabindent && + $newindent ne $goodspaceindent) { + + if (CHK("PARENTHESIS_ALIGNMENT", + "Alignment should match open parenthesis\n" . $hereprev) && + $fix && $line =~ /^\+/) { + $fixed[$fixlinenr] =~ + s/^\+[ \t]*/\+$goodtabindent/; + } + } + } + } + +# check for space after cast like "(int) foo" or "(struct foo) bar" +# avoid checking a few false positives: +# "sizeof(<type>)" or "__alignof__(<type>)" +# function pointer declarations like "(*foo)(int) = bar;" +# structure definitions like "(struct foo) { 0 };" +# multiline macros that define functions +# known attributes or the __attribute__ keyword + if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && + (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { + if (CHK("SPACING", + "No space is necessary after a cast\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/(\(\s*$Type\s*\))[ \t]+/$1/; + } + } + +# Block comment styles +# Networking with an initial /* + if ($realfile =~ m@^(drivers/net/|net/)@ && + $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && + $rawline =~ /^\+[ \t]*\*/ && + $realline > 2) { + WARN("NETWORKING_BLOCK_COMMENT_STYLE", + "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); + } + +# Block comments use * on subsequent lines + if ($prevline =~ /$;[ \t]*$/ && #ends in comment + $prevrawline =~ /^\+.*?\/\*/ && #starting /* + $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ + $rawline =~ /^\+/ && #line is new + $rawline !~ /^\+[ \t]*\*/) { #no leading * + WARN("BLOCK_COMMENT_STYLE", + "Block comments use * on subsequent lines\n" . $hereprev); + } + +# Block comments use */ on trailing lines + if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ + $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ + $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ + $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ + WARN("BLOCK_COMMENT_STYLE", + "Block comments use a trailing */ on a separate line\n" . $herecurr); + } + +# check for missing blank lines after struct/union declarations +# with exceptions for various attributes and macros + if ($prevline =~ /^[\+ ]};?\s*$/ && + $line =~ /^\+/ && + !($line =~ /^\+\s*$/ || + $line =~ /^\+\s*EXPORT_SYMBOL/ || + $line =~ /^\+\s*MODULE_/i || + $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || + $line =~ /^\+[a-z_]*init/ || + $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || + $line =~ /^\+\s*DECLARE/ || + $line =~ /^\+\s*__setup/)) { + if (CHK("LINE_SPACING", + "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && + $fix) { + fix_insert_line($fixlinenr, "\+"); + } + } + +# check for multiple consecutive blank lines + if ($prevline =~ /^[\+ ]\s*$/ && + $line =~ /^\+\s*$/ && + $last_blank_line != ($linenr - 1)) { + if (CHK("LINE_SPACING", + "Please don't use multiple blank lines\n" . $hereprev) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } + + $last_blank_line = $linenr; + } + +# check for missing blank lines after declarations + if ($sline =~ /^\+\s+\S/ && #Not at char 1 + # actual declarations + ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || + # function pointer declarations + $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + # foo bar; where foo is some local typedef or #define + $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + # known declaration macros + $prevline =~ /^\+\s+$declaration_macros/) && + # for "else if" which can look like "$Ident $Ident" + !($prevline =~ /^\+\s+$c90_Keywords\b/ || + # other possible extensions of declaration lines + $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || + # not starting a section or a macro "\" extended line + $prevline =~ /(?:\{\s*|\\)$/) && + # looks like a declaration + !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || + # function pointer declarations + $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + # foo bar; where foo is some local typedef or #define + $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + # known declaration macros + $sline =~ /^\+\s+$declaration_macros/ || + # start of struct or union or enum + $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || + # start or end of block or continuation of declaration + $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || + # bitfield continuation + $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || + # other possible extensions of declaration lines + $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && + # indentation of previous and current line are the same + (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { + if (WARN("LINE_SPACING", + "Missing a blank line after declarations\n" . $hereprev) && + $fix) { + fix_insert_line($fixlinenr, "\+"); + } + } + +# check for spaces at the beginning of a line. +# Exceptions: +# 1) within comments +# 2) indented preprocessor commands +# 3) hanging labels + if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (WARN("LEADING_SPACE", + "please, no spaces at the start of a line\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } + } + +# check we are in a valid C source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c)$/); + +# check indentation of any line with a bare else +# (but not if it is a multiple line "if (foo) return bar; else return baz;") +# if the previous line is a break or return and is indented 1 tab more... + if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { + my $tabs = length($1) + 1; + if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || + ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && + defined $lines[$linenr] && + $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { + WARN("UNNECESSARY_ELSE", + "else is not generally useful after a break or return\n" . $hereprev); + } + } + +# check indentation of a line with a break; +# if the previous line is a goto or return and is indented the same # of tabs + if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { + my $tabs = $1; + if ($prevline =~ /^\+$tabs(?:goto|return)\b/) { + WARN("UNNECESSARY_BREAK", + "break is not useful after a goto or return\n" . $hereprev); + } + } + +# discourage the addition of CONFIG_EXPERIMENTAL in #if(def). + if ($line =~ /^\+\s*\#\s*if.*\bCONFIG_EXPERIMENTAL\b/) { + WARN("CONFIG_EXPERIMENTAL", + "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); + } + +# check for RCS/CVS revision markers + if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { + WARN("CVS_KEYWORD", + "CVS style keyword markers, these will _not_ be updated\n". $herecurr); + } + +# Blackfin: don't use __builtin_bfin_[cs]sync + if ($line =~ /__builtin_bfin_csync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("CSYNC", + "use the CSYNC() macro in asm/blackfin.h\n" . $herevet); + } + if ($line =~ /__builtin_bfin_ssync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("SSYNC", + "use the SSYNC() macro in asm/blackfin.h\n" . $herevet); + } + +# check for old HOTPLUG __dev<foo> section markings + if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { + WARN("HOTPLUG_SECTION", + "Using $1 is unnecessary\n" . $herecurr); + } + +# Check for potential 'bare' types + my ($stat, $cond, $line_nr_next, $remain_next, $off_next, + $realline_next); +#print "LINE<$line>\n"; + if ($linenr >= $suppress_statement && + $realcnt && $sline =~ /.\s*\S/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0); + $stat =~ s/\n./\n /g; + $cond =~ s/\n./\n /g; + +#print "linenr<$linenr> <$stat>\n"; + # If this statement has no statement boundaries within + # it there is no point in retrying a statement scan + # until we hit end of it. + my $frag = $stat; $frag =~ s/;+\s*$//; + if ($frag !~ /(?:{|;)/) { +#print "skip<$line_nr_next>\n"; + $suppress_statement = $line_nr_next; + } + + # Find the real next line. + $realline_next = $line_nr_next; + if (defined $realline_next && + (!defined $lines[$realline_next - 1] || + substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { + $realline_next++; + } + + my $s = $stat; + $s =~ s/{.*$//s; + + # Ignore goto labels. + if ($s =~ /$Ident:\*$/s) { + + # Ignore functions being called + } elsif ($s =~ /^.\s*$Ident\s*\(/s) { + + } elsif ($s =~ /^.\s*else\b/s) { + + # declarations always start with types + } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { + my $type = $1; + $type =~ s/\s+/ /g; + possible($type, "A:" . $s); + + # definitions in global scope can only start with types + } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { + possible($1, "B:" . $s); + } + + # any (foo ... *) is a pointer cast, and foo is a type + while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { + possible($1, "C:" . $s); + } + + # Check for any sort of function declaration. + # int foo(something bar, other baz); + # void (*store_gdt)(x86_descr_ptr *); + if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { + my ($name_len) = length($1); + + my $ctx = $s; + substr($ctx, 0, $name_len + 1, ''); + $ctx =~ s/\)[^\)]*$//; + + for my $arg (split(/\s*,\s*/, $ctx)) { + if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { + + possible($1, "D:" . $s); + } + } + } + + } + +# +# Checks which may be anchored in the context. +# + +# Check for switch () and associated case and default +# statements should be at the same indent. + if ($line=~/\bswitch\s*\(.*\)/) { + my $err = ''; + my $sep = ''; + my @ctx = ctx_block_outer($linenr, $realcnt); + shift(@ctx); + for my $ctx (@ctx) { + my ($clen, $cindent) = line_stats($ctx); + if ($ctx =~ /^\+\s*(case\s+|default:)/ && + $indent != $cindent) { + $err .= "$sep$ctx\n"; + $sep = ''; + } else { + $sep = "[...]\n"; + } + } + if ($err ne '') { + ERROR("SWITCH_CASE_INDENT_LEVEL", + "switch and case should be at the same indent\n$hereline$err"); + } + } + +# if/while/etc brace do not go on next line, unless defining a do while loop, +# or if that brace on the next line is for something else + if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { + my $pre_ctx = "$1$2"; + + my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); + + if ($line =~ /^\+\t{6,}/) { + WARN("DEEP_INDENTATION", + "Too many leading tabs - consider code refactoring\n" . $herecurr); + } + + my $ctx_cnt = $realcnt - $#ctx - 1; + my $ctx = join("\n", @ctx); + + my $ctx_ln = $linenr; + my $ctx_skip = $realcnt; + + while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && + defined $lines[$ctx_ln - 1] && + $lines[$ctx_ln - 1] =~ /^-/)) { + ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; + $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); + $ctx_ln++; + } + + #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; + #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; + + if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + ERROR("OPEN_BRACE", + "that open brace { should be on the previous line\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && + $ctx =~ /\)\s*\;\s*$/ && + defined $lines[$ctx_ln - 1]) + { + my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); + if ($nindent > $indent) { + WARN("TRAILING_SEMICOLON", + "trailing semicolon indicates no statements, indent implies otherwise\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + } + } + +# Check relative indent for conditionals and blocks. + if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0) + if (!defined $stat); + my ($s, $c) = ($stat, $cond); + + substr($s, 0, length($c), ''); + + # remove inline comments + $s =~ s/$;/ /g; + $c =~ s/$;/ /g; + + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + + # Make sure we remove the line prefixes as we have + # none on the first line, and are going to readd them + # where necessary. + $s =~ s/\n./\n/gs; + while ($s =~ /\n\s+\\\n/) { + $cond_lines += $s =~ s/\n\s+\\\n/\n/g; + } + + # We want to check the first line inside the block + # starting at the end of the conditional, so remove: + # 1) any blank line termination + # 2) any opening brace { on end of the line + # 3) any do (...) { + my $continuation = 0; + my $check = 0; + $s =~ s/^.*\bdo\b//; + $s =~ s/^\s*{//; + if ($s =~ s/^\s*\\//) { + $continuation = 1; + } + if ($s =~ s/^\s*?\n//) { + $check = 1; + $cond_lines++; + } + + # Also ignore a loop construct at the end of a + # preprocessor statement. + if (($prevline =~ /^.\s*#\s*define\s/ || + $prevline =~ /\\\s*$/) && $continuation == 0) { + $check = 0; + } + + my $cond_ptr = -1; + $continuation = 0; + while ($cond_ptr != $cond_lines) { + $cond_ptr = $cond_lines; + + # If we see an #else/#elif then the code + # is not linear. + if ($s =~ /^\s*\#\s*(?:else|elif)/) { + $check = 0; + } + + # Ignore: + # 1) blank lines, they should be at 0, + # 2) preprocessor lines, and + # 3) labels. + if ($continuation || + $s =~ /^\s*?\n/ || + $s =~ /^\s*#\s*?/ || + $s =~ /^\s*$Ident\s*:/) { + $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; + if ($s =~ s/^.*?\n//) { + $cond_lines++; + } + } + } + + my (undef, $sindent) = line_stats("+" . $s); + my $stat_real = raw_line($linenr, $cond_lines); + + # Check if either of these lines are modified, else + # this is not this patch's fault. + if (!defined($stat_real) || + $stat !~ /^\+/ && $stat_real !~ /^\+/) { + $check = 0; + } + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; + + if ($check && $s ne '' && + (($sindent % 8) != 0 || + ($sindent < $indent) || + ($sindent > $indent + 8))) { + WARN("SUSPECT_CODE_INDENT", + "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); + } + } + + # Track the 'values' across context and added lines. + my $opline = $line; $opline =~ s/^./ /; + my ($curr_values, $curr_vars) = + annotate_values($opline . "\n", $prev_values); + $curr_values = $prev_values . $curr_values; + if ($dbg_values) { + my $outline = $opline; $outline =~ s/\t/ /g; + print "$linenr > .$outline\n"; + print "$linenr > $curr_values\n"; + print "$linenr > $curr_vars\n"; + } + $prev_values = substr($curr_values, -1); + +#ignore lines not being added + next if ($line =~ /^[^\+]/); + +# check for declarations of signed or unsigned without int + while ($line =~ m{($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { + my $type = $1; + my $var = $2; + $var = "" if (!defined $var); + if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { + my $sign = $1; + my $pointer = $2; + + $pointer = "" if (!defined $pointer); + + if (WARN("UNSPECIFIED_INT", + "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && + $fix) { + my $decl = trim($sign) . " int "; + my $comp_pointer = $pointer; + $comp_pointer =~ s/\s//g; + $decl .= $comp_pointer; + $decl = rtrim($decl) if ($var eq ""); + $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; + } + } + } + +# TEST: allow direct testing of the type matcher. + if ($dbg_type) { + if ($line =~ /^.\s*$Declare\s*$/) { + ERROR("TEST_TYPE", + "TEST: is type\n" . $herecurr); + } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { + ERROR("TEST_NOT_TYPE", + "TEST: is not type ($1 is)\n". $herecurr); + } + next; + } +# TEST: allow direct testing of the attribute matcher. + if ($dbg_attr) { + if ($line =~ /^.\s*$Modifier\s*$/) { + ERROR("TEST_ATTR", + "TEST: is attr\n" . $herecurr); + } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { + ERROR("TEST_NOT_ATTR", + "TEST: is not attr ($1 is)\n". $herecurr); + } + next; + } + +# check for initialisation to aggregates open brace on the next line + if ($line =~ /^.\s*{/ && + $prevline =~ /(?:^|[^=])=\s*$/) { + if (ERROR("OPEN_BRACE", + "that open brace { should be on the previous line\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/\s*=\s*$/ = {/; + fix_insert_line($fixlinenr, $fixedline); + $fixedline = $line; + $fixedline =~ s/^(.\s*){\s*/$1/; + fix_insert_line($fixlinenr, $fixedline); + } + } + +# +# Checks which are anchored on the added line. +# + +# check for malformed paths in #include statements (uses RAW line) + if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { + my $path = $1; + if ($path =~ m{//}) { + ERROR("MALFORMED_INCLUDE", + "malformed #include filename\n" . $herecurr); + } + if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { + ERROR("UAPI_INCLUDE", + "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); + } + } + +# no C99 // comments + if ($line =~ m{//}) { + if (ERROR("C99_COMMENTS", + "do not use C99 // comments\n" . $herecurr) && + $fix) { + my $line = $fixed[$fixlinenr]; + if ($line =~ /\/\/(.*)$/) { + my $comment = trim($1); + $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; + } + } + } + # Remove C99 comments. + $line =~ s@//.*@@; + $opline =~ s@//.*@@; + +# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider +# the whole statement. +#print "APW <$lines[$realline_next - 1]>\n"; + if (defined $realline_next && + exists $lines[$realline_next - 1] && + !defined $suppress_export{$realline_next} && + ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { + # Handle definitions which produce identifiers with + # a prefix: + # XXX(foo); + # EXPORT_SYMBOL(something_foo); + my $name = $1; + if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && + $name =~ /^${Ident}_$2/) { +#print "FOO C name<$name>\n"; + $suppress_export{$realline_next} = 1; + + } elsif ($stat !~ /(?: + \n.}\s*$| + ^.DEFINE_$Ident\(\Q$name\E\)| + ^.DECLARE_$Ident\(\Q$name\E\)| + ^.LIST_HEAD\(\Q$name\E\)| + ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| + \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() + )/x) { +#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; + $suppress_export{$realline_next} = 2; + } else { + $suppress_export{$realline_next} = 1; + } + } + if (!defined $suppress_export{$linenr} && + $prevline =~ /^.\s*$/ && + ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { +#print "FOO B <$lines[$linenr - 1]>\n"; + $suppress_export{$linenr} = 2; + } + if (defined $suppress_export{$linenr} && + $suppress_export{$linenr} == 2) { + WARN("EXPORT_SYMBOL", + "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); + } + +# check for global initialisers. + if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) { + if (ERROR("GLOBAL_INITIALISERS", + "do not initialise globals to $1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; + } + } +# check for static initialisers. + if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { + if (ERROR("INITIALISED_STATIC", + "do not initialise statics to $1\n" . + $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; + } + } + +# check for misordered declarations of char/short/int/long with signed/unsigned + while ($sline =~ m{(\b$TypeMisordered\b)}g) { + my $tmp = trim($1); + WARN("MISORDERED_TYPE", + "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); + } + +# check for static const char * arrays. + if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "static const char * array should probably be static const char * const\n" . + $herecurr); + } + +# check for static char foo[] = "bar" declarations. + if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "static char array declaration should probably be static const char\n" . + $herecurr); + } + +# check for const <foo> const where <foo> is not a pointer or array type + if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { + my $found = $1; + if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { + WARN("CONST_CONST", + "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); + } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { + WARN("CONST_CONST", + "'const $found const' should probably be 'const $found'\n" . $herecurr); + } + } + +# check for non-global char *foo[] = {"bar", ...} declarations. + if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "char * array declaration might be better as static const\n" . + $herecurr); + } + +# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) + if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { + my $array = $1; + if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { + my $array_div = $1; + if (WARN("ARRAY_SIZE", + "Prefer ARRAY_SIZE($array)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; + } + } + } + +# check for function declarations without arguments like "int foo()" + if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { + if (ERROR("FUNCTION_WITHOUT_ARGS", + "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; + } + } + +# check for uses of DEFINE_PCI_DEVICE_TABLE + if ($line =~ /\bDEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=/) { + if (WARN("DEFINE_PCI_DEVICE_TABLE", + "Prefer struct pci_device_id over deprecated DEFINE_PCI_DEVICE_TABLE\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b(?:static\s+|)DEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=\s*/static const struct pci_device_id $1\[\] = /; + } + } + +# check for new typedefs, only function parameters and sparse annotations +# make sense. + if ($line =~ /\btypedef\s/ && + $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && + $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && + $line !~ /\b$typeTypedefs\b/ && + $line !~ /\b__bitwise(?:__|)\b/) { + WARN("NEW_TYPEDEFS", + "do not add new typedefs\n" . $herecurr); + } + +# * goes on variable not on type + # (char*[ const]) + while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { + #print "AA<$1>\n"; + my ($ident, $from, $to) = ($1, $2, $2); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + +## print "1: from<$from> to<$to> ident<$ident>\n"; + if ($from ne $to) { + if (ERROR("POINTER_LOCATION", + "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && + $fix) { + my $sub_from = $ident; + my $sub_to = $ident; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$fixlinenr] =~ + s@\Q$sub_from\E@$sub_to@; + } + } + } + while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { + #print "BB<$1>\n"; + my ($match, $from, $to, $ident) = ($1, $2, $2, $3); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + # Modifiers should have spaces. + $to =~ s/(\b$Modifier$)/$1 /; + +## print "2: from<$from> to<$to> ident<$ident>\n"; + if ($from ne $to && $ident !~ /^$Modifier$/) { + if (ERROR("POINTER_LOCATION", + "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && + $fix) { + + my $sub_from = $match; + my $sub_to = $match; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$fixlinenr] =~ + s@\Q$sub_from\E@$sub_to@; + } + } + } + +# avoid BUG() or BUG_ON() + if ($line =~ /\b(?:BUG|BUG_ON)\b/) { + my $msg_type = \&WARN; + $msg_type = \&CHK if ($file); + &{$msg_type}("AVOID_BUG", + "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); + } + +# avoid LINUX_VERSION_CODE + if ($line =~ /\bLINUX_VERSION_CODE\b/) { + WARN("LINUX_VERSION_CODE", + "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); + } + +# check for uses of printk_ratelimit + if ($line =~ /\bprintk_ratelimit\s*\(/) { + WARN("PRINTK_RATELIMITED", + "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); + } + +# printk should use KERN_* levels. Note that follow on printk's on the +# same line do not need a level, so we use the current block context +# to try and find and validate the current printk. In summary the current +# printk includes all preceding printk's which have no newline on the end. +# we assume the first bad printk is the one to report. + if ($line =~ /\bprintk\((?!KERN_)\s*"/) { + my $ok = 0; + for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { + #print "CHECK<$lines[$ln - 1]\n"; + # we have a preceding printk if it ends + # with "\n" ignore it, else it is to blame + if ($lines[$ln - 1] =~ m{\bprintk\(}) { + if ($rawlines[$ln - 1] !~ m{\\n"}) { + $ok = 1; + } + last; + } + } + if ($ok == 0) { + WARN("PRINTK_WITHOUT_KERN_LEVEL", + "printk() should include KERN_ facility level\n" . $herecurr); + } + } + + if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { + my $orig = $1; + my $level = lc($orig); + $level = "warn" if ($level eq "warning"); + my $level2 = $level; + $level2 = "dbg" if ($level eq "debug"); + WARN("PREFER_PR_LEVEL", + "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); + } + + if ($line =~ /\bpr_warning\s*\(/) { + if (WARN("PREFER_PR_LEVEL", + "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\bpr_warning\b/pr_warn/; + } + } + + if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { + my $orig = $1; + my $level = lc($orig); + $level = "warn" if ($level eq "warning"); + $level = "dbg" if ($level eq "debug"); + WARN("PREFER_DEV_LEVEL", + "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); + } + +# ENOSYS means "bad syscall nr" and nothing else. This will have a small +# number of false positives, but assembly files are not checked, so at +# least the arch entry code will not trigger this warning. + if ($line =~ /\bENOSYS\b/) { + WARN("ENOSYS", + "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); + } + +# function brace can't be on same line, except for #defines of do while, +# or if closed on same line + if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and + !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { + if (ERROR("OPEN_BRACE", + "open brace '{' following function declarations go on the next line\n" . $herecurr) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + my $fixed_line = $rawline; + $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/; + my $line1 = $1; + my $line2 = $2; + fix_insert_line($fixlinenr, ltrim($line1)); + fix_insert_line($fixlinenr, "\+{"); + if ($line2 !~ /^\s*$/) { + fix_insert_line($fixlinenr, "\+\t" . trim($line2)); + } + } + } + +# open braces for enum, union and struct go on the same line. + if ($line =~ /^.\s*{/ && + $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { + if (ERROR("OPEN_BRACE", + "open brace '{' following $1 go on the same line\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = rtrim($prevrawline) . " {"; + fix_insert_line($fixlinenr, $fixedline); + $fixedline = $rawline; + $fixedline =~ s/^(.\s*){\s*/$1\t/; + if ($fixedline !~ /^\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + } + } + +# missing space after union, struct or enum definition + if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { + if (WARN("SPACING", + "missing space after $1 definition\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; + } + } + +# Function pointer declarations +# check spacing between type, funcptr, and args +# canonical declaration is "type (*funcptr)(args...)" + if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { + my $declare = $1; + my $pre_pointer_space = $2; + my $post_pointer_space = $3; + my $funcname = $4; + my $post_funcname_space = $5; + my $pre_args_space = $6; + +# the $Declare variable will capture all spaces after the type +# so check it for a missing trailing missing space but pointer return types +# don't need a space so don't warn for those. + my $post_declare_space = ""; + if ($declare =~ /(\s+)$/) { + $post_declare_space = $1; + $declare = rtrim($declare); + } + if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { + WARN("SPACING", + "missing space after return type\n" . $herecurr); + $post_declare_space = " "; + } + +# unnecessary space "type (*funcptr)(args...)" +# This test is not currently implemented because these declarations are +# equivalent to +# int foo(int bar, ...) +# and this is form shouldn't/doesn't generate a checkpatch warning. +# +# elsif ($declare =~ /\s{2,}$/) { +# WARN("SPACING", +# "Multiple spaces after return type\n" . $herecurr); +# } + +# unnecessary space "type ( *funcptr)(args...)" + if (defined $pre_pointer_space && + $pre_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer open parenthesis\n" . $herecurr); + } + +# unnecessary space "type (* funcptr)(args...)" + if (defined $post_pointer_space && + $post_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr )(args...)" + if (defined $post_funcname_space && + $post_funcname_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr) (args...)" + if (defined $pre_args_space && + $pre_args_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer arguments\n" . $herecurr); + } + + if (show_type("SPACING") && $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; + } + } + +# check for spacing round square brackets; allowed: +# 1. with a type on the left -- int [] a; +# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, +# 3. inside a curly brace -- = { [0...10] = 5 } + while ($line =~ /(.*?\s)\[/g) { + my ($where, $prefix) = ($-[1], $1); + if ($prefix !~ /$Type\s+$/ && + ($where != 0 || $prefix !~ /^.\s+$/) && + $prefix !~ /[{,]\s+$/) { + if (ERROR("BRACKET_SPACE", + "space prohibited before open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(\+.*?)\s+\[/$1\[/; + } + } + } + +# check for spaces between functions and their parentheses. + while ($line =~ /($Ident)\s+\(/g) { + my $name = $1; + my $ctx_before = substr($line, 0, $-[1]); + my $ctx = "$ctx_before$name"; + + # Ignore those directives where spaces _are_ permitted. + if ($name =~ /^(?: + if|for|while|switch|return|case| + volatile|__volatile__| + __attribute__|format|__extension__| + asm|__asm__)$/x) + { + # cpp #define statements have non-optional spaces, ie + # if there is a space between the name and the open + # parenthesis it is simply not a parameter group. + } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { + + # cpp #elif statement condition may start with a ( + } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { + + # If this whole things ends with a type its most + # likely a typedef for a function. + } elsif ($ctx =~ /$Type$/) { + + } else { + if (WARN("SPACING", + "space prohibited between function name and open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\b$name\s+\(/$name\(/; + } + } + } + +# Check operator spacing. + if (!($line=~/\#\s*include/)) { + my $fixed_line = ""; + my $line_fixed = 0; + + my $ops = qr{ + <<=|>>=|<=|>=|==|!=| + \+=|-=|\*=|\/=|%=|\^=|\|=|&=| + =>|->|<<|>>|<|>|=|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| + \?:|\?|: + }x; + my @elements = split(/($ops|;)/, $opline); + +## print("element count: <" . $#elements . ">\n"); +## foreach my $el (@elements) { +## print("el: <$el>\n"); +## } + + my @fix_elements = (); + my $off = 0; + + foreach my $el (@elements) { + push(@fix_elements, substr($rawline, $off, length($el))); + $off += length($el); + } + + $off = 0; + + my $blank = copy_spacing($opline); + my $last_after = -1; + + for (my $n = 0; $n < $#elements; $n += 2) { + + my $good = $fix_elements[$n] . $fix_elements[$n + 1]; + +## print("n: <$n> good: <$good>\n"); + + $off += length($elements[$n]); + + # Pick up the preceding and succeeding characters. + my $ca = substr($opline, 0, $off); + my $cc = ''; + if (length($opline) >= ($off + length($elements[$n + 1]))) { + $cc = substr($opline, $off + length($elements[$n + 1])); + } + my $cb = "$ca$;$cc"; + + my $a = ''; + $a = 'V' if ($elements[$n] ne ''); + $a = 'W' if ($elements[$n] =~ /\s$/); + $a = 'C' if ($elements[$n] =~ /$;$/); + $a = 'B' if ($elements[$n] =~ /(\[|\()$/); + $a = 'O' if ($elements[$n] eq ''); + $a = 'E' if ($ca =~ /^\s*$/); + + my $op = $elements[$n + 1]; + + my $c = ''; + if (defined $elements[$n + 2]) { + $c = 'V' if ($elements[$n + 2] ne ''); + $c = 'W' if ($elements[$n + 2] =~ /^\s/); + $c = 'C' if ($elements[$n + 2] =~ /^$;/); + $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); + $c = 'O' if ($elements[$n + 2] eq ''); + $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); + } else { + $c = 'E'; + } + + my $ctx = "${a}x${c}"; + + my $at = "(ctx:$ctx)"; + + my $ptr = substr($blank, 0, $off) . "^"; + my $hereptr = "$hereline$ptr\n"; + + # Pull out the value of this operator. + my $op_type = substr($curr_values, $off + 1, 1); + + # Get the full operator variant. + my $opv = $op . substr($curr_vars, $off, 1); + + # Ignore operators passed as parameters. + if ($op_type ne 'V' && + $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { + +# # Ignore comments +# } elsif ($op =~ /^$;+$/) { + + # ; should have either the end of line or a space or \ after it + } elsif ($op eq ';') { + if ($ctx !~ /.x[WEBC]/ && + $cc !~ /^\\/ && $cc !~ /^;/) { + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } + } + + # // is a comment + } elsif ($op eq '//') { + + # : when part of a bitfield + } elsif ($opv eq ':B') { + # skip the bitfield test for now + + # No spaces for: + # -> + } elsif ($op eq '->') { + if ($ctx =~ /Wx.|.xW/) { + if (ERROR("SPACING", + "spaces prohibited around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # , must not have a space before and must have a space on the right. + } elsif ($op eq ',') { + my $rtrim_before = 0; + my $space_after = 0; + if ($ctx =~ /Wx./) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $line_fixed = 1; + $rtrim_before = 1; + } + } + if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $line_fixed = 1; + $last_after = $n; + $space_after = 1; + } + } + if ($rtrim_before || $space_after) { + if ($rtrim_before) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + } else { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); + } + if ($space_after) { + $good .= " "; + } + } + + # '*' as part of a type definition -- reported already. + } elsif ($opv eq '*_') { + #warn "'*' is part of type\n"; + + # unary operators should have a space before and + # none after. May be left adjacent to another + # unary operator, or a cast + } elsif ($op eq '!' || $op eq '~' || + $opv eq '*U' || $opv eq '-U' || + $opv eq '&U' || $opv eq '&&U') { + if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + if (ERROR("SPACING", + "space required before that '$op' $at\n" . $hereptr)) { + if ($n != $last_after + 2) { + $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + } + if ($op eq '*' && $cc =~/\s*$Modifier\b/) { + # A unary '*' may be const + + } elsif ($ctx =~ /.xW/) { + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # unary ++ and unary -- are allowed no space on one side. + } elsif ($op eq '++' or $op eq '--') { + if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { + if (ERROR("SPACING", + "space required one side of that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } + } + if ($ctx =~ /Wx[BE]/ || + ($ctx =~ /Wx./ && $cc =~ /^;/)) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + if ($ctx =~ /ExW/) { + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # << and >> may either have or not have spaces both sides + } elsif ($op eq '<<' or $op eq '>>' or + $op eq '&' or $op eq '^' or $op eq '|' or + $op eq '+' or $op eq '-' or + $op eq '*' or $op eq '/' or + $op eq '%') + { + if ($check) { + if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { + if (CHK("SPACING", + "spaces preferred around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + $fix_elements[$n + 2] =~ s/^\s+//; + $line_fixed = 1; + } + } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { + if (CHK("SPACING", + "space preferred before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { + if (ERROR("SPACING", + "need consistent spacing around '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # A colon needs no spaces before when it is + # terminating a case value or a label. + } elsif ($opv eq ':C' || $opv eq ':L') { + if ($ctx =~ /Wx./) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + + # All the others need spaces both sides. + } elsif ($ctx !~ /[EWC]x[CWE]/) { + my $ok = 0; + + # Ignore email addresses <foo@bar> + if (($op eq '<' && + $cc =~ /^\S+\@\S+>/) || + ($op eq '>' && + $ca =~ /<\S+\@\S+$/)) + { + $ok = 1; + } + + # for asm volatile statements + # ignore a colon with another + # colon immediately before or after + if (($op eq ':') && + ($ca =~ /:$/ || $cc =~ /^:/)) { + $ok = 1; + } + + # messages are ERROR, but ?: are CHK + if ($ok == 0) { + my $msg_type = \&ERROR; + $msg_type = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); + + if (&{$msg_type}("SPACING", + "spaces required around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + } + $off += length($elements[$n + 1]); + +## print("n: <$n> GOOD: <$good>\n"); + + $fixed_line = $fixed_line . $good; + } + + if (($#elements % 2) == 0) { + $fixed_line = $fixed_line . $fix_elements[$#elements]; + } + + if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { + $fixed[$fixlinenr] = $fixed_line; + } + + + } + +# check for whitespace before a non-naked semicolon + if ($line =~ /^\+.*\S\s+;\s*$/) { + if (WARN("SPACING", + "space prohibited before semicolon\n" . $herecurr) && + $fix) { + 1 while $fixed[$fixlinenr] =~ + s/^(\+.*\S)\s+;/$1;/; + } + } + +# check for multiple assignments + if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { + CHK("MULTIPLE_ASSIGNMENTS", + "multiple assignments should be avoided\n" . $herecurr); + } + +## # check for multiple declarations, allowing for a function declaration +## # continuation. +## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && +## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { +## +## # Remove any bracketed sections to ensure we do not +## # falsly report the parameters of functions. +## my $ln = $line; +## while ($ln =~ s/\([^\(\)]*\)//g) { +## } +## if ($ln =~ /,/) { +## WARN("MULTIPLE_DECLARATION", +## "declaring multiple variables together should be avoided\n" . $herecurr); +## } +## } + +#need space before brace following if, while, etc + if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || + $line =~ /do\{/) { + if (ERROR("SPACING", + "space required before the open brace '{'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\))){/$1 {/; + } + } + +## # check for blank lines before declarations +## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && +## $prevrawline =~ /^.\s*$/) { +## WARN("SPACING", +## "No blank lines before declarations\n" . $hereprev); +## } +## + +# closing brace should have a space following it when it has anything +# on the line + if ($line =~ /}(?!(?:,|;|\)))\S/) { + if (ERROR("SPACING", + "space required after that close brace '}'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/}((?!(?:,|;|\)))\S)/} $1/; + } + } + +# check spacing on square brackets + if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { + if (ERROR("SPACING", + "space prohibited after that open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\[\s+/\[/; + } + } + if ($line =~ /\s\]/) { + if (ERROR("SPACING", + "space prohibited before that close square bracket ']'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\s+\]/\]/; + } + } + +# check spacing on parentheses + if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && + $line !~ /for\s*\(\s+;/) { + if (ERROR("SPACING", + "space prohibited after that open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\(\s+/\(/; + } + } + if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && + $line !~ /for\s*\(.*;\s+\)/ && + $line !~ /:\s+\)/) { + if (ERROR("SPACING", + "space prohibited before that close parenthesis ')'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\s+\)/\)/; + } + } + +# check unnecessary parentheses around addressof/dereference single $Lvals +# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar + + while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { + my $var = $1; + if (CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around $var\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; + } + } + +# check for unnecessary parentheses around function pointer uses +# ie: (foo->bar)(); should be foo->bar(); +# but not "if (foo->bar) (" to avoid some false positives + if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { + my $var = $2; + if (CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around function pointer $var\n" . $herecurr) && + $fix) { + my $var2 = deparenthesize($var); + $var2 =~ s/\s//g; + $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; + } + } + +#goto labels aren't indented, allow a single space however + if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and + !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { + if (WARN("INDENTED_LABEL", + "labels should not be indented\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.)\s+/$1/; + } + } + +# return is not a function + if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { + my $spacing = $1; + if ($^V && $^V ge 5.10.0 && + $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { + my $value = $1; + $value = deparenthesize($value); + if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { + ERROR("RETURN_PARENTHESES", + "return is not a function, parentheses are not required\n" . $herecurr); + } + } elsif ($spacing !~ /\s+/) { + ERROR("SPACING", + "space required before the open parenthesis '('\n" . $herecurr); + } + } + +# unnecessary return in a void function +# at end-of-function, with the previous line a single leading tab, then return; +# and the line before that not a goto label target like "out:" + if ($sline =~ /^[ \+]}\s*$/ && + $prevline =~ /^\+\treturn\s*;\s*$/ && + $linenr >= 3 && + $lines[$linenr - 3] =~ /^[ +]/ && + $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { + WARN("RETURN_VOID", + "void function return statements are not generally useful\n" . $hereprev); + } + +# if statements using unnecessary parentheses - ie: if ((foo == bar)) + if ($^V && $^V ge 5.10.0 && + $line =~ /\bif\s*((?:\(\s*){2,})/) { + my $openparens = $1; + my $count = $openparens =~ tr@\(@\(@; + my $msg = ""; + if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { + my $comp = $4; #Not $1 because of $LvalOrFunc + $msg = " - maybe == should be = ?" if ($comp eq "=="); + WARN("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses$msg\n" . $herecurr); + } + } + +# comparisons with a constant or upper case identifier on the left +# avoid cases like "foo + BAR < baz" +# only fix matches surrounded by parentheses to avoid incorrect +# conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" + if ($^V && $^V ge 5.10.0 && + $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { + my $lead = $1; + my $const = $2; + my $comp = $3; + my $to = $4; + my $newcomp = $comp; + if ($lead !~ /$Operators\s*$/ && + $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && + WARN("CONSTANT_COMPARISON", + "Comparisons should place the constant on the right side of the test\n" . $herecurr) && + $fix) { + if ($comp eq "<") { + $newcomp = ">"; + } elsif ($comp eq "<=") { + $newcomp = ">="; + } elsif ($comp eq ">") { + $newcomp = "<"; + } elsif ($comp eq ">=") { + $newcomp = "<="; + } + $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; + } + } + +# Return of what appears to be an errno should normally be negative + if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { + my $name = $1; + if ($name ne 'EOF' && $name ne 'ERROR') { + WARN("USE_NEGATIVE_ERRNO", + "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); + } + } + +# Need a space before open parenthesis after if, while etc + if ($line =~ /\b(if|while|for|switch)\(/) { + if (ERROR("SPACING", + "space required before the open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\b(if|while|for|switch)\(/$1 \(/; + } + } + +# Check for illegal assignment in if conditional -- and check for trailing +# statements after the conditional. + if ($line =~ /do\s*(?!{)/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0) + if (!defined $stat); + my ($stat_next) = ctx_statement_block($line_nr_next, + $remain_next, $off_next); + $stat_next =~ s/\n./\n /g; + ##print "stat<$stat> stat_next<$stat_next>\n"; + + if ($stat_next =~ /^\s*while\b/) { + # If the statement carries leading newlines, + # then count those as offsets. + my ($whitespace) = + ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = + statement_rawlines($whitespace) - 1; + + $suppress_whiletrailers{$line_nr_next + + $offset} = 1; + } + } + if (!defined $suppress_whiletrailers{$linenr} && + defined($stat) && defined($cond) && + $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { + my ($s, $c) = ($stat, $cond); + + if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { + ERROR("ASSIGN_IN_IF", + "do not use assignment in if condition\n" . $herecurr); + } + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + $s =~ s/$;//g; # Remove any comments + if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && + $c !~ /}\s*while\s*/) + { + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + my $stat_real = ''; + + $stat_real = raw_line($linenr, $cond_lines) + . "\n" if ($cond_lines); + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line\n" . $herecurr . $stat_real); + } + } + +# Check for bitwise tests written as boolean + if ($line =~ / + (?: + (?:\[|\(|\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\|) + | + (?:\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\||\)|\]) + )/x) + { + WARN("HEXADECIMAL_BOOLEAN_TEST", + "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); + } + +# if and else should not have general statements after it + if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { + my $s = $1; + $s =~ s/$;//g; # Remove any comments + if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line\n" . $herecurr); + } + } +# if should not continue a brace + if ($line =~ /}\s*if\b/) { + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line (or did you mean 'else if'?)\n" . + $herecurr); + } +# case and default should not have general statements after them + if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && + $line !~ /\G(?: + (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| + \s*return\s+ + )/xg) + { + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line\n" . $herecurr); + } + + # Check for }<nl>else {, these must be at the same + # indent level to be relevant to each other. + if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && + $previndent == $indent) { + if (ERROR("ELSE_AFTER_BRACE", + "else should follow close brace '}'\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/}\s*$//; + if ($fixedline !~ /^\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + $fixedline = $rawline; + $fixedline =~ s/^(.\s*)else/$1} else/; + fix_insert_line($fixlinenr, $fixedline); + } + } + + if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && + $previndent == $indent) { + my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + + if ($s =~ /^\s*;/) { + if (ERROR("WHILE_AFTER_BRACE", + "while should follow close brace '}'\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + my $trailing = $rawline; + $trailing =~ s/^\+//; + $trailing = trim($trailing); + $fixedline =~ s/}\s*$/} $trailing/; + fix_insert_line($fixlinenr, $fixedline); + } + } + } + +#Specific variable tests + while ($line =~ m{($Constant|$Lval)}g) { + my $var = $1; + +#gcc binary extension + if ($var =~ /^$Binary$/) { + if (WARN("GCC_BINARY_CONSTANT", + "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && + $fix) { + my $hexval = sprintf("0x%x", oct($var)); + $fixed[$fixlinenr] =~ + s/\b$var\b/$hexval/; + } + } + +#CamelCase + if ($var !~ /^$Constant$/ && + $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && +#Ignore Page<foo> variants + $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && +#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) + $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && +#Ignore some three character SI units explicitly, like MiB and KHz + $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { + while ($var =~ m{($Ident)}g) { + my $word = $1; + next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); + if ($check) { + seed_camelcase_includes(); + if (!$file && !$camelcase_file_seeded) { + seed_camelcase_file($realfile); + $camelcase_file_seeded = 1; + } + } + if (!defined $camelcase{$word}) { + $camelcase{$word} = 1; + CHK("CAMELCASE", + "Avoid CamelCase: <$word>\n" . $herecurr); + } + } + } + } + +#no spaces allowed after \ in define + if ($line =~ /\#\s*define.*\\\s+$/) { + if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", + "Whitespace after \\ makes next lines useless\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+$//; + } + } + +# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes +# itself <asm/foo.h> (uses RAW line) + if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { + my $file = "$1.h"; + my $checkfile = "include/linux/$file"; + if (-f "$root/$checkfile" && + $realfile ne $checkfile && + $1 !~ /$allowed_asm_includes/) + { + my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; + if ($asminclude > 0) { + if ($realfile =~ m{^arch/}) { + CHK("ARCH_INCLUDE_LINUX", + "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } else { + WARN("INCLUDE_LINUX", + "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } + } + } + } + +# multi-statement macros should be enclosed in a do while loop, grab the +# first statement and ensure its the whole macro if its not enclosed +# in a known good container + if ($realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + my $has_flow_statement = 0; + my $has_arg_concat = 0; + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $realcnt, 0); + $ctx = $dstat; + #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; + #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; + + $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); + $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); + + $dstat =~ s/^.\s*\#\s*define\s+$Ident(?:\([^\)]*\))?\s*//; + $dstat =~ s/$;//g; + $dstat =~ s/\\\n.//g; + $dstat =~ s/^\s*//s; + $dstat =~ s/\s*$//s; + + # Flatten any parentheses and braces + while ($dstat =~ s/\([^\(\)]*\)/1/ || + $dstat =~ s/\{[^\{\}]*\}/1/ || + $dstat =~ s/.\[[^\[\]]*\]/1/) + { + } + + # Flatten any obvious string concatentation. + while ($dstat =~ s/($String)\s*$Ident/$1/ || + $dstat =~ s/$Ident\s*($String)/$1/) + { + } + + # Make asm volatile uses seem like a generic function + $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; + + my $exceptions = qr{ + $Declare| + module_param_named| + MODULE_PARM_DESC| + DECLARE_PER_CPU| + DEFINE_PER_CPU| + __typeof__\(| + union| + struct| + \.$Ident\s*=\s*| + ^\"|\"$| + ^\[ + }x; + #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; + if ($dstat ne '' && + $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), + $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); + $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz + $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants + $dstat !~ /$exceptions/ && + $dstat !~ /^\.$Ident\s*=/ && # .foo = + $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo + $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) + $dstat !~ /^for\s*$Constant$/ && # for (...) + $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() + $dstat !~ /^do\s*{/ && # do {... + $dstat !~ /^\(\{/ && # ({... + $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) + { + $ctx =~ s/\n*$//; + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($ctx); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + if ($dstat =~ /;/) { + ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", + "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); + } else { + ERROR("COMPLEX_MACRO", + "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); + } + } + +# check for macros with flow control, but without ## concatenation +# ## concatenation is commonly a macro that defines a function so ignore those + if ($has_flow_statement && !$has_arg_concat) { + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($ctx); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + WARN("MACRO_WITH_FLOW_CONTROL", + "Macros with flow control statements should be avoided\n" . "$herectx"); + } + +# check for line continuations outside of #defines, preprocessor #, and asm + + } else { + if ($prevline !~ /^..*\\$/ && + $line !~ /^\+\s*\#.*\\$/ && # preprocessor + $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm + $line =~ /^\+.*\\$/) { + WARN("LINE_CONTINUATIONS", + "Avoid unnecessary line continuations\n" . $herecurr); + } + } + +# do {} while (0) macro tests: +# single-statement macros do not need to be enclosed in do while (0) loop, +# macro should not end with a semicolon + if ($^V && $^V ge 5.10.0 && + $realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $realcnt, 0); + $ctx = $dstat; + + $dstat =~ s/\\\n.//g; + $dstat =~ s/$;/ /g; + + if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { + my $stmts = $2; + my $semis = $3; + + $ctx =~ s/\n*$//; + my $cnt = statement_rawlines($ctx); + my $herectx = $here . "\n"; + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + if (($stmts =~ tr/;/;/) == 1 && + $stmts !~ /^\s*(if|while|for|switch)\b/) { + WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", + "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); + } + if (defined $semis && $semis ne "") { + WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", + "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); + } + } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { + $ctx =~ s/\n*$//; + my $cnt = statement_rawlines($ctx); + my $herectx = $here . "\n"; + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + WARN("TRAILING_SEMICOLON", + "macros should not use a trailing semicolon\n" . "$herectx"); + } + } + +# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... +# all assignments may have only one of the following with an assignment: +# . +# ALIGN(...) +# VMLINUX_SYMBOL(...) + if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { + WARN("MISSING_VMLINUX_SYMBOL", + "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); + } + +# check for redundant bracing round if etc + if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, 1); + #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; + #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; + if ($#chunks > 0 && $level == 0) { + my @allowed = (); + my $allow = 0; + my $seen = 0; + my $herectx = $here . "\n"; + my $ln = $linenr - 1; + for my $chunk (@chunks) { + my ($cond, $block) = @{$chunk}; + + # If the condition carries leading newlines, then count those as offsets. + my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = statement_rawlines($whitespace) - 1; + + $allowed[$allow] = 0; + #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; + + # We have looked at and allowed this specific line. + $suppress_ifbraces{$ln + $offset} = 1; + + $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; + $ln += statement_rawlines($block) - 1; + + substr($block, 0, length($cond), ''); + + $seen++ if ($block =~ /^\s*{/); + + #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed[$allow] = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed[$allow] = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed[$allow] = 1; + } + $allow++; + } + if ($seen) { + my $sum_allowed = 0; + foreach (@allowed) { + $sum_allowed += $_; + } + if ($sum_allowed == 0) { + WARN("BRACES", + "braces {} are not necessary for any arm of this statement\n" . $herectx); + } elsif ($sum_allowed != $allow && + $seen != $allow) { + CHK("BRACES", + "braces {} should be used on all arms of this statement\n" . $herectx); + } + } + } + } + if (!defined $suppress_ifbraces{$linenr - 1} && + $line =~ /\b(if|while|for|else)\b/) { + my $allowed = 0; + + # Check the pre-context. + if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { + #print "APW: ALLOWED: pre<$1>\n"; + $allowed = 1; + } + + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, $-[0]); + + # Check the condition. + my ($cond, $block) = @{$chunks[0]}; + #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + # Check the post-context. + if (defined $chunks[1]) { + my ($cond, $block) = @{$chunks[1]}; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if ($block =~ /^\s*\{/) { + #print "APW: ALLOWED: chunk-1 block<$block>\n"; + $allowed = 1; + } + } + if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($block); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + WARN("BRACES", + "braces {} are not necessary for single statement blocks\n" . $herectx); + } + } + +# check for unnecessary blank lines around braces + if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { + if (CHK("BRACES", + "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && + $fix && $prevrawline =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + } + } + if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { + if (CHK("BRACES", + "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } + } + +# no volatiles please + my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; + if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { + WARN("VOLATILE", + "Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); + } + +# Check for user-visible strings broken across lines, which breaks the ability +# to grep for the string. Make exceptions when the previous string ends in a +# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' +# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value + if ($line =~ /^\+\s*$String/ && + $prevline =~ /"\s*$/ && + $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { + if (WARN("SPLIT_STRING", + "quoted string split across lines\n" . $hereprev) && + $fix && + $prevrawline =~ /^\+.*"\s*$/ && + $last_coalesced_string_linenr != $linenr - 1) { + my $extracted_string = get_quoted_string($line, $rawline); + my $comma_close = ""; + if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { + $comma_close = $1; + } + + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/"\s*$//; + $fixedline .= substr($extracted_string, 1) . trim($comma_close); + fix_insert_line($fixlinenr - 1, $fixedline); + $fixedline = $rawline; + $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; + if ($fixedline !~ /\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + $last_coalesced_string_linenr = $linenr; + } + } + +# check for missing a space in a string concatenation + if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { + WARN('MISSING_SPACE', + "break quoted strings at a space character\n" . $hereprev); + } + +# check for spaces before a quoted newline + if ($rawline =~ /^.*\".*\s\\n/) { + if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", + "unnecessary whitespace before a quoted newline\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; + } + + } + +# concatenated string without spaces between elements + if ($line =~ /$String[A-Z_]/ || $line =~ /[A-Za-z0-9_]$String/) { + CHK("CONCATENATED_STRING", + "Concatenated strings should use spaces between elements\n" . $herecurr); + } + +# uncoalesced string fragments + if ($line =~ /$String\s*"/) { + WARN("STRING_FRAGMENTS", + "Consecutive strings are generally better as a single string\n" . $herecurr); + } + +# check for %L{u,d,i} and 0x%[udi] in strings + my $string; + while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { + $string = substr($rawline, $-[1], $+[1] - $-[1]); + $string =~ s/%%/__/g; + if ($string =~ /(?<!%)%[\*\d\.\$]*L[udi]/) { + WARN("PRINTF_L", + "\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr); + last; + } + if ($string =~ /0x%[\*\d\.\$\Llzth]*[udi]/) { + ERROR("PRINTF_0xDECIMAL", + "Prefixing 0x with decimal output is defective\n" . $herecurr); + } + } + +# check for line continuations in quoted strings with odd counts of " + if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { + WARN("LINE_CONTINUATIONS", + "Avoid line continuations in quoted strings\n" . $herecurr); + } + +# warn about #if 0 + if ($line =~ /^.\s*\#\s*if\s+0\b/) { + CHK("REDUNDANT_CODE", + "if this code is redundant consider removing it\n" . + $herecurr); + } + +# check for needless "if (<foo>) fn(<foo>)" uses + if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { + my $tested = quotemeta($1); + my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; + if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { + my $func = $1; + if (WARN('NEEDLESS_IF', + "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && + $fix) { + my $do_fix = 1; + my $leading_tabs = ""; + my $new_leading_tabs = ""; + if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { + $leading_tabs = $1; + } else { + $do_fix = 0; + } + if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { + $new_leading_tabs = $1; + if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { + $do_fix = 0; + } + } else { + $do_fix = 0; + } + if ($do_fix) { + fix_delete_line($fixlinenr - 1, $prevrawline); + $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; + } + } + } + } + +# check for unnecessary "Out of Memory" messages + if ($line =~ /^\+.*\b$logFunctions\s*\(/ && + $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && + (defined $1 || defined $3) && + $linenr > 3) { + my $testval = $2; + my $testline = $lines[$linenr - 3]; + + my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); +# print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); + + if ($c =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|(?:dev_)?alloc_skb)/) { + WARN("OOM_MESSAGE", + "Possible unnecessary 'out of memory' message\n" . $hereprev); + } + } + +# check for logging functions with KERN_<LEVEL> + if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && + $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { + my $level = $1; + if (WARN("UNNECESSARY_KERN_LEVEL", + "Possible unnecessary $level\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s*$level\s*//; + } + } + +# check for mask then right shift without a parentheses + if ($^V && $^V ge 5.10.0 && + $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && + $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so + WARN("MASK_THEN_SHIFT", + "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); + } + +# check for pointer comparisons to NULL + if ($^V && $^V ge 5.10.0) { + while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { + my $val = $1; + my $equal = "!"; + $equal = "" if ($4 eq "!="); + if (CHK("COMPARISON_TO_NULL", + "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; + } + } + } + +# check for bad placement of section $InitAttribute (e.g.: __initdata) + if ($line =~ /(\b$InitAttribute\b)/) { + my $attr = $1; + if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { + my $ptr = $1; + my $var = $2; + if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && + ERROR("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr)) || + ($ptr !~ /\b(union|struct)\s+$attr\b/ && + WARN("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr))) && + $fix) { + $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; + } + } + } + +# check for $InitAttributeData (ie: __initdata) with const + if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { + my $attr = $1; + $attr =~ /($InitAttributePrefix)(.*)/; + my $attr_prefix = $1; + my $attr_type = $2; + if (ERROR("INIT_ATTRIBUTE", + "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/$InitAttributeData/${attr_prefix}initconst/; + } + } + +# check for $InitAttributeConst (ie: __initconst) without const + if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { + my $attr = $1; + if (ERROR("INIT_ATTRIBUTE", + "Use of $attr requires a separate use of const\n" . $herecurr) && + $fix) { + my $lead = $fixed[$fixlinenr] =~ + /(^\+\s*(?:static\s+))/; + $lead = rtrim($1); + $lead = "$lead " if ($lead !~ /^\+$/); + $lead = "${lead}const "; + $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; + } + } + +# check for __read_mostly with const non-pointer (should just be const) + if ($line =~ /\b__read_mostly\b/ && + $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { + if (ERROR("CONST_READ_MOSTLY", + "Invalid use of __read_mostly with const type\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; + } + } + +# don't use __constant_<foo> functions outside of include/uapi/ + if ($realfile !~ m@^include/uapi/@ && + $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { + my $constant_func = $1; + my $func = $constant_func; + $func =~ s/^__constant_//; + if (WARN("CONSTANT_CONVERSION", + "$constant_func should be $func\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; + } + } + +# prefer usleep_range over udelay + if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { + my $delay = $1; + # ignore udelay's < 10, however + if (! ($delay < 10) ) { + CHK("USLEEP_RANGE", + "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr); + } + if ($delay > 2000) { + WARN("LONG_UDELAY", + "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); + } + } + +# warn about unexpectedly long msleep's + if ($line =~ /\bmsleep\s*\((\d+)\);/) { + if ($1 < 20) { + WARN("MSLEEP", + "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr); + } + } + +# check for comparisons of jiffies + if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { + WARN("JIFFIES_COMPARISON", + "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); + } + +# check for comparisons of get_jiffies_64() + if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { + WARN("JIFFIES_COMPARISON", + "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); + } + +# warn about #ifdefs in C files +# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { +# print "#ifdef in C files should be avoided\n"; +# print "$herecurr"; +# $clean = 0; +# } + +# warn about spacing in #ifdefs + if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { + if (ERROR("SPACING", + "exactly one space required after that #$1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; + } + + } + +# check for spinlock_t definitions without a comment. + if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || + $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { + my $which = $1; + if (!ctx_has_comment($first_line, $linenr)) { + CHK("UNCOMMENTED_DEFINITION", + "$1 definition without comment\n" . $herecurr); + } + } +# check for memory barriers without a comment. + + my $barriers = qr{ + mb| + rmb| + wmb| + read_barrier_depends + }x; + my $barrier_stems = qr{ + mb__before_atomic| + mb__after_atomic| + store_release| + load_acquire| + store_mb| + (?:$barriers) + }x; + my $all_barriers = qr{ + (?:$barriers)| + smp_(?:$barrier_stems)| + virt_(?:$barrier_stems) + }x; + + if ($line =~ /\b(?:$all_barriers)\s*\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + WARN("MEMORY_BARRIER", + "memory barrier without comment\n" . $herecurr); + } + } + + my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; + + if ($realfile !~ m@^include/asm-generic/@ && + $realfile !~ m@/barrier\.h$@ && + $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && + $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { + WARN("MEMORY_BARRIER", + "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); + } + +# check for waitqueue_active without a comment. + if ($line =~ /\bwaitqueue_active\s*\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + WARN("WAITQUEUE_ACTIVE", + "waitqueue_active without comment\n" . $herecurr); + } + } + +# Check for expedited grace periods that interrupt non-idle non-nohz +# online CPUs. These expedited can therefore degrade real-time response +# if used carelessly, and should be avoided where not absolutely +# needed. It is always OK to use synchronize_rcu_expedited() and +# synchronize_sched_expedited() at boot time (before real-time applications +# start) and in error situations where real-time response is compromised in +# any case. Note that synchronize_srcu_expedited() does -not- interrupt +# other CPUs, so don't warn on uses of synchronize_srcu_expedited(). +# Of course, nothing comes for free, and srcu_read_lock() and +# srcu_read_unlock() do contain full memory barriers in payment for +# synchronize_srcu_expedited() non-interruption properties. + if ($line =~ /\b(synchronize_rcu_expedited|synchronize_sched_expedited)\(/) { + WARN("EXPEDITED_RCU_GRACE_PERIOD", + "expedited RCU grace periods should be avoided where they can degrade real-time response\n" . $herecurr); + + } + +# check of hardware specific defines + if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { + CHK("ARCH_DEFINES", + "architecture specific defines should be avoided\n" . $herecurr); + } + +# Check that the storage class is at the beginning of a declaration + if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { + WARN("STORAGE_CLASS", + "storage class should be at the beginning of the declaration\n" . $herecurr) + } + +# check the location of the inline attribute, that it is between +# storage class and type. + if ($line =~ /\b$Type\s+$Inline\b/ || + $line =~ /\b$Inline\s+$Storage\b/) { + ERROR("INLINE_LOCATION", + "inline keyword should sit between storage class and type\n" . $herecurr); + } + +# Check for __inline__ and __inline, prefer inline + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b(__inline__|__inline)\b/) { + if (WARN("INLINE", + "plain inline is preferred over $1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; + + } + } + +# Check for __attribute__ packed, prefer __packed + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { + WARN("PREFER_PACKED", + "__packed is preferred over __attribute__((packed))\n" . $herecurr); + } + +# Check for __attribute__ aligned, prefer __aligned + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { + WARN("PREFER_ALIGNED", + "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); + } + +# Check for __attribute__ format(printf, prefer __printf + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { + if (WARN("PREFER_PRINTF", + "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; + + } + } + +# Check for __attribute__ format(scanf, prefer __scanf + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { + if (WARN("PREFER_SCANF", + "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; + } + } + +# Check for __attribute__ weak, or __weak declarations (may have link issues) + if ($^V && $^V ge 5.10.0 && + $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && + ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || + $line =~ /\b__weak\b/)) { + ERROR("WEAK_DECLARATION", + "Using weak declarations can have unintended link defects\n" . $herecurr); + } + +# check for c99 types like uint8_t used outside of uapi/ + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { + my $type = $1; + if ($type =~ /\b($typeC99Typedefs)\b/) { + $type = $1; + my $kernel_type = 'u'; + $kernel_type = 's' if ($type =~ /^_*[si]/); + $type =~ /(\d+)/; + $kernel_type .= $1; + if (CHK("PREFER_KERNEL_TYPES", + "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/; + } + } + } + +# check for cast of C90 native int or longer types constants + if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { + my $cast = $1; + my $const = $2; + if (WARN("TYPECAST_INT_CONSTANT", + "Unnecessary typecast of c90 int constant\n" . $herecurr) && + $fix) { + my $suffix = ""; + my $newconst = $const; + $newconst =~ s/${Int_type}$//; + $suffix .= 'U' if ($cast =~ /\bunsigned\b/); + if ($cast =~ /\blong\s+long\b/) { + $suffix .= 'LL'; + } elsif ($cast =~ /\blong\b/) { + $suffix .= 'L'; + } + $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; + } + } + +# check for sizeof(&) + if ($line =~ /\bsizeof\s*\(\s*\&/) { + WARN("SIZEOF_ADDRESS", + "sizeof(& should be avoided\n" . $herecurr); + } + +# check for sizeof without parenthesis + if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { + if (WARN("SIZEOF_PARENTHESIS", + "sizeof $1 should be sizeof($1)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; + } + } + +# check for struct spinlock declarations + if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { + WARN("USE_SPINLOCK_T", + "struct spinlock should be spinlock_t\n" . $herecurr); + } + +# check for seq_printf uses that could be seq_puts + if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { + my $fmt = get_quoted_string($line, $rawline); + $fmt =~ s/%%//g; + if ($fmt !~ /%/) { + if (WARN("PREFER_SEQ_PUTS", + "Prefer seq_puts to seq_printf\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; + } + } + } + +# Check for misused memsets + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { + + my $ms_addr = $2; + my $ms_val = $7; + my $ms_size = $12; + + if ($ms_size =~ /^(0x|)0$/i) { + ERROR("MEMSET", + "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); + } elsif ($ms_size =~ /^(0x|)1$/i) { + WARN("MEMSET", + "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); + } + } + +# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { + if (WARN("PREFER_ETHER_ADDR_COPY", + "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && + $fix) { + $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; + } + } + +# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { + WARN("PREFER_ETHER_ADDR_EQUAL", + "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") + } + +# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr +# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { + + my $ms_val = $7; + + if ($ms_val =~ /^(?:0x|)0+$/i) { + if (WARN("PREFER_ETH_ZERO_ADDR", + "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && + $fix) { + $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; + } + } elsif ($ms_val =~ /^(?:0xff|255)$/i) { + if (WARN("PREFER_ETH_BROADCAST_ADDR", + "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && + $fix) { + $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; + } + } + } + +# typecasts on min/max could be min_t/max_t + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { + if (defined $2 || defined $7) { + my $call = $1; + my $cast1 = deparenthesize($2); + my $arg1 = $3; + my $cast2 = deparenthesize($7); + my $arg2 = $8; + my $cast; + + if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { + $cast = "$cast1 or $cast2"; + } elsif ($cast1 ne "") { + $cast = $cast1; + } else { + $cast = $cast2; + } + WARN("MINMAX", + "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); + } + } + +# check usleep_range arguments + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { + my $min = $1; + my $max = $7; + if ($min eq $max) { + WARN("USLEEP_RANGE", + "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); + } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && + $min > $max) { + WARN("USLEEP_RANGE", + "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); + } + } + +# check for naked sscanf + if ($^V && $^V ge 5.10.0 && + defined $stat && + $line =~ /\bsscanf\b/ && + ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && + $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && + $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + WARN("NAKED_SSCANF", + "unchecked sscanf return value\n" . "$here\n$stat_real\n"); + } + +# check for simple sscanf that should be kstrto<foo> + if ($^V && $^V ge 5.10.0 && + defined $stat && + $line =~ /\bsscanf\b/) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { + my $format = $6; + my $count = $format =~ tr@%@%@; + if ($count == 1 && + $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { + WARN("SSCANF_TO_KSTRTO", + "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); + } + } + } + +# check for new externs in .h files. + if ($realfile =~ /\.h$/ && + $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { + if (CHK("AVOID_EXTERNS", + "extern prototypes should be avoided in .h files\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; + } + } + +# check for new externs in .c files. + if ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) + { + my $function_name = $1; + my $paren_space = $2; + + my $s = $stat; + if (defined $cond) { + substr($s, 0, length($cond), ''); + } + if ($s =~ /^\s*;/ && + $function_name ne 'uninitialized_var') + { + WARN("AVOID_EXTERNS", + "externs should be avoided in .c files\n" . $herecurr); + } + + if ($paren_space =~ /\n/) { + WARN("FUNCTION_ARGUMENTS", + "arguments for function declarations should follow identifier\n" . $herecurr); + } + + } elsif ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*extern\s+/) + { + WARN("AVOID_EXTERNS", + "externs should be avoided in .c files\n" . $herecurr); + } + +# checks for new __setup's + if ($rawline =~ /\b__setup\("([^"]*)"/) { + my $name = $1; + + if (!grep(/$name/, @setup_docs)) { + CHK("UNDOCUMENTED_SETUP", + "__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); + } + } + +# check for pointless casting of kmalloc return + if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { + WARN("UNNECESSARY_CASTS", + "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); + } + +# alloc style +# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { + CHK("ALLOC_SIZEOF_STRUCT", + "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); + } + +# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { + my $oldfunc = $3; + my $a1 = $4; + my $a2 = $10; + my $newfunc = "kmalloc_array"; + $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); + my $r1 = $a1; + my $r2 = $a2; + if ($a1 =~ /^sizeof\s*\S/) { + $r1 = $a2; + $r2 = $a1; + } + if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && + !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { + if (WARN("ALLOC_WITH_MULTIPLY", + "Prefer $newfunc over $oldfunc with multiply\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; + + } + } + } + +# check for krealloc arg reuse + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { + WARN("KREALLOC_ARG_REUSE", + "Reusing the krealloc arg is almost always a bug\n" . $herecurr); + } + +# check for alloc argument mismatch + if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { + WARN("ALLOC_ARRAY_ARGS", + "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); + } + +# check for multiple semicolons + if ($line =~ /;\s*;\s*$/) { + if (WARN("ONE_SEMICOLON", + "Statements terminations use 1 semicolon\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; + } + } + +# check for #defines like: 1 << <digit> that could be BIT(digit) + if ($line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { + my $ull = ""; + $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); + if (CHK("BIT_MACRO", + "Prefer using the BIT$ull macro\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; + } + } + +# check for case / default statements not preceded by break/fallthrough/switch + if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { + my $has_break = 0; + my $has_statement = 0; + my $count = 0; + my $prevline = $linenr; + while ($prevline > 1 && ($file || $count < 3) && !$has_break) { + $prevline--; + my $rline = $rawlines[$prevline - 1]; + my $fline = $lines[$prevline - 1]; + last if ($fline =~ /^\@\@/); + next if ($fline =~ /^\-/); + next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); + $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); + next if ($fline =~ /^.[\s$;]*$/); + $has_statement = 1; + $count++; + $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/); + } + if (!$has_break && $has_statement) { + WARN("MISSING_BREAK", + "Possible switch case/default not preceeded by break or fallthrough comment\n" . $herecurr); + } + } + +# check for switch/default statements without a break; + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { + my $ctx = ''; + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($stat); + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + WARN("DEFAULT_NO_BREAK", + "switch default: should use break\n" . $herectx); + } + +# check for gcc specific __FUNCTION__ + if ($line =~ /\b__FUNCTION__\b/) { + if (WARN("USE_FUNC", + "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; + } + } + +# check for uses of __DATE__, __TIME__, __TIMESTAMP__ + while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { + ERROR("DATE_TIME", + "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); + } + +# check for use of yield() + if ($line =~ /\byield\s*\(\s*\)/) { + WARN("YIELD", + "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); + } + +# check for comparisons against true and false + if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { + my $lead = $1; + my $arg = $2; + my $test = $3; + my $otype = $4; + my $trail = $5; + my $op = "!"; + + ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); + + my $type = lc($otype); + if ($type =~ /^(?:true|false)$/) { + if (("$test" eq "==" && "$type" eq "true") || + ("$test" eq "!=" && "$type" eq "false")) { + $op = ""; + } + + CHK("BOOL_COMPARISON", + "Using comparison to $otype is error prone\n" . $herecurr); + +## maybe suggesting a correct construct would better +## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); + + } + } + +# check for semaphores initialized locked + if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { + WARN("CONSIDER_COMPLETION", + "consider using a completion\n" . $herecurr); + } + +# recommend kstrto* over simple_strto* and strict_strto* + if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { + WARN("CONSIDER_KSTRTO", + "$1 is obsolete, use k$3 instead\n" . $herecurr); + } + +# check for __initcall(), use device_initcall() explicitly or more appropriate function please + if ($line =~ /^.\s*__initcall\s*\(/) { + WARN("USE_DEVICE_INITCALL", + "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); + } + +# check for various structs that are normally const (ops, kgdb, device_tree) + my $const_structs = qr{ + acpi_dock_ops| + address_space_operations| + backlight_ops| + block_device_operations| + dentry_operations| + dev_pm_ops| + dma_map_ops| + extent_io_ops| + file_lock_operations| + file_operations| + hv_ops| + ide_dma_ops| + intel_dvo_dev_ops| + item_operations| + iwl_ops| + kgdb_arch| + kgdb_io| + kset_uevent_ops| + lock_manager_operations| + microcode_ops| + mtrr_ops| + neigh_ops| + nlmsvc_binding| + of_device_id| + pci_raw_ops| + pipe_buf_operations| + platform_hibernation_ops| + platform_suspend_ops| + proto_ops| + rpc_pipe_ops| + seq_operations| + snd_ac97_build_ops| + soc_pcmcia_socket_ops| + stacktrace_ops| + sysfs_ops| + tty_operations| + uart_ops| + usb_mon_operations| + wd_ops}x; + if ($line !~ /\bconst\b/ && + $line =~ /\bstruct\s+($const_structs)\b/) { + WARN("CONST_STRUCT", + "struct $1 should normally be const\n" . + $herecurr); + } + +# use of NR_CPUS is usually wrong +# ignore definitions of NR_CPUS and usage to define arrays as likely right + if ($line =~ /\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) + { + WARN("NR_CPUS", + "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); + } + +# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. + if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { + ERROR("DEFINE_ARCH_HAS", + "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); + } + +# likely/unlikely comparisons similar to "(likely(foo) > 0)" + if ($^V && $^V ge 5.10.0 && + $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { + WARN("LIKELY_MISUSE", + "Using $1 should generally have parentheses around the comparison\n" . $herecurr); + } + +# whine mightly about in_atomic + if ($line =~ /\bin_atomic\s*\(/) { + if ($realfile =~ m@^drivers/@) { + ERROR("IN_ATOMIC", + "do not use in_atomic in drivers\n" . $herecurr); + } elsif ($realfile !~ m@^kernel/@) { + WARN("IN_ATOMIC", + "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); + } + } + +# check for lockdep_set_novalidate_class + if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || + $line =~ /__lockdep_no_validate__\s*\)/ ) { + if ($realfile !~ m@^kernel/lockdep@ && + $realfile !~ m@^include/linux/lockdep@ && + $realfile !~ m@^drivers/base/core@) { + ERROR("LOCKDEP", + "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); + } + } + + if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || + $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { + WARN("EXPORTED_WORLD_WRITABLE", + "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); + } + +# Mode permission misuses where it seems decimal should be octal +# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop + if ($^V && $^V ge 5.10.0 && + $line =~ /$mode_perms_search/) { + foreach my $entry (@mode_permission_funcs) { + my $func = $entry->[0]; + my $arg_pos = $entry->[1]; + + my $skip_args = ""; + if ($arg_pos > 1) { + $arg_pos--; + $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; + } + my $test = "\\b$func\\s*\\(${skip_args}([\\d]+)\\s*[,\\)]"; + if ($line =~ /$test/) { + my $val = $1; + $val = $6 if ($skip_args ne ""); + + if ($val !~ /^0$/ && + (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || + length($val) ne 4)) { + ERROR("NON_OCTAL_PERMISSIONS", + "Use 4 digit octal (0777) not decimal permissions\n" . $herecurr); + } elsif ($val =~ /^$Octal$/ && (oct($val) & 02)) { + ERROR("EXPORTED_WORLD_WRITABLE", + "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); + } + } + } + } + +# validate content of MODULE_LICENSE against list from include/linux/module.h + if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { + my $extracted_string = get_quoted_string($line, $rawline); + my $valid_licenses = qr{ + GPL| + GPL\ v2| + GPL\ and\ additional\ rights| + Dual\ BSD/GPL| + Dual\ MIT/GPL| + Dual\ MPL/GPL| + Proprietary + }x; + if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { + WARN("MODULE_LICENSE", + "unknown module license " . $extracted_string . "\n" . $herecurr); + } + } + } + + # If we have no input at all, then there is nothing to report on + # so just keep quiet. + if ($#rawlines == -1) { + exit(0); + } + + # In mailback mode only produce a report in the negative, for + # things that appear to be patches. + if ($mailback && ($clean == 1 || !$is_patch)) { + exit(0); + } + + # This is not a patch, and we are are in 'no-patch' mode so + # just keep quiet. + if (!$chk_patch && !$is_patch) { + exit(0); + } + + if (!$is_patch && $file !~ /cover-letter\.patch$/) { + ERROR("NOT_UNIFIED_DIFF", + "Does not appear to be a unified-diff format patch\n"); + } + if ($is_patch && $filename ne '-' && $chk_signoff && $signoff == 0) { + ERROR("MISSING_SIGN_OFF", + "Missing Signed-off-by: line(s)\n"); + } + + print report_dump(); + if ($summary && !($clean == 1 && $quiet == 1)) { + print "$filename " if ($summary_file); + print "total: $cnt_error errors, $cnt_warn warnings, " . + (($check)? "$cnt_chk checks, " : "") . + "$cnt_lines lines checked\n"; + } + + if ($quiet == 0) { + # If there were whitespace errors which cleanpatch can fix + # then suggest that. + if ($rpt_cleaners) { + $rpt_cleaners = 0; + print << "EOM" + +NOTE: Whitespace errors detected. + You may wish to use scripts/cleanpatch or scripts/cleanfile +EOM + } + } + + if ($clean == 0 && $fix && + ("@rawlines" ne "@fixed" || + $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { + my $newfile = $filename; + $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); + my $linecount = 0; + my $f; + + @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); + + open($f, '>', $newfile) + or die "$P: Can't open $newfile for write\n"; + foreach my $fixed_line (@fixed) { + $linecount++; + if ($file) { + if ($linecount > 3) { + $fixed_line =~ s/^\+//; + print $f $fixed_line . "\n"; + } + } else { + print $f $fixed_line . "\n"; + } + } + close($f); + + if (!$quiet) { + print << "EOM"; + +Wrote EXPERIMENTAL --fix correction(s) to '$newfile' + +Do _NOT_ trust the results written to this file. +Do _NOT_ submit these changes without inspecting them for correctness. + +This EXPERIMENTAL file is simply a convenience to help rewrite patches. +No warranties, expressed or implied... +EOM + } + } + + if ($quiet == 0) { + print "\n"; + if ($clean == 1) { + print "$vname has no obvious style problems and is ready for submission.\n"; + } else { + print "$vname has style problems, please review.\n"; + } + } + return $clean; +} diff --git a/drivers/staging/greybus/spelling.txt b/drivers/staging/greybus/spelling.txt new file mode 100644 index 000000000000..946caf3bd694 --- /dev/null +++ b/drivers/staging/greybus/spelling.txt @@ -0,0 +1,1072 @@ +# Originally from Debian's Lintian tool. Various false positives have been +# removed, and various additions have been made as they've been discovered +# in the kernel source. +# +# License: GPLv2 +# +# The format of each line is: +# mistake||correction +# +abandonning||abandoning +abigious||ambiguous +abitrate||arbitrate +abov||above +abreviated||abbreviated +absense||absence +absolut||absolute +absoulte||absolute +acccess||access +acceleratoin||acceleration +accelleration||acceleration +accesing||accessing +accesnt||accent +accessable||accessible +accesss||access +accidentaly||accidentally +accidentually||accidentally +accoding||according +accomodate||accommodate +accomodates||accommodates +accordign||according +accoring||according +accout||account +accquire||acquire +accquired||acquired +accross||across +acessable||accessible +acess||access +achitecture||architecture +acient||ancient +acitions||actions +acitve||active +acknowldegement||acknowldegement +acknowledgement||acknowledgment +ackowledge||acknowledge +ackowledged||acknowledged +acording||according +activete||activate +acumulating||accumulating +adapater||adapter +addional||additional +additionaly||additionally +addres||address +addreses||addresses +addresss||address +aditional||additional +aditionally||additionally +aditionaly||additionally +adminstrative||administrative +adress||address +adresses||addresses +adviced||advised +afecting||affecting +agaist||against +albumns||albums +alegorical||allegorical +algorith||algorithm +algorithmical||algorithmically +algoritm||algorithm +algoritms||algorithms +algorrithm||algorithm +algorritm||algorithm +allign||align +allocatrd||allocated +allocte||allocate +allpication||application +alocate||allocate +alogirhtms||algorithms +alogrithm||algorithm +alot||a lot +alow||allow +alows||allows +altough||although +alue||value +ambigious||ambiguous +amoung||among +amout||amount +analysator||analyzer +ang||and +anniversery||anniversary +annoucement||announcement +anomolies||anomalies +anomoly||anomaly +anway||anyway +aplication||application +appearence||appearance +applicaion||application +appliction||application +applictions||applications +appplications||applications +appropiate||appropriate +appropriatly||appropriately +approriate||appropriate +approriately||appropriately +apropriate||appropriate +aquainted||acquainted +aquired||acquired +aquisition||acquisition +arbitary||arbitrary +architechture||architecture +arguement||argument +arguements||arguments +aritmetic||arithmetic +arne't||aren't +arraival||arrival +artifical||artificial +artillary||artillery +asign||assign +assertation||assertion +assiged||assigned +assigment||assignment +assigments||assignments +assistent||assistant +assocation||association +associcated||associated +assotiated||associated +assum||assume +assumtpion||assumption +asuming||assuming +asycronous||asynchronous +asynchnous||asynchronous +atomatically||automatically +atomicly||atomically +attachement||attachment +attched||attached +attemps||attempts +attruibutes||attributes +authentification||authentication +automaticaly||automatically +automaticly||automatically +automatize||automate +automatized||automated +automatizes||automates +autonymous||autonomous +auxillary||auxiliary +auxilliary||auxiliary +avaiable||available +avaible||available +availabe||available +availabled||available +availablity||availability +availale||available +availavility||availability +availble||available +availiable||available +avalable||available +avaliable||available +aysnc||async +backgroud||background +backword||backward +backwords||backwards +bahavior||behavior +bakup||backup +baloon||balloon +baloons||balloons +bandwith||bandwidth +batery||battery +beacuse||because +becasue||because +becomming||becoming +becuase||because +beeing||being +befor||before +begining||beginning +beter||better +betweeen||between +bianries||binaries +bitmast||bitmask +boardcast||broadcast +borad||board +boundry||boundary +brievely||briefly +broadcat||broadcast +cacluated||calculated +caculation||calculation +calender||calendar +calle||called +calucate||calculate +calulate||calculate +cancelation||cancellation +capabilites||capabilities +capabitilies||capabilities +capatibilities||capabilities +carefuly||carefully +cariage||carriage +catagory||category +cehck||check +challange||challenge +challanges||challenges +chanell||channel +changable||changeable +channle||channel +channnel||channel +charachter||character +charachters||characters +charactor||character +charater||character +charaters||characters +charcter||character +chcek||check +chck||check +checksuming||checksumming +childern||children +childs||children +chiled||child +chked||checked +chnage||change +chnages||changes +chnnel||channel +choosen||chosen +chouse||chose +circumvernt||circumvent +claread||cleared +clared||cleared +closeing||closing +clustred||clustered +collapsable||collapsible +colorfull||colorful +comand||command +comit||commit +commerical||commercial +comming||coming +comminucation||communication +commited||committed +commiting||committing +committ||commit +commoditiy||commodity +compability||compatibility +compaibility||compatibility +compatability||compatibility +compatable||compatible +compatibiliy||compatibility +compatibilty||compatibility +compatiblity||compatibility +competion||completion +compilant||compliant +compleatly||completely +completly||completely +complient||compliant +componnents||components +compres||compress +compresion||compression +comression||compression +comunication||communication +conbination||combination +conditionaly||conditionally +conected||connected +configuratoin||configuration +configuraton||configuration +configuretion||configuration +conider||consider +conjuction||conjunction +connectinos||connections +connnection||connection +connnections||connections +consistancy||consistency +consistant||consistent +containes||contains +containts||contains +contaisn||contains +contant||contact +contence||contents +continous||continuous +continously||continuously +continueing||continuing +contraints||constraints +controled||controlled +controler||controller +controll||control +contruction||construction +contry||country +convertion||conversion +convertor||converter +convienient||convenient +convinient||convenient +corected||corrected +correponding||corresponding +correponds||corresponds +correspoding||corresponding +cotrol||control +couter||counter +coutner||counter +cryptocraphic||cryptographic +cunter||counter +curently||currently +dafault||default +deafult||default +deamon||daemon +decompres||decompress +decription||description +defailt||default +defferred||deferred +definate||definite +definately||definitely +defintion||definition +defintions||definitions +defualt||default +defult||default +deivce||device +delared||declared +delare||declare +delares||declares +delaring||declaring +delemiter||delimiter +dependancies||dependencies +dependancy||dependency +dependant||dependent +depreacted||deprecated +depreacte||deprecate +desactivate||deactivate +desciptors||descriptors +descripton||description +descrition||description +descritptor||descriptor +desctiptor||descriptor +desriptor||descriptor +desriptors||descriptors +destory||destroy +destoryed||destroyed +destorys||destroys +destroied||destroyed +detabase||database +develope||develop +developement||development +developped||developed +developpement||development +developper||developer +developpment||development +deveolpment||development +devided||divided +deviece||device +diable||disable +dictionnary||dictionary +didnt||didn't +diferent||different +differrence||difference +difinition||definition +diplay||display +direectly||directly +disapear||disappear +disapeared||disappeared +disappared||disappeared +disconnet||disconnect +discontinous||discontinuous +dispertion||dispersion +dissapears||disappears +distiction||distinction +docuentation||documentation +documantation||documentation +documentaion||documentation +documment||document +doesnt||doesn't +dorp||drop +dosen||doesn +downlad||download +downlads||downloads +druing||during +dynmaic||dynamic +easilly||easily +ecspecially||especially +edditable||editable +editting||editing +efficently||efficiently +ehther||ether +eigth||eight +eletronic||electronic +enabledi||enabled +enchanced||enhanced +encorporating||incorporating +encrupted||encrypted +encrypiton||encryption +endianess||endianness +enhaced||enhanced +enlightnment||enlightenment +enocded||encoded +enterily||entirely +enviroiment||environment +enviroment||environment +environement||environment +environent||environment +eqivalent||equivalent +equiped||equipped +equivelant||equivalent +equivilant||equivalent +eror||error +estbalishment||establishment +etsablishment||establishment +etsbalishment||establishment +excecutable||executable +exceded||exceeded +excellant||excellent +existance||existence +existant||existent +exixt||exist +exlcude||exclude +exlcusive||exclusive +exmaple||example +expecially||especially +explicite||explicit +explicitely||explicitly +explict||explicit +explictly||explicitly +expresion||expression +exprimental||experimental +extened||extended +extensability||extensibility +extention||extension +extracter||extractor +faild||failed +faill||fail +failue||failure +failuer||failure +faireness||fairness +faliure||failure +familar||familiar +fatser||faster +feauture||feature +feautures||features +fetaure||feature +fetaures||features +fileystem||filesystem +finanize||finalize +findn||find +finilizes||finalizes +finsih||finish +flusing||flushing +folloing||following +followign||following +follwing||following +forseeable||foreseeable +forse||force +fortan||fortran +forwardig||forwarding +framwork||framework +frequncy||frequency +frome||from +fucntion||function +fuction||function +fuctions||functions +funcion||function +functionallity||functionality +functionaly||functionally +functionnality||functionality +functonality||functionality +funtion||function +funtions||functions +furthur||further +futhermore||furthermore +futrue||future +gaurenteed||guaranteed +generiously||generously +genric||generic +globel||global +grabing||grabbing +grahical||graphical +grahpical||graphical +grapic||graphic +guage||gauge +guarenteed||guaranteed +guarentee||guarantee +halfs||halves +hander||handler +handfull||handful +hanled||handled +happend||happened +harware||hardware +heirarchically||hierarchically +helpfull||helpful +hierachy||hierarchy +hierarchie||hierarchy +howver||however +hsould||should +hypter||hyper +identidier||identifier +imblance||imbalance +immeadiately||immediately +immedaite||immediate +immediatelly||immediately +immediatly||immediately +immidiate||immediate +impelentation||implementation +impementated||implemented +implemantation||implementation +implemenation||implementation +implementaiton||implementation +implementated||implemented +implemention||implementation +implemetation||implementation +implemntation||implementation +implentation||implementation +implmentation||implementation +implmenting||implementing +incomming||incoming +incompatabilities||incompatibilities +incompatable||incompatible +inconsistant||inconsistent +increas||increase +incrment||increment +indendation||indentation +indended||intended +independant||independent +independantly||independently +independed||independent +indiate||indicate +inexpect||inexpected +infomation||information +informatiom||information +informations||information +informtion||information +infromation||information +ingore||ignore +inital||initial +initalised||initialized +initalise||initialize +initalize||initialize +initation||initiation +initators||initiators +initializiation||initialization +initialzed||initialized +initilization||initialization +initilize||initialize +inofficial||unofficial +insititute||institute +instal||install +inteface||interface +integreated||integrated +integrety||integrity +integrey||integrity +intendet||intended +intented||intended +interanl||internal +interchangable||interchangeable +interferring||interfering +interger||integer +intermittant||intermittent +internel||internal +interoprability||interoperability +interrface||interface +interrrupt||interrupt +interrup||interrupt +interrups||interrupts +interruptted||interrupted +interupted||interrupted +interupt||interrupt +intial||initial +intialized||initialized +intialize||initialize +intregral||integral +intrrupt||interrupt +intuative||intuitive +invaid||invalid +invalde||invald +invalide||invalid +invididual||individual +invokation||invocation +invokations||invocations +irrelevent||irrelevant +isnt||isn't +isssue||issue +itslef||itself +jave||java +jeffies||jiffies +juse||just +jus||just +kown||known +langage||language +langauage||language +langauge||language +langugage||language +lauch||launch +layed||laid +leightweight||lightweight +lengh||length +lenght||length +lenth||length +lesstiff||lesstif +libaries||libraries +libary||library +librairies||libraries +libraris||libraries +licenceing||licencing +loggging||logging +loggin||login +logile||logfile +loosing||losing +losted||lost +machinary||machinery +maintainance||maintenance +maintainence||maintenance +maintan||maintain +makeing||making +malplaced||misplaced +malplace||misplace +managable||manageable +managment||management +mangement||management +manoeuvering||maneuvering +mappping||mapping +mathimatical||mathematical +mathimatic||mathematic +mathimatics||mathematics +maxium||maximum +mechamism||mechanism +meetign||meeting +ment||meant +mergable||mergeable +mesage||message +messags||messages +messgaes||messages +messsage||message +messsages||messages +microprocesspr||microprocessor +milliseonds||milliseconds +minumum||minimum +miscelleneous||miscellaneous +misformed||malformed +mispelled||misspelled +mispelt||misspelt +miximum||maximum +mmnemonic||mnemonic +mnay||many +modeled||modelled +modulues||modules +monochorome||monochrome +monochromo||monochrome +monocrome||monochrome +mopdule||module +mroe||more +mulitplied||multiplied +multidimensionnal||multidimensional +multple||multiple +mumber||number +muticast||multicast +mutiple||multiple +mutli||multi +nams||names +navagating||navigating +nead||need +neccecary||necessary +neccesary||necessary +neccessary||necessary +necesary||necessary +negaive||negative +negoitation||negotiation +negotation||negotiation +nerver||never +nescessary||necessary +nessessary||necessary +noticable||noticeable +notications||notifications +notifed||notified +numebr||number +numner||number +obtaion||obtain +occassionally||occasionally +occationally||occasionally +occurance||occurrence +occurances||occurrences +occured||occurred +occurence||occurrence +occure||occurred +occuring||occurring +offet||offset +omitt||omit +ommiting||omitting +ommitted||omitted +onself||oneself +ony||only +operatione||operation +opertaions||operations +optionnal||optional +optmizations||optimizations +orientatied||orientated +orientied||oriented +otherise||otherwise +ouput||output +overaall||overall +overhread||overhead +overlaping||overlapping +overriden||overridden +overun||overrun +pacakge||package +pachage||package +packacge||package +packege||package +packge||package +packtes||packets +pakage||package +pallette||palette +paln||plan +paramameters||parameters +paramater||parameter +parametes||parameters +parametised||parametrised +paramter||parameter +paramters||parameters +particuarly||particularly +particularily||particularly +pased||passed +passin||passing +pathes||paths +pecularities||peculiarities +peformance||performance +peice||piece +pendantic||pedantic +peprocessor||preprocessor +perfoming||performing +permissons||permissions +peroid||period +persistance||persistence +persistant||persistent +platfrom||platform +plattform||platform +pleaes||please +ploting||plotting +plugable||pluggable +poinnter||pointer +poiter||pointer +posible||possible +positon||position +possibilites||possibilities +powerfull||powerful +preceeded||preceded +preceeding||preceding +preceed||precede +precendence||precedence +precission||precision +preemptable||preemptible +prefered||preferred +prefferably||preferably +premption||preemption +prepaired||prepared +pressre||pressure +primative||primitive +princliple||principle +priorty||priority +privilaged||privileged +privilage||privilege +priviledge||privilege +priviledges||privileges +probaly||probably +procceed||proceed +proccesors||processors +procesed||processed +proces||process +processessing||processing +processess||processes +processpr||processor +processsed||processed +processsing||processing +procteted||protected +prodecure||procedure +progams||programs +progess||progress +programers||programmers +programm||program +programms||programs +progresss||progress +promiscous||promiscuous +promps||prompts +pronnounced||pronounced +prononciation||pronunciation +pronouce||pronounce +pronunce||pronounce +propery||property +propigate||propagate +propigation||propagation +propogate||propagate +prosess||process +protable||portable +protcol||protocol +protecion||protection +protocoll||protocol +psudo||pseudo +psuedo||pseudo +psychadelic||psychedelic +pwoer||power +quering||querying +raoming||roaming +reasearcher||researcher +reasearchers||researchers +reasearch||research +recepient||recipient +receving||receiving +recieved||received +recieve||receive +reciever||receiver +recieves||receives +recogniced||recognised +recognizeable||recognizable +recommanded||recommended +recyle||recycle +redircet||redirect +redirectrion||redirection +refcounf||refcount +refence||reference +refered||referred +referenace||reference +refering||referring +refernces||references +refernnce||reference +refrence||reference +registerd||registered +registeresd||registered +registes||registers +registraration||registration +regster||register +regualar||regular +reguator||regulator +regulamentations||regulations +reigstration||registration +releated||related +relevent||relevant +remoote||remote +remore||remote +removeable||removable +repectively||respectively +replacable||replaceable +replacments||replacements +replys||replies +reponse||response +representaion||representation +reqeust||request +requiere||require +requirment||requirement +requred||required +requried||required +requst||request +reseting||resetting +resizeable||resizable +resouces||resources +resoures||resources +responce||response +ressizes||resizes +ressource||resource +ressources||resources +retransmited||retransmitted +retreived||retrieved +retreive||retrieve +retrive||retrieve +retuned||returned +reudce||reduce +reuest||request +reuqest||request +reutnred||returned +rmeoved||removed +rmeove||remove +rmeoves||removes +rountine||routine +routins||routines +rquest||request +runing||running +runned||ran +runnning||running +runtine||runtime +sacrifying||sacrificing +safly||safely +safty||safety +savable||saveable +scaned||scanned +scaning||scanning +scarch||search +seach||search +searchs||searches +secquence||sequence +secund||second +segement||segment +senarios||scenarios +sentivite||sensitive +separatly||separately +sepcify||specify +sepc||spec +seperated||separated +seperately||separately +seperate||separate +seperatly||separately +seperator||separator +sepperate||separate +sequece||sequence +sequencial||sequential +serveral||several +setts||sets +settting||setting +shotdown||shutdown +shoud||should +shouldnt||shouldn't +shoule||should +shrinked||shrunk +siginificantly||significantly +signabl||signal +similary||similarly +similiar||similar +simlar||similar +simliar||similar +simpified||simplified +singaled||signaled +singal||signal +singed||signed +sleeped||slept +softwares||software +speach||speech +specfic||specific +speciefied||specified +specifc||specific +specifed||specified +specificatin||specification +specificaton||specification +specifing||specifying +specifiying||specifying +speficied||specified +speicify||specify +speling||spelling +spinlcok||spinlock +spinock||spinlock +splitted||split +spreaded||spread +sructure||structure +stablilization||stabilization +staically||statically +staion||station +standardss||standards +standartization||standardization +standart||standard +staticly||statically +stoped||stopped +stoppped||stopped +straming||streaming +struc||struct +structres||structures +stuct||struct +stucture||structure +sturcture||structure +subdirectoires||subdirectories +suble||subtle +substract||subtract +succesfully||successfully +succesful||successful +successfull||successful +sucessfully||successfully +sucess||success +superflous||superfluous +superseeded||superseded +suplied||supplied +suported||supported +suport||support +suppored||supported +supportin||supporting +suppoted||supported +suppported||supported +suppport||support +supress||suppress +surpresses||suppresses +susbsystem||subsystem +suspicously||suspiciously +swaping||swapping +switchs||switches +symetric||symmetric +synax||syntax +synchonized||synchronized +syncronize||synchronize +syncronizing||synchronizing +syncronus||synchronous +syste||system +sytem||system +sythesis||synthesis +taht||that +targetted||targeted +targetting||targeting +teh||the +temorary||temporary +temproarily||temporarily +thier||their +threds||threads +threshhold||threshold +throught||through +thses||these +tiggered||triggered +tipically||typically +tmis||this +torerable||tolerable +tramsmitted||transmitted +tramsmit||transmit +tranfer||transfer +transciever||transceiver +transferd||transferrd +transfered||transferred +transfering||transferring +transision||transition +transmittd||transmitted +transormed||transformed +trasmission||transmission +treshold||threshold +trigerring||triggering +trun||turn +ture||true +tyep||type +udpate||update +uesd||used +unconditionaly||unconditionally +underun||underrun +unecessary||unnecessary +unexecpted||unexpected +unexpectd||unexpected +unexpeted||unexpected +unfortunatelly||unfortunately +unifiy||unify +unintialized||uninitialized +unknonw||unknown +unknow||unknown +unkown||unknown +unneedingly||unnecessarily +unresgister||unregister +unsinged||unsigned +unstabel||unstable +unsuccessfull||unsuccessful +unsuported||unsupported +untill||until +unuseful||useless +upate||update +usefule||useful +usefull||useful +usege||usage +usera||users +usualy||usually +utilites||utilities +utillities||utilities +utilties||utilities +utiltity||utility +utitity||utility +utitlty||utility +vaid||valid +vaild||valid +valide||valid +variantions||variations +varient||variant +vaule||value +verbse||verbose +verisons||versions +verison||version +verson||version +vicefersa||vice-versa +virtal||virtual +virtaul||virtual +virtiual||virtual +visiters||visitors +vitual||virtual +wating||waiting +wether||whether +whataver||whatever +whcih||which +whenver||whenever +wheter||whether +whe||when +wierd||weird +wiil||will +wirte||write +withing||within +wnat||want +workarould||workaround +writeing||writing +writting||writing +zombe||zombie +zomebie||zombie -- cgit v1.2.3-59-g8ed1b From d9984cf0f24860bc3a9d3c8f48d90a148827c5c6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 31 Mar 2016 22:35:24 -0700 Subject: greybus: scripts: create subdir for checkpatch and spelling.txt They belong in a subdir. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/checkpatch.pl | 5993 ------------------------- drivers/staging/greybus/scripts/checkpatch.pl | 5993 +++++++++++++++++++++++++ drivers/staging/greybus/scripts/spelling.txt | 1072 +++++ drivers/staging/greybus/spelling.txt | 1072 ----- 4 files changed, 7065 insertions(+), 7065 deletions(-) delete mode 100755 drivers/staging/greybus/checkpatch.pl create mode 100755 drivers/staging/greybus/scripts/checkpatch.pl create mode 100644 drivers/staging/greybus/scripts/spelling.txt delete mode 100644 drivers/staging/greybus/spelling.txt diff --git a/drivers/staging/greybus/checkpatch.pl b/drivers/staging/greybus/checkpatch.pl deleted file mode 100755 index d574d13ba963..000000000000 --- a/drivers/staging/greybus/checkpatch.pl +++ /dev/null @@ -1,5993 +0,0 @@ -#!/usr/bin/perl -w -# (c) 2001, Dave Jones. (the file handling bit) -# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit) -# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite) -# (c) 2008-2010 Andy Whitcroft <apw@canonical.com> -# Licensed under the terms of the GNU GPL License version 2 - -use strict; -use POSIX; -use File::Basename; -use Cwd 'abs_path'; -use Term::ANSIColor qw(:constants); - -my $P = $0; -my $D = dirname(abs_path($P)); - -my $V = '0.32'; - -use Getopt::Long qw(:config no_auto_abbrev); - -my $quiet = 0; -my $tree = 1; -my $chk_signoff = 1; -my $chk_patch = 1; -my $tst_only; -my $emacs = 0; -my $terse = 0; -my $showfile = 0; -my $file = 0; -my $check = 0; -my $check_orig = 0; -my $summary = 1; -my $mailback = 0; -my $summary_file = 0; -my $show_types = 0; -my $fix = 0; -my $fix_inplace = 0; -my $root; -my %debug; -my %camelcase = (); -my %use_type = (); -my @use = (); -my %ignore_type = (); -my @ignore = (); -my $help = 0; -my $configuration_file = ".checkpatch.conf"; -my $max_line_length = 80; -my $ignore_perl_version = 0; -my $minimum_perl_version = 5.10.0; -my $min_conf_desc_length = 4; -my $spelling_file = "$D/spelling.txt"; -my $codespell = 0; -my $codespellfile = "/usr/share/codespell/dictionary.txt"; -my $color = 1; - -sub help { - my ($exitcode) = @_; - - print << "EOM"; -Usage: $P [OPTION]... [FILE]... -Version: $V - -Options: - -q, --quiet quiet - --no-tree run without a kernel tree - --no-signoff do not check for 'Signed-off-by' line - --patch treat FILE as patchfile (default) - --emacs emacs compile window format - --terse one line per report - --showfile emit diffed file position, not input file position - -f, --file treat FILE as regular source file - --subjective, --strict enable more subjective tests - --types TYPE(,TYPE2...) show only these comma separated message types - --ignore TYPE(,TYPE2...) ignore various comma separated message types - --max-line-length=n set the maximum line length, if exceeded, warn - --min-conf-desc-length=n set the min description length, if shorter, warn - --show-types show the message "types" in the output - --root=PATH PATH to the kernel tree root - --no-summary suppress the per-file summary - --mailback only produce a report in case of warnings/errors - --summary-file include the filename in summary - --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of - 'values', 'possible', 'type', and 'attr' (default - is all off) - --test-only=WORD report only warnings/errors containing WORD - literally - --fix EXPERIMENTAL - may create horrible results - If correctable single-line errors exist, create - "<inputfile>.EXPERIMENTAL-checkpatch-fixes" - with potential errors corrected to the preferred - checkpatch style - --fix-inplace EXPERIMENTAL - may create horrible results - Is the same as --fix, but overwrites the input - file. It's your fault if there's no backup or git - --ignore-perl-version override checking of perl version. expect - runtime errors. - --codespell Use the codespell dictionary for spelling/typos - (default:/usr/share/codespell/dictionary.txt) - --codespellfile Use this codespell dictionary - --color Use colors when output is STDOUT (default: on) - -h, --help, --version display this help and exit - -When FILE is - read standard input. -EOM - - exit($exitcode); -} - -my $conf = which_conf($configuration_file); -if (-f $conf) { - my @conf_args; - open(my $conffile, '<', "$conf") - or warn "$P: Can't find a readable $configuration_file file $!\n"; - - while (<$conffile>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - $line =~ s/\s+/ /g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - - my @words = split(" ", $line); - foreach my $word (@words) { - last if ($word =~ m/^#/); - push (@conf_args, $word); - } - } - close($conffile); - unshift(@ARGV, @conf_args) if @conf_args; -} - -GetOptions( - 'q|quiet+' => \$quiet, - 'tree!' => \$tree, - 'signoff!' => \$chk_signoff, - 'patch!' => \$chk_patch, - 'emacs!' => \$emacs, - 'terse!' => \$terse, - 'showfile!' => \$showfile, - 'f|file!' => \$file, - 'subjective!' => \$check, - 'strict!' => \$check, - 'ignore=s' => \@ignore, - 'types=s' => \@use, - 'show-types!' => \$show_types, - 'max-line-length=i' => \$max_line_length, - 'min-conf-desc-length=i' => \$min_conf_desc_length, - 'root=s' => \$root, - 'summary!' => \$summary, - 'mailback!' => \$mailback, - 'summary-file!' => \$summary_file, - 'fix!' => \$fix, - 'fix-inplace!' => \$fix_inplace, - 'ignore-perl-version!' => \$ignore_perl_version, - 'debug=s' => \%debug, - 'test-only=s' => \$tst_only, - 'codespell!' => \$codespell, - 'codespellfile=s' => \$codespellfile, - 'color!' => \$color, - 'h|help' => \$help, - 'version' => \$help -) or help(1); - -help(0) if ($help); - -$fix = 1 if ($fix_inplace); -$check_orig = $check; - -my $exit = 0; - -if ($^V && $^V lt $minimum_perl_version) { - printf "$P: requires at least perl version %vd\n", $minimum_perl_version; - if (!$ignore_perl_version) { - exit(1); - } -} - -if ($#ARGV < 0) { - print "$P: no input files\n"; - exit(1); -} - -sub hash_save_array_words { - my ($hashRef, $arrayRef) = @_; - - my @array = split(/,/, join(',', @$arrayRef)); - foreach my $word (@array) { - $word =~ s/\s*\n?$//g; - $word =~ s/^\s*//g; - $word =~ s/\s+/ /g; - $word =~ tr/[a-z]/[A-Z]/; - - next if ($word =~ m/^\s*#/); - next if ($word =~ m/^\s*$/); - - $hashRef->{$word}++; - } -} - -sub hash_show_words { - my ($hashRef, $prefix) = @_; - - if (keys %$hashRef) { - print "\nNOTE: $prefix message types:"; - foreach my $word (sort keys %$hashRef) { - print " $word"; - } - print "\n"; - } -} - -hash_save_array_words(\%ignore_type, \@ignore); -hash_save_array_words(\%use_type, \@use); - -my $dbg_values = 0; -my $dbg_possible = 0; -my $dbg_type = 0; -my $dbg_attr = 0; -for my $key (keys %debug) { - ## no critic - eval "\${dbg_$key} = '$debug{$key}';"; - die "$@" if ($@); -} - -my $rpt_cleaners = 0; - -if ($terse) { - $emacs = 1; - $quiet++; -} - -if ($tree) { - if (defined $root) { - if (!top_of_kernel_tree($root)) { - die "$P: $root: --root does not point at a valid tree\n"; - } - } else { - if (top_of_kernel_tree('.')) { - $root = '.'; - } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && - top_of_kernel_tree($1)) { - $root = $1; - } - } - - if (!defined $root) { - print "Must be run from the top-level dir. of a kernel tree\n"; - exit(2); - } -} - -my $emitted_corrupt = 0; - -our $Ident = qr{ - [A-Za-z_][A-Za-z\d_]* - (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* - }x; -our $Storage = qr{extern|static|asmlinkage}; -our $Sparse = qr{ - __user| - __kernel| - __force| - __iomem| - __pmem| - __must_check| - __init_refok| - __kprobes| - __ref| - __rcu| - __private - }x; -our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; -our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; -our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; -our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; -our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; - -# Notes to $Attribute: -# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check -our $Attribute = qr{ - const| - __percpu| - __nocast| - __safe| - __bitwise__| - __packed__| - __packed2__| - __naked| - __maybe_unused| - __always_unused| - __noreturn| - __used| - __cold| - __pure| - __noclone| - __deprecated| - __read_mostly| - __kprobes| - $InitAttribute| - ____cacheline_aligned| - ____cacheline_aligned_in_smp| - ____cacheline_internodealigned_in_smp| - __weak - }x; -our $Modifier; -our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; -our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; -our $Lval = qr{$Ident(?:$Member)*}; - -our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; -our $Binary = qr{(?i)0b[01]+$Int_type?}; -our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; -our $Int = qr{[0-9]+$Int_type?}; -our $Octal = qr{0[0-7]+$Int_type?}; -our $String = qr{"[X\t]*"}; -our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; -our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; -our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; -our $Float = qr{$Float_hex|$Float_dec|$Float_int}; -our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; -our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; -our $Compare = qr{<=|>=|==|!=|<|(?<!-)>}; -our $Arithmetic = qr{\+|-|\*|\/|%}; -our $Operators = qr{ - <=|>=|==|!=| - =>|->|<<|>>|<|>|!|~| - &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic - }x; - -our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; - -our $BasicType; -our $NonptrType; -our $NonptrTypeMisordered; -our $NonptrTypeWithAttr; -our $Type; -our $TypeMisordered; -our $Declare; -our $DeclareMisordered; - -our $NON_ASCII_UTF8 = qr{ - [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte - | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs - | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte - | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates - | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 - | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 - | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 -}x; - -our $UTF8 = qr{ - [\x09\x0A\x0D\x20-\x7E] # ASCII - | $NON_ASCII_UTF8 -}x; - -our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; -our $typeOtherOSTypedefs = qr{(?x: - u_(?:char|short|int|long) | # bsd - u(?:nchar|short|int|long) # sysv -)}; -our $typeKernelTypedefs = qr{(?x: - (?:__)?(?:u|s|be|le)(?:8|16|32|64)| - atomic_t -)}; -our $typeTypedefs = qr{(?x: - $typeC99Typedefs\b| - $typeOtherOSTypedefs\b| - $typeKernelTypedefs\b -)}; - -our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; - -our $logFunctions = qr{(?x: - printk(?:_ratelimited|_once|)| - (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| - WARN(?:_RATELIMIT|_ONCE|)| - panic| - MODULE_[A-Z_]+| - seq_vprintf|seq_printf|seq_puts -)}; - -our $signature_tags = qr{(?xi: - Signed-off-by:| - Acked-by:| - Tested-by:| - Reviewed-by:| - Reported-by:| - Suggested-by:| - To:| - Cc: -)}; - -our @typeListMisordered = ( - qr{char\s+(?:un)?signed}, - qr{int\s+(?:(?:un)?signed\s+)?short\s}, - qr{int\s+short(?:\s+(?:un)?signed)}, - qr{short\s+int(?:\s+(?:un)?signed)}, - qr{(?:un)?signed\s+int\s+short}, - qr{short\s+(?:un)?signed}, - qr{long\s+int\s+(?:un)?signed}, - qr{int\s+long\s+(?:un)?signed}, - qr{long\s+(?:un)?signed\s+int}, - qr{int\s+(?:un)?signed\s+long}, - qr{int\s+(?:un)?signed}, - qr{int\s+long\s+long\s+(?:un)?signed}, - qr{long\s+long\s+int\s+(?:un)?signed}, - qr{long\s+long\s+(?:un)?signed\s+int}, - qr{long\s+long\s+(?:un)?signed}, - qr{long\s+(?:un)?signed}, -); - -our @typeList = ( - qr{void}, - qr{(?:(?:un)?signed\s+)?char}, - qr{(?:(?:un)?signed\s+)?short\s+int}, - qr{(?:(?:un)?signed\s+)?short}, - qr{(?:(?:un)?signed\s+)?int}, - qr{(?:(?:un)?signed\s+)?long\s+int}, - qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, - qr{(?:(?:un)?signed\s+)?long\s+long}, - qr{(?:(?:un)?signed\s+)?long}, - qr{(?:un)?signed}, - qr{float}, - qr{double}, - qr{bool}, - qr{struct\s+$Ident}, - qr{union\s+$Ident}, - qr{enum\s+$Ident}, - qr{${Ident}_t}, - qr{${Ident}_handler}, - qr{${Ident}_handler_fn}, - @typeListMisordered, -); - -our $C90_int_types = qr{(?x: - long\s+long\s+int\s+(?:un)?signed| - long\s+long\s+(?:un)?signed\s+int| - long\s+long\s+(?:un)?signed| - (?:(?:un)?signed\s+)?long\s+long\s+int| - (?:(?:un)?signed\s+)?long\s+long| - int\s+long\s+long\s+(?:un)?signed| - int\s+(?:(?:un)?signed\s+)?long\s+long| - - long\s+int\s+(?:un)?signed| - long\s+(?:un)?signed\s+int| - long\s+(?:un)?signed| - (?:(?:un)?signed\s+)?long\s+int| - (?:(?:un)?signed\s+)?long| - int\s+long\s+(?:un)?signed| - int\s+(?:(?:un)?signed\s+)?long| - - int\s+(?:un)?signed| - (?:(?:un)?signed\s+)?int -)}; - -our @typeListFile = (); -our @typeListWithAttr = ( - @typeList, - qr{struct\s+$InitAttribute\s+$Ident}, - qr{union\s+$InitAttribute\s+$Ident}, -); - -our @modifierList = ( - qr{fastcall}, -); -our @modifierListFile = (); - -our @mode_permission_funcs = ( - ["module_param", 3], - ["module_param_(?:array|named|string)", 4], - ["module_param_array_named", 5], - ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], - ["proc_create(?:_data|)", 2], - ["(?:CLASS|DEVICE|SENSOR)_ATTR", 2], -); - -#Create a search pattern for all these functions to speed up a loop below -our $mode_perms_search = ""; -foreach my $entry (@mode_permission_funcs) { - $mode_perms_search .= '|' if ($mode_perms_search ne ""); - $mode_perms_search .= $entry->[0]; -} - -our $mode_perms_world_writable = qr{ - S_IWUGO | - S_IWOTH | - S_IRWXUGO | - S_IALLUGO | - 0[0-7][0-7][2367] -}x; - -our $allowed_asm_includes = qr{(?x: - irq| - memory| - time| - reboot -)}; -# memory.h: ARM has a custom one - -# Load common spelling mistakes and build regular expression list. -my $misspellings; -my %spelling_fix; - -if (open(my $spelling, '<', $spelling_file)) { - while (<$spelling>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - - my ($suspect, $fix) = split(/\|\|/, $line); - - $spelling_fix{$suspect} = $fix; - } - close($spelling); -} else { - warn "No typos will be found - file '$spelling_file': $!\n"; -} - -if ($codespell) { - if (open(my $spelling, '<', $codespellfile)) { - while (<$spelling>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - next if ($line =~ m/, disabled/i); - - $line =~ s/,.*$//; - - my ($suspect, $fix) = split(/->/, $line); - - $spelling_fix{$suspect} = $fix; - } - close($spelling); - } else { - warn "No codespell typos will be found - file '$codespellfile': $!\n"; - } -} - -$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; - -sub build_types { - my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; - my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; - my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; - my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; - $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; - $BasicType = qr{ - (?:$typeTypedefs\b)| - (?:${all}\b) - }x; - $NonptrType = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:typeof|__typeof__)\s*\([^\)]*\)| - (?:$typeTypedefs\b)| - (?:${all}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $NonptrTypeMisordered = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:${Misordered}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $NonptrTypeWithAttr = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:typeof|__typeof__)\s*\([^\)]*\)| - (?:$typeTypedefs\b)| - (?:${allWithAttr}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $Type = qr{ - $NonptrType - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? - (?:\s+$Inline|\s+$Modifier)* - }x; - $TypeMisordered = qr{ - $NonptrTypeMisordered - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? - (?:\s+$Inline|\s+$Modifier)* - }x; - $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; - $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; -} -build_types(); - -our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; - -# Using $balanced_parens, $LvalOrFunc, or $FuncArg -# requires at least perl version v5.10.0 -# Any use must be runtime checked with $^V - -our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; -our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; -our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; - -our $declaration_macros = qr{(?x: - (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| - (?:$Storage\s+)?LIST_HEAD\s*\(| - (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\( -)}; - -sub deparenthesize { - my ($string) = @_; - return "" if (!defined($string)); - - while ($string =~ /^\s*\(.*\)\s*$/) { - $string =~ s@^\s*\(\s*@@; - $string =~ s@\s*\)\s*$@@; - } - - $string =~ s@\s+@ @g; - - return $string; -} - -sub seed_camelcase_file { - my ($file) = @_; - - return if (!(-f $file)); - - local $/; - - open(my $include_file, '<', "$file") - or warn "$P: Can't read '$file' $!\n"; - my $text = <$include_file>; - close($include_file); - - my @lines = split('\n', $text); - - foreach my $line (@lines) { - next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); - if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { - $camelcase{$1} = 1; - } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { - $camelcase{$1} = 1; - } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { - $camelcase{$1} = 1; - } - } -} - -my $camelcase_seeded = 0; -sub seed_camelcase_includes { - return if ($camelcase_seeded); - - my $files; - my $camelcase_cache = ""; - my @include_files = (); - - $camelcase_seeded = 1; - - if (-e ".git") { - my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; - chomp $git_last_include_commit; - $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; - } else { - my $last_mod_date = 0; - $files = `find $root/include -name "*.h"`; - @include_files = split('\n', $files); - foreach my $file (@include_files) { - my $date = POSIX::strftime("%Y%m%d%H%M", - localtime((stat $file)[9])); - $last_mod_date = $date if ($last_mod_date < $date); - } - $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; - } - - if ($camelcase_cache ne "" && -f $camelcase_cache) { - open(my $camelcase_file, '<', "$camelcase_cache") - or warn "$P: Can't read '$camelcase_cache' $!\n"; - while (<$camelcase_file>) { - chomp; - $camelcase{$_} = 1; - } - close($camelcase_file); - - return; - } - - if (-e ".git") { - $files = `git ls-files "include/*.h"`; - @include_files = split('\n', $files); - } - - foreach my $file (@include_files) { - seed_camelcase_file($file); - } - - if ($camelcase_cache ne "") { - unlink glob ".checkpatch-camelcase.*"; - open(my $camelcase_file, '>', "$camelcase_cache") - or warn "$P: Can't write '$camelcase_cache' $!\n"; - foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { - print $camelcase_file ("$_\n"); - } - close($camelcase_file); - } -} - -sub git_commit_info { - my ($commit, $id, $desc) = @_; - - return ($id, $desc) if ((which("git") eq "") || !(-e ".git")); - - my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`; - $output =~ s/^\s*//gm; - my @lines = split("\n", $output); - - return ($id, $desc) if ($#lines < 0); - - if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) { -# Maybe one day convert this block of bash into something that returns -# all matching commit ids, but it's very slow... -# -# echo "checking commits $1..." -# git rev-list --remotes | grep -i "^$1" | -# while read line ; do -# git log --format='%H %s' -1 $line | -# echo "commit $(cut -c 1-12,41-)" -# done - } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { - } else { - $id = substr($lines[0], 0, 12); - $desc = substr($lines[0], 41); - } - - return ($id, $desc); -} - -$chk_signoff = 0 if ($file); - -my @rawlines = (); -my @lines = (); -my @fixed = (); -my @fixed_inserted = (); -my @fixed_deleted = (); -my $fixlinenr = -1; - -my $vname; -for my $filename (@ARGV) { - my $FILE; - if ($file) { - open($FILE, '-|', "diff -u /dev/null $filename") || - die "$P: $filename: diff failed - $!\n"; - } elsif ($filename eq '-') { - open($FILE, '<&STDIN'); - } else { - open($FILE, '<', "$filename") || - die "$P: $filename: open failed - $!\n"; - } - if ($filename eq '-') { - $vname = 'Your patch'; - } else { - $vname = $filename; - } - while (<$FILE>) { - chomp; - push(@rawlines, $_); - } - close($FILE); - - if ($#ARGV > 0 && $quiet == 0) { - print '-' x length($vname) . "\n"; - print "$vname\n"; - print '-' x length($vname) . "\n"; - } - - if (!process($filename)) { - $exit = 1; - } - @rawlines = (); - @lines = (); - @fixed = (); - @fixed_inserted = (); - @fixed_deleted = (); - $fixlinenr = -1; - @modifierListFile = (); - @typeListFile = (); - build_types(); -} - -if (!$quiet) { - hash_show_words(\%use_type, "Used"); - hash_show_words(\%ignore_type, "Ignored"); - - if ($^V lt 5.10.0) { - print << "EOM" - -NOTE: perl $^V is not modern enough to detect all possible issues. - An upgrade to at least perl v5.10.0 is suggested. -EOM - } - if ($exit) { - print << "EOM" - -NOTE: If any of the errors are false positives, please report - them to the maintainer, see CHECKPATCH in MAINTAINERS. -EOM - } -} - -exit($exit); - -sub top_of_kernel_tree { - my ($root) = @_; - - my @tree_check = ( - "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", - "README", "Documentation", "arch", "include", "drivers", - "fs", "init", "ipc", "kernel", "lib", "scripts", - ); - - foreach my $check (@tree_check) { - if (! -e $root . '/' . $check) { - return 0; - } - } - return 1; -} - -sub parse_email { - my ($formatted_email) = @_; - - my $name = ""; - my $address = ""; - my $comment = ""; - - if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { - $name = $1; - $address = $2; - $comment = $3 if defined $3; - } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { - $address = $1; - $comment = $2 if defined $2; - } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { - $address = $1; - $comment = $2 if defined $2; - $formatted_email =~ s/$address.*$//; - $name = $formatted_email; - $name = trim($name); - $name =~ s/^\"|\"$//g; - # If there's a name left after stripping spaces and - # leading quotes, and the address doesn't have both - # leading and trailing angle brackets, the address - # is invalid. ie: - # "joe smith joe@smith.com" bad - # "joe smith <joe@smith.com" bad - if ($name ne "" && $address !~ /^<[^>]+>$/) { - $name = ""; - $address = ""; - $comment = ""; - } - } - - $name = trim($name); - $name =~ s/^\"|\"$//g; - $address = trim($address); - $address =~ s/^\<|\>$//g; - - if ($name =~ /[^\w \-]/i) { ##has "must quote" chars - $name =~ s/(?<!\\)"/\\"/g; ##escape quotes - $name = "\"$name\""; - } - - return ($name, $address, $comment); -} - -sub format_email { - my ($name, $address) = @_; - - my $formatted_email; - - $name = trim($name); - $name =~ s/^\"|\"$//g; - $address = trim($address); - - if ($name =~ /[^\w \-]/i) { ##has "must quote" chars - $name =~ s/(?<!\\)"/\\"/g; ##escape quotes - $name = "\"$name\""; - } - - if ("$name" eq "") { - $formatted_email = "$address"; - } else { - $formatted_email = "$name <$address>"; - } - - return $formatted_email; -} - -sub which { - my ($bin) = @_; - - foreach my $path (split(/:/, $ENV{PATH})) { - if (-e "$path/$bin") { - return "$path/$bin"; - } - } - - return ""; -} - -sub which_conf { - my ($conf) = @_; - - foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { - if (-e "$path/$conf") { - return "$path/$conf"; - } - } - - return ""; -} - -sub expand_tabs { - my ($str) = @_; - - my $res = ''; - my $n = 0; - for my $c (split(//, $str)) { - if ($c eq "\t") { - $res .= ' '; - $n++; - for (; ($n % 8) != 0; $n++) { - $res .= ' '; - } - next; - } - $res .= $c; - $n++; - } - - return $res; -} -sub copy_spacing { - (my $res = shift) =~ tr/\t/ /c; - return $res; -} - -sub line_stats { - my ($line) = @_; - - # Drop the diff line leader and expand tabs - $line =~ s/^.//; - $line = expand_tabs($line); - - # Pick the indent from the front of the line. - my ($white) = ($line =~ /^(\s*)/); - - return (length($line), length($white)); -} - -my $sanitise_quote = ''; - -sub sanitise_line_reset { - my ($in_comment) = @_; - - if ($in_comment) { - $sanitise_quote = '*/'; - } else { - $sanitise_quote = ''; - } -} -sub sanitise_line { - my ($line) = @_; - - my $res = ''; - my $l = ''; - - my $qlen = 0; - my $off = 0; - my $c; - - # Always copy over the diff marker. - $res = substr($line, 0, 1); - - for ($off = 1; $off < length($line); $off++) { - $c = substr($line, $off, 1); - - # Comments we are wacking completly including the begin - # and end, all to $;. - if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { - $sanitise_quote = '*/'; - - substr($res, $off, 2, "$;$;"); - $off++; - next; - } - if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { - $sanitise_quote = ''; - substr($res, $off, 2, "$;$;"); - $off++; - next; - } - if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { - $sanitise_quote = '//'; - - substr($res, $off, 2, $sanitise_quote); - $off++; - next; - } - - # A \ in a string means ignore the next character. - if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && - $c eq "\\") { - substr($res, $off, 2, 'XX'); - $off++; - next; - } - # Regular quotes. - if ($c eq "'" || $c eq '"') { - if ($sanitise_quote eq '') { - $sanitise_quote = $c; - - substr($res, $off, 1, $c); - next; - } elsif ($sanitise_quote eq $c) { - $sanitise_quote = ''; - } - } - - #print "c<$c> SQ<$sanitise_quote>\n"; - if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { - substr($res, $off, 1, $;); - } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { - substr($res, $off, 1, $;); - } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { - substr($res, $off, 1, 'X'); - } else { - substr($res, $off, 1, $c); - } - } - - if ($sanitise_quote eq '//') { - $sanitise_quote = ''; - } - - # The pathname on a #include may be surrounded by '<' and '>'. - if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { - my $clean = 'X' x length($1); - $res =~ s@\<.*\>@<$clean>@; - - # The whole of a #error is a string. - } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { - my $clean = 'X' x length($1); - $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; - } - - return $res; -} - -sub get_quoted_string { - my ($line, $rawline) = @_; - - return "" if ($line !~ m/($String)/g); - return substr($rawline, $-[0], $+[0] - $-[0]); -} - -sub ctx_statement_block { - my ($linenr, $remain, $off) = @_; - my $line = $linenr - 1; - my $blk = ''; - my $soff = $off; - my $coff = $off - 1; - my $coff_set = 0; - - my $loff = 0; - - my $type = ''; - my $level = 0; - my @stack = (); - my $p; - my $c; - my $len = 0; - - my $remainder; - while (1) { - @stack = (['', 0]) if ($#stack == -1); - - #warn "CSB: blk<$blk> remain<$remain>\n"; - # If we are about to drop off the end, pull in more - # context. - if ($off >= $len) { - for (; $remain > 0; $line++) { - last if (!defined $lines[$line]); - next if ($lines[$line] =~ /^-/); - $remain--; - $loff = $len; - $blk .= $lines[$line] . "\n"; - $len = length($blk); - $line++; - last; - } - # Bail if there is no further context. - #warn "CSB: blk<$blk> off<$off> len<$len>\n"; - if ($off >= $len) { - last; - } - if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { - $level++; - $type = '#'; - } - } - $p = $c; - $c = substr($blk, $off, 1); - $remainder = substr($blk, $off); - - #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; - - # Handle nested #if/#else. - if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { - push(@stack, [ $type, $level ]); - } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { - ($type, $level) = @{$stack[$#stack - 1]}; - } elsif ($remainder =~ /^#\s*endif\b/) { - ($type, $level) = @{pop(@stack)}; - } - - # Statement ends at the ';' or a close '}' at the - # outermost level. - if ($level == 0 && $c eq ';') { - last; - } - - # An else is really a conditional as long as its not else if - if ($level == 0 && $coff_set == 0 && - (!defined($p) || $p =~ /(?:\s|\}|\+)/) && - $remainder =~ /^(else)(?:\s|{)/ && - $remainder !~ /^else\s+if\b/) { - $coff = $off + length($1) - 1; - $coff_set = 1; - #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; - #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; - } - - if (($type eq '' || $type eq '(') && $c eq '(') { - $level++; - $type = '('; - } - if ($type eq '(' && $c eq ')') { - $level--; - $type = ($level != 0)? '(' : ''; - - if ($level == 0 && $coff < $soff) { - $coff = $off; - $coff_set = 1; - #warn "CSB: mark coff<$coff>\n"; - } - } - if (($type eq '' || $type eq '{') && $c eq '{') { - $level++; - $type = '{'; - } - if ($type eq '{' && $c eq '}') { - $level--; - $type = ($level != 0)? '{' : ''; - - if ($level == 0) { - if (substr($blk, $off + 1, 1) eq ';') { - $off++; - } - last; - } - } - # Preprocessor commands end at the newline unless escaped. - if ($type eq '#' && $c eq "\n" && $p ne "\\") { - $level--; - $type = ''; - $off++; - last; - } - $off++; - } - # We are truly at the end, so shuffle to the next line. - if ($off == $len) { - $loff = $len + 1; - $line++; - $remain--; - } - - my $statement = substr($blk, $soff, $off - $soff + 1); - my $condition = substr($blk, $soff, $coff - $soff + 1); - - #warn "STATEMENT<$statement>\n"; - #warn "CONDITION<$condition>\n"; - - #print "coff<$coff> soff<$off> loff<$loff>\n"; - - return ($statement, $condition, - $line, $remain + 1, $off - $loff + 1, $level); -} - -sub statement_lines { - my ($stmt) = @_; - - # Strip the diff line prefixes and rip blank lines at start and end. - $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*//; - $stmt =~ s/\s*$//; - - my @stmt_lines = ($stmt =~ /\n/g); - - return $#stmt_lines + 2; -} - -sub statement_rawlines { - my ($stmt) = @_; - - my @stmt_lines = ($stmt =~ /\n/g); - - return $#stmt_lines + 2; -} - -sub statement_block_size { - my ($stmt) = @_; - - $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*{//; - $stmt =~ s/}\s*$//; - $stmt =~ s/^\s*//; - $stmt =~ s/\s*$//; - - my @stmt_lines = ($stmt =~ /\n/g); - my @stmt_statements = ($stmt =~ /;/g); - - my $stmt_lines = $#stmt_lines + 2; - my $stmt_statements = $#stmt_statements + 1; - - if ($stmt_lines > $stmt_statements) { - return $stmt_lines; - } else { - return $stmt_statements; - } -} - -sub ctx_statement_full { - my ($linenr, $remain, $off) = @_; - my ($statement, $condition, $level); - - my (@chunks); - - # Grab the first conditional/block pair. - ($statement, $condition, $linenr, $remain, $off, $level) = - ctx_statement_block($linenr, $remain, $off); - #print "F: c<$condition> s<$statement> remain<$remain>\n"; - push(@chunks, [ $condition, $statement ]); - if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { - return ($level, $linenr, @chunks); - } - - # Pull in the following conditional/block pairs and see if they - # could continue the statement. - for (;;) { - ($statement, $condition, $linenr, $remain, $off, $level) = - ctx_statement_block($linenr, $remain, $off); - #print "C: c<$condition> s<$statement> remain<$remain>\n"; - last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); - #print "C: push\n"; - push(@chunks, [ $condition, $statement ]); - } - - return ($level, $linenr, @chunks); -} - -sub ctx_block_get { - my ($linenr, $remain, $outer, $open, $close, $off) = @_; - my $line; - my $start = $linenr - 1; - my $blk = ''; - my @o; - my @c; - my @res = (); - - my $level = 0; - my @stack = ($level); - for ($line = $start; $remain > 0; $line++) { - next if ($rawlines[$line] =~ /^-/); - $remain--; - - $blk .= $rawlines[$line]; - - # Handle nested #if/#else. - if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { - push(@stack, $level); - } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { - $level = $stack[$#stack - 1]; - } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { - $level = pop(@stack); - } - - foreach my $c (split(//, $lines[$line])) { - ##print "C<$c>L<$level><$open$close>O<$off>\n"; - if ($off > 0) { - $off--; - next; - } - - if ($c eq $close && $level > 0) { - $level--; - last if ($level == 0); - } elsif ($c eq $open) { - $level++; - } - } - - if (!$outer || $level <= 1) { - push(@res, $rawlines[$line]); - } - - last if ($level == 0); - } - - return ($level, @res); -} -sub ctx_block_outer { - my ($linenr, $remain) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); - return @r; -} -sub ctx_block { - my ($linenr, $remain) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); - return @r; -} -sub ctx_statement { - my ($linenr, $remain, $off) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); - return @r; -} -sub ctx_block_level { - my ($linenr, $remain) = @_; - - return ctx_block_get($linenr, $remain, 0, '{', '}', 0); -} -sub ctx_statement_level { - my ($linenr, $remain, $off) = @_; - - return ctx_block_get($linenr, $remain, 0, '(', ')', $off); -} - -sub ctx_locate_comment { - my ($first_line, $end_line) = @_; - - # Catch a comment on the end of the line itself. - my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); - return $current_comment if (defined $current_comment); - - # Look through the context and try and figure out if there is a - # comment. - my $in_comment = 0; - $current_comment = ''; - for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { - my $line = $rawlines[$linenr - 1]; - #warn " $line\n"; - if ($linenr == $first_line and $line =~ m@^.\s*\*@) { - $in_comment = 1; - } - if ($line =~ m@/\*@) { - $in_comment = 1; - } - if (!$in_comment && $current_comment ne '') { - $current_comment = ''; - } - $current_comment .= $line . "\n" if ($in_comment); - if ($line =~ m@\*/@) { - $in_comment = 0; - } - } - - chomp($current_comment); - return($current_comment); -} -sub ctx_has_comment { - my ($first_line, $end_line) = @_; - my $cmt = ctx_locate_comment($first_line, $end_line); - - ##print "LINE: $rawlines[$end_line - 1 ]\n"; - ##print "CMMT: $cmt\n"; - - return ($cmt ne ''); -} - -sub raw_line { - my ($linenr, $cnt) = @_; - - my $offset = $linenr - 1; - $cnt++; - - my $line; - while ($cnt) { - $line = $rawlines[$offset++]; - next if (defined($line) && $line =~ /^-/); - $cnt--; - } - - return $line; -} - -sub cat_vet { - my ($vet) = @_; - my ($res, $coded); - - $res = ''; - while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { - $res .= $1; - if ($2 ne '') { - $coded = sprintf("^%c", unpack('C', $2) + 64); - $res .= $coded; - } - } - $res =~ s/$/\$/; - - return $res; -} - -my $av_preprocessor = 0; -my $av_pending; -my @av_paren_type; -my $av_pend_colon; - -sub annotate_reset { - $av_preprocessor = 0; - $av_pending = '_'; - @av_paren_type = ('E'); - $av_pend_colon = 'O'; -} - -sub annotate_values { - my ($stream, $type) = @_; - - my $res; - my $var = '_' x length($stream); - my $cur = $stream; - - print "$stream\n" if ($dbg_values > 1); - - while (length($cur)) { - @av_paren_type = ('E') if ($#av_paren_type < 0); - print " <" . join('', @av_paren_type) . - "> <$type> <$av_pending>" if ($dbg_values > 1); - if ($cur =~ /^(\s+)/o) { - print "WS($1)\n" if ($dbg_values > 1); - if ($1 =~ /\n/ && $av_preprocessor) { - $type = pop(@av_paren_type); - $av_preprocessor = 0; - } - - } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { - print "CAST($1)\n" if ($dbg_values > 1); - push(@av_paren_type, $type); - $type = 'c'; - - } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { - print "DECLARE($1)\n" if ($dbg_values > 1); - $type = 'T'; - - } elsif ($cur =~ /^($Modifier)\s*/) { - print "MODIFIER($1)\n" if ($dbg_values > 1); - $type = 'T'; - - } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { - print "DEFINE($1,$2)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - push(@av_paren_type, $type); - if ($2 ne '') { - $av_pending = 'N'; - } - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { - print "UNDEF($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - push(@av_paren_type, $type); - - } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { - print "PRE_START($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - - push(@av_paren_type, $type); - push(@av_paren_type, $type); - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { - print "PRE_RESTART($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - - push(@av_paren_type, $av_paren_type[$#av_paren_type]); - - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:endif))/o) { - print "PRE_END($1)\n" if ($dbg_values > 1); - - $av_preprocessor = 1; - - # Assume all arms of the conditional end as this - # one does, and continue as if the #endif was not here. - pop(@av_paren_type); - push(@av_paren_type, $type); - $type = 'E'; - - } elsif ($cur =~ /^(\\\n)/o) { - print "PRECONT($1)\n" if ($dbg_values > 1); - - } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { - print "ATTR($1)\n" if ($dbg_values > 1); - $av_pending = $type; - $type = 'N'; - - } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { - print "SIZEOF($1)\n" if ($dbg_values > 1); - if (defined $2) { - $av_pending = 'V'; - } - $type = 'N'; - - } elsif ($cur =~ /^(if|while|for)\b/o) { - print "COND($1)\n" if ($dbg_values > 1); - $av_pending = 'E'; - $type = 'N'; - - } elsif ($cur =~/^(case)/o) { - print "CASE($1)\n" if ($dbg_values > 1); - $av_pend_colon = 'C'; - $type = 'N'; - - } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { - print "KEYWORD($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(\()/o) { - print "PAREN('$1')\n" if ($dbg_values > 1); - push(@av_paren_type, $av_pending); - $av_pending = '_'; - $type = 'N'; - - } elsif ($cur =~ /^(\))/o) { - my $new_type = pop(@av_paren_type); - if ($new_type ne '_') { - $type = $new_type; - print "PAREN('$1') -> $type\n" - if ($dbg_values > 1); - } else { - print "PAREN('$1')\n" if ($dbg_values > 1); - } - - } elsif ($cur =~ /^($Ident)\s*\(/o) { - print "FUNC($1)\n" if ($dbg_values > 1); - $type = 'V'; - $av_pending = 'V'; - - } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { - if (defined $2 && $type eq 'C' || $type eq 'T') { - $av_pend_colon = 'B'; - } elsif ($type eq 'E') { - $av_pend_colon = 'L'; - } - print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); - $type = 'V'; - - } elsif ($cur =~ /^($Ident|$Constant)/o) { - print "IDENT($1)\n" if ($dbg_values > 1); - $type = 'V'; - - } elsif ($cur =~ /^($Assignment)/o) { - print "ASSIGN($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~/^(;|{|})/) { - print "END($1)\n" if ($dbg_values > 1); - $type = 'E'; - $av_pend_colon = 'O'; - - } elsif ($cur =~/^(,)/) { - print "COMMA($1)\n" if ($dbg_values > 1); - $type = 'C'; - - } elsif ($cur =~ /^(\?)/o) { - print "QUESTION($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(:)/o) { - print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); - - substr($var, length($res), 1, $av_pend_colon); - if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { - $type = 'E'; - } else { - $type = 'N'; - } - $av_pend_colon = 'O'; - - } elsif ($cur =~ /^(\[)/o) { - print "CLOSE($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { - my $variant; - - print "OPV($1)\n" if ($dbg_values > 1); - if ($type eq 'V') { - $variant = 'B'; - } else { - $variant = 'U'; - } - - substr($var, length($res), 1, $variant); - $type = 'N'; - - } elsif ($cur =~ /^($Operators)/o) { - print "OP($1)\n" if ($dbg_values > 1); - if ($1 ne '++' && $1 ne '--') { - $type = 'N'; - } - - } elsif ($cur =~ /(^.)/o) { - print "C($1)\n" if ($dbg_values > 1); - } - if (defined $1) { - $cur = substr($cur, length($1)); - $res .= $type x length($1); - } - } - - return ($res, $var); -} - -sub possible { - my ($possible, $line) = @_; - my $notPermitted = qr{(?: - ^(?: - $Modifier| - $Storage| - $Type| - DEFINE_\S+ - )$| - ^(?: - goto| - return| - case| - else| - asm|__asm__| - do| - \#| - \#\#| - )(?:\s|$)| - ^(?:typedef|struct|enum)\b - )}x; - warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); - if ($possible !~ $notPermitted) { - # Check for modifiers. - $possible =~ s/\s*$Storage\s*//g; - $possible =~ s/\s*$Sparse\s*//g; - if ($possible =~ /^\s*$/) { - - } elsif ($possible =~ /\s/) { - $possible =~ s/\s*$Type\s*//g; - for my $modifier (split(' ', $possible)) { - if ($modifier !~ $notPermitted) { - warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); - push(@modifierListFile, $modifier); - } - } - - } else { - warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); - push(@typeListFile, $possible); - } - build_types(); - } else { - warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); - } -} - -my $prefix = ''; - -sub show_type { - my ($type) = @_; - - return defined $use_type{$type} if (scalar keys %use_type > 0); - - return !defined $ignore_type{$type}; -} - -sub report { - my ($level, $type, $msg) = @_; - - if (!show_type($type) || - (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { - return 0; - } - my $output = ''; - if (-t STDOUT && $color) { - if ($level eq 'ERROR') { - $output .= RED; - } elsif ($level eq 'WARNING') { - $output .= YELLOW; - } else { - $output .= GREEN; - } - } - $output .= $prefix . $level . ':'; - if ($show_types) { - $output .= BLUE if (-t STDOUT && $color); - $output .= "$type:"; - } - $output .= RESET if (-t STDOUT && $color); - $output .= ' ' . $msg . "\n"; - - if ($showfile) { - my @lines = split("\n", $output, -1); - splice(@lines, 1, 1); - $output = join("\n", @lines); - } - $output = (split('\n', $output))[0] . "\n" if ($terse); - - push(our @report, $output); - - return 1; -} - -sub report_dump { - our @report; -} - -sub fixup_current_range { - my ($lineRef, $offset, $length) = @_; - - if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { - my $o = $1; - my $l = $2; - my $no = $o + $offset; - my $nl = $l + $length; - $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; - } -} - -sub fix_inserted_deleted_lines { - my ($linesRef, $insertedRef, $deletedRef) = @_; - - my $range_last_linenr = 0; - my $delta_offset = 0; - - my $old_linenr = 0; - my $new_linenr = 0; - - my $next_insert = 0; - my $next_delete = 0; - - my @lines = (); - - my $inserted = @{$insertedRef}[$next_insert++]; - my $deleted = @{$deletedRef}[$next_delete++]; - - foreach my $old_line (@{$linesRef}) { - my $save_line = 1; - my $line = $old_line; #don't modify the array - if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename - $delta_offset = 0; - } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk - $range_last_linenr = $new_linenr; - fixup_current_range(\$line, $delta_offset, 0); - } - - while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { - $deleted = @{$deletedRef}[$next_delete++]; - $save_line = 0; - fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); - } - - while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { - push(@lines, ${$inserted}{'LINE'}); - $inserted = @{$insertedRef}[$next_insert++]; - $new_linenr++; - fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); - } - - if ($save_line) { - push(@lines, $line); - $new_linenr++; - } - - $old_linenr++; - } - - return @lines; -} - -sub fix_insert_line { - my ($linenr, $line) = @_; - - my $inserted = { - LINENR => $linenr, - LINE => $line, - }; - push(@fixed_inserted, $inserted); -} - -sub fix_delete_line { - my ($linenr, $line) = @_; - - my $deleted = { - LINENR => $linenr, - LINE => $line, - }; - - push(@fixed_deleted, $deleted); -} - -sub ERROR { - my ($type, $msg) = @_; - - if (report("ERROR", $type, $msg)) { - our $clean = 0; - our $cnt_error++; - return 1; - } - return 0; -} -sub WARN { - my ($type, $msg) = @_; - - if (report("WARNING", $type, $msg)) { - our $clean = 0; - our $cnt_warn++; - return 1; - } - return 0; -} -sub CHK { - my ($type, $msg) = @_; - - if ($check && report("CHECK", $type, $msg)) { - our $clean = 0; - our $cnt_chk++; - return 1; - } - return 0; -} - -sub check_absolute_file { - my ($absolute, $herecurr) = @_; - my $file = $absolute; - - ##print "absolute<$absolute>\n"; - - # See if any suffix of this path is a path within the tree. - while ($file =~ s@^[^/]*/@@) { - if (-f "$root/$file") { - ##print "file<$file>\n"; - last; - } - } - if (! -f _) { - return 0; - } - - # It is, so see if the prefix is acceptable. - my $prefix = $absolute; - substr($prefix, -length($file)) = ''; - - ##print "prefix<$prefix>\n"; - if ($prefix ne ".../") { - WARN("USE_RELATIVE_PATH", - "use relative pathname instead of absolute in changelog text\n" . $herecurr); - } -} - -sub trim { - my ($string) = @_; - - $string =~ s/^\s+|\s+$//g; - - return $string; -} - -sub ltrim { - my ($string) = @_; - - $string =~ s/^\s+//; - - return $string; -} - -sub rtrim { - my ($string) = @_; - - $string =~ s/\s+$//; - - return $string; -} - -sub string_find_replace { - my ($string, $find, $replace) = @_; - - $string =~ s/$find/$replace/g; - - return $string; -} - -sub tabify { - my ($leading) = @_; - - my $source_indent = 8; - my $max_spaces_before_tab = $source_indent - 1; - my $spaces_to_tab = " " x $source_indent; - - #convert leading spaces to tabs - 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; - #Remove spaces before a tab - 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; - - return "$leading"; -} - -sub pos_last_openparen { - my ($line) = @_; - - my $pos = 0; - - my $opens = $line =~ tr/\(/\(/; - my $closes = $line =~ tr/\)/\)/; - - my $last_openparen = 0; - - if (($opens == 0) || ($closes >= $opens)) { - return -1; - } - - my $len = length($line); - - for ($pos = 0; $pos < $len; $pos++) { - my $string = substr($line, $pos); - if ($string =~ /^($FuncArg|$balanced_parens)/) { - $pos += length($1) - 1; - } elsif (substr($line, $pos, 1) eq '(') { - $last_openparen = $pos; - } elsif (index($string, '(') == -1) { - last; - } - } - - return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; -} - -sub process { - my $filename = shift; - - my $linenr=0; - my $prevline=""; - my $prevrawline=""; - my $stashline=""; - my $stashrawline=""; - - my $length; - my $indent; - my $previndent=0; - my $stashindent=0; - - our $clean = 1; - my $signoff = 0; - my $is_patch = 0; - my $in_header_lines = $file ? 0 : 1; - my $in_commit_log = 0; #Scanning lines before patch - my $commit_log_possible_stack_dump = 0; - my $commit_log_long_line = 0; - my $commit_log_has_diff = 0; - my $reported_maintainer_file = 0; - my $non_utf8_charset = 0; - - my $last_blank_line = 0; - my $last_coalesced_string_linenr = -1; - - our @report = (); - our $cnt_lines = 0; - our $cnt_error = 0; - our $cnt_warn = 0; - our $cnt_chk = 0; - - # Trace the real file/line as we go. - my $realfile = ''; - my $realline = 0; - my $realcnt = 0; - my $here = ''; - my $in_comment = 0; - my $comment_edge = 0; - my $first_line = 0; - my $p1_prefix = ''; - - my $prev_values = 'E'; - - # suppression flags - my %suppress_ifbraces; - my %suppress_whiletrailers; - my %suppress_export; - my $suppress_statement = 0; - - my %signatures = (); - - # Pre-scan the patch sanitizing the lines. - # Pre-scan the patch looking for any __setup documentation. - # - my @setup_docs = (); - my $setup_docs = 0; - - my $camelcase_file_seeded = 0; - - sanitise_line_reset(); - my $line; - foreach my $rawline (@rawlines) { - $linenr++; - $line = $rawline; - - push(@fixed, $rawline) if ($fix); - - if ($rawline=~/^\+\+\+\s+(\S+)/) { - $setup_docs = 0; - if ($1 =~ m@Documentation/kernel-parameters.txt$@) { - $setup_docs = 1; - } - #next; - } - if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { - $realline=$1-1; - if (defined $2) { - $realcnt=$3+1; - } else { - $realcnt=1+1; - } - $in_comment = 0; - - # Guestimate if this is a continuing comment. Run - # the context looking for a comment "edge". If this - # edge is a close comment then we must be in a comment - # at context start. - my $edge; - my $cnt = $realcnt; - for (my $ln = $linenr + 1; $cnt > 0; $ln++) { - next if (defined $rawlines[$ln - 1] && - $rawlines[$ln - 1] =~ /^-/); - $cnt--; - #print "RAW<$rawlines[$ln - 1]>\n"; - last if (!defined $rawlines[$ln - 1]); - if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && - $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { - ($edge) = $1; - last; - } - } - if (defined $edge && $edge eq '*/') { - $in_comment = 1; - } - - # Guestimate if this is a continuing comment. If this - # is the start of a diff block and this line starts - # ' *' then it is very likely a comment. - if (!defined $edge && - $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) - { - $in_comment = 1; - } - - ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; - sanitise_line_reset($in_comment); - - } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { - # Standardise the strings and chars within the input to - # simplify matching -- only bother with positive lines. - $line = sanitise_line($rawline); - } - push(@lines, $line); - - if ($realcnt > 1) { - $realcnt-- if ($line =~ /^(?:\+| |$)/); - } else { - $realcnt = 0; - } - - #print "==>$rawline\n"; - #print "-->$line\n"; - - if ($setup_docs && $line =~ /^\+/) { - push(@setup_docs, $line); - } - } - - $prefix = ''; - - $realcnt = 0; - $linenr = 0; - $fixlinenr = -1; - foreach my $line (@lines) { - $linenr++; - $fixlinenr++; - my $sline = $line; #copy of $line - $sline =~ s/$;/ /g; #with comments as spaces - - my $rawline = $rawlines[$linenr - 1]; - -#extract the line range in the file after the patch is applied - if (!$in_commit_log && - $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { - $is_patch = 1; - $first_line = $linenr + 1; - $realline=$1-1; - if (defined $2) { - $realcnt=$3+1; - } else { - $realcnt=1+1; - } - annotate_reset(); - $prev_values = 'E'; - - %suppress_ifbraces = (); - %suppress_whiletrailers = (); - %suppress_export = (); - $suppress_statement = 0; - next; - -# track the line number as we move through the hunk, note that -# new versions of GNU diff omit the leading space on completely -# blank context lines so we need to count that too. - } elsif ($line =~ /^( |\+|$)/) { - $realline++; - $realcnt-- if ($realcnt != 0); - - # Measure the line length and indent. - ($length, $indent) = line_stats($rawline); - - # Track the previous line. - ($prevline, $stashline) = ($stashline, $line); - ($previndent, $stashindent) = ($stashindent, $indent); - ($prevrawline, $stashrawline) = ($stashrawline, $rawline); - - #warn "line<$line>\n"; - - } elsif ($realcnt == 1) { - $realcnt--; - } - - my $hunk_line = ($realcnt != 0); - - $here = "#$linenr: " if (!$file); - $here = "#$realline: " if ($file); - - my $found_file = 0; - # extract the filename as it passes - if ($line =~ /^diff --git.*?(\S+)$/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); - $in_commit_log = 0; - $found_file = 1; - } elsif ($line =~ /^\+\+\+\s+(\S+)/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); - $in_commit_log = 0; - - $p1_prefix = $1; - if (!$file && $tree && $p1_prefix ne '' && - -e "$root/$p1_prefix") { - WARN("PATCH_PREFIX", - "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); - } - - if ($realfile =~ m@^include/asm/@) { - ERROR("MODIFIED_INCLUDE_ASM", - "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n"); - } - $found_file = 1; - } - -#make up the handle for any error we report on this line - if ($showfile) { - $prefix = "$realfile:$realline: " - } elsif ($emacs) { - if ($file) { - $prefix = "$filename:$realline: "; - } else { - $prefix = "$filename:$linenr: "; - } - } - - if ($found_file) { - if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { - $check = 1; - } else { - $check = $check_orig; - } - next; - } - - $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); - - my $hereline = "$here\n$rawline\n"; - my $herecurr = "$here\n$rawline\n"; - my $hereprev = "$here\n$prevrawline\n$rawline\n"; - - $cnt_lines++ if ($realcnt != 0); - -# Check if the commit log has what seems like a diff which can confuse patch - if ($in_commit_log && !$commit_log_has_diff && - (($line =~ m@^\s+diff\b.*a/[\w/]+@ && - $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) || - $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || - $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { - ERROR("DIFF_IN_COMMIT_MSG", - "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); - $commit_log_has_diff = 1; - } - -# Check for incorrect file permissions - if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { - my $permhere = $here . "FILE: $realfile\n"; - if ($realfile !~ m@scripts/@ && - $realfile !~ /\.(py|pl|awk|sh)$/) { - ERROR("EXECUTE_PERMISSIONS", - "do not set execute permissions for source files\n" . $permhere); - } - } - -# Check the patch for a signoff: - if ($line =~ /^\s*signed-off-by:/i) { - $signoff++; - $in_commit_log = 0; - } - -# Check if MAINTAINERS is being updated. If so, there's probably no need to -# emit the "does MAINTAINERS need updating?" message on file add/move/delete - if ($line =~ /^\s*MAINTAINERS\s*\|/) { - $reported_maintainer_file = 1; - } - -# Check signature styles - if (!$in_header_lines && - $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { - my $space_before = $1; - my $sign_off = $2; - my $space_after = $3; - my $email = $4; - my $ucfirst_sign_off = ucfirst(lc($sign_off)); - - if ($sign_off !~ /$signature_tags/) { - WARN("BAD_SIGN_OFF", - "Non-standard signature: $sign_off\n" . $herecurr); - } - if (defined $space_before && $space_before ne "") { - if (WARN("BAD_SIGN_OFF", - "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - } - if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { - if (WARN("BAD_SIGN_OFF", - "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - - } - if (!defined $space_after || $space_after ne " ") { - if (WARN("BAD_SIGN_OFF", - "Use a single space after $ucfirst_sign_off\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - } - - my ($email_name, $email_address, $comment) = parse_email($email); - my $suggested_email = format_email(($email_name, $email_address)); - if ($suggested_email eq "") { - ERROR("BAD_SIGN_OFF", - "Unrecognized email address: '$email'\n" . $herecurr); - } else { - my $dequoted = $suggested_email; - $dequoted =~ s/^"//; - $dequoted =~ s/" </ </; - # Don't force email to have quotes - # Allow just an angle bracketed address - if ("$dequoted$comment" ne $email && - "<$email_address>$comment" ne $email && - "$suggested_email$comment" ne $email) { - WARN("BAD_SIGN_OFF", - "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); - } - } - -# Check for duplicate signatures - my $sig_nospace = $line; - $sig_nospace =~ s/\s//g; - $sig_nospace = lc($sig_nospace); - if (defined $signatures{$sig_nospace}) { - WARN("BAD_SIGN_OFF", - "Duplicate signature\n" . $herecurr); - } else { - $signatures{$sig_nospace} = 1; - } - } - -# Check email subject for common tools that don't need to be mentioned - if ($in_header_lines && - $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { - WARN("EMAIL_SUBJECT", - "A patch subject line should describe the change not the tool that found it\n" . $herecurr); - } - -# Check for old stable address - if ($line =~ /^\s*cc:\s*.*<?\bstable\@kernel\.org\b>?.*$/i) { - ERROR("STABLE_ADDRESS", - "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr); - } - -# Check for unwanted Gerrit info - if ($in_commit_log && $line =~ /^\s*change-id:/i) { - ERROR("GERRIT_CHANGE_ID", - "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); - } - -# Check if the commit log is in a possible stack dump - if ($in_commit_log && !$commit_log_possible_stack_dump && - ($line =~ /^\s*(?:WARNING:|BUG:)/ || - $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || - # timestamp - $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) { - # stack dump address - $commit_log_possible_stack_dump = 1; - } - -# Check for line lengths > 75 in commit log, warn once - if ($in_commit_log && !$commit_log_long_line && - length($line) > 75 && - !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || - # file delta changes - $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || - # filename then : - $line =~ /^\s*(?:Fixes:|Link:)/i || - # A Fixes: or Link: line - $commit_log_possible_stack_dump)) { - WARN("COMMIT_LOG_LONG_LINE", - "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); - $commit_log_long_line = 1; - } - -# Reset possible stack dump if a blank line is found - if ($in_commit_log && $commit_log_possible_stack_dump && - $line =~ /^\s*$/) { - $commit_log_possible_stack_dump = 0; - } - -# Check for git id commit length and improperly formed commit descriptions - if ($in_commit_log && !$commit_log_possible_stack_dump && - ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || - ($line =~ /\b[0-9a-f]{12,40}\b/i && - $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && - $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { - my $init_char = "c"; - my $orig_commit = ""; - my $short = 1; - my $long = 0; - my $case = 1; - my $space = 1; - my $hasdesc = 0; - my $hasparens = 0; - my $id = '0123456789ab'; - my $orig_desc = "commit description"; - my $description = ""; - - if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { - $init_char = $1; - $orig_commit = lc($2); - } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) { - $orig_commit = lc($1); - } - - $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i); - $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i); - $space = 0 if ($line =~ /\bcommit [0-9a-f]/i); - $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); - if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) { - $orig_desc = $1; - $hasparens = 1; - } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i && - defined $rawlines[$linenr] && - $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) { - $orig_desc = $1; - $hasparens = 1; - } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i && - defined $rawlines[$linenr] && - $rawlines[$linenr] =~ /^\s*[^"]+"\)/) { - $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i; - $orig_desc = $1; - $rawlines[$linenr] =~ /^\s*([^"]+)"\)/; - $orig_desc .= " " . $1; - $hasparens = 1; - } - - ($id, $description) = git_commit_info($orig_commit, - $id, $orig_desc); - - if ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens) { - ERROR("GIT_COMMIT_ID", - "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); - } - } - -# Check for added, moved or deleted files - if (!$reported_maintainer_file && !$in_commit_log && - ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || - $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || - ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && - (defined($1) || defined($2))))) { - $reported_maintainer_file = 1; - WARN("FILE_PATH_CHANGES", - "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); - } - -# Check for wrappage within a valid hunk of the file - if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { - ERROR("CORRUPTED_PATCH", - "patch seems to be corrupt (line wrapped?)\n" . - $herecurr) if (!$emitted_corrupt++); - } - -# Check for absolute kernel paths. - if ($tree) { - while ($line =~ m{(?:^|\s)(/\S*)}g) { - my $file = $1; - - if ($file =~ m{^(.*?)(?::\d+)+:?$} && - check_absolute_file($1, $herecurr)) { - # - } else { - check_absolute_file($file, $herecurr); - } - } - } - -# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php - if (($realfile =~ /^$/ || $line =~ /^\+/) && - $rawline !~ m/^$UTF8*$/) { - my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); - - my $blank = copy_spacing($rawline); - my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; - my $hereptr = "$hereline$ptr\n"; - - CHK("INVALID_UTF8", - "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); - } - -# Check if it's the start of a commit log -# (not a header line and we haven't seen the patch filename) - if ($in_header_lines && $realfile =~ /^$/ && - !($rawline =~ /^\s+\S/ || - $rawline =~ /^(commit\b|from\b|[\w-]+:).*$/i)) { - $in_header_lines = 0; - $in_commit_log = 1; - } - -# Check if there is UTF-8 in a commit log when a mail header has explicitly -# declined it, i.e defined some charset where it is missing. - if ($in_header_lines && - $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && - $1 !~ /utf-8/i) { - $non_utf8_charset = 1; - } - - if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && - $rawline =~ /$NON_ASCII_UTF8/) { - WARN("UTF8_BEFORE_PATCH", - "8-bit UTF-8 used in possible commit log\n" . $herecurr); - } - -# Check for various typo / spelling mistakes - if (defined($misspellings) && - ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { - while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) { - my $typo = $1; - my $typo_fix = $spelling_fix{lc($typo)}; - $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); - $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); - my $msg_type = \&WARN; - $msg_type = \&CHK if ($file); - if (&{$msg_type}("TYPO_SPELLING", - "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; - } - } - } - -# ignore non-hunk lines and lines being removed - next if (!$hunk_line || $line =~ /^-/); - -#trailing whitespace - if ($line =~ /^\+.*\015/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (ERROR("DOS_LINE_ENDINGS", - "DOS line endings\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/[\s\015]+$//; - } - } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (ERROR("TRAILING_WHITESPACE", - "trailing whitespace\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+$//; - } - - $rpt_cleaners = 1; - } - -# Check for FSF mailing addresses. - if ($rawline =~ /\bwrite to the Free/i || - $rawline =~ /\b59\s+Temple\s+Pl/i || - $rawline =~ /\b51\s+Franklin\s+St/i) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - my $msg_type = \&ERROR; - $msg_type = \&CHK if ($file); - &{$msg_type}("FSF_MAILING_ADDRESS", - "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) - } - -# check for Kconfig help text having a real description -# Only applies when adding the entry originally, after that we do not have -# sufficient context to determine whether it is indeed long enough. - if ($realfile =~ /Kconfig/ && - $line =~ /^\+\s*config\s+/) { - my $length = 0; - my $cnt = $realcnt; - my $ln = $linenr + 1; - my $f; - my $is_start = 0; - my $is_end = 0; - for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) { - $f = $lines[$ln - 1]; - $cnt-- if ($lines[$ln - 1] !~ /^-/); - $is_end = $lines[$ln - 1] =~ /^\+/; - - next if ($f =~ /^-/); - last if (!$file && $f =~ /^\@\@/); - - if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { - $is_start = 1; - } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { - $length = -1; - } - - $f =~ s/^.//; - $f =~ s/#.*//; - $f =~ s/^\s+//; - next if ($f =~ /^$/); - if ($f =~ /^\s*config\s/) { - $is_end = 1; - last; - } - $length++; - } - if ($is_start && $is_end && $length < $min_conf_desc_length) { - WARN("CONFIG_DESCRIPTION", - "please write a paragraph that describes the config symbol fully\n" . $herecurr); - } - #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; - } - -# discourage the addition of CONFIG_EXPERIMENTAL in Kconfig. - if ($realfile =~ /Kconfig/ && - $line =~ /.\s*depends on\s+.*\bEXPERIMENTAL\b/) { - WARN("CONFIG_EXPERIMENTAL", - "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); - } - -# discourage the use of boolean for type definition attributes of Kconfig options - if ($realfile =~ /Kconfig/ && - $line =~ /^\+\s*\bboolean\b/) { - WARN("CONFIG_TYPE_BOOLEAN", - "Use of boolean is deprecated, please use bool instead.\n" . $herecurr); - } - - if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && - ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { - my $flag = $1; - my $replacement = { - 'EXTRA_AFLAGS' => 'asflags-y', - 'EXTRA_CFLAGS' => 'ccflags-y', - 'EXTRA_CPPFLAGS' => 'cppflags-y', - 'EXTRA_LDFLAGS' => 'ldflags-y', - }; - - WARN("DEPRECATED_VARIABLE", - "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); - } - -# check for DT compatible documentation - if (defined $root && - (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || - ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { - - my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; - - my $dt_path = $root . "/Documentation/devicetree/bindings/"; - my $vp_file = $dt_path . "vendor-prefixes.txt"; - - foreach my $compat (@compats) { - my $compat2 = $compat; - $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/; - my $compat3 = $compat; - $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/; - `grep -Erq "$compat|$compat2|$compat3" $dt_path`; - if ( $? >> 8 ) { - WARN("UNDOCUMENTED_DT_STRING", - "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); - } - - next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; - my $vendor = $1; - `grep -Eq "^$vendor\\b" $vp_file`; - if ( $? >> 8 ) { - WARN("UNDOCUMENTED_DT_STRING", - "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); - } - } - } - -# check we are in a valid source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c|s|S|pl|sh|dtsi|dts)$/); - -# line length limit (with some exclusions) -# -# There are a few types of lines that may extend beyond $max_line_length: -# logging functions like pr_info that end in a string -# lines with a single string -# #defines that are a single string -# -# There are 3 different line length message types: -# LONG_LINE_COMMENT a comment starts before but extends beyond $max_linelength -# LONG_LINE_STRING a string starts before but extends beyond $max_line_length -# LONG_LINE all other lines longer than $max_line_length -# -# if LONG_LINE is ignored, the other 2 types are also ignored -# - - if ($line =~ /^\+/ && $length > $max_line_length) { - my $msg_type = "LONG_LINE"; - - # Check the allowed long line types first - - # logging functions that end in a string that starts - # before $max_line_length - if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = ""; - - # lines with only strings (w/ possible termination) - # #defines with only strings - } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || - $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { - $msg_type = ""; - - # Otherwise set the alternate message types - - # a comment starts before $max_line_length - } elsif ($line =~ /($;[\s$;]*)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = "LONG_LINE_COMMENT" - - # a quoted string starts before $max_line_length - } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = "LONG_LINE_STRING" - } - - if ($msg_type ne "" && - (show_type("LONG_LINE") || show_type($msg_type))) { - WARN($msg_type, - "line over $max_line_length characters\n" . $herecurr); - } - } - -# check for adding lines without a newline. - if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { - WARN("MISSING_EOF_NEWLINE", - "adding a line without newline at end of file\n" . $herecurr); - } - -# Blackfin: use hi/lo macros - if ($realfile =~ m@arch/blackfin/.*\.S$@) { - if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("LO_MACRO", - "use the LO() macro, not (... & 0xFFFF)\n" . $herevet); - } - if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("HI_MACRO", - "use the HI() macro, not (... >> 16)\n" . $herevet); - } - } - -# check we are in a valid source file C or perl if not then ignore this hunk - next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); - -# at the beginning of a line any tabs must come first and anything -# more than 8 must use tabs. - if ($rawline =~ /^\+\s* \t\s*\S/ || - $rawline =~ /^\+\s* \s*/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - $rpt_cleaners = 1; - if (ERROR("CODE_INDENT", - "code indent should use tabs where possible\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; - } - } - -# check for space before tabs. - if ($rawline =~ /^\+/ && $rawline =~ / \t/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (WARN("SPACE_BEFORE_TAB", - "please, no space before tabs\n" . $herevet) && - $fix) { - while ($fixed[$fixlinenr] =~ - s/(^\+.*) {8,8}\t/$1\t\t/) {} - while ($fixed[$fixlinenr] =~ - s/(^\+.*) +\t/$1\t/) {} - } - } - -# check for && or || at the start of a line - if ($rawline =~ /^\+\s*(&&|\|\|)/) { - CHK("LOGICAL_CONTINUATIONS", - "Logical continuations should be on the previous line\n" . $hereprev); - } - -# check multi-line statement indentation matches previous line - if ($^V && $^V ge 5.10.0 && - $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { - $prevline =~ /^\+(\t*)(.*)$/; - my $oldindent = $1; - my $rest = $2; - - my $pos = pos_last_openparen($rest); - if ($pos >= 0) { - $line =~ /^(\+| )([ \t]*)/; - my $newindent = $2; - - my $goodtabindent = $oldindent . - "\t" x ($pos / 8) . - " " x ($pos % 8); - my $goodspaceindent = $oldindent . " " x $pos; - - if ($newindent ne $goodtabindent && - $newindent ne $goodspaceindent) { - - if (CHK("PARENTHESIS_ALIGNMENT", - "Alignment should match open parenthesis\n" . $hereprev) && - $fix && $line =~ /^\+/) { - $fixed[$fixlinenr] =~ - s/^\+[ \t]*/\+$goodtabindent/; - } - } - } - } - -# check for space after cast like "(int) foo" or "(struct foo) bar" -# avoid checking a few false positives: -# "sizeof(<type>)" or "__alignof__(<type>)" -# function pointer declarations like "(*foo)(int) = bar;" -# structure definitions like "(struct foo) { 0 };" -# multiline macros that define functions -# known attributes or the __attribute__ keyword - if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && - (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { - if (CHK("SPACING", - "No space is necessary after a cast\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/(\(\s*$Type\s*\))[ \t]+/$1/; - } - } - -# Block comment styles -# Networking with an initial /* - if ($realfile =~ m@^(drivers/net/|net/)@ && - $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && - $rawline =~ /^\+[ \t]*\*/ && - $realline > 2) { - WARN("NETWORKING_BLOCK_COMMENT_STYLE", - "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); - } - -# Block comments use * on subsequent lines - if ($prevline =~ /$;[ \t]*$/ && #ends in comment - $prevrawline =~ /^\+.*?\/\*/ && #starting /* - $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ - $rawline =~ /^\+/ && #line is new - $rawline !~ /^\+[ \t]*\*/) { #no leading * - WARN("BLOCK_COMMENT_STYLE", - "Block comments use * on subsequent lines\n" . $hereprev); - } - -# Block comments use */ on trailing lines - if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ - $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ - $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ - $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ - WARN("BLOCK_COMMENT_STYLE", - "Block comments use a trailing */ on a separate line\n" . $herecurr); - } - -# check for missing blank lines after struct/union declarations -# with exceptions for various attributes and macros - if ($prevline =~ /^[\+ ]};?\s*$/ && - $line =~ /^\+/ && - !($line =~ /^\+\s*$/ || - $line =~ /^\+\s*EXPORT_SYMBOL/ || - $line =~ /^\+\s*MODULE_/i || - $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || - $line =~ /^\+[a-z_]*init/ || - $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || - $line =~ /^\+\s*DECLARE/ || - $line =~ /^\+\s*__setup/)) { - if (CHK("LINE_SPACING", - "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && - $fix) { - fix_insert_line($fixlinenr, "\+"); - } - } - -# check for multiple consecutive blank lines - if ($prevline =~ /^[\+ ]\s*$/ && - $line =~ /^\+\s*$/ && - $last_blank_line != ($linenr - 1)) { - if (CHK("LINE_SPACING", - "Please don't use multiple blank lines\n" . $hereprev) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - - $last_blank_line = $linenr; - } - -# check for missing blank lines after declarations - if ($sline =~ /^\+\s+\S/ && #Not at char 1 - # actual declarations - ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || - # function pointer declarations - $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || - # foo bar; where foo is some local typedef or #define - $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || - # known declaration macros - $prevline =~ /^\+\s+$declaration_macros/) && - # for "else if" which can look like "$Ident $Ident" - !($prevline =~ /^\+\s+$c90_Keywords\b/ || - # other possible extensions of declaration lines - $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || - # not starting a section or a macro "\" extended line - $prevline =~ /(?:\{\s*|\\)$/) && - # looks like a declaration - !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || - # function pointer declarations - $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || - # foo bar; where foo is some local typedef or #define - $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || - # known declaration macros - $sline =~ /^\+\s+$declaration_macros/ || - # start of struct or union or enum - $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || - # start or end of block or continuation of declaration - $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || - # bitfield continuation - $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || - # other possible extensions of declaration lines - $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && - # indentation of previous and current line are the same - (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { - if (WARN("LINE_SPACING", - "Missing a blank line after declarations\n" . $hereprev) && - $fix) { - fix_insert_line($fixlinenr, "\+"); - } - } - -# check for spaces at the beginning of a line. -# Exceptions: -# 1) within comments -# 2) indented preprocessor commands -# 3) hanging labels - if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (WARN("LEADING_SPACE", - "please, no spaces at the start of a line\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; - } - } - -# check we are in a valid C source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c)$/); - -# check indentation of any line with a bare else -# (but not if it is a multiple line "if (foo) return bar; else return baz;") -# if the previous line is a break or return and is indented 1 tab more... - if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { - my $tabs = length($1) + 1; - if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || - ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && - defined $lines[$linenr] && - $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { - WARN("UNNECESSARY_ELSE", - "else is not generally useful after a break or return\n" . $hereprev); - } - } - -# check indentation of a line with a break; -# if the previous line is a goto or return and is indented the same # of tabs - if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { - my $tabs = $1; - if ($prevline =~ /^\+$tabs(?:goto|return)\b/) { - WARN("UNNECESSARY_BREAK", - "break is not useful after a goto or return\n" . $hereprev); - } - } - -# discourage the addition of CONFIG_EXPERIMENTAL in #if(def). - if ($line =~ /^\+\s*\#\s*if.*\bCONFIG_EXPERIMENTAL\b/) { - WARN("CONFIG_EXPERIMENTAL", - "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); - } - -# check for RCS/CVS revision markers - if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { - WARN("CVS_KEYWORD", - "CVS style keyword markers, these will _not_ be updated\n". $herecurr); - } - -# Blackfin: don't use __builtin_bfin_[cs]sync - if ($line =~ /__builtin_bfin_csync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("CSYNC", - "use the CSYNC() macro in asm/blackfin.h\n" . $herevet); - } - if ($line =~ /__builtin_bfin_ssync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("SSYNC", - "use the SSYNC() macro in asm/blackfin.h\n" . $herevet); - } - -# check for old HOTPLUG __dev<foo> section markings - if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { - WARN("HOTPLUG_SECTION", - "Using $1 is unnecessary\n" . $herecurr); - } - -# Check for potential 'bare' types - my ($stat, $cond, $line_nr_next, $remain_next, $off_next, - $realline_next); -#print "LINE<$line>\n"; - if ($linenr >= $suppress_statement && - $realcnt && $sline =~ /.\s*\S/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0); - $stat =~ s/\n./\n /g; - $cond =~ s/\n./\n /g; - -#print "linenr<$linenr> <$stat>\n"; - # If this statement has no statement boundaries within - # it there is no point in retrying a statement scan - # until we hit end of it. - my $frag = $stat; $frag =~ s/;+\s*$//; - if ($frag !~ /(?:{|;)/) { -#print "skip<$line_nr_next>\n"; - $suppress_statement = $line_nr_next; - } - - # Find the real next line. - $realline_next = $line_nr_next; - if (defined $realline_next && - (!defined $lines[$realline_next - 1] || - substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { - $realline_next++; - } - - my $s = $stat; - $s =~ s/{.*$//s; - - # Ignore goto labels. - if ($s =~ /$Ident:\*$/s) { - - # Ignore functions being called - } elsif ($s =~ /^.\s*$Ident\s*\(/s) { - - } elsif ($s =~ /^.\s*else\b/s) { - - # declarations always start with types - } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { - my $type = $1; - $type =~ s/\s+/ /g; - possible($type, "A:" . $s); - - # definitions in global scope can only start with types - } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { - possible($1, "B:" . $s); - } - - # any (foo ... *) is a pointer cast, and foo is a type - while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { - possible($1, "C:" . $s); - } - - # Check for any sort of function declaration. - # int foo(something bar, other baz); - # void (*store_gdt)(x86_descr_ptr *); - if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { - my ($name_len) = length($1); - - my $ctx = $s; - substr($ctx, 0, $name_len + 1, ''); - $ctx =~ s/\)[^\)]*$//; - - for my $arg (split(/\s*,\s*/, $ctx)) { - if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { - - possible($1, "D:" . $s); - } - } - } - - } - -# -# Checks which may be anchored in the context. -# - -# Check for switch () and associated case and default -# statements should be at the same indent. - if ($line=~/\bswitch\s*\(.*\)/) { - my $err = ''; - my $sep = ''; - my @ctx = ctx_block_outer($linenr, $realcnt); - shift(@ctx); - for my $ctx (@ctx) { - my ($clen, $cindent) = line_stats($ctx); - if ($ctx =~ /^\+\s*(case\s+|default:)/ && - $indent != $cindent) { - $err .= "$sep$ctx\n"; - $sep = ''; - } else { - $sep = "[...]\n"; - } - } - if ($err ne '') { - ERROR("SWITCH_CASE_INDENT_LEVEL", - "switch and case should be at the same indent\n$hereline$err"); - } - } - -# if/while/etc brace do not go on next line, unless defining a do while loop, -# or if that brace on the next line is for something else - if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { - my $pre_ctx = "$1$2"; - - my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); - - if ($line =~ /^\+\t{6,}/) { - WARN("DEEP_INDENTATION", - "Too many leading tabs - consider code refactoring\n" . $herecurr); - } - - my $ctx_cnt = $realcnt - $#ctx - 1; - my $ctx = join("\n", @ctx); - - my $ctx_ln = $linenr; - my $ctx_skip = $realcnt; - - while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && - defined $lines[$ctx_ln - 1] && - $lines[$ctx_ln - 1] =~ /^-/)) { - ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; - $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); - $ctx_ln++; - } - - #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; - #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; - - if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { - ERROR("OPEN_BRACE", - "that open brace { should be on the previous line\n" . - "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); - } - if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && - $ctx =~ /\)\s*\;\s*$/ && - defined $lines[$ctx_ln - 1]) - { - my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); - if ($nindent > $indent) { - WARN("TRAILING_SEMICOLON", - "trailing semicolon indicates no statements, indent implies otherwise\n" . - "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); - } - } - } - -# Check relative indent for conditionals and blocks. - if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0) - if (!defined $stat); - my ($s, $c) = ($stat, $cond); - - substr($s, 0, length($c), ''); - - # remove inline comments - $s =~ s/$;/ /g; - $c =~ s/$;/ /g; - - # Find out how long the conditional actually is. - my @newlines = ($c =~ /\n/gs); - my $cond_lines = 1 + $#newlines; - - # Make sure we remove the line prefixes as we have - # none on the first line, and are going to readd them - # where necessary. - $s =~ s/\n./\n/gs; - while ($s =~ /\n\s+\\\n/) { - $cond_lines += $s =~ s/\n\s+\\\n/\n/g; - } - - # We want to check the first line inside the block - # starting at the end of the conditional, so remove: - # 1) any blank line termination - # 2) any opening brace { on end of the line - # 3) any do (...) { - my $continuation = 0; - my $check = 0; - $s =~ s/^.*\bdo\b//; - $s =~ s/^\s*{//; - if ($s =~ s/^\s*\\//) { - $continuation = 1; - } - if ($s =~ s/^\s*?\n//) { - $check = 1; - $cond_lines++; - } - - # Also ignore a loop construct at the end of a - # preprocessor statement. - if (($prevline =~ /^.\s*#\s*define\s/ || - $prevline =~ /\\\s*$/) && $continuation == 0) { - $check = 0; - } - - my $cond_ptr = -1; - $continuation = 0; - while ($cond_ptr != $cond_lines) { - $cond_ptr = $cond_lines; - - # If we see an #else/#elif then the code - # is not linear. - if ($s =~ /^\s*\#\s*(?:else|elif)/) { - $check = 0; - } - - # Ignore: - # 1) blank lines, they should be at 0, - # 2) preprocessor lines, and - # 3) labels. - if ($continuation || - $s =~ /^\s*?\n/ || - $s =~ /^\s*#\s*?/ || - $s =~ /^\s*$Ident\s*:/) { - $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; - if ($s =~ s/^.*?\n//) { - $cond_lines++; - } - } - } - - my (undef, $sindent) = line_stats("+" . $s); - my $stat_real = raw_line($linenr, $cond_lines); - - # Check if either of these lines are modified, else - # this is not this patch's fault. - if (!defined($stat_real) || - $stat !~ /^\+/ && $stat_real !~ /^\+/) { - $check = 0; - } - if (defined($stat_real) && $cond_lines > 1) { - $stat_real = "[...]\n$stat_real"; - } - - #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; - - if ($check && $s ne '' && - (($sindent % 8) != 0 || - ($sindent < $indent) || - ($sindent > $indent + 8))) { - WARN("SUSPECT_CODE_INDENT", - "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); - } - } - - # Track the 'values' across context and added lines. - my $opline = $line; $opline =~ s/^./ /; - my ($curr_values, $curr_vars) = - annotate_values($opline . "\n", $prev_values); - $curr_values = $prev_values . $curr_values; - if ($dbg_values) { - my $outline = $opline; $outline =~ s/\t/ /g; - print "$linenr > .$outline\n"; - print "$linenr > $curr_values\n"; - print "$linenr > $curr_vars\n"; - } - $prev_values = substr($curr_values, -1); - -#ignore lines not being added - next if ($line =~ /^[^\+]/); - -# check for declarations of signed or unsigned without int - while ($line =~ m{($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { - my $type = $1; - my $var = $2; - $var = "" if (!defined $var); - if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { - my $sign = $1; - my $pointer = $2; - - $pointer = "" if (!defined $pointer); - - if (WARN("UNSPECIFIED_INT", - "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && - $fix) { - my $decl = trim($sign) . " int "; - my $comp_pointer = $pointer; - $comp_pointer =~ s/\s//g; - $decl .= $comp_pointer; - $decl = rtrim($decl) if ($var eq ""); - $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; - } - } - } - -# TEST: allow direct testing of the type matcher. - if ($dbg_type) { - if ($line =~ /^.\s*$Declare\s*$/) { - ERROR("TEST_TYPE", - "TEST: is type\n" . $herecurr); - } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { - ERROR("TEST_NOT_TYPE", - "TEST: is not type ($1 is)\n". $herecurr); - } - next; - } -# TEST: allow direct testing of the attribute matcher. - if ($dbg_attr) { - if ($line =~ /^.\s*$Modifier\s*$/) { - ERROR("TEST_ATTR", - "TEST: is attr\n" . $herecurr); - } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { - ERROR("TEST_NOT_ATTR", - "TEST: is not attr ($1 is)\n". $herecurr); - } - next; - } - -# check for initialisation to aggregates open brace on the next line - if ($line =~ /^.\s*{/ && - $prevline =~ /(?:^|[^=])=\s*$/) { - if (ERROR("OPEN_BRACE", - "that open brace { should be on the previous line\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/\s*=\s*$/ = {/; - fix_insert_line($fixlinenr, $fixedline); - $fixedline = $line; - $fixedline =~ s/^(.\s*){\s*/$1/; - fix_insert_line($fixlinenr, $fixedline); - } - } - -# -# Checks which are anchored on the added line. -# - -# check for malformed paths in #include statements (uses RAW line) - if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { - my $path = $1; - if ($path =~ m{//}) { - ERROR("MALFORMED_INCLUDE", - "malformed #include filename\n" . $herecurr); - } - if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { - ERROR("UAPI_INCLUDE", - "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); - } - } - -# no C99 // comments - if ($line =~ m{//}) { - if (ERROR("C99_COMMENTS", - "do not use C99 // comments\n" . $herecurr) && - $fix) { - my $line = $fixed[$fixlinenr]; - if ($line =~ /\/\/(.*)$/) { - my $comment = trim($1); - $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; - } - } - } - # Remove C99 comments. - $line =~ s@//.*@@; - $opline =~ s@//.*@@; - -# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider -# the whole statement. -#print "APW <$lines[$realline_next - 1]>\n"; - if (defined $realline_next && - exists $lines[$realline_next - 1] && - !defined $suppress_export{$realline_next} && - ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { - # Handle definitions which produce identifiers with - # a prefix: - # XXX(foo); - # EXPORT_SYMBOL(something_foo); - my $name = $1; - if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && - $name =~ /^${Ident}_$2/) { -#print "FOO C name<$name>\n"; - $suppress_export{$realline_next} = 1; - - } elsif ($stat !~ /(?: - \n.}\s*$| - ^.DEFINE_$Ident\(\Q$name\E\)| - ^.DECLARE_$Ident\(\Q$name\E\)| - ^.LIST_HEAD\(\Q$name\E\)| - ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| - \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() - )/x) { -#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; - $suppress_export{$realline_next} = 2; - } else { - $suppress_export{$realline_next} = 1; - } - } - if (!defined $suppress_export{$linenr} && - $prevline =~ /^.\s*$/ && - ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { -#print "FOO B <$lines[$linenr - 1]>\n"; - $suppress_export{$linenr} = 2; - } - if (defined $suppress_export{$linenr} && - $suppress_export{$linenr} == 2) { - WARN("EXPORT_SYMBOL", - "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); - } - -# check for global initialisers. - if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) { - if (ERROR("GLOBAL_INITIALISERS", - "do not initialise globals to $1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; - } - } -# check for static initialisers. - if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { - if (ERROR("INITIALISED_STATIC", - "do not initialise statics to $1\n" . - $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; - } - } - -# check for misordered declarations of char/short/int/long with signed/unsigned - while ($sline =~ m{(\b$TypeMisordered\b)}g) { - my $tmp = trim($1); - WARN("MISORDERED_TYPE", - "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); - } - -# check for static const char * arrays. - if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "static const char * array should probably be static const char * const\n" . - $herecurr); - } - -# check for static char foo[] = "bar" declarations. - if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "static char array declaration should probably be static const char\n" . - $herecurr); - } - -# check for const <foo> const where <foo> is not a pointer or array type - if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { - my $found = $1; - if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { - WARN("CONST_CONST", - "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); - } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { - WARN("CONST_CONST", - "'const $found const' should probably be 'const $found'\n" . $herecurr); - } - } - -# check for non-global char *foo[] = {"bar", ...} declarations. - if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "char * array declaration might be better as static const\n" . - $herecurr); - } - -# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) - if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { - my $array = $1; - if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { - my $array_div = $1; - if (WARN("ARRAY_SIZE", - "Prefer ARRAY_SIZE($array)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; - } - } - } - -# check for function declarations without arguments like "int foo()" - if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { - if (ERROR("FUNCTION_WITHOUT_ARGS", - "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; - } - } - -# check for uses of DEFINE_PCI_DEVICE_TABLE - if ($line =~ /\bDEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=/) { - if (WARN("DEFINE_PCI_DEVICE_TABLE", - "Prefer struct pci_device_id over deprecated DEFINE_PCI_DEVICE_TABLE\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b(?:static\s+|)DEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=\s*/static const struct pci_device_id $1\[\] = /; - } - } - -# check for new typedefs, only function parameters and sparse annotations -# make sense. - if ($line =~ /\btypedef\s/ && - $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && - $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && - $line !~ /\b$typeTypedefs\b/ && - $line !~ /\b__bitwise(?:__|)\b/) { - WARN("NEW_TYPEDEFS", - "do not add new typedefs\n" . $herecurr); - } - -# * goes on variable not on type - # (char*[ const]) - while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { - #print "AA<$1>\n"; - my ($ident, $from, $to) = ($1, $2, $2); - - # Should start with a space. - $to =~ s/^(\S)/ $1/; - # Should not end with a space. - $to =~ s/\s+$//; - # '*'s should not have spaces between. - while ($to =~ s/\*\s+\*/\*\*/) { - } - -## print "1: from<$from> to<$to> ident<$ident>\n"; - if ($from ne $to) { - if (ERROR("POINTER_LOCATION", - "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && - $fix) { - my $sub_from = $ident; - my $sub_to = $ident; - $sub_to =~ s/\Q$from\E/$to/; - $fixed[$fixlinenr] =~ - s@\Q$sub_from\E@$sub_to@; - } - } - } - while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { - #print "BB<$1>\n"; - my ($match, $from, $to, $ident) = ($1, $2, $2, $3); - - # Should start with a space. - $to =~ s/^(\S)/ $1/; - # Should not end with a space. - $to =~ s/\s+$//; - # '*'s should not have spaces between. - while ($to =~ s/\*\s+\*/\*\*/) { - } - # Modifiers should have spaces. - $to =~ s/(\b$Modifier$)/$1 /; - -## print "2: from<$from> to<$to> ident<$ident>\n"; - if ($from ne $to && $ident !~ /^$Modifier$/) { - if (ERROR("POINTER_LOCATION", - "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && - $fix) { - - my $sub_from = $match; - my $sub_to = $match; - $sub_to =~ s/\Q$from\E/$to/; - $fixed[$fixlinenr] =~ - s@\Q$sub_from\E@$sub_to@; - } - } - } - -# avoid BUG() or BUG_ON() - if ($line =~ /\b(?:BUG|BUG_ON)\b/) { - my $msg_type = \&WARN; - $msg_type = \&CHK if ($file); - &{$msg_type}("AVOID_BUG", - "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); - } - -# avoid LINUX_VERSION_CODE - if ($line =~ /\bLINUX_VERSION_CODE\b/) { - WARN("LINUX_VERSION_CODE", - "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); - } - -# check for uses of printk_ratelimit - if ($line =~ /\bprintk_ratelimit\s*\(/) { - WARN("PRINTK_RATELIMITED", - "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); - } - -# printk should use KERN_* levels. Note that follow on printk's on the -# same line do not need a level, so we use the current block context -# to try and find and validate the current printk. In summary the current -# printk includes all preceding printk's which have no newline on the end. -# we assume the first bad printk is the one to report. - if ($line =~ /\bprintk\((?!KERN_)\s*"/) { - my $ok = 0; - for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { - #print "CHECK<$lines[$ln - 1]\n"; - # we have a preceding printk if it ends - # with "\n" ignore it, else it is to blame - if ($lines[$ln - 1] =~ m{\bprintk\(}) { - if ($rawlines[$ln - 1] !~ m{\\n"}) { - $ok = 1; - } - last; - } - } - if ($ok == 0) { - WARN("PRINTK_WITHOUT_KERN_LEVEL", - "printk() should include KERN_ facility level\n" . $herecurr); - } - } - - if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { - my $orig = $1; - my $level = lc($orig); - $level = "warn" if ($level eq "warning"); - my $level2 = $level; - $level2 = "dbg" if ($level eq "debug"); - WARN("PREFER_PR_LEVEL", - "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); - } - - if ($line =~ /\bpr_warning\s*\(/) { - if (WARN("PREFER_PR_LEVEL", - "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\bpr_warning\b/pr_warn/; - } - } - - if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { - my $orig = $1; - my $level = lc($orig); - $level = "warn" if ($level eq "warning"); - $level = "dbg" if ($level eq "debug"); - WARN("PREFER_DEV_LEVEL", - "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); - } - -# ENOSYS means "bad syscall nr" and nothing else. This will have a small -# number of false positives, but assembly files are not checked, so at -# least the arch entry code will not trigger this warning. - if ($line =~ /\bENOSYS\b/) { - WARN("ENOSYS", - "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); - } - -# function brace can't be on same line, except for #defines of do while, -# or if closed on same line - if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and - !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { - if (ERROR("OPEN_BRACE", - "open brace '{' following function declarations go on the next line\n" . $herecurr) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - my $fixed_line = $rawline; - $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/; - my $line1 = $1; - my $line2 = $2; - fix_insert_line($fixlinenr, ltrim($line1)); - fix_insert_line($fixlinenr, "\+{"); - if ($line2 !~ /^\s*$/) { - fix_insert_line($fixlinenr, "\+\t" . trim($line2)); - } - } - } - -# open braces for enum, union and struct go on the same line. - if ($line =~ /^.\s*{/ && - $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { - if (ERROR("OPEN_BRACE", - "open brace '{' following $1 go on the same line\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = rtrim($prevrawline) . " {"; - fix_insert_line($fixlinenr, $fixedline); - $fixedline = $rawline; - $fixedline =~ s/^(.\s*){\s*/$1\t/; - if ($fixedline !~ /^\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - } - } - -# missing space after union, struct or enum definition - if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { - if (WARN("SPACING", - "missing space after $1 definition\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; - } - } - -# Function pointer declarations -# check spacing between type, funcptr, and args -# canonical declaration is "type (*funcptr)(args...)" - if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { - my $declare = $1; - my $pre_pointer_space = $2; - my $post_pointer_space = $3; - my $funcname = $4; - my $post_funcname_space = $5; - my $pre_args_space = $6; - -# the $Declare variable will capture all spaces after the type -# so check it for a missing trailing missing space but pointer return types -# don't need a space so don't warn for those. - my $post_declare_space = ""; - if ($declare =~ /(\s+)$/) { - $post_declare_space = $1; - $declare = rtrim($declare); - } - if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { - WARN("SPACING", - "missing space after return type\n" . $herecurr); - $post_declare_space = " "; - } - -# unnecessary space "type (*funcptr)(args...)" -# This test is not currently implemented because these declarations are -# equivalent to -# int foo(int bar, ...) -# and this is form shouldn't/doesn't generate a checkpatch warning. -# -# elsif ($declare =~ /\s{2,}$/) { -# WARN("SPACING", -# "Multiple spaces after return type\n" . $herecurr); -# } - -# unnecessary space "type ( *funcptr)(args...)" - if (defined $pre_pointer_space && - $pre_pointer_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space after function pointer open parenthesis\n" . $herecurr); - } - -# unnecessary space "type (* funcptr)(args...)" - if (defined $post_pointer_space && - $post_pointer_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space before function pointer name\n" . $herecurr); - } - -# unnecessary space "type (*funcptr )(args...)" - if (defined $post_funcname_space && - $post_funcname_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space after function pointer name\n" . $herecurr); - } - -# unnecessary space "type (*funcptr) (args...)" - if (defined $pre_args_space && - $pre_args_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space before function pointer arguments\n" . $herecurr); - } - - if (show_type("SPACING") && $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; - } - } - -# check for spacing round square brackets; allowed: -# 1. with a type on the left -- int [] a; -# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, -# 3. inside a curly brace -- = { [0...10] = 5 } - while ($line =~ /(.*?\s)\[/g) { - my ($where, $prefix) = ($-[1], $1); - if ($prefix !~ /$Type\s+$/ && - ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /[{,]\s+$/) { - if (ERROR("BRACKET_SPACE", - "space prohibited before open square bracket '['\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(\+.*?)\s+\[/$1\[/; - } - } - } - -# check for spaces between functions and their parentheses. - while ($line =~ /($Ident)\s+\(/g) { - my $name = $1; - my $ctx_before = substr($line, 0, $-[1]); - my $ctx = "$ctx_before$name"; - - # Ignore those directives where spaces _are_ permitted. - if ($name =~ /^(?: - if|for|while|switch|return|case| - volatile|__volatile__| - __attribute__|format|__extension__| - asm|__asm__)$/x) - { - # cpp #define statements have non-optional spaces, ie - # if there is a space between the name and the open - # parenthesis it is simply not a parameter group. - } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { - - # cpp #elif statement condition may start with a ( - } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { - - # If this whole things ends with a type its most - # likely a typedef for a function. - } elsif ($ctx =~ /$Type$/) { - - } else { - if (WARN("SPACING", - "space prohibited between function name and open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\b$name\s+\(/$name\(/; - } - } - } - -# Check operator spacing. - if (!($line=~/\#\s*include/)) { - my $fixed_line = ""; - my $line_fixed = 0; - - my $ops = qr{ - <<=|>>=|<=|>=|==|!=| - \+=|-=|\*=|\/=|%=|\^=|\|=|&=| - =>|->|<<|>>|<|>|=|!|~| - &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| - \?:|\?|: - }x; - my @elements = split(/($ops|;)/, $opline); - -## print("element count: <" . $#elements . ">\n"); -## foreach my $el (@elements) { -## print("el: <$el>\n"); -## } - - my @fix_elements = (); - my $off = 0; - - foreach my $el (@elements) { - push(@fix_elements, substr($rawline, $off, length($el))); - $off += length($el); - } - - $off = 0; - - my $blank = copy_spacing($opline); - my $last_after = -1; - - for (my $n = 0; $n < $#elements; $n += 2) { - - my $good = $fix_elements[$n] . $fix_elements[$n + 1]; - -## print("n: <$n> good: <$good>\n"); - - $off += length($elements[$n]); - - # Pick up the preceding and succeeding characters. - my $ca = substr($opline, 0, $off); - my $cc = ''; - if (length($opline) >= ($off + length($elements[$n + 1]))) { - $cc = substr($opline, $off + length($elements[$n + 1])); - } - my $cb = "$ca$;$cc"; - - my $a = ''; - $a = 'V' if ($elements[$n] ne ''); - $a = 'W' if ($elements[$n] =~ /\s$/); - $a = 'C' if ($elements[$n] =~ /$;$/); - $a = 'B' if ($elements[$n] =~ /(\[|\()$/); - $a = 'O' if ($elements[$n] eq ''); - $a = 'E' if ($ca =~ /^\s*$/); - - my $op = $elements[$n + 1]; - - my $c = ''; - if (defined $elements[$n + 2]) { - $c = 'V' if ($elements[$n + 2] ne ''); - $c = 'W' if ($elements[$n + 2] =~ /^\s/); - $c = 'C' if ($elements[$n + 2] =~ /^$;/); - $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); - $c = 'O' if ($elements[$n + 2] eq ''); - $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); - } else { - $c = 'E'; - } - - my $ctx = "${a}x${c}"; - - my $at = "(ctx:$ctx)"; - - my $ptr = substr($blank, 0, $off) . "^"; - my $hereptr = "$hereline$ptr\n"; - - # Pull out the value of this operator. - my $op_type = substr($curr_values, $off + 1, 1); - - # Get the full operator variant. - my $opv = $op . substr($curr_vars, $off, 1); - - # Ignore operators passed as parameters. - if ($op_type ne 'V' && - $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { - -# # Ignore comments -# } elsif ($op =~ /^$;+$/) { - - # ; should have either the end of line or a space or \ after it - } elsif ($op eq ';') { - if ($ctx !~ /.x[WEBC]/ && - $cc !~ /^\\/ && $cc !~ /^;/) { - if (ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; - $line_fixed = 1; - } - } - - # // is a comment - } elsif ($op eq '//') { - - # : when part of a bitfield - } elsif ($opv eq ':B') { - # skip the bitfield test for now - - # No spaces for: - # -> - } elsif ($op eq '->') { - if ($ctx =~ /Wx.|.xW/) { - if (ERROR("SPACING", - "spaces prohibited around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # , must not have a space before and must have a space on the right. - } elsif ($op eq ',') { - my $rtrim_before = 0; - my $space_after = 0; - if ($ctx =~ /Wx./) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $line_fixed = 1; - $rtrim_before = 1; - } - } - if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { - if (ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr)) { - $line_fixed = 1; - $last_after = $n; - $space_after = 1; - } - } - if ($rtrim_before || $space_after) { - if ($rtrim_before) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - } else { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); - } - if ($space_after) { - $good .= " "; - } - } - - # '*' as part of a type definition -- reported already. - } elsif ($opv eq '*_') { - #warn "'*' is part of type\n"; - - # unary operators should have a space before and - # none after. May be left adjacent to another - # unary operator, or a cast - } elsif ($op eq '!' || $op eq '~' || - $opv eq '*U' || $opv eq '-U' || - $opv eq '&U' || $opv eq '&&U') { - if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { - if (ERROR("SPACING", - "space required before that '$op' $at\n" . $hereptr)) { - if ($n != $last_after + 2) { - $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - } - if ($op eq '*' && $cc =~/\s*$Modifier\b/) { - # A unary '*' may be const - - } elsif ($ctx =~ /.xW/) { - if (ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # unary ++ and unary -- are allowed no space on one side. - } elsif ($op eq '++' or $op eq '--') { - if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { - if (ERROR("SPACING", - "space required one side of that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; - $line_fixed = 1; - } - } - if ($ctx =~ /Wx[BE]/ || - ($ctx =~ /Wx./ && $cc =~ /^;/)) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - if ($ctx =~ /ExW/) { - if (ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # << and >> may either have or not have spaces both sides - } elsif ($op eq '<<' or $op eq '>>' or - $op eq '&' or $op eq '^' or $op eq '|' or - $op eq '+' or $op eq '-' or - $op eq '*' or $op eq '/' or - $op eq '%') - { - if ($check) { - if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { - if (CHK("SPACING", - "spaces preferred around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - $fix_elements[$n + 2] =~ s/^\s+//; - $line_fixed = 1; - } - } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { - if (CHK("SPACING", - "space preferred before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { - if (ERROR("SPACING", - "need consistent spacing around '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # A colon needs no spaces before when it is - # terminating a case value or a label. - } elsif ($opv eq ':C' || $opv eq ':L') { - if ($ctx =~ /Wx./) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - - # All the others need spaces both sides. - } elsif ($ctx !~ /[EWC]x[CWE]/) { - my $ok = 0; - - # Ignore email addresses <foo@bar> - if (($op eq '<' && - $cc =~ /^\S+\@\S+>/) || - ($op eq '>' && - $ca =~ /<\S+\@\S+$/)) - { - $ok = 1; - } - - # for asm volatile statements - # ignore a colon with another - # colon immediately before or after - if (($op eq ':') && - ($ca =~ /:$/ || $cc =~ /^:/)) { - $ok = 1; - } - - # messages are ERROR, but ?: are CHK - if ($ok == 0) { - my $msg_type = \&ERROR; - $msg_type = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); - - if (&{$msg_type}("SPACING", - "spaces required around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - } - $off += length($elements[$n + 1]); - -## print("n: <$n> GOOD: <$good>\n"); - - $fixed_line = $fixed_line . $good; - } - - if (($#elements % 2) == 0) { - $fixed_line = $fixed_line . $fix_elements[$#elements]; - } - - if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { - $fixed[$fixlinenr] = $fixed_line; - } - - - } - -# check for whitespace before a non-naked semicolon - if ($line =~ /^\+.*\S\s+;\s*$/) { - if (WARN("SPACING", - "space prohibited before semicolon\n" . $herecurr) && - $fix) { - 1 while $fixed[$fixlinenr] =~ - s/^(\+.*\S)\s+;/$1;/; - } - } - -# check for multiple assignments - if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { - CHK("MULTIPLE_ASSIGNMENTS", - "multiple assignments should be avoided\n" . $herecurr); - } - -## # check for multiple declarations, allowing for a function declaration -## # continuation. -## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && -## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { -## -## # Remove any bracketed sections to ensure we do not -## # falsly report the parameters of functions. -## my $ln = $line; -## while ($ln =~ s/\([^\(\)]*\)//g) { -## } -## if ($ln =~ /,/) { -## WARN("MULTIPLE_DECLARATION", -## "declaring multiple variables together should be avoided\n" . $herecurr); -## } -## } - -#need space before brace following if, while, etc - if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || - $line =~ /do\{/) { - if (ERROR("SPACING", - "space required before the open brace '{'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\))){/$1 {/; - } - } - -## # check for blank lines before declarations -## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && -## $prevrawline =~ /^.\s*$/) { -## WARN("SPACING", -## "No blank lines before declarations\n" . $hereprev); -## } -## - -# closing brace should have a space following it when it has anything -# on the line - if ($line =~ /}(?!(?:,|;|\)))\S/) { - if (ERROR("SPACING", - "space required after that close brace '}'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/}((?!(?:,|;|\)))\S)/} $1/; - } - } - -# check spacing on square brackets - if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { - if (ERROR("SPACING", - "space prohibited after that open square bracket '['\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\[\s+/\[/; - } - } - if ($line =~ /\s\]/) { - if (ERROR("SPACING", - "space prohibited before that close square bracket ']'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\s+\]/\]/; - } - } - -# check spacing on parentheses - if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && - $line !~ /for\s*\(\s+;/) { - if (ERROR("SPACING", - "space prohibited after that open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\(\s+/\(/; - } - } - if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && - $line !~ /for\s*\(.*;\s+\)/ && - $line !~ /:\s+\)/) { - if (ERROR("SPACING", - "space prohibited before that close parenthesis ')'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\s+\)/\)/; - } - } - -# check unnecessary parentheses around addressof/dereference single $Lvals -# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar - - while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { - my $var = $1; - if (CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around $var\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; - } - } - -# check for unnecessary parentheses around function pointer uses -# ie: (foo->bar)(); should be foo->bar(); -# but not "if (foo->bar) (" to avoid some false positives - if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { - my $var = $2; - if (CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around function pointer $var\n" . $herecurr) && - $fix) { - my $var2 = deparenthesize($var); - $var2 =~ s/\s//g; - $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; - } - } - -#goto labels aren't indented, allow a single space however - if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and - !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { - if (WARN("INDENTED_LABEL", - "labels should not be indented\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.)\s+/$1/; - } - } - -# return is not a function - if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { - my $spacing = $1; - if ($^V && $^V ge 5.10.0 && - $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { - my $value = $1; - $value = deparenthesize($value); - if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { - ERROR("RETURN_PARENTHESES", - "return is not a function, parentheses are not required\n" . $herecurr); - } - } elsif ($spacing !~ /\s+/) { - ERROR("SPACING", - "space required before the open parenthesis '('\n" . $herecurr); - } - } - -# unnecessary return in a void function -# at end-of-function, with the previous line a single leading tab, then return; -# and the line before that not a goto label target like "out:" - if ($sline =~ /^[ \+]}\s*$/ && - $prevline =~ /^\+\treturn\s*;\s*$/ && - $linenr >= 3 && - $lines[$linenr - 3] =~ /^[ +]/ && - $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { - WARN("RETURN_VOID", - "void function return statements are not generally useful\n" . $hereprev); - } - -# if statements using unnecessary parentheses - ie: if ((foo == bar)) - if ($^V && $^V ge 5.10.0 && - $line =~ /\bif\s*((?:\(\s*){2,})/) { - my $openparens = $1; - my $count = $openparens =~ tr@\(@\(@; - my $msg = ""; - if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { - my $comp = $4; #Not $1 because of $LvalOrFunc - $msg = " - maybe == should be = ?" if ($comp eq "=="); - WARN("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses$msg\n" . $herecurr); - } - } - -# comparisons with a constant or upper case identifier on the left -# avoid cases like "foo + BAR < baz" -# only fix matches surrounded by parentheses to avoid incorrect -# conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" - if ($^V && $^V ge 5.10.0 && - $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { - my $lead = $1; - my $const = $2; - my $comp = $3; - my $to = $4; - my $newcomp = $comp; - if ($lead !~ /$Operators\s*$/ && - $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && - WARN("CONSTANT_COMPARISON", - "Comparisons should place the constant on the right side of the test\n" . $herecurr) && - $fix) { - if ($comp eq "<") { - $newcomp = ">"; - } elsif ($comp eq "<=") { - $newcomp = ">="; - } elsif ($comp eq ">") { - $newcomp = "<"; - } elsif ($comp eq ">=") { - $newcomp = "<="; - } - $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; - } - } - -# Return of what appears to be an errno should normally be negative - if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { - my $name = $1; - if ($name ne 'EOF' && $name ne 'ERROR') { - WARN("USE_NEGATIVE_ERRNO", - "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); - } - } - -# Need a space before open parenthesis after if, while etc - if ($line =~ /\b(if|while|for|switch)\(/) { - if (ERROR("SPACING", - "space required before the open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\b(if|while|for|switch)\(/$1 \(/; - } - } - -# Check for illegal assignment in if conditional -- and check for trailing -# statements after the conditional. - if ($line =~ /do\s*(?!{)/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0) - if (!defined $stat); - my ($stat_next) = ctx_statement_block($line_nr_next, - $remain_next, $off_next); - $stat_next =~ s/\n./\n /g; - ##print "stat<$stat> stat_next<$stat_next>\n"; - - if ($stat_next =~ /^\s*while\b/) { - # If the statement carries leading newlines, - # then count those as offsets. - my ($whitespace) = - ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); - my $offset = - statement_rawlines($whitespace) - 1; - - $suppress_whiletrailers{$line_nr_next + - $offset} = 1; - } - } - if (!defined $suppress_whiletrailers{$linenr} && - defined($stat) && defined($cond) && - $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { - my ($s, $c) = ($stat, $cond); - - if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { - ERROR("ASSIGN_IN_IF", - "do not use assignment in if condition\n" . $herecurr); - } - - # Find out what is on the end of the line after the - # conditional. - substr($s, 0, length($c), ''); - $s =~ s/\n.*//g; - $s =~ s/$;//g; # Remove any comments - if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && - $c !~ /}\s*while\s*/) - { - # Find out how long the conditional actually is. - my @newlines = ($c =~ /\n/gs); - my $cond_lines = 1 + $#newlines; - my $stat_real = ''; - - $stat_real = raw_line($linenr, $cond_lines) - . "\n" if ($cond_lines); - if (defined($stat_real) && $cond_lines > 1) { - $stat_real = "[...]\n$stat_real"; - } - - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr . $stat_real); - } - } - -# Check for bitwise tests written as boolean - if ($line =~ / - (?: - (?:\[|\(|\&\&|\|\|) - \s*0[xX][0-9]+\s* - (?:\&\&|\|\|) - | - (?:\&\&|\|\|) - \s*0[xX][0-9]+\s* - (?:\&\&|\|\||\)|\]) - )/x) - { - WARN("HEXADECIMAL_BOOLEAN_TEST", - "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); - } - -# if and else should not have general statements after it - if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { - my $s = $1; - $s =~ s/$;//g; # Remove any comments - if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr); - } - } -# if should not continue a brace - if ($line =~ /}\s*if\b/) { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line (or did you mean 'else if'?)\n" . - $herecurr); - } -# case and default should not have general statements after them - if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && - $line !~ /\G(?: - (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| - \s*return\s+ - )/xg) - { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr); - } - - # Check for }<nl>else {, these must be at the same - # indent level to be relevant to each other. - if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && - $previndent == $indent) { - if (ERROR("ELSE_AFTER_BRACE", - "else should follow close brace '}'\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/}\s*$//; - if ($fixedline !~ /^\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - $fixedline = $rawline; - $fixedline =~ s/^(.\s*)else/$1} else/; - fix_insert_line($fixlinenr, $fixedline); - } - } - - if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && - $previndent == $indent) { - my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); - - # Find out what is on the end of the line after the - # conditional. - substr($s, 0, length($c), ''); - $s =~ s/\n.*//g; - - if ($s =~ /^\s*;/) { - if (ERROR("WHILE_AFTER_BRACE", - "while should follow close brace '}'\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - my $trailing = $rawline; - $trailing =~ s/^\+//; - $trailing = trim($trailing); - $fixedline =~ s/}\s*$/} $trailing/; - fix_insert_line($fixlinenr, $fixedline); - } - } - } - -#Specific variable tests - while ($line =~ m{($Constant|$Lval)}g) { - my $var = $1; - -#gcc binary extension - if ($var =~ /^$Binary$/) { - if (WARN("GCC_BINARY_CONSTANT", - "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && - $fix) { - my $hexval = sprintf("0x%x", oct($var)); - $fixed[$fixlinenr] =~ - s/\b$var\b/$hexval/; - } - } - -#CamelCase - if ($var !~ /^$Constant$/ && - $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && -#Ignore Page<foo> variants - $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && -#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) - $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && -#Ignore some three character SI units explicitly, like MiB and KHz - $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { - while ($var =~ m{($Ident)}g) { - my $word = $1; - next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); - if ($check) { - seed_camelcase_includes(); - if (!$file && !$camelcase_file_seeded) { - seed_camelcase_file($realfile); - $camelcase_file_seeded = 1; - } - } - if (!defined $camelcase{$word}) { - $camelcase{$word} = 1; - CHK("CAMELCASE", - "Avoid CamelCase: <$word>\n" . $herecurr); - } - } - } - } - -#no spaces allowed after \ in define - if ($line =~ /\#\s*define.*\\\s+$/) { - if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", - "Whitespace after \\ makes next lines useless\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+$//; - } - } - -# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes -# itself <asm/foo.h> (uses RAW line) - if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { - my $file = "$1.h"; - my $checkfile = "include/linux/$file"; - if (-f "$root/$checkfile" && - $realfile ne $checkfile && - $1 !~ /$allowed_asm_includes/) - { - my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; - if ($asminclude > 0) { - if ($realfile =~ m{^arch/}) { - CHK("ARCH_INCLUDE_LINUX", - "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } else { - WARN("INCLUDE_LINUX", - "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } - } - } - } - -# multi-statement macros should be enclosed in a do while loop, grab the -# first statement and ensure its the whole macro if its not enclosed -# in a known good container - if ($realfile !~ m@/vmlinux.lds.h$@ && - $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { - my $ln = $linenr; - my $cnt = $realcnt; - my ($off, $dstat, $dcond, $rest); - my $ctx = ''; - my $has_flow_statement = 0; - my $has_arg_concat = 0; - ($dstat, $dcond, $ln, $cnt, $off) = - ctx_statement_block($linenr, $realcnt, 0); - $ctx = $dstat; - #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; - #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; - - $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); - $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); - - $dstat =~ s/^.\s*\#\s*define\s+$Ident(?:\([^\)]*\))?\s*//; - $dstat =~ s/$;//g; - $dstat =~ s/\\\n.//g; - $dstat =~ s/^\s*//s; - $dstat =~ s/\s*$//s; - - # Flatten any parentheses and braces - while ($dstat =~ s/\([^\(\)]*\)/1/ || - $dstat =~ s/\{[^\{\}]*\}/1/ || - $dstat =~ s/.\[[^\[\]]*\]/1/) - { - } - - # Flatten any obvious string concatentation. - while ($dstat =~ s/($String)\s*$Ident/$1/ || - $dstat =~ s/$Ident\s*($String)/$1/) - { - } - - # Make asm volatile uses seem like a generic function - $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; - - my $exceptions = qr{ - $Declare| - module_param_named| - MODULE_PARM_DESC| - DECLARE_PER_CPU| - DEFINE_PER_CPU| - __typeof__\(| - union| - struct| - \.$Ident\s*=\s*| - ^\"|\"$| - ^\[ - }x; - #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; - if ($dstat ne '' && - $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), - $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); - $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz - $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants - $dstat !~ /$exceptions/ && - $dstat !~ /^\.$Ident\s*=/ && # .foo = - $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo - $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) - $dstat !~ /^for\s*$Constant$/ && # for (...) - $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() - $dstat !~ /^do\s*{/ && # do {... - $dstat !~ /^\(\{/ && # ({... - $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) - { - $ctx =~ s/\n*$//; - my $herectx = $here . "\n"; - my $cnt = statement_rawlines($ctx); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - if ($dstat =~ /;/) { - ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", - "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); - } else { - ERROR("COMPLEX_MACRO", - "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); - } - } - -# check for macros with flow control, but without ## concatenation -# ## concatenation is commonly a macro that defines a function so ignore those - if ($has_flow_statement && !$has_arg_concat) { - my $herectx = $here . "\n"; - my $cnt = statement_rawlines($ctx); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - WARN("MACRO_WITH_FLOW_CONTROL", - "Macros with flow control statements should be avoided\n" . "$herectx"); - } - -# check for line continuations outside of #defines, preprocessor #, and asm - - } else { - if ($prevline !~ /^..*\\$/ && - $line !~ /^\+\s*\#.*\\$/ && # preprocessor - $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm - $line =~ /^\+.*\\$/) { - WARN("LINE_CONTINUATIONS", - "Avoid unnecessary line continuations\n" . $herecurr); - } - } - -# do {} while (0) macro tests: -# single-statement macros do not need to be enclosed in do while (0) loop, -# macro should not end with a semicolon - if ($^V && $^V ge 5.10.0 && - $realfile !~ m@/vmlinux.lds.h$@ && - $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { - my $ln = $linenr; - my $cnt = $realcnt; - my ($off, $dstat, $dcond, $rest); - my $ctx = ''; - ($dstat, $dcond, $ln, $cnt, $off) = - ctx_statement_block($linenr, $realcnt, 0); - $ctx = $dstat; - - $dstat =~ s/\\\n.//g; - $dstat =~ s/$;/ /g; - - if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { - my $stmts = $2; - my $semis = $3; - - $ctx =~ s/\n*$//; - my $cnt = statement_rawlines($ctx); - my $herectx = $here . "\n"; - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - if (($stmts =~ tr/;/;/) == 1 && - $stmts !~ /^\s*(if|while|for|switch)\b/) { - WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", - "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); - } - if (defined $semis && $semis ne "") { - WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", - "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); - } - } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { - $ctx =~ s/\n*$//; - my $cnt = statement_rawlines($ctx); - my $herectx = $here . "\n"; - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - WARN("TRAILING_SEMICOLON", - "macros should not use a trailing semicolon\n" . "$herectx"); - } - } - -# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... -# all assignments may have only one of the following with an assignment: -# . -# ALIGN(...) -# VMLINUX_SYMBOL(...) - if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { - WARN("MISSING_VMLINUX_SYMBOL", - "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); - } - -# check for redundant bracing round if etc - if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, 1); - #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; - #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; - if ($#chunks > 0 && $level == 0) { - my @allowed = (); - my $allow = 0; - my $seen = 0; - my $herectx = $here . "\n"; - my $ln = $linenr - 1; - for my $chunk (@chunks) { - my ($cond, $block) = @{$chunk}; - - # If the condition carries leading newlines, then count those as offsets. - my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); - my $offset = statement_rawlines($whitespace) - 1; - - $allowed[$allow] = 0; - #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; - - # We have looked at and allowed this specific line. - $suppress_ifbraces{$ln + $offset} = 1; - - $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; - $ln += statement_rawlines($block) - 1; - - substr($block, 0, length($cond), ''); - - $seen++ if ($block =~ /^\s*{/); - - #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; - if (statement_lines($cond) > 1) { - #print "APW: ALLOWED: cond<$cond>\n"; - $allowed[$allow] = 1; - } - if ($block =~/\b(?:if|for|while)\b/) { - #print "APW: ALLOWED: block<$block>\n"; - $allowed[$allow] = 1; - } - if (statement_block_size($block) > 1) { - #print "APW: ALLOWED: lines block<$block>\n"; - $allowed[$allow] = 1; - } - $allow++; - } - if ($seen) { - my $sum_allowed = 0; - foreach (@allowed) { - $sum_allowed += $_; - } - if ($sum_allowed == 0) { - WARN("BRACES", - "braces {} are not necessary for any arm of this statement\n" . $herectx); - } elsif ($sum_allowed != $allow && - $seen != $allow) { - CHK("BRACES", - "braces {} should be used on all arms of this statement\n" . $herectx); - } - } - } - } - if (!defined $suppress_ifbraces{$linenr - 1} && - $line =~ /\b(if|while|for|else)\b/) { - my $allowed = 0; - - # Check the pre-context. - if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { - #print "APW: ALLOWED: pre<$1>\n"; - $allowed = 1; - } - - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, $-[0]); - - # Check the condition. - my ($cond, $block) = @{$chunks[0]}; - #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; - if (defined $cond) { - substr($block, 0, length($cond), ''); - } - if (statement_lines($cond) > 1) { - #print "APW: ALLOWED: cond<$cond>\n"; - $allowed = 1; - } - if ($block =~/\b(?:if|for|while)\b/) { - #print "APW: ALLOWED: block<$block>\n"; - $allowed = 1; - } - if (statement_block_size($block) > 1) { - #print "APW: ALLOWED: lines block<$block>\n"; - $allowed = 1; - } - # Check the post-context. - if (defined $chunks[1]) { - my ($cond, $block) = @{$chunks[1]}; - if (defined $cond) { - substr($block, 0, length($cond), ''); - } - if ($block =~ /^\s*\{/) { - #print "APW: ALLOWED: chunk-1 block<$block>\n"; - $allowed = 1; - } - } - if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { - my $herectx = $here . "\n"; - my $cnt = statement_rawlines($block); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - WARN("BRACES", - "braces {} are not necessary for single statement blocks\n" . $herectx); - } - } - -# check for unnecessary blank lines around braces - if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { - if (CHK("BRACES", - "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && - $fix && $prevrawline =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - } - } - if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { - if (CHK("BRACES", - "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - } - -# no volatiles please - my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; - if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { - WARN("VOLATILE", - "Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); - } - -# Check for user-visible strings broken across lines, which breaks the ability -# to grep for the string. Make exceptions when the previous string ends in a -# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' -# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value - if ($line =~ /^\+\s*$String/ && - $prevline =~ /"\s*$/ && - $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { - if (WARN("SPLIT_STRING", - "quoted string split across lines\n" . $hereprev) && - $fix && - $prevrawline =~ /^\+.*"\s*$/ && - $last_coalesced_string_linenr != $linenr - 1) { - my $extracted_string = get_quoted_string($line, $rawline); - my $comma_close = ""; - if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { - $comma_close = $1; - } - - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/"\s*$//; - $fixedline .= substr($extracted_string, 1) . trim($comma_close); - fix_insert_line($fixlinenr - 1, $fixedline); - $fixedline = $rawline; - $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; - if ($fixedline !~ /\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - $last_coalesced_string_linenr = $linenr; - } - } - -# check for missing a space in a string concatenation - if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { - WARN('MISSING_SPACE', - "break quoted strings at a space character\n" . $hereprev); - } - -# check for spaces before a quoted newline - if ($rawline =~ /^.*\".*\s\\n/) { - if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", - "unnecessary whitespace before a quoted newline\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; - } - - } - -# concatenated string without spaces between elements - if ($line =~ /$String[A-Z_]/ || $line =~ /[A-Za-z0-9_]$String/) { - CHK("CONCATENATED_STRING", - "Concatenated strings should use spaces between elements\n" . $herecurr); - } - -# uncoalesced string fragments - if ($line =~ /$String\s*"/) { - WARN("STRING_FRAGMENTS", - "Consecutive strings are generally better as a single string\n" . $herecurr); - } - -# check for %L{u,d,i} and 0x%[udi] in strings - my $string; - while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { - $string = substr($rawline, $-[1], $+[1] - $-[1]); - $string =~ s/%%/__/g; - if ($string =~ /(?<!%)%[\*\d\.\$]*L[udi]/) { - WARN("PRINTF_L", - "\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr); - last; - } - if ($string =~ /0x%[\*\d\.\$\Llzth]*[udi]/) { - ERROR("PRINTF_0xDECIMAL", - "Prefixing 0x with decimal output is defective\n" . $herecurr); - } - } - -# check for line continuations in quoted strings with odd counts of " - if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { - WARN("LINE_CONTINUATIONS", - "Avoid line continuations in quoted strings\n" . $herecurr); - } - -# warn about #if 0 - if ($line =~ /^.\s*\#\s*if\s+0\b/) { - CHK("REDUNDANT_CODE", - "if this code is redundant consider removing it\n" . - $herecurr); - } - -# check for needless "if (<foo>) fn(<foo>)" uses - if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { - my $tested = quotemeta($1); - my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; - if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { - my $func = $1; - if (WARN('NEEDLESS_IF', - "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && - $fix) { - my $do_fix = 1; - my $leading_tabs = ""; - my $new_leading_tabs = ""; - if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { - $leading_tabs = $1; - } else { - $do_fix = 0; - } - if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { - $new_leading_tabs = $1; - if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { - $do_fix = 0; - } - } else { - $do_fix = 0; - } - if ($do_fix) { - fix_delete_line($fixlinenr - 1, $prevrawline); - $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; - } - } - } - } - -# check for unnecessary "Out of Memory" messages - if ($line =~ /^\+.*\b$logFunctions\s*\(/ && - $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && - (defined $1 || defined $3) && - $linenr > 3) { - my $testval = $2; - my $testline = $lines[$linenr - 3]; - - my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); -# print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); - - if ($c =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|(?:dev_)?alloc_skb)/) { - WARN("OOM_MESSAGE", - "Possible unnecessary 'out of memory' message\n" . $hereprev); - } - } - -# check for logging functions with KERN_<LEVEL> - if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && - $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { - my $level = $1; - if (WARN("UNNECESSARY_KERN_LEVEL", - "Possible unnecessary $level\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s*$level\s*//; - } - } - -# check for mask then right shift without a parentheses - if ($^V && $^V ge 5.10.0 && - $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && - $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so - WARN("MASK_THEN_SHIFT", - "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); - } - -# check for pointer comparisons to NULL - if ($^V && $^V ge 5.10.0) { - while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { - my $val = $1; - my $equal = "!"; - $equal = "" if ($4 eq "!="); - if (CHK("COMPARISON_TO_NULL", - "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; - } - } - } - -# check for bad placement of section $InitAttribute (e.g.: __initdata) - if ($line =~ /(\b$InitAttribute\b)/) { - my $attr = $1; - if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { - my $ptr = $1; - my $var = $2; - if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && - ERROR("MISPLACED_INIT", - "$attr should be placed after $var\n" . $herecurr)) || - ($ptr !~ /\b(union|struct)\s+$attr\b/ && - WARN("MISPLACED_INIT", - "$attr should be placed after $var\n" . $herecurr))) && - $fix) { - $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; - } - } - } - -# check for $InitAttributeData (ie: __initdata) with const - if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { - my $attr = $1; - $attr =~ /($InitAttributePrefix)(.*)/; - my $attr_prefix = $1; - my $attr_type = $2; - if (ERROR("INIT_ATTRIBUTE", - "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/$InitAttributeData/${attr_prefix}initconst/; - } - } - -# check for $InitAttributeConst (ie: __initconst) without const - if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { - my $attr = $1; - if (ERROR("INIT_ATTRIBUTE", - "Use of $attr requires a separate use of const\n" . $herecurr) && - $fix) { - my $lead = $fixed[$fixlinenr] =~ - /(^\+\s*(?:static\s+))/; - $lead = rtrim($1); - $lead = "$lead " if ($lead !~ /^\+$/); - $lead = "${lead}const "; - $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; - } - } - -# check for __read_mostly with const non-pointer (should just be const) - if ($line =~ /\b__read_mostly\b/ && - $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { - if (ERROR("CONST_READ_MOSTLY", - "Invalid use of __read_mostly with const type\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; - } - } - -# don't use __constant_<foo> functions outside of include/uapi/ - if ($realfile !~ m@^include/uapi/@ && - $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { - my $constant_func = $1; - my $func = $constant_func; - $func =~ s/^__constant_//; - if (WARN("CONSTANT_CONVERSION", - "$constant_func should be $func\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; - } - } - -# prefer usleep_range over udelay - if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { - my $delay = $1; - # ignore udelay's < 10, however - if (! ($delay < 10) ) { - CHK("USLEEP_RANGE", - "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr); - } - if ($delay > 2000) { - WARN("LONG_UDELAY", - "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); - } - } - -# warn about unexpectedly long msleep's - if ($line =~ /\bmsleep\s*\((\d+)\);/) { - if ($1 < 20) { - WARN("MSLEEP", - "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr); - } - } - -# check for comparisons of jiffies - if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { - WARN("JIFFIES_COMPARISON", - "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); - } - -# check for comparisons of get_jiffies_64() - if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { - WARN("JIFFIES_COMPARISON", - "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); - } - -# warn about #ifdefs in C files -# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { -# print "#ifdef in C files should be avoided\n"; -# print "$herecurr"; -# $clean = 0; -# } - -# warn about spacing in #ifdefs - if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { - if (ERROR("SPACING", - "exactly one space required after that #$1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; - } - - } - -# check for spinlock_t definitions without a comment. - if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || - $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { - my $which = $1; - if (!ctx_has_comment($first_line, $linenr)) { - CHK("UNCOMMENTED_DEFINITION", - "$1 definition without comment\n" . $herecurr); - } - } -# check for memory barriers without a comment. - - my $barriers = qr{ - mb| - rmb| - wmb| - read_barrier_depends - }x; - my $barrier_stems = qr{ - mb__before_atomic| - mb__after_atomic| - store_release| - load_acquire| - store_mb| - (?:$barriers) - }x; - my $all_barriers = qr{ - (?:$barriers)| - smp_(?:$barrier_stems)| - virt_(?:$barrier_stems) - }x; - - if ($line =~ /\b(?:$all_barriers)\s*\(/) { - if (!ctx_has_comment($first_line, $linenr)) { - WARN("MEMORY_BARRIER", - "memory barrier without comment\n" . $herecurr); - } - } - - my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; - - if ($realfile !~ m@^include/asm-generic/@ && - $realfile !~ m@/barrier\.h$@ && - $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && - $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { - WARN("MEMORY_BARRIER", - "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); - } - -# check for waitqueue_active without a comment. - if ($line =~ /\bwaitqueue_active\s*\(/) { - if (!ctx_has_comment($first_line, $linenr)) { - WARN("WAITQUEUE_ACTIVE", - "waitqueue_active without comment\n" . $herecurr); - } - } - -# Check for expedited grace periods that interrupt non-idle non-nohz -# online CPUs. These expedited can therefore degrade real-time response -# if used carelessly, and should be avoided where not absolutely -# needed. It is always OK to use synchronize_rcu_expedited() and -# synchronize_sched_expedited() at boot time (before real-time applications -# start) and in error situations where real-time response is compromised in -# any case. Note that synchronize_srcu_expedited() does -not- interrupt -# other CPUs, so don't warn on uses of synchronize_srcu_expedited(). -# Of course, nothing comes for free, and srcu_read_lock() and -# srcu_read_unlock() do contain full memory barriers in payment for -# synchronize_srcu_expedited() non-interruption properties. - if ($line =~ /\b(synchronize_rcu_expedited|synchronize_sched_expedited)\(/) { - WARN("EXPEDITED_RCU_GRACE_PERIOD", - "expedited RCU grace periods should be avoided where they can degrade real-time response\n" . $herecurr); - - } - -# check of hardware specific defines - if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { - CHK("ARCH_DEFINES", - "architecture specific defines should be avoided\n" . $herecurr); - } - -# Check that the storage class is at the beginning of a declaration - if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { - WARN("STORAGE_CLASS", - "storage class should be at the beginning of the declaration\n" . $herecurr) - } - -# check the location of the inline attribute, that it is between -# storage class and type. - if ($line =~ /\b$Type\s+$Inline\b/ || - $line =~ /\b$Inline\s+$Storage\b/) { - ERROR("INLINE_LOCATION", - "inline keyword should sit between storage class and type\n" . $herecurr); - } - -# Check for __inline__ and __inline, prefer inline - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b(__inline__|__inline)\b/) { - if (WARN("INLINE", - "plain inline is preferred over $1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; - - } - } - -# Check for __attribute__ packed, prefer __packed - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { - WARN("PREFER_PACKED", - "__packed is preferred over __attribute__((packed))\n" . $herecurr); - } - -# Check for __attribute__ aligned, prefer __aligned - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { - WARN("PREFER_ALIGNED", - "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); - } - -# Check for __attribute__ format(printf, prefer __printf - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { - if (WARN("PREFER_PRINTF", - "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; - - } - } - -# Check for __attribute__ format(scanf, prefer __scanf - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { - if (WARN("PREFER_SCANF", - "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; - } - } - -# Check for __attribute__ weak, or __weak declarations (may have link issues) - if ($^V && $^V ge 5.10.0 && - $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && - ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || - $line =~ /\b__weak\b/)) { - ERROR("WEAK_DECLARATION", - "Using weak declarations can have unintended link defects\n" . $herecurr); - } - -# check for c99 types like uint8_t used outside of uapi/ - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { - my $type = $1; - if ($type =~ /\b($typeC99Typedefs)\b/) { - $type = $1; - my $kernel_type = 'u'; - $kernel_type = 's' if ($type =~ /^_*[si]/); - $type =~ /(\d+)/; - $kernel_type .= $1; - if (CHK("PREFER_KERNEL_TYPES", - "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/; - } - } - } - -# check for cast of C90 native int or longer types constants - if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { - my $cast = $1; - my $const = $2; - if (WARN("TYPECAST_INT_CONSTANT", - "Unnecessary typecast of c90 int constant\n" . $herecurr) && - $fix) { - my $suffix = ""; - my $newconst = $const; - $newconst =~ s/${Int_type}$//; - $suffix .= 'U' if ($cast =~ /\bunsigned\b/); - if ($cast =~ /\blong\s+long\b/) { - $suffix .= 'LL'; - } elsif ($cast =~ /\blong\b/) { - $suffix .= 'L'; - } - $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; - } - } - -# check for sizeof(&) - if ($line =~ /\bsizeof\s*\(\s*\&/) { - WARN("SIZEOF_ADDRESS", - "sizeof(& should be avoided\n" . $herecurr); - } - -# check for sizeof without parenthesis - if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { - if (WARN("SIZEOF_PARENTHESIS", - "sizeof $1 should be sizeof($1)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; - } - } - -# check for struct spinlock declarations - if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { - WARN("USE_SPINLOCK_T", - "struct spinlock should be spinlock_t\n" . $herecurr); - } - -# check for seq_printf uses that could be seq_puts - if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { - my $fmt = get_quoted_string($line, $rawline); - $fmt =~ s/%%//g; - if ($fmt !~ /%/) { - if (WARN("PREFER_SEQ_PUTS", - "Prefer seq_puts to seq_printf\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; - } - } - } - -# Check for misused memsets - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { - - my $ms_addr = $2; - my $ms_val = $7; - my $ms_size = $12; - - if ($ms_size =~ /^(0x|)0$/i) { - ERROR("MEMSET", - "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); - } elsif ($ms_size =~ /^(0x|)1$/i) { - WARN("MEMSET", - "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); - } - } - -# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { - if (WARN("PREFER_ETHER_ADDR_COPY", - "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && - $fix) { - $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; - } - } - -# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { - WARN("PREFER_ETHER_ADDR_EQUAL", - "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") - } - -# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr -# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { - - my $ms_val = $7; - - if ($ms_val =~ /^(?:0x|)0+$/i) { - if (WARN("PREFER_ETH_ZERO_ADDR", - "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && - $fix) { - $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; - } - } elsif ($ms_val =~ /^(?:0xff|255)$/i) { - if (WARN("PREFER_ETH_BROADCAST_ADDR", - "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && - $fix) { - $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; - } - } - } - -# typecasts on min/max could be min_t/max_t - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { - if (defined $2 || defined $7) { - my $call = $1; - my $cast1 = deparenthesize($2); - my $arg1 = $3; - my $cast2 = deparenthesize($7); - my $arg2 = $8; - my $cast; - - if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { - $cast = "$cast1 or $cast2"; - } elsif ($cast1 ne "") { - $cast = $cast1; - } else { - $cast = $cast2; - } - WARN("MINMAX", - "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); - } - } - -# check usleep_range arguments - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { - my $min = $1; - my $max = $7; - if ($min eq $max) { - WARN("USLEEP_RANGE", - "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); - } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && - $min > $max) { - WARN("USLEEP_RANGE", - "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); - } - } - -# check for naked sscanf - if ($^V && $^V ge 5.10.0 && - defined $stat && - $line =~ /\bsscanf\b/ && - ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && - $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && - $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } - WARN("NAKED_SSCANF", - "unchecked sscanf return value\n" . "$here\n$stat_real\n"); - } - -# check for simple sscanf that should be kstrto<foo> - if ($^V && $^V ge 5.10.0 && - defined $stat && - $line =~ /\bsscanf\b/) { - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } - if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { - my $format = $6; - my $count = $format =~ tr@%@%@; - if ($count == 1 && - $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { - WARN("SSCANF_TO_KSTRTO", - "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); - } - } - } - -# check for new externs in .h files. - if ($realfile =~ /\.h$/ && - $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { - if (CHK("AVOID_EXTERNS", - "extern prototypes should be avoided in .h files\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; - } - } - -# check for new externs in .c files. - if ($realfile =~ /\.c$/ && defined $stat && - $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) - { - my $function_name = $1; - my $paren_space = $2; - - my $s = $stat; - if (defined $cond) { - substr($s, 0, length($cond), ''); - } - if ($s =~ /^\s*;/ && - $function_name ne 'uninitialized_var') - { - WARN("AVOID_EXTERNS", - "externs should be avoided in .c files\n" . $herecurr); - } - - if ($paren_space =~ /\n/) { - WARN("FUNCTION_ARGUMENTS", - "arguments for function declarations should follow identifier\n" . $herecurr); - } - - } elsif ($realfile =~ /\.c$/ && defined $stat && - $stat =~ /^.\s*extern\s+/) - { - WARN("AVOID_EXTERNS", - "externs should be avoided in .c files\n" . $herecurr); - } - -# checks for new __setup's - if ($rawline =~ /\b__setup\("([^"]*)"/) { - my $name = $1; - - if (!grep(/$name/, @setup_docs)) { - CHK("UNDOCUMENTED_SETUP", - "__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); - } - } - -# check for pointless casting of kmalloc return - if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { - WARN("UNNECESSARY_CASTS", - "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); - } - -# alloc style -# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { - CHK("ALLOC_SIZEOF_STRUCT", - "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); - } - -# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { - my $oldfunc = $3; - my $a1 = $4; - my $a2 = $10; - my $newfunc = "kmalloc_array"; - $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); - my $r1 = $a1; - my $r2 = $a2; - if ($a1 =~ /^sizeof\s*\S/) { - $r1 = $a2; - $r2 = $a1; - } - if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && - !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { - if (WARN("ALLOC_WITH_MULTIPLY", - "Prefer $newfunc over $oldfunc with multiply\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; - - } - } - } - -# check for krealloc arg reuse - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { - WARN("KREALLOC_ARG_REUSE", - "Reusing the krealloc arg is almost always a bug\n" . $herecurr); - } - -# check for alloc argument mismatch - if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { - WARN("ALLOC_ARRAY_ARGS", - "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); - } - -# check for multiple semicolons - if ($line =~ /;\s*;\s*$/) { - if (WARN("ONE_SEMICOLON", - "Statements terminations use 1 semicolon\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; - } - } - -# check for #defines like: 1 << <digit> that could be BIT(digit) - if ($line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { - my $ull = ""; - $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); - if (CHK("BIT_MACRO", - "Prefer using the BIT$ull macro\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; - } - } - -# check for case / default statements not preceded by break/fallthrough/switch - if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { - my $has_break = 0; - my $has_statement = 0; - my $count = 0; - my $prevline = $linenr; - while ($prevline > 1 && ($file || $count < 3) && !$has_break) { - $prevline--; - my $rline = $rawlines[$prevline - 1]; - my $fline = $lines[$prevline - 1]; - last if ($fline =~ /^\@\@/); - next if ($fline =~ /^\-/); - next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); - $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); - next if ($fline =~ /^.[\s$;]*$/); - $has_statement = 1; - $count++; - $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/); - } - if (!$has_break && $has_statement) { - WARN("MISSING_BREAK", - "Possible switch case/default not preceeded by break or fallthrough comment\n" . $herecurr); - } - } - -# check for switch/default statements without a break; - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { - my $ctx = ''; - my $herectx = $here . "\n"; - my $cnt = statement_rawlines($stat); - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - WARN("DEFAULT_NO_BREAK", - "switch default: should use break\n" . $herectx); - } - -# check for gcc specific __FUNCTION__ - if ($line =~ /\b__FUNCTION__\b/) { - if (WARN("USE_FUNC", - "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; - } - } - -# check for uses of __DATE__, __TIME__, __TIMESTAMP__ - while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { - ERROR("DATE_TIME", - "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); - } - -# check for use of yield() - if ($line =~ /\byield\s*\(\s*\)/) { - WARN("YIELD", - "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); - } - -# check for comparisons against true and false - if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { - my $lead = $1; - my $arg = $2; - my $test = $3; - my $otype = $4; - my $trail = $5; - my $op = "!"; - - ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); - - my $type = lc($otype); - if ($type =~ /^(?:true|false)$/) { - if (("$test" eq "==" && "$type" eq "true") || - ("$test" eq "!=" && "$type" eq "false")) { - $op = ""; - } - - CHK("BOOL_COMPARISON", - "Using comparison to $otype is error prone\n" . $herecurr); - -## maybe suggesting a correct construct would better -## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); - - } - } - -# check for semaphores initialized locked - if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { - WARN("CONSIDER_COMPLETION", - "consider using a completion\n" . $herecurr); - } - -# recommend kstrto* over simple_strto* and strict_strto* - if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { - WARN("CONSIDER_KSTRTO", - "$1 is obsolete, use k$3 instead\n" . $herecurr); - } - -# check for __initcall(), use device_initcall() explicitly or more appropriate function please - if ($line =~ /^.\s*__initcall\s*\(/) { - WARN("USE_DEVICE_INITCALL", - "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); - } - -# check for various structs that are normally const (ops, kgdb, device_tree) - my $const_structs = qr{ - acpi_dock_ops| - address_space_operations| - backlight_ops| - block_device_operations| - dentry_operations| - dev_pm_ops| - dma_map_ops| - extent_io_ops| - file_lock_operations| - file_operations| - hv_ops| - ide_dma_ops| - intel_dvo_dev_ops| - item_operations| - iwl_ops| - kgdb_arch| - kgdb_io| - kset_uevent_ops| - lock_manager_operations| - microcode_ops| - mtrr_ops| - neigh_ops| - nlmsvc_binding| - of_device_id| - pci_raw_ops| - pipe_buf_operations| - platform_hibernation_ops| - platform_suspend_ops| - proto_ops| - rpc_pipe_ops| - seq_operations| - snd_ac97_build_ops| - soc_pcmcia_socket_ops| - stacktrace_ops| - sysfs_ops| - tty_operations| - uart_ops| - usb_mon_operations| - wd_ops}x; - if ($line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($const_structs)\b/) { - WARN("CONST_STRUCT", - "struct $1 should normally be const\n" . - $herecurr); - } - -# use of NR_CPUS is usually wrong -# ignore definitions of NR_CPUS and usage to define arrays as likely right - if ($line =~ /\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) - { - WARN("NR_CPUS", - "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); - } - -# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. - if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { - ERROR("DEFINE_ARCH_HAS", - "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); - } - -# likely/unlikely comparisons similar to "(likely(foo) > 0)" - if ($^V && $^V ge 5.10.0 && - $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { - WARN("LIKELY_MISUSE", - "Using $1 should generally have parentheses around the comparison\n" . $herecurr); - } - -# whine mightly about in_atomic - if ($line =~ /\bin_atomic\s*\(/) { - if ($realfile =~ m@^drivers/@) { - ERROR("IN_ATOMIC", - "do not use in_atomic in drivers\n" . $herecurr); - } elsif ($realfile !~ m@^kernel/@) { - WARN("IN_ATOMIC", - "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); - } - } - -# check for lockdep_set_novalidate_class - if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || - $line =~ /__lockdep_no_validate__\s*\)/ ) { - if ($realfile !~ m@^kernel/lockdep@ && - $realfile !~ m@^include/linux/lockdep@ && - $realfile !~ m@^drivers/base/core@) { - ERROR("LOCKDEP", - "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); - } - } - - if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || - $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { - WARN("EXPORTED_WORLD_WRITABLE", - "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); - } - -# Mode permission misuses where it seems decimal should be octal -# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop - if ($^V && $^V ge 5.10.0 && - $line =~ /$mode_perms_search/) { - foreach my $entry (@mode_permission_funcs) { - my $func = $entry->[0]; - my $arg_pos = $entry->[1]; - - my $skip_args = ""; - if ($arg_pos > 1) { - $arg_pos--; - $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; - } - my $test = "\\b$func\\s*\\(${skip_args}([\\d]+)\\s*[,\\)]"; - if ($line =~ /$test/) { - my $val = $1; - $val = $6 if ($skip_args ne ""); - - if ($val !~ /^0$/ && - (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || - length($val) ne 4)) { - ERROR("NON_OCTAL_PERMISSIONS", - "Use 4 digit octal (0777) not decimal permissions\n" . $herecurr); - } elsif ($val =~ /^$Octal$/ && (oct($val) & 02)) { - ERROR("EXPORTED_WORLD_WRITABLE", - "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); - } - } - } - } - -# validate content of MODULE_LICENSE against list from include/linux/module.h - if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { - my $extracted_string = get_quoted_string($line, $rawline); - my $valid_licenses = qr{ - GPL| - GPL\ v2| - GPL\ and\ additional\ rights| - Dual\ BSD/GPL| - Dual\ MIT/GPL| - Dual\ MPL/GPL| - Proprietary - }x; - if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { - WARN("MODULE_LICENSE", - "unknown module license " . $extracted_string . "\n" . $herecurr); - } - } - } - - # If we have no input at all, then there is nothing to report on - # so just keep quiet. - if ($#rawlines == -1) { - exit(0); - } - - # In mailback mode only produce a report in the negative, for - # things that appear to be patches. - if ($mailback && ($clean == 1 || !$is_patch)) { - exit(0); - } - - # This is not a patch, and we are are in 'no-patch' mode so - # just keep quiet. - if (!$chk_patch && !$is_patch) { - exit(0); - } - - if (!$is_patch && $file !~ /cover-letter\.patch$/) { - ERROR("NOT_UNIFIED_DIFF", - "Does not appear to be a unified-diff format patch\n"); - } - if ($is_patch && $filename ne '-' && $chk_signoff && $signoff == 0) { - ERROR("MISSING_SIGN_OFF", - "Missing Signed-off-by: line(s)\n"); - } - - print report_dump(); - if ($summary && !($clean == 1 && $quiet == 1)) { - print "$filename " if ($summary_file); - print "total: $cnt_error errors, $cnt_warn warnings, " . - (($check)? "$cnt_chk checks, " : "") . - "$cnt_lines lines checked\n"; - } - - if ($quiet == 0) { - # If there were whitespace errors which cleanpatch can fix - # then suggest that. - if ($rpt_cleaners) { - $rpt_cleaners = 0; - print << "EOM" - -NOTE: Whitespace errors detected. - You may wish to use scripts/cleanpatch or scripts/cleanfile -EOM - } - } - - if ($clean == 0 && $fix && - ("@rawlines" ne "@fixed" || - $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { - my $newfile = $filename; - $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); - my $linecount = 0; - my $f; - - @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); - - open($f, '>', $newfile) - or die "$P: Can't open $newfile for write\n"; - foreach my $fixed_line (@fixed) { - $linecount++; - if ($file) { - if ($linecount > 3) { - $fixed_line =~ s/^\+//; - print $f $fixed_line . "\n"; - } - } else { - print $f $fixed_line . "\n"; - } - } - close($f); - - if (!$quiet) { - print << "EOM"; - -Wrote EXPERIMENTAL --fix correction(s) to '$newfile' - -Do _NOT_ trust the results written to this file. -Do _NOT_ submit these changes without inspecting them for correctness. - -This EXPERIMENTAL file is simply a convenience to help rewrite patches. -No warranties, expressed or implied... -EOM - } - } - - if ($quiet == 0) { - print "\n"; - if ($clean == 1) { - print "$vname has no obvious style problems and is ready for submission.\n"; - } else { - print "$vname has style problems, please review.\n"; - } - } - return $clean; -} diff --git a/drivers/staging/greybus/scripts/checkpatch.pl b/drivers/staging/greybus/scripts/checkpatch.pl new file mode 100755 index 000000000000..d574d13ba963 --- /dev/null +++ b/drivers/staging/greybus/scripts/checkpatch.pl @@ -0,0 +1,5993 @@ +#!/usr/bin/perl -w +# (c) 2001, Dave Jones. (the file handling bit) +# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit) +# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite) +# (c) 2008-2010 Andy Whitcroft <apw@canonical.com> +# Licensed under the terms of the GNU GPL License version 2 + +use strict; +use POSIX; +use File::Basename; +use Cwd 'abs_path'; +use Term::ANSIColor qw(:constants); + +my $P = $0; +my $D = dirname(abs_path($P)); + +my $V = '0.32'; + +use Getopt::Long qw(:config no_auto_abbrev); + +my $quiet = 0; +my $tree = 1; +my $chk_signoff = 1; +my $chk_patch = 1; +my $tst_only; +my $emacs = 0; +my $terse = 0; +my $showfile = 0; +my $file = 0; +my $check = 0; +my $check_orig = 0; +my $summary = 1; +my $mailback = 0; +my $summary_file = 0; +my $show_types = 0; +my $fix = 0; +my $fix_inplace = 0; +my $root; +my %debug; +my %camelcase = (); +my %use_type = (); +my @use = (); +my %ignore_type = (); +my @ignore = (); +my $help = 0; +my $configuration_file = ".checkpatch.conf"; +my $max_line_length = 80; +my $ignore_perl_version = 0; +my $minimum_perl_version = 5.10.0; +my $min_conf_desc_length = 4; +my $spelling_file = "$D/spelling.txt"; +my $codespell = 0; +my $codespellfile = "/usr/share/codespell/dictionary.txt"; +my $color = 1; + +sub help { + my ($exitcode) = @_; + + print << "EOM"; +Usage: $P [OPTION]... [FILE]... +Version: $V + +Options: + -q, --quiet quiet + --no-tree run without a kernel tree + --no-signoff do not check for 'Signed-off-by' line + --patch treat FILE as patchfile (default) + --emacs emacs compile window format + --terse one line per report + --showfile emit diffed file position, not input file position + -f, --file treat FILE as regular source file + --subjective, --strict enable more subjective tests + --types TYPE(,TYPE2...) show only these comma separated message types + --ignore TYPE(,TYPE2...) ignore various comma separated message types + --max-line-length=n set the maximum line length, if exceeded, warn + --min-conf-desc-length=n set the min description length, if shorter, warn + --show-types show the message "types" in the output + --root=PATH PATH to the kernel tree root + --no-summary suppress the per-file summary + --mailback only produce a report in case of warnings/errors + --summary-file include the filename in summary + --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of + 'values', 'possible', 'type', and 'attr' (default + is all off) + --test-only=WORD report only warnings/errors containing WORD + literally + --fix EXPERIMENTAL - may create horrible results + If correctable single-line errors exist, create + "<inputfile>.EXPERIMENTAL-checkpatch-fixes" + with potential errors corrected to the preferred + checkpatch style + --fix-inplace EXPERIMENTAL - may create horrible results + Is the same as --fix, but overwrites the input + file. It's your fault if there's no backup or git + --ignore-perl-version override checking of perl version. expect + runtime errors. + --codespell Use the codespell dictionary for spelling/typos + (default:/usr/share/codespell/dictionary.txt) + --codespellfile Use this codespell dictionary + --color Use colors when output is STDOUT (default: on) + -h, --help, --version display this help and exit + +When FILE is - read standard input. +EOM + + exit($exitcode); +} + +my $conf = which_conf($configuration_file); +if (-f $conf) { + my @conf_args; + open(my $conffile, '<', "$conf") + or warn "$P: Can't find a readable $configuration_file file $!\n"; + + while (<$conffile>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + $line =~ s/\s+/ /g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + + my @words = split(" ", $line); + foreach my $word (@words) { + last if ($word =~ m/^#/); + push (@conf_args, $word); + } + } + close($conffile); + unshift(@ARGV, @conf_args) if @conf_args; +} + +GetOptions( + 'q|quiet+' => \$quiet, + 'tree!' => \$tree, + 'signoff!' => \$chk_signoff, + 'patch!' => \$chk_patch, + 'emacs!' => \$emacs, + 'terse!' => \$terse, + 'showfile!' => \$showfile, + 'f|file!' => \$file, + 'subjective!' => \$check, + 'strict!' => \$check, + 'ignore=s' => \@ignore, + 'types=s' => \@use, + 'show-types!' => \$show_types, + 'max-line-length=i' => \$max_line_length, + 'min-conf-desc-length=i' => \$min_conf_desc_length, + 'root=s' => \$root, + 'summary!' => \$summary, + 'mailback!' => \$mailback, + 'summary-file!' => \$summary_file, + 'fix!' => \$fix, + 'fix-inplace!' => \$fix_inplace, + 'ignore-perl-version!' => \$ignore_perl_version, + 'debug=s' => \%debug, + 'test-only=s' => \$tst_only, + 'codespell!' => \$codespell, + 'codespellfile=s' => \$codespellfile, + 'color!' => \$color, + 'h|help' => \$help, + 'version' => \$help +) or help(1); + +help(0) if ($help); + +$fix = 1 if ($fix_inplace); +$check_orig = $check; + +my $exit = 0; + +if ($^V && $^V lt $minimum_perl_version) { + printf "$P: requires at least perl version %vd\n", $minimum_perl_version; + if (!$ignore_perl_version) { + exit(1); + } +} + +if ($#ARGV < 0) { + print "$P: no input files\n"; + exit(1); +} + +sub hash_save_array_words { + my ($hashRef, $arrayRef) = @_; + + my @array = split(/,/, join(',', @$arrayRef)); + foreach my $word (@array) { + $word =~ s/\s*\n?$//g; + $word =~ s/^\s*//g; + $word =~ s/\s+/ /g; + $word =~ tr/[a-z]/[A-Z]/; + + next if ($word =~ m/^\s*#/); + next if ($word =~ m/^\s*$/); + + $hashRef->{$word}++; + } +} + +sub hash_show_words { + my ($hashRef, $prefix) = @_; + + if (keys %$hashRef) { + print "\nNOTE: $prefix message types:"; + foreach my $word (sort keys %$hashRef) { + print " $word"; + } + print "\n"; + } +} + +hash_save_array_words(\%ignore_type, \@ignore); +hash_save_array_words(\%use_type, \@use); + +my $dbg_values = 0; +my $dbg_possible = 0; +my $dbg_type = 0; +my $dbg_attr = 0; +for my $key (keys %debug) { + ## no critic + eval "\${dbg_$key} = '$debug{$key}';"; + die "$@" if ($@); +} + +my $rpt_cleaners = 0; + +if ($terse) { + $emacs = 1; + $quiet++; +} + +if ($tree) { + if (defined $root) { + if (!top_of_kernel_tree($root)) { + die "$P: $root: --root does not point at a valid tree\n"; + } + } else { + if (top_of_kernel_tree('.')) { + $root = '.'; + } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && + top_of_kernel_tree($1)) { + $root = $1; + } + } + + if (!defined $root) { + print "Must be run from the top-level dir. of a kernel tree\n"; + exit(2); + } +} + +my $emitted_corrupt = 0; + +our $Ident = qr{ + [A-Za-z_][A-Za-z\d_]* + (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* + }x; +our $Storage = qr{extern|static|asmlinkage}; +our $Sparse = qr{ + __user| + __kernel| + __force| + __iomem| + __pmem| + __must_check| + __init_refok| + __kprobes| + __ref| + __rcu| + __private + }x; +our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; +our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; +our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; +our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; +our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; + +# Notes to $Attribute: +# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check +our $Attribute = qr{ + const| + __percpu| + __nocast| + __safe| + __bitwise__| + __packed__| + __packed2__| + __naked| + __maybe_unused| + __always_unused| + __noreturn| + __used| + __cold| + __pure| + __noclone| + __deprecated| + __read_mostly| + __kprobes| + $InitAttribute| + ____cacheline_aligned| + ____cacheline_aligned_in_smp| + ____cacheline_internodealigned_in_smp| + __weak + }x; +our $Modifier; +our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; +our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; +our $Lval = qr{$Ident(?:$Member)*}; + +our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; +our $Binary = qr{(?i)0b[01]+$Int_type?}; +our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; +our $Int = qr{[0-9]+$Int_type?}; +our $Octal = qr{0[0-7]+$Int_type?}; +our $String = qr{"[X\t]*"}; +our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; +our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; +our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; +our $Float = qr{$Float_hex|$Float_dec|$Float_int}; +our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; +our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; +our $Compare = qr{<=|>=|==|!=|<|(?<!-)>}; +our $Arithmetic = qr{\+|-|\*|\/|%}; +our $Operators = qr{ + <=|>=|==|!=| + =>|->|<<|>>|<|>|!|~| + &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic + }x; + +our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; + +our $BasicType; +our $NonptrType; +our $NonptrTypeMisordered; +our $NonptrTypeWithAttr; +our $Type; +our $TypeMisordered; +our $Declare; +our $DeclareMisordered; + +our $NON_ASCII_UTF8 = qr{ + [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 +}x; + +our $UTF8 = qr{ + [\x09\x0A\x0D\x20-\x7E] # ASCII + | $NON_ASCII_UTF8 +}x; + +our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; +our $typeOtherOSTypedefs = qr{(?x: + u_(?:char|short|int|long) | # bsd + u(?:nchar|short|int|long) # sysv +)}; +our $typeKernelTypedefs = qr{(?x: + (?:__)?(?:u|s|be|le)(?:8|16|32|64)| + atomic_t +)}; +our $typeTypedefs = qr{(?x: + $typeC99Typedefs\b| + $typeOtherOSTypedefs\b| + $typeKernelTypedefs\b +)}; + +our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; + +our $logFunctions = qr{(?x: + printk(?:_ratelimited|_once|)| + (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| + WARN(?:_RATELIMIT|_ONCE|)| + panic| + MODULE_[A-Z_]+| + seq_vprintf|seq_printf|seq_puts +)}; + +our $signature_tags = qr{(?xi: + Signed-off-by:| + Acked-by:| + Tested-by:| + Reviewed-by:| + Reported-by:| + Suggested-by:| + To:| + Cc: +)}; + +our @typeListMisordered = ( + qr{char\s+(?:un)?signed}, + qr{int\s+(?:(?:un)?signed\s+)?short\s}, + qr{int\s+short(?:\s+(?:un)?signed)}, + qr{short\s+int(?:\s+(?:un)?signed)}, + qr{(?:un)?signed\s+int\s+short}, + qr{short\s+(?:un)?signed}, + qr{long\s+int\s+(?:un)?signed}, + qr{int\s+long\s+(?:un)?signed}, + qr{long\s+(?:un)?signed\s+int}, + qr{int\s+(?:un)?signed\s+long}, + qr{int\s+(?:un)?signed}, + qr{int\s+long\s+long\s+(?:un)?signed}, + qr{long\s+long\s+int\s+(?:un)?signed}, + qr{long\s+long\s+(?:un)?signed\s+int}, + qr{long\s+long\s+(?:un)?signed}, + qr{long\s+(?:un)?signed}, +); + +our @typeList = ( + qr{void}, + qr{(?:(?:un)?signed\s+)?char}, + qr{(?:(?:un)?signed\s+)?short\s+int}, + qr{(?:(?:un)?signed\s+)?short}, + qr{(?:(?:un)?signed\s+)?int}, + qr{(?:(?:un)?signed\s+)?long\s+int}, + qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, + qr{(?:(?:un)?signed\s+)?long\s+long}, + qr{(?:(?:un)?signed\s+)?long}, + qr{(?:un)?signed}, + qr{float}, + qr{double}, + qr{bool}, + qr{struct\s+$Ident}, + qr{union\s+$Ident}, + qr{enum\s+$Ident}, + qr{${Ident}_t}, + qr{${Ident}_handler}, + qr{${Ident}_handler_fn}, + @typeListMisordered, +); + +our $C90_int_types = qr{(?x: + long\s+long\s+int\s+(?:un)?signed| + long\s+long\s+(?:un)?signed\s+int| + long\s+long\s+(?:un)?signed| + (?:(?:un)?signed\s+)?long\s+long\s+int| + (?:(?:un)?signed\s+)?long\s+long| + int\s+long\s+long\s+(?:un)?signed| + int\s+(?:(?:un)?signed\s+)?long\s+long| + + long\s+int\s+(?:un)?signed| + long\s+(?:un)?signed\s+int| + long\s+(?:un)?signed| + (?:(?:un)?signed\s+)?long\s+int| + (?:(?:un)?signed\s+)?long| + int\s+long\s+(?:un)?signed| + int\s+(?:(?:un)?signed\s+)?long| + + int\s+(?:un)?signed| + (?:(?:un)?signed\s+)?int +)}; + +our @typeListFile = (); +our @typeListWithAttr = ( + @typeList, + qr{struct\s+$InitAttribute\s+$Ident}, + qr{union\s+$InitAttribute\s+$Ident}, +); + +our @modifierList = ( + qr{fastcall}, +); +our @modifierListFile = (); + +our @mode_permission_funcs = ( + ["module_param", 3], + ["module_param_(?:array|named|string)", 4], + ["module_param_array_named", 5], + ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], + ["proc_create(?:_data|)", 2], + ["(?:CLASS|DEVICE|SENSOR)_ATTR", 2], +); + +#Create a search pattern for all these functions to speed up a loop below +our $mode_perms_search = ""; +foreach my $entry (@mode_permission_funcs) { + $mode_perms_search .= '|' if ($mode_perms_search ne ""); + $mode_perms_search .= $entry->[0]; +} + +our $mode_perms_world_writable = qr{ + S_IWUGO | + S_IWOTH | + S_IRWXUGO | + S_IALLUGO | + 0[0-7][0-7][2367] +}x; + +our $allowed_asm_includes = qr{(?x: + irq| + memory| + time| + reboot +)}; +# memory.h: ARM has a custom one + +# Load common spelling mistakes and build regular expression list. +my $misspellings; +my %spelling_fix; + +if (open(my $spelling, '<', $spelling_file)) { + while (<$spelling>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + + my ($suspect, $fix) = split(/\|\|/, $line); + + $spelling_fix{$suspect} = $fix; + } + close($spelling); +} else { + warn "No typos will be found - file '$spelling_file': $!\n"; +} + +if ($codespell) { + if (open(my $spelling, '<', $codespellfile)) { + while (<$spelling>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + next if ($line =~ m/, disabled/i); + + $line =~ s/,.*$//; + + my ($suspect, $fix) = split(/->/, $line); + + $spelling_fix{$suspect} = $fix; + } + close($spelling); + } else { + warn "No codespell typos will be found - file '$codespellfile': $!\n"; + } +} + +$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; + +sub build_types { + my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; + my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; + my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; + my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; + $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + $BasicType = qr{ + (?:$typeTypedefs\b)| + (?:${all}\b) + }x; + $NonptrType = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\([^\)]*\)| + (?:$typeTypedefs\b)| + (?:${all}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $NonptrTypeMisordered = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:${Misordered}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $NonptrTypeWithAttr = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\([^\)]*\)| + (?:$typeTypedefs\b)| + (?:${allWithAttr}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $Type = qr{ + $NonptrType + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? + (?:\s+$Inline|\s+$Modifier)* + }x; + $TypeMisordered = qr{ + $NonptrTypeMisordered + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? + (?:\s+$Inline|\s+$Modifier)* + }x; + $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; + $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; +} +build_types(); + +our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; + +# Using $balanced_parens, $LvalOrFunc, or $FuncArg +# requires at least perl version v5.10.0 +# Any use must be runtime checked with $^V + +our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; +our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; +our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; + +our $declaration_macros = qr{(?x: + (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| + (?:$Storage\s+)?LIST_HEAD\s*\(| + (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\( +)}; + +sub deparenthesize { + my ($string) = @_; + return "" if (!defined($string)); + + while ($string =~ /^\s*\(.*\)\s*$/) { + $string =~ s@^\s*\(\s*@@; + $string =~ s@\s*\)\s*$@@; + } + + $string =~ s@\s+@ @g; + + return $string; +} + +sub seed_camelcase_file { + my ($file) = @_; + + return if (!(-f $file)); + + local $/; + + open(my $include_file, '<', "$file") + or warn "$P: Can't read '$file' $!\n"; + my $text = <$include_file>; + close($include_file); + + my @lines = split('\n', $text); + + foreach my $line (@lines) { + next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); + if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { + $camelcase{$1} = 1; + } + } +} + +my $camelcase_seeded = 0; +sub seed_camelcase_includes { + return if ($camelcase_seeded); + + my $files; + my $camelcase_cache = ""; + my @include_files = (); + + $camelcase_seeded = 1; + + if (-e ".git") { + my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; + chomp $git_last_include_commit; + $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; + } else { + my $last_mod_date = 0; + $files = `find $root/include -name "*.h"`; + @include_files = split('\n', $files); + foreach my $file (@include_files) { + my $date = POSIX::strftime("%Y%m%d%H%M", + localtime((stat $file)[9])); + $last_mod_date = $date if ($last_mod_date < $date); + } + $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; + } + + if ($camelcase_cache ne "" && -f $camelcase_cache) { + open(my $camelcase_file, '<', "$camelcase_cache") + or warn "$P: Can't read '$camelcase_cache' $!\n"; + while (<$camelcase_file>) { + chomp; + $camelcase{$_} = 1; + } + close($camelcase_file); + + return; + } + + if (-e ".git") { + $files = `git ls-files "include/*.h"`; + @include_files = split('\n', $files); + } + + foreach my $file (@include_files) { + seed_camelcase_file($file); + } + + if ($camelcase_cache ne "") { + unlink glob ".checkpatch-camelcase.*"; + open(my $camelcase_file, '>', "$camelcase_cache") + or warn "$P: Can't write '$camelcase_cache' $!\n"; + foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { + print $camelcase_file ("$_\n"); + } + close($camelcase_file); + } +} + +sub git_commit_info { + my ($commit, $id, $desc) = @_; + + return ($id, $desc) if ((which("git") eq "") || !(-e ".git")); + + my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`; + $output =~ s/^\s*//gm; + my @lines = split("\n", $output); + + return ($id, $desc) if ($#lines < 0); + + if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) { +# Maybe one day convert this block of bash into something that returns +# all matching commit ids, but it's very slow... +# +# echo "checking commits $1..." +# git rev-list --remotes | grep -i "^$1" | +# while read line ; do +# git log --format='%H %s' -1 $line | +# echo "commit $(cut -c 1-12,41-)" +# done + } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { + } else { + $id = substr($lines[0], 0, 12); + $desc = substr($lines[0], 41); + } + + return ($id, $desc); +} + +$chk_signoff = 0 if ($file); + +my @rawlines = (); +my @lines = (); +my @fixed = (); +my @fixed_inserted = (); +my @fixed_deleted = (); +my $fixlinenr = -1; + +my $vname; +for my $filename (@ARGV) { + my $FILE; + if ($file) { + open($FILE, '-|', "diff -u /dev/null $filename") || + die "$P: $filename: diff failed - $!\n"; + } elsif ($filename eq '-') { + open($FILE, '<&STDIN'); + } else { + open($FILE, '<', "$filename") || + die "$P: $filename: open failed - $!\n"; + } + if ($filename eq '-') { + $vname = 'Your patch'; + } else { + $vname = $filename; + } + while (<$FILE>) { + chomp; + push(@rawlines, $_); + } + close($FILE); + + if ($#ARGV > 0 && $quiet == 0) { + print '-' x length($vname) . "\n"; + print "$vname\n"; + print '-' x length($vname) . "\n"; + } + + if (!process($filename)) { + $exit = 1; + } + @rawlines = (); + @lines = (); + @fixed = (); + @fixed_inserted = (); + @fixed_deleted = (); + $fixlinenr = -1; + @modifierListFile = (); + @typeListFile = (); + build_types(); +} + +if (!$quiet) { + hash_show_words(\%use_type, "Used"); + hash_show_words(\%ignore_type, "Ignored"); + + if ($^V lt 5.10.0) { + print << "EOM" + +NOTE: perl $^V is not modern enough to detect all possible issues. + An upgrade to at least perl v5.10.0 is suggested. +EOM + } + if ($exit) { + print << "EOM" + +NOTE: If any of the errors are false positives, please report + them to the maintainer, see CHECKPATCH in MAINTAINERS. +EOM + } +} + +exit($exit); + +sub top_of_kernel_tree { + my ($root) = @_; + + my @tree_check = ( + "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", + "README", "Documentation", "arch", "include", "drivers", + "fs", "init", "ipc", "kernel", "lib", "scripts", + ); + + foreach my $check (@tree_check) { + if (! -e $root . '/' . $check) { + return 0; + } + } + return 1; +} + +sub parse_email { + my ($formatted_email) = @_; + + my $name = ""; + my $address = ""; + my $comment = ""; + + if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { + $name = $1; + $address = $2; + $comment = $3 if defined $3; + } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { + $address = $1; + $comment = $2 if defined $2; + } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { + $address = $1; + $comment = $2 if defined $2; + $formatted_email =~ s/$address.*$//; + $name = $formatted_email; + $name = trim($name); + $name =~ s/^\"|\"$//g; + # If there's a name left after stripping spaces and + # leading quotes, and the address doesn't have both + # leading and trailing angle brackets, the address + # is invalid. ie: + # "joe smith joe@smith.com" bad + # "joe smith <joe@smith.com" bad + if ($name ne "" && $address !~ /^<[^>]+>$/) { + $name = ""; + $address = ""; + $comment = ""; + } + } + + $name = trim($name); + $name =~ s/^\"|\"$//g; + $address = trim($address); + $address =~ s/^\<|\>$//g; + + if ($name =~ /[^\w \-]/i) { ##has "must quote" chars + $name =~ s/(?<!\\)"/\\"/g; ##escape quotes + $name = "\"$name\""; + } + + return ($name, $address, $comment); +} + +sub format_email { + my ($name, $address) = @_; + + my $formatted_email; + + $name = trim($name); + $name =~ s/^\"|\"$//g; + $address = trim($address); + + if ($name =~ /[^\w \-]/i) { ##has "must quote" chars + $name =~ s/(?<!\\)"/\\"/g; ##escape quotes + $name = "\"$name\""; + } + + if ("$name" eq "") { + $formatted_email = "$address"; + } else { + $formatted_email = "$name <$address>"; + } + + return $formatted_email; +} + +sub which { + my ($bin) = @_; + + foreach my $path (split(/:/, $ENV{PATH})) { + if (-e "$path/$bin") { + return "$path/$bin"; + } + } + + return ""; +} + +sub which_conf { + my ($conf) = @_; + + foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { + if (-e "$path/$conf") { + return "$path/$conf"; + } + } + + return ""; +} + +sub expand_tabs { + my ($str) = @_; + + my $res = ''; + my $n = 0; + for my $c (split(//, $str)) { + if ($c eq "\t") { + $res .= ' '; + $n++; + for (; ($n % 8) != 0; $n++) { + $res .= ' '; + } + next; + } + $res .= $c; + $n++; + } + + return $res; +} +sub copy_spacing { + (my $res = shift) =~ tr/\t/ /c; + return $res; +} + +sub line_stats { + my ($line) = @_; + + # Drop the diff line leader and expand tabs + $line =~ s/^.//; + $line = expand_tabs($line); + + # Pick the indent from the front of the line. + my ($white) = ($line =~ /^(\s*)/); + + return (length($line), length($white)); +} + +my $sanitise_quote = ''; + +sub sanitise_line_reset { + my ($in_comment) = @_; + + if ($in_comment) { + $sanitise_quote = '*/'; + } else { + $sanitise_quote = ''; + } +} +sub sanitise_line { + my ($line) = @_; + + my $res = ''; + my $l = ''; + + my $qlen = 0; + my $off = 0; + my $c; + + # Always copy over the diff marker. + $res = substr($line, 0, 1); + + for ($off = 1; $off < length($line); $off++) { + $c = substr($line, $off, 1); + + # Comments we are wacking completly including the begin + # and end, all to $;. + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { + $sanitise_quote = '*/'; + + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { + $sanitise_quote = ''; + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { + $sanitise_quote = '//'; + + substr($res, $off, 2, $sanitise_quote); + $off++; + next; + } + + # A \ in a string means ignore the next character. + if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && + $c eq "\\") { + substr($res, $off, 2, 'XX'); + $off++; + next; + } + # Regular quotes. + if ($c eq "'" || $c eq '"') { + if ($sanitise_quote eq '') { + $sanitise_quote = $c; + + substr($res, $off, 1, $c); + next; + } elsif ($sanitise_quote eq $c) { + $sanitise_quote = ''; + } + } + + #print "c<$c> SQ<$sanitise_quote>\n"; + if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { + substr($res, $off, 1, 'X'); + } else { + substr($res, $off, 1, $c); + } + } + + if ($sanitise_quote eq '//') { + $sanitise_quote = ''; + } + + # The pathname on a #include may be surrounded by '<' and '>'. + if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { + my $clean = 'X' x length($1); + $res =~ s@\<.*\>@<$clean>@; + + # The whole of a #error is a string. + } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { + my $clean = 'X' x length($1); + $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; + } + + return $res; +} + +sub get_quoted_string { + my ($line, $rawline) = @_; + + return "" if ($line !~ m/($String)/g); + return substr($rawline, $-[0], $+[0] - $-[0]); +} + +sub ctx_statement_block { + my ($linenr, $remain, $off) = @_; + my $line = $linenr - 1; + my $blk = ''; + my $soff = $off; + my $coff = $off - 1; + my $coff_set = 0; + + my $loff = 0; + + my $type = ''; + my $level = 0; + my @stack = (); + my $p; + my $c; + my $len = 0; + + my $remainder; + while (1) { + @stack = (['', 0]) if ($#stack == -1); + + #warn "CSB: blk<$blk> remain<$remain>\n"; + # If we are about to drop off the end, pull in more + # context. + if ($off >= $len) { + for (; $remain > 0; $line++) { + last if (!defined $lines[$line]); + next if ($lines[$line] =~ /^-/); + $remain--; + $loff = $len; + $blk .= $lines[$line] . "\n"; + $len = length($blk); + $line++; + last; + } + # Bail if there is no further context. + #warn "CSB: blk<$blk> off<$off> len<$len>\n"; + if ($off >= $len) { + last; + } + if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { + $level++; + $type = '#'; + } + } + $p = $c; + $c = substr($blk, $off, 1); + $remainder = substr($blk, $off); + + #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; + + # Handle nested #if/#else. + if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, [ $type, $level ]); + } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { + ($type, $level) = @{$stack[$#stack - 1]}; + } elsif ($remainder =~ /^#\s*endif\b/) { + ($type, $level) = @{pop(@stack)}; + } + + # Statement ends at the ';' or a close '}' at the + # outermost level. + if ($level == 0 && $c eq ';') { + last; + } + + # An else is really a conditional as long as its not else if + if ($level == 0 && $coff_set == 0 && + (!defined($p) || $p =~ /(?:\s|\}|\+)/) && + $remainder =~ /^(else)(?:\s|{)/ && + $remainder !~ /^else\s+if\b/) { + $coff = $off + length($1) - 1; + $coff_set = 1; + #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; + #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; + } + + if (($type eq '' || $type eq '(') && $c eq '(') { + $level++; + $type = '('; + } + if ($type eq '(' && $c eq ')') { + $level--; + $type = ($level != 0)? '(' : ''; + + if ($level == 0 && $coff < $soff) { + $coff = $off; + $coff_set = 1; + #warn "CSB: mark coff<$coff>\n"; + } + } + if (($type eq '' || $type eq '{') && $c eq '{') { + $level++; + $type = '{'; + } + if ($type eq '{' && $c eq '}') { + $level--; + $type = ($level != 0)? '{' : ''; + + if ($level == 0) { + if (substr($blk, $off + 1, 1) eq ';') { + $off++; + } + last; + } + } + # Preprocessor commands end at the newline unless escaped. + if ($type eq '#' && $c eq "\n" && $p ne "\\") { + $level--; + $type = ''; + $off++; + last; + } + $off++; + } + # We are truly at the end, so shuffle to the next line. + if ($off == $len) { + $loff = $len + 1; + $line++; + $remain--; + } + + my $statement = substr($blk, $soff, $off - $soff + 1); + my $condition = substr($blk, $soff, $coff - $soff + 1); + + #warn "STATEMENT<$statement>\n"; + #warn "CONDITION<$condition>\n"; + + #print "coff<$coff> soff<$off> loff<$loff>\n"; + + return ($statement, $condition, + $line, $remain + 1, $off - $loff + 1, $level); +} + +sub statement_lines { + my ($stmt) = @_; + + # Strip the diff line prefixes and rip blank lines at start and end. + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_rawlines { + my ($stmt) = @_; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_block_size { + my ($stmt) = @_; + + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*{//; + $stmt =~ s/}\s*$//; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + my @stmt_statements = ($stmt =~ /;/g); + + my $stmt_lines = $#stmt_lines + 2; + my $stmt_statements = $#stmt_statements + 1; + + if ($stmt_lines > $stmt_statements) { + return $stmt_lines; + } else { + return $stmt_statements; + } +} + +sub ctx_statement_full { + my ($linenr, $remain, $off) = @_; + my ($statement, $condition, $level); + + my (@chunks); + + # Grab the first conditional/block pair. + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "F: c<$condition> s<$statement> remain<$remain>\n"; + push(@chunks, [ $condition, $statement ]); + if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { + return ($level, $linenr, @chunks); + } + + # Pull in the following conditional/block pairs and see if they + # could continue the statement. + for (;;) { + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "C: c<$condition> s<$statement> remain<$remain>\n"; + last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); + #print "C: push\n"; + push(@chunks, [ $condition, $statement ]); + } + + return ($level, $linenr, @chunks); +} + +sub ctx_block_get { + my ($linenr, $remain, $outer, $open, $close, $off) = @_; + my $line; + my $start = $linenr - 1; + my $blk = ''; + my @o; + my @c; + my @res = (); + + my $level = 0; + my @stack = ($level); + for ($line = $start; $remain > 0; $line++) { + next if ($rawlines[$line] =~ /^-/); + $remain--; + + $blk .= $rawlines[$line]; + + # Handle nested #if/#else. + if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, $level); + } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { + $level = $stack[$#stack - 1]; + } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { + $level = pop(@stack); + } + + foreach my $c (split(//, $lines[$line])) { + ##print "C<$c>L<$level><$open$close>O<$off>\n"; + if ($off > 0) { + $off--; + next; + } + + if ($c eq $close && $level > 0) { + $level--; + last if ($level == 0); + } elsif ($c eq $open) { + $level++; + } + } + + if (!$outer || $level <= 1) { + push(@res, $rawlines[$line]); + } + + last if ($level == 0); + } + + return ($level, @res); +} +sub ctx_block_outer { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); + return @r; +} +sub ctx_block { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); + return @r; +} +sub ctx_statement { + my ($linenr, $remain, $off) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); + return @r; +} +sub ctx_block_level { + my ($linenr, $remain) = @_; + + return ctx_block_get($linenr, $remain, 0, '{', '}', 0); +} +sub ctx_statement_level { + my ($linenr, $remain, $off) = @_; + + return ctx_block_get($linenr, $remain, 0, '(', ')', $off); +} + +sub ctx_locate_comment { + my ($first_line, $end_line) = @_; + + # Catch a comment on the end of the line itself. + my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); + return $current_comment if (defined $current_comment); + + # Look through the context and try and figure out if there is a + # comment. + my $in_comment = 0; + $current_comment = ''; + for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { + my $line = $rawlines[$linenr - 1]; + #warn " $line\n"; + if ($linenr == $first_line and $line =~ m@^.\s*\*@) { + $in_comment = 1; + } + if ($line =~ m@/\*@) { + $in_comment = 1; + } + if (!$in_comment && $current_comment ne '') { + $current_comment = ''; + } + $current_comment .= $line . "\n" if ($in_comment); + if ($line =~ m@\*/@) { + $in_comment = 0; + } + } + + chomp($current_comment); + return($current_comment); +} +sub ctx_has_comment { + my ($first_line, $end_line) = @_; + my $cmt = ctx_locate_comment($first_line, $end_line); + + ##print "LINE: $rawlines[$end_line - 1 ]\n"; + ##print "CMMT: $cmt\n"; + + return ($cmt ne ''); +} + +sub raw_line { + my ($linenr, $cnt) = @_; + + my $offset = $linenr - 1; + $cnt++; + + my $line; + while ($cnt) { + $line = $rawlines[$offset++]; + next if (defined($line) && $line =~ /^-/); + $cnt--; + } + + return $line; +} + +sub cat_vet { + my ($vet) = @_; + my ($res, $coded); + + $res = ''; + while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { + $res .= $1; + if ($2 ne '') { + $coded = sprintf("^%c", unpack('C', $2) + 64); + $res .= $coded; + } + } + $res =~ s/$/\$/; + + return $res; +} + +my $av_preprocessor = 0; +my $av_pending; +my @av_paren_type; +my $av_pend_colon; + +sub annotate_reset { + $av_preprocessor = 0; + $av_pending = '_'; + @av_paren_type = ('E'); + $av_pend_colon = 'O'; +} + +sub annotate_values { + my ($stream, $type) = @_; + + my $res; + my $var = '_' x length($stream); + my $cur = $stream; + + print "$stream\n" if ($dbg_values > 1); + + while (length($cur)) { + @av_paren_type = ('E') if ($#av_paren_type < 0); + print " <" . join('', @av_paren_type) . + "> <$type> <$av_pending>" if ($dbg_values > 1); + if ($cur =~ /^(\s+)/o) { + print "WS($1)\n" if ($dbg_values > 1); + if ($1 =~ /\n/ && $av_preprocessor) { + $type = pop(@av_paren_type); + $av_preprocessor = 0; + } + + } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { + print "CAST($1)\n" if ($dbg_values > 1); + push(@av_paren_type, $type); + $type = 'c'; + + } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { + print "DECLARE($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^($Modifier)\s*/) { + print "MODIFIER($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { + print "DEFINE($1,$2)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + if ($2 ne '') { + $av_pending = 'N'; + } + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { + print "UNDEF($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + + } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { + print "PRE_START($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { + print "PRE_RESTART($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $av_paren_type[$#av_paren_type]); + + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:endif))/o) { + print "PRE_END($1)\n" if ($dbg_values > 1); + + $av_preprocessor = 1; + + # Assume all arms of the conditional end as this + # one does, and continue as if the #endif was not here. + pop(@av_paren_type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\\\n)/o) { + print "PRECONT($1)\n" if ($dbg_values > 1); + + } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { + print "ATTR($1)\n" if ($dbg_values > 1); + $av_pending = $type; + $type = 'N'; + + } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { + print "SIZEOF($1)\n" if ($dbg_values > 1); + if (defined $2) { + $av_pending = 'V'; + } + $type = 'N'; + + } elsif ($cur =~ /^(if|while|for)\b/o) { + print "COND($1)\n" if ($dbg_values > 1); + $av_pending = 'E'; + $type = 'N'; + + } elsif ($cur =~/^(case)/o) { + print "CASE($1)\n" if ($dbg_values > 1); + $av_pend_colon = 'C'; + $type = 'N'; + + } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { + print "KEYWORD($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(\()/o) { + print "PAREN('$1')\n" if ($dbg_values > 1); + push(@av_paren_type, $av_pending); + $av_pending = '_'; + $type = 'N'; + + } elsif ($cur =~ /^(\))/o) { + my $new_type = pop(@av_paren_type); + if ($new_type ne '_') { + $type = $new_type; + print "PAREN('$1') -> $type\n" + if ($dbg_values > 1); + } else { + print "PAREN('$1')\n" if ($dbg_values > 1); + } + + } elsif ($cur =~ /^($Ident)\s*\(/o) { + print "FUNC($1)\n" if ($dbg_values > 1); + $type = 'V'; + $av_pending = 'V'; + + } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { + if (defined $2 && $type eq 'C' || $type eq 'T') { + $av_pend_colon = 'B'; + } elsif ($type eq 'E') { + $av_pend_colon = 'L'; + } + print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Ident|$Constant)/o) { + print "IDENT($1)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Assignment)/o) { + print "ASSIGN($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~/^(;|{|})/) { + print "END($1)\n" if ($dbg_values > 1); + $type = 'E'; + $av_pend_colon = 'O'; + + } elsif ($cur =~/^(,)/) { + print "COMMA($1)\n" if ($dbg_values > 1); + $type = 'C'; + + } elsif ($cur =~ /^(\?)/o) { + print "QUESTION($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(:)/o) { + print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); + + substr($var, length($res), 1, $av_pend_colon); + if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { + $type = 'E'; + } else { + $type = 'N'; + } + $av_pend_colon = 'O'; + + } elsif ($cur =~ /^(\[)/o) { + print "CLOSE($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { + my $variant; + + print "OPV($1)\n" if ($dbg_values > 1); + if ($type eq 'V') { + $variant = 'B'; + } else { + $variant = 'U'; + } + + substr($var, length($res), 1, $variant); + $type = 'N'; + + } elsif ($cur =~ /^($Operators)/o) { + print "OP($1)\n" if ($dbg_values > 1); + if ($1 ne '++' && $1 ne '--') { + $type = 'N'; + } + + } elsif ($cur =~ /(^.)/o) { + print "C($1)\n" if ($dbg_values > 1); + } + if (defined $1) { + $cur = substr($cur, length($1)); + $res .= $type x length($1); + } + } + + return ($res, $var); +} + +sub possible { + my ($possible, $line) = @_; + my $notPermitted = qr{(?: + ^(?: + $Modifier| + $Storage| + $Type| + DEFINE_\S+ + )$| + ^(?: + goto| + return| + case| + else| + asm|__asm__| + do| + \#| + \#\#| + )(?:\s|$)| + ^(?:typedef|struct|enum)\b + )}x; + warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); + if ($possible !~ $notPermitted) { + # Check for modifiers. + $possible =~ s/\s*$Storage\s*//g; + $possible =~ s/\s*$Sparse\s*//g; + if ($possible =~ /^\s*$/) { + + } elsif ($possible =~ /\s/) { + $possible =~ s/\s*$Type\s*//g; + for my $modifier (split(' ', $possible)) { + if ($modifier !~ $notPermitted) { + warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); + push(@modifierListFile, $modifier); + } + } + + } else { + warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); + push(@typeListFile, $possible); + } + build_types(); + } else { + warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); + } +} + +my $prefix = ''; + +sub show_type { + my ($type) = @_; + + return defined $use_type{$type} if (scalar keys %use_type > 0); + + return !defined $ignore_type{$type}; +} + +sub report { + my ($level, $type, $msg) = @_; + + if (!show_type($type) || + (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { + return 0; + } + my $output = ''; + if (-t STDOUT && $color) { + if ($level eq 'ERROR') { + $output .= RED; + } elsif ($level eq 'WARNING') { + $output .= YELLOW; + } else { + $output .= GREEN; + } + } + $output .= $prefix . $level . ':'; + if ($show_types) { + $output .= BLUE if (-t STDOUT && $color); + $output .= "$type:"; + } + $output .= RESET if (-t STDOUT && $color); + $output .= ' ' . $msg . "\n"; + + if ($showfile) { + my @lines = split("\n", $output, -1); + splice(@lines, 1, 1); + $output = join("\n", @lines); + } + $output = (split('\n', $output))[0] . "\n" if ($terse); + + push(our @report, $output); + + return 1; +} + +sub report_dump { + our @report; +} + +sub fixup_current_range { + my ($lineRef, $offset, $length) = @_; + + if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { + my $o = $1; + my $l = $2; + my $no = $o + $offset; + my $nl = $l + $length; + $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; + } +} + +sub fix_inserted_deleted_lines { + my ($linesRef, $insertedRef, $deletedRef) = @_; + + my $range_last_linenr = 0; + my $delta_offset = 0; + + my $old_linenr = 0; + my $new_linenr = 0; + + my $next_insert = 0; + my $next_delete = 0; + + my @lines = (); + + my $inserted = @{$insertedRef}[$next_insert++]; + my $deleted = @{$deletedRef}[$next_delete++]; + + foreach my $old_line (@{$linesRef}) { + my $save_line = 1; + my $line = $old_line; #don't modify the array + if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename + $delta_offset = 0; + } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk + $range_last_linenr = $new_linenr; + fixup_current_range(\$line, $delta_offset, 0); + } + + while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { + $deleted = @{$deletedRef}[$next_delete++]; + $save_line = 0; + fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); + } + + while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { + push(@lines, ${$inserted}{'LINE'}); + $inserted = @{$insertedRef}[$next_insert++]; + $new_linenr++; + fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); + } + + if ($save_line) { + push(@lines, $line); + $new_linenr++; + } + + $old_linenr++; + } + + return @lines; +} + +sub fix_insert_line { + my ($linenr, $line) = @_; + + my $inserted = { + LINENR => $linenr, + LINE => $line, + }; + push(@fixed_inserted, $inserted); +} + +sub fix_delete_line { + my ($linenr, $line) = @_; + + my $deleted = { + LINENR => $linenr, + LINE => $line, + }; + + push(@fixed_deleted, $deleted); +} + +sub ERROR { + my ($type, $msg) = @_; + + if (report("ERROR", $type, $msg)) { + our $clean = 0; + our $cnt_error++; + return 1; + } + return 0; +} +sub WARN { + my ($type, $msg) = @_; + + if (report("WARNING", $type, $msg)) { + our $clean = 0; + our $cnt_warn++; + return 1; + } + return 0; +} +sub CHK { + my ($type, $msg) = @_; + + if ($check && report("CHECK", $type, $msg)) { + our $clean = 0; + our $cnt_chk++; + return 1; + } + return 0; +} + +sub check_absolute_file { + my ($absolute, $herecurr) = @_; + my $file = $absolute; + + ##print "absolute<$absolute>\n"; + + # See if any suffix of this path is a path within the tree. + while ($file =~ s@^[^/]*/@@) { + if (-f "$root/$file") { + ##print "file<$file>\n"; + last; + } + } + if (! -f _) { + return 0; + } + + # It is, so see if the prefix is acceptable. + my $prefix = $absolute; + substr($prefix, -length($file)) = ''; + + ##print "prefix<$prefix>\n"; + if ($prefix ne ".../") { + WARN("USE_RELATIVE_PATH", + "use relative pathname instead of absolute in changelog text\n" . $herecurr); + } +} + +sub trim { + my ($string) = @_; + + $string =~ s/^\s+|\s+$//g; + + return $string; +} + +sub ltrim { + my ($string) = @_; + + $string =~ s/^\s+//; + + return $string; +} + +sub rtrim { + my ($string) = @_; + + $string =~ s/\s+$//; + + return $string; +} + +sub string_find_replace { + my ($string, $find, $replace) = @_; + + $string =~ s/$find/$replace/g; + + return $string; +} + +sub tabify { + my ($leading) = @_; + + my $source_indent = 8; + my $max_spaces_before_tab = $source_indent - 1; + my $spaces_to_tab = " " x $source_indent; + + #convert leading spaces to tabs + 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; + #Remove spaces before a tab + 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; + + return "$leading"; +} + +sub pos_last_openparen { + my ($line) = @_; + + my $pos = 0; + + my $opens = $line =~ tr/\(/\(/; + my $closes = $line =~ tr/\)/\)/; + + my $last_openparen = 0; + + if (($opens == 0) || ($closes >= $opens)) { + return -1; + } + + my $len = length($line); + + for ($pos = 0; $pos < $len; $pos++) { + my $string = substr($line, $pos); + if ($string =~ /^($FuncArg|$balanced_parens)/) { + $pos += length($1) - 1; + } elsif (substr($line, $pos, 1) eq '(') { + $last_openparen = $pos; + } elsif (index($string, '(') == -1) { + last; + } + } + + return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; +} + +sub process { + my $filename = shift; + + my $linenr=0; + my $prevline=""; + my $prevrawline=""; + my $stashline=""; + my $stashrawline=""; + + my $length; + my $indent; + my $previndent=0; + my $stashindent=0; + + our $clean = 1; + my $signoff = 0; + my $is_patch = 0; + my $in_header_lines = $file ? 0 : 1; + my $in_commit_log = 0; #Scanning lines before patch + my $commit_log_possible_stack_dump = 0; + my $commit_log_long_line = 0; + my $commit_log_has_diff = 0; + my $reported_maintainer_file = 0; + my $non_utf8_charset = 0; + + my $last_blank_line = 0; + my $last_coalesced_string_linenr = -1; + + our @report = (); + our $cnt_lines = 0; + our $cnt_error = 0; + our $cnt_warn = 0; + our $cnt_chk = 0; + + # Trace the real file/line as we go. + my $realfile = ''; + my $realline = 0; + my $realcnt = 0; + my $here = ''; + my $in_comment = 0; + my $comment_edge = 0; + my $first_line = 0; + my $p1_prefix = ''; + + my $prev_values = 'E'; + + # suppression flags + my %suppress_ifbraces; + my %suppress_whiletrailers; + my %suppress_export; + my $suppress_statement = 0; + + my %signatures = (); + + # Pre-scan the patch sanitizing the lines. + # Pre-scan the patch looking for any __setup documentation. + # + my @setup_docs = (); + my $setup_docs = 0; + + my $camelcase_file_seeded = 0; + + sanitise_line_reset(); + my $line; + foreach my $rawline (@rawlines) { + $linenr++; + $line = $rawline; + + push(@fixed, $rawline) if ($fix); + + if ($rawline=~/^\+\+\+\s+(\S+)/) { + $setup_docs = 0; + if ($1 =~ m@Documentation/kernel-parameters.txt$@) { + $setup_docs = 1; + } + #next; + } + if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + $in_comment = 0; + + # Guestimate if this is a continuing comment. Run + # the context looking for a comment "edge". If this + # edge is a close comment then we must be in a comment + # at context start. + my $edge; + my $cnt = $realcnt; + for (my $ln = $linenr + 1; $cnt > 0; $ln++) { + next if (defined $rawlines[$ln - 1] && + $rawlines[$ln - 1] =~ /^-/); + $cnt--; + #print "RAW<$rawlines[$ln - 1]>\n"; + last if (!defined $rawlines[$ln - 1]); + if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && + $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { + ($edge) = $1; + last; + } + } + if (defined $edge && $edge eq '*/') { + $in_comment = 1; + } + + # Guestimate if this is a continuing comment. If this + # is the start of a diff block and this line starts + # ' *' then it is very likely a comment. + if (!defined $edge && + $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) + { + $in_comment = 1; + } + + ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; + sanitise_line_reset($in_comment); + + } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { + # Standardise the strings and chars within the input to + # simplify matching -- only bother with positive lines. + $line = sanitise_line($rawline); + } + push(@lines, $line); + + if ($realcnt > 1) { + $realcnt-- if ($line =~ /^(?:\+| |$)/); + } else { + $realcnt = 0; + } + + #print "==>$rawline\n"; + #print "-->$line\n"; + + if ($setup_docs && $line =~ /^\+/) { + push(@setup_docs, $line); + } + } + + $prefix = ''; + + $realcnt = 0; + $linenr = 0; + $fixlinenr = -1; + foreach my $line (@lines) { + $linenr++; + $fixlinenr++; + my $sline = $line; #copy of $line + $sline =~ s/$;/ /g; #with comments as spaces + + my $rawline = $rawlines[$linenr - 1]; + +#extract the line range in the file after the patch is applied + if (!$in_commit_log && + $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $is_patch = 1; + $first_line = $linenr + 1; + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + annotate_reset(); + $prev_values = 'E'; + + %suppress_ifbraces = (); + %suppress_whiletrailers = (); + %suppress_export = (); + $suppress_statement = 0; + next; + +# track the line number as we move through the hunk, note that +# new versions of GNU diff omit the leading space on completely +# blank context lines so we need to count that too. + } elsif ($line =~ /^( |\+|$)/) { + $realline++; + $realcnt-- if ($realcnt != 0); + + # Measure the line length and indent. + ($length, $indent) = line_stats($rawline); + + # Track the previous line. + ($prevline, $stashline) = ($stashline, $line); + ($previndent, $stashindent) = ($stashindent, $indent); + ($prevrawline, $stashrawline) = ($stashrawline, $rawline); + + #warn "line<$line>\n"; + + } elsif ($realcnt == 1) { + $realcnt--; + } + + my $hunk_line = ($realcnt != 0); + + $here = "#$linenr: " if (!$file); + $here = "#$realline: " if ($file); + + my $found_file = 0; + # extract the filename as it passes + if ($line =~ /^diff --git.*?(\S+)$/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@ if (!$file); + $in_commit_log = 0; + $found_file = 1; + } elsif ($line =~ /^\+\+\+\s+(\S+)/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@ if (!$file); + $in_commit_log = 0; + + $p1_prefix = $1; + if (!$file && $tree && $p1_prefix ne '' && + -e "$root/$p1_prefix") { + WARN("PATCH_PREFIX", + "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); + } + + if ($realfile =~ m@^include/asm/@) { + ERROR("MODIFIED_INCLUDE_ASM", + "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n"); + } + $found_file = 1; + } + +#make up the handle for any error we report on this line + if ($showfile) { + $prefix = "$realfile:$realline: " + } elsif ($emacs) { + if ($file) { + $prefix = "$filename:$realline: "; + } else { + $prefix = "$filename:$linenr: "; + } + } + + if ($found_file) { + if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { + $check = 1; + } else { + $check = $check_orig; + } + next; + } + + $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); + + my $hereline = "$here\n$rawline\n"; + my $herecurr = "$here\n$rawline\n"; + my $hereprev = "$here\n$prevrawline\n$rawline\n"; + + $cnt_lines++ if ($realcnt != 0); + +# Check if the commit log has what seems like a diff which can confuse patch + if ($in_commit_log && !$commit_log_has_diff && + (($line =~ m@^\s+diff\b.*a/[\w/]+@ && + $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) || + $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || + $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { + ERROR("DIFF_IN_COMMIT_MSG", + "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); + $commit_log_has_diff = 1; + } + +# Check for incorrect file permissions + if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { + my $permhere = $here . "FILE: $realfile\n"; + if ($realfile !~ m@scripts/@ && + $realfile !~ /\.(py|pl|awk|sh)$/) { + ERROR("EXECUTE_PERMISSIONS", + "do not set execute permissions for source files\n" . $permhere); + } + } + +# Check the patch for a signoff: + if ($line =~ /^\s*signed-off-by:/i) { + $signoff++; + $in_commit_log = 0; + } + +# Check if MAINTAINERS is being updated. If so, there's probably no need to +# emit the "does MAINTAINERS need updating?" message on file add/move/delete + if ($line =~ /^\s*MAINTAINERS\s*\|/) { + $reported_maintainer_file = 1; + } + +# Check signature styles + if (!$in_header_lines && + $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { + my $space_before = $1; + my $sign_off = $2; + my $space_after = $3; + my $email = $4; + my $ucfirst_sign_off = ucfirst(lc($sign_off)); + + if ($sign_off !~ /$signature_tags/) { + WARN("BAD_SIGN_OFF", + "Non-standard signature: $sign_off\n" . $herecurr); + } + if (defined $space_before && $space_before ne "") { + if (WARN("BAD_SIGN_OFF", + "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + } + if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { + if (WARN("BAD_SIGN_OFF", + "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + + } + if (!defined $space_after || $space_after ne " ") { + if (WARN("BAD_SIGN_OFF", + "Use a single space after $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + } + + my ($email_name, $email_address, $comment) = parse_email($email); + my $suggested_email = format_email(($email_name, $email_address)); + if ($suggested_email eq "") { + ERROR("BAD_SIGN_OFF", + "Unrecognized email address: '$email'\n" . $herecurr); + } else { + my $dequoted = $suggested_email; + $dequoted =~ s/^"//; + $dequoted =~ s/" </ </; + # Don't force email to have quotes + # Allow just an angle bracketed address + if ("$dequoted$comment" ne $email && + "<$email_address>$comment" ne $email && + "$suggested_email$comment" ne $email) { + WARN("BAD_SIGN_OFF", + "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); + } + } + +# Check for duplicate signatures + my $sig_nospace = $line; + $sig_nospace =~ s/\s//g; + $sig_nospace = lc($sig_nospace); + if (defined $signatures{$sig_nospace}) { + WARN("BAD_SIGN_OFF", + "Duplicate signature\n" . $herecurr); + } else { + $signatures{$sig_nospace} = 1; + } + } + +# Check email subject for common tools that don't need to be mentioned + if ($in_header_lines && + $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { + WARN("EMAIL_SUBJECT", + "A patch subject line should describe the change not the tool that found it\n" . $herecurr); + } + +# Check for old stable address + if ($line =~ /^\s*cc:\s*.*<?\bstable\@kernel\.org\b>?.*$/i) { + ERROR("STABLE_ADDRESS", + "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr); + } + +# Check for unwanted Gerrit info + if ($in_commit_log && $line =~ /^\s*change-id:/i) { + ERROR("GERRIT_CHANGE_ID", + "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); + } + +# Check if the commit log is in a possible stack dump + if ($in_commit_log && !$commit_log_possible_stack_dump && + ($line =~ /^\s*(?:WARNING:|BUG:)/ || + $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || + # timestamp + $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) { + # stack dump address + $commit_log_possible_stack_dump = 1; + } + +# Check for line lengths > 75 in commit log, warn once + if ($in_commit_log && !$commit_log_long_line && + length($line) > 75 && + !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || + # file delta changes + $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || + # filename then : + $line =~ /^\s*(?:Fixes:|Link:)/i || + # A Fixes: or Link: line + $commit_log_possible_stack_dump)) { + WARN("COMMIT_LOG_LONG_LINE", + "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); + $commit_log_long_line = 1; + } + +# Reset possible stack dump if a blank line is found + if ($in_commit_log && $commit_log_possible_stack_dump && + $line =~ /^\s*$/) { + $commit_log_possible_stack_dump = 0; + } + +# Check for git id commit length and improperly formed commit descriptions + if ($in_commit_log && !$commit_log_possible_stack_dump && + ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || + ($line =~ /\b[0-9a-f]{12,40}\b/i && + $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && + $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { + my $init_char = "c"; + my $orig_commit = ""; + my $short = 1; + my $long = 0; + my $case = 1; + my $space = 1; + my $hasdesc = 0; + my $hasparens = 0; + my $id = '0123456789ab'; + my $orig_desc = "commit description"; + my $description = ""; + + if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { + $init_char = $1; + $orig_commit = lc($2); + } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) { + $orig_commit = lc($1); + } + + $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i); + $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i); + $space = 0 if ($line =~ /\bcommit [0-9a-f]/i); + $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); + if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) { + $orig_desc = $1; + $hasparens = 1; + } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i && + defined $rawlines[$linenr] && + $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) { + $orig_desc = $1; + $hasparens = 1; + } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i && + defined $rawlines[$linenr] && + $rawlines[$linenr] =~ /^\s*[^"]+"\)/) { + $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i; + $orig_desc = $1; + $rawlines[$linenr] =~ /^\s*([^"]+)"\)/; + $orig_desc .= " " . $1; + $hasparens = 1; + } + + ($id, $description) = git_commit_info($orig_commit, + $id, $orig_desc); + + if ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens) { + ERROR("GIT_COMMIT_ID", + "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); + } + } + +# Check for added, moved or deleted files + if (!$reported_maintainer_file && !$in_commit_log && + ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || + $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || + ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && + (defined($1) || defined($2))))) { + $reported_maintainer_file = 1; + WARN("FILE_PATH_CHANGES", + "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); + } + +# Check for wrappage within a valid hunk of the file + if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { + ERROR("CORRUPTED_PATCH", + "patch seems to be corrupt (line wrapped?)\n" . + $herecurr) if (!$emitted_corrupt++); + } + +# Check for absolute kernel paths. + if ($tree) { + while ($line =~ m{(?:^|\s)(/\S*)}g) { + my $file = $1; + + if ($file =~ m{^(.*?)(?::\d+)+:?$} && + check_absolute_file($1, $herecurr)) { + # + } else { + check_absolute_file($file, $herecurr); + } + } + } + +# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php + if (($realfile =~ /^$/ || $line =~ /^\+/) && + $rawline !~ m/^$UTF8*$/) { + my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); + + my $blank = copy_spacing($rawline); + my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; + my $hereptr = "$hereline$ptr\n"; + + CHK("INVALID_UTF8", + "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); + } + +# Check if it's the start of a commit log +# (not a header line and we haven't seen the patch filename) + if ($in_header_lines && $realfile =~ /^$/ && + !($rawline =~ /^\s+\S/ || + $rawline =~ /^(commit\b|from\b|[\w-]+:).*$/i)) { + $in_header_lines = 0; + $in_commit_log = 1; + } + +# Check if there is UTF-8 in a commit log when a mail header has explicitly +# declined it, i.e defined some charset where it is missing. + if ($in_header_lines && + $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && + $1 !~ /utf-8/i) { + $non_utf8_charset = 1; + } + + if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && + $rawline =~ /$NON_ASCII_UTF8/) { + WARN("UTF8_BEFORE_PATCH", + "8-bit UTF-8 used in possible commit log\n" . $herecurr); + } + +# Check for various typo / spelling mistakes + if (defined($misspellings) && + ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { + while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) { + my $typo = $1; + my $typo_fix = $spelling_fix{lc($typo)}; + $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); + $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); + my $msg_type = \&WARN; + $msg_type = \&CHK if ($file); + if (&{$msg_type}("TYPO_SPELLING", + "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; + } + } + } + +# ignore non-hunk lines and lines being removed + next if (!$hunk_line || $line =~ /^-/); + +#trailing whitespace + if ($line =~ /^\+.*\015/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (ERROR("DOS_LINE_ENDINGS", + "DOS line endings\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/[\s\015]+$//; + } + } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (ERROR("TRAILING_WHITESPACE", + "trailing whitespace\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+$//; + } + + $rpt_cleaners = 1; + } + +# Check for FSF mailing addresses. + if ($rawline =~ /\bwrite to the Free/i || + $rawline =~ /\b59\s+Temple\s+Pl/i || + $rawline =~ /\b51\s+Franklin\s+St/i) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + my $msg_type = \&ERROR; + $msg_type = \&CHK if ($file); + &{$msg_type}("FSF_MAILING_ADDRESS", + "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) + } + +# check for Kconfig help text having a real description +# Only applies when adding the entry originally, after that we do not have +# sufficient context to determine whether it is indeed long enough. + if ($realfile =~ /Kconfig/ && + $line =~ /^\+\s*config\s+/) { + my $length = 0; + my $cnt = $realcnt; + my $ln = $linenr + 1; + my $f; + my $is_start = 0; + my $is_end = 0; + for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) { + $f = $lines[$ln - 1]; + $cnt-- if ($lines[$ln - 1] !~ /^-/); + $is_end = $lines[$ln - 1] =~ /^\+/; + + next if ($f =~ /^-/); + last if (!$file && $f =~ /^\@\@/); + + if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { + $is_start = 1; + } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { + $length = -1; + } + + $f =~ s/^.//; + $f =~ s/#.*//; + $f =~ s/^\s+//; + next if ($f =~ /^$/); + if ($f =~ /^\s*config\s/) { + $is_end = 1; + last; + } + $length++; + } + if ($is_start && $is_end && $length < $min_conf_desc_length) { + WARN("CONFIG_DESCRIPTION", + "please write a paragraph that describes the config symbol fully\n" . $herecurr); + } + #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; + } + +# discourage the addition of CONFIG_EXPERIMENTAL in Kconfig. + if ($realfile =~ /Kconfig/ && + $line =~ /.\s*depends on\s+.*\bEXPERIMENTAL\b/) { + WARN("CONFIG_EXPERIMENTAL", + "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); + } + +# discourage the use of boolean for type definition attributes of Kconfig options + if ($realfile =~ /Kconfig/ && + $line =~ /^\+\s*\bboolean\b/) { + WARN("CONFIG_TYPE_BOOLEAN", + "Use of boolean is deprecated, please use bool instead.\n" . $herecurr); + } + + if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && + ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { + my $flag = $1; + my $replacement = { + 'EXTRA_AFLAGS' => 'asflags-y', + 'EXTRA_CFLAGS' => 'ccflags-y', + 'EXTRA_CPPFLAGS' => 'cppflags-y', + 'EXTRA_LDFLAGS' => 'ldflags-y', + }; + + WARN("DEPRECATED_VARIABLE", + "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); + } + +# check for DT compatible documentation + if (defined $root && + (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || + ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { + + my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; + + my $dt_path = $root . "/Documentation/devicetree/bindings/"; + my $vp_file = $dt_path . "vendor-prefixes.txt"; + + foreach my $compat (@compats) { + my $compat2 = $compat; + $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/; + my $compat3 = $compat; + $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/; + `grep -Erq "$compat|$compat2|$compat3" $dt_path`; + if ( $? >> 8 ) { + WARN("UNDOCUMENTED_DT_STRING", + "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); + } + + next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; + my $vendor = $1; + `grep -Eq "^$vendor\\b" $vp_file`; + if ( $? >> 8 ) { + WARN("UNDOCUMENTED_DT_STRING", + "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); + } + } + } + +# check we are in a valid source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c|s|S|pl|sh|dtsi|dts)$/); + +# line length limit (with some exclusions) +# +# There are a few types of lines that may extend beyond $max_line_length: +# logging functions like pr_info that end in a string +# lines with a single string +# #defines that are a single string +# +# There are 3 different line length message types: +# LONG_LINE_COMMENT a comment starts before but extends beyond $max_linelength +# LONG_LINE_STRING a string starts before but extends beyond $max_line_length +# LONG_LINE all other lines longer than $max_line_length +# +# if LONG_LINE is ignored, the other 2 types are also ignored +# + + if ($line =~ /^\+/ && $length > $max_line_length) { + my $msg_type = "LONG_LINE"; + + # Check the allowed long line types first + + # logging functions that end in a string that starts + # before $max_line_length + if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = ""; + + # lines with only strings (w/ possible termination) + # #defines with only strings + } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || + $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { + $msg_type = ""; + + # Otherwise set the alternate message types + + # a comment starts before $max_line_length + } elsif ($line =~ /($;[\s$;]*)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = "LONG_LINE_COMMENT" + + # a quoted string starts before $max_line_length + } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = "LONG_LINE_STRING" + } + + if ($msg_type ne "" && + (show_type("LONG_LINE") || show_type($msg_type))) { + WARN($msg_type, + "line over $max_line_length characters\n" . $herecurr); + } + } + +# check for adding lines without a newline. + if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { + WARN("MISSING_EOF_NEWLINE", + "adding a line without newline at end of file\n" . $herecurr); + } + +# Blackfin: use hi/lo macros + if ($realfile =~ m@arch/blackfin/.*\.S$@) { + if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("LO_MACRO", + "use the LO() macro, not (... & 0xFFFF)\n" . $herevet); + } + if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("HI_MACRO", + "use the HI() macro, not (... >> 16)\n" . $herevet); + } + } + +# check we are in a valid source file C or perl if not then ignore this hunk + next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); + +# at the beginning of a line any tabs must come first and anything +# more than 8 must use tabs. + if ($rawline =~ /^\+\s* \t\s*\S/ || + $rawline =~ /^\+\s* \s*/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + $rpt_cleaners = 1; + if (ERROR("CODE_INDENT", + "code indent should use tabs where possible\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } + } + +# check for space before tabs. + if ($rawline =~ /^\+/ && $rawline =~ / \t/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (WARN("SPACE_BEFORE_TAB", + "please, no space before tabs\n" . $herevet) && + $fix) { + while ($fixed[$fixlinenr] =~ + s/(^\+.*) {8,8}\t/$1\t\t/) {} + while ($fixed[$fixlinenr] =~ + s/(^\+.*) +\t/$1\t/) {} + } + } + +# check for && or || at the start of a line + if ($rawline =~ /^\+\s*(&&|\|\|)/) { + CHK("LOGICAL_CONTINUATIONS", + "Logical continuations should be on the previous line\n" . $hereprev); + } + +# check multi-line statement indentation matches previous line + if ($^V && $^V ge 5.10.0 && + $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { + $prevline =~ /^\+(\t*)(.*)$/; + my $oldindent = $1; + my $rest = $2; + + my $pos = pos_last_openparen($rest); + if ($pos >= 0) { + $line =~ /^(\+| )([ \t]*)/; + my $newindent = $2; + + my $goodtabindent = $oldindent . + "\t" x ($pos / 8) . + " " x ($pos % 8); + my $goodspaceindent = $oldindent . " " x $pos; + + if ($newindent ne $goodtabindent && + $newindent ne $goodspaceindent) { + + if (CHK("PARENTHESIS_ALIGNMENT", + "Alignment should match open parenthesis\n" . $hereprev) && + $fix && $line =~ /^\+/) { + $fixed[$fixlinenr] =~ + s/^\+[ \t]*/\+$goodtabindent/; + } + } + } + } + +# check for space after cast like "(int) foo" or "(struct foo) bar" +# avoid checking a few false positives: +# "sizeof(<type>)" or "__alignof__(<type>)" +# function pointer declarations like "(*foo)(int) = bar;" +# structure definitions like "(struct foo) { 0 };" +# multiline macros that define functions +# known attributes or the __attribute__ keyword + if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && + (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { + if (CHK("SPACING", + "No space is necessary after a cast\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/(\(\s*$Type\s*\))[ \t]+/$1/; + } + } + +# Block comment styles +# Networking with an initial /* + if ($realfile =~ m@^(drivers/net/|net/)@ && + $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && + $rawline =~ /^\+[ \t]*\*/ && + $realline > 2) { + WARN("NETWORKING_BLOCK_COMMENT_STYLE", + "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); + } + +# Block comments use * on subsequent lines + if ($prevline =~ /$;[ \t]*$/ && #ends in comment + $prevrawline =~ /^\+.*?\/\*/ && #starting /* + $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ + $rawline =~ /^\+/ && #line is new + $rawline !~ /^\+[ \t]*\*/) { #no leading * + WARN("BLOCK_COMMENT_STYLE", + "Block comments use * on subsequent lines\n" . $hereprev); + } + +# Block comments use */ on trailing lines + if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ + $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ + $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ + $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ + WARN("BLOCK_COMMENT_STYLE", + "Block comments use a trailing */ on a separate line\n" . $herecurr); + } + +# check for missing blank lines after struct/union declarations +# with exceptions for various attributes and macros + if ($prevline =~ /^[\+ ]};?\s*$/ && + $line =~ /^\+/ && + !($line =~ /^\+\s*$/ || + $line =~ /^\+\s*EXPORT_SYMBOL/ || + $line =~ /^\+\s*MODULE_/i || + $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || + $line =~ /^\+[a-z_]*init/ || + $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || + $line =~ /^\+\s*DECLARE/ || + $line =~ /^\+\s*__setup/)) { + if (CHK("LINE_SPACING", + "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && + $fix) { + fix_insert_line($fixlinenr, "\+"); + } + } + +# check for multiple consecutive blank lines + if ($prevline =~ /^[\+ ]\s*$/ && + $line =~ /^\+\s*$/ && + $last_blank_line != ($linenr - 1)) { + if (CHK("LINE_SPACING", + "Please don't use multiple blank lines\n" . $hereprev) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } + + $last_blank_line = $linenr; + } + +# check for missing blank lines after declarations + if ($sline =~ /^\+\s+\S/ && #Not at char 1 + # actual declarations + ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || + # function pointer declarations + $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + # foo bar; where foo is some local typedef or #define + $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + # known declaration macros + $prevline =~ /^\+\s+$declaration_macros/) && + # for "else if" which can look like "$Ident $Ident" + !($prevline =~ /^\+\s+$c90_Keywords\b/ || + # other possible extensions of declaration lines + $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || + # not starting a section or a macro "\" extended line + $prevline =~ /(?:\{\s*|\\)$/) && + # looks like a declaration + !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || + # function pointer declarations + $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + # foo bar; where foo is some local typedef or #define + $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + # known declaration macros + $sline =~ /^\+\s+$declaration_macros/ || + # start of struct or union or enum + $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || + # start or end of block or continuation of declaration + $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || + # bitfield continuation + $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || + # other possible extensions of declaration lines + $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && + # indentation of previous and current line are the same + (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { + if (WARN("LINE_SPACING", + "Missing a blank line after declarations\n" . $hereprev) && + $fix) { + fix_insert_line($fixlinenr, "\+"); + } + } + +# check for spaces at the beginning of a line. +# Exceptions: +# 1) within comments +# 2) indented preprocessor commands +# 3) hanging labels + if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (WARN("LEADING_SPACE", + "please, no spaces at the start of a line\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } + } + +# check we are in a valid C source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c)$/); + +# check indentation of any line with a bare else +# (but not if it is a multiple line "if (foo) return bar; else return baz;") +# if the previous line is a break or return and is indented 1 tab more... + if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { + my $tabs = length($1) + 1; + if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || + ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && + defined $lines[$linenr] && + $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { + WARN("UNNECESSARY_ELSE", + "else is not generally useful after a break or return\n" . $hereprev); + } + } + +# check indentation of a line with a break; +# if the previous line is a goto or return and is indented the same # of tabs + if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { + my $tabs = $1; + if ($prevline =~ /^\+$tabs(?:goto|return)\b/) { + WARN("UNNECESSARY_BREAK", + "break is not useful after a goto or return\n" . $hereprev); + } + } + +# discourage the addition of CONFIG_EXPERIMENTAL in #if(def). + if ($line =~ /^\+\s*\#\s*if.*\bCONFIG_EXPERIMENTAL\b/) { + WARN("CONFIG_EXPERIMENTAL", + "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); + } + +# check for RCS/CVS revision markers + if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { + WARN("CVS_KEYWORD", + "CVS style keyword markers, these will _not_ be updated\n". $herecurr); + } + +# Blackfin: don't use __builtin_bfin_[cs]sync + if ($line =~ /__builtin_bfin_csync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("CSYNC", + "use the CSYNC() macro in asm/blackfin.h\n" . $herevet); + } + if ($line =~ /__builtin_bfin_ssync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("SSYNC", + "use the SSYNC() macro in asm/blackfin.h\n" . $herevet); + } + +# check for old HOTPLUG __dev<foo> section markings + if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { + WARN("HOTPLUG_SECTION", + "Using $1 is unnecessary\n" . $herecurr); + } + +# Check for potential 'bare' types + my ($stat, $cond, $line_nr_next, $remain_next, $off_next, + $realline_next); +#print "LINE<$line>\n"; + if ($linenr >= $suppress_statement && + $realcnt && $sline =~ /.\s*\S/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0); + $stat =~ s/\n./\n /g; + $cond =~ s/\n./\n /g; + +#print "linenr<$linenr> <$stat>\n"; + # If this statement has no statement boundaries within + # it there is no point in retrying a statement scan + # until we hit end of it. + my $frag = $stat; $frag =~ s/;+\s*$//; + if ($frag !~ /(?:{|;)/) { +#print "skip<$line_nr_next>\n"; + $suppress_statement = $line_nr_next; + } + + # Find the real next line. + $realline_next = $line_nr_next; + if (defined $realline_next && + (!defined $lines[$realline_next - 1] || + substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { + $realline_next++; + } + + my $s = $stat; + $s =~ s/{.*$//s; + + # Ignore goto labels. + if ($s =~ /$Ident:\*$/s) { + + # Ignore functions being called + } elsif ($s =~ /^.\s*$Ident\s*\(/s) { + + } elsif ($s =~ /^.\s*else\b/s) { + + # declarations always start with types + } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { + my $type = $1; + $type =~ s/\s+/ /g; + possible($type, "A:" . $s); + + # definitions in global scope can only start with types + } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { + possible($1, "B:" . $s); + } + + # any (foo ... *) is a pointer cast, and foo is a type + while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { + possible($1, "C:" . $s); + } + + # Check for any sort of function declaration. + # int foo(something bar, other baz); + # void (*store_gdt)(x86_descr_ptr *); + if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { + my ($name_len) = length($1); + + my $ctx = $s; + substr($ctx, 0, $name_len + 1, ''); + $ctx =~ s/\)[^\)]*$//; + + for my $arg (split(/\s*,\s*/, $ctx)) { + if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { + + possible($1, "D:" . $s); + } + } + } + + } + +# +# Checks which may be anchored in the context. +# + +# Check for switch () and associated case and default +# statements should be at the same indent. + if ($line=~/\bswitch\s*\(.*\)/) { + my $err = ''; + my $sep = ''; + my @ctx = ctx_block_outer($linenr, $realcnt); + shift(@ctx); + for my $ctx (@ctx) { + my ($clen, $cindent) = line_stats($ctx); + if ($ctx =~ /^\+\s*(case\s+|default:)/ && + $indent != $cindent) { + $err .= "$sep$ctx\n"; + $sep = ''; + } else { + $sep = "[...]\n"; + } + } + if ($err ne '') { + ERROR("SWITCH_CASE_INDENT_LEVEL", + "switch and case should be at the same indent\n$hereline$err"); + } + } + +# if/while/etc brace do not go on next line, unless defining a do while loop, +# or if that brace on the next line is for something else + if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { + my $pre_ctx = "$1$2"; + + my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); + + if ($line =~ /^\+\t{6,}/) { + WARN("DEEP_INDENTATION", + "Too many leading tabs - consider code refactoring\n" . $herecurr); + } + + my $ctx_cnt = $realcnt - $#ctx - 1; + my $ctx = join("\n", @ctx); + + my $ctx_ln = $linenr; + my $ctx_skip = $realcnt; + + while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && + defined $lines[$ctx_ln - 1] && + $lines[$ctx_ln - 1] =~ /^-/)) { + ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; + $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); + $ctx_ln++; + } + + #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; + #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; + + if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + ERROR("OPEN_BRACE", + "that open brace { should be on the previous line\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && + $ctx =~ /\)\s*\;\s*$/ && + defined $lines[$ctx_ln - 1]) + { + my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); + if ($nindent > $indent) { + WARN("TRAILING_SEMICOLON", + "trailing semicolon indicates no statements, indent implies otherwise\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + } + } + +# Check relative indent for conditionals and blocks. + if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0) + if (!defined $stat); + my ($s, $c) = ($stat, $cond); + + substr($s, 0, length($c), ''); + + # remove inline comments + $s =~ s/$;/ /g; + $c =~ s/$;/ /g; + + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + + # Make sure we remove the line prefixes as we have + # none on the first line, and are going to readd them + # where necessary. + $s =~ s/\n./\n/gs; + while ($s =~ /\n\s+\\\n/) { + $cond_lines += $s =~ s/\n\s+\\\n/\n/g; + } + + # We want to check the first line inside the block + # starting at the end of the conditional, so remove: + # 1) any blank line termination + # 2) any opening brace { on end of the line + # 3) any do (...) { + my $continuation = 0; + my $check = 0; + $s =~ s/^.*\bdo\b//; + $s =~ s/^\s*{//; + if ($s =~ s/^\s*\\//) { + $continuation = 1; + } + if ($s =~ s/^\s*?\n//) { + $check = 1; + $cond_lines++; + } + + # Also ignore a loop construct at the end of a + # preprocessor statement. + if (($prevline =~ /^.\s*#\s*define\s/ || + $prevline =~ /\\\s*$/) && $continuation == 0) { + $check = 0; + } + + my $cond_ptr = -1; + $continuation = 0; + while ($cond_ptr != $cond_lines) { + $cond_ptr = $cond_lines; + + # If we see an #else/#elif then the code + # is not linear. + if ($s =~ /^\s*\#\s*(?:else|elif)/) { + $check = 0; + } + + # Ignore: + # 1) blank lines, they should be at 0, + # 2) preprocessor lines, and + # 3) labels. + if ($continuation || + $s =~ /^\s*?\n/ || + $s =~ /^\s*#\s*?/ || + $s =~ /^\s*$Ident\s*:/) { + $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; + if ($s =~ s/^.*?\n//) { + $cond_lines++; + } + } + } + + my (undef, $sindent) = line_stats("+" . $s); + my $stat_real = raw_line($linenr, $cond_lines); + + # Check if either of these lines are modified, else + # this is not this patch's fault. + if (!defined($stat_real) || + $stat !~ /^\+/ && $stat_real !~ /^\+/) { + $check = 0; + } + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; + + if ($check && $s ne '' && + (($sindent % 8) != 0 || + ($sindent < $indent) || + ($sindent > $indent + 8))) { + WARN("SUSPECT_CODE_INDENT", + "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); + } + } + + # Track the 'values' across context and added lines. + my $opline = $line; $opline =~ s/^./ /; + my ($curr_values, $curr_vars) = + annotate_values($opline . "\n", $prev_values); + $curr_values = $prev_values . $curr_values; + if ($dbg_values) { + my $outline = $opline; $outline =~ s/\t/ /g; + print "$linenr > .$outline\n"; + print "$linenr > $curr_values\n"; + print "$linenr > $curr_vars\n"; + } + $prev_values = substr($curr_values, -1); + +#ignore lines not being added + next if ($line =~ /^[^\+]/); + +# check for declarations of signed or unsigned without int + while ($line =~ m{($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { + my $type = $1; + my $var = $2; + $var = "" if (!defined $var); + if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { + my $sign = $1; + my $pointer = $2; + + $pointer = "" if (!defined $pointer); + + if (WARN("UNSPECIFIED_INT", + "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && + $fix) { + my $decl = trim($sign) . " int "; + my $comp_pointer = $pointer; + $comp_pointer =~ s/\s//g; + $decl .= $comp_pointer; + $decl = rtrim($decl) if ($var eq ""); + $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; + } + } + } + +# TEST: allow direct testing of the type matcher. + if ($dbg_type) { + if ($line =~ /^.\s*$Declare\s*$/) { + ERROR("TEST_TYPE", + "TEST: is type\n" . $herecurr); + } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { + ERROR("TEST_NOT_TYPE", + "TEST: is not type ($1 is)\n". $herecurr); + } + next; + } +# TEST: allow direct testing of the attribute matcher. + if ($dbg_attr) { + if ($line =~ /^.\s*$Modifier\s*$/) { + ERROR("TEST_ATTR", + "TEST: is attr\n" . $herecurr); + } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { + ERROR("TEST_NOT_ATTR", + "TEST: is not attr ($1 is)\n". $herecurr); + } + next; + } + +# check for initialisation to aggregates open brace on the next line + if ($line =~ /^.\s*{/ && + $prevline =~ /(?:^|[^=])=\s*$/) { + if (ERROR("OPEN_BRACE", + "that open brace { should be on the previous line\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/\s*=\s*$/ = {/; + fix_insert_line($fixlinenr, $fixedline); + $fixedline = $line; + $fixedline =~ s/^(.\s*){\s*/$1/; + fix_insert_line($fixlinenr, $fixedline); + } + } + +# +# Checks which are anchored on the added line. +# + +# check for malformed paths in #include statements (uses RAW line) + if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { + my $path = $1; + if ($path =~ m{//}) { + ERROR("MALFORMED_INCLUDE", + "malformed #include filename\n" . $herecurr); + } + if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { + ERROR("UAPI_INCLUDE", + "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); + } + } + +# no C99 // comments + if ($line =~ m{//}) { + if (ERROR("C99_COMMENTS", + "do not use C99 // comments\n" . $herecurr) && + $fix) { + my $line = $fixed[$fixlinenr]; + if ($line =~ /\/\/(.*)$/) { + my $comment = trim($1); + $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; + } + } + } + # Remove C99 comments. + $line =~ s@//.*@@; + $opline =~ s@//.*@@; + +# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider +# the whole statement. +#print "APW <$lines[$realline_next - 1]>\n"; + if (defined $realline_next && + exists $lines[$realline_next - 1] && + !defined $suppress_export{$realline_next} && + ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { + # Handle definitions which produce identifiers with + # a prefix: + # XXX(foo); + # EXPORT_SYMBOL(something_foo); + my $name = $1; + if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && + $name =~ /^${Ident}_$2/) { +#print "FOO C name<$name>\n"; + $suppress_export{$realline_next} = 1; + + } elsif ($stat !~ /(?: + \n.}\s*$| + ^.DEFINE_$Ident\(\Q$name\E\)| + ^.DECLARE_$Ident\(\Q$name\E\)| + ^.LIST_HEAD\(\Q$name\E\)| + ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| + \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() + )/x) { +#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; + $suppress_export{$realline_next} = 2; + } else { + $suppress_export{$realline_next} = 1; + } + } + if (!defined $suppress_export{$linenr} && + $prevline =~ /^.\s*$/ && + ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { +#print "FOO B <$lines[$linenr - 1]>\n"; + $suppress_export{$linenr} = 2; + } + if (defined $suppress_export{$linenr} && + $suppress_export{$linenr} == 2) { + WARN("EXPORT_SYMBOL", + "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); + } + +# check for global initialisers. + if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) { + if (ERROR("GLOBAL_INITIALISERS", + "do not initialise globals to $1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; + } + } +# check for static initialisers. + if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { + if (ERROR("INITIALISED_STATIC", + "do not initialise statics to $1\n" . + $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; + } + } + +# check for misordered declarations of char/short/int/long with signed/unsigned + while ($sline =~ m{(\b$TypeMisordered\b)}g) { + my $tmp = trim($1); + WARN("MISORDERED_TYPE", + "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); + } + +# check for static const char * arrays. + if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "static const char * array should probably be static const char * const\n" . + $herecurr); + } + +# check for static char foo[] = "bar" declarations. + if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "static char array declaration should probably be static const char\n" . + $herecurr); + } + +# check for const <foo> const where <foo> is not a pointer or array type + if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { + my $found = $1; + if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { + WARN("CONST_CONST", + "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); + } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { + WARN("CONST_CONST", + "'const $found const' should probably be 'const $found'\n" . $herecurr); + } + } + +# check for non-global char *foo[] = {"bar", ...} declarations. + if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "char * array declaration might be better as static const\n" . + $herecurr); + } + +# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) + if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { + my $array = $1; + if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { + my $array_div = $1; + if (WARN("ARRAY_SIZE", + "Prefer ARRAY_SIZE($array)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; + } + } + } + +# check for function declarations without arguments like "int foo()" + if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { + if (ERROR("FUNCTION_WITHOUT_ARGS", + "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; + } + } + +# check for uses of DEFINE_PCI_DEVICE_TABLE + if ($line =~ /\bDEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=/) { + if (WARN("DEFINE_PCI_DEVICE_TABLE", + "Prefer struct pci_device_id over deprecated DEFINE_PCI_DEVICE_TABLE\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b(?:static\s+|)DEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=\s*/static const struct pci_device_id $1\[\] = /; + } + } + +# check for new typedefs, only function parameters and sparse annotations +# make sense. + if ($line =~ /\btypedef\s/ && + $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && + $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && + $line !~ /\b$typeTypedefs\b/ && + $line !~ /\b__bitwise(?:__|)\b/) { + WARN("NEW_TYPEDEFS", + "do not add new typedefs\n" . $herecurr); + } + +# * goes on variable not on type + # (char*[ const]) + while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { + #print "AA<$1>\n"; + my ($ident, $from, $to) = ($1, $2, $2); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + +## print "1: from<$from> to<$to> ident<$ident>\n"; + if ($from ne $to) { + if (ERROR("POINTER_LOCATION", + "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && + $fix) { + my $sub_from = $ident; + my $sub_to = $ident; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$fixlinenr] =~ + s@\Q$sub_from\E@$sub_to@; + } + } + } + while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { + #print "BB<$1>\n"; + my ($match, $from, $to, $ident) = ($1, $2, $2, $3); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + # Modifiers should have spaces. + $to =~ s/(\b$Modifier$)/$1 /; + +## print "2: from<$from> to<$to> ident<$ident>\n"; + if ($from ne $to && $ident !~ /^$Modifier$/) { + if (ERROR("POINTER_LOCATION", + "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && + $fix) { + + my $sub_from = $match; + my $sub_to = $match; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$fixlinenr] =~ + s@\Q$sub_from\E@$sub_to@; + } + } + } + +# avoid BUG() or BUG_ON() + if ($line =~ /\b(?:BUG|BUG_ON)\b/) { + my $msg_type = \&WARN; + $msg_type = \&CHK if ($file); + &{$msg_type}("AVOID_BUG", + "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); + } + +# avoid LINUX_VERSION_CODE + if ($line =~ /\bLINUX_VERSION_CODE\b/) { + WARN("LINUX_VERSION_CODE", + "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); + } + +# check for uses of printk_ratelimit + if ($line =~ /\bprintk_ratelimit\s*\(/) { + WARN("PRINTK_RATELIMITED", + "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); + } + +# printk should use KERN_* levels. Note that follow on printk's on the +# same line do not need a level, so we use the current block context +# to try and find and validate the current printk. In summary the current +# printk includes all preceding printk's which have no newline on the end. +# we assume the first bad printk is the one to report. + if ($line =~ /\bprintk\((?!KERN_)\s*"/) { + my $ok = 0; + for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { + #print "CHECK<$lines[$ln - 1]\n"; + # we have a preceding printk if it ends + # with "\n" ignore it, else it is to blame + if ($lines[$ln - 1] =~ m{\bprintk\(}) { + if ($rawlines[$ln - 1] !~ m{\\n"}) { + $ok = 1; + } + last; + } + } + if ($ok == 0) { + WARN("PRINTK_WITHOUT_KERN_LEVEL", + "printk() should include KERN_ facility level\n" . $herecurr); + } + } + + if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { + my $orig = $1; + my $level = lc($orig); + $level = "warn" if ($level eq "warning"); + my $level2 = $level; + $level2 = "dbg" if ($level eq "debug"); + WARN("PREFER_PR_LEVEL", + "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); + } + + if ($line =~ /\bpr_warning\s*\(/) { + if (WARN("PREFER_PR_LEVEL", + "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\bpr_warning\b/pr_warn/; + } + } + + if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { + my $orig = $1; + my $level = lc($orig); + $level = "warn" if ($level eq "warning"); + $level = "dbg" if ($level eq "debug"); + WARN("PREFER_DEV_LEVEL", + "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); + } + +# ENOSYS means "bad syscall nr" and nothing else. This will have a small +# number of false positives, but assembly files are not checked, so at +# least the arch entry code will not trigger this warning. + if ($line =~ /\bENOSYS\b/) { + WARN("ENOSYS", + "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); + } + +# function brace can't be on same line, except for #defines of do while, +# or if closed on same line + if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and + !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { + if (ERROR("OPEN_BRACE", + "open brace '{' following function declarations go on the next line\n" . $herecurr) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + my $fixed_line = $rawline; + $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/; + my $line1 = $1; + my $line2 = $2; + fix_insert_line($fixlinenr, ltrim($line1)); + fix_insert_line($fixlinenr, "\+{"); + if ($line2 !~ /^\s*$/) { + fix_insert_line($fixlinenr, "\+\t" . trim($line2)); + } + } + } + +# open braces for enum, union and struct go on the same line. + if ($line =~ /^.\s*{/ && + $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { + if (ERROR("OPEN_BRACE", + "open brace '{' following $1 go on the same line\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = rtrim($prevrawline) . " {"; + fix_insert_line($fixlinenr, $fixedline); + $fixedline = $rawline; + $fixedline =~ s/^(.\s*){\s*/$1\t/; + if ($fixedline !~ /^\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + } + } + +# missing space after union, struct or enum definition + if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { + if (WARN("SPACING", + "missing space after $1 definition\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; + } + } + +# Function pointer declarations +# check spacing between type, funcptr, and args +# canonical declaration is "type (*funcptr)(args...)" + if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { + my $declare = $1; + my $pre_pointer_space = $2; + my $post_pointer_space = $3; + my $funcname = $4; + my $post_funcname_space = $5; + my $pre_args_space = $6; + +# the $Declare variable will capture all spaces after the type +# so check it for a missing trailing missing space but pointer return types +# don't need a space so don't warn for those. + my $post_declare_space = ""; + if ($declare =~ /(\s+)$/) { + $post_declare_space = $1; + $declare = rtrim($declare); + } + if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { + WARN("SPACING", + "missing space after return type\n" . $herecurr); + $post_declare_space = " "; + } + +# unnecessary space "type (*funcptr)(args...)" +# This test is not currently implemented because these declarations are +# equivalent to +# int foo(int bar, ...) +# and this is form shouldn't/doesn't generate a checkpatch warning. +# +# elsif ($declare =~ /\s{2,}$/) { +# WARN("SPACING", +# "Multiple spaces after return type\n" . $herecurr); +# } + +# unnecessary space "type ( *funcptr)(args...)" + if (defined $pre_pointer_space && + $pre_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer open parenthesis\n" . $herecurr); + } + +# unnecessary space "type (* funcptr)(args...)" + if (defined $post_pointer_space && + $post_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr )(args...)" + if (defined $post_funcname_space && + $post_funcname_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr) (args...)" + if (defined $pre_args_space && + $pre_args_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer arguments\n" . $herecurr); + } + + if (show_type("SPACING") && $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; + } + } + +# check for spacing round square brackets; allowed: +# 1. with a type on the left -- int [] a; +# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, +# 3. inside a curly brace -- = { [0...10] = 5 } + while ($line =~ /(.*?\s)\[/g) { + my ($where, $prefix) = ($-[1], $1); + if ($prefix !~ /$Type\s+$/ && + ($where != 0 || $prefix !~ /^.\s+$/) && + $prefix !~ /[{,]\s+$/) { + if (ERROR("BRACKET_SPACE", + "space prohibited before open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(\+.*?)\s+\[/$1\[/; + } + } + } + +# check for spaces between functions and their parentheses. + while ($line =~ /($Ident)\s+\(/g) { + my $name = $1; + my $ctx_before = substr($line, 0, $-[1]); + my $ctx = "$ctx_before$name"; + + # Ignore those directives where spaces _are_ permitted. + if ($name =~ /^(?: + if|for|while|switch|return|case| + volatile|__volatile__| + __attribute__|format|__extension__| + asm|__asm__)$/x) + { + # cpp #define statements have non-optional spaces, ie + # if there is a space between the name and the open + # parenthesis it is simply not a parameter group. + } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { + + # cpp #elif statement condition may start with a ( + } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { + + # If this whole things ends with a type its most + # likely a typedef for a function. + } elsif ($ctx =~ /$Type$/) { + + } else { + if (WARN("SPACING", + "space prohibited between function name and open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\b$name\s+\(/$name\(/; + } + } + } + +# Check operator spacing. + if (!($line=~/\#\s*include/)) { + my $fixed_line = ""; + my $line_fixed = 0; + + my $ops = qr{ + <<=|>>=|<=|>=|==|!=| + \+=|-=|\*=|\/=|%=|\^=|\|=|&=| + =>|->|<<|>>|<|>|=|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| + \?:|\?|: + }x; + my @elements = split(/($ops|;)/, $opline); + +## print("element count: <" . $#elements . ">\n"); +## foreach my $el (@elements) { +## print("el: <$el>\n"); +## } + + my @fix_elements = (); + my $off = 0; + + foreach my $el (@elements) { + push(@fix_elements, substr($rawline, $off, length($el))); + $off += length($el); + } + + $off = 0; + + my $blank = copy_spacing($opline); + my $last_after = -1; + + for (my $n = 0; $n < $#elements; $n += 2) { + + my $good = $fix_elements[$n] . $fix_elements[$n + 1]; + +## print("n: <$n> good: <$good>\n"); + + $off += length($elements[$n]); + + # Pick up the preceding and succeeding characters. + my $ca = substr($opline, 0, $off); + my $cc = ''; + if (length($opline) >= ($off + length($elements[$n + 1]))) { + $cc = substr($opline, $off + length($elements[$n + 1])); + } + my $cb = "$ca$;$cc"; + + my $a = ''; + $a = 'V' if ($elements[$n] ne ''); + $a = 'W' if ($elements[$n] =~ /\s$/); + $a = 'C' if ($elements[$n] =~ /$;$/); + $a = 'B' if ($elements[$n] =~ /(\[|\()$/); + $a = 'O' if ($elements[$n] eq ''); + $a = 'E' if ($ca =~ /^\s*$/); + + my $op = $elements[$n + 1]; + + my $c = ''; + if (defined $elements[$n + 2]) { + $c = 'V' if ($elements[$n + 2] ne ''); + $c = 'W' if ($elements[$n + 2] =~ /^\s/); + $c = 'C' if ($elements[$n + 2] =~ /^$;/); + $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); + $c = 'O' if ($elements[$n + 2] eq ''); + $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); + } else { + $c = 'E'; + } + + my $ctx = "${a}x${c}"; + + my $at = "(ctx:$ctx)"; + + my $ptr = substr($blank, 0, $off) . "^"; + my $hereptr = "$hereline$ptr\n"; + + # Pull out the value of this operator. + my $op_type = substr($curr_values, $off + 1, 1); + + # Get the full operator variant. + my $opv = $op . substr($curr_vars, $off, 1); + + # Ignore operators passed as parameters. + if ($op_type ne 'V' && + $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { + +# # Ignore comments +# } elsif ($op =~ /^$;+$/) { + + # ; should have either the end of line or a space or \ after it + } elsif ($op eq ';') { + if ($ctx !~ /.x[WEBC]/ && + $cc !~ /^\\/ && $cc !~ /^;/) { + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } + } + + # // is a comment + } elsif ($op eq '//') { + + # : when part of a bitfield + } elsif ($opv eq ':B') { + # skip the bitfield test for now + + # No spaces for: + # -> + } elsif ($op eq '->') { + if ($ctx =~ /Wx.|.xW/) { + if (ERROR("SPACING", + "spaces prohibited around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # , must not have a space before and must have a space on the right. + } elsif ($op eq ',') { + my $rtrim_before = 0; + my $space_after = 0; + if ($ctx =~ /Wx./) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $line_fixed = 1; + $rtrim_before = 1; + } + } + if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $line_fixed = 1; + $last_after = $n; + $space_after = 1; + } + } + if ($rtrim_before || $space_after) { + if ($rtrim_before) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + } else { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); + } + if ($space_after) { + $good .= " "; + } + } + + # '*' as part of a type definition -- reported already. + } elsif ($opv eq '*_') { + #warn "'*' is part of type\n"; + + # unary operators should have a space before and + # none after. May be left adjacent to another + # unary operator, or a cast + } elsif ($op eq '!' || $op eq '~' || + $opv eq '*U' || $opv eq '-U' || + $opv eq '&U' || $opv eq '&&U') { + if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + if (ERROR("SPACING", + "space required before that '$op' $at\n" . $hereptr)) { + if ($n != $last_after + 2) { + $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + } + if ($op eq '*' && $cc =~/\s*$Modifier\b/) { + # A unary '*' may be const + + } elsif ($ctx =~ /.xW/) { + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # unary ++ and unary -- are allowed no space on one side. + } elsif ($op eq '++' or $op eq '--') { + if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { + if (ERROR("SPACING", + "space required one side of that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } + } + if ($ctx =~ /Wx[BE]/ || + ($ctx =~ /Wx./ && $cc =~ /^;/)) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + if ($ctx =~ /ExW/) { + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # << and >> may either have or not have spaces both sides + } elsif ($op eq '<<' or $op eq '>>' or + $op eq '&' or $op eq '^' or $op eq '|' or + $op eq '+' or $op eq '-' or + $op eq '*' or $op eq '/' or + $op eq '%') + { + if ($check) { + if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { + if (CHK("SPACING", + "spaces preferred around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + $fix_elements[$n + 2] =~ s/^\s+//; + $line_fixed = 1; + } + } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { + if (CHK("SPACING", + "space preferred before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { + if (ERROR("SPACING", + "need consistent spacing around '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # A colon needs no spaces before when it is + # terminating a case value or a label. + } elsif ($opv eq ':C' || $opv eq ':L') { + if ($ctx =~ /Wx./) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + + # All the others need spaces both sides. + } elsif ($ctx !~ /[EWC]x[CWE]/) { + my $ok = 0; + + # Ignore email addresses <foo@bar> + if (($op eq '<' && + $cc =~ /^\S+\@\S+>/) || + ($op eq '>' && + $ca =~ /<\S+\@\S+$/)) + { + $ok = 1; + } + + # for asm volatile statements + # ignore a colon with another + # colon immediately before or after + if (($op eq ':') && + ($ca =~ /:$/ || $cc =~ /^:/)) { + $ok = 1; + } + + # messages are ERROR, but ?: are CHK + if ($ok == 0) { + my $msg_type = \&ERROR; + $msg_type = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); + + if (&{$msg_type}("SPACING", + "spaces required around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + } + $off += length($elements[$n + 1]); + +## print("n: <$n> GOOD: <$good>\n"); + + $fixed_line = $fixed_line . $good; + } + + if (($#elements % 2) == 0) { + $fixed_line = $fixed_line . $fix_elements[$#elements]; + } + + if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { + $fixed[$fixlinenr] = $fixed_line; + } + + + } + +# check for whitespace before a non-naked semicolon + if ($line =~ /^\+.*\S\s+;\s*$/) { + if (WARN("SPACING", + "space prohibited before semicolon\n" . $herecurr) && + $fix) { + 1 while $fixed[$fixlinenr] =~ + s/^(\+.*\S)\s+;/$1;/; + } + } + +# check for multiple assignments + if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { + CHK("MULTIPLE_ASSIGNMENTS", + "multiple assignments should be avoided\n" . $herecurr); + } + +## # check for multiple declarations, allowing for a function declaration +## # continuation. +## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && +## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { +## +## # Remove any bracketed sections to ensure we do not +## # falsly report the parameters of functions. +## my $ln = $line; +## while ($ln =~ s/\([^\(\)]*\)//g) { +## } +## if ($ln =~ /,/) { +## WARN("MULTIPLE_DECLARATION", +## "declaring multiple variables together should be avoided\n" . $herecurr); +## } +## } + +#need space before brace following if, while, etc + if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || + $line =~ /do\{/) { + if (ERROR("SPACING", + "space required before the open brace '{'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\))){/$1 {/; + } + } + +## # check for blank lines before declarations +## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && +## $prevrawline =~ /^.\s*$/) { +## WARN("SPACING", +## "No blank lines before declarations\n" . $hereprev); +## } +## + +# closing brace should have a space following it when it has anything +# on the line + if ($line =~ /}(?!(?:,|;|\)))\S/) { + if (ERROR("SPACING", + "space required after that close brace '}'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/}((?!(?:,|;|\)))\S)/} $1/; + } + } + +# check spacing on square brackets + if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { + if (ERROR("SPACING", + "space prohibited after that open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\[\s+/\[/; + } + } + if ($line =~ /\s\]/) { + if (ERROR("SPACING", + "space prohibited before that close square bracket ']'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\s+\]/\]/; + } + } + +# check spacing on parentheses + if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && + $line !~ /for\s*\(\s+;/) { + if (ERROR("SPACING", + "space prohibited after that open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\(\s+/\(/; + } + } + if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && + $line !~ /for\s*\(.*;\s+\)/ && + $line !~ /:\s+\)/) { + if (ERROR("SPACING", + "space prohibited before that close parenthesis ')'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\s+\)/\)/; + } + } + +# check unnecessary parentheses around addressof/dereference single $Lvals +# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar + + while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { + my $var = $1; + if (CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around $var\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; + } + } + +# check for unnecessary parentheses around function pointer uses +# ie: (foo->bar)(); should be foo->bar(); +# but not "if (foo->bar) (" to avoid some false positives + if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { + my $var = $2; + if (CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around function pointer $var\n" . $herecurr) && + $fix) { + my $var2 = deparenthesize($var); + $var2 =~ s/\s//g; + $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; + } + } + +#goto labels aren't indented, allow a single space however + if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and + !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { + if (WARN("INDENTED_LABEL", + "labels should not be indented\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.)\s+/$1/; + } + } + +# return is not a function + if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { + my $spacing = $1; + if ($^V && $^V ge 5.10.0 && + $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { + my $value = $1; + $value = deparenthesize($value); + if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { + ERROR("RETURN_PARENTHESES", + "return is not a function, parentheses are not required\n" . $herecurr); + } + } elsif ($spacing !~ /\s+/) { + ERROR("SPACING", + "space required before the open parenthesis '('\n" . $herecurr); + } + } + +# unnecessary return in a void function +# at end-of-function, with the previous line a single leading tab, then return; +# and the line before that not a goto label target like "out:" + if ($sline =~ /^[ \+]}\s*$/ && + $prevline =~ /^\+\treturn\s*;\s*$/ && + $linenr >= 3 && + $lines[$linenr - 3] =~ /^[ +]/ && + $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { + WARN("RETURN_VOID", + "void function return statements are not generally useful\n" . $hereprev); + } + +# if statements using unnecessary parentheses - ie: if ((foo == bar)) + if ($^V && $^V ge 5.10.0 && + $line =~ /\bif\s*((?:\(\s*){2,})/) { + my $openparens = $1; + my $count = $openparens =~ tr@\(@\(@; + my $msg = ""; + if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { + my $comp = $4; #Not $1 because of $LvalOrFunc + $msg = " - maybe == should be = ?" if ($comp eq "=="); + WARN("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses$msg\n" . $herecurr); + } + } + +# comparisons with a constant or upper case identifier on the left +# avoid cases like "foo + BAR < baz" +# only fix matches surrounded by parentheses to avoid incorrect +# conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" + if ($^V && $^V ge 5.10.0 && + $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { + my $lead = $1; + my $const = $2; + my $comp = $3; + my $to = $4; + my $newcomp = $comp; + if ($lead !~ /$Operators\s*$/ && + $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && + WARN("CONSTANT_COMPARISON", + "Comparisons should place the constant on the right side of the test\n" . $herecurr) && + $fix) { + if ($comp eq "<") { + $newcomp = ">"; + } elsif ($comp eq "<=") { + $newcomp = ">="; + } elsif ($comp eq ">") { + $newcomp = "<"; + } elsif ($comp eq ">=") { + $newcomp = "<="; + } + $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; + } + } + +# Return of what appears to be an errno should normally be negative + if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { + my $name = $1; + if ($name ne 'EOF' && $name ne 'ERROR') { + WARN("USE_NEGATIVE_ERRNO", + "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); + } + } + +# Need a space before open parenthesis after if, while etc + if ($line =~ /\b(if|while|for|switch)\(/) { + if (ERROR("SPACING", + "space required before the open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\b(if|while|for|switch)\(/$1 \(/; + } + } + +# Check for illegal assignment in if conditional -- and check for trailing +# statements after the conditional. + if ($line =~ /do\s*(?!{)/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0) + if (!defined $stat); + my ($stat_next) = ctx_statement_block($line_nr_next, + $remain_next, $off_next); + $stat_next =~ s/\n./\n /g; + ##print "stat<$stat> stat_next<$stat_next>\n"; + + if ($stat_next =~ /^\s*while\b/) { + # If the statement carries leading newlines, + # then count those as offsets. + my ($whitespace) = + ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = + statement_rawlines($whitespace) - 1; + + $suppress_whiletrailers{$line_nr_next + + $offset} = 1; + } + } + if (!defined $suppress_whiletrailers{$linenr} && + defined($stat) && defined($cond) && + $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { + my ($s, $c) = ($stat, $cond); + + if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { + ERROR("ASSIGN_IN_IF", + "do not use assignment in if condition\n" . $herecurr); + } + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + $s =~ s/$;//g; # Remove any comments + if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && + $c !~ /}\s*while\s*/) + { + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + my $stat_real = ''; + + $stat_real = raw_line($linenr, $cond_lines) + . "\n" if ($cond_lines); + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line\n" . $herecurr . $stat_real); + } + } + +# Check for bitwise tests written as boolean + if ($line =~ / + (?: + (?:\[|\(|\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\|) + | + (?:\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\||\)|\]) + )/x) + { + WARN("HEXADECIMAL_BOOLEAN_TEST", + "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); + } + +# if and else should not have general statements after it + if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { + my $s = $1; + $s =~ s/$;//g; # Remove any comments + if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line\n" . $herecurr); + } + } +# if should not continue a brace + if ($line =~ /}\s*if\b/) { + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line (or did you mean 'else if'?)\n" . + $herecurr); + } +# case and default should not have general statements after them + if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && + $line !~ /\G(?: + (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| + \s*return\s+ + )/xg) + { + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line\n" . $herecurr); + } + + # Check for }<nl>else {, these must be at the same + # indent level to be relevant to each other. + if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && + $previndent == $indent) { + if (ERROR("ELSE_AFTER_BRACE", + "else should follow close brace '}'\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/}\s*$//; + if ($fixedline !~ /^\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + $fixedline = $rawline; + $fixedline =~ s/^(.\s*)else/$1} else/; + fix_insert_line($fixlinenr, $fixedline); + } + } + + if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && + $previndent == $indent) { + my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + + if ($s =~ /^\s*;/) { + if (ERROR("WHILE_AFTER_BRACE", + "while should follow close brace '}'\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + my $trailing = $rawline; + $trailing =~ s/^\+//; + $trailing = trim($trailing); + $fixedline =~ s/}\s*$/} $trailing/; + fix_insert_line($fixlinenr, $fixedline); + } + } + } + +#Specific variable tests + while ($line =~ m{($Constant|$Lval)}g) { + my $var = $1; + +#gcc binary extension + if ($var =~ /^$Binary$/) { + if (WARN("GCC_BINARY_CONSTANT", + "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && + $fix) { + my $hexval = sprintf("0x%x", oct($var)); + $fixed[$fixlinenr] =~ + s/\b$var\b/$hexval/; + } + } + +#CamelCase + if ($var !~ /^$Constant$/ && + $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && +#Ignore Page<foo> variants + $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && +#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) + $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && +#Ignore some three character SI units explicitly, like MiB and KHz + $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { + while ($var =~ m{($Ident)}g) { + my $word = $1; + next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); + if ($check) { + seed_camelcase_includes(); + if (!$file && !$camelcase_file_seeded) { + seed_camelcase_file($realfile); + $camelcase_file_seeded = 1; + } + } + if (!defined $camelcase{$word}) { + $camelcase{$word} = 1; + CHK("CAMELCASE", + "Avoid CamelCase: <$word>\n" . $herecurr); + } + } + } + } + +#no spaces allowed after \ in define + if ($line =~ /\#\s*define.*\\\s+$/) { + if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", + "Whitespace after \\ makes next lines useless\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+$//; + } + } + +# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes +# itself <asm/foo.h> (uses RAW line) + if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { + my $file = "$1.h"; + my $checkfile = "include/linux/$file"; + if (-f "$root/$checkfile" && + $realfile ne $checkfile && + $1 !~ /$allowed_asm_includes/) + { + my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; + if ($asminclude > 0) { + if ($realfile =~ m{^arch/}) { + CHK("ARCH_INCLUDE_LINUX", + "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } else { + WARN("INCLUDE_LINUX", + "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } + } + } + } + +# multi-statement macros should be enclosed in a do while loop, grab the +# first statement and ensure its the whole macro if its not enclosed +# in a known good container + if ($realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + my $has_flow_statement = 0; + my $has_arg_concat = 0; + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $realcnt, 0); + $ctx = $dstat; + #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; + #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; + + $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); + $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); + + $dstat =~ s/^.\s*\#\s*define\s+$Ident(?:\([^\)]*\))?\s*//; + $dstat =~ s/$;//g; + $dstat =~ s/\\\n.//g; + $dstat =~ s/^\s*//s; + $dstat =~ s/\s*$//s; + + # Flatten any parentheses and braces + while ($dstat =~ s/\([^\(\)]*\)/1/ || + $dstat =~ s/\{[^\{\}]*\}/1/ || + $dstat =~ s/.\[[^\[\]]*\]/1/) + { + } + + # Flatten any obvious string concatentation. + while ($dstat =~ s/($String)\s*$Ident/$1/ || + $dstat =~ s/$Ident\s*($String)/$1/) + { + } + + # Make asm volatile uses seem like a generic function + $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; + + my $exceptions = qr{ + $Declare| + module_param_named| + MODULE_PARM_DESC| + DECLARE_PER_CPU| + DEFINE_PER_CPU| + __typeof__\(| + union| + struct| + \.$Ident\s*=\s*| + ^\"|\"$| + ^\[ + }x; + #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; + if ($dstat ne '' && + $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), + $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); + $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz + $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants + $dstat !~ /$exceptions/ && + $dstat !~ /^\.$Ident\s*=/ && # .foo = + $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo + $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) + $dstat !~ /^for\s*$Constant$/ && # for (...) + $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() + $dstat !~ /^do\s*{/ && # do {... + $dstat !~ /^\(\{/ && # ({... + $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) + { + $ctx =~ s/\n*$//; + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($ctx); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + if ($dstat =~ /;/) { + ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", + "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); + } else { + ERROR("COMPLEX_MACRO", + "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); + } + } + +# check for macros with flow control, but without ## concatenation +# ## concatenation is commonly a macro that defines a function so ignore those + if ($has_flow_statement && !$has_arg_concat) { + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($ctx); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + WARN("MACRO_WITH_FLOW_CONTROL", + "Macros with flow control statements should be avoided\n" . "$herectx"); + } + +# check for line continuations outside of #defines, preprocessor #, and asm + + } else { + if ($prevline !~ /^..*\\$/ && + $line !~ /^\+\s*\#.*\\$/ && # preprocessor + $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm + $line =~ /^\+.*\\$/) { + WARN("LINE_CONTINUATIONS", + "Avoid unnecessary line continuations\n" . $herecurr); + } + } + +# do {} while (0) macro tests: +# single-statement macros do not need to be enclosed in do while (0) loop, +# macro should not end with a semicolon + if ($^V && $^V ge 5.10.0 && + $realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $realcnt, 0); + $ctx = $dstat; + + $dstat =~ s/\\\n.//g; + $dstat =~ s/$;/ /g; + + if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { + my $stmts = $2; + my $semis = $3; + + $ctx =~ s/\n*$//; + my $cnt = statement_rawlines($ctx); + my $herectx = $here . "\n"; + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + if (($stmts =~ tr/;/;/) == 1 && + $stmts !~ /^\s*(if|while|for|switch)\b/) { + WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", + "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); + } + if (defined $semis && $semis ne "") { + WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", + "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); + } + } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { + $ctx =~ s/\n*$//; + my $cnt = statement_rawlines($ctx); + my $herectx = $here . "\n"; + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + WARN("TRAILING_SEMICOLON", + "macros should not use a trailing semicolon\n" . "$herectx"); + } + } + +# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... +# all assignments may have only one of the following with an assignment: +# . +# ALIGN(...) +# VMLINUX_SYMBOL(...) + if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { + WARN("MISSING_VMLINUX_SYMBOL", + "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); + } + +# check for redundant bracing round if etc + if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, 1); + #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; + #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; + if ($#chunks > 0 && $level == 0) { + my @allowed = (); + my $allow = 0; + my $seen = 0; + my $herectx = $here . "\n"; + my $ln = $linenr - 1; + for my $chunk (@chunks) { + my ($cond, $block) = @{$chunk}; + + # If the condition carries leading newlines, then count those as offsets. + my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = statement_rawlines($whitespace) - 1; + + $allowed[$allow] = 0; + #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; + + # We have looked at and allowed this specific line. + $suppress_ifbraces{$ln + $offset} = 1; + + $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; + $ln += statement_rawlines($block) - 1; + + substr($block, 0, length($cond), ''); + + $seen++ if ($block =~ /^\s*{/); + + #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed[$allow] = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed[$allow] = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed[$allow] = 1; + } + $allow++; + } + if ($seen) { + my $sum_allowed = 0; + foreach (@allowed) { + $sum_allowed += $_; + } + if ($sum_allowed == 0) { + WARN("BRACES", + "braces {} are not necessary for any arm of this statement\n" . $herectx); + } elsif ($sum_allowed != $allow && + $seen != $allow) { + CHK("BRACES", + "braces {} should be used on all arms of this statement\n" . $herectx); + } + } + } + } + if (!defined $suppress_ifbraces{$linenr - 1} && + $line =~ /\b(if|while|for|else)\b/) { + my $allowed = 0; + + # Check the pre-context. + if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { + #print "APW: ALLOWED: pre<$1>\n"; + $allowed = 1; + } + + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, $-[0]); + + # Check the condition. + my ($cond, $block) = @{$chunks[0]}; + #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + # Check the post-context. + if (defined $chunks[1]) { + my ($cond, $block) = @{$chunks[1]}; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if ($block =~ /^\s*\{/) { + #print "APW: ALLOWED: chunk-1 block<$block>\n"; + $allowed = 1; + } + } + if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($block); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + WARN("BRACES", + "braces {} are not necessary for single statement blocks\n" . $herectx); + } + } + +# check for unnecessary blank lines around braces + if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { + if (CHK("BRACES", + "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && + $fix && $prevrawline =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + } + } + if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { + if (CHK("BRACES", + "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } + } + +# no volatiles please + my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; + if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { + WARN("VOLATILE", + "Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); + } + +# Check for user-visible strings broken across lines, which breaks the ability +# to grep for the string. Make exceptions when the previous string ends in a +# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' +# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value + if ($line =~ /^\+\s*$String/ && + $prevline =~ /"\s*$/ && + $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { + if (WARN("SPLIT_STRING", + "quoted string split across lines\n" . $hereprev) && + $fix && + $prevrawline =~ /^\+.*"\s*$/ && + $last_coalesced_string_linenr != $linenr - 1) { + my $extracted_string = get_quoted_string($line, $rawline); + my $comma_close = ""; + if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { + $comma_close = $1; + } + + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/"\s*$//; + $fixedline .= substr($extracted_string, 1) . trim($comma_close); + fix_insert_line($fixlinenr - 1, $fixedline); + $fixedline = $rawline; + $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; + if ($fixedline !~ /\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + $last_coalesced_string_linenr = $linenr; + } + } + +# check for missing a space in a string concatenation + if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { + WARN('MISSING_SPACE', + "break quoted strings at a space character\n" . $hereprev); + } + +# check for spaces before a quoted newline + if ($rawline =~ /^.*\".*\s\\n/) { + if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", + "unnecessary whitespace before a quoted newline\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; + } + + } + +# concatenated string without spaces between elements + if ($line =~ /$String[A-Z_]/ || $line =~ /[A-Za-z0-9_]$String/) { + CHK("CONCATENATED_STRING", + "Concatenated strings should use spaces between elements\n" . $herecurr); + } + +# uncoalesced string fragments + if ($line =~ /$String\s*"/) { + WARN("STRING_FRAGMENTS", + "Consecutive strings are generally better as a single string\n" . $herecurr); + } + +# check for %L{u,d,i} and 0x%[udi] in strings + my $string; + while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { + $string = substr($rawline, $-[1], $+[1] - $-[1]); + $string =~ s/%%/__/g; + if ($string =~ /(?<!%)%[\*\d\.\$]*L[udi]/) { + WARN("PRINTF_L", + "\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr); + last; + } + if ($string =~ /0x%[\*\d\.\$\Llzth]*[udi]/) { + ERROR("PRINTF_0xDECIMAL", + "Prefixing 0x with decimal output is defective\n" . $herecurr); + } + } + +# check for line continuations in quoted strings with odd counts of " + if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { + WARN("LINE_CONTINUATIONS", + "Avoid line continuations in quoted strings\n" . $herecurr); + } + +# warn about #if 0 + if ($line =~ /^.\s*\#\s*if\s+0\b/) { + CHK("REDUNDANT_CODE", + "if this code is redundant consider removing it\n" . + $herecurr); + } + +# check for needless "if (<foo>) fn(<foo>)" uses + if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { + my $tested = quotemeta($1); + my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; + if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { + my $func = $1; + if (WARN('NEEDLESS_IF', + "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && + $fix) { + my $do_fix = 1; + my $leading_tabs = ""; + my $new_leading_tabs = ""; + if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { + $leading_tabs = $1; + } else { + $do_fix = 0; + } + if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { + $new_leading_tabs = $1; + if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { + $do_fix = 0; + } + } else { + $do_fix = 0; + } + if ($do_fix) { + fix_delete_line($fixlinenr - 1, $prevrawline); + $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; + } + } + } + } + +# check for unnecessary "Out of Memory" messages + if ($line =~ /^\+.*\b$logFunctions\s*\(/ && + $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && + (defined $1 || defined $3) && + $linenr > 3) { + my $testval = $2; + my $testline = $lines[$linenr - 3]; + + my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); +# print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); + + if ($c =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|(?:dev_)?alloc_skb)/) { + WARN("OOM_MESSAGE", + "Possible unnecessary 'out of memory' message\n" . $hereprev); + } + } + +# check for logging functions with KERN_<LEVEL> + if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && + $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { + my $level = $1; + if (WARN("UNNECESSARY_KERN_LEVEL", + "Possible unnecessary $level\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s*$level\s*//; + } + } + +# check for mask then right shift without a parentheses + if ($^V && $^V ge 5.10.0 && + $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && + $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so + WARN("MASK_THEN_SHIFT", + "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); + } + +# check for pointer comparisons to NULL + if ($^V && $^V ge 5.10.0) { + while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { + my $val = $1; + my $equal = "!"; + $equal = "" if ($4 eq "!="); + if (CHK("COMPARISON_TO_NULL", + "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; + } + } + } + +# check for bad placement of section $InitAttribute (e.g.: __initdata) + if ($line =~ /(\b$InitAttribute\b)/) { + my $attr = $1; + if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { + my $ptr = $1; + my $var = $2; + if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && + ERROR("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr)) || + ($ptr !~ /\b(union|struct)\s+$attr\b/ && + WARN("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr))) && + $fix) { + $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; + } + } + } + +# check for $InitAttributeData (ie: __initdata) with const + if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { + my $attr = $1; + $attr =~ /($InitAttributePrefix)(.*)/; + my $attr_prefix = $1; + my $attr_type = $2; + if (ERROR("INIT_ATTRIBUTE", + "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/$InitAttributeData/${attr_prefix}initconst/; + } + } + +# check for $InitAttributeConst (ie: __initconst) without const + if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { + my $attr = $1; + if (ERROR("INIT_ATTRIBUTE", + "Use of $attr requires a separate use of const\n" . $herecurr) && + $fix) { + my $lead = $fixed[$fixlinenr] =~ + /(^\+\s*(?:static\s+))/; + $lead = rtrim($1); + $lead = "$lead " if ($lead !~ /^\+$/); + $lead = "${lead}const "; + $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; + } + } + +# check for __read_mostly with const non-pointer (should just be const) + if ($line =~ /\b__read_mostly\b/ && + $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { + if (ERROR("CONST_READ_MOSTLY", + "Invalid use of __read_mostly with const type\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; + } + } + +# don't use __constant_<foo> functions outside of include/uapi/ + if ($realfile !~ m@^include/uapi/@ && + $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { + my $constant_func = $1; + my $func = $constant_func; + $func =~ s/^__constant_//; + if (WARN("CONSTANT_CONVERSION", + "$constant_func should be $func\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; + } + } + +# prefer usleep_range over udelay + if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { + my $delay = $1; + # ignore udelay's < 10, however + if (! ($delay < 10) ) { + CHK("USLEEP_RANGE", + "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr); + } + if ($delay > 2000) { + WARN("LONG_UDELAY", + "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); + } + } + +# warn about unexpectedly long msleep's + if ($line =~ /\bmsleep\s*\((\d+)\);/) { + if ($1 < 20) { + WARN("MSLEEP", + "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr); + } + } + +# check for comparisons of jiffies + if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { + WARN("JIFFIES_COMPARISON", + "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); + } + +# check for comparisons of get_jiffies_64() + if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { + WARN("JIFFIES_COMPARISON", + "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); + } + +# warn about #ifdefs in C files +# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { +# print "#ifdef in C files should be avoided\n"; +# print "$herecurr"; +# $clean = 0; +# } + +# warn about spacing in #ifdefs + if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { + if (ERROR("SPACING", + "exactly one space required after that #$1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; + } + + } + +# check for spinlock_t definitions without a comment. + if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || + $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { + my $which = $1; + if (!ctx_has_comment($first_line, $linenr)) { + CHK("UNCOMMENTED_DEFINITION", + "$1 definition without comment\n" . $herecurr); + } + } +# check for memory barriers without a comment. + + my $barriers = qr{ + mb| + rmb| + wmb| + read_barrier_depends + }x; + my $barrier_stems = qr{ + mb__before_atomic| + mb__after_atomic| + store_release| + load_acquire| + store_mb| + (?:$barriers) + }x; + my $all_barriers = qr{ + (?:$barriers)| + smp_(?:$barrier_stems)| + virt_(?:$barrier_stems) + }x; + + if ($line =~ /\b(?:$all_barriers)\s*\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + WARN("MEMORY_BARRIER", + "memory barrier without comment\n" . $herecurr); + } + } + + my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; + + if ($realfile !~ m@^include/asm-generic/@ && + $realfile !~ m@/barrier\.h$@ && + $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && + $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { + WARN("MEMORY_BARRIER", + "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); + } + +# check for waitqueue_active without a comment. + if ($line =~ /\bwaitqueue_active\s*\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + WARN("WAITQUEUE_ACTIVE", + "waitqueue_active without comment\n" . $herecurr); + } + } + +# Check for expedited grace periods that interrupt non-idle non-nohz +# online CPUs. These expedited can therefore degrade real-time response +# if used carelessly, and should be avoided where not absolutely +# needed. It is always OK to use synchronize_rcu_expedited() and +# synchronize_sched_expedited() at boot time (before real-time applications +# start) and in error situations where real-time response is compromised in +# any case. Note that synchronize_srcu_expedited() does -not- interrupt +# other CPUs, so don't warn on uses of synchronize_srcu_expedited(). +# Of course, nothing comes for free, and srcu_read_lock() and +# srcu_read_unlock() do contain full memory barriers in payment for +# synchronize_srcu_expedited() non-interruption properties. + if ($line =~ /\b(synchronize_rcu_expedited|synchronize_sched_expedited)\(/) { + WARN("EXPEDITED_RCU_GRACE_PERIOD", + "expedited RCU grace periods should be avoided where they can degrade real-time response\n" . $herecurr); + + } + +# check of hardware specific defines + if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { + CHK("ARCH_DEFINES", + "architecture specific defines should be avoided\n" . $herecurr); + } + +# Check that the storage class is at the beginning of a declaration + if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { + WARN("STORAGE_CLASS", + "storage class should be at the beginning of the declaration\n" . $herecurr) + } + +# check the location of the inline attribute, that it is between +# storage class and type. + if ($line =~ /\b$Type\s+$Inline\b/ || + $line =~ /\b$Inline\s+$Storage\b/) { + ERROR("INLINE_LOCATION", + "inline keyword should sit between storage class and type\n" . $herecurr); + } + +# Check for __inline__ and __inline, prefer inline + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b(__inline__|__inline)\b/) { + if (WARN("INLINE", + "plain inline is preferred over $1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; + + } + } + +# Check for __attribute__ packed, prefer __packed + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { + WARN("PREFER_PACKED", + "__packed is preferred over __attribute__((packed))\n" . $herecurr); + } + +# Check for __attribute__ aligned, prefer __aligned + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { + WARN("PREFER_ALIGNED", + "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); + } + +# Check for __attribute__ format(printf, prefer __printf + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { + if (WARN("PREFER_PRINTF", + "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; + + } + } + +# Check for __attribute__ format(scanf, prefer __scanf + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { + if (WARN("PREFER_SCANF", + "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; + } + } + +# Check for __attribute__ weak, or __weak declarations (may have link issues) + if ($^V && $^V ge 5.10.0 && + $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && + ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || + $line =~ /\b__weak\b/)) { + ERROR("WEAK_DECLARATION", + "Using weak declarations can have unintended link defects\n" . $herecurr); + } + +# check for c99 types like uint8_t used outside of uapi/ + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { + my $type = $1; + if ($type =~ /\b($typeC99Typedefs)\b/) { + $type = $1; + my $kernel_type = 'u'; + $kernel_type = 's' if ($type =~ /^_*[si]/); + $type =~ /(\d+)/; + $kernel_type .= $1; + if (CHK("PREFER_KERNEL_TYPES", + "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/; + } + } + } + +# check for cast of C90 native int or longer types constants + if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { + my $cast = $1; + my $const = $2; + if (WARN("TYPECAST_INT_CONSTANT", + "Unnecessary typecast of c90 int constant\n" . $herecurr) && + $fix) { + my $suffix = ""; + my $newconst = $const; + $newconst =~ s/${Int_type}$//; + $suffix .= 'U' if ($cast =~ /\bunsigned\b/); + if ($cast =~ /\blong\s+long\b/) { + $suffix .= 'LL'; + } elsif ($cast =~ /\blong\b/) { + $suffix .= 'L'; + } + $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; + } + } + +# check for sizeof(&) + if ($line =~ /\bsizeof\s*\(\s*\&/) { + WARN("SIZEOF_ADDRESS", + "sizeof(& should be avoided\n" . $herecurr); + } + +# check for sizeof without parenthesis + if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { + if (WARN("SIZEOF_PARENTHESIS", + "sizeof $1 should be sizeof($1)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; + } + } + +# check for struct spinlock declarations + if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { + WARN("USE_SPINLOCK_T", + "struct spinlock should be spinlock_t\n" . $herecurr); + } + +# check for seq_printf uses that could be seq_puts + if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { + my $fmt = get_quoted_string($line, $rawline); + $fmt =~ s/%%//g; + if ($fmt !~ /%/) { + if (WARN("PREFER_SEQ_PUTS", + "Prefer seq_puts to seq_printf\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; + } + } + } + +# Check for misused memsets + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { + + my $ms_addr = $2; + my $ms_val = $7; + my $ms_size = $12; + + if ($ms_size =~ /^(0x|)0$/i) { + ERROR("MEMSET", + "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); + } elsif ($ms_size =~ /^(0x|)1$/i) { + WARN("MEMSET", + "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); + } + } + +# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { + if (WARN("PREFER_ETHER_ADDR_COPY", + "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && + $fix) { + $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; + } + } + +# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { + WARN("PREFER_ETHER_ADDR_EQUAL", + "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") + } + +# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr +# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { + + my $ms_val = $7; + + if ($ms_val =~ /^(?:0x|)0+$/i) { + if (WARN("PREFER_ETH_ZERO_ADDR", + "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && + $fix) { + $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; + } + } elsif ($ms_val =~ /^(?:0xff|255)$/i) { + if (WARN("PREFER_ETH_BROADCAST_ADDR", + "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && + $fix) { + $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; + } + } + } + +# typecasts on min/max could be min_t/max_t + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { + if (defined $2 || defined $7) { + my $call = $1; + my $cast1 = deparenthesize($2); + my $arg1 = $3; + my $cast2 = deparenthesize($7); + my $arg2 = $8; + my $cast; + + if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { + $cast = "$cast1 or $cast2"; + } elsif ($cast1 ne "") { + $cast = $cast1; + } else { + $cast = $cast2; + } + WARN("MINMAX", + "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); + } + } + +# check usleep_range arguments + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { + my $min = $1; + my $max = $7; + if ($min eq $max) { + WARN("USLEEP_RANGE", + "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); + } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && + $min > $max) { + WARN("USLEEP_RANGE", + "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); + } + } + +# check for naked sscanf + if ($^V && $^V ge 5.10.0 && + defined $stat && + $line =~ /\bsscanf\b/ && + ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && + $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && + $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + WARN("NAKED_SSCANF", + "unchecked sscanf return value\n" . "$here\n$stat_real\n"); + } + +# check for simple sscanf that should be kstrto<foo> + if ($^V && $^V ge 5.10.0 && + defined $stat && + $line =~ /\bsscanf\b/) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { + my $format = $6; + my $count = $format =~ tr@%@%@; + if ($count == 1 && + $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { + WARN("SSCANF_TO_KSTRTO", + "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); + } + } + } + +# check for new externs in .h files. + if ($realfile =~ /\.h$/ && + $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { + if (CHK("AVOID_EXTERNS", + "extern prototypes should be avoided in .h files\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; + } + } + +# check for new externs in .c files. + if ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) + { + my $function_name = $1; + my $paren_space = $2; + + my $s = $stat; + if (defined $cond) { + substr($s, 0, length($cond), ''); + } + if ($s =~ /^\s*;/ && + $function_name ne 'uninitialized_var') + { + WARN("AVOID_EXTERNS", + "externs should be avoided in .c files\n" . $herecurr); + } + + if ($paren_space =~ /\n/) { + WARN("FUNCTION_ARGUMENTS", + "arguments for function declarations should follow identifier\n" . $herecurr); + } + + } elsif ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*extern\s+/) + { + WARN("AVOID_EXTERNS", + "externs should be avoided in .c files\n" . $herecurr); + } + +# checks for new __setup's + if ($rawline =~ /\b__setup\("([^"]*)"/) { + my $name = $1; + + if (!grep(/$name/, @setup_docs)) { + CHK("UNDOCUMENTED_SETUP", + "__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); + } + } + +# check for pointless casting of kmalloc return + if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { + WARN("UNNECESSARY_CASTS", + "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); + } + +# alloc style +# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { + CHK("ALLOC_SIZEOF_STRUCT", + "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); + } + +# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { + my $oldfunc = $3; + my $a1 = $4; + my $a2 = $10; + my $newfunc = "kmalloc_array"; + $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); + my $r1 = $a1; + my $r2 = $a2; + if ($a1 =~ /^sizeof\s*\S/) { + $r1 = $a2; + $r2 = $a1; + } + if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && + !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { + if (WARN("ALLOC_WITH_MULTIPLY", + "Prefer $newfunc over $oldfunc with multiply\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; + + } + } + } + +# check for krealloc arg reuse + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { + WARN("KREALLOC_ARG_REUSE", + "Reusing the krealloc arg is almost always a bug\n" . $herecurr); + } + +# check for alloc argument mismatch + if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { + WARN("ALLOC_ARRAY_ARGS", + "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); + } + +# check for multiple semicolons + if ($line =~ /;\s*;\s*$/) { + if (WARN("ONE_SEMICOLON", + "Statements terminations use 1 semicolon\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; + } + } + +# check for #defines like: 1 << <digit> that could be BIT(digit) + if ($line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { + my $ull = ""; + $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); + if (CHK("BIT_MACRO", + "Prefer using the BIT$ull macro\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; + } + } + +# check for case / default statements not preceded by break/fallthrough/switch + if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { + my $has_break = 0; + my $has_statement = 0; + my $count = 0; + my $prevline = $linenr; + while ($prevline > 1 && ($file || $count < 3) && !$has_break) { + $prevline--; + my $rline = $rawlines[$prevline - 1]; + my $fline = $lines[$prevline - 1]; + last if ($fline =~ /^\@\@/); + next if ($fline =~ /^\-/); + next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); + $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); + next if ($fline =~ /^.[\s$;]*$/); + $has_statement = 1; + $count++; + $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/); + } + if (!$has_break && $has_statement) { + WARN("MISSING_BREAK", + "Possible switch case/default not preceeded by break or fallthrough comment\n" . $herecurr); + } + } + +# check for switch/default statements without a break; + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { + my $ctx = ''; + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($stat); + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + WARN("DEFAULT_NO_BREAK", + "switch default: should use break\n" . $herectx); + } + +# check for gcc specific __FUNCTION__ + if ($line =~ /\b__FUNCTION__\b/) { + if (WARN("USE_FUNC", + "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; + } + } + +# check for uses of __DATE__, __TIME__, __TIMESTAMP__ + while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { + ERROR("DATE_TIME", + "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); + } + +# check for use of yield() + if ($line =~ /\byield\s*\(\s*\)/) { + WARN("YIELD", + "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); + } + +# check for comparisons against true and false + if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { + my $lead = $1; + my $arg = $2; + my $test = $3; + my $otype = $4; + my $trail = $5; + my $op = "!"; + + ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); + + my $type = lc($otype); + if ($type =~ /^(?:true|false)$/) { + if (("$test" eq "==" && "$type" eq "true") || + ("$test" eq "!=" && "$type" eq "false")) { + $op = ""; + } + + CHK("BOOL_COMPARISON", + "Using comparison to $otype is error prone\n" . $herecurr); + +## maybe suggesting a correct construct would better +## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); + + } + } + +# check for semaphores initialized locked + if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { + WARN("CONSIDER_COMPLETION", + "consider using a completion\n" . $herecurr); + } + +# recommend kstrto* over simple_strto* and strict_strto* + if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { + WARN("CONSIDER_KSTRTO", + "$1 is obsolete, use k$3 instead\n" . $herecurr); + } + +# check for __initcall(), use device_initcall() explicitly or more appropriate function please + if ($line =~ /^.\s*__initcall\s*\(/) { + WARN("USE_DEVICE_INITCALL", + "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); + } + +# check for various structs that are normally const (ops, kgdb, device_tree) + my $const_structs = qr{ + acpi_dock_ops| + address_space_operations| + backlight_ops| + block_device_operations| + dentry_operations| + dev_pm_ops| + dma_map_ops| + extent_io_ops| + file_lock_operations| + file_operations| + hv_ops| + ide_dma_ops| + intel_dvo_dev_ops| + item_operations| + iwl_ops| + kgdb_arch| + kgdb_io| + kset_uevent_ops| + lock_manager_operations| + microcode_ops| + mtrr_ops| + neigh_ops| + nlmsvc_binding| + of_device_id| + pci_raw_ops| + pipe_buf_operations| + platform_hibernation_ops| + platform_suspend_ops| + proto_ops| + rpc_pipe_ops| + seq_operations| + snd_ac97_build_ops| + soc_pcmcia_socket_ops| + stacktrace_ops| + sysfs_ops| + tty_operations| + uart_ops| + usb_mon_operations| + wd_ops}x; + if ($line !~ /\bconst\b/ && + $line =~ /\bstruct\s+($const_structs)\b/) { + WARN("CONST_STRUCT", + "struct $1 should normally be const\n" . + $herecurr); + } + +# use of NR_CPUS is usually wrong +# ignore definitions of NR_CPUS and usage to define arrays as likely right + if ($line =~ /\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) + { + WARN("NR_CPUS", + "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); + } + +# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. + if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { + ERROR("DEFINE_ARCH_HAS", + "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); + } + +# likely/unlikely comparisons similar to "(likely(foo) > 0)" + if ($^V && $^V ge 5.10.0 && + $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { + WARN("LIKELY_MISUSE", + "Using $1 should generally have parentheses around the comparison\n" . $herecurr); + } + +# whine mightly about in_atomic + if ($line =~ /\bin_atomic\s*\(/) { + if ($realfile =~ m@^drivers/@) { + ERROR("IN_ATOMIC", + "do not use in_atomic in drivers\n" . $herecurr); + } elsif ($realfile !~ m@^kernel/@) { + WARN("IN_ATOMIC", + "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); + } + } + +# check for lockdep_set_novalidate_class + if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || + $line =~ /__lockdep_no_validate__\s*\)/ ) { + if ($realfile !~ m@^kernel/lockdep@ && + $realfile !~ m@^include/linux/lockdep@ && + $realfile !~ m@^drivers/base/core@) { + ERROR("LOCKDEP", + "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); + } + } + + if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || + $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { + WARN("EXPORTED_WORLD_WRITABLE", + "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); + } + +# Mode permission misuses where it seems decimal should be octal +# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop + if ($^V && $^V ge 5.10.0 && + $line =~ /$mode_perms_search/) { + foreach my $entry (@mode_permission_funcs) { + my $func = $entry->[0]; + my $arg_pos = $entry->[1]; + + my $skip_args = ""; + if ($arg_pos > 1) { + $arg_pos--; + $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; + } + my $test = "\\b$func\\s*\\(${skip_args}([\\d]+)\\s*[,\\)]"; + if ($line =~ /$test/) { + my $val = $1; + $val = $6 if ($skip_args ne ""); + + if ($val !~ /^0$/ && + (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || + length($val) ne 4)) { + ERROR("NON_OCTAL_PERMISSIONS", + "Use 4 digit octal (0777) not decimal permissions\n" . $herecurr); + } elsif ($val =~ /^$Octal$/ && (oct($val) & 02)) { + ERROR("EXPORTED_WORLD_WRITABLE", + "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); + } + } + } + } + +# validate content of MODULE_LICENSE against list from include/linux/module.h + if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { + my $extracted_string = get_quoted_string($line, $rawline); + my $valid_licenses = qr{ + GPL| + GPL\ v2| + GPL\ and\ additional\ rights| + Dual\ BSD/GPL| + Dual\ MIT/GPL| + Dual\ MPL/GPL| + Proprietary + }x; + if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { + WARN("MODULE_LICENSE", + "unknown module license " . $extracted_string . "\n" . $herecurr); + } + } + } + + # If we have no input at all, then there is nothing to report on + # so just keep quiet. + if ($#rawlines == -1) { + exit(0); + } + + # In mailback mode only produce a report in the negative, for + # things that appear to be patches. + if ($mailback && ($clean == 1 || !$is_patch)) { + exit(0); + } + + # This is not a patch, and we are are in 'no-patch' mode so + # just keep quiet. + if (!$chk_patch && !$is_patch) { + exit(0); + } + + if (!$is_patch && $file !~ /cover-letter\.patch$/) { + ERROR("NOT_UNIFIED_DIFF", + "Does not appear to be a unified-diff format patch\n"); + } + if ($is_patch && $filename ne '-' && $chk_signoff && $signoff == 0) { + ERROR("MISSING_SIGN_OFF", + "Missing Signed-off-by: line(s)\n"); + } + + print report_dump(); + if ($summary && !($clean == 1 && $quiet == 1)) { + print "$filename " if ($summary_file); + print "total: $cnt_error errors, $cnt_warn warnings, " . + (($check)? "$cnt_chk checks, " : "") . + "$cnt_lines lines checked\n"; + } + + if ($quiet == 0) { + # If there were whitespace errors which cleanpatch can fix + # then suggest that. + if ($rpt_cleaners) { + $rpt_cleaners = 0; + print << "EOM" + +NOTE: Whitespace errors detected. + You may wish to use scripts/cleanpatch or scripts/cleanfile +EOM + } + } + + if ($clean == 0 && $fix && + ("@rawlines" ne "@fixed" || + $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { + my $newfile = $filename; + $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); + my $linecount = 0; + my $f; + + @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); + + open($f, '>', $newfile) + or die "$P: Can't open $newfile for write\n"; + foreach my $fixed_line (@fixed) { + $linecount++; + if ($file) { + if ($linecount > 3) { + $fixed_line =~ s/^\+//; + print $f $fixed_line . "\n"; + } + } else { + print $f $fixed_line . "\n"; + } + } + close($f); + + if (!$quiet) { + print << "EOM"; + +Wrote EXPERIMENTAL --fix correction(s) to '$newfile' + +Do _NOT_ trust the results written to this file. +Do _NOT_ submit these changes without inspecting them for correctness. + +This EXPERIMENTAL file is simply a convenience to help rewrite patches. +No warranties, expressed or implied... +EOM + } + } + + if ($quiet == 0) { + print "\n"; + if ($clean == 1) { + print "$vname has no obvious style problems and is ready for submission.\n"; + } else { + print "$vname has style problems, please review.\n"; + } + } + return $clean; +} diff --git a/drivers/staging/greybus/scripts/spelling.txt b/drivers/staging/greybus/scripts/spelling.txt new file mode 100644 index 000000000000..946caf3bd694 --- /dev/null +++ b/drivers/staging/greybus/scripts/spelling.txt @@ -0,0 +1,1072 @@ +# Originally from Debian's Lintian tool. Various false positives have been +# removed, and various additions have been made as they've been discovered +# in the kernel source. +# +# License: GPLv2 +# +# The format of each line is: +# mistake||correction +# +abandonning||abandoning +abigious||ambiguous +abitrate||arbitrate +abov||above +abreviated||abbreviated +absense||absence +absolut||absolute +absoulte||absolute +acccess||access +acceleratoin||acceleration +accelleration||acceleration +accesing||accessing +accesnt||accent +accessable||accessible +accesss||access +accidentaly||accidentally +accidentually||accidentally +accoding||according +accomodate||accommodate +accomodates||accommodates +accordign||according +accoring||according +accout||account +accquire||acquire +accquired||acquired +accross||across +acessable||accessible +acess||access +achitecture||architecture +acient||ancient +acitions||actions +acitve||active +acknowldegement||acknowldegement +acknowledgement||acknowledgment +ackowledge||acknowledge +ackowledged||acknowledged +acording||according +activete||activate +acumulating||accumulating +adapater||adapter +addional||additional +additionaly||additionally +addres||address +addreses||addresses +addresss||address +aditional||additional +aditionally||additionally +aditionaly||additionally +adminstrative||administrative +adress||address +adresses||addresses +adviced||advised +afecting||affecting +agaist||against +albumns||albums +alegorical||allegorical +algorith||algorithm +algorithmical||algorithmically +algoritm||algorithm +algoritms||algorithms +algorrithm||algorithm +algorritm||algorithm +allign||align +allocatrd||allocated +allocte||allocate +allpication||application +alocate||allocate +alogirhtms||algorithms +alogrithm||algorithm +alot||a lot +alow||allow +alows||allows +altough||although +alue||value +ambigious||ambiguous +amoung||among +amout||amount +analysator||analyzer +ang||and +anniversery||anniversary +annoucement||announcement +anomolies||anomalies +anomoly||anomaly +anway||anyway +aplication||application +appearence||appearance +applicaion||application +appliction||application +applictions||applications +appplications||applications +appropiate||appropriate +appropriatly||appropriately +approriate||appropriate +approriately||appropriately +apropriate||appropriate +aquainted||acquainted +aquired||acquired +aquisition||acquisition +arbitary||arbitrary +architechture||architecture +arguement||argument +arguements||arguments +aritmetic||arithmetic +arne't||aren't +arraival||arrival +artifical||artificial +artillary||artillery +asign||assign +assertation||assertion +assiged||assigned +assigment||assignment +assigments||assignments +assistent||assistant +assocation||association +associcated||associated +assotiated||associated +assum||assume +assumtpion||assumption +asuming||assuming +asycronous||asynchronous +asynchnous||asynchronous +atomatically||automatically +atomicly||atomically +attachement||attachment +attched||attached +attemps||attempts +attruibutes||attributes +authentification||authentication +automaticaly||automatically +automaticly||automatically +automatize||automate +automatized||automated +automatizes||automates +autonymous||autonomous +auxillary||auxiliary +auxilliary||auxiliary +avaiable||available +avaible||available +availabe||available +availabled||available +availablity||availability +availale||available +availavility||availability +availble||available +availiable||available +avalable||available +avaliable||available +aysnc||async +backgroud||background +backword||backward +backwords||backwards +bahavior||behavior +bakup||backup +baloon||balloon +baloons||balloons +bandwith||bandwidth +batery||battery +beacuse||because +becasue||because +becomming||becoming +becuase||because +beeing||being +befor||before +begining||beginning +beter||better +betweeen||between +bianries||binaries +bitmast||bitmask +boardcast||broadcast +borad||board +boundry||boundary +brievely||briefly +broadcat||broadcast +cacluated||calculated +caculation||calculation +calender||calendar +calle||called +calucate||calculate +calulate||calculate +cancelation||cancellation +capabilites||capabilities +capabitilies||capabilities +capatibilities||capabilities +carefuly||carefully +cariage||carriage +catagory||category +cehck||check +challange||challenge +challanges||challenges +chanell||channel +changable||changeable +channle||channel +channnel||channel +charachter||character +charachters||characters +charactor||character +charater||character +charaters||characters +charcter||character +chcek||check +chck||check +checksuming||checksumming +childern||children +childs||children +chiled||child +chked||checked +chnage||change +chnages||changes +chnnel||channel +choosen||chosen +chouse||chose +circumvernt||circumvent +claread||cleared +clared||cleared +closeing||closing +clustred||clustered +collapsable||collapsible +colorfull||colorful +comand||command +comit||commit +commerical||commercial +comming||coming +comminucation||communication +commited||committed +commiting||committing +committ||commit +commoditiy||commodity +compability||compatibility +compaibility||compatibility +compatability||compatibility +compatable||compatible +compatibiliy||compatibility +compatibilty||compatibility +compatiblity||compatibility +competion||completion +compilant||compliant +compleatly||completely +completly||completely +complient||compliant +componnents||components +compres||compress +compresion||compression +comression||compression +comunication||communication +conbination||combination +conditionaly||conditionally +conected||connected +configuratoin||configuration +configuraton||configuration +configuretion||configuration +conider||consider +conjuction||conjunction +connectinos||connections +connnection||connection +connnections||connections +consistancy||consistency +consistant||consistent +containes||contains +containts||contains +contaisn||contains +contant||contact +contence||contents +continous||continuous +continously||continuously +continueing||continuing +contraints||constraints +controled||controlled +controler||controller +controll||control +contruction||construction +contry||country +convertion||conversion +convertor||converter +convienient||convenient +convinient||convenient +corected||corrected +correponding||corresponding +correponds||corresponds +correspoding||corresponding +cotrol||control +couter||counter +coutner||counter +cryptocraphic||cryptographic +cunter||counter +curently||currently +dafault||default +deafult||default +deamon||daemon +decompres||decompress +decription||description +defailt||default +defferred||deferred +definate||definite +definately||definitely +defintion||definition +defintions||definitions +defualt||default +defult||default +deivce||device +delared||declared +delare||declare +delares||declares +delaring||declaring +delemiter||delimiter +dependancies||dependencies +dependancy||dependency +dependant||dependent +depreacted||deprecated +depreacte||deprecate +desactivate||deactivate +desciptors||descriptors +descripton||description +descrition||description +descritptor||descriptor +desctiptor||descriptor +desriptor||descriptor +desriptors||descriptors +destory||destroy +destoryed||destroyed +destorys||destroys +destroied||destroyed +detabase||database +develope||develop +developement||development +developped||developed +developpement||development +developper||developer +developpment||development +deveolpment||development +devided||divided +deviece||device +diable||disable +dictionnary||dictionary +didnt||didn't +diferent||different +differrence||difference +difinition||definition +diplay||display +direectly||directly +disapear||disappear +disapeared||disappeared +disappared||disappeared +disconnet||disconnect +discontinous||discontinuous +dispertion||dispersion +dissapears||disappears +distiction||distinction +docuentation||documentation +documantation||documentation +documentaion||documentation +documment||document +doesnt||doesn't +dorp||drop +dosen||doesn +downlad||download +downlads||downloads +druing||during +dynmaic||dynamic +easilly||easily +ecspecially||especially +edditable||editable +editting||editing +efficently||efficiently +ehther||ether +eigth||eight +eletronic||electronic +enabledi||enabled +enchanced||enhanced +encorporating||incorporating +encrupted||encrypted +encrypiton||encryption +endianess||endianness +enhaced||enhanced +enlightnment||enlightenment +enocded||encoded +enterily||entirely +enviroiment||environment +enviroment||environment +environement||environment +environent||environment +eqivalent||equivalent +equiped||equipped +equivelant||equivalent +equivilant||equivalent +eror||error +estbalishment||establishment +etsablishment||establishment +etsbalishment||establishment +excecutable||executable +exceded||exceeded +excellant||excellent +existance||existence +existant||existent +exixt||exist +exlcude||exclude +exlcusive||exclusive +exmaple||example +expecially||especially +explicite||explicit +explicitely||explicitly +explict||explicit +explictly||explicitly +expresion||expression +exprimental||experimental +extened||extended +extensability||extensibility +extention||extension +extracter||extractor +faild||failed +faill||fail +failue||failure +failuer||failure +faireness||fairness +faliure||failure +familar||familiar +fatser||faster +feauture||feature +feautures||features +fetaure||feature +fetaures||features +fileystem||filesystem +finanize||finalize +findn||find +finilizes||finalizes +finsih||finish +flusing||flushing +folloing||following +followign||following +follwing||following +forseeable||foreseeable +forse||force +fortan||fortran +forwardig||forwarding +framwork||framework +frequncy||frequency +frome||from +fucntion||function +fuction||function +fuctions||functions +funcion||function +functionallity||functionality +functionaly||functionally +functionnality||functionality +functonality||functionality +funtion||function +funtions||functions +furthur||further +futhermore||furthermore +futrue||future +gaurenteed||guaranteed +generiously||generously +genric||generic +globel||global +grabing||grabbing +grahical||graphical +grahpical||graphical +grapic||graphic +guage||gauge +guarenteed||guaranteed +guarentee||guarantee +halfs||halves +hander||handler +handfull||handful +hanled||handled +happend||happened +harware||hardware +heirarchically||hierarchically +helpfull||helpful +hierachy||hierarchy +hierarchie||hierarchy +howver||however +hsould||should +hypter||hyper +identidier||identifier +imblance||imbalance +immeadiately||immediately +immedaite||immediate +immediatelly||immediately +immediatly||immediately +immidiate||immediate +impelentation||implementation +impementated||implemented +implemantation||implementation +implemenation||implementation +implementaiton||implementation +implementated||implemented +implemention||implementation +implemetation||implementation +implemntation||implementation +implentation||implementation +implmentation||implementation +implmenting||implementing +incomming||incoming +incompatabilities||incompatibilities +incompatable||incompatible +inconsistant||inconsistent +increas||increase +incrment||increment +indendation||indentation +indended||intended +independant||independent +independantly||independently +independed||independent +indiate||indicate +inexpect||inexpected +infomation||information +informatiom||information +informations||information +informtion||information +infromation||information +ingore||ignore +inital||initial +initalised||initialized +initalise||initialize +initalize||initialize +initation||initiation +initators||initiators +initializiation||initialization +initialzed||initialized +initilization||initialization +initilize||initialize +inofficial||unofficial +insititute||institute +instal||install +inteface||interface +integreated||integrated +integrety||integrity +integrey||integrity +intendet||intended +intented||intended +interanl||internal +interchangable||interchangeable +interferring||interfering +interger||integer +intermittant||intermittent +internel||internal +interoprability||interoperability +interrface||interface +interrrupt||interrupt +interrup||interrupt +interrups||interrupts +interruptted||interrupted +interupted||interrupted +interupt||interrupt +intial||initial +intialized||initialized +intialize||initialize +intregral||integral +intrrupt||interrupt +intuative||intuitive +invaid||invalid +invalde||invald +invalide||invalid +invididual||individual +invokation||invocation +invokations||invocations +irrelevent||irrelevant +isnt||isn't +isssue||issue +itslef||itself +jave||java +jeffies||jiffies +juse||just +jus||just +kown||known +langage||language +langauage||language +langauge||language +langugage||language +lauch||launch +layed||laid +leightweight||lightweight +lengh||length +lenght||length +lenth||length +lesstiff||lesstif +libaries||libraries +libary||library +librairies||libraries +libraris||libraries +licenceing||licencing +loggging||logging +loggin||login +logile||logfile +loosing||losing +losted||lost +machinary||machinery +maintainance||maintenance +maintainence||maintenance +maintan||maintain +makeing||making +malplaced||misplaced +malplace||misplace +managable||manageable +managment||management +mangement||management +manoeuvering||maneuvering +mappping||mapping +mathimatical||mathematical +mathimatic||mathematic +mathimatics||mathematics +maxium||maximum +mechamism||mechanism +meetign||meeting +ment||meant +mergable||mergeable +mesage||message +messags||messages +messgaes||messages +messsage||message +messsages||messages +microprocesspr||microprocessor +milliseonds||milliseconds +minumum||minimum +miscelleneous||miscellaneous +misformed||malformed +mispelled||misspelled +mispelt||misspelt +miximum||maximum +mmnemonic||mnemonic +mnay||many +modeled||modelled +modulues||modules +monochorome||monochrome +monochromo||monochrome +monocrome||monochrome +mopdule||module +mroe||more +mulitplied||multiplied +multidimensionnal||multidimensional +multple||multiple +mumber||number +muticast||multicast +mutiple||multiple +mutli||multi +nams||names +navagating||navigating +nead||need +neccecary||necessary +neccesary||necessary +neccessary||necessary +necesary||necessary +negaive||negative +negoitation||negotiation +negotation||negotiation +nerver||never +nescessary||necessary +nessessary||necessary +noticable||noticeable +notications||notifications +notifed||notified +numebr||number +numner||number +obtaion||obtain +occassionally||occasionally +occationally||occasionally +occurance||occurrence +occurances||occurrences +occured||occurred +occurence||occurrence +occure||occurred +occuring||occurring +offet||offset +omitt||omit +ommiting||omitting +ommitted||omitted +onself||oneself +ony||only +operatione||operation +opertaions||operations +optionnal||optional +optmizations||optimizations +orientatied||orientated +orientied||oriented +otherise||otherwise +ouput||output +overaall||overall +overhread||overhead +overlaping||overlapping +overriden||overridden +overun||overrun +pacakge||package +pachage||package +packacge||package +packege||package +packge||package +packtes||packets +pakage||package +pallette||palette +paln||plan +paramameters||parameters +paramater||parameter +parametes||parameters +parametised||parametrised +paramter||parameter +paramters||parameters +particuarly||particularly +particularily||particularly +pased||passed +passin||passing +pathes||paths +pecularities||peculiarities +peformance||performance +peice||piece +pendantic||pedantic +peprocessor||preprocessor +perfoming||performing +permissons||permissions +peroid||period +persistance||persistence +persistant||persistent +platfrom||platform +plattform||platform +pleaes||please +ploting||plotting +plugable||pluggable +poinnter||pointer +poiter||pointer +posible||possible +positon||position +possibilites||possibilities +powerfull||powerful +preceeded||preceded +preceeding||preceding +preceed||precede +precendence||precedence +precission||precision +preemptable||preemptible +prefered||preferred +prefferably||preferably +premption||preemption +prepaired||prepared +pressre||pressure +primative||primitive +princliple||principle +priorty||priority +privilaged||privileged +privilage||privilege +priviledge||privilege +priviledges||privileges +probaly||probably +procceed||proceed +proccesors||processors +procesed||processed +proces||process +processessing||processing +processess||processes +processpr||processor +processsed||processed +processsing||processing +procteted||protected +prodecure||procedure +progams||programs +progess||progress +programers||programmers +programm||program +programms||programs +progresss||progress +promiscous||promiscuous +promps||prompts +pronnounced||pronounced +prononciation||pronunciation +pronouce||pronounce +pronunce||pronounce +propery||property +propigate||propagate +propigation||propagation +propogate||propagate +prosess||process +protable||portable +protcol||protocol +protecion||protection +protocoll||protocol +psudo||pseudo +psuedo||pseudo +psychadelic||psychedelic +pwoer||power +quering||querying +raoming||roaming +reasearcher||researcher +reasearchers||researchers +reasearch||research +recepient||recipient +receving||receiving +recieved||received +recieve||receive +reciever||receiver +recieves||receives +recogniced||recognised +recognizeable||recognizable +recommanded||recommended +recyle||recycle +redircet||redirect +redirectrion||redirection +refcounf||refcount +refence||reference +refered||referred +referenace||reference +refering||referring +refernces||references +refernnce||reference +refrence||reference +registerd||registered +registeresd||registered +registes||registers +registraration||registration +regster||register +regualar||regular +reguator||regulator +regulamentations||regulations +reigstration||registration +releated||related +relevent||relevant +remoote||remote +remore||remote +removeable||removable +repectively||respectively +replacable||replaceable +replacments||replacements +replys||replies +reponse||response +representaion||representation +reqeust||request +requiere||require +requirment||requirement +requred||required +requried||required +requst||request +reseting||resetting +resizeable||resizable +resouces||resources +resoures||resources +responce||response +ressizes||resizes +ressource||resource +ressources||resources +retransmited||retransmitted +retreived||retrieved +retreive||retrieve +retrive||retrieve +retuned||returned +reudce||reduce +reuest||request +reuqest||request +reutnred||returned +rmeoved||removed +rmeove||remove +rmeoves||removes +rountine||routine +routins||routines +rquest||request +runing||running +runned||ran +runnning||running +runtine||runtime +sacrifying||sacrificing +safly||safely +safty||safety +savable||saveable +scaned||scanned +scaning||scanning +scarch||search +seach||search +searchs||searches +secquence||sequence +secund||second +segement||segment +senarios||scenarios +sentivite||sensitive +separatly||separately +sepcify||specify +sepc||spec +seperated||separated +seperately||separately +seperate||separate +seperatly||separately +seperator||separator +sepperate||separate +sequece||sequence +sequencial||sequential +serveral||several +setts||sets +settting||setting +shotdown||shutdown +shoud||should +shouldnt||shouldn't +shoule||should +shrinked||shrunk +siginificantly||significantly +signabl||signal +similary||similarly +similiar||similar +simlar||similar +simliar||similar +simpified||simplified +singaled||signaled +singal||signal +singed||signed +sleeped||slept +softwares||software +speach||speech +specfic||specific +speciefied||specified +specifc||specific +specifed||specified +specificatin||specification +specificaton||specification +specifing||specifying +specifiying||specifying +speficied||specified +speicify||specify +speling||spelling +spinlcok||spinlock +spinock||spinlock +splitted||split +spreaded||spread +sructure||structure +stablilization||stabilization +staically||statically +staion||station +standardss||standards +standartization||standardization +standart||standard +staticly||statically +stoped||stopped +stoppped||stopped +straming||streaming +struc||struct +structres||structures +stuct||struct +stucture||structure +sturcture||structure +subdirectoires||subdirectories +suble||subtle +substract||subtract +succesfully||successfully +succesful||successful +successfull||successful +sucessfully||successfully +sucess||success +superflous||superfluous +superseeded||superseded +suplied||supplied +suported||supported +suport||support +suppored||supported +supportin||supporting +suppoted||supported +suppported||supported +suppport||support +supress||suppress +surpresses||suppresses +susbsystem||subsystem +suspicously||suspiciously +swaping||swapping +switchs||switches +symetric||symmetric +synax||syntax +synchonized||synchronized +syncronize||synchronize +syncronizing||synchronizing +syncronus||synchronous +syste||system +sytem||system +sythesis||synthesis +taht||that +targetted||targeted +targetting||targeting +teh||the +temorary||temporary +temproarily||temporarily +thier||their +threds||threads +threshhold||threshold +throught||through +thses||these +tiggered||triggered +tipically||typically +tmis||this +torerable||tolerable +tramsmitted||transmitted +tramsmit||transmit +tranfer||transfer +transciever||transceiver +transferd||transferrd +transfered||transferred +transfering||transferring +transision||transition +transmittd||transmitted +transormed||transformed +trasmission||transmission +treshold||threshold +trigerring||triggering +trun||turn +ture||true +tyep||type +udpate||update +uesd||used +unconditionaly||unconditionally +underun||underrun +unecessary||unnecessary +unexecpted||unexpected +unexpectd||unexpected +unexpeted||unexpected +unfortunatelly||unfortunately +unifiy||unify +unintialized||uninitialized +unknonw||unknown +unknow||unknown +unkown||unknown +unneedingly||unnecessarily +unresgister||unregister +unsinged||unsigned +unstabel||unstable +unsuccessfull||unsuccessful +unsuported||unsupported +untill||until +unuseful||useless +upate||update +usefule||useful +usefull||useful +usege||usage +usera||users +usualy||usually +utilites||utilities +utillities||utilities +utilties||utilities +utiltity||utility +utitity||utility +utitlty||utility +vaid||valid +vaild||valid +valide||valid +variantions||variations +varient||variant +vaule||value +verbse||verbose +verisons||versions +verison||version +verson||version +vicefersa||vice-versa +virtal||virtual +virtaul||virtual +virtiual||virtual +visiters||visitors +vitual||virtual +wating||waiting +wether||whether +whataver||whatever +whcih||which +whenver||whenever +wheter||whether +whe||when +wierd||weird +wiil||will +wirte||write +withing||within +wnat||want +workarould||workaround +writeing||writing +writting||writing +zombe||zombie +zomebie||zombie diff --git a/drivers/staging/greybus/spelling.txt b/drivers/staging/greybus/spelling.txt deleted file mode 100644 index 946caf3bd694..000000000000 --- a/drivers/staging/greybus/spelling.txt +++ /dev/null @@ -1,1072 +0,0 @@ -# Originally from Debian's Lintian tool. Various false positives have been -# removed, and various additions have been made as they've been discovered -# in the kernel source. -# -# License: GPLv2 -# -# The format of each line is: -# mistake||correction -# -abandonning||abandoning -abigious||ambiguous -abitrate||arbitrate -abov||above -abreviated||abbreviated -absense||absence -absolut||absolute -absoulte||absolute -acccess||access -acceleratoin||acceleration -accelleration||acceleration -accesing||accessing -accesnt||accent -accessable||accessible -accesss||access -accidentaly||accidentally -accidentually||accidentally -accoding||according -accomodate||accommodate -accomodates||accommodates -accordign||according -accoring||according -accout||account -accquire||acquire -accquired||acquired -accross||across -acessable||accessible -acess||access -achitecture||architecture -acient||ancient -acitions||actions -acitve||active -acknowldegement||acknowldegement -acknowledgement||acknowledgment -ackowledge||acknowledge -ackowledged||acknowledged -acording||according -activete||activate -acumulating||accumulating -adapater||adapter -addional||additional -additionaly||additionally -addres||address -addreses||addresses -addresss||address -aditional||additional -aditionally||additionally -aditionaly||additionally -adminstrative||administrative -adress||address -adresses||addresses -adviced||advised -afecting||affecting -agaist||against -albumns||albums -alegorical||allegorical -algorith||algorithm -algorithmical||algorithmically -algoritm||algorithm -algoritms||algorithms -algorrithm||algorithm -algorritm||algorithm -allign||align -allocatrd||allocated -allocte||allocate -allpication||application -alocate||allocate -alogirhtms||algorithms -alogrithm||algorithm -alot||a lot -alow||allow -alows||allows -altough||although -alue||value -ambigious||ambiguous -amoung||among -amout||amount -analysator||analyzer -ang||and -anniversery||anniversary -annoucement||announcement -anomolies||anomalies -anomoly||anomaly -anway||anyway -aplication||application -appearence||appearance -applicaion||application -appliction||application -applictions||applications -appplications||applications -appropiate||appropriate -appropriatly||appropriately -approriate||appropriate -approriately||appropriately -apropriate||appropriate -aquainted||acquainted -aquired||acquired -aquisition||acquisition -arbitary||arbitrary -architechture||architecture -arguement||argument -arguements||arguments -aritmetic||arithmetic -arne't||aren't -arraival||arrival -artifical||artificial -artillary||artillery -asign||assign -assertation||assertion -assiged||assigned -assigment||assignment -assigments||assignments -assistent||assistant -assocation||association -associcated||associated -assotiated||associated -assum||assume -assumtpion||assumption -asuming||assuming -asycronous||asynchronous -asynchnous||asynchronous -atomatically||automatically -atomicly||atomically -attachement||attachment -attched||attached -attemps||attempts -attruibutes||attributes -authentification||authentication -automaticaly||automatically -automaticly||automatically -automatize||automate -automatized||automated -automatizes||automates -autonymous||autonomous -auxillary||auxiliary -auxilliary||auxiliary -avaiable||available -avaible||available -availabe||available -availabled||available -availablity||availability -availale||available -availavility||availability -availble||available -availiable||available -avalable||available -avaliable||available -aysnc||async -backgroud||background -backword||backward -backwords||backwards -bahavior||behavior -bakup||backup -baloon||balloon -baloons||balloons -bandwith||bandwidth -batery||battery -beacuse||because -becasue||because -becomming||becoming -becuase||because -beeing||being -befor||before -begining||beginning -beter||better -betweeen||between -bianries||binaries -bitmast||bitmask -boardcast||broadcast -borad||board -boundry||boundary -brievely||briefly -broadcat||broadcast -cacluated||calculated -caculation||calculation -calender||calendar -calle||called -calucate||calculate -calulate||calculate -cancelation||cancellation -capabilites||capabilities -capabitilies||capabilities -capatibilities||capabilities -carefuly||carefully -cariage||carriage -catagory||category -cehck||check -challange||challenge -challanges||challenges -chanell||channel -changable||changeable -channle||channel -channnel||channel -charachter||character -charachters||characters -charactor||character -charater||character -charaters||characters -charcter||character -chcek||check -chck||check -checksuming||checksumming -childern||children -childs||children -chiled||child -chked||checked -chnage||change -chnages||changes -chnnel||channel -choosen||chosen -chouse||chose -circumvernt||circumvent -claread||cleared -clared||cleared -closeing||closing -clustred||clustered -collapsable||collapsible -colorfull||colorful -comand||command -comit||commit -commerical||commercial -comming||coming -comminucation||communication -commited||committed -commiting||committing -committ||commit -commoditiy||commodity -compability||compatibility -compaibility||compatibility -compatability||compatibility -compatable||compatible -compatibiliy||compatibility -compatibilty||compatibility -compatiblity||compatibility -competion||completion -compilant||compliant -compleatly||completely -completly||completely -complient||compliant -componnents||components -compres||compress -compresion||compression -comression||compression -comunication||communication -conbination||combination -conditionaly||conditionally -conected||connected -configuratoin||configuration -configuraton||configuration -configuretion||configuration -conider||consider -conjuction||conjunction -connectinos||connections -connnection||connection -connnections||connections -consistancy||consistency -consistant||consistent -containes||contains -containts||contains -contaisn||contains -contant||contact -contence||contents -continous||continuous -continously||continuously -continueing||continuing -contraints||constraints -controled||controlled -controler||controller -controll||control -contruction||construction -contry||country -convertion||conversion -convertor||converter -convienient||convenient -convinient||convenient -corected||corrected -correponding||corresponding -correponds||corresponds -correspoding||corresponding -cotrol||control -couter||counter -coutner||counter -cryptocraphic||cryptographic -cunter||counter -curently||currently -dafault||default -deafult||default -deamon||daemon -decompres||decompress -decription||description -defailt||default -defferred||deferred -definate||definite -definately||definitely -defintion||definition -defintions||definitions -defualt||default -defult||default -deivce||device -delared||declared -delare||declare -delares||declares -delaring||declaring -delemiter||delimiter -dependancies||dependencies -dependancy||dependency -dependant||dependent -depreacted||deprecated -depreacte||deprecate -desactivate||deactivate -desciptors||descriptors -descripton||description -descrition||description -descritptor||descriptor -desctiptor||descriptor -desriptor||descriptor -desriptors||descriptors -destory||destroy -destoryed||destroyed -destorys||destroys -destroied||destroyed -detabase||database -develope||develop -developement||development -developped||developed -developpement||development -developper||developer -developpment||development -deveolpment||development -devided||divided -deviece||device -diable||disable -dictionnary||dictionary -didnt||didn't -diferent||different -differrence||difference -difinition||definition -diplay||display -direectly||directly -disapear||disappear -disapeared||disappeared -disappared||disappeared -disconnet||disconnect -discontinous||discontinuous -dispertion||dispersion -dissapears||disappears -distiction||distinction -docuentation||documentation -documantation||documentation -documentaion||documentation -documment||document -doesnt||doesn't -dorp||drop -dosen||doesn -downlad||download -downlads||downloads -druing||during -dynmaic||dynamic -easilly||easily -ecspecially||especially -edditable||editable -editting||editing -efficently||efficiently -ehther||ether -eigth||eight -eletronic||electronic -enabledi||enabled -enchanced||enhanced -encorporating||incorporating -encrupted||encrypted -encrypiton||encryption -endianess||endianness -enhaced||enhanced -enlightnment||enlightenment -enocded||encoded -enterily||entirely -enviroiment||environment -enviroment||environment -environement||environment -environent||environment -eqivalent||equivalent -equiped||equipped -equivelant||equivalent -equivilant||equivalent -eror||error -estbalishment||establishment -etsablishment||establishment -etsbalishment||establishment -excecutable||executable -exceded||exceeded -excellant||excellent -existance||existence -existant||existent -exixt||exist -exlcude||exclude -exlcusive||exclusive -exmaple||example -expecially||especially -explicite||explicit -explicitely||explicitly -explict||explicit -explictly||explicitly -expresion||expression -exprimental||experimental -extened||extended -extensability||extensibility -extention||extension -extracter||extractor -faild||failed -faill||fail -failue||failure -failuer||failure -faireness||fairness -faliure||failure -familar||familiar -fatser||faster -feauture||feature -feautures||features -fetaure||feature -fetaures||features -fileystem||filesystem -finanize||finalize -findn||find -finilizes||finalizes -finsih||finish -flusing||flushing -folloing||following -followign||following -follwing||following -forseeable||foreseeable -forse||force -fortan||fortran -forwardig||forwarding -framwork||framework -frequncy||frequency -frome||from -fucntion||function -fuction||function -fuctions||functions -funcion||function -functionallity||functionality -functionaly||functionally -functionnality||functionality -functonality||functionality -funtion||function -funtions||functions -furthur||further -futhermore||furthermore -futrue||future -gaurenteed||guaranteed -generiously||generously -genric||generic -globel||global -grabing||grabbing -grahical||graphical -grahpical||graphical -grapic||graphic -guage||gauge -guarenteed||guaranteed -guarentee||guarantee -halfs||halves -hander||handler -handfull||handful -hanled||handled -happend||happened -harware||hardware -heirarchically||hierarchically -helpfull||helpful -hierachy||hierarchy -hierarchie||hierarchy -howver||however -hsould||should -hypter||hyper -identidier||identifier -imblance||imbalance -immeadiately||immediately -immedaite||immediate -immediatelly||immediately -immediatly||immediately -immidiate||immediate -impelentation||implementation -impementated||implemented -implemantation||implementation -implemenation||implementation -implementaiton||implementation -implementated||implemented -implemention||implementation -implemetation||implementation -implemntation||implementation -implentation||implementation -implmentation||implementation -implmenting||implementing -incomming||incoming -incompatabilities||incompatibilities -incompatable||incompatible -inconsistant||inconsistent -increas||increase -incrment||increment -indendation||indentation -indended||intended -independant||independent -independantly||independently -independed||independent -indiate||indicate -inexpect||inexpected -infomation||information -informatiom||information -informations||information -informtion||information -infromation||information -ingore||ignore -inital||initial -initalised||initialized -initalise||initialize -initalize||initialize -initation||initiation -initators||initiators -initializiation||initialization -initialzed||initialized -initilization||initialization -initilize||initialize -inofficial||unofficial -insititute||institute -instal||install -inteface||interface -integreated||integrated -integrety||integrity -integrey||integrity -intendet||intended -intented||intended -interanl||internal -interchangable||interchangeable -interferring||interfering -interger||integer -intermittant||intermittent -internel||internal -interoprability||interoperability -interrface||interface -interrrupt||interrupt -interrup||interrupt -interrups||interrupts -interruptted||interrupted -interupted||interrupted -interupt||interrupt -intial||initial -intialized||initialized -intialize||initialize -intregral||integral -intrrupt||interrupt -intuative||intuitive -invaid||invalid -invalde||invald -invalide||invalid -invididual||individual -invokation||invocation -invokations||invocations -irrelevent||irrelevant -isnt||isn't -isssue||issue -itslef||itself -jave||java -jeffies||jiffies -juse||just -jus||just -kown||known -langage||language -langauage||language -langauge||language -langugage||language -lauch||launch -layed||laid -leightweight||lightweight -lengh||length -lenght||length -lenth||length -lesstiff||lesstif -libaries||libraries -libary||library -librairies||libraries -libraris||libraries -licenceing||licencing -loggging||logging -loggin||login -logile||logfile -loosing||losing -losted||lost -machinary||machinery -maintainance||maintenance -maintainence||maintenance -maintan||maintain -makeing||making -malplaced||misplaced -malplace||misplace -managable||manageable -managment||management -mangement||management -manoeuvering||maneuvering -mappping||mapping -mathimatical||mathematical -mathimatic||mathematic -mathimatics||mathematics -maxium||maximum -mechamism||mechanism -meetign||meeting -ment||meant -mergable||mergeable -mesage||message -messags||messages -messgaes||messages -messsage||message -messsages||messages -microprocesspr||microprocessor -milliseonds||milliseconds -minumum||minimum -miscelleneous||miscellaneous -misformed||malformed -mispelled||misspelled -mispelt||misspelt -miximum||maximum -mmnemonic||mnemonic -mnay||many -modeled||modelled -modulues||modules -monochorome||monochrome -monochromo||monochrome -monocrome||monochrome -mopdule||module -mroe||more -mulitplied||multiplied -multidimensionnal||multidimensional -multple||multiple -mumber||number -muticast||multicast -mutiple||multiple -mutli||multi -nams||names -navagating||navigating -nead||need -neccecary||necessary -neccesary||necessary -neccessary||necessary -necesary||necessary -negaive||negative -negoitation||negotiation -negotation||negotiation -nerver||never -nescessary||necessary -nessessary||necessary -noticable||noticeable -notications||notifications -notifed||notified -numebr||number -numner||number -obtaion||obtain -occassionally||occasionally -occationally||occasionally -occurance||occurrence -occurances||occurrences -occured||occurred -occurence||occurrence -occure||occurred -occuring||occurring -offet||offset -omitt||omit -ommiting||omitting -ommitted||omitted -onself||oneself -ony||only -operatione||operation -opertaions||operations -optionnal||optional -optmizations||optimizations -orientatied||orientated -orientied||oriented -otherise||otherwise -ouput||output -overaall||overall -overhread||overhead -overlaping||overlapping -overriden||overridden -overun||overrun -pacakge||package -pachage||package -packacge||package -packege||package -packge||package -packtes||packets -pakage||package -pallette||palette -paln||plan -paramameters||parameters -paramater||parameter -parametes||parameters -parametised||parametrised -paramter||parameter -paramters||parameters -particuarly||particularly -particularily||particularly -pased||passed -passin||passing -pathes||paths -pecularities||peculiarities -peformance||performance -peice||piece -pendantic||pedantic -peprocessor||preprocessor -perfoming||performing -permissons||permissions -peroid||period -persistance||persistence -persistant||persistent -platfrom||platform -plattform||platform -pleaes||please -ploting||plotting -plugable||pluggable -poinnter||pointer -poiter||pointer -posible||possible -positon||position -possibilites||possibilities -powerfull||powerful -preceeded||preceded -preceeding||preceding -preceed||precede -precendence||precedence -precission||precision -preemptable||preemptible -prefered||preferred -prefferably||preferably -premption||preemption -prepaired||prepared -pressre||pressure -primative||primitive -princliple||principle -priorty||priority -privilaged||privileged -privilage||privilege -priviledge||privilege -priviledges||privileges -probaly||probably -procceed||proceed -proccesors||processors -procesed||processed -proces||process -processessing||processing -processess||processes -processpr||processor -processsed||processed -processsing||processing -procteted||protected -prodecure||procedure -progams||programs -progess||progress -programers||programmers -programm||program -programms||programs -progresss||progress -promiscous||promiscuous -promps||prompts -pronnounced||pronounced -prononciation||pronunciation -pronouce||pronounce -pronunce||pronounce -propery||property -propigate||propagate -propigation||propagation -propogate||propagate -prosess||process -protable||portable -protcol||protocol -protecion||protection -protocoll||protocol -psudo||pseudo -psuedo||pseudo -psychadelic||psychedelic -pwoer||power -quering||querying -raoming||roaming -reasearcher||researcher -reasearchers||researchers -reasearch||research -recepient||recipient -receving||receiving -recieved||received -recieve||receive -reciever||receiver -recieves||receives -recogniced||recognised -recognizeable||recognizable -recommanded||recommended -recyle||recycle -redircet||redirect -redirectrion||redirection -refcounf||refcount -refence||reference -refered||referred -referenace||reference -refering||referring -refernces||references -refernnce||reference -refrence||reference -registerd||registered -registeresd||registered -registes||registers -registraration||registration -regster||register -regualar||regular -reguator||regulator -regulamentations||regulations -reigstration||registration -releated||related -relevent||relevant -remoote||remote -remore||remote -removeable||removable -repectively||respectively -replacable||replaceable -replacments||replacements -replys||replies -reponse||response -representaion||representation -reqeust||request -requiere||require -requirment||requirement -requred||required -requried||required -requst||request -reseting||resetting -resizeable||resizable -resouces||resources -resoures||resources -responce||response -ressizes||resizes -ressource||resource -ressources||resources -retransmited||retransmitted -retreived||retrieved -retreive||retrieve -retrive||retrieve -retuned||returned -reudce||reduce -reuest||request -reuqest||request -reutnred||returned -rmeoved||removed -rmeove||remove -rmeoves||removes -rountine||routine -routins||routines -rquest||request -runing||running -runned||ran -runnning||running -runtine||runtime -sacrifying||sacrificing -safly||safely -safty||safety -savable||saveable -scaned||scanned -scaning||scanning -scarch||search -seach||search -searchs||searches -secquence||sequence -secund||second -segement||segment -senarios||scenarios -sentivite||sensitive -separatly||separately -sepcify||specify -sepc||spec -seperated||separated -seperately||separately -seperate||separate -seperatly||separately -seperator||separator -sepperate||separate -sequece||sequence -sequencial||sequential -serveral||several -setts||sets -settting||setting -shotdown||shutdown -shoud||should -shouldnt||shouldn't -shoule||should -shrinked||shrunk -siginificantly||significantly -signabl||signal -similary||similarly -similiar||similar -simlar||similar -simliar||similar -simpified||simplified -singaled||signaled -singal||signal -singed||signed -sleeped||slept -softwares||software -speach||speech -specfic||specific -speciefied||specified -specifc||specific -specifed||specified -specificatin||specification -specificaton||specification -specifing||specifying -specifiying||specifying -speficied||specified -speicify||specify -speling||spelling -spinlcok||spinlock -spinock||spinlock -splitted||split -spreaded||spread -sructure||structure -stablilization||stabilization -staically||statically -staion||station -standardss||standards -standartization||standardization -standart||standard -staticly||statically -stoped||stopped -stoppped||stopped -straming||streaming -struc||struct -structres||structures -stuct||struct -stucture||structure -sturcture||structure -subdirectoires||subdirectories -suble||subtle -substract||subtract -succesfully||successfully -succesful||successful -successfull||successful -sucessfully||successfully -sucess||success -superflous||superfluous -superseeded||superseded -suplied||supplied -suported||supported -suport||support -suppored||supported -supportin||supporting -suppoted||supported -suppported||supported -suppport||support -supress||suppress -surpresses||suppresses -susbsystem||subsystem -suspicously||suspiciously -swaping||swapping -switchs||switches -symetric||symmetric -synax||syntax -synchonized||synchronized -syncronize||synchronize -syncronizing||synchronizing -syncronus||synchronous -syste||system -sytem||system -sythesis||synthesis -taht||that -targetted||targeted -targetting||targeting -teh||the -temorary||temporary -temproarily||temporarily -thier||their -threds||threads -threshhold||threshold -throught||through -thses||these -tiggered||triggered -tipically||typically -tmis||this -torerable||tolerable -tramsmitted||transmitted -tramsmit||transmit -tranfer||transfer -transciever||transceiver -transferd||transferrd -transfered||transferred -transfering||transferring -transision||transition -transmittd||transmitted -transormed||transformed -trasmission||transmission -treshold||threshold -trigerring||triggering -trun||turn -ture||true -tyep||type -udpate||update -uesd||used -unconditionaly||unconditionally -underun||underrun -unecessary||unnecessary -unexecpted||unexpected -unexpectd||unexpected -unexpeted||unexpected -unfortunatelly||unfortunately -unifiy||unify -unintialized||uninitialized -unknonw||unknown -unknow||unknown -unkown||unknown -unneedingly||unnecessarily -unresgister||unregister -unsinged||unsigned -unstabel||unstable -unsuccessfull||unsuccessful -unsuported||unsupported -untill||until -unuseful||useless -upate||update -usefule||useful -usefull||useful -usege||usage -usera||users -usualy||usually -utilites||utilities -utillities||utilities -utilties||utilities -utiltity||utility -utitity||utility -utitlty||utility -vaid||valid -vaild||valid -valide||valid -variantions||variations -varient||variant -vaule||value -verbse||verbose -verisons||versions -verison||version -verson||version -vicefersa||vice-versa -virtal||virtual -virtaul||virtual -virtiual||virtual -visiters||visitors -vitual||virtual -wating||waiting -wether||whether -whataver||whatever -whcih||which -whenver||whenever -wheter||whether -whe||when -wierd||weird -wiil||will -wirte||write -withing||within -wnat||want -workarould||workaround -writeing||writing -writting||writing -zombe||zombie -zomebie||zombie -- cgit v1.2.3-59-g8ed1b From 88a3011e91f7c557798301d47f830320580795cf Mon Sep 17 00:00:00 2001 From: Georgi Dobrev <dobrev_georgi@projectara.com> Date: Thu, 24 Mar 2016 13:37:21 +0200 Subject: greybus: Added a sysfs entry to power down the SVC Added a sysfs entry called pwr_off. When a "1" is passed to it, it sends a GB_SVC_TYPE_PWR_DOWN command to the SVC, powering it down along with the switch and INA231 chips. Testing Done: Tested on EVT1_5, works. Signed-off-by: Georgi Dobrev <dobrev_georgi@projectara.com> --- drivers/staging/greybus/greybus_protocols.h | 1 + drivers/staging/greybus/svc.c | 28 ++++++++++++++++++++++++++++ drivers/staging/greybus/svc.h | 1 + 3 files changed, 30 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 06888e029473..a160e73a76a9 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -798,6 +798,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_EJECT 0x11 #define GB_SVC_TYPE_KEY_EVENT 0x12 #define GB_SVC_TYPE_PING 0x13 +#define GB_SVC_TYPE_PWR_DOWN 0x1d /* * SVC version request/response has the same payload as diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index a19e575de029..f96c645558be 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -99,11 +99,31 @@ static ssize_t watchdog_store(struct device *dev, } static DEVICE_ATTR_RW(watchdog); +static ssize_t pwr_off_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct gb_svc *svc = to_gb_svc(dev); + int retval; + bool user_request; + + retval = strtobool(buf, &user_request); + if (retval) { + return retval; + } + if (user_request) { + retval = gb_svc_pwr_off(svc); + } + return len; +} +static DEVICE_ATTR_WO(pwr_off); + static struct attribute *svc_attrs[] = { &dev_attr_endo_id.attr, &dev_attr_ap_intf_id.attr, &dev_attr_intf_eject.attr, &dev_attr_watchdog.attr, + &dev_attr_pwr_off.attr, NULL, }; ATTRIBUTE_GROUPS(svc); @@ -320,6 +340,14 @@ int gb_svc_ping(struct gb_svc *svc) } EXPORT_SYMBOL_GPL(gb_svc_ping); +int gb_svc_pwr_off(struct gb_svc *svc) +{ + return gb_operation_sync_timeout(svc->connection, GB_SVC_TYPE_PWR_DOWN, + NULL, 0, NULL, 0, + GB_OPERATION_TIMEOUT_DEFAULT * 2); +} +EXPORT_SYMBOL_GPL(gb_svc_pwr_off); + static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 8950baff9aef..09d868877c82 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -71,6 +71,7 @@ void gb_svc_watchdog_destroy(struct gb_svc *svc); bool gb_svc_watchdog_enabled(struct gb_svc *svc); int gb_svc_watchdog_enable(struct gb_svc *svc); int gb_svc_watchdog_disable(struct gb_svc *svc); +int gb_svc_pwr_off(struct gb_svc *svc); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From f053f44c9f7f85dd2bab589930d5f3171bb1eaf7 Mon Sep 17 00:00:00 2001 From: Akash Choudhari <akashtc@google.com> Date: Fri, 1 Apr 2016 20:10:01 -0700 Subject: greybus: Revert "Added a sysfs entry to power down the SVC" This reverts commit a1d8f2c3856804ed26157104bb203edf4c882a6c. --- drivers/staging/greybus/greybus_protocols.h | 1 - drivers/staging/greybus/svc.c | 28 ---------------------------- drivers/staging/greybus/svc.h | 1 - 3 files changed, 30 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index a160e73a76a9..06888e029473 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -798,7 +798,6 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_EJECT 0x11 #define GB_SVC_TYPE_KEY_EVENT 0x12 #define GB_SVC_TYPE_PING 0x13 -#define GB_SVC_TYPE_PWR_DOWN 0x1d /* * SVC version request/response has the same payload as diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index f96c645558be..a19e575de029 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -99,31 +99,11 @@ static ssize_t watchdog_store(struct device *dev, } static DEVICE_ATTR_RW(watchdog); -static ssize_t pwr_off_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t len) -{ - struct gb_svc *svc = to_gb_svc(dev); - int retval; - bool user_request; - - retval = strtobool(buf, &user_request); - if (retval) { - return retval; - } - if (user_request) { - retval = gb_svc_pwr_off(svc); - } - return len; -} -static DEVICE_ATTR_WO(pwr_off); - static struct attribute *svc_attrs[] = { &dev_attr_endo_id.attr, &dev_attr_ap_intf_id.attr, &dev_attr_intf_eject.attr, &dev_attr_watchdog.attr, - &dev_attr_pwr_off.attr, NULL, }; ATTRIBUTE_GROUPS(svc); @@ -340,14 +320,6 @@ int gb_svc_ping(struct gb_svc *svc) } EXPORT_SYMBOL_GPL(gb_svc_ping); -int gb_svc_pwr_off(struct gb_svc *svc) -{ - return gb_operation_sync_timeout(svc->connection, GB_SVC_TYPE_PWR_DOWN, - NULL, 0, NULL, 0, - GB_OPERATION_TIMEOUT_DEFAULT * 2); -} -EXPORT_SYMBOL_GPL(gb_svc_pwr_off); - static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 09d868877c82..8950baff9aef 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -71,7 +71,6 @@ void gb_svc_watchdog_destroy(struct gb_svc *svc); bool gb_svc_watchdog_enabled(struct gb_svc *svc); int gb_svc_watchdog_enable(struct gb_svc *svc); int gb_svc_watchdog_disable(struct gb_svc *svc); -int gb_svc_pwr_off(struct gb_svc *svc); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From 0c35631bce4d0bece5a8e823b7f9ef115c712c4f Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Fri, 1 Apr 2016 11:15:42 +0530 Subject: greybus: checkpatch.pl: Mark --no-tree as default option Don't know why, but checkpatch checks if we are running it from top of a kernel tree or not, but then it also provides an option to suppress the warning using --no-tree. Instead of forcing everyone to use this every time, lets make this behavior default. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/scripts/checkpatch.pl b/drivers/staging/greybus/scripts/checkpatch.pl index d574d13ba963..9930b73211b3 100755 --- a/drivers/staging/greybus/scripts/checkpatch.pl +++ b/drivers/staging/greybus/scripts/checkpatch.pl @@ -19,7 +19,7 @@ my $V = '0.32'; use Getopt::Long qw(:config no_auto_abbrev); my $quiet = 0; -my $tree = 1; +my $tree = 0; my $chk_signoff = 1; my $chk_patch = 1; my $tst_only; -- cgit v1.2.3-59-g8ed1b From 5a53e02eaf223c42c6ca8d9664c1191ea7c2b106 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sun, 3 Apr 2016 12:18:35 +0530 Subject: greybus: firmware: Rename to bootrom protocol Align with Greybus specifications and rename Firmware Protocol driver as Bootrom Protocol driver. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/bootrom.c | 382 ++++++++++++++++++++++++++++ drivers/staging/greybus/bootrom.h | 16 ++ drivers/staging/greybus/core.c | 14 +- drivers/staging/greybus/firmware.c | 382 ---------------------------- drivers/staging/greybus/firmware.h | 16 -- drivers/staging/greybus/greybus_manifest.h | 4 +- drivers/staging/greybus/greybus_protocols.h | 80 +++--- 8 files changed, 448 insertions(+), 448 deletions(-) create mode 100644 drivers/staging/greybus/bootrom.c create mode 100644 drivers/staging/greybus/bootrom.h delete mode 100644 drivers/staging/greybus/firmware.c delete mode 100644 drivers/staging/greybus/firmware.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index ec92891a5773..65259ea9d111 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -9,7 +9,7 @@ greybus-y := core.o \ control.o \ svc.o \ svc_watchdog.o \ - firmware.o \ + bootrom.o \ operation.o \ legacy.o diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c new file mode 100644 index 000000000000..3cbe9feff53d --- /dev/null +++ b/drivers/staging/greybus/bootrom.c @@ -0,0 +1,382 @@ +/* + * BOOTROM Greybus driver. + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include <linux/firmware.h> + +#include "bootrom.h" +#include "greybus.h" + + +struct gb_bootrom { + struct gb_connection *connection; + const struct firmware *fw; + u8 protocol_major; + u8 protocol_minor; +}; + +static void free_firmware(struct gb_bootrom *bootrom) +{ + release_firmware(bootrom->fw); + bootrom->fw = NULL; +} + +/* + * The es2 chip doesn't have VID/PID programmed into the hardware and we need to + * hack that up to distinguish different modules and their firmware blobs. + * + * This fetches VID/PID (over bootrom protocol) for es2 chip only, when VID/PID + * already sent during hotplug are 0. + * + * Otherwise, we keep intf->vendor_id/product_id same as what's passed + * during hotplug. + */ +static void bootrom_es2_fixup_vid_pid(struct gb_bootrom *bootrom) +{ + struct gb_bootrom_get_vid_pid_response response; + struct gb_connection *connection = bootrom->connection; + struct gb_interface *intf = connection->bundle->intf; + int ret; + + if (!(intf->quirks & GB_INTERFACE_QUIRK_NO_ARA_IDS)) + return; + + ret = gb_operation_sync(connection, GB_BOOTROM_TYPE_GET_VID_PID, + NULL, 0, &response, sizeof(response)); + if (ret) { + dev_err(&connection->bundle->dev, + "Bootrom get vid/pid operation failed (%d)\n", ret); + return; + } + + /* + * NOTE: This is hacked, so that the same values of VID/PID can be used + * by next firmware level as well. The uevent for bootrom will still + * have VID/PID as 0, though after this point the sysfs files will start + * showing the updated values. But yeah, that's a bit racy as the same + * sysfs files would be showing 0 before this point. + */ + intf->vendor_id = le32_to_cpu(response.vendor_id); + intf->product_id = le32_to_cpu(response.product_id); + + dev_dbg(&connection->bundle->dev, "Bootrom got vid (0x%x)/pid (0x%x)\n", + intf->vendor_id, intf->product_id); +} + +/* This returns path of the firmware blob on the disk */ +static int download_firmware(struct gb_bootrom *bootrom, u8 stage) +{ + struct gb_connection *connection = bootrom->connection; + struct gb_interface *intf = connection->bundle->intf; + char firmware_name[48]; + int rc; + + /* Already have a firmware, free it */ + if (bootrom->fw) + free_firmware(bootrom); + + /* + * Create firmware name + * + * XXX Name it properly.. + */ + snprintf(firmware_name, sizeof(firmware_name), + "ara_%08x_%08x_%08x_%08x_%02x.tftf", + intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, + intf->vendor_id, intf->product_id, stage); + + // FIXME: + // Turn to dev_dbg later after everyone has valid bootloaders with good + // ids, but leave this as dev_info for now to make it easier to track + // down "empty" vid/pid modules. + dev_info(&connection->bundle->dev, "Firmware file '%s' requested\n", + firmware_name); + + rc = request_firmware(&bootrom->fw, firmware_name, + &connection->bundle->dev); + if (rc) + dev_err(&connection->bundle->dev, + "Firware request for %s has failed : %d", + firmware_name, rc); + return rc; +} + +static int gb_bootrom_firmware_size_request(struct gb_operation *op) +{ + struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); + struct gb_bootrom_firmware_size_request *size_request = op->request->payload; + struct gb_bootrom_firmware_size_response *size_response; + struct device *dev = &op->connection->bundle->dev; + int ret; + + if (op->request->payload_size != sizeof(*size_request)) { + dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", + __func__, op->request->payload_size, + sizeof(*size_request)); + return -EINVAL; + } + + ret = download_firmware(bootrom, size_request->stage); + if (ret) { + dev_err(dev, "%s: failed to download firmware (%d)\n", __func__, + ret); + return ret; + } + + if (!gb_operation_response_alloc(op, sizeof(*size_response), + GFP_KERNEL)) { + dev_err(dev, "%s: error allocating response\n", __func__); + free_firmware(bootrom); + return -ENOMEM; + } + + size_response = op->response->payload; + size_response->size = cpu_to_le32(bootrom->fw->size); + + dev_dbg(dev, "%s: firmware size %d bytes\n", __func__, size_response->size); + + return 0; +} + +static int gb_bootrom_get_firmware(struct gb_operation *op) +{ + struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); + const struct firmware *fw = bootrom->fw; + struct gb_bootrom_get_firmware_request *firmware_request; + struct gb_bootrom_get_firmware_response *firmware_response; + struct device *dev = &op->connection->bundle->dev; + unsigned int offset, size; + + if (op->request->payload_size != sizeof(*firmware_request)) { + dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", + __func__, op->request->payload_size, + sizeof(*firmware_request)); + return -EINVAL; + } + + if (!fw) { + dev_err(dev, "%s: firmware not available\n", __func__); + return -EINVAL; + } + + firmware_request = op->request->payload; + offset = le32_to_cpu(firmware_request->offset); + size = le32_to_cpu(firmware_request->size); + + if (offset >= fw->size || size > fw->size - offset) { + dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", + offset, size); + return -EINVAL; + } + + if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, + GFP_KERNEL)) { + dev_err(dev, "%s: error allocating response\n", __func__); + return -ENOMEM; + } + + firmware_response = op->response->payload; + memcpy(firmware_response->data, fw->data + offset, size); + + dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, + size); + + return 0; +} + +static int gb_bootrom_ready_to_boot(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_bootrom_ready_to_boot_request *rtb_request; + struct device *dev = &connection->bundle->dev; + u8 status; + + if (op->request->payload_size != sizeof(*rtb_request)) { + dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", + __func__, op->request->payload_size, + sizeof(*rtb_request)); + return -EINVAL; + } + + rtb_request = op->request->payload; + status = rtb_request->status; + + /* Return error if the blob was invalid */ + if (status == GB_BOOTROM_BOOT_STATUS_INVALID) + return -EINVAL; + + /* + * XXX Should we return error for insecure firmware? + */ + dev_dbg(dev, "ready to boot: 0x%x, 0\n", status); + + return 0; +} + +static int gb_bootrom_request_handler(struct gb_operation *op) +{ + u8 type = op->type; + + switch (type) { + case GB_BOOTROM_TYPE_FIRMWARE_SIZE: + return gb_bootrom_firmware_size_request(op); + case GB_BOOTROM_TYPE_GET_FIRMWARE: + return gb_bootrom_get_firmware(op); + case GB_BOOTROM_TYPE_READY_TO_BOOT: + return gb_bootrom_ready_to_boot(op); + default: + dev_err(&op->connection->bundle->dev, + "unsupported request: %u\n", type); + return -EINVAL; + } +} + +static int gb_bootrom_get_version(struct gb_bootrom *bootrom) +{ + struct gb_bundle *bundle = bootrom->connection->bundle; + struct gb_bootrom_version_request request; + struct gb_bootrom_version_response response; + int ret; + + request.major = GB_BOOTROM_VERSION_MAJOR; + request.minor = GB_BOOTROM_VERSION_MINOR; + + ret = gb_operation_sync(bootrom->connection, + GB_BOOTROM_TYPE_VERSION, + &request, sizeof(request), &response, + sizeof(response)); + if (ret) { + dev_err(&bundle->dev, + "failed to get protocol version: %d\n", + ret); + return ret; + } + + if (response.major > request.major) { + dev_err(&bundle->dev, + "unsupported major protocol version (%u > %u)\n", + response.major, request.major); + return -ENOTSUPP; + } + + bootrom->protocol_major = response.major; + bootrom->protocol_minor = response.minor; + + dev_dbg(&bundle->dev, "%s - %u.%u\n", __func__, response.major, + response.minor); + + return 0; +} + +static int gb_bootrom_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; + struct gb_bootrom *bootrom; + int ret; + + if (bundle->num_cports != 1) + return -ENODEV; + + cport_desc = &bundle->cport_desc[0]; + if (cport_desc->protocol_id != GREYBUS_PROTOCOL_BOOTROM) + return -ENODEV; + + bootrom = kzalloc(sizeof(*bootrom), GFP_KERNEL); + if (!bootrom) + return -ENOMEM; + + connection = gb_connection_create(bundle, + le16_to_cpu(cport_desc->id), + gb_bootrom_request_handler); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto err_free_bootrom; + } + + gb_connection_set_data(connection, bootrom); + + bootrom->connection = connection; + + greybus_set_drvdata(bundle, bootrom); + + ret = gb_connection_enable_tx(connection); + if (ret) + goto err_connection_destroy; + + ret = gb_bootrom_get_version(bootrom); + if (ret) + goto err_connection_disable; + + bootrom_es2_fixup_vid_pid(bootrom); + + ret = gb_connection_enable(connection); + if (ret) + goto err_connection_disable; + + /* Tell bootrom we're ready. */ + ret = gb_operation_sync(connection, GB_BOOTROM_TYPE_AP_READY, NULL, 0, + NULL, 0); + if (ret) { + dev_err(&connection->bundle->dev, + "failed to send AP READY: %d\n", ret); + goto err_connection_disable; + } + + dev_dbg(&bundle->dev, "AP_READY sent\n"); + + return 0; + +err_connection_disable: + gb_connection_disable(connection); +err_connection_destroy: + gb_connection_destroy(connection); +err_free_bootrom: + kfree(bootrom); + + return ret; +} + +static void gb_bootrom_disconnect(struct gb_bundle *bundle) +{ + struct gb_bootrom *bootrom = greybus_get_drvdata(bundle); + + dev_dbg(&bundle->dev, "%s\n", __func__); + + gb_connection_disable(bootrom->connection); + + /* Release firmware */ + if (bootrom->fw) + free_firmware(bootrom); + + gb_connection_destroy(bootrom->connection); + kfree(bootrom); +} + +static const struct greybus_bundle_id gb_bootrom_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BOOTROM) }, + { } +}; + +static struct greybus_driver gb_bootrom_driver = { + .name = "bootrom", + .probe = gb_bootrom_probe, + .disconnect = gb_bootrom_disconnect, + .id_table = gb_bootrom_id_table, +}; + +int gb_bootrom_init(void) +{ + return greybus_register(&gb_bootrom_driver); +} + +void gb_bootrom_exit(void) +{ + greybus_deregister(&gb_bootrom_driver); +} diff --git a/drivers/staging/greybus/bootrom.h b/drivers/staging/greybus/bootrom.h new file mode 100644 index 000000000000..fd2d1938ef21 --- /dev/null +++ b/drivers/staging/greybus/bootrom.h @@ -0,0 +1,16 @@ +/* + * Greybus bootrom code + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __BOOTROM_H +#define __BOOTROM_H + +int gb_bootrom_init(void); +void gb_bootrom_exit(void); + +#endif /* __BOOTROM_H */ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index ca7469ee8994..d8680667ca32 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -10,7 +10,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define CREATE_TRACE_POINTS -#include "firmware.h" +#include "bootrom.h" #include "greybus.h" #include "greybus_trace.h" #include "legacy.h" @@ -243,10 +243,10 @@ static int __init gb_init(void) goto error_operation; } - retval = gb_firmware_init(); + retval = gb_bootrom_init(); if (retval) { - pr_err("gb_firmware_init failed\n"); - goto error_firmware; + pr_err("gb_bootrom_init failed\n"); + goto error_bootrom; } retval = gb_legacy_init(); @@ -258,8 +258,8 @@ static int __init gb_init(void) return 0; /* Success */ error_legacy: - gb_firmware_exit(); -error_firmware: + gb_bootrom_exit(); +error_bootrom: gb_operation_exit(); error_operation: gb_hd_exit(); @@ -275,7 +275,7 @@ module_init(gb_init); static void __exit gb_exit(void) { gb_legacy_exit(); - gb_firmware_exit(); + gb_bootrom_exit(); gb_operation_exit(); gb_hd_exit(); bus_unregister(&greybus_bus_type); diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c deleted file mode 100644 index b1e373c16440..000000000000 --- a/drivers/staging/greybus/firmware.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * FIRMWARE Greybus driver. - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include <linux/firmware.h> - -#include "firmware.h" -#include "greybus.h" - - -struct gb_firmware { - struct gb_connection *connection; - const struct firmware *fw; - u8 protocol_major; - u8 protocol_minor; -}; - -static void free_firmware(struct gb_firmware *firmware) -{ - release_firmware(firmware->fw); - firmware->fw = NULL; -} - -/* - * The es2 chip doesn't have VID/PID programmed into the hardware and we need to - * hack that up to distinguish different modules and their firmware blobs. - * - * This fetches VID/PID (over firmware protocol) for es2 chip only, when VID/PID - * already sent during hotplug are 0. - * - * Otherwise, we keep intf->vendor_id/product_id same as what's passed - * during hotplug. - */ -static void firmware_es2_fixup_vid_pid(struct gb_firmware *firmware) -{ - struct gb_firmware_get_vid_pid_response response; - struct gb_connection *connection = firmware->connection; - struct gb_interface *intf = connection->bundle->intf; - int ret; - - if (!(intf->quirks & GB_INTERFACE_QUIRK_NO_ARA_IDS)) - return; - - ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_GET_VID_PID, - NULL, 0, &response, sizeof(response)); - if (ret) { - dev_err(&connection->bundle->dev, - "Firmware get vid/pid operation failed (%d)\n", ret); - return; - } - - /* - * NOTE: This is hacked, so that the same values of VID/PID can be used - * by next firmware level as well. The uevent for bootrom will still - * have VID/PID as 0, though after this point the sysfs files will start - * showing the updated values. But yeah, that's a bit racy as the same - * sysfs files would be showing 0 before this point. - */ - intf->vendor_id = le32_to_cpu(response.vendor_id); - intf->product_id = le32_to_cpu(response.product_id); - - dev_dbg(&connection->bundle->dev, "Firmware got vid (0x%x)/pid (0x%x)\n", - intf->vendor_id, intf->product_id); -} - -/* This returns path of the firmware blob on the disk */ -static int download_firmware(struct gb_firmware *firmware, u8 stage) -{ - struct gb_connection *connection = firmware->connection; - struct gb_interface *intf = connection->bundle->intf; - char firmware_name[48]; - int rc; - - /* Already have a firmware, free it */ - if (firmware->fw) - free_firmware(firmware); - - /* - * Create firmware name - * - * XXX Name it properly.. - */ - snprintf(firmware_name, sizeof(firmware_name), - "ara_%08x_%08x_%08x_%08x_%02x.tftf", - intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, - intf->vendor_id, intf->product_id, stage); - - // FIXME: - // Turn to dev_dbg later after everyone has valid bootloaders with good - // ids, but leave this as dev_info for now to make it easier to track - // down "empty" vid/pid modules. - dev_info(&connection->bundle->dev, "Firmware file '%s' requested\n", - firmware_name); - - rc = request_firmware(&firmware->fw, firmware_name, - &connection->bundle->dev); - if (rc) - dev_err(&connection->bundle->dev, - "Firware request for %s has failed : %d", - firmware_name, rc); - return rc; -} - -static int gb_firmware_size_request(struct gb_operation *op) -{ - struct gb_firmware *firmware = gb_connection_get_data(op->connection); - struct gb_firmware_size_request *size_request = op->request->payload; - struct gb_firmware_size_response *size_response; - struct device *dev = &op->connection->bundle->dev; - int ret; - - if (op->request->payload_size != sizeof(*size_request)) { - dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", - __func__, op->request->payload_size, - sizeof(*size_request)); - return -EINVAL; - } - - ret = download_firmware(firmware, size_request->stage); - if (ret) { - dev_err(dev, "%s: failed to download firmware (%d)\n", __func__, - ret); - return ret; - } - - if (!gb_operation_response_alloc(op, sizeof(*size_response), - GFP_KERNEL)) { - dev_err(dev, "%s: error allocating response\n", __func__); - free_firmware(firmware); - return -ENOMEM; - } - - size_response = op->response->payload; - size_response->size = cpu_to_le32(firmware->fw->size); - - dev_dbg(dev, "%s: firmware size %d bytes\n", __func__, size_response->size); - - return 0; -} - -static int gb_firmware_get_firmware(struct gb_operation *op) -{ - struct gb_firmware *firmware = gb_connection_get_data(op->connection); - const struct firmware *fw = firmware->fw; - struct gb_firmware_get_firmware_request *firmware_request; - struct gb_firmware_get_firmware_response *firmware_response; - struct device *dev = &op->connection->bundle->dev; - unsigned int offset, size; - - if (op->request->payload_size != sizeof(*firmware_request)) { - dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", - __func__, op->request->payload_size, - sizeof(*firmware_request)); - return -EINVAL; - } - - if (!fw) { - dev_err(dev, "%s: firmware not available\n", __func__); - return -EINVAL; - } - - firmware_request = op->request->payload; - offset = le32_to_cpu(firmware_request->offset); - size = le32_to_cpu(firmware_request->size); - - if (offset >= fw->size || size > fw->size - offset) { - dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", - offset, size); - return -EINVAL; - } - - if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, - GFP_KERNEL)) { - dev_err(dev, "%s: error allocating response\n", __func__); - return -ENOMEM; - } - - firmware_response = op->response->payload; - memcpy(firmware_response->data, fw->data + offset, size); - - dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, - size); - - return 0; -} - -static int gb_firmware_ready_to_boot(struct gb_operation *op) -{ - struct gb_connection *connection = op->connection; - struct gb_firmware_ready_to_boot_request *rtb_request; - struct device *dev = &connection->bundle->dev; - u8 status; - - if (op->request->payload_size != sizeof(*rtb_request)) { - dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", - __func__, op->request->payload_size, - sizeof(*rtb_request)); - return -EINVAL; - } - - rtb_request = op->request->payload; - status = rtb_request->status; - - /* Return error if the blob was invalid */ - if (status == GB_FIRMWARE_BOOT_STATUS_INVALID) - return -EINVAL; - - /* - * XXX Should we return error for insecure firmware? - */ - dev_dbg(dev, "ready to boot: 0x%x, 0\n", status); - - return 0; -} - -static int gb_firmware_request_handler(struct gb_operation *op) -{ - u8 type = op->type; - - switch (type) { - case GB_FIRMWARE_TYPE_FIRMWARE_SIZE: - return gb_firmware_size_request(op); - case GB_FIRMWARE_TYPE_GET_FIRMWARE: - return gb_firmware_get_firmware(op); - case GB_FIRMWARE_TYPE_READY_TO_BOOT: - return gb_firmware_ready_to_boot(op); - default: - dev_err(&op->connection->bundle->dev, - "unsupported request: %u\n", type); - return -EINVAL; - } -} - -static int gb_firmware_get_version(struct gb_firmware *firmware) -{ - struct gb_bundle *bundle = firmware->connection->bundle; - struct gb_firmware_version_request request; - struct gb_firmware_version_response response; - int ret; - - request.major = GB_FIRMWARE_VERSION_MAJOR; - request.minor = GB_FIRMWARE_VERSION_MINOR; - - ret = gb_operation_sync(firmware->connection, - GB_FIRMWARE_TYPE_VERSION, - &request, sizeof(request), &response, - sizeof(response)); - if (ret) { - dev_err(&bundle->dev, - "failed to get protocol version: %d\n", - ret); - return ret; - } - - if (response.major > request.major) { - dev_err(&bundle->dev, - "unsupported major protocol version (%u > %u)\n", - response.major, request.major); - return -ENOTSUPP; - } - - firmware->protocol_major = response.major; - firmware->protocol_minor = response.minor; - - dev_dbg(&bundle->dev, "%s - %u.%u\n", __func__, response.major, - response.minor); - - return 0; -} - -static int gb_firmware_probe(struct gb_bundle *bundle, - const struct greybus_bundle_id *id) -{ - struct greybus_descriptor_cport *cport_desc; - struct gb_connection *connection; - struct gb_firmware *firmware; - int ret; - - if (bundle->num_cports != 1) - return -ENODEV; - - cport_desc = &bundle->cport_desc[0]; - if (cport_desc->protocol_id != GREYBUS_PROTOCOL_FIRMWARE) - return -ENODEV; - - firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); - if (!firmware) - return -ENOMEM; - - connection = gb_connection_create(bundle, - le16_to_cpu(cport_desc->id), - gb_firmware_request_handler); - if (IS_ERR(connection)) { - ret = PTR_ERR(connection); - goto err_free_firmware; - } - - gb_connection_set_data(connection, firmware); - - firmware->connection = connection; - - greybus_set_drvdata(bundle, firmware); - - ret = gb_connection_enable_tx(connection); - if (ret) - goto err_connection_destroy; - - ret = gb_firmware_get_version(firmware); - if (ret) - goto err_connection_disable; - - firmware_es2_fixup_vid_pid(firmware); - - ret = gb_connection_enable(connection); - if (ret) - goto err_connection_disable; - - /* Tell bootrom we're ready. */ - ret = gb_operation_sync(connection, GB_FIRMWARE_TYPE_AP_READY, NULL, 0, - NULL, 0); - if (ret) { - dev_err(&connection->bundle->dev, - "failed to send AP READY: %d\n", ret); - goto err_connection_disable; - } - - dev_dbg(&bundle->dev, "AP_READY sent\n"); - - return 0; - -err_connection_disable: - gb_connection_disable(connection); -err_connection_destroy: - gb_connection_destroy(connection); -err_free_firmware: - kfree(firmware); - - return ret; -} - -static void gb_firmware_disconnect(struct gb_bundle *bundle) -{ - struct gb_firmware *firmware = greybus_get_drvdata(bundle); - - dev_dbg(&bundle->dev, "%s\n", __func__); - - gb_connection_disable(firmware->connection); - - /* Release firmware */ - if (firmware->fw) - free_firmware(firmware); - - gb_connection_destroy(firmware->connection); - kfree(firmware); -} - -static const struct greybus_bundle_id gb_firmware_id_table[] = { - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) }, - { } -}; - -static struct greybus_driver gb_firmware_driver = { - .name = "firmware", - .probe = gb_firmware_probe, - .disconnect = gb_firmware_disconnect, - .id_table = gb_firmware_id_table, -}; - -int gb_firmware_init(void) -{ - return greybus_register(&gb_firmware_driver); -} - -void gb_firmware_exit(void) -{ - greybus_deregister(&gb_firmware_driver); -} diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h deleted file mode 100644 index f25744c1648e..000000000000 --- a/drivers/staging/greybus/firmware.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Greybus firmware code - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __FIRMWARE_H -#define __FIRMWARE_H - -int gb_firmware_init(void); -void gb_firmware_exit(void); - -#endif /* __FIRMWARE_H */ diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 12b6e74ee345..460fced979a4 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -43,7 +43,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_AUDIO_MGMT = 0x12, GREYBUS_PROTOCOL_AUDIO_DATA = 0x13, GREYBUS_PROTOCOL_SVC = 0x14, - GREYBUS_PROTOCOL_FIRMWARE = 0x15, + GREYBUS_PROTOCOL_BOOTROM = 0x15, GREYBUS_PROTOCOL_CAMERA_DATA = 0x16, /* ... */ GREYBUS_PROTOCOL_RAW = 0xfe, @@ -72,7 +72,7 @@ enum greybus_class_type { GREYBUS_CLASS_AUDIO = 0x12, /* 0x13 is unused */ /* 0x14 is unused */ - GREYBUS_CLASS_FIRMWARE = 0x15, + GREYBUS_CLASS_BOOTROM = 0x15, /* ... */ GREYBUS_CLASS_RAW = 0xfe, GREYBUS_CLASS_VENDOR = 0xff, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 06888e029473..959ff1b7d8ce 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -212,70 +212,70 @@ struct gb_control_timesync_authoritative_request { #define GB_APB_REQUEST_CPORT_FEAT_EN 0x0b #define GB_APB_REQUEST_CPORT_FEAT_DIS 0x0c -/* Firmware Protocol */ - -/* Version of the Greybus firmware protocol we support */ -#define GB_FIRMWARE_VERSION_MAJOR 0x00 -#define GB_FIRMWARE_VERSION_MINOR 0x01 - -/* Greybus firmware request types */ -#define GB_FIRMWARE_TYPE_VERSION 0x01 -#define GB_FIRMWARE_TYPE_FIRMWARE_SIZE 0x02 -#define GB_FIRMWARE_TYPE_GET_FIRMWARE 0x03 -#define GB_FIRMWARE_TYPE_READY_TO_BOOT 0x04 -#define GB_FIRMWARE_TYPE_AP_READY 0x05 /* Request with no-payload */ -#define GB_FIRMWARE_TYPE_GET_VID_PID 0x06 /* Request with no-payload */ - -/* Greybus firmware boot stages */ -#define GB_FIRMWARE_BOOT_STAGE_ONE 0x01 /* Reserved for the boot ROM */ -#define GB_FIRMWARE_BOOT_STAGE_TWO 0x02 /* Firmware package to be loaded by the boot ROM */ -#define GB_FIRMWARE_BOOT_STAGE_THREE 0x03 /* Module personality package loaded by Stage 2 firmware */ - -/* Greybus firmware ready to boot status */ -#define GB_FIRMWARE_BOOT_STATUS_INVALID 0x00 /* Firmware blob could not be validated */ -#define GB_FIRMWARE_BOOT_STATUS_INSECURE 0x01 /* Firmware blob is valid but insecure */ -#define GB_FIRMWARE_BOOT_STATUS_SECURE 0x02 /* Firmware blob is valid and secure */ - -/* Max firmware data fetch size in bytes */ -#define GB_FIRMWARE_FETCH_MAX 2000 - -struct gb_firmware_version_request { +/* Bootrom Protocol */ + +/* Version of the Greybus bootrom protocol we support */ +#define GB_BOOTROM_VERSION_MAJOR 0x00 +#define GB_BOOTROM_VERSION_MINOR 0x01 + +/* Greybus bootrom request types */ +#define GB_BOOTROM_TYPE_VERSION 0x01 +#define GB_BOOTROM_TYPE_FIRMWARE_SIZE 0x02 +#define GB_BOOTROM_TYPE_GET_FIRMWARE 0x03 +#define GB_BOOTROM_TYPE_READY_TO_BOOT 0x04 +#define GB_BOOTROM_TYPE_AP_READY 0x05 /* Request with no-payload */ +#define GB_BOOTROM_TYPE_GET_VID_PID 0x06 /* Request with no-payload */ + +/* Greybus bootrom boot stages */ +#define GB_BOOTROM_BOOT_STAGE_ONE 0x01 /* Reserved for the boot ROM */ +#define GB_BOOTROM_BOOT_STAGE_TWO 0x02 /* Bootrom package to be loaded by the boot ROM */ +#define GB_BOOTROM_BOOT_STAGE_THREE 0x03 /* Module personality package loaded by Stage 2 firmware */ + +/* Greybus bootrom ready to boot status */ +#define GB_BOOTROM_BOOT_STATUS_INVALID 0x00 /* Firmware blob could not be validated */ +#define GB_BOOTROM_BOOT_STATUS_INSECURE 0x01 /* Firmware blob is valid but insecure */ +#define GB_BOOTROM_BOOT_STATUS_SECURE 0x02 /* Firmware blob is valid and secure */ + +/* Max bootrom data fetch size in bytes */ +#define GB_BOOTROM_FETCH_MAX 2000 + +struct gb_bootrom_version_request { __u8 major; __u8 minor; } __packed; -struct gb_firmware_version_response { +struct gb_bootrom_version_response { __u8 major; __u8 minor; } __packed; -/* Firmware protocol firmware size request/response */ -struct gb_firmware_size_request { +/* Bootrom protocol firmware size request/response */ +struct gb_bootrom_firmware_size_request { __u8 stage; } __packed; -struct gb_firmware_size_response { +struct gb_bootrom_firmware_size_response { __le32 size; } __packed; -/* Firmware protocol get firmware request/response */ -struct gb_firmware_get_firmware_request { +/* Bootrom protocol get firmware request/response */ +struct gb_bootrom_get_firmware_request { __le32 offset; __le32 size; } __packed; -struct gb_firmware_get_firmware_response { +struct gb_bootrom_get_firmware_response { __u8 data[0]; } __packed; -/* Firmware protocol Ready to boot request */ -struct gb_firmware_ready_to_boot_request { +/* Bootrom protocol Ready to boot request */ +struct gb_bootrom_ready_to_boot_request { __u8 status; } __packed; -/* Firmware protocol Ready to boot response has no payload */ +/* Bootrom protocol Ready to boot response has no payload */ -/* Firmware protocol get VID/PID request has no payload */ -struct gb_firmware_get_vid_pid_response { +/* Bootrom protocol get VID/PID request has no payload */ +struct gb_bootrom_get_vid_pid_response { __le32 vendor_id; __le32 product_id; } __packed; -- cgit v1.2.3-59-g8ed1b From dc5cc72cc6c219868ad5b87b26c79ac778444210 Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Date: Fri, 1 Apr 2016 17:32:43 +0300 Subject: greybus: camera: Add metadata format Add support for greybus metadata format. Greybus metadata format id is 0x41. Signed-off-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Acked-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 2de91d59a54e..6db89efb8f0e 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -91,6 +91,10 @@ static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { { .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, .gb_format = 0x40, + }, + { + .mbus_code = V4L2_MBUS_FMT_ARA_METADATA_1X8, + .gb_format = 0x41, } }; -- cgit v1.2.3-59-g8ed1b From a9234bfd6cec4420b5bef29d77dce1f9cb0543e2 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 30 Mar 2016 13:23:55 +0530 Subject: greybus: audio_manager: Split device type into i/p & o/p devices Currently, single field is used to report device type say SPK, MIC, HS, HP, etc. However above HAL expects separate fields for input & ouput device types. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_manager.h | 3 +- drivers/staging/greybus/audio_manager_module.c | 38 +++++++++++++++++++------- drivers/staging/greybus/audio_manager_sysfs.c | 7 +++-- drivers/staging/greybus/audio_module.c | 3 +- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/audio_manager.h b/drivers/staging/greybus/audio_manager.h index 9ca7ac09719e..c4ca09754a6a 100644 --- a/drivers/staging/greybus/audio_manager.h +++ b/drivers/staging/greybus/audio_manager.h @@ -22,7 +22,8 @@ struct gb_audio_manager_module_descriptor { int vid; int pid; int cport; - unsigned int devices; + unsigned int ip_devices; + unsigned int op_devices; }; struct gb_audio_manager_module { diff --git a/drivers/staging/greybus/audio_manager_module.c b/drivers/staging/greybus/audio_manager_module.c index e5cffa362671..a10e96ad79c1 100644 --- a/drivers/staging/greybus/audio_manager_module.c +++ b/drivers/staging/greybus/audio_manager_module.c @@ -122,16 +122,27 @@ static struct gb_audio_manager_module_attribute gb_audio_module_cport_attribute = __ATTR(cport, 0664, gb_audio_module_cport_show, NULL); -static ssize_t gb_audio_module_devices_show( +static ssize_t gb_audio_module_ip_devices_show( struct gb_audio_manager_module *module, struct gb_audio_manager_module_attribute *attr, char *buf) { - return sprintf(buf, "0x%X", module->desc.devices); + return sprintf(buf, "0x%X", module->desc.ip_devices); } static struct gb_audio_manager_module_attribute - gb_audio_module_devices_attribute = - __ATTR(devices, 0664, gb_audio_module_devices_show, NULL); + gb_audio_module_ip_devices_attribute = + __ATTR(ip_devices, 0664, gb_audio_module_ip_devices_show, NULL); + +static ssize_t gb_audio_module_op_devices_show( + struct gb_audio_manager_module *module, + struct gb_audio_manager_module_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%X", module->desc.op_devices); +} + +static struct gb_audio_manager_module_attribute + gb_audio_module_op_devices_attribute = + __ATTR(op_devices, 0664, gb_audio_module_op_devices_show, NULL); static struct attribute *gb_audio_module_default_attrs[] = { &gb_audio_module_name_attribute.attr, @@ -139,7 +150,8 @@ static struct attribute *gb_audio_module_default_attrs[] = { &gb_audio_module_vid_attribute.attr, &gb_audio_module_pid_attribute.attr, &gb_audio_module_cport_attribute.attr, - &gb_audio_module_devices_attribute.attr, + &gb_audio_module_ip_devices_attribute.attr, + &gb_audio_module_op_devices_attribute.attr, NULL, /* need to NULL terminate the list of attributes */ }; @@ -156,7 +168,8 @@ static void send_add_uevent(struct gb_audio_manager_module *module) char vid_string[64]; char pid_string[64]; char cport_string[64]; - char devices_string[64]; + char ip_devices_string[64]; + char op_devices_string[64]; char *envp[] = { name_string, @@ -164,7 +177,8 @@ static void send_add_uevent(struct gb_audio_manager_module *module) vid_string, pid_string, cport_string, - devices_string, + ip_devices_string, + op_devices_string, NULL }; @@ -173,7 +187,10 @@ static void send_add_uevent(struct gb_audio_manager_module *module) snprintf(vid_string, 64, "VID=%d", module->desc.vid); snprintf(pid_string, 64, "PID=%d", module->desc.pid); snprintf(cport_string, 64, "CPORT=%d", module->desc.cport); - snprintf(devices_string, 64, "DEVICES=0x%X", module->desc.devices); + snprintf(ip_devices_string, 64, "I/P DEVICES=0x%X", + module->desc.ip_devices); + snprintf(op_devices_string, 64, "O/P DEVICES=0x%X", + module->desc.op_devices); kobject_uevent_env(&module->kobj, KOBJ_ADD, envp); } @@ -229,12 +246,13 @@ int gb_audio_manager_module_create( void gb_audio_manager_module_dump(struct gb_audio_manager_module *module) { - pr_info("audio module #%d name=%s slot=%d vid=%d pid=%d cport=%d devices=0x%X\n", + pr_info("audio module #%d name=%s slot=%d vid=%d pid=%d cport=%d i/p devices=0x%X o/p devices=0x%X\n", module->id, module->desc.name, module->desc.slot, module->desc.vid, module->desc.pid, module->desc.cport, - module->desc.devices); + module->desc.ip_devices, + module->desc.op_devices); } diff --git a/drivers/staging/greybus/audio_manager_sysfs.c b/drivers/staging/greybus/audio_manager_sysfs.c index c713f5f7aaca..d8bf8591ff9e 100644 --- a/drivers/staging/greybus/audio_manager_sysfs.c +++ b/drivers/staging/greybus/audio_manager_sysfs.c @@ -20,11 +20,12 @@ static ssize_t manager_sysfs_add_store( int num = sscanf(buf, "name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s " - "slot=%d vid=%d pid=%d cport=%d devices=0x%X", + "slot=%d vid=%d pid=%d cport=%d i/p devices=0x%X" + "o/p devices=0x%X", desc.name, &desc.slot, &desc.vid, &desc.pid, - &desc.cport, &desc.devices); + &desc.cport, &desc.ip_devices, &desc.op_devices); - if (num != 6) + if (num != 7) return -EINVAL; num = gb_audio_manager_add(&desc); diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 9039aa63e040..a7f961f85e21 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -353,7 +353,8 @@ static int gb_audio_probe(struct gb_bundle *bundle, desc.vid = 2; /* todo */ desc.pid = 3; /* todo */ desc.cport = gbmodule->dev_id; - desc.devices = 0x2; /* todo */ + desc.op_devices = 0x2; /* todo */ + desc.ip_devices = 0x0; /* todo */ gbmodule->manager_id = gb_audio_manager_add(&desc); dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name); -- cgit v1.2.3-59-g8ed1b From 89de9a06213240b9266f9f368a867cf90d0024bf Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 30 Mar 2016 13:23:56 +0530 Subject: greybus: audio: Update device type based on widget types Device type info shared to above HAL is currently hard coded to SPK only. Actual device type is identifed while parsing widget types from topology FW shared by codec module. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.h | 17 +++++++++++++++++ drivers/staging/greybus/audio_module.c | 4 ++-- drivers/staging/greybus/audio_topology.c | 5 +++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 165b3595dae9..6182b20c5a27 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -36,6 +36,21 @@ enum gbcodec_reg_index { GBCODEC_REG_COUNT }; +/* device_type should be same as defined in audio.h (Android media layer) */ +enum { + GBAUDIO_DEVICE_NONE = 0x0, + /* reserved bits */ + GBAUDIO_DEVICE_BIT_IN = 0x80000000, + GBAUDIO_DEVICE_BIT_DEFAULT = 0x40000000, + /* output devices */ + GBAUDIO_DEVICE_OUT_SPEAKER = 0x2, + GBAUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, + GBAUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, + /* input devices */ + GBAUDIO_DEVICE_IN_BUILTIN_MIC = GBAUDIO_DEVICE_BIT_IN | 0x4, + GBAUDIO_DEVICE_IN_WIRED_HEADSET = GBAUDIO_DEVICE_BIT_IN | 0x10, +}; + /* bit 0-SPK, 1-HP, 2-DAC, * 4-MIC, 5-HSMIC, 6-MIC2 */ @@ -144,6 +159,8 @@ struct gbaudio_module_info { /* need to share this info to above user space */ int manager_id; char name[NAME_SIZE]; + unsigned int ip_devices; + unsigned int op_devices; /* jack related */ char jack_name[NAME_SIZE]; diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index a7f961f85e21..dd43b6d5ae07 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -353,8 +353,8 @@ static int gb_audio_probe(struct gb_bundle *bundle, desc.vid = 2; /* todo */ desc.pid = 3; /* todo */ desc.cport = gbmodule->dev_id; - desc.op_devices = 0x2; /* todo */ - desc.ip_devices = 0x0; /* todo */ + desc.op_devices = gbmodule->op_devices; + desc.ip_devices = gbmodule->ip_devices; gbmodule->manager_id = gb_audio_manager_add(&desc); dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name); diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 79161c1b74ce..e423e6d16a25 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -665,15 +665,20 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, case snd_soc_dapm_spk: *dw = (struct snd_soc_dapm_widget) SND_SOC_DAPM_SPK(w->name, gbcodec_event_spk); + module->op_devices |= GBAUDIO_DEVICE_OUT_SPEAKER; break; case snd_soc_dapm_hp: *dw = (struct snd_soc_dapm_widget) SND_SOC_DAPM_HP(w->name, gbcodec_event_hp); module->num_jacks++; + module->op_devices |= (GBAUDIO_DEVICE_OUT_WIRED_HEADSET + | GBAUDIO_DEVICE_OUT_WIRED_HEADPHONE); + module->ip_devices |= GBAUDIO_DEVICE_IN_WIRED_HEADSET; break; case snd_soc_dapm_mic: *dw = (struct snd_soc_dapm_widget) SND_SOC_DAPM_MIC(w->name, gbcodec_event_int_mic); + module->ip_devices |= GBAUDIO_DEVICE_IN_BUILTIN_MIC; break; case snd_soc_dapm_output: *dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_OUTPUT(w->name); -- cgit v1.2.3-59-g8ed1b From ddb10c8acc8554ffb68bdb6d80d0e277e124cb2b Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Apr 2016 20:15:30 -0700 Subject: greybus: svc: add Interface power measurements support This change implements the AP Power Monitor functions for obtaining current/voltage/power on a specific rail of an Interface. Testing Done: $ cat /sys/bus/greybus/devices/1-3/current_now 103 $ cat /sys/bus/greybus/devices/1-3/power_now 303 $ cat /sys/bus/greybus/devices/1-3/voltage_now 203 Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Bartosz Golaszewski <bgolaszewski@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 21 ++++++++ drivers/staging/greybus/greybus_protocols.h | 23 +++++++++ drivers/staging/greybus/interface.c | 60 ++++++++++++++++++++++ drivers/staging/greybus/svc.c | 38 ++++++++++++++ drivers/staging/greybus/svc.h | 2 + 5 files changed, 144 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 17b1a2919ef9..281b05cb889c 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -164,3 +164,24 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: If the SVC watchdog is enabled or not. Writing 0 to this file will disable the watchdog, writing 1 will enable it. + +What: /sys/bus/greybus/device/N-I/voltage_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Voltage measurement of the interface in microvolts (uV) + +What: /sys/bus/greybus/device/N-I/current_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Current measurement of the interface in microamps (uA) + +What: /sys/bus/greybus/device/N-I/power_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Power measurement of the interface in microwatts (uW) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 959ff1b7d8ce..e3ef3c96456d 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -798,6 +798,10 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_EJECT 0x11 #define GB_SVC_TYPE_KEY_EVENT 0x12 #define GB_SVC_TYPE_PING 0x13 +#define GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET 0x14 +#define GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET 0x15 +#define GB_SVC_TYPE_PWRMON_SAMPLE_GET 0x16 +#define GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET 0x17 /* * SVC version request/response has the same payload as @@ -963,6 +967,25 @@ struct gb_svc_key_event_request { #define GB_SVC_KEY_PRESSED 0x01 } __packed; +#define GB_SVC_PWRMON_TYPE_CURR 0x01 +#define GB_SVC_PWRMON_TYPE_VOL 0x02 +#define GB_SVC_PWRMON_TYPE_PWR 0x03 + +#define GB_SVC_PWRMON_GET_SAMPLE_OK 0x00 +#define GB_SVC_PWRMON_GET_SAMPLE_INVAL 0x01 +#define GB_SVC_PWRMON_GET_SAMPLE_NOSUPP 0x02 +#define GB_SVC_PWRMON_GET_SAMPLE_HWERR 0x03 + +struct gb_svc_pwrmon_intf_sample_get_request { + __u8 intf_id; + __u8 measurement_type; +} __packed; + +struct gb_svc_pwrmon_intf_sample_get_response { + __u8 result; + __le32 measurement; +} __packed; + /* RAW */ /* Version of the Greybus raw protocol we support */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 27dbd79d2e19..a4bee41feec4 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -263,6 +263,63 @@ static ssize_t version_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(version); +static ssize_t voltage_now_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + int ret; + u32 measurement; + + ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id, + GB_SVC_PWRMON_TYPE_VOL, + &measurement); + if (ret) { + dev_err(&intf->dev, "failed to get voltage sample (%d)\n", ret); + return ret; + } + + return sprintf(buf, "%u\n", measurement); +} +static DEVICE_ATTR_RO(voltage_now); + +static ssize_t current_now_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + int ret; + u32 measurement; + + ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id, + GB_SVC_PWRMON_TYPE_CURR, + &measurement); + if (ret) { + dev_err(&intf->dev, "failed to get current sample (%d)\n", ret); + return ret; + } + + return sprintf(buf, "%u\n", measurement); +} +static DEVICE_ATTR_RO(current_now); + +static ssize_t power_now_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + int ret; + u32 measurement; + + ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id, + GB_SVC_PWRMON_TYPE_PWR, + &measurement); + if (ret) { + dev_err(&intf->dev, "failed to get power sample (%d)\n", ret); + return ret; + } + + return sprintf(buf, "%u\n", measurement); +} +static DEVICE_ATTR_RO(power_now); + static struct attribute *interface_attrs[] = { &dev_attr_ddbl1_manufacturer_id.attr, &dev_attr_ddbl1_product_id.attr, @@ -273,6 +330,9 @@ static struct attribute *interface_attrs[] = { &dev_attr_product_string.attr, &dev_attr_serial_number.attr, &dev_attr_version.attr, + &dev_attr_voltage_now.attr, + &dev_attr_current_now.attr, + &dev_attr_power_now.attr, NULL, }; ATTRIBUTE_GROUPS(interface); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index a19e575de029..b79d81717360 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -99,6 +99,44 @@ static ssize_t watchdog_store(struct device *dev, } static DEVICE_ATTR_RW(watchdog); +int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, + u8 measurement_type, u32 *value) +{ + struct gb_svc_pwrmon_intf_sample_get_request request; + struct gb_svc_pwrmon_intf_sample_get_response response; + int ret; + + request.intf_id = intf_id; + request.measurement_type = measurement_type; + + ret = gb_operation_sync(svc->connection, + GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) { + dev_err(&svc->dev, "failed to get intf sample (%d)\n", ret); + return ret; + } + + if (response.result) { + dev_err(&svc->dev, + "UniPro error while getting intf power sample (%d %d): %d\n", + intf_id, measurement_type, response.result); + switch (response.result) { + case GB_SVC_PWRMON_GET_SAMPLE_INVAL: + return -EINVAL; + case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: + return -ENOSYS; + default: + return -EIO; + } + } + + *value = le32_to_cpu(response.measurement); + + return 0; +} + static struct attribute *svc_attrs[] = { &dev_attr_endo_id.attr, &dev_attr_ap_intf_id.attr, diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 8950baff9aef..08f8e3705b43 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -48,6 +48,8 @@ int gb_svc_add(struct gb_svc *svc); void gb_svc_del(struct gb_svc *svc); void gb_svc_put(struct gb_svc *svc); +int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, + u8 measurement_type, u32 *value); int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, u8 intf2_id, u8 dev2_id); -- cgit v1.2.3-59-g8ed1b From b4905038965fb986d8496e47cc52a08428408ae5 Mon Sep 17 00:00:00 2001 From: Evgeniy Borisov <borisov_evgeniy@projectara.com> Date: Wed, 6 Apr 2016 15:22:45 +0300 Subject: greybus: camera-gb: Extend the configure streams interface Extending the configure streams interface with CSI params. Getting CSI frequency data form configure streams response. * num_lanes - Number of CSI data lanes * clk_freq - CSI clock frequency in Hz * lines_per_second - Total number of lines in a second of transmission (blanking included) From the AP side we need to know for the CSI speed configuration. This information is needed for dynamically bandwidth calculations. NOTE: Change should be along merged with corresponding interface change in kernel: "camera: Extend the configure streams interface with CSI params" Signed-off-by: Evgeniy Borisov <eborisov@mm-sol.com> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 17 +++++++++++++---- drivers/staging/greybus/gb-camera.h | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 6db89efb8f0e..d3ee5b7240db 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -203,7 +203,8 @@ struct ap_csi_config_request { static int gb_camera_configure_streams(struct gb_camera *gcam, unsigned int *num_streams, unsigned int *flags, - struct gb_camera_stream_config *streams) + struct gb_camera_stream_config *streams, + struct gb_camera_csi_params *csi_params) { struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; @@ -309,6 +310,12 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, ret = gb_hd_output(gcam->connection->hd, &csi_cfg, sizeof(csi_cfg), GB_APB_REQUEST_CSI_TX_CONTROL, false); + if (csi_params) { + csi_params->num_lanes = csi_cfg.num_lanes; + /* Transmitting two bits per cycle. (DDR clock) */ + csi_params->clk_freq = csi_cfg.bus_freq / 2; + csi_params->lines_per_second = csi_cfg.lines_per_second; + } } else { csi_cfg.csi_id = 1; ret = gb_hd_output(gcam->connection->hd, &csi_cfg, @@ -442,7 +449,8 @@ static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len) } static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, - unsigned int *flags, struct gb_camera_stream *streams) + unsigned int *flags, struct gb_camera_stream *streams, + struct gb_camera_csi_params *csi_params) { struct gb_camera *gcam = priv; struct gb_camera_stream_config *gb_streams; @@ -469,7 +477,7 @@ static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY; ret = gb_camera_configure_streams(gcam, &gb_nstreams, - &gb_flags, gb_streams); + &gb_flags, gb_streams, csi_params); if (ret < 0) goto done; if (gb_nstreams > *nstreams) { @@ -643,7 +651,8 @@ static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, goto done; } - ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams); + ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams, + NULL); if (ret < 0) goto done; diff --git a/drivers/staging/greybus/gb-camera.h b/drivers/staging/greybus/gb-camera.h index 273b4fa6dd4f..cc9c012249e2 100644 --- a/drivers/staging/greybus/gb-camera.h +++ b/drivers/staging/greybus/gb-camera.h @@ -24,10 +24,24 @@ struct gb_camera_stream { unsigned int max_size; }; +/** + * struct gb_camera_csi_params - CSI configuration parameters + * @num_lanes: number of CSI data lanes + * @clk_freq: CSI clock frequency in Hz + * @lines_per_second: total number of lines in a second of transmission + * (blanking included) + */ +struct gb_camera_csi_params { + unsigned int num_lanes; + unsigned int clk_freq; + unsigned int lines_per_second; +}; + struct gb_camera_ops { ssize_t (*capabilities)(void *priv, char *buf, size_t len); int (*configure_streams)(void *priv, unsigned int *nstreams, - unsigned int *flags, struct gb_camera_stream *streams); + unsigned int *flags, struct gb_camera_stream *streams, + struct gb_camera_csi_params *csi_params); int (*capture)(void *priv, u32 request_id, unsigned int streams, unsigned int num_frames, size_t settings_size, const void *settings); -- cgit v1.2.3-59-g8ed1b From a7ddda1f5aa47779071dd4c6f3a5e65c0086e49f Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Tue, 12 Apr 2016 17:18:47 +0530 Subject: greybus: arche-platfrom: Get rid of 2sec delay in USB3613 configuration Earlier during boot sequence implementation, we had seen race between USb3613 and APB boot, and since APB boot time is ~2sec, we delayed USb3613 configuration for 2sec after APB deassertion of reset. This obviously won't work in the case of suspend/resume, where we would like to put APB into OFF state and coldboot in resume. With the latest FW changes, we do not see any race issue. I have done regression testing (> 50 iteration of reboot + unipro link up and down) without any issues. So lets get rid of the 2sec delay with this patch. Testing Done: Tested on EVT 1.5 platform. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Tested-by: David Lin <dtwlin@google.com> Reviewed-by: David Lin <dtwlin@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 3293661d876a..48cdb0fffcfc 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -52,7 +52,6 @@ struct arche_platform_drvdata { int num_apbs; - struct delayed_work delayed_work; enum svc_wakedetect_state wake_detect_state; int wake_detect_irq; spinlock_t lock; @@ -101,24 +100,6 @@ static int apb_poweroff(struct device *dev, void *data) return 0; } -/** - * hub_conf_delayed_work - Configures USB3613 device to HUB mode - * - * The idea here is to split the APB coldboot operation with slow HUB configuration, - * so that driver response to wake/detect event can be met. - * So expectation is, once code reaches here, means initial unipro linkup - * between APB<->Switch was successful, so now just take it to AP. - */ -static void hub_conf_delayed_work(struct work_struct *work) -{ - struct arche_platform_drvdata *arche_pdata = - container_of(work, struct arche_platform_drvdata, delayed_work.work); - - /* Enable HUB3613 into HUB mode. */ - if (usb3613_hub_mode_ctrl(true)) - dev_warn(arche_pdata->dev, "failed to control hub device\n"); -} - static void assert_wakedetect(struct arche_platform_drvdata *arche_pdata) { /* Assert wake/detect = Detect event from AP */ @@ -150,9 +131,11 @@ static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) /* Bring APB out of reset: cold boot sequence */ device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); + /* Enable HUB3613 into HUB mode. */ + if (usb3613_hub_mode_ctrl(true)) + dev_warn(arche_pdata->dev, "failed to control hub device\n"); + spin_lock_irqsave(&arche_pdata->lock, flags); - /* USB HUB configuration */ - schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); arche_pdata->wake_detect_state = WD_STATE_IDLE; spin_unlock_irqrestore(&arche_pdata->lock, flags); @@ -178,8 +161,6 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) if (time_before(jiffies, arche_pdata->wake_detect_start + msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { - /* No harm with cancellation, even if not pending */ - cancel_delayed_work(&arche_pdata->delayed_work); arche_pdata->wake_detect_state = WD_STATE_IDLE; } else { /* Check we are not in middle of irq thread already */ @@ -196,8 +177,6 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) /* wake/detect falling */ if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { arche_pdata->wake_detect_start = jiffies; - /* No harm with cancellation even if it is not pending*/ - cancel_delayed_work(&arche_pdata->delayed_work); /* * In the begining, when wake/detect goes low (first time), we assume * it is meant for coldboot and set the flag. If wake/detect line stays low @@ -494,7 +473,6 @@ static int arche_platform_probe(struct platform_device *pdev) } assert_wakedetect(arche_pdata); - INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work); dev_info(dev, "Device registered successfully\n"); return 0; @@ -520,7 +498,6 @@ static int arche_platform_remove(struct platform_device *pdev) struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); device_remove_file(&pdev->dev, &dev_attr_state); - cancel_delayed_work_sync(&arche_pdata->delayed_work); device_for_each_child(&pdev->dev, NULL, arche_remove_child); arche_platform_poweroff_seq(arche_pdata); platform_set_drvdata(pdev, NULL); -- cgit v1.2.3-59-g8ed1b From 777471a445242e34c790b5b80d031a013933fd05 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 12 Apr 2016 17:15:11 +0100 Subject: greybus: Fixup __u64, __u32 to __le64, __le32 in timesync declarations A number of data in TimeSync command structures are declared __u64/__u32 instead of __le64/__le32, I forgot to put this through an x86_64 compile before presentation for merge and as a result didn't catch this error. This patch fixes. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 2 +- drivers/staging/greybus/greybus_protocols.h | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 8475f1577325..91a7fcac4988 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -269,7 +269,7 @@ int gb_control_timesync_authoritative(struct gb_control *control, int i; for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) - request.frame_time[i] = frame_time[i]; + request.frame_time[i] = cpu_to_le64(frame_time[i]); return gb_operation_sync(control->connection, GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e3ef3c96456d..e0c0493605ca 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -180,7 +180,7 @@ struct gb_control_timesync_enable_request { /* timesync enable response has no payload */ struct gb_control_timesync_authoritative_request { - __u64 frame_time[GB_TIMESYNC_MAX_STROBES]; + __le64 frame_time[GB_TIMESYNC_MAX_STROBES]; } __packed; /* timesync authoritative response has no payload */ @@ -911,16 +911,16 @@ struct gb_svc_route_destroy_request { struct gb_svc_timesync_enable_request { __u8 count; - __u64 frame_time; - __u32 strobe_delay; - __u32 strobe_mask; - __u32 refclk; + __le64 frame_time; + __le32 strobe_delay; + __le32 strobe_mask; + __le32 refclk; } __packed; /* timesync enable response has no payload */ /* timesync authoritative request has no payload */ struct gb_svc_timesync_authoritative_response { - __u64 frame_time[GB_TIMESYNC_MAX_STROBES]; + __le64 frame_time[GB_TIMESYNC_MAX_STROBES]; }; #define GB_SVC_UNIPRO_FAST_MODE 0x01 -- cgit v1.2.3-59-g8ed1b From 9160b7c7652a80f48250bbdc151fa0af3ccf024f Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Sat, 16 Apr 2016 01:15:15 +0530 Subject: greybus: arche-platform: Power-off unipro subsystem upon suspend Switch off APBs/SVC/Switch in the suspend call notifier. Note that this is an interim solution to enable fishfooding. Testing done: - Passed QA sanity test on EVT1.5 - Suspend current measured at ~70mW Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: Updated commit description] Tested-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 48cdb0fffcfc..3ff4f69685bd 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -19,6 +19,7 @@ #include <linux/pm.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/suspend.h> #include <linux/time.h> #include "arche_platform.h" @@ -56,6 +57,7 @@ struct arche_platform_drvdata { int wake_detect_irq; spinlock_t lock; unsigned long wake_detect_start; + struct notifier_block pm_notifier; struct device *dev; }; @@ -339,6 +341,31 @@ static ssize_t state_show(struct device *dev, static DEVICE_ATTR_RW(state); +static int arche_platform_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + struct arche_platform_drvdata *arche_pdata = + container_of(notifier, struct arche_platform_drvdata, + pm_notifier); + + switch (pm_event) { + case PM_SUSPEND_PREPARE: + if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) + return NOTIFY_STOP; + device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); + arche_platform_poweroff_seq(arche_pdata); + break; + case PM_POST_SUSPEND: + arche_platform_coldboot_seq(arche_pdata); + assert_wakedetect(arche_pdata); + break; + default: + break; + } + + return NOTIFY_DONE; +} + static int arche_platform_probe(struct platform_device *pdev) { struct arche_platform_drvdata *arche_pdata; @@ -474,6 +501,13 @@ static int arche_platform_probe(struct platform_device *pdev) assert_wakedetect(arche_pdata); + arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; + ret = register_pm_notifier(&arche_pdata->pm_notifier); + if (ret) { + dev_err(dev, "failed to register pm notifier %d\n", ret); + goto err_populate; + } + dev_info(dev, "Device registered successfully\n"); return 0; @@ -497,6 +531,7 @@ static int arche_platform_remove(struct platform_device *pdev) { struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); + unregister_pm_notifier(&arche_pdata->pm_notifier); device_remove_file(&pdev->dev, &dev_attr_state); device_for_each_child(&pdev->dev, NULL, arche_remove_child); arche_platform_poweroff_seq(arche_pdata); -- cgit v1.2.3-59-g8ed1b From 192c70dcf6c6f8c39a108f9ba56e916808f23cca Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Sat, 16 Apr 2016 01:15:16 +0530 Subject: greybus: svc watchdog: Disable watchdog upon entering suspend SVC watchdog should be disabled when device is entering suspend mode. Testing done: - Sanity tested on EVT1.5 - Check no SVC ping during the suspend process - Check SVC watchdog is back on pinging once device is resumed Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: Removed unwanted check in notifier callback and Updated commit description] Tested-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc_watchdog.c | 46 +++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc_watchdog.c b/drivers/staging/greybus/svc_watchdog.c index 9bd68f4f4cf7..6cd3bac59852 100644 --- a/drivers/staging/greybus/svc_watchdog.c +++ b/drivers/staging/greybus/svc_watchdog.c @@ -7,6 +7,7 @@ */ #include <linux/delay.h> +#include <linux/suspend.h> #include <linux/workqueue.h> #include "greybus.h" @@ -16,10 +17,31 @@ struct gb_svc_watchdog { struct delayed_work work; struct gb_svc *svc; bool enabled; + struct notifier_block pm_notifier; }; static struct delayed_work reset_work; +static int svc_watchdog_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + struct gb_svc_watchdog *watchdog = + container_of(notifier, struct gb_svc_watchdog, pm_notifier); + + switch (pm_event) { + case PM_SUSPEND_PREPARE: + gb_svc_watchdog_disable(watchdog->svc); + break; + case PM_POST_SUSPEND: + gb_svc_watchdog_enable(watchdog->svc); + break; + default: + break; + } + + return NOTIFY_DONE; +} + static void greybus_reset(struct work_struct *work) { static char start_path[256] = "/system/bin/start"; @@ -82,6 +104,7 @@ static void do_work(struct work_struct *work) int gb_svc_watchdog_create(struct gb_svc *svc) { struct gb_svc_watchdog *watchdog; + int retval; if (svc->watchdog) return 0; @@ -95,7 +118,27 @@ int gb_svc_watchdog_create(struct gb_svc *svc) INIT_DELAYED_WORK(&watchdog->work, do_work); svc->watchdog = watchdog; - return gb_svc_watchdog_enable(svc); + watchdog->pm_notifier.notifier_call = svc_watchdog_pm_notifier; + retval = register_pm_notifier(&watchdog->pm_notifier); + if (retval) { + dev_err(&svc->dev, "error registering pm notifier(%d)\n", + retval); + goto svc_watchdog_create_err; + } + + retval = gb_svc_watchdog_enable(svc); + if (retval) { + dev_err(&svc->dev, "error enabling watchdog (%d)\n", retval); + unregister_pm_notifier(&watchdog->pm_notifier); + goto svc_watchdog_create_err; + } + return retval; + +svc_watchdog_create_err: + svc->watchdog = NULL; + kfree(watchdog); + + return retval; } void gb_svc_watchdog_destroy(struct gb_svc *svc) @@ -105,6 +148,7 @@ void gb_svc_watchdog_destroy(struct gb_svc *svc) if (!watchdog) return; + unregister_pm_notifier(&watchdog->pm_notifier); gb_svc_watchdog_disable(svc); svc->watchdog = NULL; kfree(watchdog); -- cgit v1.2.3-59-g8ed1b From 0928b2e4401cebd7fd2b8aebc41be60dbe4c2ef3 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Wed, 20 Apr 2016 14:40:22 +0100 Subject: greybus: spi: fix message transfer over greybus The actual implementation of transfer_one_message have problems with some cases in the possible transfer options. We try to maximize the number of spi transfers in one greybus operation and need to save state until the full message is dispatch over greybus. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Tested-by: Philip Yang <philipy@bsquare.com> Tested-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/spi.c | 193 +++++++++++++++++++++++++++++++++--------- 1 file changed, 153 insertions(+), 40 deletions(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 12e8cd8b1655..0bae7f12ccf1 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -18,6 +18,11 @@ struct gb_spi { struct gb_connection *connection; + struct spi_transfer *first_xfer; + struct spi_transfer *last_xfer; + u32 rx_xfer_offset; + u32 tx_xfer_offset; + u32 last_xfer_size; u16 mode; u16 flags; u32 bits_per_word_mask; @@ -26,6 +31,13 @@ struct gb_spi { u32 max_speed_hz; }; +#define GB_SPI_STATE_MSG_DONE ((void *)0) +#define GB_SPI_STATE_MSG_IDLE ((void *)1) +#define GB_SPI_STATE_MSG_RUNNING ((void *)2) +#define GB_SPI_STATE_OP_READY ((void *)3) +#define GB_SPI_STATE_OP_DONE ((void *)4) +#define GB_SPI_STATE_MSG_ERROR ((void *)-1) + static struct spi_master *get_master_from_spi(struct gb_spi *spi) { return gb_connection_get_data(spi->connection); @@ -76,58 +88,120 @@ static size_t calc_tx_xfer_size(u32 tx_size, u32 count, size_t len, return len; } +static void clean_xfer_state(struct gb_spi *spi) +{ + spi->first_xfer = NULL; + spi->last_xfer = NULL; + spi->rx_xfer_offset = 0; + spi->tx_xfer_offset = 0; + spi->last_xfer_size = 0; +} + +static int setup_next_xfer(struct gb_spi *spi, struct spi_message *msg) +{ + struct spi_transfer *last_xfer = spi->last_xfer; + + if (msg->state != GB_SPI_STATE_OP_DONE) + return 0; + + /* + * if we transferred all content of the last transfer, reset values and + * check if this was the last transfer in the message + */ + if ((spi->tx_xfer_offset + spi->last_xfer_size == last_xfer->len) || + (spi->rx_xfer_offset + spi->last_xfer_size == last_xfer->len)) { + spi->tx_xfer_offset = 0; + spi->rx_xfer_offset = 0; + if (last_xfer == list_last_entry(&msg->transfers, + struct spi_transfer, + transfer_list)) + msg->state = GB_SPI_STATE_MSG_DONE; + else + spi->first_xfer = list_next_entry(last_xfer, + transfer_list); + return 0; + } + + spi->first_xfer = last_xfer; + if (last_xfer->tx_buf) + spi->tx_xfer_offset += spi->last_xfer_size; + + if (last_xfer->rx_buf) + spi->rx_xfer_offset += spi->last_xfer_size; + + return 0; +} + +static struct spi_transfer *get_next_xfer(struct spi_transfer *xfer, + struct spi_message *msg) +{ + if (xfer == list_last_entry(&msg->transfers, struct spi_transfer, + transfer_list)) + return NULL; + + return list_next_entry(xfer, transfer_list); +} + /* Routines to transfer data */ static struct gb_operation * -gb_spi_operation_create(struct gb_connection *connection, - struct spi_message *msg, u32 *total_len) +gb_spi_operation_create(struct gb_spi *spi, struct gb_connection *connection, + struct spi_message *msg) { struct gb_spi_transfer_request *request; struct spi_device *dev = msg->spi; struct spi_transfer *xfer; struct gb_spi_transfer *gb_xfer; struct gb_operation *operation; - struct spi_transfer *last_xfer = NULL; u32 tx_size = 0, rx_size = 0, count = 0, xfer_len = 0, request_size; - u32 tx_xfer_size = 0, rx_xfer_size = 0, last_xfer_size = 0; + u32 tx_xfer_size = 0, rx_xfer_size = 0, len; + u32 total_len = 0; size_t data_max; void *tx_data; data_max = gb_operation_get_payload_size_max(connection); + xfer = spi->first_xfer; /* Find number of transfers queued and tx/rx length in the message */ - list_for_each_entry(xfer, &msg->transfers, transfer_list) { + + while (msg->state != GB_SPI_STATE_OP_READY) { + msg->state = GB_SPI_STATE_MSG_RUNNING; + spi->last_xfer = xfer; + if (!xfer->tx_buf && !xfer->rx_buf) { dev_err(&connection->bundle->dev, "bufferless transfer, length %u\n", xfer->len); + msg->state = GB_SPI_STATE_MSG_ERROR; return NULL; } - last_xfer = xfer; tx_xfer_size = 0; rx_xfer_size = 0; if (xfer->tx_buf) { + len = xfer->len - spi->tx_xfer_offset; if (!tx_header_fit_operation(tx_size, count, data_max)) break; tx_xfer_size = calc_tx_xfer_size(tx_size, count, - xfer->len, data_max); - last_xfer_size = tx_xfer_size; + len, data_max); + spi->last_xfer_size = tx_xfer_size; } if (xfer->rx_buf) { + len = xfer->len - spi->rx_xfer_offset; rx_xfer_size = calc_rx_xfer_size(rx_size, &tx_xfer_size, - xfer->len, data_max); - last_xfer_size = rx_xfer_size; + len, data_max); + spi->last_xfer_size = rx_xfer_size; } tx_size += tx_xfer_size; rx_size += rx_xfer_size; - *total_len += last_xfer_size; + total_len += spi->last_xfer_size; count++; - if (xfer->len != last_xfer_size) - break; + xfer = get_next_xfer(xfer, msg); + if (!xfer || total_len >= data_max) + msg->state = GB_SPI_STATE_OP_READY; } /* @@ -153,9 +227,10 @@ gb_spi_operation_create(struct gb_connection *connection, tx_data = gb_xfer + count; /* place tx data after last gb_xfer */ /* Fill in the transfers array */ - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - if (last_xfer && xfer == last_xfer) - xfer_len = last_xfer_size; + xfer = spi->first_xfer; + while (msg->state != GB_SPI_STATE_OP_DONE) { + if (xfer == spi->last_xfer) + xfer_len = spi->last_xfer_size; else xfer_len = xfer->len; @@ -168,34 +243,54 @@ gb_spi_operation_create(struct gb_connection *connection, /* Copy tx data */ if (xfer->tx_buf) { gb_xfer->rdwr |= GB_SPI_XFER_WRITE; - memcpy(tx_data, xfer->tx_buf, xfer_len); + memcpy(tx_data, xfer->tx_buf + spi->tx_xfer_offset, + xfer_len); tx_data += xfer_len; } if (xfer->rx_buf) gb_xfer->rdwr |= GB_SPI_XFER_READ; - if (last_xfer && xfer == last_xfer) - break; + if (xfer == spi->last_xfer) { + msg->state = GB_SPI_STATE_OP_DONE; + continue; + } gb_xfer++; + xfer = get_next_xfer(xfer, msg); } + msg->actual_length += total_len; + return operation; } -static void gb_spi_decode_response(struct spi_message *msg, +static void gb_spi_decode_response(struct gb_spi *spi, struct spi_message *msg, struct gb_spi_transfer_response *response) { - struct spi_transfer *xfer; + struct spi_transfer *xfer = spi->first_xfer; void *rx_data = response->data; + u32 xfer_len; - list_for_each_entry(xfer, &msg->transfers, transfer_list) { + while (xfer) { /* Copy rx data */ if (xfer->rx_buf) { - memcpy(xfer->rx_buf, rx_data, xfer->len); - rx_data += xfer->len; + if (xfer == spi->first_xfer) + xfer_len = xfer->len - spi->rx_xfer_offset; + else if (xfer == spi->last_xfer) + xfer_len = spi->last_xfer_size; + else + xfer_len = xfer->len; + + memcpy(xfer->rx_buf + spi->rx_xfer_offset, rx_data, + xfer_len); + rx_data += xfer_len; } + + if (xfer == spi->last_xfer) + break; + + xfer = list_next_entry(xfer, transfer_list); } } @@ -206,27 +301,45 @@ static int gb_spi_transfer_one_message(struct spi_master *master, struct gb_connection *connection = spi->connection; struct gb_spi_transfer_response *response; struct gb_operation *operation; - u32 len = 0; - int ret; + int ret = 0; + + spi->first_xfer = list_first_entry_or_null(&msg->transfers, + struct spi_transfer, + transfer_list); + if (!spi->first_xfer) { + ret = -ENOMEM; + goto out; + } - operation = gb_spi_operation_create(connection, msg, &len); - if (!operation) - return -ENOMEM; + msg->state = GB_SPI_STATE_MSG_IDLE; + + while (msg->state != GB_SPI_STATE_MSG_DONE && + msg->state != GB_SPI_STATE_MSG_ERROR) { + operation = gb_spi_operation_create(spi, connection, msg); + if (!operation) { + msg->state = GB_SPI_STATE_MSG_ERROR; + ret = -EINVAL; + continue; + } - ret = gb_operation_request_send_sync(operation); - if (!ret) { - response = operation->response->payload; - if (response) - gb_spi_decode_response(msg, response); - } else { - dev_err(&connection->bundle->dev, + ret = gb_operation_request_send_sync(operation); + if (!ret) { + response = operation->response->payload; + if (response) + gb_spi_decode_response(spi, msg, response); + } else { + dev_err(&connection->bundle->dev, "transfer operation failed: %d\n", ret); - } + msg->state = GB_SPI_STATE_MSG_ERROR; + } - gb_operation_put(operation); + gb_operation_put(operation); + setup_next_xfer(spi, msg); + } - msg->actual_length = len; - msg->status = 0; +out: + msg->status = ret; + clean_xfer_state(spi); spi_finalize_current_message(master); return ret; -- cgit v1.2.3-59-g8ed1b From d1d67714a4ece3c1438c9d7def324ee0424e7cbd Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Wed, 20 Apr 2016 14:40:23 +0100 Subject: greybus: spi: use timeout request send operation When transfer speed is too slow (less than 17Khz) the operation can take longer than the default greybus timeout. Because of this we need to use the request_send_sync_timeout and calculate the correct timeout for each operation. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Tested-by: Philip Yang <philipy@bsquare.com> Tested-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/spi.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 0bae7f12ccf1..ce706ed218e2 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -23,6 +23,7 @@ struct gb_spi { u32 rx_xfer_offset; u32 tx_xfer_offset; u32 last_xfer_size; + unsigned int op_timeout; u16 mode; u16 flags; u32 bits_per_word_mask; @@ -38,6 +39,8 @@ struct gb_spi { #define GB_SPI_STATE_OP_DONE ((void *)4) #define GB_SPI_STATE_MSG_ERROR ((void *)-1) +#define XFER_TIMEOUT_TOLERANCE 200 + static struct spi_master *get_master_from_spi(struct gb_spi *spi) { return gb_connection_get_data(spi->connection); @@ -95,6 +98,7 @@ static void clean_xfer_state(struct gb_spi *spi) spi->rx_xfer_offset = 0; spi->tx_xfer_offset = 0; spi->last_xfer_size = 0; + spi->op_timeout = 0; } static int setup_next_xfer(struct gb_spi *spi, struct spi_message *msg) @@ -112,6 +116,7 @@ static int setup_next_xfer(struct gb_spi *spi, struct spi_message *msg) (spi->rx_xfer_offset + spi->last_xfer_size == last_xfer->len)) { spi->tx_xfer_offset = 0; spi->rx_xfer_offset = 0; + spi->op_timeout = 0; if (last_xfer == list_last_entry(&msg->transfers, struct spi_transfer, transfer_list)) @@ -155,6 +160,7 @@ gb_spi_operation_create(struct gb_spi *spi, struct gb_connection *connection, u32 tx_size = 0, rx_size = 0, count = 0, xfer_len = 0, request_size; u32 tx_xfer_size = 0, rx_xfer_size = 0, len; u32 total_len = 0; + unsigned int xfer_timeout; size_t data_max; void *tx_data; @@ -234,6 +240,13 @@ gb_spi_operation_create(struct gb_spi *spi, struct gb_connection *connection, else xfer_len = xfer->len; + /* make sure we do not timeout in a slow transfer */ + xfer_timeout = xfer_len * 8 * MSEC_PER_SEC / xfer->speed_hz; + xfer_timeout += GB_OPERATION_TIMEOUT_DEFAULT; + + if (xfer_timeout > spi->op_timeout) + spi->op_timeout = xfer_timeout; + gb_xfer->speed_hz = cpu_to_le32(xfer->speed_hz); gb_xfer->len = cpu_to_le32(xfer_len); gb_xfer->delay_usecs = cpu_to_le16(xfer->delay_usecs); @@ -322,7 +335,8 @@ static int gb_spi_transfer_one_message(struct spi_master *master, continue; } - ret = gb_operation_request_send_sync(operation); + ret = gb_operation_request_send_sync_timeout(operation, + spi->op_timeout); if (!ret) { response = operation->response->payload; if (response) -- cgit v1.2.3-59-g8ed1b From 7d963cbe8302d16d2171ecc729782f0d2e7cfbe7 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:18:55 +0200 Subject: greybus: manifest: fix illegal free in error path The manifest-parsing code could end up leaving the interface vendor_string set to an error pointer that we'd eventually try to free when destroying the interface. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/manifest.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 070afc619f84..cca4592c15ad 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -384,15 +384,18 @@ static bool gb_manifest_parse_interface(struct gb_interface *intf, struct manifest_desc *interface_desc) { struct greybus_descriptor_interface *desc_intf = interface_desc->data; + char *str; /* Handle the strings first--they can fail */ - intf->vendor_string = gb_string_get(intf, desc_intf->vendor_stringid); - if (IS_ERR(intf->vendor_string)) + str = gb_string_get(intf, desc_intf->vendor_stringid); + if (IS_ERR(str)) return false; + intf->vendor_string = str; - intf->product_string = gb_string_get(intf, desc_intf->product_stringid); - if (IS_ERR(intf->product_string)) + str = gb_string_get(intf, desc_intf->product_stringid); + if (IS_ERR(str)) goto out_free_vendor_string; + intf->product_string = str; /* Release the interface descriptor, now that we're done with it */ release_manifest_descriptor(interface_desc); -- cgit v1.2.3-59-g8ed1b From aba2fc626e02d493a816bd9e89d338509ca5accc Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:18:56 +0200 Subject: greybus: Documentation: move the interface power attributes Move the interface power attributes after the other interface attributes to keep the attributes grouped by device type. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 42 +++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 281b05cb889c..d8bf30e90586 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -81,6 +81,27 @@ Description: Interface version represented as <16 bit major number>.<16 bit minor number>. +What: /sys/bus/greybus/device/N-I/voltage_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Voltage measurement of the interface in microvolts (uV) + +What: /sys/bus/greybus/device/N-I/current_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Current measurement of the interface in microamps (uA) + +What: /sys/bus/greybus/device/N-I/power_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Power measurement of the interface in microwatts (uW) + What: /sys/bus/greybus/device/N-I.B Date: October 2015 KernelVersion: 4.XX @@ -164,24 +185,3 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: If the SVC watchdog is enabled or not. Writing 0 to this file will disable the watchdog, writing 1 will enable it. - -What: /sys/bus/greybus/device/N-I/voltage_now -Date: March 2016 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman <greg@kroah.com> -Description: - Voltage measurement of the interface in microvolts (uV) - -What: /sys/bus/greybus/device/N-I/current_now -Date: March 2016 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman <greg@kroah.com> -Description: - Current measurement of the interface in microamps (uA) - -What: /sys/bus/greybus/device/N-I/power_now -Date: March 2016 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman <greg@kroah.com> -Description: - Power measurement of the interface in microwatts (uW) -- cgit v1.2.3-59-g8ed1b From 78b609b14c432d9ce9a994b31b6d18ae3fd41288 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:18:57 +0200 Subject: greybus: Documentation/sysfs: remove interface unique_id attribute Remove the interface unique_id attribute, which there is currently no plan to ever implement. Note that the Ara serial numbers are already exposed through the serial_number attribute. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/unique_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/unique_id | 0 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/unique_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/unique_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/unique_id diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/unique_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/unique_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/unique_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/unique_id deleted file mode 100644 index e69de29bb2d1..000000000000 -- cgit v1.2.3-59-g8ed1b From 9e5e6fa6bf66503f4eb48601f3d7b58c8a5f2b0a Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:18:58 +0200 Subject: greybus: Documentation/sysfs: rename the svc eject attribute The svc eject attribute was added as an interim solution and is still used to implement a form of forced ejection. This will soon be superseded by the module eject attribute, which will provide an interface for clean eject. We may keep the forced-eject mechanism around indefinitely, albeit possibly with a different name (e.g. forced_intf_eject). Either way, update the example tree to reflect the actual name, intf_eject, which currently used for this svc attribute. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject | 0 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/eject create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/eject create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/eject deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/eject b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/eject deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject new file mode 100644 index 000000000000..e69de29bb2d1 -- cgit v1.2.3-59-g8ed1b From 078ef067bca6725ab71c0c8d58112ae66097062f Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:18:59 +0200 Subject: greybus: Documentation/sysfs: add example control devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add control devices to the example sysfs tree. Control devices are named <bus_id>-<module_id>.<intf_id>.ctrl and expose attributes that are specific to the greybus interface type. Specifically, dummy interfaces do not have a control device. Currently, only the vendor and product strings extracted from the manifest are exported. A subtree of the example tree now looks as follows: greybus1/ ├── 1-5 │   ├── 1-5.5 │   │   ├── 1-5.5.2 │   │   │   ├── bundle_class │   │   │   ├── bundle_id │   │   │   └── state │   │   ├── 1-5.5.ctrl │   │   │   ├── product_string │   │   │   └── vendor_string │   │   ├── ddbl1_manufacturer_id │   │   ├── ddbl1_product_id │   │   ├── interface_id │   │   ├── product_id │   │   ├── serial_number │   │   └── vendor_id │   ├── 1-5.6 │   │   └── interface_id │   ├── eject │   ├── module_id │   └── num_interfaces └── 1-svc Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string | 0 .../Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string | 0 .../Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string | 0 .../Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string | 0 .../Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string | 0 .../Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string | 0 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string new file mode 100644 index 000000000000..e69de29bb2d1 -- cgit v1.2.3-59-g8ed1b From a530a8be395ff2df384ca729013890f3f040cf99 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:00 +0200 Subject: greybus: interface: disable interface on registration failures Disable and deactivate an interface immediately on registration failures. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b79d81717360..5a16ad33b74b 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -518,7 +518,12 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) goto err_interface_deactivate; } - gb_interface_add(intf); + ret = gb_interface_add(intf); + if (ret) { + gb_interface_disable(intf); + gb_interface_deactivate(intf); + return; + } return; -- cgit v1.2.3-59-g8ed1b From 1ed8cdef405806c246b62a1ba926e0251fdaa531 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:01 +0200 Subject: greybus: control: move timesync-operation functions Move the timesync-operation functions above the control-object management functions, which is where all other operation implementations reside. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 72 +++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 91a7fcac4988..3c1f66400a79 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -177,6 +177,42 @@ int gb_control_get_interface_version_operation(struct gb_interface *intf) return 0; } +int gb_control_timesync_enable(struct gb_control *control, u8 count, + u64 frame_time, u32 strobe_delay, u32 refclk) +{ + struct gb_control_timesync_enable_request request; + + request.count = count; + request.frame_time = cpu_to_le64(frame_time); + request.strobe_delay = cpu_to_le32(strobe_delay); + request.refclk = cpu_to_le32(refclk); + return gb_operation_sync(control->connection, + GB_CONTROL_TYPE_TIMESYNC_ENABLE, &request, + sizeof(request), NULL, 0); +} + +int gb_control_timesync_disable(struct gb_control *control) +{ + return gb_operation_sync(control->connection, + GB_CONTROL_TYPE_TIMESYNC_DISABLE, NULL, 0, + NULL, 0); +} + +int gb_control_timesync_authoritative(struct gb_control *control, + u64 *frame_time, u8 count) +{ + struct gb_control_timesync_authoritative_request request; + int i; + + for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) + request.frame_time[i] = cpu_to_le64(frame_time[i]); + + return gb_operation_sync(control->connection, + GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE, + &request, sizeof(request), + NULL, 0); +} + struct gb_control *gb_control_create(struct gb_interface *intf) { struct gb_control *control; @@ -240,39 +276,3 @@ void gb_control_destroy(struct gb_control *control) gb_connection_destroy(control->connection); kfree(control); } - -int gb_control_timesync_enable(struct gb_control *control, u8 count, - u64 frame_time, u32 strobe_delay, u32 refclk) -{ - struct gb_control_timesync_enable_request request; - - request.count = count; - request.frame_time = cpu_to_le64(frame_time); - request.strobe_delay = cpu_to_le32(strobe_delay); - request.refclk = cpu_to_le32(refclk); - return gb_operation_sync(control->connection, - GB_CONTROL_TYPE_TIMESYNC_ENABLE, &request, - sizeof(request), NULL, 0); -} - -int gb_control_timesync_disable(struct gb_control *control) -{ - return gb_operation_sync(control->connection, - GB_CONTROL_TYPE_TIMESYNC_DISABLE, NULL, 0, - NULL, 0); -} - -int gb_control_timesync_authoritative(struct gb_control *control, - u64 *frame_time, u8 count) -{ - struct gb_control_timesync_authoritative_request request; - int i; - - for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) - request.frame_time[i] = cpu_to_le64(frame_time[i]); - - return gb_operation_sync(control->connection, - GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE, - &request, sizeof(request), - NULL, 0); -} -- cgit v1.2.3-59-g8ed1b From a6e5b014b8fe0bd4cb1e1ca0380320a200605742 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:02 +0200 Subject: greybus: core: make the control object be a device Make the control object be a greybus device. The control device will be used to expose attributes specific to greybus-type interfaces. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 28 +++++++++++++++++++++++++--- drivers/staging/greybus/control.h | 8 ++++++-- drivers/staging/greybus/core.c | 5 +++++ drivers/staging/greybus/greybus.h | 6 ++++++ drivers/staging/greybus/interface.c | 2 +- 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 3c1f66400a79..c303bb2937bc 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -213,6 +213,20 @@ int gb_control_timesync_authoritative(struct gb_control *control, NULL, 0); } +static void gb_control_release(struct device *dev) +{ + struct gb_control *control = to_gb_control(dev); + + gb_connection_destroy(control->connection); + + kfree(control); +} + +struct device_type greybus_control_type = { + .name = "greybus_control", + .release = gb_control_release, +}; + struct gb_control *gb_control_create(struct gb_interface *intf) { struct gb_control *control; @@ -221,6 +235,8 @@ struct gb_control *gb_control_create(struct gb_interface *intf) if (!control) return NULL; + control->intf = intf; + control->connection = gb_connection_create_control(intf); if (IS_ERR(control->connection)) { dev_err(&intf->dev, @@ -230,6 +246,13 @@ struct gb_control *gb_control_create(struct gb_interface *intf) return NULL; } + control->dev.parent = &intf->dev; + control->dev.bus = &greybus_bus_type; + control->dev.type = &greybus_control_type; + control->dev.dma_mask = intf->dev.dma_mask; + device_initialize(&control->dev); + dev_set_name(&control->dev, "%s.ctrl", dev_name(&intf->dev)); + gb_connection_set_data(control->connection, control); return control; @@ -271,8 +294,7 @@ void gb_control_disable(struct gb_control *control) gb_connection_disable(control->connection); } -void gb_control_destroy(struct gb_control *control) +void gb_control_put(struct gb_control *control) { - gb_connection_destroy(control->connection); - kfree(control); + put_device(&control->dev); } diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 949f1a3c433b..697f901b34a7 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -11,18 +11,22 @@ #define __CONTROL_H struct gb_control { - struct gb_connection *connection; + struct device dev; + struct gb_interface *intf; + + struct gb_connection *connection; u8 protocol_major; u8 protocol_minor; bool has_bundle_version; }; +#define to_gb_control(d) container_of(d, struct gb_control, dev) struct gb_control *gb_control_create(struct gb_interface *intf); int gb_control_enable(struct gb_control *control); void gb_control_disable(struct gb_control *control); -void gb_control_destroy(struct gb_control *control); +void gb_control_put(struct gb_control *control); int gb_control_get_bundle_versions(struct gb_control *control); int gb_control_connected_operation(struct gb_control *control, u16 cport_id); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index d8680667ca32..9f143e5a7c9c 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -86,6 +86,7 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { struct gb_host_device *hd; struct gb_interface *intf = NULL; + struct gb_control *control = NULL; struct gb_bundle *bundle = NULL; struct gb_svc *svc = NULL; @@ -94,6 +95,10 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) } else if (is_gb_interface(dev)) { intf = to_gb_interface(dev); hd = intf->hd; + } else if (is_gb_control(dev)) { + control = to_gb_control(dev); + intf = control->intf; + hd = intf->hd; } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); intf = bundle->intf; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2fc79faad28e..4ab5f608b8cd 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -113,6 +113,7 @@ extern struct bus_type greybus_bus_type; extern struct device_type greybus_hd_type; extern struct device_type greybus_interface_type; +extern struct device_type greybus_control_type; extern struct device_type greybus_bundle_type; extern struct device_type greybus_svc_type; @@ -126,6 +127,11 @@ static inline int is_gb_interface(const struct device *dev) return dev->type == &greybus_interface_type; } +static inline int is_gb_control(const struct device *dev) +{ + return dev->type == &greybus_control_type; +} + static inline int is_gb_bundle(const struct device *dev) { return dev->type == &greybus_bundle_type; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index a4bee41feec4..56a320732ef9 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -360,7 +360,7 @@ static void gb_interface_release(struct device *dev) kfree(intf->vendor_string); if (intf->control) - gb_control_destroy(intf->control); + gb_control_put(intf->control); kfree(intf); } -- cgit v1.2.3-59-g8ed1b From 7326e07b588cace823a36d4a563838b051b8b8a5 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:03 +0200 Subject: greybus: interface: register control device at hotplug Make sure to register also the control device along with any bundles when registering an interface. Note that we currently ignore failures to register the control device just as we do for bundle devices. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 21 +++++++++++++++++++++ drivers/staging/greybus/control.h | 2 ++ drivers/staging/greybus/interface.c | 4 ++++ 3 files changed, 27 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index c303bb2937bc..606361906482 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -294,6 +294,27 @@ void gb_control_disable(struct gb_control *control) gb_connection_disable(control->connection); } +int gb_control_add(struct gb_control *control) +{ + int ret; + + ret = device_add(&control->dev); + if (ret) { + dev_err(&control->dev, + "failed to register control device: %d\n", + ret); + return ret; + } + + return 0; +} + +void gb_control_del(struct gb_control *control) +{ + if (device_is_registered(&control->dev)) + device_del(&control->dev); +} + void gb_control_put(struct gb_control *control) { put_device(&control->dev); diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 697f901b34a7..102e937a982c 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -26,6 +26,8 @@ struct gb_control { struct gb_control *gb_control_create(struct gb_interface *intf); int gb_control_enable(struct gb_control *control); void gb_control_disable(struct gb_control *control); +int gb_control_add(struct gb_control *control); +void gb_control_del(struct gb_control *control); void gb_control_put(struct gb_control *control); int gb_control_get_bundle_versions(struct gb_control *control); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 56a320732ef9..e0e08731c055 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -537,6 +537,7 @@ void gb_interface_disable(struct gb_interface *intf) list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); + gb_control_del(intf->control); gb_control_disable(intf->control); } @@ -557,6 +558,9 @@ int gb_interface_add(struct gb_interface *intf) dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n", intf->ddbl1_manufacturer_id, intf->ddbl1_product_id); + /* NOTE: ignoring errors for now */ + gb_control_add(intf->control); + list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) { ret = gb_bundle_add(bundle); if (ret) { -- cgit v1.2.3-59-g8ed1b From 7c8eb12dbb4c1b43ce705a8ba1a5c9c1191e75d8 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:04 +0200 Subject: greybus: interface: move vendor and product strings to control device The control device is an abstraction of the control connection over which a greybus manifest is retrieved. As interfaces switch modes (e.g. after boot-over-unipro) they expose new manifests, which can contain different vendor and product strings. Eventually control devices will be deregistered and recreated after an interface mode switch, while the interface itself remains registered. Note that only interfaces of type greybus will have control devices. Specifically, dummy interfaces will not. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 28 ++++++++++----------- drivers/staging/greybus/control.c | 29 ++++++++++++++++++++++ drivers/staging/greybus/control.h | 3 +++ drivers/staging/greybus/interface.c | 7 ------ drivers/staging/greybus/interface.h | 4 --- drivers/staging/greybus/manifest.c | 13 +++++----- 6 files changed, 53 insertions(+), 31 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index d8bf30e90586..4831efb9d0f7 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -52,13 +52,6 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Product ID of a Greybus interface. -What: /sys/bus/greybus/device/N-I/product_string -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman <greg@kroah.com> -Description: - Product ID string of a Greybus interface. - What: /sys/bus/greybus/device/N-I/vendor_id Date: October 2015 KernelVersion: 4.XX @@ -66,13 +59,6 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Vendor ID of a Greybus interface. -What: /sys/bus/greybus/device/N-I/vendor_string -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman <greg@kroah.com> -Description: - Vendor ID string of a Greybus interface block. - What: /sys/bus/greybus/device/N-I/version Date: October 2015 KernelVersion: 4.XX @@ -110,6 +96,20 @@ Description: A bundle B on the Interface I, B is replaced by a 1-byte number representing the bundle. +What: /sys/bus/greybus/device/N-I.ctrl/product_string +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Product ID string of a Greybus interface. + +What: /sys/bus/greybus/device/N-I.ctrl/vendor_string +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Vendor ID string of a Greybus interface. + What: /sys/bus/greybus/device/N-I.B/bundle_class Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 606361906482..58a3d6089701 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -213,12 +213,40 @@ int gb_control_timesync_authoritative(struct gb_control *control, NULL, 0); } +static ssize_t vendor_string_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_control *control = to_gb_control(dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", control->vendor_string); +} +static DEVICE_ATTR_RO(vendor_string); + +static ssize_t product_string_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_control *control = to_gb_control(dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", control->product_string); +} +static DEVICE_ATTR_RO(product_string); + +static struct attribute *control_attrs[] = { + &dev_attr_vendor_string.attr, + &dev_attr_product_string.attr, + NULL, +}; +ATTRIBUTE_GROUPS(control); + static void gb_control_release(struct device *dev) { struct gb_control *control = to_gb_control(dev); gb_connection_destroy(control->connection); + kfree(control->vendor_string); + kfree(control->product_string); + kfree(control); } @@ -249,6 +277,7 @@ struct gb_control *gb_control_create(struct gb_interface *intf) control->dev.parent = &intf->dev; control->dev.bus = &greybus_bus_type; control->dev.type = &greybus_control_type; + control->dev.groups = control_groups; control->dev.dma_mask = intf->dev.dma_mask; device_initialize(&control->dev); dev_set_name(&control->dev, "%s.ctrl", dev_name(&intf->dev)); diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 102e937a982c..56fa2592762b 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -20,6 +20,9 @@ struct gb_control { u8 protocol_minor; bool has_bundle_version; + + char *vendor_string; + char *product_string; }; #define to_gb_control(d) container_of(d, struct gb_control, dev) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index e0e08731c055..ed56f2de7dbd 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -249,8 +249,6 @@ gb_interface_attr(ddbl1_product_id, "0x%08x"); gb_interface_attr(interface_id, "%u"); gb_interface_attr(vendor_id, "0x%08x"); gb_interface_attr(product_id, "0x%08x"); -gb_interface_attr(vendor_string, "%s"); -gb_interface_attr(product_string, "%s"); gb_interface_attr(serial_number, "0x%016llx"); static ssize_t version_show(struct device *dev, struct device_attribute *attr, @@ -326,8 +324,6 @@ static struct attribute *interface_attrs[] = { &dev_attr_interface_id.attr, &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, - &dev_attr_vendor_string.attr, - &dev_attr_product_string.attr, &dev_attr_serial_number.attr, &dev_attr_version.attr, &dev_attr_voltage_now.attr, @@ -356,9 +352,6 @@ static void gb_interface_release(struct device *dev) { struct gb_interface *intf = to_gb_interface(dev); - kfree(intf->product_string); - kfree(intf->vendor_string); - if (intf->control) gb_control_put(intf->control); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 15c687f5dbc1..567b6c8ae713 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -25,10 +25,6 @@ struct gb_interface { u8 interface_id; /* Physical location within the Endo */ u8 device_id; - /* Information taken from the manifest descriptor */ - char *vendor_string; - char *product_string; - u32 ddbl1_manufacturer_id; u32 ddbl1_product_id; u32 vendor_id; diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index cca4592c15ad..886c5fb91d9f 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -384,18 +384,19 @@ static bool gb_manifest_parse_interface(struct gb_interface *intf, struct manifest_desc *interface_desc) { struct greybus_descriptor_interface *desc_intf = interface_desc->data; + struct gb_control *control = intf->control; char *str; /* Handle the strings first--they can fail */ str = gb_string_get(intf, desc_intf->vendor_stringid); if (IS_ERR(str)) return false; - intf->vendor_string = str; + control->vendor_string = str; str = gb_string_get(intf, desc_intf->product_stringid); if (IS_ERR(str)) goto out_free_vendor_string; - intf->product_string = str; + control->product_string = str; /* Release the interface descriptor, now that we're done with it */ release_manifest_descriptor(interface_desc); @@ -408,11 +409,11 @@ static bool gb_manifest_parse_interface(struct gb_interface *intf, return true; out_err: - kfree(intf->product_string); - intf->product_string = NULL; + kfree(control->product_string); + control->product_string = NULL; out_free_vendor_string: - kfree(intf->vendor_string); - intf->vendor_string = NULL; + kfree(control->vendor_string); + control->vendor_string = NULL; return false; } -- cgit v1.2.3-59-g8ed1b From b6147e4fb13e926878dbef9adae429faf8d8c2dd Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:05 +0200 Subject: greybus: control: return error pointer when failing to create control device Return an error pointer when failing to create a control device. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 13 ++++++++----- drivers/staging/greybus/interface.c | 6 ++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 58a3d6089701..20aa366c3883 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -257,23 +257,26 @@ struct device_type greybus_control_type = { struct gb_control *gb_control_create(struct gb_interface *intf) { + struct gb_connection *connection; struct gb_control *control; control = kzalloc(sizeof(*control), GFP_KERNEL); if (!control) - return NULL; + return ERR_PTR(-ENOMEM); control->intf = intf; - control->connection = gb_connection_create_control(intf); - if (IS_ERR(control->connection)) { + connection = gb_connection_create_control(intf); + if (IS_ERR(connection)) { dev_err(&intf->dev, "failed to create control connection: %ld\n", - PTR_ERR(control->connection)); + PTR_ERR(connection)); kfree(control); - return NULL; + return ERR_CAST(connection); } + control->connection = connection; + control->dev.parent = &intf->dev; control->dev.bus = &greybus_bus_type; control->dev.type = &greybus_control_type; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index ed56f2de7dbd..f6271127ffaa 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -381,6 +381,7 @@ struct device_type greybus_interface_type = { struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id) { + struct gb_control *control; struct gb_interface *intf; intf = kzalloc(sizeof(*intf), GFP_KERNEL); @@ -403,11 +404,12 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, device_initialize(&intf->dev); dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id); - intf->control = gb_control_create(intf); - if (!intf->control) { + control = gb_control_create(intf); + if (IS_ERR(control)) { put_device(&intf->dev); return NULL; } + intf->control = control; list_add(&intf->links, &hd->interfaces); -- cgit v1.2.3-59-g8ed1b From 49605839bcd644016e2ec3b1534104e2cb3ce984 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:06 +0200 Subject: greybus: interface: reduce control-device lifetime Make the control-device lifetime coincide with when the interface is enabled (enumerated). This is needed to be able register a new control device after a mode switch. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 34 ++++++++++++++++++++++------------ drivers/staging/greybus/interface.h | 1 + 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index f6271127ffaa..5c87fbcd8937 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -352,9 +352,6 @@ static void gb_interface_release(struct device *dev) { struct gb_interface *intf = to_gb_interface(dev); - if (intf->control) - gb_control_put(intf->control); - kfree(intf); } @@ -381,7 +378,6 @@ struct device_type greybus_interface_type = { struct gb_interface *gb_interface_create(struct gb_host_device *hd, u8 interface_id) { - struct gb_control *control; struct gb_interface *intf; intf = kzalloc(sizeof(*intf), GFP_KERNEL); @@ -404,13 +400,6 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, device_initialize(&intf->dev); dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id); - control = gb_control_create(intf); - if (IS_ERR(control)) { - put_device(&intf->dev); - return NULL; - } - intf->control = control; - list_add(&intf->links, &hd->interfaces); return intf; @@ -442,6 +431,7 @@ void gb_interface_deactivate(struct gb_interface *intf) */ int gb_interface_enable(struct gb_interface *intf) { + struct gb_control *control; struct gb_bundle *bundle, *tmp; int ret, size; void *manifest; @@ -453,9 +443,17 @@ int gb_interface_enable(struct gb_interface *intf) } /* Establish control connection */ + control = gb_control_create(intf); + if (IS_ERR(control)) { + dev_err(&intf->dev, "failed to create control device: %lu\n", + PTR_ERR(control)); + return PTR_ERR(control); + } + intf->control = control; + ret = gb_control_enable(intf->control); if (ret) - return ret; + goto err_put_control; /* Get manifest size using control protocol on CPort */ size = gb_control_get_manifest_size_operation(intf); @@ -503,6 +501,8 @@ int gb_interface_enable(struct gb_interface *intf) kfree(manifest); + intf->enabled = true; + return 0; err_destroy_bundles: @@ -512,6 +512,9 @@ err_free_manifest: kfree(manifest); err_disable_control: gb_control_disable(intf->control); +err_put_control: + gb_control_put(intf->control); + intf->control = NULL; return ret; } @@ -522,6 +525,9 @@ void gb_interface_disable(struct gb_interface *intf) struct gb_bundle *bundle; struct gb_bundle *next; + if (!intf->enabled) + return; + /* * Disable the control-connection early to avoid operation timeouts * when the interface is already gone. @@ -534,6 +540,10 @@ void gb_interface_disable(struct gb_interface *intf) gb_control_del(intf->control); gb_control_disable(intf->control); + gb_control_put(intf->control); + intf->control = NULL; + + intf->enabled = false; } /* Register an interface and its bundles. */ diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 567b6c8ae713..63ba696c14a5 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -39,6 +39,7 @@ struct gb_interface { unsigned long quirks; bool disconnected; + bool enabled; }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) -- cgit v1.2.3-59-g8ed1b From 87a4c819a545e41eacab0de1e250173963dacbf8 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:07 +0200 Subject: greybus: svc: make sure to deactivate all interfaces on disconnect Make sure to deactivate all interfaces when the svc is going away. This is needed to eventually be able to do controlled teardown of the unipro network. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 5a16ad33b74b..25978e718ece 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -899,6 +899,7 @@ static void gb_svc_remove_interfaces(struct gb_svc *svc) list_for_each_entry_safe(intf, tmp, &svc->hd->interfaces, links) { gb_interface_disable(intf); + gb_interface_deactivate(intf); gb_interface_remove(intf); } } -- cgit v1.2.3-59-g8ed1b From 96fb6c340b4abb295ae5c7e904befef33a1d0325 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:08 +0200 Subject: greybus: svc: keep interfaces registered during mode switch Keep a detected interface registered until it is physically removed. Specifically, do not re-register an interface that is switching mode. Note that this also allows us to get rid of some nasty hacks from core. The Ara VID/PID bootrom hack for ES2 will continue to work, but is now mostly confined to the bootrom driver. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 32 +++++++++++---------- drivers/staging/greybus/svc.c | 55 ++++++++++--------------------------- 2 files changed, 31 insertions(+), 56 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 5c87fbcd8937..89fe901cb0a3 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -426,8 +426,9 @@ void gb_interface_deactivate(struct gb_interface *intf) } /* - * Enable an interface by enabling its control connection and fetching the - * manifest and other information over it. + * Enable an interface by enabling its control connection, fetching the + * manifest and other information over it, and finally registering its child + * devices. */ int gb_interface_enable(struct gb_interface *intf) { @@ -499,6 +500,19 @@ int gb_interface_enable(struct gb_interface *intf) if (ret) goto err_destroy_bundles; + /* Register the control device and any bundles */ + ret = gb_control_add(intf->control); + if (ret) + goto err_destroy_bundles; + + list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) { + ret = gb_bundle_add(bundle); + if (ret) { + gb_bundle_destroy(bundle); + continue; + } + } + kfree(manifest); intf->enabled = true; @@ -546,10 +560,9 @@ void gb_interface_disable(struct gb_interface *intf) intf->enabled = false; } -/* Register an interface and its bundles. */ +/* Register an interface. */ int gb_interface_add(struct gb_interface *intf) { - struct gb_bundle *bundle, *tmp; int ret; ret = device_add(&intf->dev); @@ -563,17 +576,6 @@ int gb_interface_add(struct gb_interface *intf) dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n", intf->ddbl1_manufacturer_id, intf->ddbl1_product_id); - /* NOTE: ignoring errors for now */ - gb_control_add(intf->control); - - list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) { - ret = gb_bundle_add(bundle); - if (ret) { - gb_bundle_destroy(bundle); - continue; - } - } - return 0; } diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 25978e718ece..a9ef16ecd0d9 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -452,8 +452,6 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) struct gb_host_device *hd = connection->hd; struct gb_interface *intf; u8 intf_id; - u32 vendor_id = 0; - u32 product_id = 0; int ret; /* The request message size has already been verified. */ @@ -464,27 +462,15 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) intf = gb_interface_find(hd, intf_id); if (intf) { - /* HACK: Save Ara VID/PID for ES2 hack below */ - vendor_id = intf->vendor_id; - product_id = intf->product_id; - - /* - * We have received a hotplug request for an interface that - * already exists. - * - * This can happen in cases like: - * - bootrom loading the firmware image and booting into that, - * which only generates a hotplug event. i.e. no hot-unplug - * event. - * - Or the firmware on the module crashed and sent hotplug - * request again to the SVC, which got propagated to AP. - * - * Remove the interface and add it again, and let user know - * about this with a print message. - */ - dev_info(&svc->dev, "removing interface %u to add it again\n", + dev_info(&svc->dev, "mode switch detected on interface %u\n", intf_id); - gb_svc_intf_remove(svc, intf); + + /* Mark as disconnected to prevent I/O during disable. */ + intf->disconnected = true; + gb_interface_disable(intf); + intf->disconnected = false; + + goto enable_interface; } intf = gb_interface_create(hd, intf_id); @@ -498,19 +484,15 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) if (ret) { dev_err(&svc->dev, "failed to activate interface %u: %d\n", intf_id, ret); - goto err_interface_add; + gb_interface_add(intf); + return; } - /* - * HACK: Use Ara VID/PID from earlier boot stage. - * - * FIXME: remove quirk with ES2 support - */ - if (intf->quirks & GB_INTERFACE_QUIRK_NO_ARA_IDS) { - intf->vendor_id = vendor_id; - intf->product_id = product_id; - } + ret = gb_interface_add(intf); + if (ret) + goto err_interface_deactivate; +enable_interface: ret = gb_interface_enable(intf); if (ret) { dev_err(&svc->dev, "failed to enable interface %u: %d\n", @@ -518,19 +500,10 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) goto err_interface_deactivate; } - ret = gb_interface_add(intf); - if (ret) { - gb_interface_disable(intf); - gb_interface_deactivate(intf); - return; - } - return; err_interface_deactivate: gb_interface_deactivate(intf); -err_interface_add: - gb_interface_add(intf); } static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) -- cgit v1.2.3-59-g8ed1b From 41d514020f4844905f349cd51476d03db254317a Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Apr 2016 19:19:09 +0200 Subject: greybus: svc: remove interface-remove helper Remove unnecessary interface-remove helper. Also add comment about why the disconnected flag is set. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index a9ef16ecd0d9..516a452ab213 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -435,15 +435,6 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } -static void gb_svc_intf_remove(struct gb_svc *svc, struct gb_interface *intf) -{ - intf->disconnected = true; - - gb_interface_disable(intf); - gb_interface_deactivate(intf); - gb_interface_remove(intf); -} - static void gb_svc_process_intf_hotplug(struct gb_operation *operation) { struct gb_svc_intf_hotplug_request *request; @@ -527,7 +518,12 @@ static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) return; } - gb_svc_intf_remove(svc, intf); + /* Mark as disconnected to prevent I/O during disable. */ + intf->disconnected = true; + + gb_interface_disable(intf); + gb_interface_deactivate(intf); + gb_interface_remove(intf); } static void gb_svc_process_deferred_request(struct work_struct *work) -- cgit v1.2.3-59-g8ed1b From b4b1b7eda9fe0d5a399d9095b2e8d5657726538c Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Wed, 20 Apr 2016 20:47:20 +0200 Subject: greybus: uart: Update line coding settings only when needed The check for line coding changed should use memcmp and not memcpy. Testing done: trivial Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index c580fe06f554..be718918c135 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -383,7 +383,7 @@ static void gb_tty_set_termios(struct tty_struct *tty, send_control(gb_tty, newctrl); } - if (memcpy(&gb_tty->line_coding, &newline, sizeof(newline))) { + if (memcmp(&gb_tty->line_coding, &newline, sizeof(newline))) { memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); send_line_coding(gb_tty); } -- cgit v1.2.3-59-g8ed1b From 05a849191f964cf684777fed9ac471d59f2f18ef Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Mon, 18 Apr 2016 13:11:31 -0700 Subject: greybus: es2: Fix apb_log null pointer exception Read on apb_log causes null pointer exception due to the missing es2 device pointer passing to the debugfs. Testing done: - Enable apb_log and cat it Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 239adf7a3749..9e06efb77243 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -842,7 +842,7 @@ static void usb_log_enable(struct es2_ap_dev *es2) return; /* XXX We will need to rename this per APB */ es2->apb_log_dentry = debugfs_create_file("apb_log", S_IRUGO, - gb_debugfs_get(), NULL, + gb_debugfs_get(), es2, &apb_log_fops); } -- cgit v1.2.3-59-g8ed1b From 9504677c9a9ef4123e4bc9fb8f6903b92453ea6f Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Wed, 20 Apr 2016 16:55:08 -0700 Subject: greybus: svc: add AP power measurements debugfs support This change adds the AP Power Monitor functions to read out all the rails power information monitored by the SVC. Testing Done: - $ cat /d/greybus/1-svc/pwrmon/*/* and validate the output with the svc stub power monitor functions - $ tree /d/greybus/1-svc/pwrmon | | | |---pwrmon | | | | |---DUMMY_RAIL_1 | | | | | |---current_now | | | | | |---power_now | | | | | |---voltage_now | | | | |---DUMMY_RAIL_2 | | | | | |---current_now | | | | | |---power_now | | | | | |---voltage_now | | | | |---DUMMY_RAIL_3 | | | | | |---current_now | | | | | |---power_now | | | | | |---voltage_now | | | | |---DUMMY_RAIL_4 | | | | | |---current_now | | | | | |---power_now | | | | | |---voltage_now Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 22 +++ drivers/staging/greybus/svc.c | 231 ++++++++++++++++++++++++++++ drivers/staging/greybus/svc.h | 10 ++ 3 files changed, 263 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e0c0493605ca..82798cf3ea2b 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -967,6 +967,18 @@ struct gb_svc_key_event_request { #define GB_SVC_KEY_PRESSED 0x01 } __packed; +#define GB_SVC_PWRMON_MAX_RAIL_COUNT 254 + +struct gb_svc_pwrmon_rail_count_get_response { + __u8 rail_count; +} __packed; + +#define GB_SVC_PWRMON_RAIL_NAME_BUFSIZE 32 + +struct gb_svc_pwrmon_rail_names_get_response { + __u8 name[0][GB_SVC_PWRMON_RAIL_NAME_BUFSIZE]; +} __packed; + #define GB_SVC_PWRMON_TYPE_CURR 0x01 #define GB_SVC_PWRMON_TYPE_VOL 0x02 #define GB_SVC_PWRMON_TYPE_PWR 0x03 @@ -976,6 +988,16 @@ struct gb_svc_key_event_request { #define GB_SVC_PWRMON_GET_SAMPLE_NOSUPP 0x02 #define GB_SVC_PWRMON_GET_SAMPLE_HWERR 0x03 +struct gb_svc_pwrmon_sample_get_request { + __u8 rail_id; + __u8 measurement_type; +} __packed; + +struct gb_svc_pwrmon_sample_get_response { + __u8 result; + __le32 measurement; +} __packed; + struct gb_svc_pwrmon_intf_sample_get_request { __u8 intf_id; __u8 measurement_type; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 516a452ab213..1791dcce5217 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -7,6 +7,7 @@ * Released under the GPLv2 only. */ +#include <linux/debugfs.h> #include <linux/input.h> #include <linux/workqueue.h> @@ -99,6 +100,78 @@ static ssize_t watchdog_store(struct device *dev, } static DEVICE_ATTR_RW(watchdog); +static int gb_svc_pwrmon_rail_count_get(struct gb_svc *svc, u8 *value) +{ + struct gb_svc_pwrmon_rail_count_get_response response; + int ret; + + ret = gb_operation_sync(svc->connection, + GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET, NULL, 0, + &response, sizeof(response)); + if (ret) { + dev_err(&svc->dev, "failed to get rail count (%d)\n", ret); + return ret; + } + + *value = response.rail_count; + + return 0; +} + +static int gb_svc_pwrmon_rail_names_get(struct gb_svc *svc, + struct gb_svc_pwrmon_rail_names_get_response *response, + size_t bufsize) +{ + int ret; + + ret = gb_operation_sync(svc->connection, + GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET, NULL, 0, + response, bufsize); + if (ret) { + dev_err(&svc->dev, "failed to get rail names (%d)\n", ret); + return ret; + } + + return 0; +} + +static int gb_svc_pwrmon_sample_get(struct gb_svc *svc, u8 rail_id, + u8 measurement_type, u32 *value) +{ + struct gb_svc_pwrmon_sample_get_request request; + struct gb_svc_pwrmon_sample_get_response response; + int ret; + + request.rail_id = rail_id; + request.measurement_type = measurement_type; + + ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_PWRMON_SAMPLE_GET, + &request, sizeof(request), + &response, sizeof(response)); + if (ret) { + dev_err(&svc->dev, "failed to get rail sample (%d)\n", ret); + return ret; + } + + if (response.result) { + dev_err(&svc->dev, + "UniPro error while getting rail power sample (%d %d): %d\n", + rail_id, measurement_type, response.result); + switch (response.result) { + case GB_SVC_PWRMON_GET_SAMPLE_INVAL: + return -EINVAL; + case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: + return -ENOSYS; + default: + return -EIO; + } + } + + *value = le32_to_cpu(response.measurement); + + return 0; +} + int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, u8 measurement_type, u32 *value) { @@ -393,6 +466,161 @@ static int gb_svc_version_request(struct gb_operation *op) return 0; } +static ssize_t pwr_debugfs_voltage_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + struct svc_debugfs_pwrmon_rail *pwrmon_rails = file->f_inode->i_private; + struct gb_svc *svc = pwrmon_rails->svc; + int ret, desc; + u32 value; + char buff[16]; + + ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, + GB_SVC_PWRMON_TYPE_VOL, &value); + if (ret) { + dev_err(&svc->dev, + "failed to get voltage sample ret=%d id=%d\n", + ret, pwrmon_rails->id); + return ret; + } + + desc = scnprintf(buff, sizeof(buff), "%u\n", value); + + return simple_read_from_buffer(buf, len, offset, buff, desc); +} + +static ssize_t pwr_debugfs_current_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + struct svc_debugfs_pwrmon_rail *pwrmon_rails = file->f_inode->i_private; + struct gb_svc *svc = pwrmon_rails->svc; + int ret, desc; + u32 value; + char buff[16]; + + ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, + GB_SVC_PWRMON_TYPE_CURR, &value); + if (ret) { + dev_err(&svc->dev, + "failed to get current sample ret=%d id=%d\n", + ret, pwrmon_rails->id); + return ret; + } + + desc = scnprintf(buff, sizeof(buff), "%u\n", value); + + return simple_read_from_buffer(buf, len, offset, buff, desc); +} + +static ssize_t pwr_debugfs_power_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + struct svc_debugfs_pwrmon_rail *pwrmon_rails = file->f_inode->i_private; + struct gb_svc *svc = pwrmon_rails->svc; + int ret, desc; + u32 value; + char buff[16]; + + ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, + GB_SVC_PWRMON_TYPE_PWR, &value); + if (ret) { + dev_err(&svc->dev, "failed to get power sample ret=%d id=%d\n", + ret, pwrmon_rails->id); + return ret; + } + + desc = scnprintf(buff, sizeof(buff), "%u\n", value); + + return simple_read_from_buffer(buf, len, offset, buff, desc); +} + +static const struct file_operations pwrmon_debugfs_voltage_fops = { + .read = pwr_debugfs_voltage_read, +}; + +static const struct file_operations pwrmon_debugfs_current_fops = { + .read = pwr_debugfs_current_read, +}; + +static const struct file_operations pwrmon_debugfs_power_fops = { + .read = pwr_debugfs_power_read, +}; + +static void svc_pwrmon_debugfs_init(struct gb_svc *svc) +{ + int i; + size_t bufsize; + struct dentry *dent; + + dent = debugfs_create_dir("pwrmon", svc->debugfs_dentry); + if (IS_ERR_OR_NULL(dent)) + return; + + if (gb_svc_pwrmon_rail_count_get(svc, &svc->rail_count)) + goto err_pwrmon_debugfs; + + if (!svc->rail_count || svc->rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT) + goto err_pwrmon_debugfs; + + bufsize = GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * svc->rail_count; + + svc->rail_names = kzalloc(bufsize, GFP_KERNEL); + if (!svc->rail_names) + goto err_pwrmon_debugfs; + + svc->pwrmon_rails = kcalloc(svc->rail_count, sizeof(*svc->pwrmon_rails), + GFP_KERNEL); + if (!svc->pwrmon_rails) + goto err_pwrmon_debugfs_free; + + if (gb_svc_pwrmon_rail_names_get(svc, svc->rail_names, bufsize)) + goto err_pwrmon_debugfs_free; + + for (i = 0; i < svc->rail_count; i++) { + struct dentry *dir; + struct svc_debugfs_pwrmon_rail *rail = &svc->pwrmon_rails[i]; + char fname[GB_SVC_PWRMON_RAIL_NAME_BUFSIZE]; + + snprintf(fname, sizeof(fname), "%s", + (char *)&svc->rail_names->name[i]); + + rail->id = i; + rail->svc = svc; + + dir = debugfs_create_dir(fname, dent); + debugfs_create_file("voltage_now", S_IRUGO, dir, rail, + &pwrmon_debugfs_voltage_fops); + debugfs_create_file("current_now", S_IRUGO, dir, rail, + &pwrmon_debugfs_current_fops); + debugfs_create_file("power_now", S_IRUGO, dir, rail, + &pwrmon_debugfs_power_fops); + }; + return; + +err_pwrmon_debugfs_free: + kfree(svc->rail_names); + svc->rail_names = NULL; + + kfree(svc->pwrmon_rails); + svc->pwrmon_rails = NULL; + +err_pwrmon_debugfs: + debugfs_remove(dent); +} + +static void svc_debugfs_init(struct gb_svc *svc) +{ + svc->debugfs_dentry = debugfs_create_dir(dev_name(&svc->dev), + gb_debugfs_get()); + svc_pwrmon_debugfs_init(svc); +} + +static void svc_debugfs_exit(struct gb_svc *svc) +{ + debugfs_remove_recursive(svc->debugfs_dentry); + kfree(svc->rail_names); +} + static int gb_svc_hello(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -432,6 +660,8 @@ static int gb_svc_hello(struct gb_operation *op) return ret; } + svc_debugfs_init(svc); + return 0; } @@ -882,6 +1112,7 @@ void gb_svc_del(struct gb_svc *svc) * from the request handler. */ if (device_is_registered(&svc->dev)) { + svc_debugfs_exit(svc); gb_svc_watchdog_destroy(svc); input_unregister_device(svc->input); device_del(&svc->dev); diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 08f8e3705b43..72bc716bf2fe 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -22,6 +22,11 @@ enum gb_svc_state { struct gb_svc_watchdog; +struct svc_debugfs_pwrmon_rail { + u8 id; + struct gb_svc *svc; +}; + struct gb_svc { struct device dev; @@ -40,6 +45,11 @@ struct gb_svc { struct input_dev *input; char *input_phys; struct gb_svc_watchdog *watchdog; + + struct dentry *debugfs_dentry; + struct svc_debugfs_pwrmon_rail *pwrmon_rails; + struct gb_svc_pwrmon_rail_names_get_response *rail_names; + u8 rail_count; }; #define to_gb_svc(d) container_of(d, struct gb_svc, dev) -- cgit v1.2.3-59-g8ed1b From 698bdcbf87f3c8a2f998058d58f4baa8ab8e192a Mon Sep 17 00:00:00 2001 From: Evgeniy Borisov <borisov_evgeniy@projectara.com> Date: Mon, 18 Apr 2016 16:27:36 +0300 Subject: greybus: camera-gb: Remove hardcode for CSI TX number of lanes The number of CSI TX lanes is hardcoded to 4. Removing this and start using value from configure stream response. NOTE: The patch depends on the CSI init change: "Use GB CSI params to init camera sub-devs" Signed-off-by: Evgeniy Borisov <eborisov@mm-sol.com> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index d3ee5b7240db..956fbf05b8e0 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -304,7 +304,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, if (nstreams) { csi_cfg.csi_id = 1; csi_cfg.flags = 0; - csi_cfg.num_lanes = 4; + csi_cfg.num_lanes = resp->num_lanes; csi_cfg.bus_freq = cpu_to_le32(960000000); csi_cfg.lines_per_second = resp->lines_per_second; ret = gb_hd_output(gcam->connection->hd, &csi_cfg, -- cgit v1.2.3-59-g8ed1b From de86e251f87fbc22efc935911f570c97f9b95cac Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 21 Apr 2016 08:13:01 +0530 Subject: greybus: bootrom: fix typo s/Firware/Firmware Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index 3cbe9feff53d..cf750beb3403 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -101,7 +101,7 @@ static int download_firmware(struct gb_bootrom *bootrom, u8 stage) &connection->bundle->dev); if (rc) dev_err(&connection->bundle->dev, - "Firware request for %s has failed : %d", + "Firmware request for %s has failed : %d", firmware_name, rc); return rc; } -- cgit v1.2.3-59-g8ed1b From 36460e8a895d1f7209c5a0d7c6a87b2ecc68cc4c Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 21 Apr 2016 08:11:57 +0530 Subject: greybus: audio-codec: Staticize few routines to fix build warnings This fixes below warnings .. greybus/audio_codec.c:20:32: warning: symbol 'find_data' was not declared. Should it be static? greybus/audio_codec.c:955:6: warning: symbol 'gbaudio_codec_cleanup' was not declared. Should it be static? Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 2c6142d9817a..ba3bd36f0c0a 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -17,8 +17,8 @@ static struct gbaudio_codec_info *gbcodec; -struct gbaudio_data_connection *find_data(struct gbaudio_module_info *module, - const char *name) +static struct gbaudio_data_connection * +find_data(struct gbaudio_module_info *module, const char *name) { struct gbaudio_data_connection *data; @@ -952,7 +952,7 @@ int gbaudio_register_module(struct gbaudio_module_info *module) } EXPORT_SYMBOL(gbaudio_register_module); -void gbaudio_codec_cleanup(struct gbaudio_module_info *module) +static void gbaudio_codec_cleanup(struct gbaudio_module_info *module) { struct gbaudio_data_connection *data; int pb_state = gbcodec->stream[0].state; -- cgit v1.2.3-59-g8ed1b From a0f997bd5db42eb21d42f04d10cbe03f3fc4fa91 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 21 Apr 2016 11:31:13 +0530 Subject: greybus: es2: Allow proper release of greybus host device The usb core disables the Interface prior to calling ->disconnect() for the usb driver. That disallows the es2 driver to remove the greybus host device and every entity below it in a clean way, as the greybus core may want to do few operations over the usb connection before getting removed. And so we see bunch of errors while we remove the es2 module, like: "urb cport in error -108 (dropped)" The usb core has a special per-driver flag, 'soft_unbind', for such usb drivers. If this flag is set by a driver, the usb core doesn't disable the Interface prior to calling ->disconnect(). Set that flag for es2. Tested by removing the gb-es2.ko module on both EVT1.5 and qemu with gbsim. The interface isn't disabled by the core and the driver is still able initiate greybus operations over the Interface. This can be properly tested only after the next patch which removes the greybus host device before disabling the urbs. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 9e06efb77243..0cc9fe660d12 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1083,6 +1083,7 @@ static struct usb_driver es2_ap_driver = { .probe = ap_probe, .disconnect = ap_disconnect, .id_table = id_table, + .soft_unbind = 1, }; module_usb_driver(es2_ap_driver); -- cgit v1.2.3-59-g8ed1b From ecdd3a2d59321ebd46c595dbadf24f50364a052a Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 21 Apr 2016 11:31:14 +0530 Subject: greybus: es2: disable cport-in after remove greybus host device This allows greybus to do operations before being removed. Tested by removing the gb-es2.ko module on both EVT1.5 and qemu with gbsim. The driver removes the greybus host device successfully before disabling the urbs. The errors are still coming ("urb cport in error -2 (dropped)"), but with an error value -2 (ENOENT) instead of -108. And that happens while the urbs are disabled, not while doing greybus operations. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 0cc9fe660d12..8138bc22efbc 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -695,11 +695,11 @@ static void ap_disconnect(struct usb_interface *interface) struct es2_ap_dev *es2 = usb_get_intfdata(interface); int i; + gb_hd_del(es2->hd); + for (i = 0; i < NUM_BULKS; ++i) es2_cport_in_disable(es2, &es2->cport_in[i]); - gb_hd_del(es2->hd); - es2_destroy(es2); } -- cgit v1.2.3-59-g8ed1b From 62de6e0a7262edebc4debd99585b19dc10d2741d Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 21 Apr 2016 11:31:15 +0530 Subject: greybus: es2: don't protest when getting -ENOENT/ESHUTDOWN USB errors -ENOENT or -ESHUTDOWN happens when the urbs are being killed from ->disconnect() callback. Don't complain to userspace about this, as the user will see this on es2 module removal. Tested by removing the gb-es2.ko module on both EVT1.5 and qemu with gbsim. The driver doesn't throw anymore errors like: "urb cport in error -2 (dropped)". Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 8138bc22efbc..0a29708cdb74 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -715,6 +715,11 @@ static void cport_in_callback(struct urb *urb) if (status) { if ((status == -EAGAIN) || (status == -EPROTO)) goto exit; + + /* The urb is being unlinked */ + if (status == -ENOENT || status == -ESHUTDOWN) + return; + dev_err(dev, "urb cport in error %d (dropped)\n", status); return; } -- cgit v1.2.3-59-g8ed1b From 2437f1c6b582d1d676c08b6069859c11266c69da Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 21 Apr 2016 11:31:16 +0530 Subject: greybus: es2: move ap_disconnect() below ap_probe() This makes it more readable, as the functions are present in this order in the structure as well. Also keeping these two makes more sense. Tested by removing the gb-es2.ko module on both EVT1.5 and qemu with gbsim. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 0a29708cdb74..1cef13d3fb5d 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -690,19 +690,6 @@ static void es2_destroy(struct es2_ap_dev *es2) usb_put_dev(udev); } -static void ap_disconnect(struct usb_interface *interface) -{ - struct es2_ap_dev *es2 = usb_get_intfdata(interface); - int i; - - gb_hd_del(es2->hd); - - for (i = 0; i < NUM_BULKS; ++i) - es2_cport_in_disable(es2, &es2->cport_in[i]); - - es2_destroy(es2); -} - static void cport_in_callback(struct urb *urb) { struct gb_host_device *hd = urb->context; @@ -1083,6 +1070,19 @@ error: return retval; } +static void ap_disconnect(struct usb_interface *interface) +{ + struct es2_ap_dev *es2 = usb_get_intfdata(interface); + int i; + + gb_hd_del(es2->hd); + + for (i = 0; i < NUM_BULKS; ++i) + es2_cport_in_disable(es2, &es2->cport_in[i]); + + es2_destroy(es2); +} + static struct usb_driver es2_ap_driver = { .name = "es2_ap_driver", .probe = ap_probe, -- cgit v1.2.3-59-g8ed1b From 89f2df438b20f2176fcc8fc92201a708f0d3659b Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 21 Apr 2016 11:43:36 +0200 Subject: greybus: svc: keep error messages uniform All SVC error messages, except for a few recently added ones, place the errno last after a colon (:). Let's at least try to be consistent within the svc code. Note that this format also allows for more concise messages without risk for ambiguity. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 1791dcce5217..ff89da6172c2 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -109,7 +109,7 @@ static int gb_svc_pwrmon_rail_count_get(struct gb_svc *svc, u8 *value) GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET, NULL, 0, &response, sizeof(response)); if (ret) { - dev_err(&svc->dev, "failed to get rail count (%d)\n", ret); + dev_err(&svc->dev, "failed to get rail count: %d\n", ret); return ret; } @@ -128,7 +128,7 @@ static int gb_svc_pwrmon_rail_names_get(struct gb_svc *svc, GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET, NULL, 0, response, bufsize); if (ret) { - dev_err(&svc->dev, "failed to get rail names (%d)\n", ret); + dev_err(&svc->dev, "failed to get rail names: %d\n", ret); return ret; } @@ -149,7 +149,7 @@ static int gb_svc_pwrmon_sample_get(struct gb_svc *svc, u8 rail_id, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&svc->dev, "failed to get rail sample (%d)\n", ret); + dev_err(&svc->dev, "failed to get rail sample: %d\n", ret); return ret; } @@ -187,7 +187,7 @@ int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&svc->dev, "failed to get intf sample (%d)\n", ret); + dev_err(&svc->dev, "failed to get intf sample: %d\n", ret); return ret; } @@ -479,8 +479,8 @@ static ssize_t pwr_debugfs_voltage_read(struct file *file, char __user *buf, GB_SVC_PWRMON_TYPE_VOL, &value); if (ret) { dev_err(&svc->dev, - "failed to get voltage sample ret=%d id=%d\n", - ret, pwrmon_rails->id); + "failed to get voltage sample %u: %d\n", + pwrmon_rails->id, ret); return ret; } @@ -502,8 +502,8 @@ static ssize_t pwr_debugfs_current_read(struct file *file, char __user *buf, GB_SVC_PWRMON_TYPE_CURR, &value); if (ret) { dev_err(&svc->dev, - "failed to get current sample ret=%d id=%d\n", - ret, pwrmon_rails->id); + "failed to get current sample %u: %d\n", + pwrmon_rails->id, ret); return ret; } @@ -524,8 +524,8 @@ static ssize_t pwr_debugfs_power_read(struct file *file, char __user *buf, ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, GB_SVC_PWRMON_TYPE_PWR, &value); if (ret) { - dev_err(&svc->dev, "failed to get power sample ret=%d id=%d\n", - ret, pwrmon_rails->id); + dev_err(&svc->dev, "failed to get power sample %u: %d\n", + pwrmon_rails->id, ret); return ret; } -- cgit v1.2.3-59-g8ed1b From 5b35ef95ef9b71406db3fe860b8ce8206b6576c7 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 21 Apr 2016 11:43:37 +0200 Subject: greybus: svc: fix pwrmon return value Errno -ENOSYS is reserved for missing syscalls, replace it with ENOMSG. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index ff89da6172c2..1cf608987c3b 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -161,7 +161,7 @@ static int gb_svc_pwrmon_sample_get(struct gb_svc *svc, u8 rail_id, case GB_SVC_PWRMON_GET_SAMPLE_INVAL: return -EINVAL; case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: - return -ENOSYS; + return -ENOMSG; default: return -EIO; } -- cgit v1.2.3-59-g8ed1b From f35fdb2fbdc9435b723775b6aa561590413131c6 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 21 Apr 2016 11:43:38 +0200 Subject: greybus: svc: fix function-parameter indentation We really shouldn't be passing response structures around this way, but since we now are, let's at least make sure not to break the 80 col limit. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 1cf608987c3b..d285361cc521 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -119,8 +119,8 @@ static int gb_svc_pwrmon_rail_count_get(struct gb_svc *svc, u8 *value) } static int gb_svc_pwrmon_rail_names_get(struct gb_svc *svc, - struct gb_svc_pwrmon_rail_names_get_response *response, - size_t bufsize) + struct gb_svc_pwrmon_rail_names_get_response *response, + size_t bufsize) { int ret; -- cgit v1.2.3-59-g8ed1b From f2bf63a365425fa7df56c4e1dbe5a6bb29680324 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Fri, 22 Apr 2016 11:13:19 -0700 Subject: greybus: audio: acquire wakelock during active playback use pm_stay_awake & pm_relax to avoid suspend sequence during active playback testing Done: Music Playback ongoing $ cat /sys/devices/soc.0/qcom,ara-codec.82/power/wakeup_active 1 Music Playback stopped $ cat /sys/devices/soc.0/qcom,ara-codec.82/power/wakeup_active 0 Tested-by: David Lin <dtwlin@google.com> Reviewed-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Akash Choudhari <akashtc@google.com> Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> --- drivers/staging/greybus/audio_codec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index ba3bd36f0c0a..9ed35c4da280 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -7,6 +7,7 @@ */ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #include <sound/soc.h> #include <sound/pcm_params.h> #include <uapi/linux/input.h> @@ -388,6 +389,8 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, codec->stream[substream->stream].state = state; codec->stream[substream->stream].dai_name = dai->name; mutex_unlock(&codec->lock); + /* to prevent suspend in case of active audio */ + pm_stay_awake(dai->dev); return ret; } @@ -470,6 +473,7 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, codec->stream[substream->stream].state = state; codec->stream[substream->stream].dai_name = NULL; mutex_unlock(&codec->lock); + pm_relax(dai->dev); return; } @@ -1094,7 +1098,7 @@ static int gbcodec_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, info); gbcodec = info; - /* Empty function for now */ + device_init_wakeup(codec->dev, 1); return 0; } -- cgit v1.2.3-59-g8ed1b From 7b62b61c752a4700ecf11d63a7ec40aeb3cee66c Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 20 Apr 2016 11:48:37 +0530 Subject: greybus: arche-ctrl: Don't expose driver internals to arche-platform driver We have chosen the *ugly* way of registering two platform drivers from the module_init() of only one of them, so that we can avoid having two separate modules for them. But we should still be doing this in a sane way. There is no need to expose internals of arche-ctrl to arche-platform, like PM-ops, probe, resume, id-table, etc. Just expose an init and a exit callback. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-apb-ctrl.c | 34 +++++++++++++++++++++++++++----- drivers/staging/greybus/arche-platform.c | 19 ++---------------- drivers/staging/greybus/arche_platform.h | 7 ++----- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 3a092a5b7fb8..a9d78a85939a 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -358,7 +358,7 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, return 0; } -int arche_apb_ctrl_probe(struct platform_device *pdev) +static int arche_apb_ctrl_probe(struct platform_device *pdev) { int ret; struct arche_apb_ctrl_drvdata *apb; @@ -393,7 +393,7 @@ int arche_apb_ctrl_probe(struct platform_device *pdev) return 0; } -int arche_apb_ctrl_remove(struct platform_device *pdev) +static int arche_apb_ctrl_remove(struct platform_device *pdev) { device_remove_file(&pdev->dev, &dev_attr_state); poweroff_seq(pdev); @@ -430,6 +430,30 @@ static int arche_apb_ctrl_resume(struct device *dev) return 0; } -SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, - arche_apb_ctrl_suspend, - arche_apb_ctrl_resume); +static SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, arche_apb_ctrl_suspend, + arche_apb_ctrl_resume); + +static struct of_device_id arche_apb_ctrl_of_match[] = { + { .compatible = "usbffff,2", }, + { }, +}; + +static struct platform_driver arche_apb_ctrl_device_driver = { + .probe = arche_apb_ctrl_probe, + .remove = arche_apb_ctrl_remove, + .driver = { + .name = "arche-apb-ctrl", + .pm = &arche_apb_ctrl_pm_ops, + .of_match_table = arche_apb_ctrl_of_match, + } +}; + +int __init arche_apb_init(void) +{ + return platform_driver_register(&arche_apb_ctrl_device_driver); +} + +void __exit arche_apb_exit(void) +{ + platform_driver_unregister(&arche_apb_ctrl_device_driver); +} diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 3ff4f69685bd..d1083cfbc081 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -580,11 +580,6 @@ static struct of_device_id arche_platform_of_match[] = { { }, }; -static struct of_device_id arche_apb_ctrl_of_match[] = { - { .compatible = "usbffff,2", }, - { }, -}; - static struct of_device_id arche_combined_id[] = { { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ { .compatible = "usbffff,2", }, @@ -602,16 +597,6 @@ static struct platform_driver arche_platform_device_driver = { } }; -static struct platform_driver arche_apb_ctrl_device_driver = { - .probe = arche_apb_ctrl_probe, - .remove = arche_apb_ctrl_remove, - .driver = { - .name = "arche-apb-ctrl", - .pm = &arche_apb_ctrl_pm_ops, - .of_match_table = arche_apb_ctrl_of_match, - } -}; - static int __init arche_init(void) { int retval; @@ -620,7 +605,7 @@ static int __init arche_init(void) if (retval) return retval; - retval = platform_driver_register(&arche_apb_ctrl_device_driver); + retval = arche_apb_init(); if (retval) platform_driver_unregister(&arche_platform_device_driver); @@ -630,7 +615,7 @@ module_init(arche_init); static void __exit arche_exit(void) { - platform_driver_unregister(&arche_apb_ctrl_device_driver); + arche_apb_exit(); platform_driver_unregister(&arche_platform_device_driver); } module_exit(arche_exit); diff --git a/drivers/staging/greybus/arche_platform.h b/drivers/staging/greybus/arche_platform.h index 700c548d68db..69b627b978b8 100644 --- a/drivers/staging/greybus/arche_platform.h +++ b/drivers/staging/greybus/arche_platform.h @@ -18,8 +18,8 @@ enum arche_platform_state { }; -int arche_apb_ctrl_probe(struct platform_device *pdev); -int arche_apb_ctrl_remove(struct platform_device *pdev); +int __init arche_apb_init(void); +void __exit arche_apb_exit(void); /* Operational states for the APB device */ int apb_ctrl_coldboot(struct device *dev); @@ -27,7 +27,4 @@ int apb_ctrl_fw_flashing(struct device *dev); int apb_ctrl_standby_boot(struct device *dev); void apb_ctrl_poweroff(struct device *dev); - -extern const struct dev_pm_ops arche_apb_ctrl_pm_ops; - #endif /* __ARCHE_PLATFORM_H */ -- cgit v1.2.3-59-g8ed1b From d7ed7cbfe695d0a77613b305bed0483a1c9569ca Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Mon, 25 Apr 2016 17:18:17 +0530 Subject: greybus: audio: acquire wakelock during active playback This change was missed while merging original patch commit-id: 53c765c33f4a69c31027ec012e717d303bd4feca Thus submitting it again. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 9ed35c4da280..20feea1c64d3 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -410,6 +410,7 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, if (list_empty(&codec->module_list)) { dev_err(codec->dev, "No codec module available\n"); mutex_unlock(&codec->lock); + pm_relax(dai->dev); return; } -- cgit v1.2.3-59-g8ed1b From 12185197eac3355c7c7c7bce0e81b0c65baa3010 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:20 +0200 Subject: greybus: svc: use a common prefix for debugfs functions Use the common gb_svc functions also for the recently added svc functions. Having a common prefix clearly signals where the code resides, something which improves readability and helps during debugging (e.g. stack traces). Note that all functions in svc.c except for these three use the common prefix with the exception of the pwr_debugfs callbacks (that still use *a* common prefix) and the attribute accessors (than can not have a common prefix due to some macro magic). Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index d285361cc521..f9829f1f6741 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -546,7 +546,7 @@ static const struct file_operations pwrmon_debugfs_power_fops = { .read = pwr_debugfs_power_read, }; -static void svc_pwrmon_debugfs_init(struct gb_svc *svc) +static void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) { int i; size_t bufsize; @@ -608,14 +608,14 @@ err_pwrmon_debugfs: debugfs_remove(dent); } -static void svc_debugfs_init(struct gb_svc *svc) +static void gb_svc_debugfs_init(struct gb_svc *svc) { svc->debugfs_dentry = debugfs_create_dir(dev_name(&svc->dev), gb_debugfs_get()); - svc_pwrmon_debugfs_init(svc); + gb_svc_pwrmon_debugfs_init(svc); } -static void svc_debugfs_exit(struct gb_svc *svc) +static void gb_svc_debugfs_exit(struct gb_svc *svc) { debugfs_remove_recursive(svc->debugfs_dentry); kfree(svc->rail_names); @@ -660,7 +660,7 @@ static int gb_svc_hello(struct gb_operation *op) return ret; } - svc_debugfs_init(svc); + gb_svc_debugfs_init(svc); return 0; } @@ -1112,7 +1112,7 @@ void gb_svc_del(struct gb_svc *svc) * from the request handler. */ if (device_is_registered(&svc->dev)) { - svc_debugfs_exit(svc); + gb_svc_debugfs_exit(svc); gb_svc_watchdog_destroy(svc); input_unregister_device(svc->input); device_del(&svc->dev); -- cgit v1.2.3-59-g8ed1b From 4ff80a59ac3bbdacaf93598eed396677f29c4673 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:21 +0200 Subject: greybus: Documentation/sysfs: add entry for control device Add an entry for the recently added interface control device. Also move the bundle-device entry below the control-device entries. Reported-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 4831efb9d0f7..89b15b0428f7 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -88,13 +88,13 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Power measurement of the interface in microwatts (uW) -What: /sys/bus/greybus/device/N-I.B +What: /sys/bus/greybus/device/N-I.ctrl Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: - A bundle B on the Interface I, B is replaced by a 1-byte - number representing the bundle. + Abstract control device for interface I that represents the + current mode of an enumerated Greybus interface. What: /sys/bus/greybus/device/N-I.ctrl/product_string Date: October 2015 @@ -110,6 +110,14 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Vendor ID string of a Greybus interface. +What: /sys/bus/greybus/device/N-I.B +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + A bundle B on the Interface I, B is replaced by a 1-byte + number representing the bundle. + What: /sys/bus/greybus/device/N-I.B/bundle_class Date: October 2015 KernelVersion: 4.XX -- cgit v1.2.3-59-g8ed1b From deba0c03e9c12e2053ad5241c2fd59276439f2cd Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:22 +0200 Subject: greybus: Documentation/sysfs: sort entries alphabetically The attribute entries have been kept mostly sorted within each device type. Let's move the three more-recently added interface attributes that were not. Suggested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 89b15b0428f7..cf6d3eec793a 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -14,6 +14,13 @@ Description: An Interface I on the bus N, where I is the 1-byte interface ID. +What: /sys/bus/greybus/device/N-I/current_now +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Current measurement of the interface in microamps (uA) + What: /sys/bus/greybus/device/N-I/ddbl1_manufacturer_id Date: October 2015 KernelVersion: 4.XX @@ -37,13 +44,12 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The ID of a Greybus interface. -What: /sys/bus/greybus/device/N-I/serial_number -Date: October 2015 +What: /sys/bus/greybus/device/N-I/power_now +Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: - Serial Number of the Greybus interface, represented by a 64 bit - hexadecimal number. + Power measurement of the interface in microwatts (uW) What: /sys/bus/greybus/device/N-I/product_id Date: October 2015 @@ -52,6 +58,14 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Product ID of a Greybus interface. +What: /sys/bus/greybus/device/N-I/serial_number +Date: October 2015 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Serial Number of the Greybus interface, represented by a 64 bit + hexadecimal number. + What: /sys/bus/greybus/device/N-I/vendor_id Date: October 2015 KernelVersion: 4.XX @@ -74,20 +88,6 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Voltage measurement of the interface in microvolts (uV) -What: /sys/bus/greybus/device/N-I/current_now -Date: March 2016 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman <greg@kroah.com> -Description: - Current measurement of the interface in microamps (uA) - -What: /sys/bus/greybus/device/N-I/power_now -Date: March 2016 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman <greg@kroah.com> -Description: - Power measurement of the interface in microwatts (uW) - What: /sys/bus/greybus/device/N-I.ctrl Date: October 2015 KernelVersion: 4.XX -- cgit v1.2.3-59-g8ed1b From 844fcbfeb6491d95b7e19b4705f9eb576a210536 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:23 +0200 Subject: greybus: svc: refactor interface re-enable Add interface re-enable helper that is used during mode switch to disable and re-enable (enumerate) an interface. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index f9829f1f6741..cde59d02400b 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -665,6 +665,24 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } +static void gb_svc_intf_reenable(struct gb_svc *svc, struct gb_interface *intf) +{ + int ret; + + /* Mark as disconnected to prevent I/O during disable. */ + intf->disconnected = true; + gb_interface_disable(intf); + intf->disconnected = false; + + ret = gb_interface_enable(intf); + if (ret) { + dev_err(&svc->dev, "failed to enable interface %u: %d\n", + intf->interface_id, ret); + + gb_interface_deactivate(intf); + } +} + static void gb_svc_process_intf_hotplug(struct gb_operation *operation) { struct gb_svc_intf_hotplug_request *request; @@ -686,12 +704,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) dev_info(&svc->dev, "mode switch detected on interface %u\n", intf_id); - /* Mark as disconnected to prevent I/O during disable. */ - intf->disconnected = true; - gb_interface_disable(intf); - intf->disconnected = false; - - goto enable_interface; + return gb_svc_intf_reenable(svc, intf); } intf = gb_interface_create(hd, intf_id); @@ -713,7 +726,6 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) if (ret) goto err_interface_deactivate; -enable_interface: ret = gb_interface_enable(intf); if (ret) { dev_err(&svc->dev, "failed to enable interface %u: %d\n", -- cgit v1.2.3-59-g8ed1b From b15d97d77017dc168c74c5e9aacfe14ff74dcbe9 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:24 +0200 Subject: greybus: core: add module abstraction Add Greybus module abstraction that will be used to implement controlled module removal (eject) and represent module geometry. Greybus module devices correspond to physical modules and have one or more interfaces. Modules have an id that is identical to the id of their primary interface, which in turn is the interface with lowest numbered id. The module name is constructed from the bus and module id: <bus_id>-<module_id> Interfaces, bundles, and control devices are consequently renamed as <bus_id>-<module_id>.<interface_id> <bus_id>-<module_id>.<interface_id>.<bundle_id> <bus_id>-<module_id>.<interface_id>.ctrl As before, interface ids (and therefore in a sense now also module ids) correspond to physical interface positions on the frame. Modules have the following attributes: module_id num_interfaces where module_id is the id of the module and num_interface the number of interfaces the module has. Note that until SVC module-size detection has been implemented, all interfaces are considered to be part of 1x2 modules. Specifically, the two interfaces of a 2x2 module will be presented as two 1x2 modules for now. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 63 +++++--- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/core.c | 11 ++ drivers/staging/greybus/greybus.h | 7 + drivers/staging/greybus/hd.c | 2 +- drivers/staging/greybus/hd.h | 2 +- drivers/staging/greybus/interface.c | 40 ++--- drivers/staging/greybus/interface.h | 8 +- drivers/staging/greybus/module.c | 178 +++++++++++++++++++++ drivers/staging/greybus/module.h | 34 ++++ drivers/staging/greybus/svc.c | 83 +++++----- 11 files changed, 333 insertions(+), 96 deletions(-) create mode 100644 drivers/staging/greybus/module.c create mode 100644 drivers/staging/greybus/module.h diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index cf6d3eec793a..41ffc40b8efb 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -6,22 +6,45 @@ Description: The "root" greybus device for the Greybus device tree, or bus, where N is a dynamically assigned 1-based id. -What: /sys/bus/greybus/device/N-I +What: /sys/bus/greybus/device/N-M +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + A Module M on the bus N, where M is the 1-byte interface + ID of the module's primary interface. + +What: /sys/bus/greybus/device/N-M/module_id +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + The ID of a Greybus module, corresponding to the ID of its + primary interface. + +What: /sys/bus/greybus/device/N-M/num_interfaces +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + The number of interfaces of a module. + +What: /sys/bus/greybus/device/N-M.I Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: - An Interface I on the bus N, where I is the 1-byte interface - ID. + An Interface I on the bus N and module N-M, where I is the + 1-byte interface ID. -What: /sys/bus/greybus/device/N-I/current_now +What: /sys/bus/greybus/device/N-M.I/current_now Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Current measurement of the interface in microamps (uA) -What: /sys/bus/greybus/device/N-I/ddbl1_manufacturer_id +What: /sys/bus/greybus/device/N-M.I/ddbl1_manufacturer_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -29,7 +52,7 @@ Description: Unipro Device Descriptor Block Level 1 manufacturer ID for the greybus Interface. -What: /sys/bus/greybus/device/N-I/ddbl1_product_id +What: /sys/bus/greybus/device/N-M.I/ddbl1_product_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -37,28 +60,28 @@ Description: Unipro Device Descriptor Block Level 1 product ID for the greybus Interface. -What: /sys/bus/greybus/device/N-I/interface_id +What: /sys/bus/greybus/device/N-M.I/interface_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The ID of a Greybus interface. -What: /sys/bus/greybus/device/N-I/power_now +What: /sys/bus/greybus/device/N-M.I/power_now Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Power measurement of the interface in microwatts (uW) -What: /sys/bus/greybus/device/N-I/product_id +What: /sys/bus/greybus/device/N-M.I/product_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Product ID of a Greybus interface. -What: /sys/bus/greybus/device/N-I/serial_number +What: /sys/bus/greybus/device/N-M.I/serial_number Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -66,14 +89,14 @@ Description: Serial Number of the Greybus interface, represented by a 64 bit hexadecimal number. -What: /sys/bus/greybus/device/N-I/vendor_id +What: /sys/bus/greybus/device/N-M.I/vendor_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Vendor ID of a Greybus interface. -What: /sys/bus/greybus/device/N-I/version +What: /sys/bus/greybus/device/N-M.I/version Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -81,14 +104,14 @@ Description: Interface version represented as <16 bit major number>.<16 bit minor number>. -What: /sys/bus/greybus/device/N-I/voltage_now +What: /sys/bus/greybus/device/N-M.I/voltage_now Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Voltage measurement of the interface in microvolts (uV) -What: /sys/bus/greybus/device/N-I.ctrl +What: /sys/bus/greybus/device/N-M.I.ctrl Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -96,21 +119,21 @@ Description: Abstract control device for interface I that represents the current mode of an enumerated Greybus interface. -What: /sys/bus/greybus/device/N-I.ctrl/product_string +What: /sys/bus/greybus/device/N-M.I.ctrl/product_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Product ID string of a Greybus interface. -What: /sys/bus/greybus/device/N-I.ctrl/vendor_string +What: /sys/bus/greybus/device/N-M.I.ctrl/vendor_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Vendor ID string of a Greybus interface. -What: /sys/bus/greybus/device/N-I.B +What: /sys/bus/greybus/device/N-M.I.B Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -118,21 +141,21 @@ Description: A bundle B on the Interface I, B is replaced by a 1-byte number representing the bundle. -What: /sys/bus/greybus/device/N-I.B/bundle_class +What: /sys/bus/greybus/device/N-M.I.B/bundle_class Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The greybus class of the bundle B. -What: /sys/bus/greybus/device/N-I.B/bundle_id +What: /sys/bus/greybus/device/N-M.I.B/bundle_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The interface-unique id of the bundle B. -What: /sys/bus/greybus/device/N-I.B/state +What: /sys/bus/greybus/device/N-M.I.B/state Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 65259ea9d111..8ec607460ec0 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -2,6 +2,7 @@ greybus-y := core.o \ debugfs.o \ hd.o \ manifest.o \ + module.o \ interface.o \ bundle.o \ connection.o \ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9f143e5a7c9c..70a66e28471d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -85,6 +85,7 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { struct gb_host_device *hd; + struct gb_module *module = NULL; struct gb_interface *intf = NULL; struct gb_control *control = NULL; struct gb_bundle *bundle = NULL; @@ -92,8 +93,12 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) if (is_gb_host_device(dev)) { hd = to_gb_host_device(dev); + } else if (is_gb_module(dev)) { + module = to_gb_module(dev); + hd = module->hd; } else if (is_gb_interface(dev)) { intf = to_gb_interface(dev); + module = intf->module; hd = intf->hd; } else if (is_gb_control(dev)) { control = to_gb_control(dev); @@ -102,6 +107,7 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); intf = bundle->intf; + module = intf->module; hd = intf->hd; } else if (is_gb_svc(dev)) { svc = to_gb_svc(dev); @@ -114,6 +120,11 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "BUS=%u", hd->bus_id)) return -ENOMEM; + if (module) { + if (add_uevent_var(env, "MODULE=%u", module->module_id)) + return -ENOMEM; + } + if (intf) { if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) return -ENOMEM; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 4ab5f608b8cd..f5447e7592e9 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -28,6 +28,7 @@ #include "hd.h" #include "svc.h" #include "control.h" +#include "module.h" #include "interface.h" #include "bundle.h" #include "connection.h" @@ -112,6 +113,7 @@ struct dentry *gb_debugfs_get(void); extern struct bus_type greybus_bus_type; extern struct device_type greybus_hd_type; +extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_control_type; extern struct device_type greybus_bundle_type; @@ -122,6 +124,11 @@ static inline int is_gb_host_device(const struct device *dev) return dev->type == &greybus_hd_type; } +static inline int is_gb_module(const struct device *dev) +{ + return dev->type == &greybus_module_type; +} + static inline int is_gb_interface(const struct device *dev) { return dev->type == &greybus_interface_type; diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 2c13860e4a05..762cc035b128 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -89,7 +89,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, hd->bus_id = ret; hd->driver = driver; - INIT_LIST_HEAD(&hd->interfaces); + INIT_LIST_HEAD(&hd->modules); INIT_LIST_HEAD(&hd->connections); ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index b481dd03bd73..ff71936a7932 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -34,7 +34,7 @@ struct gb_host_device { int bus_id; const struct gb_hd_driver *driver; - struct list_head interfaces; + struct list_head modules; struct list_head connections; struct ida cport_id_map; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 89fe901cb0a3..d51c5635f22b 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -333,21 +333,6 @@ static struct attribute *interface_attrs[] = { }; ATTRIBUTE_GROUPS(interface); - -// FIXME, odds are you don't want to call this function, rework the caller to -// not need it please. -struct gb_interface *gb_interface_find(struct gb_host_device *hd, - u8 interface_id) -{ - struct gb_interface *intf; - - list_for_each_entry(intf, &hd->interfaces, links) - if (intf->interface_id == interface_id) - return intf; - - return NULL; -} - static void gb_interface_release(struct device *dev) { struct gb_interface *intf = to_gb_interface(dev); @@ -371,13 +356,11 @@ struct device_type greybus_interface_type = { * * Returns a pointer to the new interfce or a null pointer if a * failure occurs due to memory exhaustion. - * - * Locking: Caller ensures serialisation with gb_interface_remove and - * gb_interface_find. */ -struct gb_interface *gb_interface_create(struct gb_host_device *hd, +struct gb_interface *gb_interface_create(struct gb_module *module, u8 interface_id) { + struct gb_host_device *hd = module->hd; struct gb_interface *intf; intf = kzalloc(sizeof(*intf), GFP_KERNEL); @@ -385,6 +368,7 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, return NULL; intf->hd = hd; /* XXX refcount? */ + intf->module = module; intf->interface_id = interface_id; INIT_LIST_HEAD(&intf->bundles); INIT_LIST_HEAD(&intf->manifest_descs); @@ -392,15 +376,14 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd, /* Invalid device id to start with */ intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; - intf->dev.parent = &hd->dev; + intf->dev.parent = &module->dev; intf->dev.bus = &greybus_bus_type; intf->dev.type = &greybus_interface_type; intf->dev.groups = interface_groups; - intf->dev.dma_mask = hd->dev.dma_mask; + intf->dev.dma_mask = module->dev.dma_mask; device_initialize(&intf->dev); - dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id); - - list_add(&intf->links, &hd->interfaces); + dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev), + interface_id); return intf; } @@ -579,15 +562,16 @@ int gb_interface_add(struct gb_interface *intf) return 0; } -/* Deregister an interface and drop its reference. */ -void gb_interface_remove(struct gb_interface *intf) +/* Deregister an interface. */ +void gb_interface_del(struct gb_interface *intf) { if (device_is_registered(&intf->dev)) { device_del(&intf->dev); dev_info(&intf->dev, "Interface removed\n"); } +} - list_del(&intf->links); - +void gb_interface_put(struct gb_interface *intf) +{ put_device(&intf->dev); } diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 63ba696c14a5..9185c7f1bd32 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -20,7 +20,7 @@ struct gb_interface { struct gb_control *control; struct list_head bundles; - struct list_head links; /* gb_host_device->interfaces */ + struct list_head module_node; struct list_head manifest_descs; u8 interface_id; /* Physical location within the Endo */ u8 device_id; @@ -35,6 +35,7 @@ struct gb_interface { u16 version_minor; struct gb_host_device *hd; + struct gb_module *module; unsigned long quirks; @@ -46,13 +47,14 @@ struct gb_interface { struct gb_interface *gb_interface_find(struct gb_host_device *hd, u8 interface_id); -struct gb_interface *gb_interface_create(struct gb_host_device *hd, +struct gb_interface *gb_interface_create(struct gb_module *module, u8 interface_id); int gb_interface_activate(struct gb_interface *intf); void gb_interface_deactivate(struct gb_interface *intf); int gb_interface_enable(struct gb_interface *intf); void gb_interface_disable(struct gb_interface *intf); int gb_interface_add(struct gb_interface *intf); -void gb_interface_remove(struct gb_interface *intf); +void gb_interface_del(struct gb_interface *intf); +void gb_interface_put(struct gb_interface *intf); #endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c new file mode 100644 index 000000000000..5c498e0a4eaf --- /dev/null +++ b/drivers/staging/greybus/module.c @@ -0,0 +1,178 @@ +/* + * Greybus Module code + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + + +static ssize_t module_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_module *module = to_gb_module(dev); + + return sprintf(buf, "%u\n", module->module_id); +} +static DEVICE_ATTR_RO(module_id); + +static ssize_t num_interfaces_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_module *module = to_gb_module(dev); + + return sprintf(buf, "%zu\n", module->num_interfaces); +} +static DEVICE_ATTR_RO(num_interfaces); + +static struct attribute *module_attrs[] = { + &dev_attr_module_id.attr, + &dev_attr_num_interfaces.attr, + NULL, +}; +ATTRIBUTE_GROUPS(module); + +static void gb_module_release(struct device *dev) +{ + struct gb_module *module = to_gb_module(dev); + + kfree(module); +} + +struct device_type greybus_module_type = { + .name = "greybus_module", + .release = gb_module_release, +}; + +struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id, + size_t num_interfaces) +{ + struct gb_interface *intf; + struct gb_module *module; + int i; + + module = kzalloc(sizeof(*module) + num_interfaces * sizeof(intf), + GFP_KERNEL); + if (!module) + return NULL; + + module->hd = hd; + module->module_id = module_id; + module->num_interfaces = num_interfaces; + + module->dev.parent = &hd->dev; + module->dev.bus = &greybus_bus_type; + module->dev.type = &greybus_module_type; + module->dev.groups = module_groups; + module->dev.dma_mask = hd->dev.dma_mask; + device_initialize(&module->dev); + dev_set_name(&module->dev, "%d-%u", hd->bus_id, module_id); + + for (i = 0; i < num_interfaces; ++i) { + intf = gb_interface_create(module, module_id + i); + if (!intf) { + dev_err(&module->dev, "failed to create interface %u\n", + module_id + i); + goto err_put_interfaces; + } + module->interfaces[i] = intf; + } + + return module; + +err_put_interfaces: + for (--i; i > 0; --i) + gb_interface_put(module->interfaces[i]); + + put_device(&module->dev); + + return NULL; +} + +/* + * Register and enable an interface after first attempting to activate it. + */ +static void gb_module_register_interface(struct gb_interface *intf) +{ + struct gb_module *module = intf->module; + u8 intf_id = intf->interface_id; + int ret; + + ret = gb_interface_activate(intf); + if (ret) { + dev_err(&module->dev, "failed to activate interface %u: %d\n", + intf_id, ret); + gb_interface_add(intf); + return; + } + + ret = gb_interface_add(intf); + if (ret) + goto err_interface_deactivate; + + ret = gb_interface_enable(intf); + if (ret) { + dev_err(&module->dev, "failed to enable interface %u: %d\n", + intf_id, ret); + goto err_interface_deactivate; + } + + return; + +err_interface_deactivate: + gb_interface_deactivate(intf); +} + +static void gb_module_deregister_interface(struct gb_interface *intf) +{ + /* Mark as disconnected to prevent I/O during disable. */ + if (intf->module->disconnected) + intf->disconnected = true; + + gb_interface_disable(intf); + gb_interface_deactivate(intf); + + gb_interface_del(intf); +} + +/* Register a module and its interfaces. */ +int gb_module_add(struct gb_module *module) +{ + size_t i; + int ret; + + ret = device_add(&module->dev); + if (ret) { + dev_err(&module->dev, "failed to register module: %d\n", ret); + return ret; + } + + for (i = 0; i < module->num_interfaces; ++i) + gb_module_register_interface(module->interfaces[i]); + + return 0; +} + +/* Deregister a module and its interfaces. */ +void gb_module_del(struct gb_module *module) +{ + size_t i; + + for (i = 0; i < module->num_interfaces; ++i) + gb_module_deregister_interface(module->interfaces[i]); + + device_del(&module->dev); +} + +void gb_module_put(struct gb_module *module) +{ + size_t i; + + for (i = 0; i < module->num_interfaces; ++i) + gb_interface_put(module->interfaces[i]); + + put_device(&module->dev); +} diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h new file mode 100644 index 000000000000..88a97ce04243 --- /dev/null +++ b/drivers/staging/greybus/module.h @@ -0,0 +1,34 @@ +/* + * Greybus Module code + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __MODULE_H +#define __MODULE_H + +struct gb_module { + struct device dev; + struct gb_host_device *hd; + + struct list_head hd_node; + + u8 module_id; + size_t num_interfaces; + + bool disconnected; + + struct gb_interface *interfaces[0]; +}; +#define to_gb_module(d) container_of(d, struct gb_module, dev) + +struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id, + size_t num_interfaces); +int gb_module_add(struct gb_module *module); +void gb_module_del(struct gb_module *module); +void gb_module_put(struct gb_module *module); + +#endif /* __MODULE_H */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index cde59d02400b..94016954a6ab 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -665,6 +665,19 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } +static struct gb_module *gb_svc_module_lookup(struct gb_svc *svc, u8 module_id) +{ + struct gb_host_device *hd = svc->hd; + struct gb_module *module; + + list_for_each_entry(module, &hd->modules, hd_node) { + if (module->module_id == module_id) + return module; + } + + return NULL; +} + static void gb_svc_intf_reenable(struct gb_svc *svc, struct gb_interface *intf) { int ret; @@ -689,7 +702,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) struct gb_connection *connection = operation->connection; struct gb_svc *svc = gb_connection_get_data(connection); struct gb_host_device *hd = connection->hd; - struct gb_interface *intf; + struct gb_module *module; u8 intf_id; int ret; @@ -699,52 +712,35 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); - intf = gb_interface_find(hd, intf_id); - if (intf) { + /* All modules are considered 1x2 for now */ + module = gb_svc_module_lookup(svc, intf_id); + if (module) { dev_info(&svc->dev, "mode switch detected on interface %u\n", intf_id); - return gb_svc_intf_reenable(svc, intf); + return gb_svc_intf_reenable(svc, module->interfaces[0]); } - intf = gb_interface_create(hd, intf_id); - if (!intf) { - dev_err(&svc->dev, "failed to create interface %u\n", - intf_id); + module = gb_module_create(hd, intf_id, 1); + if (!module) { + dev_err(&svc->dev, "failed to create module\n"); return; } - ret = gb_interface_activate(intf); + ret = gb_module_add(module); if (ret) { - dev_err(&svc->dev, "failed to activate interface %u: %d\n", - intf_id, ret); - gb_interface_add(intf); + gb_module_put(module); return; } - ret = gb_interface_add(intf); - if (ret) - goto err_interface_deactivate; - - ret = gb_interface_enable(intf); - if (ret) { - dev_err(&svc->dev, "failed to enable interface %u: %d\n", - intf_id, ret); - goto err_interface_deactivate; - } - - return; - -err_interface_deactivate: - gb_interface_deactivate(intf); + list_add(&module->hd_node, &hd->modules); } static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) { struct gb_svc *svc = gb_connection_get_data(operation->connection); struct gb_svc_intf_hot_unplug_request *request; - struct gb_host_device *hd = operation->connection->hd; - struct gb_interface *intf; + struct gb_module *module; u8 intf_id; /* The request message size has already been verified. */ @@ -753,19 +749,19 @@ static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); - intf = gb_interface_find(hd, intf_id); - if (!intf) { + /* All modules are considered 1x2 for now */ + module = gb_svc_module_lookup(svc, intf_id); + if (!module) { dev_warn(&svc->dev, "could not find hot-unplug interface %u\n", intf_id); return; } - /* Mark as disconnected to prevent I/O during disable. */ - intf->disconnected = true; + module->disconnected = true; - gb_interface_disable(intf); - gb_interface_deactivate(intf); - gb_interface_remove(intf); + gb_module_del(module); + list_del(&module->hd_node); + gb_module_put(module); } static void gb_svc_process_deferred_request(struct work_struct *work) @@ -1104,14 +1100,15 @@ int gb_svc_add(struct gb_svc *svc) return 0; } -static void gb_svc_remove_interfaces(struct gb_svc *svc) +static void gb_svc_remove_modules(struct gb_svc *svc) { - struct gb_interface *intf, *tmp; + struct gb_host_device *hd = svc->hd; + struct gb_module *module, *tmp; - list_for_each_entry_safe(intf, tmp, &svc->hd->interfaces, links) { - gb_interface_disable(intf); - gb_interface_deactivate(intf); - gb_interface_remove(intf); + list_for_each_entry_safe(module, tmp, &hd->modules, hd_node) { + gb_module_del(module); + list_del(&module->hd_node); + gb_module_put(module); } } @@ -1132,7 +1129,7 @@ void gb_svc_del(struct gb_svc *svc) flush_workqueue(svc->wq); - gb_svc_remove_interfaces(svc); + gb_svc_remove_modules(svc); } void gb_svc_put(struct gb_svc *svc) -- cgit v1.2.3-59-g8ed1b From 36602a2981c85de7b0b8600eb12cbad3d80eacd9 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:25 +0200 Subject: greybus: module: implement controlled module removal Implement controlled module removal through a new module attribute "eject". When a non-zero argument is written to the attribute, all interfaces of the module are disabled (e.g. bundles are deregistered) and deactivated (e.g. powered off) before instructing the SVC to physically eject the module. Note that the module device is not deregistered until the SVC has reported the physical removal of all of its interfaces. A new interface mutex is added to enforce interface state-change serialisation. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 8 ++++ drivers/staging/greybus/interface.c | 22 +++++++++- drivers/staging/greybus/interface.h | 3 ++ drivers/staging/greybus/module.c | 48 +++++++++++++++++++++- drivers/staging/greybus/svc.c | 4 ++ 5 files changed, 83 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 41ffc40b8efb..a18ee7eed75a 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -14,6 +14,14 @@ Description: A Module M on the bus N, where M is the 1-byte interface ID of the module's primary interface. +What: /sys/bus/greybus/device/N-M/eject +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + Writing a non-zero argument to this attibute disables the + module's interfaces before physically ejecting it. + What: /sys/bus/greybus/device/N-M/module_id Date: March 2016 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index d51c5635f22b..2553312dc332 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -372,6 +372,7 @@ struct gb_interface *gb_interface_create(struct gb_module *module, intf->interface_id = interface_id; INIT_LIST_HEAD(&intf->bundles); INIT_LIST_HEAD(&intf->manifest_descs); + mutex_init(&intf->mutex); /* Invalid device id to start with */ intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; @@ -388,10 +389,18 @@ struct gb_interface *gb_interface_create(struct gb_module *module, return intf; } +/* + * Activate an interface. + * + * Locking: Caller holds the interface mutex. + */ int gb_interface_activate(struct gb_interface *intf) { int ret; + if (intf->ejected) + return -ENODEV; + ret = gb_interface_read_dme(intf); if (ret) return ret; @@ -403,6 +412,11 @@ int gb_interface_activate(struct gb_interface *intf) return 0; } +/* + * Deactivate an interface. + * + * Locking: Caller holds the interface mutex. + */ void gb_interface_deactivate(struct gb_interface *intf) { gb_interface_route_destroy(intf); @@ -412,6 +426,8 @@ void gb_interface_deactivate(struct gb_interface *intf) * Enable an interface by enabling its control connection, fetching the * manifest and other information over it, and finally registering its child * devices. + * + * Locking: Caller holds the interface mutex. */ int gb_interface_enable(struct gb_interface *intf) { @@ -516,7 +532,11 @@ err_put_control: return ret; } -/* Disable an interface and destroy its bundles. */ +/* + * Disable an interface and destroy its bundles. + * + * Locking: Caller holds the interface mutex. + */ void gb_interface_disable(struct gb_interface *intf) { struct gb_bundle *bundle; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 9185c7f1bd32..61399e7ea102 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -39,7 +39,10 @@ struct gb_interface { unsigned long quirks; + struct mutex mutex; + bool disconnected; + bool ejected; bool enabled; }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 5c498e0a4eaf..5077253037c8 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -10,6 +10,43 @@ #include "greybus.h" +static ssize_t eject_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct gb_module *module = to_gb_module(dev); + struct gb_interface *intf; + size_t i; + long val; + int ret; + + ret = kstrtol(buf, 0, &val); + if (ret) + return ret; + + if (!val) + return len; + + for (i = 0; i < module->num_interfaces; ++i) { + intf = module->interfaces[i]; + + mutex_lock(&intf->mutex); + /* Set flag to prevent concurrent activation. */ + intf->ejected = true; + gb_interface_disable(intf); + gb_interface_deactivate(intf); + mutex_unlock(&intf->mutex); + } + + /* Tell the SVC to eject the primary interface. */ + ret = gb_svc_intf_eject(module->hd->svc, module->module_id); + if (ret) + return ret; + + return len; +} +static DEVICE_ATTR_WO(eject); + static ssize_t module_id_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -29,6 +66,7 @@ static ssize_t num_interfaces_show(struct device *dev, static DEVICE_ATTR_RO(num_interfaces); static struct attribute *module_attrs[] = { + &dev_attr_eject.attr, &dev_attr_module_id.attr, &dev_attr_num_interfaces.attr, NULL, @@ -101,12 +139,14 @@ static void gb_module_register_interface(struct gb_interface *intf) u8 intf_id = intf->interface_id; int ret; + mutex_lock(&intf->mutex); + ret = gb_interface_activate(intf); if (ret) { dev_err(&module->dev, "failed to activate interface %u: %d\n", intf_id, ret); gb_interface_add(intf); - return; + goto err_unlock; } ret = gb_interface_add(intf); @@ -120,10 +160,14 @@ static void gb_module_register_interface(struct gb_interface *intf) goto err_interface_deactivate; } + mutex_unlock(&intf->mutex); + return; err_interface_deactivate: gb_interface_deactivate(intf); +err_unlock: + mutex_unlock(&intf->mutex); } static void gb_module_deregister_interface(struct gb_interface *intf) @@ -132,8 +176,10 @@ static void gb_module_deregister_interface(struct gb_interface *intf) if (intf->module->disconnected) intf->disconnected = true; + mutex_lock(&intf->mutex); gb_interface_disable(intf); gb_interface_deactivate(intf); + mutex_unlock(&intf->mutex); gb_interface_del(intf); } diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 94016954a6ab..96b3027db528 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -682,6 +682,8 @@ static void gb_svc_intf_reenable(struct gb_svc *svc, struct gb_interface *intf) { int ret; + mutex_lock(&intf->mutex); + /* Mark as disconnected to prevent I/O during disable. */ intf->disconnected = true; gb_interface_disable(intf); @@ -694,6 +696,8 @@ static void gb_svc_intf_reenable(struct gb_svc *svc, struct gb_interface *intf) gb_interface_deactivate(intf); } + + mutex_unlock(&intf->mutex); } static void gb_svc_process_intf_hotplug(struct gb_operation *operation) -- cgit v1.2.3-59-g8ed1b From 1e1565e5fee47362baee92506749d80167a1456e Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:26 +0200 Subject: greybus: interface: add active state flag Add active state flag to avoid deactivating an interface which is already off. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 7 +++++++ drivers/staging/greybus/interface.h | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 2553312dc332..e0c38f158435 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -409,6 +409,8 @@ int gb_interface_activate(struct gb_interface *intf) if (ret) return ret; + intf->active = true; + return 0; } @@ -419,7 +421,12 @@ int gb_interface_activate(struct gb_interface *intf) */ void gb_interface_deactivate(struct gb_interface *intf) { + if (!intf->active) + return; + gb_interface_route_destroy(intf); + + intf->active = false; } /* diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 61399e7ea102..c9c1c92ece46 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -43,6 +43,7 @@ struct gb_interface { bool disconnected; bool ejected; + bool active; bool enabled; }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) -- cgit v1.2.3-59-g8ed1b From 017482b28decd5b72da2c6c661249e512e6665a8 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:27 +0200 Subject: greybus: svc: add stub functions for v_sys, refclk and unipro Add stub functions for v_sys, refclk and unipro resource management. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 21 +++++++++++++++++++++ drivers/staging/greybus/svc.h | 3 +++ 2 files changed, 24 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 96b3027db528..cac9789b8f67 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -253,6 +253,27 @@ int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) return 0; } +int gb_svc_intf_vsys_set(struct gb_svc *svc, u8 intf_id, bool enable) +{ + /* FIXME: implement */ + + return 0; +} + +int gb_svc_intf_refclk_set(struct gb_svc *svc, u8 intf_id, bool enable) +{ + /* FIXME: implement */ + + return 0; +} + +int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable) +{ + /* FIXME: implement */ + + return 0; +} + int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value) { diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 72bc716bf2fe..055948ce9e2b 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -69,6 +69,9 @@ int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id); +int gb_svc_intf_vsys_set(struct gb_svc *svc, u8 intf_id, bool enable); +int gb_svc_intf_refclk_set(struct gb_svc *svc, u8 intf_id, bool enable); +int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable); int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value); int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, -- cgit v1.2.3-59-g8ed1b From 1e8e22b5cb462e9c4da9c988b3355565cdef1d34 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:28 +0200 Subject: greybus: svc: add stub interface-activate function Add message structures (based on the current spec) for the SVC Interface Activate operation, and a stub function that always return the Greybus interface type. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 14 ++++++++++++++ drivers/staging/greybus/svc.c | 9 +++++++++ drivers/staging/greybus/svc.h | 1 + 3 files changed, 24 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 82798cf3ea2b..9ef87972903e 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -802,6 +802,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET 0x15 #define GB_SVC_TYPE_PWRMON_SAMPLE_GET 0x16 #define GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET 0x17 +#define GB_SVC_TYPE_INTF_ACTIVATE 0x27 /* * SVC version request/response has the same payload as @@ -1008,6 +1009,19 @@ struct gb_svc_pwrmon_intf_sample_get_response { __le32 measurement; } __packed; +struct gb_svc_intf_activate_request { + __u8 intf_id; +} __packed; + +#define GB_SVC_INTF_TYPE_UNKNOWN 0x00 +#define GB_SVC_INTF_TYPE_DUMMY 0x01 +#define GB_SVC_INTF_TYPE_UNIPRO 0x02 +#define GB_SVC_INTF_TYPE_GREYBUS 0x03 + +struct gb_svc_intf_activate_response { + __u8 intf_type; +} __packed; + /* RAW */ /* Version of the Greybus raw protocol we support */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index cac9789b8f67..1b370195e4c3 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -274,6 +274,15 @@ int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable) return 0; } +int gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type) +{ + /* FIXME: implement */ + + *intf_type = GB_SVC_INTF_TYPE_GREYBUS; + + return 0; +} + int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value) { diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 055948ce9e2b..546061198cb9 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -72,6 +72,7 @@ int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id); int gb_svc_intf_vsys_set(struct gb_svc *svc, u8 intf_id, bool enable); int gb_svc_intf_refclk_set(struct gb_svc *svc, u8 intf_id, bool enable); int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable); +int gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type); int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value); int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, -- cgit v1.2.3-59-g8ed1b From ec562f28a7de6a4a4c3f790e5cfb5e61e97419b5 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:29 +0200 Subject: greybus: interface: implement interface activation and power down Implement the interface activation and power-down sequences. Note that all the required SVC operations have not yet been implemented so some stub functions are used for now. Support for hibernating the UniPro link depends on future power-management work so a stub function is used also for this. Interface type handling will be refined later. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 124 +++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index e0c38f158435..f6c058a63fa2 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -389,6 +389,95 @@ struct gb_interface *gb_interface_create(struct gb_module *module, return intf; } +static int gb_interface_vsys_set(struct gb_interface *intf, bool enable) +{ + struct gb_svc *svc = intf->hd->svc; + int ret; + + dev_dbg(&intf->dev, "%s - %d\n", __func__, enable); + + ret = gb_svc_intf_vsys_set(svc, intf->interface_id, enable); + if (ret) { + dev_err(&intf->dev, "failed to enable v_sys: %d\n", ret); + return ret; + } + + return 0; +} + +static int gb_interface_refclk_set(struct gb_interface *intf, bool enable) +{ + struct gb_svc *svc = intf->hd->svc; + int ret; + + dev_dbg(&intf->dev, "%s - %d\n", __func__, enable); + + ret = gb_svc_intf_refclk_set(svc, intf->interface_id, enable); + if (ret) { + dev_err(&intf->dev, "failed to enable refclk: %d\n", ret); + return ret; + } + + return 0; +} + +static int gb_interface_unipro_set(struct gb_interface *intf, bool enable) +{ + struct gb_svc *svc = intf->hd->svc; + int ret; + + dev_dbg(&intf->dev, "%s - %d\n", __func__, enable); + + ret = gb_svc_intf_unipro_set(svc, intf->interface_id, enable); + if (ret) { + dev_err(&intf->dev, "failed to enable UniPro: %d\n", ret); + return ret; + } + + return 0; +} + +static int gb_interface_activate_operation(struct gb_interface *intf) +{ + struct gb_svc *svc = intf->hd->svc; + u8 type; + int ret; + + dev_dbg(&intf->dev, "%s\n", __func__); + + ret = gb_svc_intf_activate(svc, intf->interface_id, &type); + if (ret) { + dev_err(&intf->dev, "failed to activate: %d\n", ret); + return ret; + } + + switch (type) { + case GB_SVC_INTF_TYPE_DUMMY: + dev_info(&intf->dev, "dummy interface detected\n"); + /* FIXME: handle as an error for now */ + return -ENODEV; + case GB_SVC_INTF_TYPE_UNIPRO: + dev_err(&intf->dev, "interface type UniPro not supported\n"); + return -ENODEV; + case GB_SVC_INTF_TYPE_GREYBUS: + break; + default: + dev_err(&intf->dev, "unknown interface type: %u\n", type); + return -ENODEV; + } + + return 0; +} + +static int gb_interface_hibernate_link(struct gb_interface *intf) +{ + dev_dbg(&intf->dev, "%s\n", __func__); + + /* FIXME: implement */ + + return 0; +} + /* * Activate an interface. * @@ -401,17 +490,44 @@ int gb_interface_activate(struct gb_interface *intf) if (intf->ejected) return -ENODEV; - ret = gb_interface_read_dme(intf); + ret = gb_interface_vsys_set(intf, true); if (ret) return ret; + ret = gb_interface_refclk_set(intf, true); + if (ret) + goto err_vsys_disable; + + ret = gb_interface_unipro_set(intf, true); + if (ret) + goto err_refclk_disable; + + ret = gb_interface_activate_operation(intf); + if (ret) + goto err_unipro_disable; + + ret = gb_interface_read_dme(intf); + if (ret) + goto err_hibernate_link; + ret = gb_interface_route_create(intf); if (ret) - return ret; + goto err_hibernate_link; intf->active = true; return 0; + +err_hibernate_link: + gb_interface_hibernate_link(intf); +err_unipro_disable: + gb_interface_unipro_set(intf, false); +err_refclk_disable: + gb_interface_refclk_set(intf, false); +err_vsys_disable: + gb_interface_vsys_set(intf, false); + + return ret; } /* @@ -425,6 +541,10 @@ void gb_interface_deactivate(struct gb_interface *intf) return; gb_interface_route_destroy(intf); + gb_interface_hibernate_link(intf); + gb_interface_unipro_set(intf, false); + gb_interface_refclk_set(intf, false); + gb_interface_vsys_set(intf, false); intf->active = false; } -- cgit v1.2.3-59-g8ed1b From 22bb9380d620b9632fb47220133ee6ab3fd36f22 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:30 +0200 Subject: greybus: svc: implement module inserted and removed operations Implement the new module inserted and removed operations. The SVC sends these after detecting a module insertion or removal, and in the former case after having determined the module geometry (i.e. position and size). Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 16 ++++ drivers/staging/greybus/svc.c | 124 ++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9ef87972903e..ece8c64ef13d 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -802,6 +802,8 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET 0x15 #define GB_SVC_TYPE_PWRMON_SAMPLE_GET 0x16 #define GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET 0x17 +#define GB_SVC_TYPE_MODULE_INSERTED 0x1f +#define GB_SVC_TYPE_MODULE_REMOVED 0x20 #define GB_SVC_TYPE_INTF_ACTIVATE 0x27 /* @@ -1009,6 +1011,20 @@ struct gb_svc_pwrmon_intf_sample_get_response { __le32 measurement; } __packed; +#define GB_SVC_MODULE_INSERTED_FLAG_NO_PRIMARY 0x0001 + +struct gb_svc_module_inserted_request { + __u8 primary_intf_id; + __u8 intf_count; + __le16 flags; +} __packed; +/* module_inserted response has no payload */ + +struct gb_svc_module_removed_request { + __u8 primary_intf_id; +} __packed; +/* module_removed response has no payload */ + struct gb_svc_intf_activate_request { __u8 intf_id; } __packed; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 1b370195e4c3..9d90014ab298 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -798,6 +798,82 @@ static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) gb_module_put(module); } +static void gb_svc_process_module_inserted(struct gb_operation *operation) +{ + struct gb_svc_module_inserted_request *request; + struct gb_connection *connection = operation->connection; + struct gb_svc *svc = gb_connection_get_data(connection); + struct gb_host_device *hd = svc->hd; + struct gb_module *module; + size_t num_interfaces; + u8 module_id; + u16 flags; + int ret; + + /* The request message size has already been verified. */ + request = operation->request->payload; + module_id = request->primary_intf_id; + num_interfaces = request->intf_count; + flags = le16_to_cpu(request->flags); + + dev_dbg(&svc->dev, "%s - id = %u, num_interfaces = %zu, flags = 0x%04x\n", + __func__, module_id, num_interfaces, flags); + + if (flags & GB_SVC_MODULE_INSERTED_FLAG_NO_PRIMARY) { + dev_warn(&svc->dev, "no primary interface detected on module %u\n", + module_id); + } + + module = gb_svc_module_lookup(svc, module_id); + if (module) { + dev_warn(&svc->dev, "unexpected module-inserted event %u\n", + module_id); + return; + } + + module = gb_module_create(hd, module_id, num_interfaces); + if (!module) { + dev_err(&svc->dev, "failed to create module\n"); + return; + } + + ret = gb_module_add(module); + if (ret) { + gb_module_put(module); + return; + } + + list_add(&module->hd_node, &hd->modules); +} + +static void gb_svc_process_module_removed(struct gb_operation *operation) +{ + struct gb_svc_module_removed_request *request; + struct gb_connection *connection = operation->connection; + struct gb_svc *svc = gb_connection_get_data(connection); + struct gb_module *module; + u8 module_id; + + /* The request message size has already been verified. */ + request = operation->request->payload; + module_id = request->primary_intf_id; + + dev_dbg(&svc->dev, "%s - id = %u\n", __func__, module_id); + + module = gb_svc_module_lookup(svc, module_id); + if (!module) { + dev_warn(&svc->dev, "unexpected module-removed event %u\n", + module_id); + return; + } + + module->disconnected = true; + + gb_module_del(module); + list_del(&module->hd_node); + gb_module_put(module); +} + static void gb_svc_process_deferred_request(struct work_struct *work) { struct gb_svc_deferred_request *dr; @@ -817,6 +893,12 @@ static void gb_svc_process_deferred_request(struct work_struct *work) case GB_SVC_TYPE_INTF_HOT_UNPLUG: gb_svc_process_intf_hot_unplug(operation); break; + case GB_SVC_TYPE_MODULE_INSERTED: + gb_svc_process_module_inserted(operation); + break; + case GB_SVC_TYPE_MODULE_REMOVED: + gb_svc_process_module_removed(operation); + break; default: dev_err(&svc->dev, "bad deferred request type: 0x%02x\n", type); } @@ -957,6 +1039,44 @@ static int gb_svc_key_event_recv(struct gb_operation *op) return 0; } +static int gb_svc_module_inserted_recv(struct gb_operation *op) +{ + struct gb_svc *svc = gb_connection_get_data(op->connection); + struct gb_svc_module_inserted_request *request; + + if (op->request->payload_size < sizeof(*request)) { + dev_warn(&svc->dev, "short module-inserted request received (%zu < %zu)\n", + op->request->payload_size, sizeof(*request)); + return -EINVAL; + } + + request = op->request->payload; + + dev_dbg(&svc->dev, "%s - id = %u\n", __func__, + request->primary_intf_id); + + return gb_svc_queue_deferred_request(op); +} + +static int gb_svc_module_removed_recv(struct gb_operation *op) +{ + struct gb_svc *svc = gb_connection_get_data(op->connection); + struct gb_svc_module_removed_request *request; + + if (op->request->payload_size < sizeof(*request)) { + dev_warn(&svc->dev, "short module-removed request received (%zu < %zu)\n", + op->request->payload_size, sizeof(*request)); + return -EINVAL; + } + + request = op->request->payload; + + dev_dbg(&svc->dev, "%s - id = %u\n", __func__, + request->primary_intf_id); + + return gb_svc_queue_deferred_request(op); +} + static int gb_svc_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -1014,6 +1134,10 @@ static int gb_svc_request_handler(struct gb_operation *op) return gb_svc_intf_reset_recv(op); case GB_SVC_TYPE_KEY_EVENT: return gb_svc_key_event_recv(op); + case GB_SVC_TYPE_MODULE_INSERTED: + return gb_svc_module_inserted_recv(op); + case GB_SVC_TYPE_MODULE_REMOVED: + return gb_svc_module_removed_recv(op); default: dev_warn(&svc->dev, "unsupported request 0x%02x\n", type); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From b482b0d6f099e282c7499b5b1a3a69747bf4fa8b Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Sat, 23 Apr 2016 18:47:31 +0200 Subject: greybus: svc: implement interface mailbox event Implement the new interface mailbox-event operation. The event is sent by the SVC under certain conditions when an interface updates its mailbox value. Specifically, this event will be used to implement the new mode-switch functionality. Upon reception the AP verifies that the interface is known and that the mailbox has the expected MAILBOX_GREYBUS value. If so, the interface is disabled before being re-enabled (re-enumerated). Note that during mode-switch, the interface will typically already be in a disabled state when the mailbox is written (with the ES3 bootrom being the notable exception). Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 13 ++++ drivers/staging/greybus/svc.c | 96 +++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index ece8c64ef13d..bf3c132a3266 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -805,6 +805,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_MODULE_INSERTED 0x1f #define GB_SVC_TYPE_MODULE_REMOVED 0x20 #define GB_SVC_TYPE_INTF_ACTIVATE 0x27 +#define GB_SVC_TYPE_INTF_MAILBOX_EVENT 0x29 /* * SVC version request/response has the same payload as @@ -1038,6 +1039,18 @@ struct gb_svc_intf_activate_response { __u8 intf_type; } __packed; +#define GB_SVC_INTF_MAILBOX_NONE 0x00 +#define GB_SVC_INTF_MAILBOX_AP 0x01 +#define GB_SVC_INTF_MAILBOX_GREYBUS 0x02 + +struct gb_svc_intf_mailbox_event_request { + __u8 intf_id; + __le16 result_code; + __le32 mailbox; +} __packed; +/* intf_mailbox_event response has no payload */ + + /* RAW */ /* Version of the Greybus raw protocol we support */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 9d90014ab298..0a49698ce298 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -695,6 +695,27 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } +static struct gb_interface *gb_svc_interface_lookup(struct gb_svc *svc, + u8 intf_id) +{ + struct gb_host_device *hd = svc->hd; + struct gb_module *module; + size_t num_interfaces; + u8 module_id; + + list_for_each_entry(module, &hd->modules, hd_node) { + module_id = module->module_id; + num_interfaces = module->num_interfaces; + + if (intf_id >= module_id && + intf_id < module_id + num_interfaces) { + return module->interfaces[intf_id - module_id]; + } + } + + return NULL; +} + static struct gb_module *gb_svc_module_lookup(struct gb_svc *svc, u8 module_id) { struct gb_host_device *hd = svc->hd; @@ -874,6 +895,58 @@ static void gb_svc_process_module_removed(struct gb_operation *operation) gb_module_put(module); } +static void gb_svc_process_intf_mailbox_event(struct gb_operation *operation) +{ + struct gb_svc_intf_mailbox_event_request *request; + struct gb_connection *connection = operation->connection; + struct gb_svc *svc = gb_connection_get_data(connection); + struct gb_interface *intf; + u8 intf_id; + u16 result_code; + u32 mailbox; + + /* The request message size has already been verified. */ + request = operation->request->payload; + intf_id = request->intf_id; + result_code = le16_to_cpu(request->result_code); + mailbox = le32_to_cpu(request->mailbox); + + dev_dbg(&svc->dev, "%s - id = %u, result = 0x%04x, mailbox = 0x%08x\n", + __func__, intf_id, result_code, mailbox); + + intf = gb_svc_interface_lookup(svc, intf_id); + if (!intf) { + dev_warn(&svc->dev, "unexpected mailbox event %u\n", intf_id); + return; + } + + if (result_code) { + dev_warn(&svc->dev, + "mailbox event %u with UniPro error: 0x%04x\n", + intf_id, result_code); + goto err_disable_interface; + } + + if (mailbox != GB_SVC_INTF_MAILBOX_GREYBUS) { + dev_warn(&svc->dev, + "mailbox event %u with unexected value: 0x%08x\n", + intf_id, mailbox); + goto err_disable_interface; + } + + dev_info(&svc->dev, "mode switch detected on interface %u\n", intf_id); + + gb_svc_intf_reenable(svc, intf); + + return; + +err_disable_interface: + mutex_lock(&intf->mutex); + gb_interface_disable(intf); + gb_interface_deactivate(intf); + mutex_unlock(&intf->mutex); +} + static void gb_svc_process_deferred_request(struct work_struct *work) { struct gb_svc_deferred_request *dr; @@ -899,6 +972,9 @@ static void gb_svc_process_deferred_request(struct work_struct *work) case GB_SVC_TYPE_MODULE_REMOVED: gb_svc_process_module_removed(operation); break; + case GB_SVC_TYPE_INTF_MAILBOX_EVENT: + gb_svc_process_intf_mailbox_event(operation); + break; default: dev_err(&svc->dev, "bad deferred request type: 0x%02x\n", type); } @@ -1077,6 +1153,24 @@ static int gb_svc_module_removed_recv(struct gb_operation *op) return gb_svc_queue_deferred_request(op); } +static int gb_svc_intf_mailbox_event_recv(struct gb_operation *op) +{ + struct gb_svc *svc = gb_connection_get_data(op->connection); + struct gb_svc_intf_mailbox_event_request *request; + + if (op->request->payload_size < sizeof(*request)) { + dev_warn(&svc->dev, "short mailbox request received (%zu < %zu)\n", + op->request->payload_size, sizeof(*request)); + return -EINVAL; + } + + request = op->request->payload; + + dev_dbg(&svc->dev, "%s - id = %u\n", __func__, request->intf_id); + + return gb_svc_queue_deferred_request(op); +} + static int gb_svc_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -1138,6 +1232,8 @@ static int gb_svc_request_handler(struct gb_operation *op) return gb_svc_module_inserted_recv(op); case GB_SVC_TYPE_MODULE_REMOVED: return gb_svc_module_removed_recv(op); + case GB_SVC_TYPE_INTF_MAILBOX_EVENT: + return gb_svc_intf_mailbox_event_recv(op); default: dev_warn(&svc->dev, "unsupported request 0x%02x\n", type); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 3fd747a63e836baab1739bab7f18d9227a083312 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Fri, 22 Apr 2016 19:03:42 -0700 Subject: greybus: svc: clean up gb_svc struct for pwrmon The power rail names and counts are unnecessarily stored in the gb_svc structure once the SVC created, this causes waste of memory usage. This patch removes rail names and rail counts storage from th gb_svc structure. Testing Done: - Validated the readings from /d/greybus/1-svc/pwrmon/* Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 27 ++++++++++++++------------- drivers/staging/greybus/svc.h | 2 -- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 0a49698ce298..b30b2277fa86 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -581,38 +581,40 @@ static void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) int i; size_t bufsize; struct dentry *dent; + struct gb_svc_pwrmon_rail_names_get_response *rail_names; + u8 rail_count; dent = debugfs_create_dir("pwrmon", svc->debugfs_dentry); if (IS_ERR_OR_NULL(dent)) return; - if (gb_svc_pwrmon_rail_count_get(svc, &svc->rail_count)) + if (gb_svc_pwrmon_rail_count_get(svc, &rail_count)) goto err_pwrmon_debugfs; - if (!svc->rail_count || svc->rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT) + if (!rail_count || rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT) goto err_pwrmon_debugfs; - bufsize = GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * svc->rail_count; + bufsize = GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * rail_count; - svc->rail_names = kzalloc(bufsize, GFP_KERNEL); - if (!svc->rail_names) + rail_names = kzalloc(bufsize, GFP_KERNEL); + if (!rail_names) goto err_pwrmon_debugfs; - svc->pwrmon_rails = kcalloc(svc->rail_count, sizeof(*svc->pwrmon_rails), + svc->pwrmon_rails = kcalloc(rail_count, sizeof(*svc->pwrmon_rails), GFP_KERNEL); if (!svc->pwrmon_rails) goto err_pwrmon_debugfs_free; - if (gb_svc_pwrmon_rail_names_get(svc, svc->rail_names, bufsize)) + if (gb_svc_pwrmon_rail_names_get(svc, rail_names, bufsize)) goto err_pwrmon_debugfs_free; - for (i = 0; i < svc->rail_count; i++) { + for (i = 0; i < rail_count; i++) { struct dentry *dir; struct svc_debugfs_pwrmon_rail *rail = &svc->pwrmon_rails[i]; char fname[GB_SVC_PWRMON_RAIL_NAME_BUFSIZE]; snprintf(fname, sizeof(fname), "%s", - (char *)&svc->rail_names->name[i]); + (char *)&rail_names->name[i]); rail->id = i; rail->svc = svc; @@ -625,12 +627,12 @@ static void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) debugfs_create_file("power_now", S_IRUGO, dir, rail, &pwrmon_debugfs_power_fops); }; + + kfree(rail_names); return; err_pwrmon_debugfs_free: - kfree(svc->rail_names); - svc->rail_names = NULL; - + kfree(rail_names); kfree(svc->pwrmon_rails); svc->pwrmon_rails = NULL; @@ -648,7 +650,6 @@ static void gb_svc_debugfs_init(struct gb_svc *svc) static void gb_svc_debugfs_exit(struct gb_svc *svc) { debugfs_remove_recursive(svc->debugfs_dentry); - kfree(svc->rail_names); } static int gb_svc_hello(struct gb_operation *op) diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 546061198cb9..7268db6e2643 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -48,8 +48,6 @@ struct gb_svc { struct dentry *debugfs_dentry; struct svc_debugfs_pwrmon_rail *pwrmon_rails; - struct gb_svc_pwrmon_rail_names_get_response *rail_names; - u8 rail_count; }; #define to_gb_svc(d) container_of(d, struct gb_svc, dev) -- cgit v1.2.3-59-g8ed1b From 9983ea6b766aa701707e50c4b767ca892ee9ba7d Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Fri, 22 Apr 2016 19:03:43 -0700 Subject: greybus: svc: free pwrmon_rails memory upon exit For every time SVC instance is created, memories for storing the rail IDs are allocated, however, they are not freed when the SVC is destroyed. This patch fixes the memory leak by freeing the memory when debugfs for SVC is no longer needed. Testing Done: - Check pwrmon debugfs after turning on and off SVC Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b30b2277fa86..78fe4dc37131 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -650,6 +650,8 @@ static void gb_svc_debugfs_init(struct gb_svc *svc) static void gb_svc_debugfs_exit(struct gb_svc *svc) { debugfs_remove_recursive(svc->debugfs_dentry); + kfree(svc->pwrmon_rails); + svc->pwrmon_rails = NULL; } static int gb_svc_hello(struct gb_operation *op) -- cgit v1.2.3-59-g8ed1b From dc0f0285f80558e800411d583de71358a581dfae Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 25 Apr 2016 21:50:36 +0530 Subject: greybus: Bundle: Initialize dma_mask for bundle device We missed this only for bundle device, fix it. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bundle.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 1714482bd34d..1810b62457bc 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -125,6 +125,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, bundle->dev.bus = &greybus_bus_type; bundle->dev.type = &greybus_bundle_type; bundle->dev.groups = bundle_groups; + bundle->dev.dma_mask = intf->dev.dma_mask; device_initialize(&bundle->dev); dev_set_name(&bundle->dev, "%s.%d", dev_name(&intf->dev), bundle_id); -- cgit v1.2.3-59-g8ed1b From 2b8c2b51000e3d056f9aa27a64b93feabddf77a4 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 21 Apr 2016 22:14:02 +0530 Subject: greybus: audio: Changes in response to ASoC cleanup Update Makefile in response to SND_SOC_DYNAMIC_DAILINK cflag removal. Update files for msm-dynamic-dailink.h header file removal. Update in response to API name changes. Also, acquire sound card controls_rwsem before adding kcontrols to avoid deadlock. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 6 ++---- drivers/staging/greybus/audio_codec.c | 11 +++++++++-- drivers/staging/greybus/audio_module.c | 1 - 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 8ec607460ec0..5bdccccbe171 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -52,11 +52,9 @@ obj-m += gb-es2.o ifeq ($(CONFIG_USB_HSIC_USB3613),y) obj-m += gb-arche.o endif -ifeq ($(CONFIG_SND_SOC_DYNAMIC_DAILINK),y) - obj-m += gb-audio-codec.o -obj-m += gb-audio-module.o -endif ifeq ($(CONFIG_ARCH_MSM8994),y) + obj-m += gb-audio-codec.o + obj-m += gb-audio-module.o obj-m += gb-camera.o endif obj-m += gb-audio-gb.o diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 20feea1c64d3..a2b81ba78196 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -898,6 +898,7 @@ int gbaudio_register_module(struct gbaudio_module_info *module) { int ret; struct snd_soc_codec *codec; + struct snd_card *card; struct snd_soc_jack *jack = NULL; if (!gbcodec) { @@ -906,6 +907,9 @@ int gbaudio_register_module(struct gbaudio_module_info *module) } codec = gbcodec->codec; + card = codec->card->snd_card; + + down_write(&card->controls_rwsem); mutex_lock(&gbcodec->lock); if (module->num_dais) { @@ -913,12 +917,14 @@ int gbaudio_register_module(struct gbaudio_module_info *module) "%d:DAIs not supported via gbcodec driver\n", module->num_dais); mutex_unlock(&gbcodec->lock); + up_write(&card->controls_rwsem); return -EINVAL; } ret = gbaudio_init_jack(module, codec); if (ret) { mutex_unlock(&gbcodec->lock); + up_write(&card->controls_rwsem); return ret; } @@ -936,7 +942,7 @@ int gbaudio_register_module(struct gbaudio_module_info *module) if (codec->card->instantiated) { ret = snd_soc_dapm_new_widgets(&codec->dapm); if (!ret) - snd_soc_dapm_link_dai_widgets_component(codec->card, + snd_soc_dapm_link_component_dai_widgets(codec->card, &codec->dapm); } @@ -953,6 +959,7 @@ int gbaudio_register_module(struct gbaudio_module_info *module) dev_dbg(codec->dev, "Registered %s module\n", module->name); mutex_unlock(&gbcodec->lock); + up_write(&card->controls_rwsem); return 0; } EXPORT_SYMBOL(gbaudio_register_module); @@ -1061,7 +1068,7 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) if (module->controls) { dev_dbg(codec->dev, "Removing %d controls\n", module->num_controls); - soc_remove_codec_controls(codec, module->controls, + snd_soc_remove_codec_controls(codec, module->controls, module->num_controls); } if (module->dapm_widgets) { diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index dd43b6d5ae07..bdc5ec50b30b 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -9,7 +9,6 @@ #include <linux/module.h> #include <sound/soc.h> #include <sound/pcm_params.h> -#include <sound/msm-dynamic-dailink.h> #include "audio_codec.h" #include "audio_apbridgea.h" -- cgit v1.2.3-59-g8ed1b From 54e9070b24def1dfaf07e78fee6ea18f7ff57bcc Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Sat, 23 Apr 2016 20:04:05 +0530 Subject: greybus: audio: Reorder gb_deactivate sequence to avoid protocol error gb_activate_tx/rx is triggered from _prepare() & gb_deactivate from shutdown(). This may cause protocol error in case shutdown executes without _prepare due to some hw_params failure. Also, reorganise _prepare & _shutdown calls to make it more readable & cleaner. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 267 ++++++++++++++++++++++------------ 1 file changed, 171 insertions(+), 96 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index a2b81ba78196..7a9abbf0ed5a 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -395,12 +395,88 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, return ret; } +static int gbmodule_shutdown_tx(struct gbaudio_module_info *module, + struct gbaudio_data_connection *data, + int codec_state, struct device *dev) +{ + int ret, module_state; + __u16 i2s_port, cportid; + + module_state = module->ctrlstate[0]; + if (module_state == GBAUDIO_CODEC_SHUTDOWN) { + dev_dbg(dev, "%s: module already configured\n", + module->name); + return 0; + } + + if (codec_state == GBAUDIO_CODEC_STOP) { + ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0); + if (ret) + return ret; + } + + /* deactivate */ + cportid = data->connection->intf_cport_id; + if (module_state >= GBAUDIO_CODEC_PREPARE) { + ret = gb_audio_gb_deactivate_tx(module->mgmt_connection, + cportid); + if (ret) + return ret; + } + + /* unregister cport */ + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(data->connection, i2s_port, + cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); + + return ret; +} + +static int gbmodule_shutdown_rx(struct gbaudio_module_info *module, + struct gbaudio_data_connection *data, + int codec_state, struct device *dev) +{ + int ret, module_state; + __u16 i2s_port, cportid; + + module_state = module->ctrlstate[1]; + if (module_state == GBAUDIO_CODEC_SHUTDOWN) { + dev_dbg(dev, "%s: module already configured\n", + module->name); + return 0; + } + + if (codec_state == GBAUDIO_CODEC_STOP) { + ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0); + if (ret) + return ret; + } + + /* deactivate */ + cportid = data->connection->intf_cport_id; + if (module_state >= GBAUDIO_CODEC_PREPARE) { + ret = gb_audio_gb_deactivate_rx(module->mgmt_connection, + cportid); + if (ret) + return ret; + } + + /* unregister cport */ + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(data->connection, i2s_port, + cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); + + return ret; +} + static void gbcodec_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret; - __u16 i2s_port, cportid; - int state, module_state; + int ret, state; struct gbaudio_module_info *module; struct gbaudio_data_connection *data; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); @@ -423,14 +499,6 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, mutex_unlock(&module->lock); continue; } - module_state = module->ctrlstate[substream->stream]; - if (module_state == GBAUDIO_CODEC_SHUTDOWN) { - dev_dbg(codec->dev, "%s: module already configured\n", - module->name); - mutex_unlock(&module->lock); - continue; - } - /* find the dai */ data = find_data(module, dai->name); if (!data) { @@ -440,32 +508,18 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, continue; } - /* deactivate */ - cportid = data->connection->intf_cport_id; switch (substream->stream) { - case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_gb_deactivate_rx(module->mgmt_connection, - cportid); - /* unregister cport */ - i2s_port = 0; /* fixed for now */ - cportid = data->connection->hd_cport_id; - ret = gb_audio_apbridgea_unregister_cport( - data->connection, i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_RX); - break; case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_gb_deactivate_tx(module->mgmt_connection, - cportid); - /* unregister cport */ - i2s_port = 0; /* fixed for now */ - cportid = data->connection->hd_cport_id; - ret = gb_audio_apbridgea_unregister_cport( - data->connection, i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_TX); + ret = gbmodule_shutdown_tx(module, data, state, + dai->dev); + break; + case SNDRV_PCM_STREAM_CAPTURE: + ret = gbmodule_shutdown_rx(module, data, state, + dai->dev); break; } - dev_dbg(dai->dev, "Unregister %s:%d DAI, ret:%d\n", dai->name, - cportid, ret); + dev_dbg(dai->dev, "Unregister %s DAI, ret:%d\n", dai->name, + ret); state = GBAUDIO_CODEC_SHUTDOWN; module->ctrlstate[substream->stream] = state; dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); @@ -589,14 +643,82 @@ func_exit: return ret; } +static int gbmodule_prepare_tx(struct gbaudio_module_info *module, + struct gbaudio_data_connection *data, + int codec_state, struct device *dev) +{ + int ret; + uint16_t data_cport; + + data_cport = data->connection->intf_cport_id; + ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection, data_cport, + 192); + if (ret) { + dev_err(dev, "%d:Error during set_tx_data_size, cport:%d\n", + ret, data_cport); + return ret; + } + if (codec_state < GBAUDIO_CODEC_PREPARE) { + ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0, + 192); + if (ret) { + dev_err(dev, + "%d:Error during apbridgea set_tx_data_size, cport\n", + ret); + return ret; + } + } + ret = gb_audio_gb_activate_tx(module->mgmt_connection, + data_cport); + if (ret) + dev_err(dev, "%s:Error during activate stream,%d\n", + module->name, ret); + + return ret; +} + +static int gbmodule_prepare_rx(struct gbaudio_module_info *module, + struct gbaudio_data_connection *data, + int codec_state, struct device *dev) +{ + int ret; + uint16_t data_cport; + + data_cport = data->connection->intf_cport_id; + + ret = gb_audio_gb_set_rx_data_size(module->mgmt_connection, data_cport, + 192); + if (ret) { + dev_err(dev, "%d:Error during set_rx_data_size, cport:%d\n", + ret, data_cport); + return ret; + } + if (codec_state < GBAUDIO_CODEC_PREPARE) { + ret = gb_audio_apbridgea_set_rx_data_size(data->connection, 0, + 192); + if (ret) { + dev_err(dev, + "%d:Error during apbridgea_set_rx_data_size\n", + ret); + return ret; + } + } + ret = gb_audio_gb_activate_rx(module->mgmt_connection, + data_cport); + if (ret) + dev_err(dev, "%s:Error during activate stream,%d\n", + module->name, ret); + + return ret; +} + static int gbcodec_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int ret; - uint16_t data_cport; - struct gbaudio_data_connection *data; struct gbaudio_module_info *module; int state; + struct gbaudio_data_connection *data; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); mutex_lock(&codec->lock); @@ -623,71 +745,24 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, mutex_unlock(&module->lock); continue; } - /* deactivate rx/tx */ - data_cport = data->connection->intf_cport_id; switch (substream->stream) { - case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_gb_set_rx_data_size( - module->mgmt_connection, - data_cport, 192); - if (ret) { - dev_err(dai->dev, - "%d:Error during set_rx_data_size, cport:%d\n", - ret, data_cport); - mutex_unlock(&module->lock); - goto func_exit; - } - if (state < GBAUDIO_CODEC_PREPARE) { - ret = gb_audio_apbridgea_set_rx_data_size( - data->connection, 0, - 192); - if (ret) { - dev_err(dai->dev, - "%d:Error during apbridgea_set_rx_data_size\n", - ret); - mutex_unlock(&module->lock); - goto func_exit; - } - } - ret = gb_audio_gb_activate_rx(module->mgmt_connection, - data_cport); - if (ret) - dev_err(dai->dev, - "%s:Error during activate stream,%d\n", - module->name, ret); - break; case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_gb_set_tx_data_size( - module->mgmt_connection, - data_cport, 192); - if (ret) { - dev_err(dai->dev, - "%d:Error during module set_tx_data_size, cport:%d\n", - ret, data_cport); - mutex_unlock(&module->lock); - goto func_exit; - } - if (state < GBAUDIO_CODEC_PREPARE) { - ret = gb_audio_apbridgea_set_tx_data_size( - data->connection, 0, - 192); - if (ret) { - dev_err(dai->dev, - "%d:Error during apbridgea set_tx_data_size, cport\n", - ret); - mutex_unlock(&module->lock); - goto func_exit; - } - } - ret = gb_audio_gb_activate_tx(module->mgmt_connection, - data_cport); - if (ret) - dev_err(dai->dev, - "%s:Error during activate stream,%d\n", - module->name, ret); + ret = gbmodule_prepare_tx(module, data, state, + dai->dev); + break; + case SNDRV_PCM_STREAM_CAPTURE: + ret = gbmodule_prepare_rx(module, data, state, + dai->dev); break; } + if (ret == -ENODEV) + continue; + if (ret) { + mutex_unlock(&module->lock); + goto func_exit; + } + state = GBAUDIO_CODEC_PREPARE; module->ctrlstate[substream->stream] = state; dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); @@ -697,7 +772,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, func_exit: mutex_unlock(&codec->lock); - return 0; + return ret; } static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, -- cgit v1.2.3-59-g8ed1b From 475f498bcedb15634ca041b4eea2c58622558e03 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 13 Apr 2016 09:53:38 +0530 Subject: greybus: audio: Rename widget_type to jack_attrubute This fields actually populates jack attribute. Thus, renamed to reflect the real purpose. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index bf3c132a3266..713b56989735 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1922,7 +1922,7 @@ struct gb_audio_deactivate_rx_request { struct gb_audio_jack_event_request { __u8 widget_id; - __u8 widget_type; + __u8 jack_attribute; __u8 event; } __packed; -- cgit v1.2.3-59-g8ed1b From 05ab5f0daa524915176d170f3873b97ad10d7c43 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 13 Apr 2016 09:53:39 +0530 Subject: greybus: audio: Identify jack type based on attributes reported Originally, idea was to use widget_type to identify jack_type. However, suggestive way is to identify jack based on jack attributes. Changes already exists in codec FW to report jack attributes. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index bdc5ec50b30b..05bda486f490 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -27,7 +27,7 @@ static int gbaudio_request_jack(struct gbaudio_module_info *module, int report, button_status; dev_warn(module->dev, "Jack Event received: type: %u, event: %u\n", - req->widget_type, req->event); + req->jack_attribute, req->event); mutex_lock(&module->lock); if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) { @@ -43,18 +43,9 @@ static int gbaudio_request_jack(struct gbaudio_module_info *module, return 0; } - report &= ~GBCODEC_JACK_MASK; /* currently supports Headphone, Headset & Lineout only */ - if (req->widget_type && GB_AUDIO_WIDGET_TYPE_HP) - report |= SND_JACK_HEADPHONE & GBCODEC_JACK_MASK; - - if (req->widget_type && GB_AUDIO_WIDGET_TYPE_MIC) - report = SND_JACK_MICROPHONE & GBCODEC_JACK_MASK; - - if (req->widget_type && GB_AUDIO_WIDGET_TYPE_LINE) - report = (report & GBCODEC_JACK_MASK) | - SND_JACK_LINEOUT | SND_JACK_LINEIN; - + report &= ~GBCODEC_JACK_MASK; + report |= req->jack_attribute & GBCODEC_JACK_MASK; if (module->jack_type) dev_warn(module->dev, "Modifying jack from %d to %d\n", module->jack_type, report); -- cgit v1.2.3-59-g8ed1b From 9e04fb7b1627ad55b63c4e0927f696ecb1a2563a Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Tue, 26 Apr 2016 10:20:49 +0530 Subject: greybus: firmware: Add firmware management bundle driver All firmware packages on the Modules or Interfaces are now managed by a special Firmware Management Protocol. The Interface Manifest shall at least contain the Firmware Management Bundle and a Firmware Management Protocol CPort within it. The bundle may contain additional CPorts based on the extra functionality required to manage firmware packages. For example, this is how the Firmware Management Bundle of the Interface Manifest may look like: ; Firmware Management Bundle (Bundle 1): [bundle-descriptor 1] class = 0x16 ; (Mandatory) Firmware Management Protocol on CPort 1 [cport-descriptor 1] bundle = 1 protocol = 0x18 ; (Optional) Firmware Download Protocol on CPort 2 [cport-descriptor 2] bundle = 1 protocol = 0x17 ; (Optional) SPI protocol on CPort 3 [cport-descriptor 3] bundle = 1 protocol = 0x0b ; (Optional) Component Authentication Protocol (CAP) on CPort 4 [cport-descriptor 4] bundle = 1 protocol = 0xXX //TBD This patch adds the basic firmware-management bundle driver, which just creates a firmware-management connection. Support for individual protocols will be added separately. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/fw-core.c | 123 +++++++++++++++++++++++++++++ drivers/staging/greybus/greybus_manifest.h | 3 + 3 files changed, 128 insertions(+) create mode 100644 drivers/staging/greybus/fw-core.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 5bdccccbe171..1a120d928485 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -39,6 +39,7 @@ gb-audio-apbridgea-y := audio_apbridgea.o gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o +gb-firmware-y := fw-core.o obj-m += greybus.o obj-m += gb-phy.o @@ -60,6 +61,7 @@ endif obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o obj-m += gb-audio-manager.o +obj-m += gb-firmware.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c new file mode 100644 index 000000000000..4720d595cc2b --- /dev/null +++ b/drivers/staging/greybus/fw-core.c @@ -0,0 +1,123 @@ +/* + * Greybus Firmware Core Bundle Driver. + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/firmware.h> +#include "greybus.h" + +struct gb_fw_core { + struct gb_connection *mgmt_connection; +}; + +static int gb_fw_core_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; + struct gb_fw_core *fw_core; + int ret, i; + u16 cport_id; + u8 protocol_id; + + fw_core = kzalloc(sizeof(*fw_core), GFP_KERNEL); + if (!fw_core) + return -ENOMEM; + + /* Parse CPorts and create connections */ + for (i = 0; i < bundle->num_cports; i++) { + cport_desc = &bundle->cport_desc[i]; + cport_id = le16_to_cpu(cport_desc->id); + protocol_id = cport_desc->protocol_id; + + switch (protocol_id) { + case GREYBUS_PROTOCOL_FW_MANAGEMENT: + /* Disallow multiple Firmware Management CPorts */ + if (fw_core->mgmt_connection) { + dev_err(&bundle->dev, + "multiple management CPorts found\n"); + ret = -EINVAL; + goto err_destroy_connections; + } + + connection = gb_connection_create(bundle, cport_id, + NULL); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + dev_err(&bundle->dev, + "failed to create management connection (%d)\n", + ret); + goto err_free_fw_core; + } + + fw_core->mgmt_connection = connection; + break; + default: + dev_err(&bundle->dev, "invalid protocol id (0x%02x)\n", + protocol_id); + ret = -EINVAL; + goto err_free_fw_core; + } + } + + /* Firmware Management connection is mandatory */ + if (!fw_core->mgmt_connection) { + dev_err(&bundle->dev, "missing management connection\n"); + ret = -ENODEV; + goto err_free_fw_core; + } + + greybus_set_drvdata(bundle, fw_core); + + return 0; + +err_destroy_connections: + gb_connection_destroy(fw_core->mgmt_connection); +err_free_fw_core: + kfree(fw_core); + + return ret; +} + +static void gb_fw_core_disconnect(struct gb_bundle *bundle) +{ + struct gb_fw_core *fw_core = greybus_get_drvdata(bundle); + + gb_connection_destroy(fw_core->mgmt_connection); + + kfree(fw_core); +} + +static const struct greybus_bundle_id gb_fw_core_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FW_MANAGEMENT) }, + { } +}; + +static struct greybus_driver gb_fw_core_driver = { + .name = "gb-firmware", + .probe = gb_fw_core_probe, + .disconnect = gb_fw_core_disconnect, + .id_table = gb_fw_core_id_table, +}; + +static int fw_core_init(void) +{ + return greybus_register(&gb_fw_core_driver); +} +module_init(fw_core_init); + +static void __exit fw_core_exit(void) +{ + greybus_deregister(&gb_fw_core_driver); +} +module_exit(fw_core_exit); + +MODULE_ALIAS("greybus:firmware"); +MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>"); +MODULE_DESCRIPTION("Greybus Firmware Bundle Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 460fced979a4..5d00f7de5041 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -45,6 +45,8 @@ enum greybus_protocol { GREYBUS_PROTOCOL_SVC = 0x14, GREYBUS_PROTOCOL_BOOTROM = 0x15, GREYBUS_PROTOCOL_CAMERA_DATA = 0x16, + GREYBUS_PROTOCOL_FW_DOWNLOAD = 0x17, + GREYBUS_PROTOCOL_FW_MANAGEMENT = 0x18, /* ... */ GREYBUS_PROTOCOL_RAW = 0xfe, GREYBUS_PROTOCOL_VENDOR = 0xff, @@ -73,6 +75,7 @@ enum greybus_class_type { /* 0x13 is unused */ /* 0x14 is unused */ GREYBUS_CLASS_BOOTROM = 0x15, + GREYBUS_CLASS_FW_MANAGEMENT = 0x16, /* ... */ GREYBUS_CLASS_RAW = 0xfe, GREYBUS_CLASS_VENDOR = 0xff, -- cgit v1.2.3-59-g8ed1b From 6ddbed576606a454353638dbbbcc2f66f0fc712d Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 28 Apr 2016 16:19:39 +0200 Subject: greybus: interface: fix broken UniPro flow control A recent commit inadvertently disabled E2EFC on all interface connections, due to a failure to clear the ES3 bootrom quirk flags during mode switch. Testing Done: Verified that the CPort flags are again set to 0x7 for non-bootrom interface connections. Fixes: 5b638080e94e ("svc: keep interfaces registered during mode switch") Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index f6c058a63fa2..b5ad1ac9d3df 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -226,6 +226,9 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) intf->quirks |= GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; intf->quirks |= GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION; break; + default: + intf->quirks &= ~GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; + intf->quirks &= ~GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION; } /* Clear the init status. */ -- cgit v1.2.3-59-g8ed1b From fbbd2b7c0876550b20c6ac0ee2df90ecc262712c Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Fri, 29 Apr 2016 07:13:23 -0700 Subject: greybus: hd: add bus_id attribute The greybus host device id can only be read by parsing the uevent if one wants to identify a specific host device 'or' bus. This is 'lsgb' uses today. This change adds a bus_id attribute so libraries can identify multiple host devices 'or' bus if they exist. Testing Done: Tested on Arche, 'cat /sys/bus/greybus/devices/greysbus1/bus_id' Signed-off-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/hd.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 762cc035b128..28634720221d 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -24,6 +24,21 @@ int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, } EXPORT_SYMBOL_GPL(gb_hd_output); +static ssize_t bus_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_host_device *hd = to_gb_host_device(dev); + + return sprintf(buf, "%d\n", hd->bus_id); +} +static DEVICE_ATTR_RO(bus_id); + +static struct attribute *bus_attrs[] = { + &dev_attr_bus_id.attr, + NULL +}; +ATTRIBUTE_GROUPS(bus); + static void gb_hd_release(struct device *dev) { struct gb_host_device *hd = to_gb_host_device(dev); @@ -98,6 +113,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, hd->dev.parent = parent; hd->dev.bus = &greybus_bus_type; hd->dev.type = &greybus_hd_type; + hd->dev.groups = bus_groups; hd->dev.dma_mask = hd->dev.parent->dma_mask; device_initialize(&hd->dev); dev_set_name(&hd->dev, "greybus%d", hd->bus_id); -- cgit v1.2.3-59-g8ed1b From dc9e9cf450fb22ad8d4836bc2879ad3428a1b4eb Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Fri, 29 Apr 2016 07:13:24 -0700 Subject: greybus: Documentation/sysfs: add entry for host device bus_id Add description for greybus host device bus_id attribute Signed-off-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 7 +++++++ drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id | 1 + drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id | 1 + 3 files changed, 9 insertions(+) create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index a18ee7eed75a..044ede60dfec 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -6,6 +6,13 @@ Description: The "root" greybus device for the Greybus device tree, or bus, where N is a dynamically assigned 1-based id. +What: /sys/bus/greybus/devices/greybusN/bus_id +Date: April 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + The ID of the "root" greybus device, or bus. + What: /sys/bus/greybus/device/N-M Date: March 2016 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id @@ -0,0 +1 @@ +1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id @@ -0,0 +1 @@ +2 -- cgit v1.2.3-59-g8ed1b From 16ba59cedf070a28dfd84d4f3713a9494f50fe4a Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Fri, 29 Apr 2016 07:13:25 -0700 Subject: greybus: Documentation/sysfs: fix sysfs paths correct sysfs paths typo. (s/device/devices). Signed-off-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 58 +++++++++++----------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 044ede60dfec..0e1c2a37b24f 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -1,4 +1,4 @@ -What: /sys/bus/greybus/device/greybusN +What: /sys/bus/greybus/devices/greybusN Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -13,7 +13,7 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The ID of the "root" greybus device, or bus. -What: /sys/bus/greybus/device/N-M +What: /sys/bus/greybus/devices/N-M Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -21,7 +21,7 @@ Description: A Module M on the bus N, where M is the 1-byte interface ID of the module's primary interface. -What: /sys/bus/greybus/device/N-M/eject +What: /sys/bus/greybus/devices/N-M/eject Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -29,7 +29,7 @@ Description: Writing a non-zero argument to this attibute disables the module's interfaces before physically ejecting it. -What: /sys/bus/greybus/device/N-M/module_id +What: /sys/bus/greybus/devices/N-M/module_id Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -37,14 +37,14 @@ Description: The ID of a Greybus module, corresponding to the ID of its primary interface. -What: /sys/bus/greybus/device/N-M/num_interfaces +What: /sys/bus/greybus/devices/N-M/num_interfaces Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The number of interfaces of a module. -What: /sys/bus/greybus/device/N-M.I +What: /sys/bus/greybus/devices/N-M.I Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -52,14 +52,14 @@ Description: An Interface I on the bus N and module N-M, where I is the 1-byte interface ID. -What: /sys/bus/greybus/device/N-M.I/current_now +What: /sys/bus/greybus/devices/N-M.I/current_now Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Current measurement of the interface in microamps (uA) -What: /sys/bus/greybus/device/N-M.I/ddbl1_manufacturer_id +What: /sys/bus/greybus/devices/N-M.I/ddbl1_manufacturer_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -67,7 +67,7 @@ Description: Unipro Device Descriptor Block Level 1 manufacturer ID for the greybus Interface. -What: /sys/bus/greybus/device/N-M.I/ddbl1_product_id +What: /sys/bus/greybus/devices/N-M.I/ddbl1_product_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -75,28 +75,28 @@ Description: Unipro Device Descriptor Block Level 1 product ID for the greybus Interface. -What: /sys/bus/greybus/device/N-M.I/interface_id +What: /sys/bus/greybus/devices/N-M.I/interface_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The ID of a Greybus interface. -What: /sys/bus/greybus/device/N-M.I/power_now +What: /sys/bus/greybus/devices/N-M.I/power_now Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Power measurement of the interface in microwatts (uW) -What: /sys/bus/greybus/device/N-M.I/product_id +What: /sys/bus/greybus/devices/N-M.I/product_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Product ID of a Greybus interface. -What: /sys/bus/greybus/device/N-M.I/serial_number +What: /sys/bus/greybus/devices/N-M.I/serial_number Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -104,14 +104,14 @@ Description: Serial Number of the Greybus interface, represented by a 64 bit hexadecimal number. -What: /sys/bus/greybus/device/N-M.I/vendor_id +What: /sys/bus/greybus/devices/N-M.I/vendor_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Vendor ID of a Greybus interface. -What: /sys/bus/greybus/device/N-M.I/version +What: /sys/bus/greybus/devices/N-M.I/version Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -119,14 +119,14 @@ Description: Interface version represented as <16 bit major number>.<16 bit minor number>. -What: /sys/bus/greybus/device/N-M.I/voltage_now +What: /sys/bus/greybus/devices/N-M.I/voltage_now Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Voltage measurement of the interface in microvolts (uV) -What: /sys/bus/greybus/device/N-M.I.ctrl +What: /sys/bus/greybus/devices/N-M.I.ctrl Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -134,21 +134,21 @@ Description: Abstract control device for interface I that represents the current mode of an enumerated Greybus interface. -What: /sys/bus/greybus/device/N-M.I.ctrl/product_string +What: /sys/bus/greybus/devices/N-M.I.ctrl/product_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Product ID string of a Greybus interface. -What: /sys/bus/greybus/device/N-M.I.ctrl/vendor_string +What: /sys/bus/greybus/devices/N-M.I.ctrl/vendor_string Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Vendor ID string of a Greybus interface. -What: /sys/bus/greybus/device/N-M.I.B +What: /sys/bus/greybus/devices/N-M.I.B Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -156,21 +156,21 @@ Description: A bundle B on the Interface I, B is replaced by a 1-byte number representing the bundle. -What: /sys/bus/greybus/device/N-M.I.B/bundle_class +What: /sys/bus/greybus/devices/N-M.I.B/bundle_class Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The greybus class of the bundle B. -What: /sys/bus/greybus/device/N-M.I.B/bundle_id +What: /sys/bus/greybus/devices/N-M.I.B/bundle_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The interface-unique id of the bundle B. -What: /sys/bus/greybus/device/N-M.I.B/state +What: /sys/bus/greybus/devices/N-M.I.B/state Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -183,14 +183,14 @@ Description: value can be read. It's a "poor-man's IPC", yes, but simplifies the Android userspace code immensely. -What: /sys/bus/greybus/device/N-svc +What: /sys/bus/greybus/devices/N-svc Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The singleton SVC device of bus N. -What: /sys/bus/greybus/device/N-svc/ap_intf_id +What: /sys/bus/greybus/devices/N-svc/ap_intf_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -200,7 +200,7 @@ Description: The interface positions are defined in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/N-svc/endo_id +What: /sys/bus/greybus/devices/N-svc/endo_id Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -209,7 +209,7 @@ Description: defined by the Endo layout scheme, documented in the ARA Module Developer Kit. -What: /sys/bus/greybus/device/N-svc/intf_eject +What: /sys/bus/greybus/devices/N-svc/intf_eject Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> @@ -217,14 +217,14 @@ Description: Write the number of the interface that you wish to forcibly eject from the system. -What: /sys/bus/greybus/device/N-svc/version +What: /sys/bus/greybus/devices/N-svc/version Date: October 2015 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The version number of the firmware in the SVC device. -What: /sys/bus/greybus/device/N-svc/watchdog +What: /sys/bus/greybus/devices/N-svc/watchdog Date: October 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> -- cgit v1.2.3-59-g8ed1b From cca69fabeb715777c612f4223c91a2eca8f99c3a Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 29 Apr 2016 17:08:29 +0200 Subject: greybus: interface: remove unused prototype Remove unused function prototype that was left after a recent change. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index c9c1c92ece46..7a0366d08e32 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -48,9 +48,6 @@ struct gb_interface { }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) -struct gb_interface *gb_interface_find(struct gb_host_device *hd, - u8 interface_id); - struct gb_interface *gb_interface_create(struct gb_module *module, u8 interface_id); int gb_interface_activate(struct gb_interface *intf); -- cgit v1.2.3-59-g8ed1b From 410abddb0389aa1e456f980b336eb20948ccce51 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 29 Apr 2016 17:08:30 +0200 Subject: greybus: fix outdated kernel-doc comment Update the function name, which has gained a timeout suffix. Also fix the kernel-doc formatting. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5818d7c92c4e..5af65353b141 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -1006,7 +1006,7 @@ void gb_operation_cancel_incoming(struct gb_operation *operation, int errno) } /** - * gb_operation_sync: implement a "simple" synchronous gb operation. + * gb_operation_sync_timeout() - implement a "simple" synchronous operation * @connection: the Greybus connection to send this to * @type: the type of operation to send * @request: pointer to a memory buffer to copy the request from -- cgit v1.2.3-59-g8ed1b From 613c15e86b7ac30cb3e3f16e13ee367845a941b2 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 29 Apr 2016 17:08:31 +0200 Subject: greybus: operation: update gb_operation_request_send() documentation Fix and update gb_operation_request_send() documentation and add kernel-doc formatting. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 5af65353b141..bb8995583f6a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -627,13 +627,20 @@ static void gb_operation_sync_callback(struct gb_operation *operation) complete(&operation->completion); } -/* - * Send an operation request message. The caller has filled in any payload so - * the request message is ready to go. The callback function supplied will be - * called when the response message has arrived indicating the operation is - * complete. In that case, the callback function is responsible for fetching - * the result of the operation using gb_operation_result() if desired, and - * dropping the initial reference to the operation. +/** + * gb_operation_request_send() - send an operation request message + * @operation: the operation to initiate + * @callback: the operation completion callback + * @gfp: the memory flags to use for any allocations + * + * The caller has filled in any payload so the request message is ready to go. + * The callback function supplied will be called when the response message has + * arrived, or the operation is cancelled, indicating that the operation is + * complete. The callback function can fetch the result of the operation using + * gb_operation_result() if desired. + * + * Return: 0 if the request was successfully queued in the host-driver queues, + * or a negative errno. */ int gb_operation_request_send(struct gb_operation *operation, gb_operation_callback callback, -- cgit v1.2.3-59-g8ed1b From 3e2ee2c1e50541b611087be9e2e55b8f77477cf6 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 29 Apr 2016 17:08:32 +0200 Subject: greybus: operation: add support for initiating unidirectional operations Add support for initiating unidirectional operations, that is, sending requests that do not require responses. Note that we already handle incoming unidirectional operations. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 38 ++++++++++++++++++++++++++----------- drivers/staging/greybus/operation.h | 3 ++- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index bb8995583f6a..9fb7993a86e8 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -635,9 +635,10 @@ static void gb_operation_sync_callback(struct gb_operation *operation) * * The caller has filled in any payload so the request message is ready to go. * The callback function supplied will be called when the response message has - * arrived, or the operation is cancelled, indicating that the operation is - * complete. The callback function can fetch the result of the operation using - * gb_operation_result() if desired. + * arrived, a unidirectional request has been sent, or the operation is + * cancelled, indicating that the operation is complete. The callback function + * can fetch the result of the operation using gb_operation_result() if + * desired. * * Return: 0 if the request was successfully queued in the host-driver queues, * or a negative errno. @@ -653,6 +654,7 @@ int gb_operation_request_send(struct gb_operation *operation, if (!callback) return -EINVAL; + /* * Record the callback function, which is executed in * non-atomic (workqueue) context when the final result @@ -662,10 +664,15 @@ int gb_operation_request_send(struct gb_operation *operation, /* * Assign the operation's id, and store it in the request header. - * Zero is a reserved operation id. + * Zero is a reserved operation id for unidirectional operations. */ - cycle = (unsigned int)atomic_inc_return(&connection->op_cycle); - operation->id = (u16)(cycle % U16_MAX + 1); + if (gb_operation_is_unidirectional(operation)) { + operation->id = 0; + } else { + cycle = (unsigned int)atomic_inc_return(&connection->op_cycle); + operation->id = (u16)(cycle % U16_MAX + 1); + } + header = operation->request->header; header->operation_id = cpu_to_le16(operation->id); @@ -799,10 +806,11 @@ void greybus_message_sent(struct gb_host_device *hd, * reference to the operation. If an error occurred, report * it. * - * For requests, if there's no error, there's nothing more - * to do until the response arrives. If an error occurred - * attempting to send it, record that as the result of - * the operation and schedule its completion. + * For requests, if there's no error and the operation in not + * unidirectional, there's nothing more to do until the response + * arrives. If an error occurred attempting to send it, or if the + * operation is unidrectional, record the result of the operation and + * schedule its completion. */ if (message == operation->response) { if (status) { @@ -810,9 +818,10 @@ void greybus_message_sent(struct gb_host_device *hd, "%s: error sending response 0x%02x: %d\n", connection->name, operation->type, status); } + gb_operation_put_active(operation); gb_operation_put(operation); - } else if (status) { + } else if (status || gb_operation_is_unidirectional(operation)) { if (gb_operation_result_set(operation, status)) { queue_work(gb_operation_completion_wq, &operation->work); @@ -876,6 +885,13 @@ static void gb_connection_recv_response(struct gb_connection *connection, int errno = gb_operation_status_map(result); size_t message_size; + if (!operation_id) { + dev_err(&connection->hd->dev, + "%s: invalid response id 0 received\n", + connection->name); + return; + } + operation = gb_operation_find_outgoing(connection, operation_id); if (!operation) { dev_err(&connection->hd->dev, diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 38e5303a511f..c88efe11c22b 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -67,7 +67,8 @@ struct gb_message { #define GB_OPERATION_FLAG_UNIDIRECTIONAL BIT(1) #define GB_OPERATION_FLAG_SHORT_RESPONSE BIT(2) -#define GB_OPERATION_FLAG_USER_MASK GB_OPERATION_FLAG_SHORT_RESPONSE +#define GB_OPERATION_FLAG_USER_MASK (GB_OPERATION_FLAG_SHORT_RESPONSE | \ + GB_OPERATION_FLAG_UNIDIRECTIONAL) /* * A Greybus operation is a remote procedure call performed over a -- cgit v1.2.3-59-g8ed1b From 5fdc027d5441aecb94ce44de0402bdec3a731de8 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 29 Apr 2016 17:08:33 +0200 Subject: greybus: operation: add helper functions for unidirectional operations Add helper functions for initiating unidirectional operations and waiting for them to have been acknowledged as sent by the host device. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 47 +++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/operation.h | 10 ++++++++ 2 files changed, 57 insertions(+) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 9fb7993a86e8..fcae74793740 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -1090,6 +1090,53 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, } EXPORT_SYMBOL_GPL(gb_operation_sync_timeout); +/** + * gb_operation_unidirectional_timeout() - initiate a unidirectional operation + * @connection: connection to use + * @type: type of operation to send + * @request: memory buffer to copy the request from + * @request_size: size of @request + * @timeout: send timeout in milliseconds + * + * Initiate a unidirectional operation by sending a request message and + * waiting for it to be acknowledged as sent by the host device. + * + * Note that successful send of a unidirectional operation does not imply that + * the request as actually reached the remote end of the connection. + */ +int gb_operation_unidirectional_timeout(struct gb_connection *connection, + int type, void *request, int request_size, + unsigned int timeout) +{ + struct gb_operation *operation; + int ret; + + if (request_size && !request) + return -EINVAL; + + operation = gb_operation_create_flags(connection, type, + request_size, 0, + GB_OPERATION_FLAG_UNIDIRECTIONAL, + GFP_KERNEL); + if (!operation) + return -ENOMEM; + + if (request_size) + memcpy(operation->request->payload, request, request_size); + + ret = gb_operation_request_send_sync_timeout(operation, timeout); + if (ret) { + dev_err(&connection->hd->dev, + "%s: unidirectional operation of type 0x%02x failed: %d\n", + connection->name, type, ret); + } + + gb_operation_put(operation); + + return ret; +} +EXPORT_SYMBOL_GPL(gb_operation_unidirectional_timeout); + int __init gb_operation_init(void) { gb_message_cache = kmem_cache_create("gb_message_cache", diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index c88efe11c22b..8ef8f514dafa 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -178,6 +178,9 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, void *request, int request_size, void *response, int response_size, unsigned int timeout); +int gb_operation_unidirectional_timeout(struct gb_connection *connection, + int type, void *request, int request_size, + unsigned int timeout); static inline int gb_operation_sync(struct gb_connection *connection, int type, void *request, int request_size, @@ -188,6 +191,13 @@ static inline int gb_operation_sync(struct gb_connection *connection, int type, GB_OPERATION_TIMEOUT_DEFAULT); } +static inline int gb_operation_unidirectional(struct gb_connection *connection, + int type, void *request, int request_size) +{ + return gb_operation_unidirectional_timeout(connection, type, + request, request_size, GB_OPERATION_TIMEOUT_DEFAULT); +} + int gb_operation_init(void); void gb_operation_exit(void); -- cgit v1.2.3-59-g8ed1b From 1c3aead12ea4df73b7ae552aa4f4e83bc1f0baa6 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 29 Apr 2016 17:08:34 +0200 Subject: greybus: control: implement mode-switch operation Implement the unidirectional mode-switch operation. This operation will be used in the implementation of the new generic mode-switch functionality. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 7 +++++++ drivers/staging/greybus/control.h | 1 + drivers/staging/greybus/greybus_protocols.h | 1 + 3 files changed, 9 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 20aa366c3883..1b28899cd336 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -149,6 +149,13 @@ int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id) sizeof(request), NULL, 0); } +int gb_control_mode_switch_operation(struct gb_control *control) +{ + return gb_operation_unidirectional(control->connection, + GB_CONTROL_TYPE_MODE_SWITCH, + NULL, 0); +} + int gb_control_get_interface_version_operation(struct gb_interface *intf) { struct gb_control_interface_version_response response; diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 56fa2592762b..f108bcc2e059 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -36,6 +36,7 @@ void gb_control_put(struct gb_control *control); int gb_control_get_bundle_versions(struct gb_control *control); int gb_control_connected_operation(struct gb_control *control, u16 cport_id); int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id); +int gb_control_mode_switch_operation(struct gb_control *control); int gb_control_get_manifest_size_operation(struct gb_interface *intf); int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, size_t size); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 713b56989735..c074402633c6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -123,6 +123,7 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE 0x09 #define GB_CONTROL_TYPE_INTERFACE_VERSION 0x0a #define GB_CONTROL_TYPE_BUNDLE_VERSION 0x0b +#define GB_CONTROL_TYPE_MODE_SWITCH 0x0e struct gb_control_version_request { __u8 major; -- cgit v1.2.3-59-g8ed1b From b1f8bfea65183b4575e3dde64641f7340a3dfa38 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 29 Apr 2016 17:08:35 +0200 Subject: greybus: loopback: remove unsupported version request Remove the unsupported version request from the loopback-driver request handler. Unsupported requests are already handled and logged using the default case. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/loopback.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 5e009e1955ab..ccb2799c3b0a 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -800,9 +800,6 @@ static int gb_loopback_request_handler(struct gb_operation *operation) /* By convention, the AP initiates the version operation */ switch (operation->type) { - case GB_REQUEST_TYPE_PROTOCOL_VERSION: - dev_err(dev, "module-initiated version operation\n"); - return -EINVAL; case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_SINK: return 0; -- cgit v1.2.3-59-g8ed1b From a2cf2e594626ce8c1e6944f6a44a7d481b84b82d Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 29 Apr 2016 17:08:36 +0200 Subject: greybus: svc: define the version request Define the SVC version request, which need not need to stay the same as the legacy version request. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 14 ++++++++++---- drivers/staging/greybus/svc.c | 8 ++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c074402633c6..07988fb10e0d 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -781,6 +781,7 @@ struct gb_spi_transfer_response { #define GB_SVC_VERSION_MINOR 0x01 /* Greybus SVC request types */ +#define GB_SVC_TYPE_PROTOCOL_VERSION 0x01 #define GB_SVC_TYPE_SVC_HELLO 0x02 #define GB_SVC_TYPE_INTF_DEVICE_ID 0x03 #define GB_SVC_TYPE_INTF_HOTPLUG 0x04 @@ -808,10 +809,15 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_ACTIVATE 0x27 #define GB_SVC_TYPE_INTF_MAILBOX_EVENT 0x29 -/* - * SVC version request/response has the same payload as - * gb_protocol_version_request/response. - */ +struct gb_svc_version_request { + __u8 major; + __u8 minor; +} __packed; + +struct gb_svc_version_response { + __u8 major; + __u8 minor; +} __packed; /* SVC protocol hello request */ struct gb_svc_hello_request { diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 78fe4dc37131..577e680f6a90 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -465,8 +465,8 @@ static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_svc *svc = gb_connection_get_data(connection); - struct gb_protocol_version_request *request; - struct gb_protocol_version_response *response; + struct gb_svc_version_request *request; + struct gb_svc_version_response *response; if (op->request->payload_size < sizeof(*request)) { dev_err(&svc->dev, "short version request (%zu < %zu)\n", @@ -1192,7 +1192,7 @@ static int gb_svc_request_handler(struct gb_operation *op) * need to protect 'state' for any races. */ switch (type) { - case GB_REQUEST_TYPE_PROTOCOL_VERSION: + case GB_SVC_TYPE_PROTOCOL_VERSION: if (svc->state != GB_SVC_STATE_RESET) ret = -EINVAL; break; @@ -1213,7 +1213,7 @@ static int gb_svc_request_handler(struct gb_operation *op) } switch (type) { - case GB_REQUEST_TYPE_PROTOCOL_VERSION: + case GB_SVC_TYPE_PROTOCOL_VERSION: ret = gb_svc_version_request(op); if (!ret) svc->state = GB_SVC_STATE_PROTOCOL_VERSION; -- cgit v1.2.3-59-g8ed1b From 7adb32b429ce38bae39e277ae2cc37c93770104f Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 29 Apr 2016 17:08:37 +0200 Subject: greybus: operation: fix definition of the invalid type The invalid request type has been redefined as 0x7f. Also remove the redundant redefinition of the invalid type from the operation header. Note that operation type 0x00 has been repurposed for the new generic ping operation, which will be used to implement proper connection tear down. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 4 ++-- drivers/staging/greybus/operation.c | 12 +++++++----- drivers/staging/greybus/operation.h | 6 ------ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 07988fb10e0d..f1e8b345a761 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -95,9 +95,9 @@ struct gb_operation_msg_hdr { } __packed; -/* Generic request numbers supported by all modules */ -#define GB_REQUEST_TYPE_INVALID 0x00 +/* Generic request types */ #define GB_REQUEST_TYPE_PROTOCOL_VERSION 0x01 +#define GB_REQUEST_TYPE_INVALID 0x7f struct gb_protocol_version_request { __u8 major; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index fcae74793740..e7e9b0e72269 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -282,10 +282,10 @@ static void gb_operation_message_init(struct gb_host_device *hd, /* * The type supplied for incoming message buffers will be - * 0x00. Such buffers will be overwritten by arriving data - * so there's no need to initialize the message header. + * GB_REQUEST_TYPE_INVALID. Such buffers will be overwritten by + * arriving data so there's no need to initialize the message header. */ - if (type != GB_OPERATION_TYPE_INVALID) { + if (type != GB_REQUEST_TYPE_INVALID) { u16 message_size = (u16)(sizeof(*header) + payload_size); /* @@ -536,7 +536,7 @@ gb_operation_create_flags(struct gb_connection *connection, size_t response_size, unsigned long flags, gfp_t gfp) { - if (WARN_ON_ONCE(type == GB_OPERATION_TYPE_INVALID)) + if (WARN_ON_ONCE(type == GB_REQUEST_TYPE_INVALID)) return NULL; if (WARN_ON_ONCE(type & GB_MESSAGE_TYPE_RESPONSE)) type &= ~GB_MESSAGE_TYPE_RESPONSE; @@ -573,7 +573,9 @@ gb_operation_create_incoming(struct gb_connection *connection, u16 id, flags |= GB_OPERATION_FLAG_UNIDIRECTIONAL; operation = gb_operation_create_common(connection, type, - request_size, 0, flags, GFP_ATOMIC); + request_size, + GB_REQUEST_TYPE_INVALID, + flags, GFP_ATOMIC); if (!operation) return NULL; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 8ef8f514dafa..117d7df2e0bd 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -17,12 +17,6 @@ struct gb_operation; /* The default amount of time a request is given to complete */ #define GB_OPERATION_TIMEOUT_DEFAULT 1000 /* milliseconds */ -/* - * No protocol may define an operation that has numeric value 0x00. - * It is reserved as an explicitly invalid value. - */ -#define GB_OPERATION_TYPE_INVALID ((u8)0x00) - /* * The top bit of the type in an operation message header indicates * whether the message is a request (bit clear) or response (bit set) -- cgit v1.2.3-59-g8ed1b From cca22207673896262443c72f19b049552d65f88e Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 28 Apr 2016 10:06:38 +0530 Subject: greybus: firmware: Add firmware-download protocol driver This patch adds Firmware Download Protocol support to firmware core, which allows an Interface to download a firmware package over Unipro. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Jun Li <li_jun@projectara.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/firmware.h | 20 ++ drivers/staging/greybus/fw-core.c | 37 +++- drivers/staging/greybus/fw-download.c | 299 ++++++++++++++++++++++++++++ drivers/staging/greybus/greybus_protocols.h | 37 ++++ 5 files changed, 392 insertions(+), 3 deletions(-) create mode 100644 drivers/staging/greybus/firmware.h create mode 100644 drivers/staging/greybus/fw-download.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1a120d928485..43c0c9910de1 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -39,7 +39,7 @@ gb-audio-apbridgea-y := audio_apbridgea.o gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o -gb-firmware-y := fw-core.o +gb-firmware-y := fw-core.o fw-download.o obj-m += greybus.o obj-m += gb-phy.o diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h new file mode 100644 index 000000000000..c5736d5f96a2 --- /dev/null +++ b/drivers/staging/greybus/firmware.h @@ -0,0 +1,20 @@ +/* + * Greybus Firmware Management Header + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __FIRMWARE_H +#define __FIRMWARE_H + +#include "greybus.h" + +/* Firmware Download Protocol specific functions */ +int gb_fw_download_request_handler(struct gb_operation *op); +int gb_fw_download_connection_init(struct gb_connection *connection); +void gb_fw_download_connection_exit(struct gb_connection *connection); + +#endif /* __FIRMWARE_H */ diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c index 4720d595cc2b..a6865276855a 100644 --- a/drivers/staging/greybus/fw-core.c +++ b/drivers/staging/greybus/fw-core.c @@ -9,9 +9,11 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/firmware.h> +#include "firmware.h" #include "greybus.h" struct gb_fw_core { + struct gb_connection *download_connection; struct gb_connection *mgmt_connection; }; @@ -52,10 +54,29 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, dev_err(&bundle->dev, "failed to create management connection (%d)\n", ret); - goto err_free_fw_core; + goto err_destroy_connections; } fw_core->mgmt_connection = connection; + break; + case GREYBUS_PROTOCOL_FW_DOWNLOAD: + /* Disallow multiple Firmware Download CPorts */ + if (fw_core->download_connection) { + dev_err(&bundle->dev, + "multiple download CPorts found\n"); + ret = -EINVAL; + goto err_destroy_connections; + } + + connection = gb_connection_create(bundle, cport_id, + gb_fw_download_request_handler); + if (IS_ERR(connection)) { + dev_err(&bundle->dev, "failed to create download connection (%ld)\n", + PTR_ERR(connection)); + } else { + fw_core->download_connection = connection; + } + break; default: dev_err(&bundle->dev, "invalid protocol id (0x%02x)\n", @@ -69,7 +90,16 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, if (!fw_core->mgmt_connection) { dev_err(&bundle->dev, "missing management connection\n"); ret = -ENODEV; - goto err_free_fw_core; + goto err_destroy_connections; + } + + ret = gb_fw_download_connection_init(fw_core->download_connection); + if (ret) { + /* We may still be able to work with the Interface */ + dev_err(&bundle->dev, "failed to initialize firmware download connection, disable it (%d)\n", + ret); + gb_connection_destroy(fw_core->download_connection); + fw_core->download_connection = NULL; } greybus_set_drvdata(bundle, fw_core); @@ -77,6 +107,7 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, return 0; err_destroy_connections: + gb_connection_destroy(fw_core->download_connection); gb_connection_destroy(fw_core->mgmt_connection); err_free_fw_core: kfree(fw_core); @@ -88,6 +119,8 @@ static void gb_fw_core_disconnect(struct gb_bundle *bundle) { struct gb_fw_core *fw_core = greybus_get_drvdata(bundle); + gb_fw_download_connection_exit(fw_core->download_connection); + gb_connection_destroy(fw_core->download_connection); gb_connection_destroy(fw_core->mgmt_connection); kfree(fw_core); diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c new file mode 100644 index 000000000000..836b02b9d7c6 --- /dev/null +++ b/drivers/staging/greybus/fw-download.c @@ -0,0 +1,299 @@ +/* + * Greybus Firmware Download Protocol Driver. + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include <linux/firmware.h> +#include "firmware.h" +#include "greybus.h" + +struct fw_request { + u8 firmware_id; + char name[56]; /* ara_%08x_%08x_%08x_%08x_%s.tftf */ + const struct firmware *fw; + struct list_head node; +}; + +struct fw_download { + struct device *parent; + struct gb_connection *connection; + struct list_head fw_requests; + struct ida id_map; +}; + +static struct fw_request *match_firmware(struct fw_download *fw_download, + u8 firmware_id) +{ + struct fw_request *fw_req; + + list_for_each_entry(fw_req, &fw_download->fw_requests, node) { + if (fw_req->firmware_id == firmware_id) + return fw_req; + } + + return NULL; +} + +static void free_firmware(struct fw_download *fw_download, + struct fw_request *fw_req) +{ + list_del(&fw_req->node); + release_firmware(fw_req->fw); + ida_simple_remove(&fw_download->id_map, fw_req->firmware_id); + kfree(fw_req); +} + +/* This returns path of the firmware blob on the disk */ +static struct fw_request *find_firmware(struct fw_download *fw_download, + const char *tag) +{ + struct gb_interface *intf = fw_download->connection->bundle->intf; + struct fw_request *fw_req; + int ret; + + fw_req = kzalloc(sizeof(*fw_req), GFP_KERNEL); + if (!fw_req) + return ERR_PTR(-ENOMEM); + + /* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */ + ret = ida_simple_get(&fw_download->id_map, 1, 256, GFP_KERNEL); + if (ret < 0) { + dev_err(fw_download->parent, + "failed to allocate firmware id (%d)\n", ret); + goto err_free_req; + } + fw_req->firmware_id = ret; + + snprintf(fw_req->name, sizeof(fw_req->name), + "ara_%08x_%08x_%08x_%08x_%s.tftf", + intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, + intf->vendor_id, intf->product_id, tag); + + dev_info(fw_download->parent, "Requested firmware package '%s'\n", + fw_req->name); + + ret = request_firmware(&fw_req->fw, fw_req->name, fw_download->parent); + if (ret) { + dev_err(fw_download->parent, + "firmware request failed for %s (%d)\n", fw_req->name, + ret); + goto err_free_id; + } + + list_add(&fw_req->node, &fw_download->fw_requests); + + return fw_req; + +err_free_id: + ida_simple_remove(&fw_download->id_map, fw_req->firmware_id); +err_free_req: + kfree(fw_req); + + return ERR_PTR(ret); +} + +static int fw_download_find_firmware(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct fw_download *fw_download = gb_connection_get_data(connection); + struct gb_fw_download_find_firmware_request *request; + struct gb_fw_download_find_firmware_response *response; + struct fw_request *fw_req; + const char *tag; + + if (op->request->payload_size != sizeof(*request)) { + dev_err(fw_download->parent, + "illegal size of find firmware request (%zu != %zu)\n", + op->request->payload_size, sizeof(*request)); + return -EINVAL; + } + + request = op->request->payload; + tag = (const char *)(request->firmware_tag); + + /* firmware_tag should be null-terminated */ + if (strnlen(tag, GB_FIRMWARE_TAG_MAX_LEN) == GB_FIRMWARE_TAG_MAX_LEN) { + dev_err(fw_download->parent, + "firmware-tag is not null-terminated\n"); + return -EINVAL; + } + + fw_req = find_firmware(fw_download, request->firmware_tag); + if (IS_ERR(fw_req)) + return PTR_ERR(fw_req); + + if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) { + dev_err(fw_download->parent, "error allocating response\n"); + free_firmware(fw_download, fw_req); + return -ENOMEM; + } + + response = op->response->payload; + response->firmware_id = fw_req->firmware_id; + response->size = cpu_to_le32(fw_req->fw->size); + + dev_dbg(fw_download->parent, + "firmware size is %zu bytes\n", fw_req->fw->size); + + return 0; +} + +static int fw_download_fetch_firmware(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct fw_download *fw_download = gb_connection_get_data(connection); + struct gb_fw_download_fetch_firmware_request *request; + struct gb_fw_download_fetch_firmware_response *response; + struct fw_request *fw_req; + const struct firmware *fw; + unsigned int offset, size; + u8 firmware_id; + + if (op->request->payload_size != sizeof(*request)) { + dev_err(fw_download->parent, + "Illegal size of fetch firmware request (%zu %zu)\n", + op->request->payload_size, sizeof(*request)); + return -EINVAL; + } + + request = op->request->payload; + offset = le32_to_cpu(request->offset); + size = le32_to_cpu(request->size); + firmware_id = request->firmware_id; + + fw_req = match_firmware(fw_download, firmware_id); + if (!fw_req) { + dev_err(fw_download->parent, + "firmware not available for id: %02u\n", firmware_id); + return -EINVAL; + } + + fw = fw_req->fw; + + if (offset >= fw->size || size > fw->size - offset) { + dev_err(fw_download->parent, + "bad fetch firmware request (offs = %u, size = %u)\n", + offset, size); + return -EINVAL; + } + + if (!gb_operation_response_alloc(op, sizeof(*response) + size, + GFP_KERNEL)) { + dev_err(fw_download->parent, + "error allocating fetch firmware response\n"); + return -ENOMEM; + } + + response = op->response->payload; + memcpy(response->data, fw->data + offset, size); + + dev_dbg(fw_download->parent, + "responding with firmware (offs = %u, size = %u)\n", offset, + size); + + return 0; +} + +static int fw_download_release_firmware(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct fw_download *fw_download = gb_connection_get_data(connection); + struct gb_fw_download_release_firmware_request *request; + struct fw_request *fw_req; + u8 firmware_id; + + if (op->request->payload_size != sizeof(*request)) { + dev_err(fw_download->parent, + "Illegal size of release firmware request (%zu %zu)\n", + op->request->payload_size, sizeof(*request)); + return -EINVAL; + } + + request = op->request->payload; + firmware_id = request->firmware_id; + + fw_req = match_firmware(fw_download, firmware_id); + if (!fw_req) { + dev_err(fw_download->parent, + "firmware not available for id: %02u\n", firmware_id); + return -EINVAL; + } + + free_firmware(fw_download, fw_req); + + dev_dbg(fw_download->parent, "release firmware\n"); + + return 0; +} + +int gb_fw_download_request_handler(struct gb_operation *op) +{ + u8 type = op->type; + + switch (type) { + case GB_FW_DOWNLOAD_TYPE_FIND_FIRMWARE: + return fw_download_find_firmware(op); + case GB_FW_DOWNLOAD_TYPE_FETCH_FIRMWARE: + return fw_download_fetch_firmware(op); + case GB_FW_DOWNLOAD_TYPE_RELEASE_FIRMWARE: + return fw_download_release_firmware(op); + default: + dev_err(&op->connection->bundle->dev, + "unsupported request: %u\n", type); + return -EINVAL; + } +} + +int gb_fw_download_connection_init(struct gb_connection *connection) +{ + struct fw_download *fw_download; + int ret; + + if (!connection) + return 0; + + fw_download = kzalloc(sizeof(*fw_download), GFP_KERNEL); + if (!fw_download) + return -ENOMEM; + + fw_download->parent = &connection->bundle->dev; + INIT_LIST_HEAD(&fw_download->fw_requests); + ida_init(&fw_download->id_map); + gb_connection_set_data(connection, fw_download); + fw_download->connection = connection; + + ret = gb_connection_enable(connection); + if (ret) + goto err_destroy_id_map; + + return 0; + +err_destroy_id_map: + ida_destroy(&fw_download->id_map); + kfree(fw_download); + + return ret; +} + +void gb_fw_download_connection_exit(struct gb_connection *connection) +{ + struct fw_download *fw_download; + struct fw_request *fw_req, *tmp; + + if (!connection) + return; + + fw_download = gb_connection_get_data(connection); + gb_connection_disable(fw_download->connection); + + /* Release pending firmware packages */ + list_for_each_entry_safe(fw_req, tmp, &fw_download->fw_requests, node) + free_firmware(fw_download, fw_req); + + ida_destroy(&fw_download->id_map); + kfree(fw_download); +} diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index f1e8b345a761..b73acc0c3ee5 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -213,6 +213,43 @@ struct gb_control_timesync_authoritative_request { #define GB_APB_REQUEST_CPORT_FEAT_EN 0x0b #define GB_APB_REQUEST_CPORT_FEAT_DIS 0x0c +/* Firmware Download Protocol */ + +/* Request Types */ +#define GB_FW_DOWNLOAD_TYPE_FIND_FIRMWARE 0x01 +#define GB_FW_DOWNLOAD_TYPE_FETCH_FIRMWARE 0x02 +#define GB_FW_DOWNLOAD_TYPE_RELEASE_FIRMWARE 0x03 + +#define GB_FIRMWARE_TAG_MAX_LEN 10 + +/* firmware download find firmware request/response */ +struct gb_fw_download_find_firmware_request { + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; +} __packed; + +struct gb_fw_download_find_firmware_response { + __u8 firmware_id; + __le32 size; +} __packed; + +/* firmware download fetch firmware request/response */ +struct gb_fw_download_fetch_firmware_request { + __u8 firmware_id; + __le32 offset; + __le32 size; +} __packed; + +struct gb_fw_download_fetch_firmware_response { + __u8 data[0]; +} __packed; + +/* firmware download release firmware request */ +struct gb_fw_download_release_firmware_request { + __u8 firmware_id; +} __packed; +/* firmware download release firmware response has no payload */ + + /* Bootrom Protocol */ /* Version of the Greybus bootrom protocol we support */ -- cgit v1.2.3-59-g8ed1b From 7557d0481bdeed86780c073e591df0330ec88a9e Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 2 May 2016 10:51:49 +0530 Subject: greybus: fw-download: Create a macro for length of firmware file's name Create a macro representing the length of the firmware file's name and use that instead of using magic number in the code. Suggested-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-download.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 836b02b9d7c6..e60abde2d704 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -11,9 +11,12 @@ #include "firmware.h" #include "greybus.h" +/* Length of the string in format: ara_%08x_%08x_%08x_%08x_%s.tftf */ +#define FW_NAME_LEN 56 + struct fw_request { u8 firmware_id; - char name[56]; /* ara_%08x_%08x_%08x_%08x_%s.tftf */ + char name[FW_NAME_LEN]; const struct firmware *fw; struct list_head node; }; -- cgit v1.2.3-59-g8ed1b From 84e0e38744c5d814650e4acec34cea585d04cc96 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 4 May 2016 16:29:19 +0530 Subject: greybus: audio:gb_manager: Use proper locking around kobject_xxx read/write_lock_irqsave mechanism was used to protect modules list & kobject_xxx() in gb_audio_manager. Since kobject_xxx calls can sleep spin_lock variants can't be used there. So use rw_sem for protecting modules_list. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_manager.c | 37 +++++++++++++-------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c index bc5be4907c81..619bdee46ee5 100644 --- a/drivers/staging/greybus/audio_manager.c +++ b/drivers/staging/greybus/audio_manager.c @@ -19,7 +19,7 @@ static struct kset *manager_kset; static LIST_HEAD(modules_list); -static DEFINE_RWLOCK(modules_lock); +static DECLARE_RWSEM(modules_rwsem); static DEFINE_IDA(module_id); /* helpers */ @@ -42,7 +42,6 @@ static struct gb_audio_manager_module *gb_audio_manager_get_locked(int id) int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc) { struct gb_audio_manager_module *module; - unsigned long flags; int id; int err; @@ -55,9 +54,9 @@ int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc) } /* Add it to the list */ - write_lock_irqsave(&modules_lock, flags); + down_write(&modules_rwsem); list_add_tail(&module->list, &modules_list); - write_unlock_irqrestore(&modules_lock, flags); + up_write(&modules_rwsem); return module->id; } @@ -66,20 +65,18 @@ EXPORT_SYMBOL_GPL(gb_audio_manager_add); int gb_audio_manager_remove(int id) { struct gb_audio_manager_module *module; - unsigned long flags; - write_lock_irqsave(&modules_lock, flags); + down_write(&modules_rwsem); module = gb_audio_manager_get_locked(id); if (!module) { - write_unlock_irqrestore(&modules_lock, flags); + up_write(&modules_rwsem); return -EINVAL; } - - ida_simple_remove(&module_id, module->id); list_del(&module->list); kobject_put(&module->kobj); - write_unlock_irqrestore(&modules_lock, flags); + up_write(&modules_rwsem); + ida_simple_remove(&module_id, module->id); return 0; } EXPORT_SYMBOL_GPL(gb_audio_manager_remove); @@ -88,9 +85,8 @@ void gb_audio_manager_remove_all(void) { struct gb_audio_manager_module *module, *next; int is_empty = 1; - unsigned long flags; - write_lock_irqsave(&modules_lock, flags); + down_write(&modules_rwsem); list_for_each_entry_safe(module, next, &modules_list, list) { list_del(&module->list); @@ -99,7 +95,7 @@ void gb_audio_manager_remove_all(void) is_empty = list_empty(&modules_list); - write_unlock_irqrestore(&modules_lock, flags); + up_write(&modules_rwsem); if (!is_empty) pr_warn("Not all nodes were deleted\n"); @@ -109,12 +105,11 @@ EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all); struct gb_audio_manager_module *gb_audio_manager_get_module(int id) { struct gb_audio_manager_module *module; - unsigned long flags; - read_lock_irqsave(&modules_lock, flags); + down_read(&modules_rwsem); module = gb_audio_manager_get_locked(id); kobject_get(&module->kobj); - read_unlock_irqrestore(&modules_lock, flags); + up_read(&modules_rwsem); return module; } EXPORT_SYMBOL_GPL(gb_audio_manager_get_module); @@ -128,11 +123,10 @@ EXPORT_SYMBOL_GPL(gb_audio_manager_put_module); int gb_audio_manager_dump_module(int id) { struct gb_audio_manager_module *module; - unsigned long flags; - read_lock_irqsave(&modules_lock, flags); + down_read(&modules_rwsem); module = gb_audio_manager_get_locked(id); - read_unlock_irqrestore(&modules_lock, flags); + up_read(&modules_rwsem); if (!module) return -EINVAL; @@ -146,14 +140,13 @@ void gb_audio_manager_dump_all(void) { struct gb_audio_manager_module *module; int count = 0; - unsigned long flags; - read_lock_irqsave(&modules_lock, flags); + down_read(&modules_rwsem); list_for_each_entry(module, &modules_list, list) { gb_audio_manager_module_dump(module); count++; } - read_unlock_irqrestore(&modules_lock, flags); + up_read(&modules_rwsem); pr_info("Number of connected modules: %d\n", count); } -- cgit v1.2.3-59-g8ed1b From e6ab7a154c5dbfc0ad08bf1570efea22f2c7ee6c Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 4 May 2016 16:29:20 +0530 Subject: greybus: audio: Cleanup GB Audio bundle driver. Remove unnecessary lock & list maintained in GB audio bundle driver Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 05bda486f490..1e29901024cd 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -14,9 +14,6 @@ #include "audio_apbridgea.h" #include "audio_manager.h" -static DEFINE_MUTEX(gb_codec_list_lock); -static LIST_HEAD(gb_codec_list); - /* * gb_snd management functions */ @@ -241,16 +238,13 @@ static int gb_audio_probe(struct gb_bundle *bundle, if (bundle->num_cports < 2) return -ENODEV; - mutex_lock(&gb_codec_list_lock); /* * There can be only one Management connection and any number of data * connections. */ gbmodule = devm_kzalloc(dev, sizeof(*gbmodule), GFP_KERNEL); - if (!gbmodule) { - mutex_unlock(&gb_codec_list_lock); + if (!gbmodule) return -ENOMEM; - } gbmodule->num_data_connections = bundle->num_cports - 1; mutex_init(&gbmodule->lock); @@ -348,7 +342,6 @@ static int gb_audio_probe(struct gb_bundle *bundle, gbmodule->manager_id = gb_audio_manager_add(&desc); dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name); - mutex_unlock(&gb_codec_list_lock); return 0; @@ -378,7 +371,6 @@ destroy_connections: gb_connection_destroy(gbmodule->mgmt_connection); devm_kfree(dev, gbmodule); - mutex_unlock(&gb_codec_list_lock); return ret; } @@ -388,8 +380,6 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); struct gbaudio_data_connection *dai, *_dai; - mutex_lock(&gb_codec_list_lock); - gbaudio_unregister_module(gbmodule); /* inform uevent to above layers */ @@ -409,7 +399,6 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) gbmodule->mgmt_connection = NULL; devm_kfree(&bundle->dev, gbmodule); - mutex_unlock(&gb_codec_list_lock); } static const struct greybus_bundle_id gb_audio_id_table[] = { -- cgit v1.2.3-59-g8ed1b From 852859ab8186e7a492fdec5db5ef219bf625c21a Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 4 May 2016 16:29:21 +0530 Subject: greybus: audio: Remove redundant lock protection & is_connected field Each module maintains connected status & a lock to protect it. Using codec->lock we can safely serialize ASoC specific callbacks (in response to mixer_ctl update or dai_ops) and gb module disconnect. Thus is_connected field can be removed. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 56 ---------------------------------- drivers/staging/greybus/audio_codec.h | 2 -- drivers/staging/greybus/audio_module.c | 9 ------ 3 files changed, 67 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 7a9abbf0ed5a..c50720ee30fe 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -60,7 +60,6 @@ static int gbaudio_module_disable(struct gbaudio_codec_info *codec, dai_name = codec->stream[dir].dai_name; - mutex_lock(&module->lock); module_state = module->ctrlstate[dir]; if (module_state == GBAUDIO_CODEC_SHUTDOWN) { dev_dbg(codec->dev, "%s: module already configured\n", @@ -129,7 +128,6 @@ static int gbaudio_module_disable(struct gbaudio_codec_info *codec, module->ctrlstate[dir] = GBAUDIO_CODEC_SHUTDOWN; func_exit: - mutex_unlock(&module->lock); mutex_unlock(&codec->lock); return ret; } @@ -160,7 +158,6 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, rate = codec->stream[dir].rate; sig_bits = codec->stream[dir].sig_bits; - mutex_lock(&module->lock); module_state = module->ctrlstate[dir]; if (module_state == codec_state) { dev_dbg(codec->dev, "%s: module already configured\n", @@ -272,7 +269,6 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, func_exit: module->ctrlstate[dir] = module_state; - mutex_unlock(&module->lock); mutex_unlock(&codec->lock); return ret; } @@ -342,18 +338,11 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, state = codec->stream[substream->stream].state; list_for_each_entry(module, &codec->module_list, list) { - mutex_lock(&module->lock); - if (!module->is_connected) { - mutex_unlock(&module->lock); - continue; - } - /* find the dai */ data = find_data(module, dai->name); if (!data) { dev_err(dai->dev, "%s:%s DATA connection missing\n", dai->name, module->name); - mutex_unlock(&module->lock); continue; } @@ -375,7 +364,6 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, break; default: dev_err(dai->dev, "Inavlid stream\n"); - mutex_unlock(&module->lock); mutex_unlock(&codec->lock); return -EINVAL; } @@ -384,7 +372,6 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, state = GBAUDIO_CODEC_STARTUP; module->ctrlstate[substream->stream] = state; dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); - mutex_unlock(&module->lock); } codec->stream[substream->stream].state = state; codec->stream[substream->stream].dai_name = dai->name; @@ -492,19 +479,11 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, state = codec->stream[substream->stream].state; list_for_each_entry(module, &codec->module_list, list) { - mutex_lock(&module->lock); - if (!module->is_connected) { - dev_err(dai->dev, "%s:%s module not connected\n", - __func__, module->name); - mutex_unlock(&module->lock); - continue; - } /* find the dai */ data = find_data(module, dai->name); if (!data) { dev_err(dai->dev, "%s:%s DATA connection missing\n", dai->name, module->name); - mutex_unlock(&module->lock); continue; } @@ -523,7 +502,6 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, state = GBAUDIO_CODEC_SHUTDOWN; module->ctrlstate[substream->stream] = state; dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); - mutex_unlock(&module->lock); } codec->stream[substream->stream].state = state; codec->stream[substream->stream].dai_name = NULL; @@ -583,21 +561,11 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, state = codec->stream[substream->stream].state; list_for_each_entry(module, &codec->module_list, list) { - mutex_lock(&module->lock); - if (!module->is_connected) { - dev_err(dai->dev, "%s:%s module not connected\n", - __func__, module->name); - ret = -ENODEV; - mutex_unlock(&module->lock); - continue; - } - /* find the data connection */ data = find_data(module, dai->name); if (!data) { dev_err(dai->dev, "%s:%s DATA connection missing\n", dai->name, module->name); - mutex_unlock(&module->lock); continue; } @@ -612,7 +580,6 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, format, rate, channels, sig_bits); if (ret) { dev_err(dai->dev, "%d: Error during set_pcm\n", ret); - mutex_unlock(&module->lock); goto func_exit; } if (state < GBAUDIO_CODEC_HWPARAMS) { @@ -623,14 +590,12 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, if (ret) { dev_err(dai->dev, "%d: Error during set_config\n", ret); - mutex_unlock(&module->lock); goto func_exit; } } state = GBAUDIO_CODEC_HWPARAMS; module->ctrlstate[substream->stream] = state; dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); - mutex_unlock(&module->lock); } codec->stream[substream->stream].state = state; codec->stream[substream->stream].format = format; @@ -731,18 +696,11 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, state = codec->stream[substream->stream].state; list_for_each_entry(module, &codec->module_list, list) { - mutex_lock(&module->lock); - if (!module->is_connected) { - mutex_unlock(&module->lock); - continue; - } - /* find the dai */ data = find_data(module, dai->name); if (!data) { dev_err(dai->dev, "%s:%s DATA connection missing\n", dai->name, module->name); - mutex_unlock(&module->lock); continue; } @@ -759,14 +717,12 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, if (ret == -ENODEV) continue; if (ret) { - mutex_unlock(&module->lock); goto func_exit; } state = GBAUDIO_CODEC_PREPARE; module->ctrlstate[substream->stream] = state; dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); - mutex_unlock(&module->lock); } codec->stream[substream->stream].state = state; @@ -824,12 +780,6 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, } list_for_each_entry(module, &codec->module_list, list) { - mutex_lock(&module->lock); - if (!module->is_connected) { - mutex_unlock(&module->lock); - continue; - } - /* find the dai */ data = find_data(module, dai->name); if (data) @@ -839,7 +789,6 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(dai->dev, "%s:%s DATA connection missing\n", dai->name, module->name); ret = -ENODEV; - mutex_unlock(&module->lock); goto func_exit; } if (start && tx) { @@ -873,7 +822,6 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, if (ret) dev_err(dai->dev, "%s:Error during %s stream:%d\n", module->name, start ? "Start" : "Stop", ret); - mutex_unlock(&module->lock); func_exit: mutex_unlock(&codec->lock); @@ -1118,7 +1066,6 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) down_write(&card->controls_rwsem); mutex_lock(&gbcodec->lock); dev_dbg(codec->dev, "Process Unregister %s module\n", module->name); - mutex_lock(&module->lock); #ifdef CONFIG_SND_JACK /* free jack devices for this module from codec->jack_list */ @@ -1133,7 +1080,6 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) gbaudio_codec_cleanup(module); - module->is_connected = 0; if (module->dapm_routes) { dev_dbg(codec->dev, "Removing %d routes\n", module->num_dapm_routes); @@ -1153,8 +1099,6 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) module->num_dapm_widgets); } - mutex_unlock(&module->lock); - list_del(&module->list); dev_dbg(codec->dev, "Unregistered %s module\n", module->name); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 6182b20c5a27..5a19467c1623 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -172,8 +172,6 @@ struct gbaudio_module_info { struct snd_soc_jack button_jack; /* used by codec_ops */ - struct mutex lock; - int is_connected; int ctrlstate[2]; /* PB/CAP */ /* connection info */ diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 1e29901024cd..3b763710ddf6 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -26,12 +26,10 @@ static int gbaudio_request_jack(struct gbaudio_module_info *module, dev_warn(module->dev, "Jack Event received: type: %u, event: %u\n", req->jack_attribute, req->event); - mutex_lock(&module->lock); if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) { module->jack_type = 0; button_status = module->button_status; module->button_status = 0; - mutex_unlock(&module->lock); if (button_status) snd_soc_jack_report(&module->button_jack, 0, GBCODEC_JACK_BUTTON_MASK); @@ -48,7 +46,6 @@ static int gbaudio_request_jack(struct gbaudio_module_info *module, module->jack_type, report); module->jack_type = report; - mutex_unlock(&module->lock); snd_soc_jack_report(&module->headset_jack, report, GBCODEC_JACK_MASK); return 0; @@ -63,10 +60,8 @@ static int gbaudio_request_button(struct gbaudio_module_info *module, req->button_id, req->event); /* currently supports 4 buttons only */ - mutex_lock(&module->lock); if (!module->jack_type) { dev_err(module->dev, "Jack not present. Bogus event!!\n"); - mutex_unlock(&module->lock); return -EINVAL; } @@ -100,8 +95,6 @@ static int gbaudio_request_button(struct gbaudio_module_info *module, module->button_status = report; - mutex_unlock(&module->lock); - snd_soc_jack_report(&module->button_jack, report, GBCODEC_JACK_BUTTON_MASK); @@ -247,7 +240,6 @@ static int gb_audio_probe(struct gb_bundle *bundle, return -ENOMEM; gbmodule->num_data_connections = bundle->num_cports - 1; - mutex_init(&gbmodule->lock); INIT_LIST_HEAD(&gbmodule->data_list); INIT_LIST_HEAD(&gbmodule->widget_list); INIT_LIST_HEAD(&gbmodule->ctl_list); @@ -327,7 +319,6 @@ static int gb_audio_probe(struct gb_bundle *bundle, if (ret) goto disable_data_connection; } - gbmodule->is_connected = 1; /* inform above layer for uevent */ dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); -- cgit v1.2.3-59-g8ed1b From bb9986ef6b311fb3f4de2150bd050c1d9e1d9d65 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 4 May 2016 16:29:22 +0530 Subject: greybus: audio: Reorganize sequence in GB audio bundle & codec driver Modify sequence of register_module & unregister_module in bundle driver. This would affect the uevent generated for above user space. Accordingly, we need to modify snd_soc_xxx sequence in register_module() in codec driver. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 34 +++++++++++++++------------------- drivers/staging/greybus/audio_module.c | 21 +++++++++++---------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index c50720ee30fe..dbcff218badd 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -963,27 +963,27 @@ int gbaudio_register_module(struct gbaudio_module_info *module) /* card already instantiated, create widgets here only */ if (codec->card->instantiated) { - ret = snd_soc_dapm_new_widgets(&codec->dapm); - if (!ret) - snd_soc_dapm_link_component_dai_widgets(codec->card, - &codec->dapm); - } - + snd_soc_dapm_link_component_dai_widgets(codec->card, + &codec->dapm); #ifdef CONFIG_SND_JACK - /* register jack devices for this module from codec->jack_list */ - list_for_each_entry(jack, &codec->jack_list, list) { - if ((jack == &module->headset_jack) - || (jack == &module->button_jack)) - snd_device_register(codec->card->snd_card, jack->jack); - } + /* register jack devices for this module from codec->jack_list */ + list_for_each_entry(jack, &codec->jack_list, list) { + if ((jack == &module->headset_jack) + || (jack == &module->button_jack)) + snd_device_register(codec->card->snd_card, + jack->jack); + } #endif + } list_add(&module->list, &gbcodec->module_list); + if (codec->card->instantiated) + ret = snd_soc_dapm_new_widgets(&codec->dapm); dev_dbg(codec->dev, "Registered %s module\n", module->name); mutex_unlock(&gbcodec->lock); up_write(&card->controls_rwsem); - return 0; + return ret; } EXPORT_SYMBOL(gbaudio_register_module); @@ -1060,11 +1060,10 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) dev_dbg(codec->dev, "Unregister %s module\n", module->name); - /* complete widget processing, if ongoing */ - snd_soc_dapm_sync(&codec->dapm); - down_write(&card->controls_rwsem); mutex_lock(&gbcodec->lock); + gbaudio_codec_cleanup(module); + list_del(&module->list); dev_dbg(codec->dev, "Process Unregister %s module\n", module->name); #ifdef CONFIG_SND_JACK @@ -1078,8 +1077,6 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) } #endif - gbaudio_codec_cleanup(module); - if (module->dapm_routes) { dev_dbg(codec->dev, "Removing %d routes\n", module->num_dapm_routes); @@ -1099,7 +1096,6 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) module->num_dapm_widgets); } - list_del(&module->list); dev_dbg(codec->dev, "Unregistered %s module\n", module->name); mutex_unlock(&gbcodec->lock); diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 3b763710ddf6..27e5ff19dca6 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -308,18 +308,22 @@ static int gb_audio_probe(struct gb_bundle *bundle, } gbmodule->topology = topology; - /* register module with gbcodec */ - ret = gbaudio_register_module(gbmodule); - if (ret) - goto release_topology; - /* Initialize data connections */ list_for_each_entry(dai, &gbmodule->data_list, list) { ret = gb_connection_enable(dai->connection); - if (ret) + if (ret) { + dev_err(dev, + "%d:Error while enabling %d:data connection\n", + ret, dai->data_cport); goto disable_data_connection; + } } + /* register module with gbcodec */ + ret = gbaudio_register_module(gbmodule); + if (ret) + goto disable_data_connection; + /* inform above layer for uevent */ dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); /* prepare for the audio manager */ @@ -339,9 +343,6 @@ static int gb_audio_probe(struct gb_bundle *bundle, disable_data_connection: list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) gb_connection_disable(dai->connection); - gbaudio_unregister_module(gbmodule); - -release_topology: gbaudio_tplg_release(gbmodule); gbmodule->topology = NULL; @@ -371,11 +372,11 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); struct gbaudio_data_connection *dai, *_dai; - gbaudio_unregister_module(gbmodule); /* inform uevent to above layers */ gb_audio_manager_remove(gbmodule->manager_id); + gbaudio_unregister_module(gbmodule); gbaudio_tplg_release(gbmodule); gbmodule->topology = NULL; kfree(gbmodule->topology); -- cgit v1.2.3-59-g8ed1b From c188fdc8176a8cb18b5d81cccac590311251208e Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 4 May 2016 16:29:23 +0530 Subject: greybus: audio: Reduce codec->lock granularity Earlier codec->lock protects almost complete register/unregister module function. This can be reduced to specific operations. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index dbcff218badd..23dde708c76f 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -933,20 +933,17 @@ int gbaudio_register_module(struct gbaudio_module_info *module) card = codec->card->snd_card; down_write(&card->controls_rwsem); - mutex_lock(&gbcodec->lock); if (module->num_dais) { dev_err(gbcodec->dev, "%d:DAIs not supported via gbcodec driver\n", module->num_dais); - mutex_unlock(&gbcodec->lock); up_write(&card->controls_rwsem); return -EINVAL; } ret = gbaudio_init_jack(module, codec); if (ret) { - mutex_unlock(&gbcodec->lock); up_write(&card->controls_rwsem); return ret; } @@ -976,12 +973,14 @@ int gbaudio_register_module(struct gbaudio_module_info *module) #endif } + mutex_lock(&gbcodec->lock); list_add(&module->list, &gbcodec->module_list); + mutex_unlock(&gbcodec->lock); + if (codec->card->instantiated) ret = snd_soc_dapm_new_widgets(&codec->dapm); dev_dbg(codec->dev, "Registered %s module\n", module->name); - mutex_unlock(&gbcodec->lock); up_write(&card->controls_rwsem); return ret; } @@ -1065,6 +1064,7 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) gbaudio_codec_cleanup(module); list_del(&module->list); dev_dbg(codec->dev, "Process Unregister %s module\n", module->name); + mutex_unlock(&gbcodec->lock); #ifdef CONFIG_SND_JACK /* free jack devices for this module from codec->jack_list */ @@ -1098,7 +1098,6 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) dev_dbg(codec->dev, "Unregistered %s module\n", module->name); - mutex_unlock(&gbcodec->lock); up_write(&card->controls_rwsem); } EXPORT_SYMBOL(gbaudio_unregister_module); -- cgit v1.2.3-59-g8ed1b From 0634874a584e8bd875a83850c656e5adefc53bde Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 4 May 2016 16:29:24 +0530 Subject: greybus: audio: Use standard API to set connections' private data Use standard API greybus_set_drvdata() while setting private data pointers for mgmt & data connection. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 27e5ff19dca6..d2dd5b82ce41 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -173,7 +173,7 @@ static int gb_audio_add_mgmt_connection(struct gbaudio_module_info *gbmodule, if (IS_ERR(connection)) return PTR_ERR(connection); - connection->private = gbmodule; + greybus_set_drvdata(bundle, gbmodule); gbmodule->mgmt_connection = connection; return 0; @@ -201,7 +201,7 @@ static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule, return PTR_ERR(connection); } - connection->private = gbmodule; + greybus_set_drvdata(bundle, gbmodule); /* dai->name should be same as codec->dai_name */ strlcpy(dai->name, "greybus-apb1", NAME_SIZE); dai->data_cport = connection->intf_cport_id; -- cgit v1.2.3-59-g8ed1b From c81b1ec7853d083981b6edd1d7397d94b88dd597 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Tue, 3 May 2016 16:13:12 +0530 Subject: greybus: bootrom: Implement timeouts to detect Module failures Its possible that the Module may fail to download the next stage firmware, or to jump into it and boot into the new personality. We have already seen examples of both of these cases on EVT 1.5. This patch implements timeouts in the bootrom bundle driver, which now expects the next request from the Module to be received at the AP within 1 second of the previous request/response. The time interval can be increased later if required. The timeouts are added between: - AP_READY and FIRMWARE_SIZE operations - FIRMWARE_SIZE and GET_FIRMWARE operations - Two GET_FIRMWARE operations - GET_FIRMWARE and READY_TO_BOOT operations - READY_TO_BOOT operation and the call to the ->disconnect() event of the bootrom bundle (once the new hotplug request is received). The timeout for the last case is kept at 5 seconds right now (random value), as it may take a bit longer. Because 'bootrom->fw' can be accessed simultaneously (from timeout handler and incoming requests) and one of them can potentially free the '->fw' structure, a mutex is also added to take care of such races while accessing 'bootrom->fw' structure. Also note that the '!bootrom->fw' check is moved to free_firmware() routine. Tested on EVT 1.5, by faking errors on certain requests, so that the bootrom doesn't send any more requests. Normal case is working just fine for audio and GP modules. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 124 ++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index cf750beb3403..baada45329a2 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -8,24 +8,48 @@ */ #include <linux/firmware.h> +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/timer.h> #include "bootrom.h" #include "greybus.h" +/* Timeout, in jiffies, within which the next request must be received */ +#define NEXT_REQ_TIMEOUT_J msecs_to_jiffies(1000) struct gb_bootrom { struct gb_connection *connection; const struct firmware *fw; u8 protocol_major; u8 protocol_minor; + struct timer_list timer; + struct mutex mutex; /* Protects bootrom->fw */ }; static void free_firmware(struct gb_bootrom *bootrom) { + if (!bootrom->fw) + return; + release_firmware(bootrom->fw); bootrom->fw = NULL; } +static void gb_bootrom_timedout(unsigned long data) +{ + struct gb_bootrom *bootrom = (struct gb_bootrom *)data; + struct device *dev = &bootrom->connection->bundle->dev; + + dev_err(dev, "Timed out waiting for request from the Module\n"); + + mutex_lock(&bootrom->mutex); + free_firmware(bootrom); + mutex_unlock(&bootrom->mutex); + + /* TODO: Power-off Module ? */ +} + /* * The es2 chip doesn't have VID/PID programmed into the hardware and we need to * hack that up to distinguish different modules and their firmware blobs. @@ -77,8 +101,7 @@ static int download_firmware(struct gb_bootrom *bootrom, u8 stage) int rc; /* Already have a firmware, free it */ - if (bootrom->fw) - free_firmware(bootrom); + free_firmware(bootrom); /* * Create firmware name @@ -114,25 +137,32 @@ static int gb_bootrom_firmware_size_request(struct gb_operation *op) struct device *dev = &op->connection->bundle->dev; int ret; + /* Disable timeouts */ + del_timer_sync(&bootrom->timer); + if (op->request->payload_size != sizeof(*size_request)) { dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", __func__, op->request->payload_size, sizeof(*size_request)); - return -EINVAL; + ret = -EINVAL; + goto mod_timer; } + mutex_lock(&bootrom->mutex); + ret = download_firmware(bootrom, size_request->stage); if (ret) { dev_err(dev, "%s: failed to download firmware (%d)\n", __func__, ret); - return ret; + goto unlock; } if (!gb_operation_response_alloc(op, sizeof(*size_response), GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); free_firmware(bootrom); - return -ENOMEM; + ret = -ENOMEM; + goto unlock; } size_response = op->response->payload; @@ -140,28 +170,44 @@ static int gb_bootrom_firmware_size_request(struct gb_operation *op) dev_dbg(dev, "%s: firmware size %d bytes\n", __func__, size_response->size); - return 0; +unlock: + mutex_unlock(&bootrom->mutex); + +mod_timer: + /* Refresh timeout */ + mod_timer(&bootrom->timer, jiffies + NEXT_REQ_TIMEOUT_J); + + return ret; } static int gb_bootrom_get_firmware(struct gb_operation *op) { struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); - const struct firmware *fw = bootrom->fw; + const struct firmware *fw; struct gb_bootrom_get_firmware_request *firmware_request; struct gb_bootrom_get_firmware_response *firmware_response; struct device *dev = &op->connection->bundle->dev; unsigned int offset, size; + int ret = 0; + + /* Disable timeouts */ + del_timer_sync(&bootrom->timer); if (op->request->payload_size != sizeof(*firmware_request)) { dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", __func__, op->request->payload_size, sizeof(*firmware_request)); - return -EINVAL; + ret = -EINVAL; + goto mod_timer; } + mutex_lock(&bootrom->mutex); + + fw = bootrom->fw; if (!fw) { dev_err(dev, "%s: firmware not available\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } firmware_request = op->request->payload; @@ -171,13 +217,15 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) if (offset >= fw->size || size > fw->size - offset) { dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", offset, size); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); - return -ENOMEM; + ret = -ENOMEM; + goto unlock; } firmware_response = op->response->payload; @@ -186,36 +234,59 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, size); - return 0; +unlock: + mutex_unlock(&bootrom->mutex); + +mod_timer: + /* Refresh timeout */ + mod_timer(&bootrom->timer, jiffies + NEXT_REQ_TIMEOUT_J); + + return ret; } static int gb_bootrom_ready_to_boot(struct gb_operation *op) { struct gb_connection *connection = op->connection; + struct gb_bootrom *bootrom = gb_connection_get_data(connection); struct gb_bootrom_ready_to_boot_request *rtb_request; struct device *dev = &connection->bundle->dev; u8 status; + int ret = 0; + + /* Disable timeouts */ + del_timer_sync(&bootrom->timer); if (op->request->payload_size != sizeof(*rtb_request)) { dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", __func__, op->request->payload_size, sizeof(*rtb_request)); - return -EINVAL; + ret = -EINVAL; + goto mod_timer; } rtb_request = op->request->payload; status = rtb_request->status; /* Return error if the blob was invalid */ - if (status == GB_BOOTROM_BOOT_STATUS_INVALID) - return -EINVAL; + if (status == GB_BOOTROM_BOOT_STATUS_INVALID) { + ret = -EINVAL; + goto mod_timer; + } /* * XXX Should we return error for insecure firmware? */ dev_dbg(dev, "ready to boot: 0x%x, 0\n", status); - return 0; +mod_timer: + /* + * Refresh timeout, the Interface shall load the new personality and + * send a new hotplug request, which shall get rid of the bootrom + * connection. As that can take some time, increase the timeout a bit. + */ + mod_timer(&bootrom->timer, jiffies + 5 * NEXT_REQ_TIMEOUT_J); + + return ret; } static int gb_bootrom_request_handler(struct gb_operation *op) @@ -304,6 +375,11 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, bootrom->connection = connection; + mutex_init(&bootrom->mutex); + init_timer(&bootrom->timer); + bootrom->timer.function = gb_bootrom_timedout; + bootrom->timer.data = (unsigned long)bootrom; + greybus_set_drvdata(bundle, bootrom); ret = gb_connection_enable_tx(connection); @@ -329,6 +405,9 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, goto err_connection_disable; } + /* Refresh timeout */ + mod_timer(&bootrom->timer, jiffies + NEXT_REQ_TIMEOUT_J); + dev_dbg(&bundle->dev, "AP_READY sent\n"); return 0; @@ -351,9 +430,16 @@ static void gb_bootrom_disconnect(struct gb_bundle *bundle) gb_connection_disable(bootrom->connection); - /* Release firmware */ - if (bootrom->fw) - free_firmware(bootrom); + /* Disable timeouts */ + del_timer_sync(&bootrom->timer); + + /* + * Release firmware: + * + * As the connection and the timer are already disabled, we don't need + * to lock access to bootrom->fw here. + */ + free_firmware(bootrom); gb_connection_destroy(bootrom->connection); kfree(bootrom); -- cgit v1.2.3-59-g8ed1b From 3e29bcf4b148b8983dcb30faec0b59f681eabe9e Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Wed, 4 May 2016 16:21:29 -0700 Subject: greybus: core: add MODULE uevent var for all control devices The new ctrl device under interface is missing a MODULE uevent var, add it. Testing Done: cat 'uevent' from ctrl device. $ cat 1-3.3.ctrl/uevent DEVTYPE=greybus_control BUS=1 MODULE=3 INTERFACE=3 GREYBUS_ID=fffe0001/ffee0011 Signed-off-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 70a66e28471d..3fdc40597f16 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -103,6 +103,7 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) } else if (is_gb_control(dev)) { control = to_gb_control(dev); intf = control->intf; + module = intf->module; hd = intf->hd; } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); -- cgit v1.2.3-59-g8ed1b From ee2f2074fdb20d4939c943d0372f3751d833dedf Mon Sep 17 00:00:00 2001 From: Mitchell Tasman <tasman@leaflabs.com> Date: Wed, 4 May 2016 17:30:23 -0400 Subject: greybus: svc: reconfig APBridgeA-Switch link to handle required load SW-4894, SW-4389, and share a common root cause, namely that the power-on reset configuration of the APBridgeA-Switch link of PWM Gear 1, 1 Lane, Slow Auto, is insufficient to handle some required traffic loads, such as 3 audio streams plus boot-over-UniPro or 4 audio streams. The correct long-term solution is to implement a UniPro Power Mode Manager as in that considers the demands placed on the network, and adjusts power modes accordingly. The present commit implements a short-term, brute-force hack to allow continued system testing: - Upon receiving an SVC HELLO request, schedule deferred work to reconfigure the APB1-Switch link to PWM G2, 1 lane, Slow Auto - When the Camera driver transitions a White Camera module from active to inactive, return the APB1-Switch link to PWM G2, 1 lane, Slow Auto The Camera driver already steps up the APBridgeA-Camera link speed while a camera module is active, which affords sufficient margin for simultaneous audio and hotplug activity, and the Camera driver already steps down the link speed thereafter: the change made by the present patch is simply to tweak the stepped-down power mode to match the new baseline configuration. Signed-off-by: Mitchell Tasman <tasman@leaflabs.com> Tested-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 4 ++-- drivers/staging/greybus/svc.c | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 956fbf05b8e0..ba76f5633256 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -131,9 +131,9 @@ static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, ret = gb_svc_intf_set_power_mode(svc, intf_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, + 2, 1, GB_SVC_UNIPRO_SLOW_AUTO_MODE, - 1, 2, + 2, 1, 0, 0); return ret; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 577e680f6a90..f3ee94e4b03e 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -23,6 +23,8 @@ struct gb_svc_deferred_request { }; +static int gb_svc_queue_deferred_request(struct gb_operation *operation); + static ssize_t endo_id_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -695,7 +697,7 @@ static int gb_svc_hello(struct gb_operation *op) gb_svc_debugfs_init(svc); - return 0; + return gb_svc_queue_deferred_request(op); } static struct gb_interface *gb_svc_interface_lookup(struct gb_svc *svc, @@ -754,6 +756,35 @@ static void gb_svc_intf_reenable(struct gb_svc *svc, struct gb_interface *intf) mutex_unlock(&intf->mutex); } +static void gb_svc_process_hello_deferred(struct gb_operation *operation) +{ + struct gb_connection *connection = operation->connection; + struct gb_svc *svc = gb_connection_get_data(connection); + int ret; + + /* + * XXX This is a hack/work-around to reconfigure the APBridgeA-Switch + * link to PWM G2, 1 Lane, Slow Auto, so that it has sufficient + * bandwidth for 3 audio streams plus boot-over-UniPro of a hot-plugged + * module. + * + * The code should be removed once SW-2217, Heuristic for UniPro + * Power Mode Changes is resolved. + */ + ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, + GB_SVC_UNIPRO_HS_SERIES_A, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 2, 1, + GB_SVC_UNIPRO_SLOW_AUTO_MODE, + 2, 1, + 0, 0); + + if (ret) + dev_warn(&svc->dev, + "power mode change failed on AP to switch link: %d\n", + ret); +} + static void gb_svc_process_intf_hotplug(struct gb_operation *operation) { struct gb_svc_intf_hotplug_request *request; @@ -963,6 +994,9 @@ static void gb_svc_process_deferred_request(struct work_struct *work) type = operation->request->header->type; switch (type) { + case GB_SVC_TYPE_SVC_HELLO: + gb_svc_process_hello_deferred(operation); + break; case GB_SVC_TYPE_INTF_HOTPLUG: gb_svc_process_intf_hotplug(operation); break; -- cgit v1.2.3-59-g8ed1b From c77f85bbc91acafeafd4143a23a225fe81c7e294 Mon Sep 17 00:00:00 2001 From: Dinko Mironov <dmironov@mm-sol.com> Date: Thu, 5 May 2016 19:58:22 +0300 Subject: greybus: audio: Fix incorrect counting of 'ida' Function gb_audio_manager_remove_all() to remove all audio modules, doesn't control correctly 'ida' counting. Signed-off-by: Dinko Mironov <dmironov@mm-sol.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_manager.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c index 619bdee46ee5..9def014cd47c 100644 --- a/drivers/staging/greybus/audio_manager.c +++ b/drivers/staging/greybus/audio_manager.c @@ -91,6 +91,7 @@ void gb_audio_manager_remove_all(void) list_for_each_entry_safe(module, next, &modules_list, list) { list_del(&module->list); kobject_put(&module->kobj); + ida_simple_remove(&module_id, module->id); } is_empty = list_empty(&modules_list); -- cgit v1.2.3-59-g8ed1b From 0decdd55b38ac8276a1039654e529120f65ee366 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 5 May 2016 08:49:58 +0530 Subject: greybus: Revert "interface: Fetch and expose version of interface's firmware" This reverts commit b957ade7b3e4ab8c149c53346dbf02e977b7f3a7. The interface version is now managed as part of the firmware-management protocol. This operation is already removed from the greybus specifications. Drop interface version support from greybus. Tested with gbsim (sysfs file not available after this patch). Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 8 ------- drivers/staging/greybus/control.c | 28 ---------------------- drivers/staging/greybus/control.h | 1 - drivers/staging/greybus/greybus_protocols.h | 8 +------ drivers/staging/greybus/interface.c | 15 ------------ drivers/staging/greybus/interface.h | 3 --- 6 files changed, 1 insertion(+), 62 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 0e1c2a37b24f..3740173e3cd0 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -111,14 +111,6 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Vendor ID of a Greybus interface. -What: /sys/bus/greybus/devices/N-M.I/version -Date: October 2015 -KernelVersion: 4.XX -Contact: Greg Kroah-Hartman <greg@kroah.com> -Description: - Interface version represented as <16 bit major number>.<16 bit - minor number>. - What: /sys/bus/greybus/devices/N-M.I/voltage_now Date: March 2016 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 1b28899cd336..13c68f3f01de 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -156,34 +156,6 @@ int gb_control_mode_switch_operation(struct gb_control *control) NULL, 0); } -int gb_control_get_interface_version_operation(struct gb_interface *intf) -{ - struct gb_control_interface_version_response response; - struct gb_connection *connection = intf->control->connection; - int ret; - - if (intf->quirks & GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION) - return 0; - - ret = gb_operation_sync(connection, GB_CONTROL_TYPE_INTERFACE_VERSION, - NULL, 0, &response, sizeof(response)); - if (ret) { - dev_err(&connection->intf->dev, - "failed to get interface version: %d\n", ret); - /* - * FIXME: Return success until the time we bump version of - * control protocol. The interface-version is already set to - * 0.0, so no need to update that. - */ - return 0; - } - - intf->version_major = le16_to_cpu(response.major); - intf->version_minor = le16_to_cpu(response.minor); - - return 0; -} - int gb_control_timesync_enable(struct gb_control *control, u8 count, u64 frame_time, u32 strobe_delay, u32 refclk) { diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index f108bcc2e059..773cdde4dc44 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -40,7 +40,6 @@ int gb_control_mode_switch_operation(struct gb_control *control); int gb_control_get_manifest_size_operation(struct gb_interface *intf); int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, size_t size); -int gb_control_get_interface_version_operation(struct gb_interface *intf); int gb_control_timesync_enable(struct gb_control *control, u8 count, u64 frame_time, u32 strobe_delay, u32 refclk); int gb_control_timesync_disable(struct gb_control *control); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b73acc0c3ee5..5f5b92864ac2 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -121,7 +121,7 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_TIMESYNC_ENABLE 0x07 #define GB_CONTROL_TYPE_TIMESYNC_DISABLE 0x08 #define GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE 0x09 -#define GB_CONTROL_TYPE_INTERFACE_VERSION 0x0a +/* Unused 0x0a */ #define GB_CONTROL_TYPE_BUNDLE_VERSION 0x0b #define GB_CONTROL_TYPE_MODE_SWITCH 0x0e @@ -164,12 +164,6 @@ struct gb_control_disconnected_request { } __packed; /* Control protocol [dis]connected response has no payload */ -/* Control protocol interface version request has no payload */ -struct gb_control_interface_version_response { - __le16 major; - __le16 minor; -} __packed; - #define GB_TIMESYNC_MAX_STROBES 0x04 struct gb_control_timesync_enable_request { diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index b5ad1ac9d3df..aed45bcef376 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -254,16 +254,6 @@ gb_interface_attr(vendor_id, "0x%08x"); gb_interface_attr(product_id, "0x%08x"); gb_interface_attr(serial_number, "0x%016llx"); -static ssize_t version_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct gb_interface *intf = to_gb_interface(dev); - - return scnprintf(buf, PAGE_SIZE, "%u.%u\n", intf->version_major, - intf->version_minor); -} -static DEVICE_ATTR_RO(version); - static ssize_t voltage_now_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -328,7 +318,6 @@ static struct attribute *interface_attrs[] = { &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, &dev_attr_serial_number.attr, - &dev_attr_version.attr, &dev_attr_voltage_now.attr, &dev_attr_current_now.attr, &dev_attr_power_now.attr, @@ -621,10 +610,6 @@ int gb_interface_enable(struct gb_interface *intf) goto err_destroy_bundles; } - ret = gb_control_get_interface_version_operation(intf); - if (ret) - goto err_destroy_bundles; - ret = gb_control_get_bundle_versions(intf->control); if (ret) goto err_destroy_bundles; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 7a0366d08e32..7a2162a89f05 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -31,9 +31,6 @@ struct gb_interface { u32 product_id; u64 serial_number; - u16 version_major; - u16 version_minor; - struct gb_host_device *hd; struct gb_module *module; -- cgit v1.2.3-59-g8ed1b From 1d5f9ef9ef8de94331ce5ab31d4b05324885ce6c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 5 May 2016 14:32:27 +0530 Subject: greybus: gpbridge: implement gpbridge "bus" logic This creates a gpbridge "bus" that will be used to create devices that are the bridged phy devices that correspond to the protocols being implemented. Testing Done: Tested on gbsim. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: 1.Changed code to retain init/exit fns of drivers. 2.Exit path fix. 3. Fixed review comments] Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 278 +++++++++++++++++++++++++++++++++++-- drivers/staging/greybus/gpbridge.h | 47 +++++++ 2 files changed, 313 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 9be936cb422f..5a12b344f065 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -19,8 +19,266 @@ #include "greybus.h" #include "gpbridge.h" +struct gpbridge_host { + struct gb_bundle *bundle; + struct list_head devices; +}; + +static DEFINE_IDA(gpbridge_id); + +static void gpbdev_release(struct device *dev) +{ + struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); + + ida_simple_remove(&gpbridge_id, gpbdev->id); + kfree(gpbdev); +} + +static int gpbdev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + /* FIXME add something here, userspace will care about these... */ + return 0; +} + +static const struct gpbridge_device_id * +gpbdev_match_cport(struct greybus_descriptor_cport *cport_desc, + const struct gpbridge_device_id *id) +{ + if (!id) + return NULL; + + for (; id->protocol_id; id++) + if (id->protocol_id == cport_desc->protocol_id) + return id; + + return NULL; +} + +static const struct gpbridge_device_id *gpbdev_match_id(struct gb_bundle *bundle, + const struct gpbridge_device_id *id_table) +{ + const struct gpbridge_device_id *id; + int i; + + if (!id_table || !bundle || bundle->num_cports == 0) + return NULL; + + for (i = 0; i < bundle->num_cports; i++) { + id = gpbdev_match_cport(&bundle->cport_desc[i], id_table); + if (id) + return id; + } + + return NULL; +} + +static int gpbdev_match(struct device *dev, struct device_driver *drv) +{ + struct gpbridge_driver *gpbdrv = to_gpbridge_driver(drv); + struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); + const struct gpbridge_device_id *id; + + id = gpbdev_match_id(gpbdev->bundle, gpbdrv->id_table); + if (id) + return 1; + + return 0; +} + +static int gpbdev_probe(struct device *dev) +{ + struct gpbridge_driver *gpbdrv = to_gpbridge_driver(dev->driver); + struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); + const struct gpbridge_device_id *id; + + id = gpbdev_match_id(gpbdev->bundle, gpbdrv->id_table); + if (!id) + return -ENODEV; + + return gpbdrv->probe(gpbdev, id); +} + +static int gpbdev_remove(struct device *dev) +{ + struct gpbridge_driver *gpbdrv = to_gpbridge_driver(dev->driver); + struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); + + gpbdrv->remove(gpbdev); + return 0; +} + +static struct bus_type gpbridge_bus_type = { + .name = "gpbridge", + .match = gpbdev_match, + .probe = gpbdev_probe, + .remove = gpbdev_remove, + .uevent = gpbdev_uevent, +}; + +int gb_gpbridge_register_driver(struct gpbridge_driver *driver, + struct module *owner, const char *mod_name) +{ + int retval; + + if (greybus_disabled()) + return -ENODEV; + + driver->driver.bus = &gpbridge_bus_type; + driver->driver.name = driver->name; + driver->driver.owner = owner; + driver->driver.mod_name = mod_name; + + retval = driver_register(&driver->driver); + if (retval) + return retval; + + pr_info("registered new driver %s\n", driver->name); + return 0; +} + +void gb_gpbridge_deregister_driver(struct gpbridge_driver *driver) +{ + driver_unregister(&driver->driver); +} + +int gb_gpbridge_get_version(struct gb_connection *connection) +{ + struct gb_protocol_version_request request; + struct gb_protocol_version_response response; + int retval; + + request.major = 1; + request.minor = 0; + + retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, + &request, sizeof(request), &response, + sizeof(response)); + if (retval) + return retval; + + /* FIXME - do proper version negotiation here someday... */ + + connection->module_major = response.major; + connection->module_minor = response.minor; + + dev_dbg(&connection->hd->dev, "%s: v%u.%u\n", connection->name, + response.major, response.minor); + + return 0; +} + +static struct gpbridge_device *gb_gpbridge_create_dev(struct gb_bundle *bundle, + struct greybus_descriptor_cport *cport_desc) +{ + struct gpbridge_device *gpbdev; + int retval; + int id; + + id = ida_simple_get(&gpbridge_id, 0, 0, GFP_KERNEL); + if (id < 0) + return ERR_PTR(id); + + gpbdev = kzalloc(sizeof(*gpbdev), GFP_KERNEL); + if (!gpbdev) { + ida_simple_remove(&gpbridge_id, id); + return ERR_PTR(-ENOMEM); + } + + gpbdev->id = id; + gpbdev->bundle = bundle; + gpbdev->cport_desc = cport_desc; + gpbdev->dev.parent = &bundle->dev; + gpbdev->dev.bus = &gpbridge_bus_type; + gpbdev->dev.release = gpbdev_release; + gpbdev->dev.groups = NULL; + gpbdev->dev.dma_mask = bundle->dev.dma_mask; + dev_set_name(&gpbdev->dev, "gpb%d", id); + + retval = device_register(&gpbdev->dev); + if (retval) { + put_device(&gpbdev->dev); + return ERR_PTR(retval); + } + + return gpbdev; +} + +static void gb_gpbridge_disconnect(struct gb_bundle *bundle) +{ + struct gpbridge_host *gpb_host = greybus_get_drvdata(bundle); + struct gpbridge_device *gpbdev, *temp; + + list_for_each_entry_safe(gpbdev, temp, &gpb_host->devices, list) { + list_del(&gpbdev->list); + device_unregister(&gpbdev->dev); + } + + kfree(gpb_host); +} + +static int gb_gpbridge_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + struct gpbridge_host *gpb_host; + struct gpbridge_device *gpbdev; + int i; + + if (bundle->num_cports == 0) + return -ENODEV; + + gpb_host = kzalloc(sizeof(*gpb_host), GFP_KERNEL); + if (!gpb_host) + return -ENOMEM; + + gpb_host->bundle = bundle; + INIT_LIST_HEAD(&gpb_host->devices); + greybus_set_drvdata(bundle, gpb_host); + + /* + * Create a bunch of children devices, one per cport, and bind the + * bridged phy drivers to them. + */ + for (i = 0; i < bundle->num_cports; ++i) { + gpbdev = gb_gpbridge_create_dev(bundle, &bundle->cport_desc[i]); + if (IS_ERR(gpbdev)) { + gb_gpbridge_disconnect(bundle); + return PTR_ERR(gpbdev); + } + list_add(&gpbdev->list, &gpb_host->devices); + } + + return 0; +} + +static const struct greybus_bundle_id gb_gpbridge_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, + { }, +}; +MODULE_DEVICE_TABLE(greybus, gb_gpbridge_id_table); + +static struct greybus_driver gb_gpbridge_driver = { + .name = "gpbridge", + .probe = gb_gpbridge_probe, + .disconnect = gb_gpbridge_disconnect, + .id_table = gb_gpbridge_id_table, +}; + static int __init gpbridge_init(void) { + int retval; + + retval = bus_register(&gpbridge_bus_type); + if (retval) { + pr_err("gpbridge bus register failed (%d)\n", retval); + return retval; + } + + retval = greybus_register(&gb_gpbridge_driver); + if (retval) { + pr_err("error registering greybus driver\n"); + goto error_gpbridge; + } + if (gb_gpio_protocol_init()) { pr_err("error initializing gpio protocol\n"); goto error_gpio; @@ -65,6 +323,10 @@ error_uart: error_pwm: gb_gpio_protocol_exit(); error_gpio: + greybus_deregister(&gb_gpbridge_driver); +error_gpbridge: + bus_unregister(&gpbridge_bus_type); + ida_destroy(&gpbridge_id); return -EPROTO; } module_init(gpbridge_init); @@ -78,19 +340,11 @@ static void __exit gpbridge_exit(void) gb_uart_protocol_exit(); gb_pwm_protocol_exit(); gb_gpio_protocol_exit(); + + greybus_deregister(&gb_gpbridge_driver); + bus_unregister(&gpbridge_bus_type); + ida_destroy(&gpbridge_id); } module_exit(gpbridge_exit); -/* - * One large list of all classes we support in the gpbridge.ko module. - * - * Due to limitations in older kernels, the different phy .c files can not - * contain their own MODULE_DEVICE_TABLE(), so put them all here for now. - */ -static const struct greybus_bundle_id bridged_phy_id_table[] = { - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, - { }, -}; -MODULE_DEVICE_TABLE(greybus, bridged_phy_id_table); - MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 50ee87b8f737..431cb7bc142f 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -9,6 +9,53 @@ #ifndef __GPBRIDGE_H #define __GPBRIDGE_H +struct gpbridge_device { + u32 id; + struct greybus_descriptor_cport *cport_desc; + struct gb_bundle *bundle; + struct list_head list; + struct device dev; +}; +#define to_gpbridge_dev(d) container_of(d, struct gpbridge_device, dev) + +static inline void *gb_gpbridge_get_data(struct gpbridge_device *gdev) +{ + return dev_get_drvdata(&gdev->dev); +} + +static inline void gb_gpbridge_set_data(struct gpbridge_device *gdev, void *data) +{ + dev_set_drvdata(&gdev->dev, data); +} + +struct gpbridge_device_id { + __u8 protocol_id; +}; + +#define GPBRIDGE_PROTOCOL(p) \ + .protocol_id = (p), + +struct gpbridge_driver { + const char *name; + int (*probe)(struct gpbridge_device *, + const struct gpbridge_device_id *id); + void (*remove)(struct gpbridge_device *); + const struct gpbridge_device_id *id_table; + + struct device_driver driver; +}; +#define to_gpbridge_driver(d) container_of(d, struct gpbridge_driver, driver) + +int gb_gpbridge_get_version(struct gb_connection *connection); +int gb_gpbridge_register_driver(struct gpbridge_driver *driver, + struct module *owner, const char *mod_name); +void gb_gpbridge_deregister_driver(struct gpbridge_driver *driver); + +#define gb_gpbridge_register(driver) \ + gb_gpbridge_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) +#define gb_gpbridge_deregister(driver) \ + gb_gpbridge_deregister_driver(driver) + extern int gb_gpio_protocol_init(void); extern void gb_gpio_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From ac4cbc575153fe8044d1e7d7742889ab0850913a Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Thu, 5 May 2016 14:32:28 +0530 Subject: greybus: gpbridge: Add gpbridge driver init/exit helper macros In order to help bridged-phy drivers to define init()/exit() functions, define helper macro in gpbridge.h file. Testing Done: Tested on gbsim. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 431cb7bc142f..aa2cdc08223f 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -56,6 +56,16 @@ void gb_gpbridge_deregister_driver(struct gpbridge_driver *driver); #define gb_gpbridge_deregister(driver) \ gb_gpbridge_deregister_driver(driver) +#define gb_gpbridge_builtin_driver(__driver) \ + int __init gb_##__driver##_init(void) \ +{ \ + return gb_gpbridge_register(&__driver); \ +} \ +void gb_##__driver##_exit(void) \ +{ \ + gb_gpbridge_deregister(&__driver); \ +} + extern int gb_gpio_protocol_init(void); extern void gb_gpio_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From 6d58e7144473f46c245792380c8fc1c7e928147e Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Thu, 5 May 2016 14:32:29 +0530 Subject: greybus: connection: export gb_connection_disable_rx() fn gb_connection_disable_rx() fn is required to be used by other modules (e.g. bridged-phy drivers) and so export it. Testing Done: Tested on gbsim. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1880f8f6065f..1564db84b4ed 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -617,6 +617,7 @@ void gb_connection_disable_rx(struct gb_connection *connection) out_unlock: mutex_unlock(&connection->mutex); } +EXPORT_SYMBOL_GPL(gb_connection_disable_rx); void gb_connection_disable(struct gb_connection *connection) { -- cgit v1.2.3-59-g8ed1b From 7dbe1f497b445ead3a6c5f0895d002960a2b07f2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 5 May 2016 14:32:30 +0530 Subject: greybus: UART: convert to a gpbridge driver This converts the UART driver to be a gpbridge driver, moving it away from the "legacy" interface. Testing Done: Tested on gbsim. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: 1.Changed code to retain init/exit fns of drivers. 2.Exit path fix. 3. Fixed review comments] Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 9 +-- drivers/staging/greybus/gpbridge.h | 4 +- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/uart.c | 114 ++++++++++++++++++++++++------------- 4 files changed, 80 insertions(+), 48 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 5a12b344f065..2a23a309b83e 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -252,6 +252,7 @@ static int gb_gpbridge_probe(struct gb_bundle *bundle, static const struct greybus_bundle_id gb_gpbridge_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, { }, }; MODULE_DEVICE_TABLE(greybus, gb_gpbridge_id_table); @@ -287,8 +288,8 @@ static int __init gpbridge_init(void) pr_err("error initializing pwm protocol\n"); goto error_pwm; } - if (gb_uart_protocol_init()) { - pr_err("error initializing uart protocol\n"); + if (gb_uart_driver_init()) { + pr_err("error initializing uart driver\n"); goto error_uart; } if (gb_sdio_protocol_init()) { @@ -317,7 +318,7 @@ error_i2c: error_usb: gb_sdio_protocol_exit(); error_sdio: - gb_uart_protocol_exit(); + gb_uart_driver_exit(); error_uart: gb_pwm_protocol_exit(); error_pwm: @@ -337,7 +338,7 @@ static void __exit gpbridge_exit(void) gb_i2c_protocol_exit(); gb_usb_protocol_exit(); gb_sdio_protocol_exit(); - gb_uart_protocol_exit(); + gb_uart_driver_exit(); gb_pwm_protocol_exit(); gb_gpio_protocol_exit(); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index aa2cdc08223f..fb17f02861d1 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -72,8 +72,8 @@ extern void gb_gpio_protocol_exit(void); extern int gb_pwm_protocol_init(void); extern void gb_pwm_protocol_exit(void); -extern int gb_uart_protocol_init(void); -extern void gb_uart_protocol_exit(void); +extern int gb_uart_driver_init(void); +extern void gb_uart_driver_exit(void); extern int gb_sdio_protocol_init(void); extern void gb_sdio_protocol_exit(void); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 58b277979a60..f391a5dccba4 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -238,7 +238,6 @@ static void legacy_disconnect(struct gb_bundle *bundle) static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index be718918c135..eb83c04d7b69 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -42,6 +42,7 @@ struct gb_tty_line_coding { }; struct gb_tty { + struct gpbridge_device *gpbdev; struct tty_port port; void *buffer; size_t buffer_payload_max; @@ -78,7 +79,7 @@ static int gb_uart_receive_data_handler(struct gb_operation *op) unsigned long tty_flags = TTY_NORMAL; if (request->payload_size < sizeof(*receive_data)) { - dev_err(&connection->bundle->dev, + dev_err(&gb_tty->gpbdev->dev, "short receive-data request received (%zu < %zu)\n", request->payload_size, sizeof(*receive_data)); return -EINVAL; @@ -88,7 +89,7 @@ static int gb_uart_receive_data_handler(struct gb_operation *op) recv_data_size = le16_to_cpu(receive_data->size); if (recv_data_size != request->payload_size - sizeof(*receive_data)) { - dev_err(&connection->bundle->dev, + dev_err(&gb_tty->gpbdev->dev, "malformed receive-data request received (%u != %zu)\n", recv_data_size, request->payload_size - sizeof(*receive_data)); @@ -113,7 +114,7 @@ static int gb_uart_receive_data_handler(struct gb_operation *op) count = tty_insert_flip_string_fixed_flag(port, receive_data->data, tty_flags, recv_data_size); if (count != recv_data_size) { - dev_err(&connection->bundle->dev, + dev_err(&gb_tty->gpbdev->dev, "UART: RX 0x%08x bytes only wrote 0x%08x\n", recv_data_size, count); } @@ -130,7 +131,7 @@ static int gb_uart_serial_state_handler(struct gb_operation *op) struct gb_uart_serial_state_request *serial_state; if (request->payload_size < sizeof(*serial_state)) { - dev_err(&connection->bundle->dev, + dev_err(&gb_tty->gpbdev->dev, "short serial-state event received (%zu < %zu)\n", request->payload_size, sizeof(*serial_state)); return -EINVAL; @@ -142,9 +143,11 @@ static int gb_uart_serial_state_handler(struct gb_operation *op) return 0; } -static int gb_uart_request_recv(u8 type, struct gb_operation *op) +static int gb_uart_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; + struct gb_tty *gb_tty = gb_connection_get_data(connection); + int type = op->type; int ret; switch (type) { @@ -155,7 +158,7 @@ static int gb_uart_request_recv(u8 type, struct gb_operation *op) ret = gb_uart_serial_state_handler(op); break; default: - dev_err(&connection->bundle->dev, + dev_err(&gb_tty->gpbdev->dev, "unsupported unsolicited request: 0x%02x\n", type); ret = -EINVAL; } @@ -209,7 +212,7 @@ static int send_break(struct gb_tty *gb_tty, u8 state) struct gb_uart_set_break_request request; if ((state != 0) && (state != 1)) { - dev_err(&gb_tty->connection->bundle->dev, + dev_err(&gb_tty->gpbdev->dev, "invalid break state of %d\n", state); return -EINVAL; } @@ -619,8 +622,10 @@ static struct tty_port_operations null_ops = { }; static int gb_tty_init(void); static void gb_tty_exit(void); -static int gb_uart_connection_init(struct gb_connection *connection) +static int gb_uart_probe(struct gpbridge_device *gpbdev, + const struct gpbridge_device_id *id) { + struct gb_connection *connection; size_t max_payload; struct gb_tty *gb_tty; struct device *tty_dev; @@ -639,13 +644,21 @@ static int gb_uart_connection_init(struct gb_connection *connection) gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); if (!gb_tty) { retval = -ENOMEM; - goto error_alloc; + goto exit_tty; + } + + connection = gb_connection_create(gpbdev->bundle, + le16_to_cpu(gpbdev->cport_desc->id), + gb_uart_request_handler); + if (IS_ERR(connection)) { + retval = PTR_ERR(connection); + goto exit_tty_free; } max_payload = gb_operation_get_payload_size_max(connection); if (max_payload < sizeof(struct gb_uart_send_data_request)) { retval = -EINVAL; - goto error_payload; + goto exit_connection_destroy; } gb_tty->buffer_payload_max = max_payload - @@ -654,22 +667,19 @@ static int gb_uart_connection_init(struct gb_connection *connection) gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); if (!gb_tty->buffer) { retval = -ENOMEM; - goto error_payload; + goto exit_connection_destroy; } - gb_tty->connection = connection; - gb_connection_set_data(connection, gb_tty); - minor = alloc_minor(gb_tty); if (minor < 0) { if (minor == -ENOSPC) { dev_err(&connection->bundle->dev, "no more free minor numbers\n"); retval = -ENODEV; - goto error_minor; + } else { + retval = minor; } - retval = minor; - goto error_minor; + goto exit_buf_free; } gb_tty->minor = minor; @@ -681,6 +691,19 @@ static int gb_uart_connection_init(struct gb_connection *connection) tty_port_init(&gb_tty->port); gb_tty->port.ops = &null_ops; + gb_tty->connection = connection; + gb_tty->gpbdev = gpbdev; + gb_connection_set_data(connection, gb_tty); + gb_gpbridge_set_data(gpbdev, gb_tty); + + retval = gb_connection_enable_tx(connection); + if (retval) + goto exit_release_minor; + + retval = gb_gpbridge_get_version(connection); + if (retval) + goto exit_connection_disable; + send_control(gb_tty, gb_tty->ctrlout); /* initialize the uart to be 9600n81 */ @@ -690,41 +713,47 @@ static int gb_uart_connection_init(struct gb_connection *connection) gb_tty->line_coding.data_bits = 8; send_line_coding(gb_tty); + retval = gb_connection_enable(connection); + if (retval) + goto exit_connection_disable; + tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, - &connection->bundle->dev); + &gpbdev->dev); if (IS_ERR(tty_dev)) { retval = PTR_ERR(tty_dev); - goto error; + goto exit_connection_disable; } + return 0; -error: - tty_port_destroy(&gb_tty->port); + +exit_connection_disable: + gb_connection_disable(connection); +exit_release_minor: release_minor(gb_tty); -error_minor: - gb_connection_set_data(connection, NULL); +exit_buf_free: kfree(gb_tty->buffer); -error_payload: +exit_connection_destroy: + gb_connection_destroy(connection); +exit_tty_free: kfree(gb_tty); -error_alloc: +exit_tty: if (atomic_dec_return(&reference_count) == 0) gb_tty_exit(); + return retval; } -static void gb_uart_connection_exit(struct gb_connection *connection) +static void gb_uart_remove(struct gpbridge_device *gpbdev) { - struct gb_tty *gb_tty = gb_connection_get_data(connection); + struct gb_tty *gb_tty = gb_gpbridge_get_data(gpbdev); + struct gb_connection *connection = gb_tty->connection; struct tty_struct *tty; - if (!gb_tty) - return; - mutex_lock(&gb_tty->mutex); gb_tty->disconnected = true; wake_up_all(&gb_tty->wioctl); - gb_connection_set_data(connection, NULL); mutex_unlock(&gb_tty->mutex); tty = tty_port_tty_get(&gb_tty->port); @@ -732,13 +761,15 @@ static void gb_uart_connection_exit(struct gb_connection *connection) tty_vhangup(tty); tty_kref_put(tty); } - /* FIXME - stop all traffic */ + gb_connection_disable_rx(connection); tty_unregister_device(gb_tty_driver, gb_tty->minor); /* FIXME - free transmit / receive buffers */ + gb_connection_disable(connection); tty_port_destroy(&gb_tty->port); + gb_connection_destroy(connection); kfree(gb_tty->buffer); kfree(gb_tty); @@ -790,14 +821,15 @@ static void gb_tty_exit(void) idr_destroy(&tty_minors); } -static struct gb_protocol uart_protocol = { - .name = "uart", - .id = GREYBUS_PROTOCOL_UART, - .major = GB_UART_VERSION_MAJOR, - .minor = GB_UART_VERSION_MINOR, - .connection_init = gb_uart_connection_init, - .connection_exit = gb_uart_connection_exit, - .request_recv = gb_uart_request_recv, +static const struct gpbridge_device_id gb_uart_id_table[] = { + { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_UART) }, + { }, }; -gb_builtin_protocol_driver(uart_protocol); +static struct gpbridge_driver uart_driver = { + .name = "uart", + .probe = gb_uart_probe, + .remove = gb_uart_remove, + .id_table = gb_uart_id_table, +}; +gb_gpbridge_builtin_driver(uart_driver); -- cgit v1.2.3-59-g8ed1b From 320549086d1e985c09fd6635075ab45a3421038e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 5 May 2016 14:32:31 +0530 Subject: greybus: PWM: convert to a gpbridge driver This converts the PWM driver to be a gpbridge driver, moving it away from the "legacy" interface. Testing Done: Tested on gbsim. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: 1.Changed code to retain init/exit fns of drivers. 2.Exit path fix. 3. Fixed review comments] Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 9 +++--- drivers/staging/greybus/gpbridge.h | 4 +-- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/pwm.c | 66 ++++++++++++++++++++++++++------------ 4 files changed, 53 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 2a23a309b83e..a446749789f9 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -252,6 +252,7 @@ static int gb_gpbridge_probe(struct gb_bundle *bundle, static const struct greybus_bundle_id gb_gpbridge_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, { }, }; @@ -284,8 +285,8 @@ static int __init gpbridge_init(void) pr_err("error initializing gpio protocol\n"); goto error_gpio; } - if (gb_pwm_protocol_init()) { - pr_err("error initializing pwm protocol\n"); + if (gb_pwm_driver_init()) { + pr_err("error initializing pwm driver\n"); goto error_pwm; } if (gb_uart_driver_init()) { @@ -320,7 +321,7 @@ error_usb: error_sdio: gb_uart_driver_exit(); error_uart: - gb_pwm_protocol_exit(); + gb_pwm_driver_exit(); error_pwm: gb_gpio_protocol_exit(); error_gpio: @@ -339,7 +340,7 @@ static void __exit gpbridge_exit(void) gb_usb_protocol_exit(); gb_sdio_protocol_exit(); gb_uart_driver_exit(); - gb_pwm_protocol_exit(); + gb_pwm_driver_exit(); gb_gpio_protocol_exit(); greybus_deregister(&gb_gpbridge_driver); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index fb17f02861d1..8681bd33e671 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -69,8 +69,8 @@ void gb_##__driver##_exit(void) \ extern int gb_gpio_protocol_init(void); extern void gb_gpio_protocol_exit(void); -extern int gb_pwm_protocol_init(void); -extern void gb_pwm_protocol_exit(void); +extern int gb_pwm_driver_init(void); +extern void gb_pwm_driver_exit(void); extern int gb_uart_driver_init(void); extern void gb_uart_driver_exit(void); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index f391a5dccba4..f9bcdded9b6a 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -240,7 +240,6 @@ static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, { } diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 176301a49865..b11e77df4a6c 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -178,8 +178,10 @@ static const struct pwm_ops gb_pwm_ops = { .owner = THIS_MODULE, }; -static int gb_pwm_connection_init(struct gb_connection *connection) +static int gb_pwm_probe(struct gpbridge_device *gpbdev, + const struct gpbridge_device_id *id) { + struct gb_connection *connection; struct gb_pwm_chip *pwmc; struct pwm_chip *pwm; int ret; @@ -187,17 +189,35 @@ static int gb_pwm_connection_init(struct gb_connection *connection) pwmc = kzalloc(sizeof(*pwmc), GFP_KERNEL); if (!pwmc) return -ENOMEM; + + connection = gb_connection_create(gpbdev->bundle, + le16_to_cpu(gpbdev->cport_desc->id), + NULL); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto exit_pwmc_free; + } + pwmc->connection = connection; gb_connection_set_data(connection, pwmc); + gb_gpbridge_set_data(gpbdev, pwmc); + + ret = gb_connection_enable(connection); + if (ret) + goto exit_connection_destroy; + + ret = gb_gpbridge_get_version(connection); + if (ret) + goto exit_connection_disable; /* Query number of pwms present */ ret = gb_pwm_count_operation(pwmc); if (ret) - goto out_err; + goto exit_connection_disable; pwm = &pwmc->chip; - pwm->dev = &connection->bundle->dev; + pwm->dev = &gpbdev->dev; pwm->ops = &gb_pwm_ops; pwm->base = -1; /* Allocate base dynamically */ pwm->npwm = pwmc->pwm_max + 1; @@ -205,36 +225,42 @@ static int gb_pwm_connection_init(struct gb_connection *connection) ret = pwmchip_add(pwm); if (ret) { - dev_err(&connection->bundle->dev, + dev_err(&gpbdev->dev, "failed to register PWM: %d\n", ret); - goto out_err; + goto exit_connection_disable; } return 0; -out_err: + +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); +exit_pwmc_free: kfree(pwmc); return ret; } -static void gb_pwm_connection_exit(struct gb_connection *connection) +static void gb_pwm_remove(struct gpbridge_device *gpbdev) { - struct gb_pwm_chip *pwmc = gb_connection_get_data(connection); - if (!pwmc) - return; + struct gb_pwm_chip *pwmc = gb_gpbridge_get_data(gpbdev); + struct gb_connection *connection = pwmc->connection; pwmchip_remove(&pwmc->chip); - /* kref_put(pwmc->connection) */ + gb_connection_disable(connection); + gb_connection_destroy(connection); kfree(pwmc); } -static struct gb_protocol pwm_protocol = { - .name = "pwm", - .id = GREYBUS_PROTOCOL_PWM, - .major = GB_PWM_VERSION_MAJOR, - .minor = GB_PWM_VERSION_MINOR, - .connection_init = gb_pwm_connection_init, - .connection_exit = gb_pwm_connection_exit, - .request_recv = NULL, /* no incoming requests */ +static const struct gpbridge_device_id gb_pwm_id_table[] = { + { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_PWM) }, + { }, }; -gb_builtin_protocol_driver(pwm_protocol); +static struct gpbridge_driver pwm_driver = { + .name = "pwm", + .probe = gb_pwm_probe, + .remove = gb_pwm_remove, + .id_table = gb_pwm_id_table, +}; +gb_gpbridge_builtin_driver(pwm_driver); -- cgit v1.2.3-59-g8ed1b From cf1d8bee59aa995fcc6514be7a67e9575bbe5498 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 5 May 2016 14:32:32 +0530 Subject: greybus: I2C: convert to a gpbridge driver This converts the I2C driver to be a gpbridge driver, moving it away from the "legacy" interface. Testing Done: Tested on gbsim. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: 1.Changed code to retain init/exit fns of drivers. 2.Exit path fix. 3. Fixed review comments] Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 9 ++--- drivers/staging/greybus/gpbridge.h | 4 +-- drivers/staging/greybus/i2c.c | 71 +++++++++++++++++++++++++++----------- drivers/staging/greybus/legacy.c | 1 - 4 files changed, 57 insertions(+), 28 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index a446749789f9..e3c11cba3a51 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -252,6 +252,7 @@ static int gb_gpbridge_probe(struct gb_bundle *bundle, static const struct greybus_bundle_id gb_gpbridge_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, { }, @@ -301,8 +302,8 @@ static int __init gpbridge_init(void) pr_err("error initializing usb protocol\n"); goto error_usb; } - if (gb_i2c_protocol_init()) { - pr_err("error initializing i2c protocol\n"); + if (gb_i2c_driver_init()) { + pr_err("error initializing i2c driver\n"); goto error_i2c; } if (gb_spi_protocol_init()) { @@ -313,7 +314,7 @@ static int __init gpbridge_init(void) return 0; error_spi: - gb_i2c_protocol_exit(); + gb_i2c_driver_exit(); error_i2c: gb_usb_protocol_exit(); error_usb: @@ -336,7 +337,7 @@ module_init(gpbridge_init); static void __exit gpbridge_exit(void) { gb_spi_protocol_exit(); - gb_i2c_protocol_exit(); + gb_i2c_driver_exit(); gb_usb_protocol_exit(); gb_sdio_protocol_exit(); gb_uart_driver_exit(); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 8681bd33e671..a64e9cb35f5e 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -81,8 +81,8 @@ extern void gb_sdio_protocol_exit(void); extern int gb_usb_protocol_init(void); extern void gb_usb_protocol_exit(void); -extern int gb_i2c_protocol_init(void); -extern void gb_i2c_protocol_exit(void); +extern int gb_i2c_driver_init(void); +extern void gb_i2c_driver_exit(void); extern int gb_spi_protocol_init(void); extern void gb_spi_protocol_exit(void); diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 73b85815d1eb..b49e8b455ab1 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -17,6 +17,7 @@ struct gb_i2c_device { struct gb_connection *connection; + struct gpbridge_device *gpbdev; u32 functionality; @@ -71,6 +72,7 @@ static struct gb_operation * gb_i2c_operation_create(struct gb_connection *connection, struct i2c_msg *msgs, u32 msg_count) { + struct gb_i2c_device *gb_i2c_dev = gb_connection_get_data(connection); struct gb_i2c_transfer_request *request; struct gb_operation *operation; struct gb_i2c_transfer_op *op; @@ -83,7 +85,7 @@ gb_i2c_operation_create(struct gb_connection *connection, u32 i; if (msg_count > (u32)U16_MAX) { - dev_err(&connection->bundle->dev, "msg_count (%u) too big\n", + dev_err(&gb_i2c_dev->gpbdev->dev, "msg_count (%u) too big\n", msg_count); return NULL; } @@ -166,7 +168,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, struct i2c_msg *msgs, u32 msg_count) { struct gb_connection *connection = gb_i2c_dev->connection; - struct device *dev = &connection->bundle->dev; + struct device *dev = &gb_i2c_dev->gpbdev->dev; struct gb_operation *operation; int ret; @@ -240,8 +242,10 @@ static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) return gb_i2c_functionality_operation(gb_i2c_dev); } -static int gb_i2c_connection_init(struct gb_connection *connection) +static int gb_i2c_probe(struct gpbridge_device *gpbdev, + const struct gpbridge_device_id *id) { + struct gb_connection *connection; struct gb_i2c_device *gb_i2c_dev; struct i2c_adapter *adapter; int ret; @@ -250,12 +254,30 @@ static int gb_i2c_connection_init(struct gb_connection *connection) if (!gb_i2c_dev) return -ENOMEM; - gb_i2c_dev->connection = connection; /* refcount? */ + connection = gb_connection_create(gpbdev->bundle, + le16_to_cpu(gpbdev->cport_desc->id), + NULL); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto exit_i2cdev_free; + } + + gb_i2c_dev->connection = connection; gb_connection_set_data(connection, gb_i2c_dev); + gb_i2c_dev->gpbdev = gpbdev; + gb_gpbridge_set_data(gpbdev, gb_i2c_dev); + + ret = gb_connection_enable(connection); + if (ret) + goto exit_connection_destroy; + + ret = gb_gpbridge_get_version(connection); + if (ret) + goto exit_connection_disable; ret = gb_i2c_device_setup(gb_i2c_dev); if (ret) - goto out_err; + goto exit_connection_disable; /* Looks good; up our i2c adapter */ adapter = &gb_i2c_dev->adapter; @@ -264,39 +286,46 @@ static int gb_i2c_connection_init(struct gb_connection *connection) adapter->algo = &gb_i2c_algorithm; /* adapter->algo_data = what? */ - adapter->dev.parent = &connection->bundle->dev; + adapter->dev.parent = &gpbdev->dev; snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); i2c_set_adapdata(adapter, gb_i2c_dev); ret = i2c_add_adapter(adapter); if (ret) - goto out_err; + goto exit_connection_disable; return 0; -out_err: - /* kref_put(gb_i2c_dev->connection) */ + +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); +exit_i2cdev_free: kfree(gb_i2c_dev); return ret; } -static void gb_i2c_connection_exit(struct gb_connection *connection) +static void gb_i2c_remove(struct gpbridge_device *gpbdev) { - struct gb_i2c_device *gb_i2c_dev = gb_connection_get_data(connection); + struct gb_i2c_device *gb_i2c_dev = gb_gpbridge_get_data(gpbdev); + struct gb_connection *connection = gb_i2c_dev->connection; i2c_del_adapter(&gb_i2c_dev->adapter); - /* kref_put(gb_i2c_dev->connection) */ + gb_connection_disable(connection); + gb_connection_destroy(connection); kfree(gb_i2c_dev); } -static struct gb_protocol i2c_protocol = { - .name = "i2c", - .id = GREYBUS_PROTOCOL_I2C, - .major = GB_I2C_VERSION_MAJOR, - .minor = GB_I2C_VERSION_MINOR, - .connection_init = gb_i2c_connection_init, - .connection_exit = gb_i2c_connection_exit, - .request_recv = NULL, /* no incoming requests */ +static const struct gpbridge_device_id gb_i2c_id_table[] = { + { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_I2C) }, + { }, }; -gb_builtin_protocol_driver(i2c_protocol); +static struct gpbridge_driver i2c_driver = { + .name = "i2c", + .probe = gb_i2c_probe, + .remove = gb_i2c_remove, + .id_table = gb_i2c_id_table, +}; +gb_gpbridge_builtin_driver(i2c_driver); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index f9bcdded9b6a..f734646fd30a 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -237,7 +237,6 @@ static void legacy_disconnect(struct gb_bundle *bundle) static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, -- cgit v1.2.3-59-g8ed1b From 315bea0e9605a2f506606bdb2a531149a022394d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 5 May 2016 14:32:33 +0530 Subject: greybus: GPIO: convert to a gpbridge driver This converts the GPIO driver to be a gpbridge driver, moving it away from the "legacy" interface. Testing Done: Tested on gbsim. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: 1.Changed code to retain init/exit fns of drivers. 2.Exit path fix. 3. Fixed review comments] Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 9 ++-- drivers/staging/greybus/gpbridge.h | 4 +- drivers/staging/greybus/gpio.c | 100 ++++++++++++++++++++++++------------- drivers/staging/greybus/legacy.c | 1 - 4 files changed, 73 insertions(+), 41 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index e3c11cba3a51..a65ab1609127 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -252,6 +252,7 @@ static int gb_gpbridge_probe(struct gb_bundle *bundle, static const struct greybus_bundle_id gb_gpbridge_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, @@ -282,8 +283,8 @@ static int __init gpbridge_init(void) goto error_gpbridge; } - if (gb_gpio_protocol_init()) { - pr_err("error initializing gpio protocol\n"); + if (gb_gpio_driver_init()) { + pr_err("error initializing gpio driver\n"); goto error_gpio; } if (gb_pwm_driver_init()) { @@ -324,7 +325,7 @@ error_sdio: error_uart: gb_pwm_driver_exit(); error_pwm: - gb_gpio_protocol_exit(); + gb_gpio_driver_exit(); error_gpio: greybus_deregister(&gb_gpbridge_driver); error_gpbridge: @@ -342,7 +343,7 @@ static void __exit gpbridge_exit(void) gb_sdio_protocol_exit(); gb_uart_driver_exit(); gb_pwm_driver_exit(); - gb_gpio_protocol_exit(); + gb_gpio_driver_exit(); greybus_deregister(&gb_gpbridge_driver); bus_unregister(&gpbridge_bus_type); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index a64e9cb35f5e..f5161c56cc98 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -66,8 +66,8 @@ void gb_##__driver##_exit(void) \ gb_gpbridge_deregister(&__driver); \ } -extern int gb_gpio_protocol_init(void); -extern void gb_gpio_protocol_exit(void); +extern int gb_gpio_driver_init(void); +extern void gb_gpio_driver_exit(void); extern int gb_pwm_driver_init(void); extern void gb_pwm_driver_exit(void); diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 440ff44c8524..4f0695b2b20e 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -33,6 +33,7 @@ struct gb_gpio_line { }; struct gb_gpio_controller { + struct gpbridge_device *gpbdev; struct gb_connection *connection; u8 line_max; /* max line number */ struct gb_gpio_line *lines; @@ -78,7 +79,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, u8 which) { - struct device *dev = &ggc->connection->bundle->dev; + struct device *dev = &ggc->gpbdev->dev; struct gb_gpio_deactivate_request request; int ret; @@ -96,7 +97,7 @@ static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, u8 which) { - struct device *dev = &ggc->connection->bundle->dev; + struct device *dev = &ggc->gpbdev->dev; struct gb_gpio_get_direction_request request; struct gb_gpio_get_direction_response response; int ret; @@ -150,7 +151,7 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, u8 which) { - struct device *dev = &ggc->connection->bundle->dev; + struct device *dev = &ggc->gpbdev->dev; struct gb_gpio_get_value_request request; struct gb_gpio_get_value_response response; int ret; @@ -177,7 +178,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, static void gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, u8 which, bool value_high) { - struct device *dev = &ggc->connection->bundle->dev; + struct device *dev = &ggc->gpbdev->dev; struct gb_gpio_set_value_request request; int ret; @@ -216,7 +217,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, static void _gb_gpio_irq_mask(struct gb_gpio_controller *ggc, u8 hwirq) { - struct device *dev = &ggc->connection->bundle->dev; + struct device *dev = &ggc->gpbdev->dev; struct gb_gpio_irq_mask_request request; int ret; @@ -230,7 +231,7 @@ static void _gb_gpio_irq_mask(struct gb_gpio_controller *ggc, u8 hwirq) static void _gb_gpio_irq_unmask(struct gb_gpio_controller *ggc, u8 hwirq) { - struct device *dev = &ggc->connection->bundle->dev; + struct device *dev = &ggc->gpbdev->dev; struct gb_gpio_irq_unmask_request request; int ret; @@ -245,7 +246,7 @@ static void _gb_gpio_irq_unmask(struct gb_gpio_controller *ggc, u8 hwirq) static void _gb_gpio_irq_set_type(struct gb_gpio_controller *ggc, u8 hwirq, u8 type) { - struct device *dev = &ggc->connection->bundle->dev; + struct device *dev = &ggc->gpbdev->dev; struct gb_gpio_irq_type_request request; int ret; @@ -284,7 +285,7 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_chip *chip = irq_data_to_gpio_chip(d); struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); struct gb_gpio_line *line = &ggc->lines[d->hwirq]; - struct device *dev = &ggc->connection->bundle->dev; + struct device *dev = &ggc->gpbdev->dev; u8 irq_type; switch (type) { @@ -347,13 +348,14 @@ static void gb_gpio_irq_bus_sync_unlock(struct irq_data *d) mutex_unlock(&ggc->irq_lock); } -static int gb_gpio_request_recv(u8 type, struct gb_operation *op) +static int gb_gpio_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct device *dev = &connection->bundle->dev; struct gb_gpio_controller *ggc = gb_connection_get_data(connection); + struct device *dev = &ggc->gpbdev->dev; struct gb_message *request; struct gb_gpio_irq_event_request *event; + u8 type = op->type; int irq; struct irq_desc *desc; @@ -622,8 +624,10 @@ static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return irq_find_mapping(ggc->irqdomain, offset); } -static int gb_gpio_connection_init(struct gb_connection *connection) +static int gb_gpio_probe(struct gpbridge_device *gpbdev, + const struct gpbridge_device_id *id) { + struct gb_connection *connection; struct gb_gpio_controller *ggc; struct gpio_chip *gpio; struct irq_chip *irqc; @@ -632,12 +636,31 @@ static int gb_gpio_connection_init(struct gb_connection *connection) ggc = kzalloc(sizeof(*ggc), GFP_KERNEL); if (!ggc) return -ENOMEM; + + connection = gb_connection_create(gpbdev->bundle, + le16_to_cpu(gpbdev->cport_desc->id), + gb_gpio_request_handler); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto exit_ggc_free; + } + ggc->connection = connection; gb_connection_set_data(connection, ggc); + ggc->gpbdev = gpbdev; + gb_gpbridge_set_data(gpbdev, ggc); + + ret = gb_connection_enable_tx(connection); + if (ret) + goto exit_connection_destroy; + + ret = gb_gpbridge_get_version(connection); + if (ret) + goto exit_connection_disable; ret = gb_gpio_controller_setup(ggc); if (ret) - goto err_free_controller; + goto exit_connection_disable; irqc = &ggc->irqc; irqc->irq_mask = gb_gpio_irq_mask; @@ -653,9 +676,9 @@ static int gb_gpio_connection_init(struct gb_connection *connection) gpio->label = "greybus_gpio"; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) - gpio->parent = &connection->bundle->dev; + gpio->parent = &gpbdev->dev; #else - gpio->dev = &connection->bundle->dev; + gpio->dev = &gpbdev->dev; #endif gpio->owner = THIS_MODULE; @@ -672,11 +695,15 @@ static int gb_gpio_connection_init(struct gb_connection *connection) gpio->ngpio = ggc->line_max + 1; gpio->can_sleep = true; + ret = gb_connection_enable(connection); + if (ret) + goto exit_line_free; + ret = gpiochip_add(gpio); if (ret) { dev_err(&connection->bundle->dev, "failed to add gpio chip: %d\n", ret); - goto err_free_lines; + goto exit_line_free; } ret = gb_gpio_irqchip_add(gpio, irqc, 0, @@ -684,42 +711,47 @@ static int gb_gpio_connection_init(struct gb_connection *connection) if (ret) { dev_err(&connection->bundle->dev, "failed to add irq chip: %d\n", ret); - goto irqchip_err; + goto exit_gpiochip_remove; } return 0; -irqchip_err: +exit_gpiochip_remove: gb_gpiochip_remove(gpio); -err_free_lines: +exit_line_free: kfree(ggc->lines); -err_free_controller: +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); +exit_ggc_free: kfree(ggc); return ret; } -static void gb_gpio_connection_exit(struct gb_connection *connection) +static void gb_gpio_remove(struct gpbridge_device *gpbdev) { - struct gb_gpio_controller *ggc = gb_connection_get_data(connection); - - if (!ggc) - return; + struct gb_gpio_controller *ggc = gb_gpbridge_get_data(gpbdev); + struct gb_connection *connection = ggc->connection; + gb_connection_disable_rx(connection); gb_gpio_irqchip_remove(ggc); gb_gpiochip_remove(&ggc->chip); - /* kref_put(ggc->connection) */ + gb_connection_disable(connection); + gb_connection_destroy(connection); kfree(ggc->lines); kfree(ggc); } -static struct gb_protocol gpio_protocol = { - .name = "gpio", - .id = GREYBUS_PROTOCOL_GPIO, - .major = GB_GPIO_VERSION_MAJOR, - .minor = GB_GPIO_VERSION_MINOR, - .connection_init = gb_gpio_connection_init, - .connection_exit = gb_gpio_connection_exit, - .request_recv = gb_gpio_request_recv, +static const struct gpbridge_device_id gb_gpio_id_table[] = { + { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_GPIO) }, + { }, }; -gb_builtin_protocol_driver(gpio_protocol); +static struct gpbridge_driver gpio_driver = { + .name = "gpio", + .probe = gb_gpio_probe, + .remove = gb_gpio_remove, + .id_table = gb_gpio_id_table, +}; +gb_gpbridge_builtin_driver(gpio_driver); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index f734646fd30a..ee122ad2334a 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -236,7 +236,6 @@ static void legacy_disconnect(struct gb_bundle *bundle) } static const struct greybus_bundle_id legacy_id_table[] = { - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, -- cgit v1.2.3-59-g8ed1b From dcd2086aa87e274e2f2f96eae449a27548637dd9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 5 May 2016 14:32:34 +0530 Subject: greybus: SDIO: convert to a gpbridge driver This converts the SDIO driver to be a gpbridge driver, moving it away from the "legacy" interface. Testing Done: Tested on gbsim. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: 1.Changed code to retain init/exit fns of drivers. 2.Exit path fix. 3. Fixed review comments] Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 9 +++-- drivers/staging/greybus/gpbridge.h | 4 +- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/sdio.c | 83 ++++++++++++++++++++++++++------------ 4 files changed, 64 insertions(+), 33 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index a65ab1609127..d228e276f788 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -255,6 +255,7 @@ static const struct greybus_bundle_id gb_gpbridge_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, { }, }; @@ -295,8 +296,8 @@ static int __init gpbridge_init(void) pr_err("error initializing uart driver\n"); goto error_uart; } - if (gb_sdio_protocol_init()) { - pr_err("error initializing sdio protocol\n"); + if (gb_sdio_driver_init()) { + pr_err("error initializing sdio driver\n"); goto error_sdio; } if (gb_usb_protocol_init()) { @@ -319,7 +320,7 @@ error_spi: error_i2c: gb_usb_protocol_exit(); error_usb: - gb_sdio_protocol_exit(); + gb_sdio_driver_exit(); error_sdio: gb_uart_driver_exit(); error_uart: @@ -340,7 +341,7 @@ static void __exit gpbridge_exit(void) gb_spi_protocol_exit(); gb_i2c_driver_exit(); gb_usb_protocol_exit(); - gb_sdio_protocol_exit(); + gb_sdio_driver_exit(); gb_uart_driver_exit(); gb_pwm_driver_exit(); gb_gpio_driver_exit(); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index f5161c56cc98..3f1d19eb35f3 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -75,8 +75,8 @@ extern void gb_pwm_driver_exit(void); extern int gb_uart_driver_init(void); extern void gb_uart_driver_exit(void); -extern int gb_sdio_protocol_init(void); -extern void gb_sdio_protocol_exit(void); +extern int gb_sdio_driver_init(void); +extern void gb_sdio_driver_exit(void); extern int gb_usb_protocol_init(void); extern void gb_usb_protocol_exit(void); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index ee122ad2334a..d94282d2dbe5 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -237,7 +237,6 @@ static void legacy_disconnect(struct gb_bundle *bundle) static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, { } diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index d4cbcb972e94..8ee4d4c5e73d 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -19,6 +19,7 @@ struct gb_sdio_host { struct gb_connection *connection; + struct gpbridge_device *gpbdev; struct mmc_host *mmc; struct mmc_request *mrq; struct mutex lock; /* lock for this host */ @@ -199,11 +200,12 @@ static int _gb_sdio_process_events(struct gb_sdio_host *host, u8 event) return 0; } -static int gb_sdio_event_recv(u8 type, struct gb_operation *op) +static int gb_sdio_request_handler(struct gb_operation *op) { struct gb_sdio_host *host = gb_connection_get_data(op->connection); struct gb_message *request; struct gb_sdio_event_request *payload; + u8 type = op->type; int ret = 0; u8 event; @@ -706,27 +708,47 @@ static const struct mmc_host_ops gb_sdio_ops = { .get_cd = gb_mmc_get_cd, }; -static int gb_sdio_connection_init(struct gb_connection *connection) +static int gb_sdio_probe(struct gpbridge_device *gpbdev, + const struct gpbridge_device_id *id) { + struct gb_connection *connection; struct mmc_host *mmc; struct gb_sdio_host *host; size_t max_buffer; int ret = 0; - mmc = mmc_alloc_host(sizeof(*host), &connection->bundle->dev); + mmc = mmc_alloc_host(sizeof(*host), &gpbdev->dev); if (!mmc) return -ENOMEM; + connection = gb_connection_create(gpbdev->bundle, + le16_to_cpu(gpbdev->cport_desc->id), + gb_sdio_request_handler); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto exit_mmc_free; + } + host = mmc_priv(mmc); host->mmc = mmc; host->removed = true; host->connection = connection; gb_connection_set_data(connection, host); + host->gpbdev = gpbdev; + gb_gpbridge_set_data(gpbdev, host); + + ret = gb_connection_enable_tx(connection); + if (ret) + goto exit_connection_destroy; + + ret = gb_gpbridge_get_version(connection); + if (ret) + goto exit_connection_disable; ret = gb_sdio_get_caps(host); if (ret < 0) - goto free_mmc; + goto exit_connection_disable; mmc->ops = &gb_sdio_ops; @@ -740,45 +762,50 @@ static int gb_sdio_connection_init(struct gb_connection *connection) host->xfer_buffer = kzalloc(max_buffer, GFP_KERNEL); if (!host->xfer_buffer) { ret = -ENOMEM; - goto free_mmc; + goto exit_connection_disable; } mutex_init(&host->lock); spin_lock_init(&host->xfer); host->mrq_workqueue = alloc_workqueue("mmc-%s", 0, 1, - dev_name(&connection->bundle->dev)); + dev_name(&gpbdev->dev)); if (!host->mrq_workqueue) { ret = -ENOMEM; - goto free_buffer; + goto exit_buf_free; } INIT_WORK(&host->mrqwork, gb_sdio_mrq_work); + ret = gb_connection_enable(connection); + if (ret) + goto exit_wq_destroy; + ret = mmc_add_host(mmc); if (ret < 0) - goto free_work; + goto exit_wq_destroy; host->removed = false; ret = _gb_sdio_process_events(host, host->queued_events); host->queued_events = 0; return ret; -free_work: +exit_wq_destroy: destroy_workqueue(host->mrq_workqueue); -free_buffer: +exit_buf_free: kfree(host->xfer_buffer); -free_mmc: - gb_connection_set_data(connection, NULL); +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); +exit_mmc_free: mmc_free_host(mmc); return ret; } -static void gb_sdio_connection_exit(struct gb_connection *connection) +static void gb_sdio_remove(struct gpbridge_device *gpbdev) { + struct gb_sdio_host *host = gb_gpbridge_get_data(gpbdev); + struct gb_connection *connection = host->connection; struct mmc_host *mmc; - struct gb_sdio_host *host = gb_connection_get_data(connection); - - if (!host) - return; mutex_lock(&host->lock); host->removed = true; @@ -788,19 +815,23 @@ static void gb_sdio_connection_exit(struct gb_connection *connection) flush_workqueue(host->mrq_workqueue); destroy_workqueue(host->mrq_workqueue); + gb_connection_disable_rx(connection); mmc_remove_host(mmc); + gb_connection_disable(connection); + gb_connection_destroy(connection); kfree(host->xfer_buffer); mmc_free_host(mmc); } -static struct gb_protocol sdio_protocol = { - .name = "sdio", - .id = GREYBUS_PROTOCOL_SDIO, - .major = GB_SDIO_VERSION_MAJOR, - .minor = GB_SDIO_VERSION_MINOR, - .connection_init = gb_sdio_connection_init, - .connection_exit = gb_sdio_connection_exit, - .request_recv = gb_sdio_event_recv, +static const struct gpbridge_device_id gb_sdio_id_table[] = { + { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SDIO) }, + { }, }; -gb_builtin_protocol_driver(sdio_protocol); +static struct gpbridge_driver sdio_driver = { + .name = "sdio", + .probe = gb_sdio_probe, + .remove = gb_sdio_remove, + .id_table = gb_sdio_id_table, +}; +gb_gpbridge_builtin_driver(sdio_driver); -- cgit v1.2.3-59-g8ed1b From ba3e67001b42626dec862410310d30db586074d5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 5 May 2016 14:32:35 +0530 Subject: greybus: SPI: convert to a gpbridge driver This converts the SPI driver to be a gpbridge driver, moving it away from the "legacy" interface. Testing Done: Tested on gbsim. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: 1.Changed code to retain init/exit fns of drivers. 2.Exit path fix. 3. Fixed review comments] Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 7 ++-- drivers/staging/greybus/gpbridge.h | 4 +- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/spi.c | 79 +++++++++++++++++++++++++++----------- 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index d228e276f788..1dc6c8f85778 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -256,6 +256,7 @@ static const struct greybus_bundle_id gb_gpbridge_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, { }, }; @@ -308,8 +309,8 @@ static int __init gpbridge_init(void) pr_err("error initializing i2c driver\n"); goto error_i2c; } - if (gb_spi_protocol_init()) { - pr_err("error initializing spi protocol\n"); + if (gb_spi_driver_init()) { + pr_err("error initializing spi driver\n"); goto error_spi; } @@ -338,7 +339,7 @@ module_init(gpbridge_init); static void __exit gpbridge_exit(void) { - gb_spi_protocol_exit(); + gb_spi_driver_exit(); gb_i2c_driver_exit(); gb_usb_protocol_exit(); gb_sdio_driver_exit(); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 3f1d19eb35f3..ab3900363ec8 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -84,8 +84,8 @@ extern void gb_usb_protocol_exit(void); extern int gb_i2c_driver_init(void); extern void gb_i2c_driver_exit(void); -extern int gb_spi_protocol_init(void); -extern void gb_spi_protocol_exit(void); +extern int gb_spi_driver_init(void); +extern void gb_spi_driver_exit(void); #endif /* __GPBRIDGE_H */ diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index d94282d2dbe5..95d1eda98f72 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -237,7 +237,6 @@ static void legacy_disconnect(struct gb_bundle *bundle) static const struct greybus_bundle_id legacy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, { } }; diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index ce706ed218e2..dc811b142432 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -18,6 +18,7 @@ struct gb_spi { struct gb_connection *connection; + struct gpbridge_device *gpbdev; struct spi_transfer *first_xfer; struct spi_transfer *last_xfer; u32 rx_xfer_offset; @@ -174,7 +175,7 @@ gb_spi_operation_create(struct gb_spi *spi, struct gb_connection *connection, spi->last_xfer = xfer; if (!xfer->tx_buf && !xfer->rx_buf) { - dev_err(&connection->bundle->dev, + dev_err(&spi->gpbdev->dev, "bufferless transfer, length %u\n", xfer->len); msg->state = GB_SPI_STATE_MSG_ERROR; return NULL; @@ -342,7 +343,7 @@ static int gb_spi_transfer_one_message(struct spi_master *master, if (response) gb_spi_decode_response(spi, msg, response); } else { - dev_err(&connection->bundle->dev, + dev_err(&spi->gpbdev->dev, "transfer operation failed: %d\n", ret); msg->state = GB_SPI_STATE_MSG_ERROR; } @@ -450,28 +451,48 @@ static int gb_spi_setup_device(struct gb_spi *spi, u8 cs) return 0; } -static int gb_spi_connection_init(struct gb_connection *connection) +static int gb_spi_probe(struct gpbridge_device *gpbdev, + const struct gpbridge_device_id *id) { + struct gb_connection *connection; struct gb_spi *spi; struct spi_master *master; int ret; u8 i; /* Allocate master with space for data */ - master = spi_alloc_master(&connection->bundle->dev, sizeof(*spi)); + master = spi_alloc_master(&gpbdev->dev, sizeof(*spi)); if (!master) { - dev_err(&connection->bundle->dev, "cannot alloc SPI master\n"); + dev_err(&gpbdev->dev, "cannot alloc SPI master\n"); return -ENOMEM; } + connection = gb_connection_create(gpbdev->bundle, + le16_to_cpu(gpbdev->cport_desc->id), + NULL); + if (IS_ERR(connection)) { + ret = PTR_ERR(connection); + goto exit_spi_put; + } + spi = spi_master_get_devdata(master); spi->connection = connection; gb_connection_set_data(connection, master); + spi->gpbdev = gpbdev; + gb_gpbridge_set_data(gpbdev, master); + + ret = gb_connection_enable(connection); + if (ret) + goto exit_connection_destroy; + + ret = gb_gpbridge_get_version(connection); + if (ret) + goto exit_connection_disable; /* get master configuration */ ret = gb_spi_get_master_config(spi); if (ret) - goto out_put_master; + goto exit_connection_disable; master->bus_num = -1; /* Allow spi-core to allocate it dynamically */ master->num_chipselect = spi->num_chipselect; @@ -486,42 +507,54 @@ static int gb_spi_connection_init(struct gb_connection *connection) ret = spi_register_master(master); if (ret < 0) - goto out_put_master; + goto exit_connection_disable; /* now, fetch the devices configuration */ for (i = 0; i < spi->num_chipselect; i++) { ret = gb_spi_setup_device(spi, i); if (ret < 0) { - dev_err(&connection->bundle->dev, - "failed to allocated spi device: %d\n", ret); - spi_unregister_master(master); - break; + dev_err(&gpbdev->dev, + "failed to allocate spi device %d: %d\n", + i, ret); + goto exit_spi_unregister; } } return ret; -out_put_master: +exit_spi_unregister: + spi_unregister_master(master); +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); +exit_spi_put: spi_master_put(master); return ret; } -static void gb_spi_connection_exit(struct gb_connection *connection) +static void gb_spi_remove(struct gpbridge_device *gpbdev) { - struct spi_master *master = gb_connection_get_data(connection); + struct spi_master *master = gb_gpbridge_get_data(gpbdev); + struct gb_spi *spi = spi_master_get_devdata(master); + struct gb_connection *connection = spi->connection; spi_unregister_master(master); + gb_connection_disable(connection); + gb_connection_destroy(connection); + spi_master_put(master); } -static struct gb_protocol spi_protocol = { - .name = "spi", - .id = GREYBUS_PROTOCOL_SPI, - .major = GB_SPI_VERSION_MAJOR, - .minor = GB_SPI_VERSION_MINOR, - .connection_init = gb_spi_connection_init, - .connection_exit = gb_spi_connection_exit, - .request_recv = NULL, +static const struct gpbridge_device_id gb_spi_id_table[] = { + { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, + { }, }; -gb_builtin_protocol_driver(spi_protocol); +static struct gpbridge_driver spi_driver = { + .name = "spi", + .probe = gb_spi_probe, + .remove = gb_spi_remove, + .id_table = gb_spi_id_table, +}; +gb_gpbridge_builtin_driver(spi_driver); -- cgit v1.2.3-59-g8ed1b From 4dda7e96cfc6474d88682547180a9e42687c5a6e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 5 May 2016 14:32:36 +0530 Subject: greybus: USB: convert to a gpbridge driver This converts the USB driver to be a gpbridge driver, moving it away from the "legacy" interface. It's not like this code even does anything at the moment, how much trouble could we cause with this change? :) Testing Done: Tested on gbsim. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> [vaibhav.hiremath@linaro.org: 1.Changed code to retain init/exit fns of drivers. 2.Exit path fix. 3. Fixed review comments] Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 11 +++--- drivers/staging/greybus/gpbridge.h | 4 +-- drivers/staging/greybus/legacy.c | 1 - drivers/staging/greybus/usb.c | 68 ++++++++++++++++++++++++++------------ 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 1dc6c8f85778..993cfacb65aa 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -258,6 +258,7 @@ static const struct greybus_bundle_id gb_gpbridge_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, { }, }; MODULE_DEVICE_TABLE(greybus, gb_gpbridge_id_table); @@ -301,8 +302,8 @@ static int __init gpbridge_init(void) pr_err("error initializing sdio driver\n"); goto error_sdio; } - if (gb_usb_protocol_init()) { - pr_err("error initializing usb protocol\n"); + if (gb_usb_driver_init()) { + pr_err("error initializing usb driver\n"); goto error_usb; } if (gb_i2c_driver_init()) { @@ -319,7 +320,7 @@ static int __init gpbridge_init(void) error_spi: gb_i2c_driver_exit(); error_i2c: - gb_usb_protocol_exit(); + gb_usb_driver_exit(); error_usb: gb_sdio_driver_exit(); error_sdio: @@ -333,7 +334,7 @@ error_gpio: error_gpbridge: bus_unregister(&gpbridge_bus_type); ida_destroy(&gpbridge_id); - return -EPROTO; + return retval; } module_init(gpbridge_init); @@ -341,7 +342,7 @@ static void __exit gpbridge_exit(void) { gb_spi_driver_exit(); gb_i2c_driver_exit(); - gb_usb_protocol_exit(); + gb_usb_driver_exit(); gb_sdio_driver_exit(); gb_uart_driver_exit(); gb_pwm_driver_exit(); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index ab3900363ec8..1af20d88af4b 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -78,8 +78,8 @@ extern void gb_uart_driver_exit(void); extern int gb_sdio_driver_init(void); extern void gb_sdio_driver_exit(void); -extern int gb_usb_protocol_init(void); -extern void gb_usb_protocol_exit(void); +extern int gb_usb_driver_init(void); +extern void gb_usb_driver_exit(void); extern int gb_i2c_driver_init(void); extern void gb_i2c_driver_exit(void); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 95d1eda98f72..06bd85cc4459 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -236,7 +236,6 @@ static void legacy_disconnect(struct gb_bundle *bundle) } static const struct greybus_bundle_id legacy_id_table[] = { - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, { } }; diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 25a6c7e5e5ba..2b4789bde0c2 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -38,6 +38,7 @@ struct gb_usb_hub_control_response { struct gb_usb_device { struct gb_connection *connection; + struct gpbridge_device *gpbdev; }; static inline struct gb_usb_device *to_gb_usb_device(struct usb_hcd *hcd) @@ -58,8 +59,7 @@ static void hcd_stop(struct usb_hcd *hcd) ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, NULL, 0, NULL, 0); if (ret) - dev_err(&dev->connection->bundle->dev, - "HCD stop failed '%d'\n", ret); + dev_err(&dev->gpbdev->dev, "HCD stop failed '%d'\n", ret); } static int hcd_start(struct usb_hcd *hcd) @@ -71,8 +71,7 @@ static int hcd_start(struct usb_hcd *hcd) ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, NULL, 0, NULL, 0); if (ret) { - dev_err(&dev->connection->bundle->dev, - "HCD start failed '%d'\n", ret); + dev_err(&dev->gpbdev->dev, "HCD start failed '%d'\n", ret); return ret; } @@ -162,24 +161,43 @@ static struct hc_driver usb_gb_hc_driver = { .hub_control = hub_control, }; -static int gb_usb_connection_init(struct gb_connection *connection) +static int gb_usb_probe(struct gpbridge_device *gpbdev, + const struct gpbridge_device_id *id) { - struct device *dev = &connection->bundle->dev; + struct gb_connection *connection; + struct device *dev = &gpbdev->dev; struct gb_usb_device *gb_usb_dev; struct usb_hcd *hcd; - int retval; hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); if (!hcd) return -ENOMEM; + connection = gb_connection_create(gpbdev->bundle, + le16_to_cpu(gpbdev->cport_desc->id), + NULL); + if (IS_ERR(connection)) { + retval = PTR_ERR(connection); + goto exit_usb_put; + } + gb_usb_dev = to_gb_usb_device(hcd); gb_usb_dev->connection = connection; gb_connection_set_data(connection, gb_usb_dev); + gb_usb_dev->gpbdev = gpbdev; + gb_gpbridge_set_data(gpbdev, gb_usb_dev); hcd->has_tt = 1; + retval = gb_connection_enable(connection); + if (retval) + goto exit_connection_destroy; + + retval = gb_gpbridge_get_version(connection); + if (retval) + goto exit_connection_disable; + /* * FIXME: The USB bridged-PHY protocol driver depends on changes to * USB core which are not yet upstream. @@ -189,38 +207,46 @@ static int gb_usb_connection_init(struct gb_connection *connection) if (1) { dev_warn(dev, "USB protocol disabled\n"); retval = -EPROTONOSUPPORT; - goto err_put_hcd; + goto exit_connection_disable; } retval = usb_add_hcd(hcd, 0, 0); if (retval) - goto err_put_hcd; + goto exit_connection_disable; return 0; -err_put_hcd: +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); +exit_usb_put: usb_put_hcd(hcd); return retval; } -static void gb_usb_connection_exit(struct gb_connection *connection) +static void gb_usb_remove(struct gpbridge_device *gpbdev) { - struct gb_usb_device *gb_usb_dev = gb_connection_get_data(connection); + struct gb_usb_device *gb_usb_dev = gb_gpbridge_get_data(gpbdev); + struct gb_connection *connection = gb_usb_dev->connection; struct usb_hcd *hcd = gb_usb_device_to_hcd(gb_usb_dev); usb_remove_hcd(hcd); + gb_connection_disable(connection); + gb_connection_destroy(connection); usb_put_hcd(hcd); } -static struct gb_protocol usb_protocol = { - .name = "usb", - .id = GREYBUS_PROTOCOL_USB, - .major = GB_USB_VERSION_MAJOR, - .minor = GB_USB_VERSION_MINOR, - .connection_init = gb_usb_connection_init, - .connection_exit = gb_usb_connection_exit, - .request_recv = NULL, /* FIXME we have requests!!! */ +static const struct gpbridge_device_id gb_usb_id_table[] = { + { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_USB) }, + { }, }; -gb_builtin_protocol_driver(usb_protocol); +static struct gpbridge_driver usb_driver = { + .name = "usb", + .probe = gb_usb_probe, + .remove = gb_usb_remove, + .id_table = gb_usb_id_table, +}; +gb_gpbridge_builtin_driver(usb_driver); -- cgit v1.2.3-59-g8ed1b From ceadf9def1329c856fe94ca8aecc9ff7866e827b Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Thu, 5 May 2016 14:32:37 +0530 Subject: greybus: Documentation: Add sysfs information about bridged-phy device Update the documentation (sysfs-bus-greybus) for recent conversion of bridged-phy drivers to real drivers. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 3740173e3cd0..8605d705b617 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -162,6 +162,14 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The interface-unique id of the bundle B. +What: /sys/bus/greybus/devices/N-M.I.B/gpbX +Date: April 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + The General Purpose Bridged PHY device of the bundle B, + where X is a dynamically assigned 0-based id. + What: /sys/bus/greybus/devices/N-M.I.B/state Date: October 2015 KernelVersion: 4.XX -- cgit v1.2.3-59-g8ed1b From 73d292d8381d9c384796c876ba752ccd18a09d1f Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 5 May 2016 14:32:38 +0530 Subject: greybus: uart: Kill reference_count hack This was done long back and was probably the best bet then, but it looks really bad to have it this way now. Kill the hack and implement proper driver init()/exit() routines to do the same thing. Tested using gbsim by hotplugging uart manifest and removing it later. Also tried removing the gb-phy module to test the exit path properly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/uart.c | 51 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index eb83c04d7b69..0d8fcb55d9aa 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -65,7 +65,6 @@ struct gb_tty { static struct tty_driver *gb_tty_driver; static DEFINE_IDR(tty_minors); static DEFINE_MUTEX(table_lock); -static atomic_t reference_count = ATOMIC_INIT(0); static int gb_uart_receive_data_handler(struct gb_operation *op) { @@ -553,6 +552,7 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) } while (!retval); return retval; + } static int get_serial_usage(struct gb_tty *gb_tty, @@ -619,9 +619,6 @@ static const struct tty_operations gb_ops = { static struct tty_port_operations null_ops = { }; -static int gb_tty_init(void); -static void gb_tty_exit(void); - static int gb_uart_probe(struct gpbridge_device *gpbdev, const struct gpbridge_device_id *id) { @@ -632,20 +629,9 @@ static int gb_uart_probe(struct gpbridge_device *gpbdev, int retval; int minor; - /* First time here, initialize the tty structures */ - if (atomic_inc_return(&reference_count) == 1) { - retval = gb_tty_init(); - if (retval) { - atomic_dec(&reference_count); - return retval; - } - } - gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); - if (!gb_tty) { - retval = -ENOMEM; - goto exit_tty; - } + if (!gb_tty) + return -ENOMEM; connection = gb_connection_create(gpbdev->bundle, le16_to_cpu(gpbdev->cport_desc->id), @@ -737,9 +723,6 @@ exit_connection_destroy: gb_connection_destroy(connection); exit_tty_free: kfree(gb_tty); -exit_tty: - if (atomic_dec_return(&reference_count) == 0) - gb_tty_exit(); return retval; } @@ -772,10 +755,6 @@ static void gb_uart_remove(struct gpbridge_device *gpbdev) gb_connection_destroy(connection); kfree(gb_tty->buffer); kfree(gb_tty); - - /* If last device is gone, tear down the tty structures */ - if (atomic_dec_return(&reference_count) == 0) - gb_tty_exit(); } static int gb_tty_init(void) @@ -832,4 +811,26 @@ static struct gpbridge_driver uart_driver = { .remove = gb_uart_remove, .id_table = gb_uart_id_table, }; -gb_gpbridge_builtin_driver(uart_driver); + +int gb_uart_driver_init(void) +{ + int ret; + + ret = gb_tty_init(); + if (ret) + return ret; + + ret = gb_gpbridge_register(&uart_driver); + if (ret) { + gb_tty_exit(); + return ret; + } + + return 0; +} + +void gb_uart_driver_exit(void) +{ + gb_gpbridge_deregister(&uart_driver); + gb_tty_exit(); +} -- cgit v1.2.3-59-g8ed1b From 5015d5cff9ff0aabbe744522f3c5b5a945a649ef Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 5 May 2016 15:34:54 +0100 Subject: greybus: greybus_trace.h: Fix dodgy indentation We should use tabs not spaces when indenting multi-line macros and ensure that the relevant '\' characters are aligned to each-other. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 6f3e10164e3c..849cfa095aac 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -17,9 +17,9 @@ struct gb_message; struct gb_host_device; -#define gb_bundle_name(message) \ - (message->operation->connection->bundle ? \ - dev_name(&message->operation->connection->bundle->dev) : \ +#define gb_bundle_name(message) \ + (message->operation->connection->bundle ? \ + dev_name(&message->operation->connection->bundle->dev) : \ dev_name(&message->operation->connection->hd->svc->dev)) DECLARE_EVENT_CLASS(gb_message, -- cgit v1.2.3-59-g8ed1b From 596e4a1933e4f6f2b19d5c05884d35cb91e3cf58 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 5 May 2016 15:34:55 +0100 Subject: greybus: greybus_protocols.h Add SVC_TIMESYNC_WAKE_PINS_ACQUIRE/RELEASE Its necessary to establish an initial state on the wake-detect lines before enabling timesync on APB/GPB in order to ensure all GPIO edge-transitions are correctly interpreted by the receiving processor. This patch adds the operations defined in the Greybus specification to greybus_protocols.h, this involves adding the SVC_TIMESYNC_WAKE_PINS_ACQUIRE and SVC_TIMESYNC_WAKE_PINS_RELEASE commands and moving the 'strobe_mask' parameter from 'struct gb_svc_timesync_enable_request' to 'struct gb_svc_timesync_wd_pins_acquire_request' since the communication of the strobe_mask will be communicated before preparing any of the bridges to receive the TimeSync pulses. A separate patch to the greybus specification describes the transition between the wake sub-state and the timesync sub-state. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 5f5b92864ac2..d5a63d38a28a 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -835,6 +835,8 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET 0x15 #define GB_SVC_TYPE_PWRMON_SAMPLE_GET 0x16 #define GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET 0x17 +#define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_ACQUIRE 0x18 +#define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE 0x19 #define GB_SVC_TYPE_MODULE_INSERTED 0x1f #define GB_SVC_TYPE_MODULE_REMOVED 0x20 #define GB_SVC_TYPE_INTF_ACTIVATE 0x27 @@ -955,7 +957,6 @@ struct gb_svc_timesync_enable_request { __u8 count; __le64 frame_time; __le32 strobe_delay; - __le32 strobe_mask; __le32 refclk; } __packed; /* timesync enable response has no payload */ @@ -965,6 +966,15 @@ struct gb_svc_timesync_authoritative_response { __le64 frame_time[GB_TIMESYNC_MAX_STROBES]; }; +struct gb_svc_timesync_wake_pins_acquire_request { + __le32 strobe_mask; +}; + +/* timesync wake pins acquire response has no payload */ + +/* timesync wake pins release request has no payload */ +/* timesync wake pins release response has no payload */ + #define GB_SVC_UNIPRO_FAST_MODE 0x01 #define GB_SVC_UNIPRO_SLOW_MODE 0x02 #define GB_SVC_UNIPRO_FAST_AUTO_MODE 0x04 -- cgit v1.2.3-59-g8ed1b From 521d0d538036267fcc1bfab9f2f6e550250caf72 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 5 May 2016 15:34:56 +0100 Subject: greybus: greybus_protocols.h Add SVC_TIMESYNC_PING In order to verify TimeSync functionality we require a TimeSync-ping operation where the AP can command the SVC to initiate a single strobe of downstream TimeSync slaves, and report the FrameTime at the strobe. Ping will only be valid after the system has transitioned to a TIMESYNC_ACTIVE state. In the active state each TimeSync slave will graph a single incoming SVC strobe as a ping and will store its frame time. The AP will then gather each last-event FrameTime for presentation to user-space in the AP and/or further automation based on the reported FrameTimes at the SVC ping. This patch adds the SVC ping command definition to greybus_protocols.h. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index d5a63d38a28a..9b21a35292d1 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -837,6 +837,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET 0x17 #define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_ACQUIRE 0x18 #define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE 0x19 +#define GB_SVC_TYPE_TIMESYNC_PING 0x1a #define GB_SVC_TYPE_MODULE_INSERTED 0x1f #define GB_SVC_TYPE_MODULE_REMOVED 0x20 #define GB_SVC_TYPE_INTF_ACTIVATE 0x27 @@ -975,6 +976,11 @@ struct gb_svc_timesync_wake_pins_acquire_request { /* timesync wake pins release request has no payload */ /* timesync wake pins release response has no payload */ +/* timesync svc ping request has no payload */ +struct gb_svc_timesync_ping_response { + __le64 frame_time; +} __packed; + #define GB_SVC_UNIPRO_FAST_MODE 0x01 #define GB_SVC_UNIPRO_SLOW_MODE 0x02 #define GB_SVC_UNIPRO_FAST_AUTO_MODE 0x04 -- cgit v1.2.3-59-g8ed1b From 6c1ca56d85377498bec760064c02e33f4678a2dd Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Thu, 5 May 2016 15:34:57 +0100 Subject: greybus: arche-platform: Rename spinlock variable lock => wake_detect_lock With addition of exported function, required for TIMESYNC operation, we need more locking mechanism for driver state, so to avoid confusion rename existing spinlock variable to its appropriate name. Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index d1083cfbc081..9a117176ad50 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -55,7 +55,7 @@ struct arche_platform_drvdata { enum svc_wakedetect_state wake_detect_state; int wake_detect_irq; - spinlock_t lock; + spinlock_t wake_lock; /* Protect wake_detect_state */ unsigned long wake_detect_start; struct notifier_block pm_notifier; @@ -117,15 +117,15 @@ static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) struct arche_platform_drvdata *arche_pdata = devid; unsigned long flags; - spin_lock_irqsave(&arche_pdata->lock, flags); + spin_lock_irqsave(&arche_pdata->wake_lock, flags); if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { /* Something is wrong */ - spin_unlock_irqrestore(&arche_pdata->lock, flags); + spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); return IRQ_HANDLED; } arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START; - spin_unlock_irqrestore(&arche_pdata->lock, flags); + spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); /* It should complete power cycle, so first make sure it is poweroff */ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); @@ -137,9 +137,9 @@ static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) if (usb3613_hub_mode_ctrl(true)) dev_warn(arche_pdata->dev, "failed to control hub device\n"); - spin_lock_irqsave(&arche_pdata->lock, flags); + spin_lock_irqsave(&arche_pdata->wake_lock, flags); arche_pdata->wake_detect_state = WD_STATE_IDLE; - spin_unlock_irqrestore(&arche_pdata->lock, flags); + spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); return IRQ_HANDLED; } @@ -149,7 +149,7 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) struct arche_platform_drvdata *arche_pdata = devid; unsigned long flags; - spin_lock_irqsave(&arche_pdata->lock, flags); + spin_lock_irqsave(&arche_pdata->wake_lock, flags); if (gpio_get_value(arche_pdata->wake_detect_gpio)) { /* wake/detect rising */ @@ -170,7 +170,9 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) WD_STATE_COLDBOOT_START) { arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_TRIG; - spin_unlock_irqrestore(&arche_pdata->lock, flags); + spin_unlock_irqrestore( + &arche_pdata->wake_lock, + flags); return IRQ_WAKE_THREAD; } } @@ -188,7 +190,7 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) } } - spin_unlock_irqrestore(&arche_pdata->lock, flags); + spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); return IRQ_HANDLED; } @@ -257,9 +259,9 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda /* Send disconnect/detach event to SVC */ gpio_direction_output(arche_pdata->wake_detect_gpio, 0); usleep_range(100, 200); - spin_lock_irqsave(&arche_pdata->lock, flags); + spin_lock_irqsave(&arche_pdata->wake_lock, flags); arche_pdata->wake_detect_state = WD_STATE_IDLE; - spin_unlock_irqrestore(&arche_pdata->lock, flags); + spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); clk_disable_unprepare(arche_pdata->svc_ref_clk); } @@ -465,7 +467,7 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->dev = &pdev->dev; - spin_lock_init(&arche_pdata->lock); + spin_lock_init(&arche_pdata->wake_lock); arche_pdata->wake_detect_irq = gpio_to_irq(arche_pdata->wake_detect_gpio); -- cgit v1.2.3-59-g8ed1b From 886aba558b9e63c7025cabfaff8ca17f9fc02985 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Thu, 5 May 2016 15:34:58 +0100 Subject: greybus: arche-platform: Export fn to allow timesync driver to change the state With the addition of the timesync driver and looking at the hardware interfaces we have, its clear we need to add a new arche-platform state. This patch adds ARCHE_PLATFORM_STATE_TIME_SYNC to the arche-platform driver to facilitate transition to the TIME_SYNC state if-and-only-if the arche-platform driver is in the ACTIVE state. This is mainly needed as wake/detect lines are shared between TIMESYNC operation and basic control functionality of APBs. So during TIMESYNC we want to make sure that the events on wake/detect lines are ignored by the arche-platform APB reset logic. This patch adds one exported function, which can be invoked from timesync driver code, allowing, switching between ARCHE_PLATFORM_STATE_TIME_SYNC <=> ARCHE_PLATFORM_STATE_ACTIVE states. [ bryan.odonoghue@linaro.org: Added mutex, massaged commit text ] Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Suggested-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 125 +++++++++++++++++++++++++++++-- drivers/staging/greybus/arche_platform.h | 2 + 2 files changed, 119 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 9a117176ad50..4eceec39ae99 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -55,13 +55,97 @@ struct arche_platform_drvdata { enum svc_wakedetect_state wake_detect_state; int wake_detect_irq; - spinlock_t wake_lock; /* Protect wake_detect_state */ + spinlock_t wake_lock; /* Protect wake_detect_state */ + struct mutex platform_state_mutex; /* Protect state */ unsigned long wake_detect_start; struct notifier_block pm_notifier; struct device *dev; }; +/* + * arche_platform_change_state: Change the operational state + * + * This exported function allows external drivers to change the state + * of the arche-platform driver. + * Note that this function only supports transitions between two states + * with limited functionality. + * + * - ARCHE_PLATFORM_STATE_TIME_SYNC: + * Once set, allows timesync operations between SVC <=> AP and makes + * sure that arche-platform driver ignores any subsequent events/pulses + * from SVC over wake/detect. + * + * - ARCHE_PLATFORM_STATE_ACTIVE: + * Puts back driver to active state, where any pulse from SVC on wake/detect + * line would trigger either cold/standby boot. + * Note: Transition request from this function does not trigger cold/standby + * boot. It just puts back driver book keeping variable back to ACTIVE + * state and restores the interrupt. + * + * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently + * satisfy the requested state-transition or -EINVAL for all other + * state-transition requests. + */ +int arche_platform_change_state(enum arche_platform_state state) +{ + struct arche_platform_drvdata *arche_pdata; + struct platform_device *pdev; + struct device_node *np; + int ret = -EAGAIN; + unsigned long flags; + + np = of_find_compatible_node(NULL, NULL, "google,arche-platform"); + if (!np) { + pr_err("google,arche-platform device node not found\n"); + return -ENODEV; + } + + pdev = of_find_device_by_node(np); + if (!pdev) { + pr_err("arche-platform device not found\n"); + return -ENODEV; + } + + arche_pdata = platform_get_drvdata(pdev); + + mutex_lock(&arche_pdata->platform_state_mutex); + spin_lock_irqsave(&arche_pdata->wake_lock, flags); + if (arche_pdata->wake_detect_state != WD_STATE_IDLE) { + dev_err(arche_pdata->dev, + "driver busy with wake/detect line ops\n"); + goto exit; + } + + if (arche_pdata->state == state) { + ret = 0; + goto exit; + } + + if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF || + arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY || + arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) { + dev_err(arche_pdata->dev, "busy, request to retry later\n"); + goto exit; + } + + if (state == ARCHE_PLATFORM_STATE_TIME_SYNC) { + disable_irq(arche_pdata->wake_detect_irq); + arche_pdata->state = ARCHE_PLATFORM_STATE_TIME_SYNC; + } else if (state == ARCHE_PLATFORM_STATE_ACTIVE) { + arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE; + enable_irq(arche_pdata->wake_detect_irq); + } else { + dev_err(arche_pdata->dev, "invalid state transition request\n"); + } +exit: + spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); + mutex_unlock(&arche_pdata->platform_state_mutex); + of_node_put(np); + return ret; +} +EXPORT_SYMBOL_GPL(arche_platform_change_state); + static inline void svc_reset_onoff(unsigned int gpio, bool onoff) { gpio_set_value(gpio, onoff); @@ -195,6 +279,9 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) return IRQ_HANDLED; } +/* + * Requires arche_pdata->platform_state_mutex to be held + */ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) { int ret; @@ -226,6 +313,9 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat return 0; } +/* + * Requires arche_pdata->platform_state_mutex to be held + */ static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) @@ -246,6 +336,9 @@ static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_ } +/* + * Requires arche_pdata->platform_state_mutex to be held + */ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) { unsigned long flags; @@ -280,9 +373,11 @@ static ssize_t state_store(struct device *dev, struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); int ret = 0; + mutex_lock(&arche_pdata->platform_state_mutex); + if (sysfs_streq(buf, "off")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) - return count; + goto exit; /* If SVC goes down, bring down APB's as well */ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); @@ -291,19 +386,19 @@ static ssize_t state_store(struct device *dev, } else if (sysfs_streq(buf, "active")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) - return count; + goto exit; ret = arche_platform_coldboot_seq(arche_pdata); assert_wakedetect(arche_pdata); } else if (sysfs_streq(buf, "standby")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) - return count; + goto exit; dev_warn(arche_pdata->dev, "standby state not supported\n"); } else if (sysfs_streq(buf, "fw_flashing")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) - return count; + goto exit; /* First we want to make sure we power off everything * and then enter FW flashing state */ @@ -319,6 +414,8 @@ static ssize_t state_store(struct device *dev, ret = -EINVAL; } +exit: + mutex_unlock(&arche_pdata->platform_state_mutex); return ret ? ret : count; } @@ -336,6 +433,8 @@ static ssize_t state_show(struct device *dev, return sprintf(buf, "standby\n"); case ARCHE_PLATFORM_STATE_FW_FLASHING: return sprintf(buf, "fw_flashing\n"); + case ARCHE_PLATFORM_STATE_TIME_SYNC: + return sprintf(buf, "time_sync\n"); default: return sprintf(buf, "unknown state\n"); } @@ -349,11 +448,15 @@ static int arche_platform_pm_notifier(struct notifier_block *notifier, struct arche_platform_drvdata *arche_pdata = container_of(notifier, struct arche_platform_drvdata, pm_notifier); + int ret = NOTIFY_DONE; + mutex_lock(&arche_pdata->platform_state_mutex); switch (pm_event) { case PM_SUSPEND_PREPARE: - if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) - return NOTIFY_STOP; + if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { + ret = NOTIFY_STOP; + break; + } device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); arche_platform_poweroff_seq(arche_pdata); break; @@ -364,8 +467,9 @@ static int arche_platform_pm_notifier(struct notifier_block *notifier, default: break; } + mutex_unlock(&arche_pdata->platform_state_mutex); - return NOTIFY_DONE; + return ret; } static int arche_platform_probe(struct platform_device *pdev) @@ -468,6 +572,7 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->dev = &pdev->dev; spin_lock_init(&arche_pdata->wake_lock); + mutex_init(&arche_pdata->platform_state_mutex); arche_pdata->wake_detect_irq = gpio_to_irq(arche_pdata->wake_detect_gpio); @@ -489,6 +594,7 @@ static int arche_platform_probe(struct platform_device *pdev) return ret; } + mutex_lock(&arche_pdata->platform_state_mutex); ret = arche_platform_coldboot_seq(arche_pdata); if (ret) { dev_err(dev, "Failed to cold boot svc %d\n", ret); @@ -505,6 +611,8 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; ret = register_pm_notifier(&arche_pdata->pm_notifier); + mutex_unlock(&arche_pdata->platform_state_mutex); + if (ret) { dev_err(dev, "failed to register pm notifier %d\n", ret); goto err_populate; @@ -516,6 +624,7 @@ static int arche_platform_probe(struct platform_device *pdev) err_populate: arche_platform_poweroff_seq(arche_pdata); err_coldboot: + mutex_unlock(&arche_pdata->platform_state_mutex); device_remove_file(&pdev->dev, &dev_attr_state); return ret; } diff --git a/drivers/staging/greybus/arche_platform.h b/drivers/staging/greybus/arche_platform.h index 69b627b978b8..0f2d29208f7f 100644 --- a/drivers/staging/greybus/arche_platform.h +++ b/drivers/staging/greybus/arche_platform.h @@ -15,8 +15,10 @@ enum arche_platform_state { ARCHE_PLATFORM_STATE_ACTIVE, ARCHE_PLATFORM_STATE_STANDBY, ARCHE_PLATFORM_STATE_FW_FLASHING, + ARCHE_PLATFORM_STATE_TIME_SYNC, }; +int arche_platform_change_state(enum arche_platform_state state); int __init arche_apb_init(void); void __exit arche_apb_exit(void); -- cgit v1.2.3-59-g8ed1b From c4058b7926a44387b1d2480be0f209edb74e7f1c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 5 May 2016 15:34:59 +0100 Subject: greybus: arche-platform: Rework platform/wd-line state transition logic This patch simplifies and improves the readability of the internal state transition logic of arche platform driver state transition logic by: 1. Making a dedicated function to state-transition the platform code. 2. Condense the decision to swtich states down to a single switch statement instead of four separate if/else clauses. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Suggested-by: Alex Elder <elder@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 73 +++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 4eceec39ae99..83d823cf5232 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -63,6 +63,13 @@ struct arche_platform_drvdata { struct device *dev; }; +/* Requires calling context to hold arche_pdata->spinlock */ +static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata, + enum arche_platform_state state) +{ + arche_pdata->state = state; +} + /* * arche_platform_change_state: Change the operational state * @@ -122,22 +129,26 @@ int arche_platform_change_state(enum arche_platform_state state) goto exit; } - if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF || - arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY || - arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) { - dev_err(arche_pdata->dev, "busy, request to retry later\n"); - goto exit; - } - - if (state == ARCHE_PLATFORM_STATE_TIME_SYNC) { + switch (state) { + case ARCHE_PLATFORM_STATE_TIME_SYNC: disable_irq(arche_pdata->wake_detect_irq); - arche_pdata->state = ARCHE_PLATFORM_STATE_TIME_SYNC; - } else if (state == ARCHE_PLATFORM_STATE_ACTIVE) { - arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE; + break; + case ARCHE_PLATFORM_STATE_ACTIVE: enable_irq(arche_pdata->wake_detect_irq); - } else { - dev_err(arche_pdata->dev, "invalid state transition request\n"); + break; + case ARCHE_PLATFORM_STATE_OFF: + case ARCHE_PLATFORM_STATE_STANDBY: + case ARCHE_PLATFORM_STATE_FW_FLASHING: + dev_err(arche_pdata->dev, "busy, request to retry later\n"); + goto exit; + default: + ret = -EINVAL; + dev_err(arche_pdata->dev, + "invalid state transition request\n"); + goto exit; } + arche_platform_set_state(arche_pdata, state); + ret = 0; exit: spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); mutex_unlock(&arche_pdata->platform_state_mutex); @@ -146,6 +157,14 @@ exit: } EXPORT_SYMBOL_GPL(arche_platform_change_state); +/* Requires arche_pdata->wake_lock is held by calling context */ +static void arche_platform_set_wake_detect_state( + struct arche_platform_drvdata *arche_pdata, + enum svc_wakedetect_state state) +{ + arche_pdata->wake_detect_state = state; +} + static inline void svc_reset_onoff(unsigned int gpio, bool onoff) { gpio_set_value(gpio, onoff); @@ -208,7 +227,8 @@ static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) return IRQ_HANDLED; } - arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START; + arche_platform_set_wake_detect_state(arche_pdata, + WD_STATE_COLDBOOT_START); spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); /* It should complete power cycle, so first make sure it is poweroff */ @@ -222,7 +242,7 @@ static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) dev_warn(arche_pdata->dev, "failed to control hub device\n"); spin_lock_irqsave(&arche_pdata->wake_lock, flags); - arche_pdata->wake_detect_state = WD_STATE_IDLE; + arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); return IRQ_HANDLED; @@ -247,13 +267,14 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) if (time_before(jiffies, arche_pdata->wake_detect_start + msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { - arche_pdata->wake_detect_state = WD_STATE_IDLE; + arche_platform_set_wake_detect_state(arche_pdata, + WD_STATE_IDLE); } else { /* Check we are not in middle of irq thread already */ if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_START) { - arche_pdata->wake_detect_state = - WD_STATE_COLDBOOT_TRIG; + arche_platform_set_wake_detect_state(arche_pdata, + WD_STATE_COLDBOOT_TRIG); spin_unlock_irqrestore( &arche_pdata->wake_lock, flags); @@ -270,7 +291,8 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) * it is meant for coldboot and set the flag. If wake/detect line stays low * beyond 30msec, then it is coldboot else fallback to standby boot. */ - arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT; + arche_platform_set_wake_detect_state(arche_pdata, + WD_STATE_BOOT_INIT); } } @@ -308,7 +330,7 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat svc_reset_onoff(arche_pdata->svc_reset_gpio, !arche_pdata->is_reset_act_hi); - arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE; + arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE); return 0; } @@ -332,7 +354,7 @@ static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_ svc_reset_onoff(arche_pdata->svc_reset_gpio, !arche_pdata->is_reset_act_hi); - arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING; + arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING); } @@ -353,7 +375,8 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda gpio_direction_output(arche_pdata->wake_detect_gpio, 0); usleep_range(100, 200); spin_lock_irqsave(&arche_pdata->wake_lock, flags); - arche_pdata->wake_detect_state = WD_STATE_IDLE; + arche_platform_set_wake_detect_state(arche_pdata, + WD_STATE_IDLE); spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); clk_disable_unprepare(arche_pdata->svc_ref_clk); @@ -363,7 +386,7 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda svc_reset_onoff(arche_pdata->svc_reset_gpio, arche_pdata->is_reset_act_hi); - arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; + arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); } static ssize_t state_store(struct device *dev, @@ -502,7 +525,7 @@ static int arche_platform_probe(struct platform_device *pdev) dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); return ret; } - arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; + arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, "svc,sysboot-gpio", 0); @@ -567,7 +590,7 @@ static int arche_platform_probe(struct platform_device *pdev) } /* deassert wake detect */ gpio_direction_output(arche_pdata->wake_detect_gpio, 0); - arche_pdata->wake_detect_state = WD_STATE_IDLE; + arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); arche_pdata->dev = &pdev->dev; -- cgit v1.2.3-59-g8ed1b From f63a896e532b12f9104749b13861080981b64bad Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 5 May 2016 15:33:19 +0530 Subject: greybus: fw-download: Manage firmware requests with kref This patch updates the fw-download core to manage firmware requests with kref. This is required for the next patch, which will introduce timeouts for firmware downloads. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-download.c | 57 ++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index e60abde2d704..6bd26180bca6 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -19,6 +19,9 @@ struct fw_request { char name[FW_NAME_LEN]; const struct firmware *fw; struct list_head node; + + struct kref kref; + struct fw_download *fw_download; }; struct fw_download { @@ -28,26 +31,49 @@ struct fw_download { struct ida id_map; }; -static struct fw_request *match_firmware(struct fw_download *fw_download, - u8 firmware_id) +static void fw_req_release(struct kref *kref) +{ + struct fw_request *fw_req = container_of(kref, struct fw_request, kref); + + dev_dbg(fw_req->fw_download->parent, "firmware %s released\n", + fw_req->name); + + release_firmware(fw_req->fw); + ida_simple_remove(&fw_req->fw_download->id_map, fw_req->firmware_id); + kfree(fw_req); +} + +/* Caller must call put_fw_req() after using struct fw_request */ +static struct fw_request *get_fw_req(struct fw_download *fw_download, + u8 firmware_id) { struct fw_request *fw_req; list_for_each_entry(fw_req, &fw_download->fw_requests, node) { - if (fw_req->firmware_id == firmware_id) + if (fw_req->firmware_id == firmware_id) { + kref_get(&fw_req->kref); return fw_req; + } } return NULL; } +/* + * Incoming requests are serialized for a connection, and this will never be + * racy. + */ +static void put_fw_req(struct fw_request *fw_req) +{ + kref_put(&fw_req->kref, fw_req_release); +} + static void free_firmware(struct fw_download *fw_download, struct fw_request *fw_req) { list_del(&fw_req->node); - release_firmware(fw_req->fw); - ida_simple_remove(&fw_download->id_map, fw_req->firmware_id); - kfree(fw_req); + + put_fw_req(fw_req); } /* This returns path of the firmware blob on the disk */ @@ -87,6 +113,8 @@ static struct fw_request *find_firmware(struct fw_download *fw_download, goto err_free_id; } + fw_req->fw_download = fw_download; + kref_init(&fw_req->kref); list_add(&fw_req->node, &fw_download->fw_requests); return fw_req; @@ -155,6 +183,7 @@ static int fw_download_fetch_firmware(struct gb_operation *op) const struct firmware *fw; unsigned int offset, size; u8 firmware_id; + int ret = 0; if (op->request->payload_size != sizeof(*request)) { dev_err(fw_download->parent, @@ -168,7 +197,7 @@ static int fw_download_fetch_firmware(struct gb_operation *op) size = le32_to_cpu(request->size); firmware_id = request->firmware_id; - fw_req = match_firmware(fw_download, firmware_id); + fw_req = get_fw_req(fw_download, firmware_id); if (!fw_req) { dev_err(fw_download->parent, "firmware not available for id: %02u\n", firmware_id); @@ -181,14 +210,16 @@ static int fw_download_fetch_firmware(struct gb_operation *op) dev_err(fw_download->parent, "bad fetch firmware request (offs = %u, size = %u)\n", offset, size); - return -EINVAL; + ret = -EINVAL; + goto put_fw; } if (!gb_operation_response_alloc(op, sizeof(*response) + size, GFP_KERNEL)) { dev_err(fw_download->parent, "error allocating fetch firmware response\n"); - return -ENOMEM; + ret = -ENOMEM; + goto put_fw; } response = op->response->payload; @@ -198,7 +229,10 @@ static int fw_download_fetch_firmware(struct gb_operation *op) "responding with firmware (offs = %u, size = %u)\n", offset, size); - return 0; +put_fw: + put_fw_req(fw_req); + + return ret; } static int fw_download_release_firmware(struct gb_operation *op) @@ -219,7 +253,7 @@ static int fw_download_release_firmware(struct gb_operation *op) request = op->request->payload; firmware_id = request->firmware_id; - fw_req = match_firmware(fw_download, firmware_id); + fw_req = get_fw_req(fw_download, firmware_id); if (!fw_req) { dev_err(fw_download->parent, "firmware not available for id: %02u\n", firmware_id); @@ -227,6 +261,7 @@ static int fw_download_release_firmware(struct gb_operation *op) } free_firmware(fw_download, fw_req); + put_fw_req(fw_req); dev_dbg(fw_download->parent, "release firmware\n"); -- cgit v1.2.3-59-g8ed1b From 69c8763eb883a331832b58e842fa0b01321ed9d6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 5 May 2016 15:33:20 +0530 Subject: greybus: fw-download: Introduce timeouts for firmware downloads As per greybus specification, the AP can apply, implementation dependent, timeouts for: - The time interval between the Find Firmware Response and the first Fetch Firmware Request. - The time interval between a Fetch Firmware Response and the next Fetch Firmware Request. - The time interval between a Fetch Firmware Response and the Release Firmware Request. - The time interval between the Find Firmware Response and the Release Firmware Request. This patch implements those timeouts. The timeout period for the first three cases is fixed to one-second and the timeout for the last one is finalized at runtime, dependent on the total size of the firmware. There can be two possible paths now, which may race for freeing or getting the 'struct fw_request'. They are: - Request handler: initiated from the Module side. - Timeout handler: initiated on timeout of the programmed timer. And so a mutex is added to avoid races. Every caller which needs to access the 'struct fw_request' increments the reference count, so that the structure doesn't get freed in parallel. Once the structure is freed and reference is put by all the users, the structure is freed. If we timeout while waiting for a request from the Module, the AP frees the 'struct fw_request', but does *not* free the request-id. This is done to guarantee that a delayed request from the Module for the expired id, doesn't get access to a new 'struct fw_request' allocated later with the same id. Tested with gbsim by hacking its code to delay the release request and indefinitely fetch the same section of the firmware package. Both timed out on the AP side and the 'struct fw_request' is free properly. Further requests work fine after few are timed out. And rmmod (followed by more similar testing) works just fine. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-download.c | 158 +++++++++++++++++++++++++++++++--- 1 file changed, 145 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 6bd26180bca6..0ebea37e6778 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -8,18 +8,30 @@ */ #include <linux/firmware.h> +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/timer.h> #include "firmware.h" #include "greybus.h" /* Length of the string in format: ara_%08x_%08x_%08x_%08x_%s.tftf */ #define FW_NAME_LEN 56 +/* Estimated minimum buffer size, actual size can be smaller than this */ +#define MIN_FETCH_SIZE 512 +/* Timeout, in jiffies, within which fetch or release firmware must be called */ +#define NEXT_REQ_TIMEOUT_J msecs_to_jiffies(1000) struct fw_request { u8 firmware_id; + bool disabled; + bool timedout; char name[FW_NAME_LEN]; const struct firmware *fw; struct list_head node; + struct timer_list timer; + /* Timeout, in jiffies, within which the firmware shall download */ + unsigned long release_timeout_j; struct kref kref; struct fw_download *fw_download; }; @@ -29,6 +41,7 @@ struct fw_download { struct gb_connection *connection; struct list_head fw_requests; struct ida id_map; + struct mutex mutex; }; static void fw_req_release(struct kref *kref) @@ -39,50 +52,120 @@ static void fw_req_release(struct kref *kref) fw_req->name); release_firmware(fw_req->fw); - ida_simple_remove(&fw_req->fw_download->id_map, fw_req->firmware_id); + + /* + * The request timed out and the module may send a fetch-fw or + * release-fw request later. Lets block the id we allocated for this + * request, so that the AP doesn't refer to a later fw-request (with + * same firmware_id) for the old timedout fw-request. + * + * NOTE: + * + * This also means that after 255 timeouts we will fail to service new + * firmware downloads. But what else can we do in that case anyway? Lets + * just hope that it never happens. + */ + if (!fw_req->timedout) + ida_simple_remove(&fw_req->fw_download->id_map, + fw_req->firmware_id); + kfree(fw_req); } +/* + * Incoming requests are serialized for a connection, and the only race possible + * is between the timeout handler freeing this and an incoming request. + * + * The operations on the fw-request list are protected by the mutex and + * get_fw_req() increments the reference count before returning a fw_req pointer + * to the users. + * + * free_firmware() also takes the mutex while removing an entry from the list, + * it guarantees that every user of fw_req has taken a kref-reference by now and + * we wouldn't have any new users. + * + * Once the last user drops the reference, the fw_req structure is freed. + */ +static void put_fw_req(struct fw_request *fw_req) +{ + kref_put(&fw_req->kref, fw_req_release); +} + /* Caller must call put_fw_req() after using struct fw_request */ static struct fw_request *get_fw_req(struct fw_download *fw_download, u8 firmware_id) { struct fw_request *fw_req; + mutex_lock(&fw_download->mutex); + list_for_each_entry(fw_req, &fw_download->fw_requests, node) { if (fw_req->firmware_id == firmware_id) { kref_get(&fw_req->kref); - return fw_req; + goto unlock; } } - return NULL; -} + fw_req = NULL; -/* - * Incoming requests are serialized for a connection, and this will never be - * racy. - */ -static void put_fw_req(struct fw_request *fw_req) -{ - kref_put(&fw_req->kref, fw_req_release); +unlock: + mutex_unlock(&fw_download->mutex); + + return fw_req; } static void free_firmware(struct fw_download *fw_download, struct fw_request *fw_req) { + /* Already disabled from timeout handlers */ + if (fw_req->disabled) + return; + + mutex_lock(&fw_download->mutex); list_del(&fw_req->node); + mutex_unlock(&fw_download->mutex); + fw_req->disabled = true; put_fw_req(fw_req); } +static void fw_request_timedout(unsigned long data) +{ + struct fw_request *fw_req = (struct fw_request *)data; + struct fw_download *fw_download = fw_req->fw_download; + + dev_err(fw_download->parent, + "Timed out waiting for fetch / release firmware requests: %u\n", + fw_req->firmware_id); + + fw_req->timedout = true; + free_firmware(fw_download, fw_req); +} + +static int exceeds_release_timeout(struct fw_request *fw_req) +{ + struct fw_download *fw_download = fw_req->fw_download; + + if (time_before(jiffies, fw_req->release_timeout_j)) + return 0; + + dev_err(fw_download->parent, + "Firmware download didn't finish in time, abort: %d\n", + fw_req->firmware_id); + + fw_req->timedout = true; + free_firmware(fw_download, fw_req); + + return -ETIMEDOUT; +} + /* This returns path of the firmware blob on the disk */ static struct fw_request *find_firmware(struct fw_download *fw_download, const char *tag) { struct gb_interface *intf = fw_download->connection->bundle->intf; struct fw_request *fw_req; - int ret; + int ret, req_count; fw_req = kzalloc(sizeof(*fw_req), GFP_KERNEL); if (!fw_req) @@ -115,7 +198,20 @@ static struct fw_request *find_firmware(struct fw_download *fw_download, fw_req->fw_download = fw_download; kref_init(&fw_req->kref); + + mutex_lock(&fw_download->mutex); list_add(&fw_req->node, &fw_download->fw_requests); + mutex_unlock(&fw_download->mutex); + + /* Timeout, in jiffies, within which firmware should get loaded */ + req_count = DIV_ROUND_UP(fw_req->fw->size, MIN_FETCH_SIZE); + fw_req->release_timeout_j = jiffies + req_count * NEXT_REQ_TIMEOUT_J; + + init_timer(&fw_req->timer); + fw_req->timer.function = fw_request_timedout; + fw_req->timer.expires = jiffies + NEXT_REQ_TIMEOUT_J; + fw_req->timer.data = (unsigned long)fw_req; + add_timer(&fw_req->timer); return fw_req; @@ -204,6 +300,24 @@ static int fw_download_fetch_firmware(struct gb_operation *op) return -EINVAL; } + /* Make sure timer handler isn't running in parallel */ + del_timer_sync(&fw_req->timer); + + /* We timed-out before reaching here ? */ + if (fw_req->disabled) { + ret = -ETIMEDOUT; + goto put_fw; + } + + /* + * Firmware download must finish within a limited time interval. If it + * doesn't, then we might have a buggy Module on the other side. Abort + * download. + */ + ret = exceeds_release_timeout(fw_req); + if (ret) + goto put_fw; + fw = fw_req->fw; if (offset >= fw->size || size > fw->size - offset) { @@ -229,6 +343,9 @@ static int fw_download_fetch_firmware(struct gb_operation *op) "responding with firmware (offs = %u, size = %u)\n", offset, size); + /* Refresh timeout */ + mod_timer(&fw_req->timer, jiffies + NEXT_REQ_TIMEOUT_J); + put_fw: put_fw_req(fw_req); @@ -260,6 +377,8 @@ static int fw_download_release_firmware(struct gb_operation *op) return -EINVAL; } + del_timer_sync(&fw_req->timer); + free_firmware(fw_download, fw_req); put_fw_req(fw_req); @@ -303,6 +422,7 @@ int gb_fw_download_connection_init(struct gb_connection *connection) ida_init(&fw_download->id_map); gb_connection_set_data(connection, fw_download); fw_download->connection = connection; + mutex_init(&fw_download->mutex); ret = gb_connection_enable(connection); if (ret) @@ -328,9 +448,21 @@ void gb_fw_download_connection_exit(struct gb_connection *connection) fw_download = gb_connection_get_data(connection); gb_connection_disable(fw_download->connection); + /* + * Make sure we have a reference to the pending requests, before they + * are freed from the timeout handler. + */ + mutex_lock(&fw_download->mutex); + list_for_each_entry(fw_req, &fw_download->fw_requests, node) + kref_get(&fw_req->kref); + mutex_unlock(&fw_download->mutex); + /* Release pending firmware packages */ - list_for_each_entry_safe(fw_req, tmp, &fw_download->fw_requests, node) + list_for_each_entry_safe(fw_req, tmp, &fw_download->fw_requests, node) { + del_timer_sync(&fw_req->timer); free_firmware(fw_download, fw_req); + put_fw_req(fw_req); + } ida_destroy(&fw_download->id_map); kfree(fw_download); -- cgit v1.2.3-59-g8ed1b From 03b4fa4b5dd9ecd11c27cdaeacf46453a948df5a Mon Sep 17 00:00:00 2001 From: Jeffrey Carlyle <jcarlyle@google.com> Date: Fri, 6 May 2016 11:29:37 -0700 Subject: greybus: Revert "bootrom: Implement timeouts to detect Module failures" This reverts commit 571348253032a86e4f0102d4dfadd390d0ea7e64. With this patch gb_bootrom_timedout was getting called in interrupt context and then proceeding to call functions that might block. In practical terms, this was leading to a kernel BUG at mm/vmalloc.c:1650. Signed-off-by: Jeffrey Carlyle <jcarlyle@google.com> Acked-by: Johan Hovold <johan@hovoldconsulting.com> --- drivers/staging/greybus/bootrom.c | 124 ++++++-------------------------------- 1 file changed, 19 insertions(+), 105 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index baada45329a2..cf750beb3403 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -8,48 +8,24 @@ */ #include <linux/firmware.h> -#include <linux/jiffies.h> -#include <linux/mutex.h> -#include <linux/timer.h> #include "bootrom.h" #include "greybus.h" -/* Timeout, in jiffies, within which the next request must be received */ -#define NEXT_REQ_TIMEOUT_J msecs_to_jiffies(1000) struct gb_bootrom { struct gb_connection *connection; const struct firmware *fw; u8 protocol_major; u8 protocol_minor; - struct timer_list timer; - struct mutex mutex; /* Protects bootrom->fw */ }; static void free_firmware(struct gb_bootrom *bootrom) { - if (!bootrom->fw) - return; - release_firmware(bootrom->fw); bootrom->fw = NULL; } -static void gb_bootrom_timedout(unsigned long data) -{ - struct gb_bootrom *bootrom = (struct gb_bootrom *)data; - struct device *dev = &bootrom->connection->bundle->dev; - - dev_err(dev, "Timed out waiting for request from the Module\n"); - - mutex_lock(&bootrom->mutex); - free_firmware(bootrom); - mutex_unlock(&bootrom->mutex); - - /* TODO: Power-off Module ? */ -} - /* * The es2 chip doesn't have VID/PID programmed into the hardware and we need to * hack that up to distinguish different modules and their firmware blobs. @@ -101,7 +77,8 @@ static int download_firmware(struct gb_bootrom *bootrom, u8 stage) int rc; /* Already have a firmware, free it */ - free_firmware(bootrom); + if (bootrom->fw) + free_firmware(bootrom); /* * Create firmware name @@ -137,32 +114,25 @@ static int gb_bootrom_firmware_size_request(struct gb_operation *op) struct device *dev = &op->connection->bundle->dev; int ret; - /* Disable timeouts */ - del_timer_sync(&bootrom->timer); - if (op->request->payload_size != sizeof(*size_request)) { dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", __func__, op->request->payload_size, sizeof(*size_request)); - ret = -EINVAL; - goto mod_timer; + return -EINVAL; } - mutex_lock(&bootrom->mutex); - ret = download_firmware(bootrom, size_request->stage); if (ret) { dev_err(dev, "%s: failed to download firmware (%d)\n", __func__, ret); - goto unlock; + return ret; } if (!gb_operation_response_alloc(op, sizeof(*size_response), GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); free_firmware(bootrom); - ret = -ENOMEM; - goto unlock; + return -ENOMEM; } size_response = op->response->payload; @@ -170,44 +140,28 @@ static int gb_bootrom_firmware_size_request(struct gb_operation *op) dev_dbg(dev, "%s: firmware size %d bytes\n", __func__, size_response->size); -unlock: - mutex_unlock(&bootrom->mutex); - -mod_timer: - /* Refresh timeout */ - mod_timer(&bootrom->timer, jiffies + NEXT_REQ_TIMEOUT_J); - - return ret; + return 0; } static int gb_bootrom_get_firmware(struct gb_operation *op) { struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); - const struct firmware *fw; + const struct firmware *fw = bootrom->fw; struct gb_bootrom_get_firmware_request *firmware_request; struct gb_bootrom_get_firmware_response *firmware_response; struct device *dev = &op->connection->bundle->dev; unsigned int offset, size; - int ret = 0; - - /* Disable timeouts */ - del_timer_sync(&bootrom->timer); if (op->request->payload_size != sizeof(*firmware_request)) { dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", __func__, op->request->payload_size, sizeof(*firmware_request)); - ret = -EINVAL; - goto mod_timer; + return -EINVAL; } - mutex_lock(&bootrom->mutex); - - fw = bootrom->fw; if (!fw) { dev_err(dev, "%s: firmware not available\n", __func__); - ret = -EINVAL; - goto unlock; + return -EINVAL; } firmware_request = op->request->payload; @@ -217,15 +171,13 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) if (offset >= fw->size || size > fw->size - offset) { dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", offset, size); - ret = -EINVAL; - goto unlock; + return -EINVAL; } if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); - ret = -ENOMEM; - goto unlock; + return -ENOMEM; } firmware_response = op->response->payload; @@ -234,59 +186,36 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, size); -unlock: - mutex_unlock(&bootrom->mutex); - -mod_timer: - /* Refresh timeout */ - mod_timer(&bootrom->timer, jiffies + NEXT_REQ_TIMEOUT_J); - - return ret; + return 0; } static int gb_bootrom_ready_to_boot(struct gb_operation *op) { struct gb_connection *connection = op->connection; - struct gb_bootrom *bootrom = gb_connection_get_data(connection); struct gb_bootrom_ready_to_boot_request *rtb_request; struct device *dev = &connection->bundle->dev; u8 status; - int ret = 0; - - /* Disable timeouts */ - del_timer_sync(&bootrom->timer); if (op->request->payload_size != sizeof(*rtb_request)) { dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", __func__, op->request->payload_size, sizeof(*rtb_request)); - ret = -EINVAL; - goto mod_timer; + return -EINVAL; } rtb_request = op->request->payload; status = rtb_request->status; /* Return error if the blob was invalid */ - if (status == GB_BOOTROM_BOOT_STATUS_INVALID) { - ret = -EINVAL; - goto mod_timer; - } + if (status == GB_BOOTROM_BOOT_STATUS_INVALID) + return -EINVAL; /* * XXX Should we return error for insecure firmware? */ dev_dbg(dev, "ready to boot: 0x%x, 0\n", status); -mod_timer: - /* - * Refresh timeout, the Interface shall load the new personality and - * send a new hotplug request, which shall get rid of the bootrom - * connection. As that can take some time, increase the timeout a bit. - */ - mod_timer(&bootrom->timer, jiffies + 5 * NEXT_REQ_TIMEOUT_J); - - return ret; + return 0; } static int gb_bootrom_request_handler(struct gb_operation *op) @@ -375,11 +304,6 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, bootrom->connection = connection; - mutex_init(&bootrom->mutex); - init_timer(&bootrom->timer); - bootrom->timer.function = gb_bootrom_timedout; - bootrom->timer.data = (unsigned long)bootrom; - greybus_set_drvdata(bundle, bootrom); ret = gb_connection_enable_tx(connection); @@ -405,9 +329,6 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, goto err_connection_disable; } - /* Refresh timeout */ - mod_timer(&bootrom->timer, jiffies + NEXT_REQ_TIMEOUT_J); - dev_dbg(&bundle->dev, "AP_READY sent\n"); return 0; @@ -430,16 +351,9 @@ static void gb_bootrom_disconnect(struct gb_bundle *bundle) gb_connection_disable(bootrom->connection); - /* Disable timeouts */ - del_timer_sync(&bootrom->timer); - - /* - * Release firmware: - * - * As the connection and the timer are already disabled, we don't need - * to lock access to bootrom->fw here. - */ - free_firmware(bootrom); + /* Release firmware */ + if (bootrom->fw) + free_firmware(bootrom); gb_connection_destroy(bootrom->connection); kfree(bootrom); -- cgit v1.2.3-59-g8ed1b From 144763bf8efd8bfc6b0c582fdf3f77564fc0296f Mon Sep 17 00:00:00 2001 From: Jeffrey Carlyle <jcarlyle@google.com> Date: Fri, 6 May 2016 12:43:52 -0700 Subject: greybus: svc: implement svc_intf_{vsys,refclk,unipro}_{enable,disable} operations Add SVC operations for fine grain control over vsys, refclk, and unipro port power. Testing done: used "new" firmware boot sequence to verify that modules were correctly detected and booted. Signed-off-by: Jeffrey Carlyle <jcarlyle@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 43 +++++++++++++++++++++++ drivers/staging/greybus/svc.c | 54 +++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 9b21a35292d1..c77fb25bbb48 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -840,6 +840,12 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_TIMESYNC_PING 0x1a #define GB_SVC_TYPE_MODULE_INSERTED 0x1f #define GB_SVC_TYPE_MODULE_REMOVED 0x20 +#define GB_SVC_TYPE_INTF_VSYS_ENABLE 0x21 +#define GB_SVC_TYPE_INTF_VSYS_DISABLE 0x22 +#define GB_SVC_TYPE_INTF_REFCLK_ENABLE 0x23 +#define GB_SVC_TYPE_INTF_REFCLK_DISABLE 0x24 +#define GB_SVC_TYPE_INTF_UNIPRO_ENABLE 0x25 +#define GB_SVC_TYPE_INTF_UNIPRO_DISABLE 0x26 #define GB_SVC_TYPE_INTF_ACTIVATE 0x27 #define GB_SVC_TYPE_INTF_MAILBOX_EVENT 0x29 @@ -954,6 +960,43 @@ struct gb_svc_route_destroy_request { } __packed; /* route destroy response has no payload */ +/* used for svc_intf_vsys_{enable,disable} */ +struct gb_svc_intf_vsys_request { + __u8 intf_id; +} __packed; + +struct gb_svc_intf_vsys_response { + __u8 result_code; +#define GB_SVC_INTF_VSYS_OK 0x00 +#define GB_SVC_INTF_VSYS_BUSY 0x01 +#define GB_SVC_INTF_VSYS_FAIL 0x02 +} __packed; + +/* used for svc_intf_refclk_{enable,disable} */ +struct gb_svc_intf_refclk_request { + __u8 intf_id; +} __packed; + +struct gb_svc_intf_refclk_response { + __u8 result_code; +#define GB_SVC_INTF_REFCLK_OK 0x00 +#define GB_SVC_INTF_REFCLK_BUSY 0x01 +#define GB_SVC_INTF_REFCLK_FAIL 0x02 +} __packed; + +/* used for svc_intf_unipro_{enable,disable} */ +struct gb_svc_intf_unipro_request { + __u8 intf_id; +} __packed; + +struct gb_svc_intf_unipro_response { + __u8 result_code; +#define GB_SVC_INTF_UNIPRO_OK 0x00 +#define GB_SVC_INTF_UNIPRO_BUSY 0x01 +#define GB_SVC_INTF_UNIPRO_FAIL 0x02 +#define GB_SVC_INTF_UNIPRO_NOT_OFF 0x03 +} __packed; + struct gb_svc_timesync_enable_request { __u8 count; __le64 frame_time; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index f3ee94e4b03e..1e6cab0173c4 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -257,22 +257,70 @@ int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) int gb_svc_intf_vsys_set(struct gb_svc *svc, u8 intf_id, bool enable) { - /* FIXME: implement */ + struct gb_svc_intf_vsys_request request; + struct gb_svc_intf_vsys_response response; + int type, ret; + + request.intf_id = intf_id; + if (enable) + type = GB_SVC_TYPE_INTF_VSYS_ENABLE; + else + type = GB_SVC_TYPE_INTF_VSYS_DISABLE; + + ret = gb_operation_sync(svc->connection, type, + &request, sizeof(request), + &response, sizeof(response)); + if (ret < 0) + return ret; + if (response.result_code != GB_SVC_INTF_VSYS_OK) + return -EREMOTEIO; return 0; } int gb_svc_intf_refclk_set(struct gb_svc *svc, u8 intf_id, bool enable) { - /* FIXME: implement */ + struct gb_svc_intf_refclk_request request; + struct gb_svc_intf_refclk_response response; + int type, ret; + request.intf_id = intf_id; + + if (enable) + type = GB_SVC_TYPE_INTF_REFCLK_ENABLE; + else + type = GB_SVC_TYPE_INTF_REFCLK_DISABLE; + + ret = gb_operation_sync(svc->connection, type, + &request, sizeof(request), + &response, sizeof(response)); + if (ret < 0) + return ret; + if (response.result_code != GB_SVC_INTF_REFCLK_OK) + return -EREMOTEIO; return 0; } int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable) { - /* FIXME: implement */ + struct gb_svc_intf_unipro_request request; + struct gb_svc_intf_unipro_response response; + int type, ret; + + request.intf_id = intf_id; + + if (enable) + type = GB_SVC_TYPE_INTF_UNIPRO_ENABLE; + else + type = GB_SVC_TYPE_INTF_UNIPRO_DISABLE; + ret = gb_operation_sync(svc->connection, type, + &request, sizeof(request), + &response, sizeof(response)); + if (ret < 0) + return ret; + if (response.result_code != GB_SVC_INTF_UNIPRO_OK) + return -EREMOTEIO; return 0; } -- cgit v1.2.3-59-g8ed1b From 140026b32f953593eb8678d67fcc95534efe1b49 Mon Sep 17 00:00:00 2001 From: Jeffrey Carlyle <jcarlyle@google.com> Date: Fri, 6 May 2016 12:43:53 -0700 Subject: greybus: svc: implement svc_intf_activate With upcoming firmware changes we will switch from an SVC-driven module boot sequence to an AP-driven module sequence. This operation allows the AP to request the SVC to boot a module to which the AP has previouslt requested power be applied. This operation will also determine if the remote interface is a dummy module, UniPro-only module, or full Greybus module. Testing done: Tested together with "new" firmware boot sequence to verify that modules are detected and booted as expected. Signed-off-by: Jeffrey Carlyle <jcarlyle@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 1e6cab0173c4..a98593c0a39b 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -15,7 +15,8 @@ #define SVC_KEY_ARA_BUTTON KEY_A -#define SVC_INTF_EJECT_TIMEOUT 9000 +#define SVC_INTF_EJECT_TIMEOUT 9000 +#define SVC_INTF_ACTIVATE_TIMEOUT 6000 struct gb_svc_deferred_request { struct work_struct work; @@ -326,9 +327,21 @@ int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable) int gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type) { - /* FIXME: implement */ + struct gb_svc_intf_activate_request request; + struct gb_svc_intf_activate_response response; + int ret; + + request.intf_id = intf_id; + + ret = gb_operation_sync_timeout(svc->connection, + GB_SVC_TYPE_INTF_ACTIVATE, + &request, sizeof(request), + &response, sizeof(response), + SVC_INTF_ACTIVATE_TIMEOUT); + if (ret < 0) + return ret; - *intf_type = GB_SVC_INTF_TYPE_GREYBUS; + *intf_type = response.intf_type; return 0; } -- cgit v1.2.3-59-g8ed1b From a956d939af9e73d3375f52d5c5c63ab824e59c8b Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 10:59:00 +0530 Subject: greybus: bootrom: Implement timeouts to detect Module failures Its possible that the Module may fail to download the next stage firmware, or to jump into it and boot into the new personality. We have already seen examples of both of these cases on EVT 1.5. This patch implements timeouts in the bootrom bundle driver, which now expects the next request from the Module to be received at the AP within 1 second of the previous request/response. The time interval can be increased later if required. The timeouts are added between: - AP_READY and FIRMWARE_SIZE operations - FIRMWARE_SIZE and GET_FIRMWARE operations - Two GET_FIRMWARE operations - GET_FIRMWARE and READY_TO_BOOT operations - READY_TO_BOOT operation and the call to the ->disconnect() event of the bootrom bundle (once the new hotplug request is received). The timeout for the last case is kept at 5 seconds right now (random value), as it may take a bit longer. Because 'bootrom->fw' can be accessed simultaneously (from timeout handler and incoming requests) and one of them can potentially free the '->fw' structure, a mutex is also added to take care of such races while accessing 'bootrom->fw' structure. Also note that the '!bootrom->fw' check is moved to free_firmware() routine. Note that this version uses delayed-work (instead of timers used in earlier attempt), as we need to call routines that can sleep from the timeout handler. Tested on EVT 1.5, by faking errors on certain requests, so that the bootrom doesn't send any more requests. Normal case is working just fine for audio and GP modules. This is tested with build arche_440. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 122 ++++++++++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index cf750beb3403..f375a879242c 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -8,24 +8,49 @@ */ #include <linux/firmware.h> +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> #include "bootrom.h" #include "greybus.h" +/* Timeout, in jiffies, within which the next request must be received */ +#define NEXT_REQ_TIMEOUT_J msecs_to_jiffies(1000) struct gb_bootrom { struct gb_connection *connection; const struct firmware *fw; u8 protocol_major; u8 protocol_minor; + struct delayed_work dwork; + struct mutex mutex; /* Protects bootrom->fw */ }; static void free_firmware(struct gb_bootrom *bootrom) { + if (!bootrom->fw) + return; + release_firmware(bootrom->fw); bootrom->fw = NULL; } +static void gb_bootrom_timedout(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct gb_bootrom *bootrom = container_of(dwork, struct gb_bootrom, dwork); + struct device *dev = &bootrom->connection->bundle->dev; + + dev_err(dev, "Timed out waiting for request from the Module\n"); + + mutex_lock(&bootrom->mutex); + free_firmware(bootrom); + mutex_unlock(&bootrom->mutex); + + /* TODO: Power-off Module ? */ +} + /* * The es2 chip doesn't have VID/PID programmed into the hardware and we need to * hack that up to distinguish different modules and their firmware blobs. @@ -77,8 +102,7 @@ static int download_firmware(struct gb_bootrom *bootrom, u8 stage) int rc; /* Already have a firmware, free it */ - if (bootrom->fw) - free_firmware(bootrom); + free_firmware(bootrom); /* * Create firmware name @@ -114,25 +138,32 @@ static int gb_bootrom_firmware_size_request(struct gb_operation *op) struct device *dev = &op->connection->bundle->dev; int ret; + /* Disable timeouts */ + cancel_delayed_work_sync(&bootrom->dwork); + if (op->request->payload_size != sizeof(*size_request)) { dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", __func__, op->request->payload_size, sizeof(*size_request)); - return -EINVAL; + ret = -EINVAL; + goto queue_work; } + mutex_lock(&bootrom->mutex); + ret = download_firmware(bootrom, size_request->stage); if (ret) { dev_err(dev, "%s: failed to download firmware (%d)\n", __func__, ret); - return ret; + goto unlock; } if (!gb_operation_response_alloc(op, sizeof(*size_response), GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); free_firmware(bootrom); - return -ENOMEM; + ret = -ENOMEM; + goto unlock; } size_response = op->response->payload; @@ -140,28 +171,44 @@ static int gb_bootrom_firmware_size_request(struct gb_operation *op) dev_dbg(dev, "%s: firmware size %d bytes\n", __func__, size_response->size); - return 0; +unlock: + mutex_unlock(&bootrom->mutex); + +queue_work: + /* Refresh timeout */ + schedule_delayed_work(&bootrom->dwork, NEXT_REQ_TIMEOUT_J); + + return ret; } static int gb_bootrom_get_firmware(struct gb_operation *op) { struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); - const struct firmware *fw = bootrom->fw; + const struct firmware *fw; struct gb_bootrom_get_firmware_request *firmware_request; struct gb_bootrom_get_firmware_response *firmware_response; struct device *dev = &op->connection->bundle->dev; unsigned int offset, size; + int ret = 0; + + /* Disable timeouts */ + cancel_delayed_work_sync(&bootrom->dwork); if (op->request->payload_size != sizeof(*firmware_request)) { dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", __func__, op->request->payload_size, sizeof(*firmware_request)); - return -EINVAL; + ret = -EINVAL; + goto queue_work; } + mutex_lock(&bootrom->mutex); + + fw = bootrom->fw; if (!fw) { dev_err(dev, "%s: firmware not available\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto unlock; } firmware_request = op->request->payload; @@ -171,13 +218,15 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) if (offset >= fw->size || size > fw->size - offset) { dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", offset, size); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, GFP_KERNEL)) { dev_err(dev, "%s: error allocating response\n", __func__); - return -ENOMEM; + ret = -ENOMEM; + goto unlock; } firmware_response = op->response->payload; @@ -186,36 +235,59 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, size); - return 0; +unlock: + mutex_unlock(&bootrom->mutex); + +queue_work: + /* Refresh timeout */ + schedule_delayed_work(&bootrom->dwork, NEXT_REQ_TIMEOUT_J); + + return ret; } static int gb_bootrom_ready_to_boot(struct gb_operation *op) { struct gb_connection *connection = op->connection; + struct gb_bootrom *bootrom = gb_connection_get_data(connection); struct gb_bootrom_ready_to_boot_request *rtb_request; struct device *dev = &connection->bundle->dev; u8 status; + int ret = 0; + + /* Disable timeouts */ + cancel_delayed_work_sync(&bootrom->dwork); if (op->request->payload_size != sizeof(*rtb_request)) { dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", __func__, op->request->payload_size, sizeof(*rtb_request)); - return -EINVAL; + ret = -EINVAL; + goto queue_work; } rtb_request = op->request->payload; status = rtb_request->status; /* Return error if the blob was invalid */ - if (status == GB_BOOTROM_BOOT_STATUS_INVALID) - return -EINVAL; + if (status == GB_BOOTROM_BOOT_STATUS_INVALID) { + ret = -EINVAL; + goto queue_work; + } /* * XXX Should we return error for insecure firmware? */ dev_dbg(dev, "ready to boot: 0x%x, 0\n", status); - return 0; +queue_work: + /* + * Refresh timeout, the Interface shall load the new personality and + * send a new hotplug request, which shall get rid of the bootrom + * connection. As that can take some time, increase the timeout a bit. + */ + schedule_delayed_work(&bootrom->dwork, 5 * NEXT_REQ_TIMEOUT_J); + + return ret; } static int gb_bootrom_request_handler(struct gb_operation *op) @@ -304,6 +376,8 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, bootrom->connection = connection; + mutex_init(&bootrom->mutex); + INIT_DELAYED_WORK(&bootrom->dwork, gb_bootrom_timedout); greybus_set_drvdata(bundle, bootrom); ret = gb_connection_enable_tx(connection); @@ -329,6 +403,9 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, goto err_connection_disable; } + /* Refresh timeout */ + schedule_delayed_work(&bootrom->dwork, NEXT_REQ_TIMEOUT_J); + dev_dbg(&bundle->dev, "AP_READY sent\n"); return 0; @@ -351,9 +428,16 @@ static void gb_bootrom_disconnect(struct gb_bundle *bundle) gb_connection_disable(bootrom->connection); - /* Release firmware */ - if (bootrom->fw) - free_firmware(bootrom); + /* Disable timeouts */ + cancel_delayed_work_sync(&bootrom->dwork); + + /* + * Release firmware: + * + * As the connection and the delayed work are already disabled, we don't + * need to lock access to bootrom->fw here. + */ + free_firmware(bootrom); gb_connection_destroy(bootrom->connection); kfree(bootrom); -- cgit v1.2.3-59-g8ed1b From c6cc8e73eb188156494e061bdb93966b0c7d9443 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 10:59:01 +0530 Subject: greybus: fw-download: Replace timer with delayed-work The timeout-handlers need to call routines that can sleep and those can't be called from interrupt context. The timer-handler is called in interrupt context and so will hit a BUG() in vmalloc.c. This patch moves away from timers to delayed-work, whose timeout handler gets called in process context and can call the sleep-able routines safely. Note that this issue wasn't hit earlier when the initial patch for timeouts was implemented due to some issues in the build arche_420. But with the new build arche_440, the BUG started crashing the phone on timeouts and so this fix is required. Tested on EVT 1.5 by triggering fake timeouts, by not sending release-firmware request for example. This is tested with build arche_440. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-download.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 0ebea37e6778..42cbbf4ac077 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -10,7 +10,7 @@ #include <linux/firmware.h> #include <linux/jiffies.h> #include <linux/mutex.h> -#include <linux/timer.h> +#include <linux/workqueue.h> #include "firmware.h" #include "greybus.h" @@ -29,7 +29,7 @@ struct fw_request { const struct firmware *fw; struct list_head node; - struct timer_list timer; + struct delayed_work dwork; /* Timeout, in jiffies, within which the firmware shall download */ unsigned long release_timeout_j; struct kref kref; @@ -129,9 +129,10 @@ static void free_firmware(struct fw_download *fw_download, put_fw_req(fw_req); } -static void fw_request_timedout(unsigned long data) +static void fw_request_timedout(struct work_struct *work) { - struct fw_request *fw_req = (struct fw_request *)data; + struct delayed_work *dwork = to_delayed_work(work); + struct fw_request *fw_req = container_of(dwork, struct fw_request, dwork); struct fw_download *fw_download = fw_req->fw_download; dev_err(fw_download->parent, @@ -207,11 +208,8 @@ static struct fw_request *find_firmware(struct fw_download *fw_download, req_count = DIV_ROUND_UP(fw_req->fw->size, MIN_FETCH_SIZE); fw_req->release_timeout_j = jiffies + req_count * NEXT_REQ_TIMEOUT_J; - init_timer(&fw_req->timer); - fw_req->timer.function = fw_request_timedout; - fw_req->timer.expires = jiffies + NEXT_REQ_TIMEOUT_J; - fw_req->timer.data = (unsigned long)fw_req; - add_timer(&fw_req->timer); + INIT_DELAYED_WORK(&fw_req->dwork, fw_request_timedout); + schedule_delayed_work(&fw_req->dwork, NEXT_REQ_TIMEOUT_J); return fw_req; @@ -300,8 +298,8 @@ static int fw_download_fetch_firmware(struct gb_operation *op) return -EINVAL; } - /* Make sure timer handler isn't running in parallel */ - del_timer_sync(&fw_req->timer); + /* Make sure work handler isn't running in parallel */ + cancel_delayed_work_sync(&fw_req->dwork); /* We timed-out before reaching here ? */ if (fw_req->disabled) { @@ -344,7 +342,7 @@ static int fw_download_fetch_firmware(struct gb_operation *op) size); /* Refresh timeout */ - mod_timer(&fw_req->timer, jiffies + NEXT_REQ_TIMEOUT_J); + schedule_delayed_work(&fw_req->dwork, NEXT_REQ_TIMEOUT_J); put_fw: put_fw_req(fw_req); @@ -377,7 +375,7 @@ static int fw_download_release_firmware(struct gb_operation *op) return -EINVAL; } - del_timer_sync(&fw_req->timer); + cancel_delayed_work_sync(&fw_req->dwork); free_firmware(fw_download, fw_req); put_fw_req(fw_req); @@ -459,7 +457,7 @@ void gb_fw_download_connection_exit(struct gb_connection *connection) /* Release pending firmware packages */ list_for_each_entry_safe(fw_req, tmp, &fw_download->fw_requests, node) { - del_timer_sync(&fw_req->timer); + cancel_delayed_work_sync(&fw_req->dwork); free_firmware(fw_download, fw_req); put_fw_req(fw_req); } -- cgit v1.2.3-59-g8ed1b From c77bac08eca8b7dff3f76662cc8d439c111b8d0f Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Fri, 6 May 2016 18:16:24 -0700 Subject: greybus: operation: fix an inconsistent indent This patch fixes an inconsistent indent. Testing Done: - Build & boot Signed-off-by: David Lin <dtwlin@google.com> Acked-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index e7e9b0e72269..62d0c4546d09 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -238,7 +238,7 @@ static void gb_operation_request_handle(struct gb_operation *operation) dev_err(&connection->hd->dev, "%s: failed to send response %d for type 0x%02x: %d\n", connection->name, status, operation->type, ret); - return; + return; } } -- cgit v1.2.3-59-g8ed1b From 03fba180ae4cc3bad27bd792e53dbb7ed2a9f7e7 Mon Sep 17 00:00:00 2001 From: Jeffrey Carlyle <jcarlyle@google.com> Date: Wed, 11 May 2016 10:08:55 -0700 Subject: greybus: svc: support status in svc_intf_activate response Update per Greybus spec. Status attribute added to activate response to return more detailed information about errors during activate. If the Greybus response is GB_OP_SUCCESS, the caller must also check the status attribute in the response to determine if any other errors occurred. Testing done: along with matchine firmware change, verified that modules were detected and enumerated as expected. Signed-off-by: Jeffrey Carlyle <jcarlyle@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/staging/greybus/greybus_protocols.h | 19 +++++++++++++++++++ drivers/staging/greybus/svc.c | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c77fb25bbb48..ca5eb3b580aa 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -849,6 +849,24 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_ACTIVATE 0x27 #define GB_SVC_TYPE_INTF_MAILBOX_EVENT 0x29 +/* Greybus SVC protocol status values */ +#define GB_SVC_OP_SUCCESS 0x00 +#define GB_SVC_OP_UNKNOWN_ERROR 0x01 +#define GB_SVC_INTF_NOT_DETECTED 0x02 +#define GB_SVC_INTF_NO_UPRO_LINK 0x03 +#define GB_SVC_INTF_UPRO_NOT_DOWN 0x04 +#define GB_SVC_INTF_UPRO_NOT_HIBERNATED 0x05 +#define GB_SVC_INTF_NO_V_SYS 0x06 +#define GB_SVC_INTF_V_CHG 0x07 +#define GB_SVC_INTF_WAKE_BUSY 0x08 +#define GB_SVC_INTF_NO_REFCLK 0x09 +#define GB_SVC_INTF_RELEASING 0x0a +#define GB_SVC_INTF_NO_ORDER 0x0b +#define GB_SVC_INTF_MBOX_SET 0x0c +#define GB_SVC_INTF_BAD_MBOX 0x0d +#define GB_SVC_INTF_OP_TIMEOUT 0x0e +#define GB_SVC_PWRMON_OP_NOT_PRESENT 0x0f + struct gb_svc_version_request { __u8 major; __u8 minor; @@ -1133,6 +1151,7 @@ struct gb_svc_intf_activate_request { #define GB_SVC_INTF_TYPE_GREYBUS 0x03 struct gb_svc_intf_activate_response { + __u8 status; __u8 intf_type; } __packed; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index a98593c0a39b..3bdf778d6d5d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -340,6 +340,11 @@ int gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type) SVC_INTF_ACTIVATE_TIMEOUT); if (ret < 0) return ret; + if (response.status != GB_SVC_OP_SUCCESS) { + dev_err(&svc->dev, "failed to activate interface %u: %u\n", + intf_id, response.status); + return -EREMOTEIO; + } *intf_type = response.intf_type; -- cgit v1.2.3-59-g8ed1b From ae0f6454bbe74df324e47041e68018649ce0bcba Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Mon, 9 May 2016 14:40:09 +0200 Subject: greybus: interface: fix type in enable error message Fix type of PTR_ERR in an interface-enable error message. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index aed45bcef376..0b829388a13c 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -564,7 +564,7 @@ int gb_interface_enable(struct gb_interface *intf) /* Establish control connection */ control = gb_control_create(intf); if (IS_ERR(control)) { - dev_err(&intf->dev, "failed to create control device: %lu\n", + dev_err(&intf->dev, "failed to create control device: %ld\n", PTR_ERR(control)); return PTR_ERR(control); } -- cgit v1.2.3-59-g8ed1b From 792db396d3209f4e20aea8c73407ac1f34b45a2b Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Mon, 9 May 2016 14:40:10 +0200 Subject: greybus: interface: remove unused interface-version quirk The interface-version request was just removed so remove the now unused interface-version quirk. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 5 +---- drivers/staging/greybus/interface.h | 5 ++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 0b829388a13c..a1435e94f5a1 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -217,18 +217,15 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) /* * Check if the interface is executing the quirky ES3 bootrom that - * requires E2EFC, CSD and CSV to be disabled and that does not - * support the interface-version request. + * requires E2EFC, CSD and CSV to be disabled. */ switch (init_status) { case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED: case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: intf->quirks |= GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; - intf->quirks |= GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION; break; default: intf->quirks &= ~GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; - intf->quirks &= ~GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION; } /* Clear the init status. */ diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 7a2162a89f05..1a25234351e2 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -11,9 +11,8 @@ #define __INTERFACE_H #define GB_INTERFACE_QUIRK_NO_CPORT_FEATURES BIT(0) -#define GB_INTERFACE_QUIRK_NO_INTERFACE_VERSION BIT(1) -#define GB_INTERFACE_QUIRK_NO_INIT_STATUS BIT(2) -#define GB_INTERFACE_QUIRK_NO_ARA_IDS BIT(3) +#define GB_INTERFACE_QUIRK_NO_INIT_STATUS BIT(1) +#define GB_INTERFACE_QUIRK_NO_ARA_IDS BIT(2) struct gb_interface { struct device dev; -- cgit v1.2.3-59-g8ed1b From 6f8a028fd17bbb3102c81dc4fd30f2d722234b5e Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 12 May 2016 12:20:02 +0530 Subject: greybus: gpbridge: Expose protocol_id in sysfs Right now, userspace doesn't have any way to find what protocol does a gpbridge device implement. And this is essential for the scripts to know, to expect what kind of device will be present inside the gpbN directory. Expose 'protocol_id' in sysfs to fix that. Tested by checking that the field appears with GP module on EVT 1.5. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 993cfacb65aa..64ab8ddf2212 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -26,6 +26,22 @@ struct gpbridge_host { static DEFINE_IDA(gpbridge_id); +static ssize_t protocol_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); + + return sprintf(buf, "0x%02x\n", gpbdev->cport_desc->protocol_id); +} +static DEVICE_ATTR_RO(protocol_id); + +static struct attribute *gpbdev_attrs[] = { + &dev_attr_protocol_id.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(gpbdev); + static void gpbdev_release(struct device *dev) { struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); @@ -190,7 +206,7 @@ static struct gpbridge_device *gb_gpbridge_create_dev(struct gb_bundle *bundle, gpbdev->dev.parent = &bundle->dev; gpbdev->dev.bus = &gpbridge_bus_type; gpbdev->dev.release = gpbdev_release; - gpbdev->dev.groups = NULL; + gpbdev->dev.groups = gpbdev_groups; gpbdev->dev.dma_mask = bundle->dev.dma_mask; dev_set_name(&gpbdev->dev, "gpb%d", id); -- cgit v1.2.3-59-g8ed1b From e4e55360dd6b2263bbbc83bf0f04b0bd9bb1f8fd Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 12 May 2016 11:34:05 +0530 Subject: greybus: core: Rename greybus_module_match() greybus_module_match() doesn't match modules anymore but bundle devices and should be named correctly. Though we can use greybus_bundle_match() as well, rest of the kernel uses terminology like 'greybus_match_device' and so choosing that instead. Compile tested. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 3fdc40597f16..a44846dd5d79 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -64,7 +64,7 @@ greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id) return NULL; } -static int greybus_module_match(struct device *dev, struct device_driver *drv) +static int greybus_match_device(struct device *dev, struct device_driver *drv) { struct greybus_driver *driver = to_greybus_driver(drv); struct gb_bundle *bundle; @@ -151,7 +151,7 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) struct bus_type greybus_bus_type = { .name = "greybus", - .match = greybus_module_match, + .match = greybus_match_device, .uevent = greybus_uevent, }; -- cgit v1.2.3-59-g8ed1b From 501cc8bd09485624beaeecf476739400977489b4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 12 May 2016 11:26:48 +0530 Subject: greybus: Fix probing of gpbridge devices The gpbridge core tries to match the driver's id-table against all CPorts available within the bundle for which the gpbridge bus was created. The gpbdev here is unique for a cport_desc and only a single cport_desc->protocol_id should be matched with the driver's id-table. Fix it. Tested on EVT 1.5 with a special manifest for GP module, where multiple CPorts are part of the same Bridged PHY bundle. Fixes: 75223f666687 ("gpbridge: implement gpbridge "bus" logic") Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 64ab8ddf2212..adb317deb417 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -57,44 +57,27 @@ static int gpbdev_uevent(struct device *dev, struct kobj_uevent_env *env) } static const struct gpbridge_device_id * -gpbdev_match_cport(struct greybus_descriptor_cport *cport_desc, - const struct gpbridge_device_id *id) +gpbdev_match_id(struct gpbridge_device *gpbdev, struct gpbridge_driver *gpbdrv) { + const struct gpbridge_device_id *id = gpbdrv->id_table; + if (!id) return NULL; for (; id->protocol_id; id++) - if (id->protocol_id == cport_desc->protocol_id) + if (id->protocol_id == gpbdev->cport_desc->protocol_id) return id; return NULL; } -static const struct gpbridge_device_id *gpbdev_match_id(struct gb_bundle *bundle, - const struct gpbridge_device_id *id_table) -{ - const struct gpbridge_device_id *id; - int i; - - if (!id_table || !bundle || bundle->num_cports == 0) - return NULL; - - for (i = 0; i < bundle->num_cports; i++) { - id = gpbdev_match_cport(&bundle->cport_desc[i], id_table); - if (id) - return id; - } - - return NULL; -} - static int gpbdev_match(struct device *dev, struct device_driver *drv) { struct gpbridge_driver *gpbdrv = to_gpbridge_driver(drv); struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); const struct gpbridge_device_id *id; - id = gpbdev_match_id(gpbdev->bundle, gpbdrv->id_table); + id = gpbdev_match_id(gpbdev, gpbdrv); if (id) return 1; @@ -107,7 +90,7 @@ static int gpbdev_probe(struct device *dev) struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); const struct gpbridge_device_id *id; - id = gpbdev_match_id(gpbdev->bundle, gpbdrv->id_table); + id = gpbdev_match_id(gpbdev, gpbdrv); if (!id) return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 0e9b41ab98ec8065263e6def8baebff1c1cd2c31 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:17:55 +0200 Subject: greybus: connection: add no-flow-control connection flag Add a no-flow-control connection flag, which is set for connection that should not have any flow-control feature enabled. This flag is specifically set for all connections to the legacy ES3 bootrom, and will also be used by the camera driver eventually. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 7 ++++--- drivers/staging/greybus/connection.h | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1564db84b4ed..9e8828109b1e 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -183,6 +183,8 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, connection->bundle = bundle; connection->handler = handler; connection->flags = flags; + if (intf && (intf->quirks & GB_INTERFACE_QUIRK_NO_CPORT_FEATURES)) + connection->flags |= GB_CONNECTION_FLAG_NO_FLOWCTRL; connection->state = GB_CONNECTION_STATE_DISABLED; atomic_set(&connection->op_cycle, 0); @@ -338,11 +340,10 @@ gb_connection_svc_connection_create(struct gb_connection *connection) intf = connection->intf; /* - * Enable either E2EFC or CSD, unless the interface does not support - * any CPort features. + * Enable either E2EFC or CSD, unless no flow control is requested. */ cport_flags = GB_SVC_CPORT_FLAG_CSV_N; - if (intf->quirks & GB_INTERFACE_QUIRK_NO_CPORT_FEATURES) { + if (gb_connection_flow_control_disabled(connection)) { cport_flags |= GB_SVC_CPORT_FLAG_CSD_N; } else if (gb_connection_e2efc_enabled(connection)) { cport_flags |= GB_SVC_CPORT_FLAG_CSD_N | diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 443d27bc3e35..bb25abf188f3 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -14,6 +14,7 @@ #include <linux/kfifo.h> #define GB_CONNECTION_FLAG_CSD BIT(0) +#define GB_CONNECTION_FLAG_NO_FLOWCTRL BIT(1) enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, @@ -88,6 +89,12 @@ static inline bool gb_connection_e2efc_enabled(struct gb_connection *connection) return !(connection->flags & GB_CONNECTION_FLAG_CSD); } +static inline bool +gb_connection_flow_control_disabled(struct gb_connection *connection) +{ + return connection->flags & GB_CONNECTION_FLAG_NO_FLOWCTRL; +} + static inline void *gb_connection_get_data(struct gb_connection *connection) { return connection->private; -- cgit v1.2.3-59-g8ed1b From ca1f8f809024db5fb1434d7e864a356fd5132c03 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:17:56 +0200 Subject: greybus: connection: add offloaded connection flag Add an offloaded connection flag, which is used to mark a connection as offloaded and prevent drivers from initiating operation over it. This will be used for the audio and camera data connections. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.h | 6 ++++++ drivers/staging/greybus/operation.c | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index bb25abf188f3..c740c3816f0d 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -15,6 +15,7 @@ #define GB_CONNECTION_FLAG_CSD BIT(0) #define GB_CONNECTION_FLAG_NO_FLOWCTRL BIT(1) +#define GB_CONNECTION_FLAG_OFFLOADED BIT(2) enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, @@ -95,6 +96,11 @@ gb_connection_flow_control_disabled(struct gb_connection *connection) return connection->flags & GB_CONNECTION_FLAG_NO_FLOWCTRL; } +static inline bool gb_connection_is_offloaded(struct gb_connection *connection) +{ + return connection->flags & GB_CONNECTION_FLAG_OFFLOADED; +} + static inline void *gb_connection_get_data(struct gb_connection *connection) { return connection->private; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 62d0c4546d09..716627e863c0 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -654,6 +654,9 @@ int gb_operation_request_send(struct gb_operation *operation, unsigned int cycle; int ret; + if (gb_connection_is_offloaded(connection)) + return -EBUSY; + if (!callback) return -EINVAL; @@ -950,8 +953,9 @@ void gb_connection_recv(struct gb_connection *connection, size_t msg_size; u16 operation_id; - if (connection->state != GB_CONNECTION_STATE_ENABLED && - connection->state != GB_CONNECTION_STATE_ENABLED_TX) { + if ((connection->state != GB_CONNECTION_STATE_ENABLED && + connection->state != GB_CONNECTION_STATE_ENABLED_TX) || + gb_connection_is_offloaded(connection)) { dev_warn(dev, "%s: dropping %zu received bytes\n", connection->name, size); return; -- cgit v1.2.3-59-g8ed1b From 781ac8662ffafa89e6675c3eb608ec9c569f24b7 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:17:57 +0200 Subject: greybus: connection: add helper for creating offloaded connection Add helper for creating offloaded connection. This can later be extended to support more elaborate resource management. Also fix a minor white space issue. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 13 +++++++++++++ drivers/staging/greybus/connection.h | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 9e8828109b1e..95855d1cad9f 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -264,6 +264,19 @@ gb_connection_create_flags(struct gb_bundle *bundle, u16 cport_id, } EXPORT_SYMBOL_GPL(gb_connection_create_flags); +struct gb_connection * +gb_connection_create_offloaded(struct gb_bundle *bundle, u16 cport_id, + unsigned long flags) +{ + struct gb_interface *intf = bundle->intf; + + flags |= GB_CONNECTION_FLAG_OFFLOADED; + + return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, + NULL, flags); +} +EXPORT_SYMBOL_GPL(gb_connection_create_offloaded); + static int gb_connection_hd_cport_enable(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index c740c3816f0d..a8da9ca214be 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -64,9 +64,11 @@ struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, struct gb_connection *gb_connection_create_control(struct gb_interface *intf); struct gb_connection *gb_connection_create(struct gb_bundle *bundle, u16 cport_id, gb_request_handler_t handler); -struct gb_connection * gb_connection_create_flags(struct gb_bundle *bundle, +struct gb_connection *gb_connection_create_flags(struct gb_bundle *bundle, u16 cport_id, gb_request_handler_t handler, unsigned long flags); +struct gb_connection *gb_connection_create_offloaded(struct gb_bundle *bundle, + u16 cport_id, unsigned long flags); void gb_connection_destroy(struct gb_connection *connection); static inline bool gb_connection_is_static(struct gb_connection *connection) -- cgit v1.2.3-59-g8ed1b From a8af16313d18ce75d56c256fdfefed53a0572d45 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:17:58 +0200 Subject: greybus: audio: mark the data connection as offloaded Mark the data-connection as offloaded, that is, under control of the host device (AP-bridge). This prevents messages from being sent from or forwarded to the AP. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index d2dd5b82ce41..57d3b0292231 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -146,15 +146,6 @@ static int gbaudio_codec_request_handler(struct gb_operation *op) return ret; } -static int gbaudio_data_connection_request_handler(struct gb_operation *op) -{ - struct gb_connection *connection = op->connection; - - dev_warn(&connection->bundle->dev, "Audio Event received\n"); - - return 0; -} - static int gb_audio_add_mgmt_connection(struct gbaudio_module_info *gbmodule, struct greybus_descriptor_cport *cport_desc, struct gb_bundle *bundle) @@ -192,9 +183,8 @@ static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule, return -ENOMEM; } - connection = gb_connection_create_flags(bundle, + connection = gb_connection_create_offloaded(bundle, le16_to_cpu(cport_desc->id), - gbaudio_data_connection_request_handler, GB_CONNECTION_FLAG_CSD); if (IS_ERR(connection)) { devm_kfree(gbmodule->dev, dai); -- cgit v1.2.3-59-g8ed1b From 74a5d93cee1fb08d8e576888418ddd401aba6713 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:17:59 +0200 Subject: greybus: hd: move CPort allocation to host-device code Move host-device CPort allocation to the host-device code. Proper CPort allocation requires knowledge of the hardware and must be handled by the host-device driver. This is an intermediate step that moves the generic CPort-allocation code to the host-device code. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 30 ++++++++---------------------- drivers/staging/greybus/hd.c | 26 ++++++++++++++++++++++++++ drivers/staging/greybus/hd.h | 3 +++ 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 95855d1cad9f..ac99fc043e54 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -142,21 +142,8 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, unsigned long flags) { struct gb_connection *connection; - struct ida *id_map = &hd->cport_id_map; - int ida_start, ida_end; int ret; - if (hd_cport_id < 0) { - ida_start = 0; - ida_end = hd->num_cports; - } else if (hd_cport_id < hd->num_cports) { - ida_start = hd_cport_id; - ida_end = hd_cport_id + 1; - } else { - dev_err(&hd->dev, "cport %d not available\n", hd_cport_id); - return ERR_PTR(-EINVAL); - } - mutex_lock(&gb_connection_mutex); if (intf && gb_connection_intf_find(intf, cport_id)) { @@ -165,15 +152,17 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, goto err_unlock; } - ret = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); - if (ret < 0) + ret = gb_hd_cport_allocate(hd, hd_cport_id); + if (ret < 0) { + dev_err(&hd->dev, "failed to allocate cport: %d\n", ret); goto err_unlock; + } hd_cport_id = ret; connection = kzalloc(sizeof(*connection), GFP_KERNEL); if (!connection) { ret = -ENOMEM; - goto err_remove_ida; + goto err_hd_cport_release; } connection->hd_cport_id = hd_cport_id; @@ -219,8 +208,8 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, err_free_connection: kfree(connection); -err_remove_ida: - ida_simple_remove(id_map, hd_cport_id); +err_hd_cport_release: + gb_hd_cport_release(hd, hd_cport_id); err_unlock: mutex_unlock(&gb_connection_mutex); @@ -658,8 +647,6 @@ EXPORT_SYMBOL_GPL(gb_connection_disable); /* Caller must have disabled the connection before destroying it. */ void gb_connection_destroy(struct gb_connection *connection) { - struct ida *id_map; - if (!connection) return; @@ -672,8 +659,7 @@ void gb_connection_destroy(struct gb_connection *connection) destroy_workqueue(connection->wq); - id_map = &connection->hd->cport_id_map; - ida_simple_remove(id_map, connection->hd_cport_id); + gb_hd_cport_release(connection->hd, connection->hd_cport_id); connection->hd_cport_id = CPORT_ID_BAD; mutex_unlock(&gb_connection_mutex); diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 28634720221d..f24e06b0d697 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -39,6 +39,32 @@ static struct attribute *bus_attrs[] = { }; ATTRIBUTE_GROUPS(bus); +/* Locking: Caller guarantees serialisation */ +int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id) +{ + struct ida *id_map = &hd->cport_id_map; + int ida_start, ida_end; + + if (cport_id < 0) { + ida_start = 0; + ida_end = hd->num_cports; + } else if (cport_id < hd->num_cports) { + ida_start = cport_id; + ida_end = cport_id + 1; + } else { + dev_err(&hd->dev, "cport %d not available\n", cport_id); + return -EINVAL; + } + + return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); +} + +/* Locking: Caller guarantees serialisation */ +void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id) +{ + ida_simple_remove(&hd->cport_id_map, cport_id); +} + static void gb_hd_release(struct device *dev) { struct gb_host_device *hd = to_gb_host_device(dev); diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index ff71936a7932..4a197ac9ed80 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -50,6 +50,9 @@ struct gb_host_device { }; #define to_gb_host_device(d) container_of(d, struct gb_host_device, dev) +int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id); +void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id); + struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, struct device *parent, size_t buffer_size_max, -- cgit v1.2.3-59-g8ed1b From be5d01f10bdb782d31039930a9414c2f880e3430 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:18:00 +0200 Subject: greybus: es2: fix probe error handling Make sure to return -ENODEV when the expected endpoints are missing and stop relying on a default error. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 1cef13d3fb5d..f8bc3ec227fb 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -938,7 +938,7 @@ static int ap_probe(struct usb_interface *interface, struct usb_endpoint_descriptor *endpoint; int bulk_in = 0; int bulk_out = 0; - int retval = -ENOMEM; + int retval; int i; int num_cports; int cport_id; @@ -1002,6 +1002,7 @@ static int ap_probe(struct usb_interface *interface, } if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); + retval = -ENODEV; goto error; } @@ -1014,11 +1015,15 @@ static int ap_probe(struct usb_interface *interface, u8 *buffer; urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) + if (!urb) { + retval = -ENOMEM; goto error; + } buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL); - if (!buffer) + if (!buffer) { + retval = -ENOMEM; goto error; + } usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, @@ -1035,8 +1040,10 @@ static int ap_probe(struct usb_interface *interface, struct urb *urb; urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) + if (!urb) { + retval = -ENOMEM; goto error; + } es2->cport_out_urb[i] = urb; es2->cport_out_urb_busy[i] = false; /* just to be anal */ -- cgit v1.2.3-59-g8ed1b From 050615076699e537b26ed0934e105e78e7f1e765 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:18:01 +0200 Subject: greybus: es2: clean up CDSI CPort reservation Clean up the CDSI CPort reservation by adding a host-device helper and CPort defines. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 25 ++++++++++++++++--------- drivers/staging/greybus/hd.c | 15 +++++++++++++++ drivers/staging/greybus/hd.h | 1 + 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index f8bc3ec227fb..c8ee779bf3f9 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -18,6 +18,11 @@ #include "connection.h" #include "greybus_trace.h" + +/* Fixed CPort numbers */ +#define ES2_CPORT_CDSI0 16 +#define ES2_CPORT_CDSI1 17 + /* Memory sizes for the buffers sent to/from the ES2 controller */ #define ES2_GBUF_MSG_SIZE_MAX 2048 @@ -941,7 +946,6 @@ static int ap_probe(struct usb_interface *interface, int retval; int i; int num_cports; - int cport_id; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -960,14 +964,6 @@ static int ap_probe(struct usb_interface *interface, return PTR_ERR(hd); } - /* - * CPorts 16 and 17 are reserved for CDSI0 and CDSI1, make sure they - * won't be allocated dynamically. - */ - do { - cport_id = ida_simple_get(&hd->cport_id_map, 16, 18, GFP_KERNEL); - } while (cport_id > 0); - es2 = hd_to_es2(hd); es2->hd = hd; es2->usb_intf = interface; @@ -976,6 +972,17 @@ static int ap_probe(struct usb_interface *interface, INIT_KFIFO(es2->apb_log_fifo); usb_set_intfdata(interface, es2); + /* + * Reserve the CDSI0 and CDSI1 CPorts so they won't be allocated + * dynamically. + */ + retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI0); + if (retval) + goto error; + retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI1); + if (retval) + goto error; + es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep), GFP_KERNEL); if (!es2->cport_to_ep) { diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index f24e06b0d697..b87e086748b2 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -39,6 +39,21 @@ static struct attribute *bus_attrs[] = { }; ATTRIBUTE_GROUPS(bus); +int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id) +{ + struct ida *id_map = &hd->cport_id_map; + int ret; + + ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL); + if (ret < 0) { + dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(gb_hd_cport_reserve); + /* Locking: Caller guarantees serialisation */ int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id) { diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 4a197ac9ed80..5d74e0c45162 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -50,6 +50,7 @@ struct gb_host_device { }; #define to_gb_host_device(d) container_of(d, struct gb_host_device, dev) +int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id); int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id); void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id); -- cgit v1.2.3-59-g8ed1b From f2aae1c6e60f2acd636c005951a2c932bc5c8d05 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:18:02 +0200 Subject: greybus: hd: generalise cport allocation Generalise CPort allocation by allowing host-device drivers to override the default implementation. Also pass the connection flags down the stack as such information is needed for proper CPort allocation. Specifically, this will initially be used to allow the camera driver to allocate the dedicated CDSI CPorts. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/hd.c | 11 ++++++++++- drivers/staging/greybus/hd.h | 6 +++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ac99fc043e54..395a9dfc99c0 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -152,7 +152,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, goto err_unlock; } - ret = gb_hd_cport_allocate(hd, hd_cport_id); + ret = gb_hd_cport_allocate(hd, hd_cport_id, flags); if (ret < 0) { dev_err(&hd->dev, "failed to allocate cport: %d\n", ret); goto err_unlock; diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index b87e086748b2..fba6d766209f 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -55,11 +55,15 @@ int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id) EXPORT_SYMBOL_GPL(gb_hd_cport_reserve); /* Locking: Caller guarantees serialisation */ -int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id) +int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id, + unsigned long flags) { struct ida *id_map = &hd->cport_id_map; int ida_start, ida_end; + if (hd->driver->cport_allocate) + return hd->driver->cport_allocate(hd, cport_id, flags); + if (cport_id < 0) { ida_start = 0; ida_end = hd->num_cports; @@ -77,6 +81,11 @@ int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id) /* Locking: Caller guarantees serialisation */ void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id) { + if (hd->driver->cport_release) { + hd->driver->cport_release(hd, cport_id); + return; + } + ida_simple_remove(&hd->cport_id_map, cport_id); } diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 5d74e0c45162..80573aed56ef 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -16,6 +16,9 @@ struct gb_message; struct gb_hd_driver { size_t hd_priv_size; + int (*cport_allocate)(struct gb_host_device *hd, int cport_id, + unsigned long flags); + void (*cport_release)(struct gb_host_device *hd, u16 cport_id); int (*cport_enable)(struct gb_host_device *hd, u16 cport_id); int (*cport_disable)(struct gb_host_device *hd, u16 cport_id); int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id, @@ -51,7 +54,8 @@ struct gb_host_device { #define to_gb_host_device(d) container_of(d, struct gb_host_device, dev) int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id); -int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id); +int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id, + unsigned long flags); void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id); struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, -- cgit v1.2.3-59-g8ed1b From e7b848c288dbcfb4b153ca5c772c8573caf739fb Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:18:03 +0200 Subject: greybus: es2: clean up cport-reset handling Move handling of CPort-reset exceptions to the reset handler. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index c8ee779bf3f9..f06a322f851a 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -506,6 +506,11 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) struct usb_device *udev = es2->usb_dev; int retval; + switch (cport_id) { + case GB_SVC_CPORT_ID: + return 0; + } + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), GB_APB_REQUEST_RESET_CPORT, USB_DIR_OUT | USB_TYPE_VENDOR | @@ -524,11 +529,9 @@ static int cport_enable(struct gb_host_device *hd, u16 cport_id) { int retval; - if (cport_id != GB_SVC_CPORT_ID) { - retval = cport_reset(hd, cport_id); - if (retval) - return retval; - } + retval = cport_reset(hd, cport_id); + if (retval) + return retval; return 0; } -- cgit v1.2.3-59-g8ed1b From 045d356114557781f4b703ff569f2e3785e87b2e Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:18:04 +0200 Subject: greybus: es2: add support for CDSI1 allocation Use the new CPort-allocation callbacks to allow for rudimentary resource management of the CDSI CPorts. How to manage offloaded resources in a generic fashion is yet to be determined, but this minimal implementation will allow core to manage the camera data connection so that the current camera-driver hacks can be removed. This is specifically required to be able to implement proper connection closing and for power management. Note that the CDSI CPorts can not (currently) be reset through the USB vendor request. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.h | 1 + drivers/staging/greybus/es2.c | 59 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index a8da9ca214be..b152e2340591 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -16,6 +16,7 @@ #define GB_CONNECTION_FLAG_CSD BIT(0) #define GB_CONNECTION_FLAG_NO_FLOWCTRL BIT(1) #define GB_CONNECTION_FLAG_OFFLOADED BIT(2) +#define GB_CONNECTION_FLAG_CDSI1 BIT(3) enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index f06a322f851a..998b41ebc53e 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -99,6 +99,8 @@ struct es2_ap_dev { bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; spinlock_t cport_out_urb_lock; + bool cdsi1_in_use; + int *cport_to_ep; struct task_struct *apb_log_task; @@ -508,6 +510,8 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) switch (cport_id) { case GB_SVC_CPORT_ID: + case ES2_CPORT_CDSI0: + case ES2_CPORT_CDSI1: return 0; } @@ -525,6 +529,59 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) return 0; } +static int es2_cport_allocate(struct gb_host_device *hd, int cport_id, + unsigned long flags) +{ + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct ida *id_map = &hd->cport_id_map; + int ida_start, ida_end; + + switch (cport_id) { + case ES2_CPORT_CDSI0: + case ES2_CPORT_CDSI1: + dev_err(&hd->dev, "cport %d not available\n", cport_id); + return -EBUSY; + } + + if (flags & GB_CONNECTION_FLAG_OFFLOADED && + flags & GB_CONNECTION_FLAG_CDSI1) { + if (es2->cdsi1_in_use) { + dev_err(&hd->dev, "CDSI1 already in use\n"); + return -EBUSY; + } + + es2->cdsi1_in_use = true; + + return ES2_CPORT_CDSI1; + } + + if (cport_id < 0) { + ida_start = 0; + ida_end = hd->num_cports; + } else if (cport_id < hd->num_cports) { + ida_start = cport_id; + ida_end = cport_id + 1; + } else { + dev_err(&hd->dev, "cport %d not available\n", cport_id); + return -EINVAL; + } + + return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); +} + +static void es2_cport_release(struct gb_host_device *hd, u16 cport_id) +{ + struct es2_ap_dev *es2 = hd_to_es2(hd); + + switch (cport_id) { + case ES2_CPORT_CDSI1: + es2->cdsi1_in_use = false; + return; + } + + ida_simple_remove(&hd->cport_id_map, cport_id); +} + static int cport_enable(struct gb_host_device *hd, u16 cport_id) { int retval; @@ -621,6 +678,8 @@ static struct gb_hd_driver es2_driver = { .hd_priv_size = sizeof(struct es2_ap_dev), .message_send = message_send, .message_cancel = message_cancel, + .cport_allocate = es2_cport_allocate, + .cport_release = es2_cport_release, .cport_enable = cport_enable, .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, -- cgit v1.2.3-59-g8ed1b From 3b05d8508f82fc18fe7ef0618671c0603c490c0d Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:18:05 +0200 Subject: greybus: connection: add camera-data connected workaround Firmware currently lacks a representation of the offloaded CDSI connections and connected requests sent for these ports therefore fails. Add a temporary work-around until this has been resolved in firmware. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 395a9dfc99c0..f803d40413ac 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -402,6 +402,16 @@ static int gb_connection_control_connected(struct gb_connection *connection) if (gb_connection_is_static(connection)) return 0; + /* + * HACK: Suppress connected request for the offloaded camera + * connection as it is currently not supported by firmware. Note that + * the corresponding non-fatal disconnected event is still sent. + */ + if (gb_connection_is_offloaded(connection) && + connection->flags & GB_CONNECTION_FLAG_CDSI1) { + return 0; + } + control = connection->intf->control; if (connection == control->connection) -- cgit v1.2.3-59-g8ed1b From 3ba9fa5c9220e5a6dbbe0b88fc40f857f299b040 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 11 May 2016 10:18:06 +0200 Subject: greybus: camera: fix data-connection handling Now that core supports offloaded connections, we can remove the hack that was used to setup the data connection. Note that offloaded-resource management may need to be refined later, but the current minimal implementation is enough to allow core to manage the connections (e.g. needed for proper connection tear down and power management). This will also allow the camera driver to be converted to a bundle driver. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index ba76f5633256..4831ad652c04 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -42,7 +42,7 @@ struct gb_camera_debugfs_buffer { */ struct gb_camera { struct gb_connection *connection; - bool data_connected; + struct gb_connection *data_connection; struct { struct dentry *root; @@ -900,13 +900,9 @@ static void gb_camera_cleanup(struct gb_camera *gcam) { gb_camera_debugfs_cleanup(gcam); - if (gcam->data_connected) { - struct gb_interface *intf = gcam->connection->intf; - struct gb_svc *svc = gcam->connection->hd->svc; - - gb_svc_connection_destroy(svc, intf->interface_id, - ES2_APB_CDSI0_CPORT, svc->ap_intf_id, - ES2_APB_CDSI1_CPORT); + if (gcam->data_connection) { + gb_connection_disable(gcam->data_connection); + gb_connection_destroy(gcam->data_connection); } kfree(gcam); @@ -914,9 +910,9 @@ static void gb_camera_cleanup(struct gb_camera *gcam) static int gb_camera_connection_init(struct gb_connection *connection) { - struct gb_svc *svc = connection->hd->svc; + struct gb_connection *data; struct gb_camera *gcam; - u8 cport_flags; + unsigned long data_flags; int ret; gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); @@ -930,14 +926,20 @@ static int gb_camera_connection_init(struct gb_connection *connection) * Create the data connection between camera module CDSI0 and APB CDS1. * The CPort IDs are hardcoded by the ES2 bridges. */ - cport_flags = GB_SVC_CPORT_FLAG_CSD_N | GB_SVC_CPORT_FLAG_CSV_N; - ret = gb_svc_connection_create(svc, connection->intf->interface_id, - ES2_APB_CDSI0_CPORT, svc->ap_intf_id, - ES2_APB_CDSI1_CPORT, cport_flags); - if (ret < 0) + data_flags = GB_CONNECTION_FLAG_NO_FLOWCTRL | GB_CONNECTION_FLAG_CDSI1; + + data = gb_connection_create_offloaded(connection->bundle, + ES2_APB_CDSI0_CPORT, + data_flags); + if (IS_ERR(data)) { + ret = PTR_ERR(data); goto error; + } + gcam->data_connection = data; - gcam->data_connected = true; + ret = gb_connection_enable(data); + if (ret) + goto error; ret = gb_camera_debugfs_init(gcam); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From 14a5f47b29bf4c92ba4233cdd89ea5ce67acac0f Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Fri, 13 May 2016 11:51:03 -0700 Subject: greybus: gpbridge: add gpbridge device type Right now, there is no way to know the device type of gpbridge (bridged-phy) devices in userspace. So, add that. Testing Done: Tested by reading the 'uevent' for gpb device of gpbridge module Signed-off-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index adb317deb417..82fbf26bc78b 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -50,6 +50,11 @@ static void gpbdev_release(struct device *dev) kfree(gpbdev); } +struct device_type greybus_gpbdev_type = { + .name = "gpbridge_device", + .release = gpbdev_release, +}; + static int gpbdev_uevent(struct device *dev, struct kobj_uevent_env *env) { /* FIXME add something here, userspace will care about these... */ @@ -188,7 +193,7 @@ static struct gpbridge_device *gb_gpbridge_create_dev(struct gb_bundle *bundle, gpbdev->cport_desc = cport_desc; gpbdev->dev.parent = &bundle->dev; gpbdev->dev.bus = &gpbridge_bus_type; - gpbdev->dev.release = gpbdev_release; + gpbdev->dev.type = &greybus_gpbdev_type; gpbdev->dev.groups = gpbdev_groups; gpbdev->dev.dma_mask = bundle->dev.dma_mask; dev_set_name(&gpbdev->dev, "gpb%d", id); -- cgit v1.2.3-59-g8ed1b From c7092b22fc7d0debae114fe37cf3b967997e1cdb Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Fri, 13 May 2016 11:51:04 -0700 Subject: greybus: gpbridge: make gpbridge device ids start with 1 Making gpb device ids consistent with all other devices on greybus Testing Done: Tested using gpb module to make sure the first gpbX/ device starts with 1. Signed-off-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 82fbf26bc78b..b22f5e452e9d 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -178,7 +178,7 @@ static struct gpbridge_device *gb_gpbridge_create_dev(struct gb_bundle *bundle, int retval; int id; - id = ida_simple_get(&gpbridge_id, 0, 0, GFP_KERNEL); + id = ida_simple_get(&gpbridge_id, 1, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); -- cgit v1.2.3-59-g8ed1b From 6458492e9c019bd15c9a543171df1cbe27122543 Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Fri, 13 May 2016 11:51:05 -0700 Subject: greybus: gpbridge: add uevent vars for gpbridge devices The uevent vars now include module, interface, greybus_id, bundle id, class and gpbridge device specific properties. This make it consistent with how we are reporting uevents for all other greybus devices. Testing Done: Tested by reading uevent from gpbridge devices that enumerate using gpb module Signed-off-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index b22f5e452e9d..c8b3822db80c 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -57,7 +57,31 @@ struct device_type greybus_gpbdev_type = { static int gpbdev_uevent(struct device *dev, struct kobj_uevent_env *env) { - /* FIXME add something here, userspace will care about these... */ + struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); + struct greybus_descriptor_cport *cport_desc = gpbdev->cport_desc; + struct gb_bundle *bundle = gpbdev->bundle; + struct gb_interface *intf = bundle->intf; + struct gb_module *module = intf->module; + struct gb_host_device *hd = intf->hd; + + if (add_uevent_var(env, "BUS=%u", hd->bus_id)) + return -ENOMEM; + if (add_uevent_var(env, "MODULE=%u", module->module_id)) + return -ENOMEM; + if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) + return -ENOMEM; + if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x", + intf->vendor_id, intf->product_id)) + return -ENOMEM; + if (add_uevent_var(env, "BUNDLE=%u", gpbdev->bundle->id)) + return -ENOMEM; + if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class)) + return -ENOMEM; + if (add_uevent_var(env, "GPBDEV_ID=%u", gpbdev->id)) + return -ENOMEM; + if (add_uevent_var(env, "PROTOCOL_ID=%02x", cport_desc->protocol_id)) + return -ENOMEM; + return 0; } -- cgit v1.2.3-59-g8ed1b From a974f469ff097f8f6e242f3ef5b26ba3604fbaf2 Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Fri, 13 May 2016 12:00:56 -0700 Subject: greybus: arche-platform: Fix the MODULE_LICENSE to match with header The header calls out the license to be GPL v2, while the module declares itself as "GPL" Testing Done: Trivial Signed-off-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 83d823cf5232..e3854349b36c 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -754,6 +754,6 @@ static void __exit arche_exit(void) } module_exit(arche_exit); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); MODULE_DESCRIPTION("Arche Platform Driver"); -- cgit v1.2.3-59-g8ed1b From 5705020fbe2a274a1a49709e688b4cc471425036 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 12 May 2016 12:43:50 +0100 Subject: greybus: svc: Add TimeSync SVC commands Simple addition of the TimeSync commands defined in the specification. Adds: - svc.c::timesync_enable(u8 count, u64 frame_time, u32 strobe_delay, u32 refclk) Commands the SVC to initiate count TimeSync strobe pulses with strobe_delay microseconds delay between each strobe to the specified bit-mask of Interface IDs indicated in a previous timesync_wake_pins_acquire command. The frame_time parameter indicates the initial time the SVC should base the first strobe from. The refclk parameter indicates the APs clock rate, the SVC should ensure its own clock ticks at this rate. Once enabled the SVC may not enter a low-power mode which will result in the reference timer used to track time switching off. The SVC will capture the authoritative FrameTime at each strobe and store these values for later propagation to the AP with the timesync_authoritative request. - svc.c::timesync_disable(void) Commands the SVC to immediately halt TimeSync logic. This will allow the SVC to transition into low-power modes where the reference timer being used for TimeSync may switch off. - svc.c::timesync_authoritative(u64 *frame_time) Used by the AP Module to ask the SVC for the authoritative FrameTime as captured at each TimeSync strobe. - svc.c::timesync_ping(u64 *frame_time) Used by the AP Module to command the SVC to initiate a single strobe on a specified bit-mask of Interface IDs communicated in a previous timesync_wake_pins_acquire command. SVC will latch the FrameTime on the rising edge of the outbound pulse and will return the FrameTime to the AP Module in the response phase of the greybus transaction. - svc::timesync_wake_pins_acquire(u32 strobe_mask) Used by the AP to tell the SVC to set a bit-mask of wake lines associated with a bit-mask of Interface IDs to a known initial state prior to the SVC generating a TimeSync related pulse such as timesync-enable or timesync-ping. - svc::timesync_wake_pins_release(void) Used by the AP to tell the SVC to release all wake-detect lines in the timesync active state as previously specified in the timesync_wake_pins_acquire operation. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 85 +++++++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/svc.h | 7 ++++ 2 files changed, 92 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 3bdf778d6d5d..80e8cf04ade9 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -459,6 +459,91 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); +int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, + u32 strobe_delay, u32 refclk) +{ + struct gb_connection *connection = svc->connection; + struct gb_svc_timesync_enable_request request; + + request.count = count; + request.frame_time = cpu_to_le64(frame_time); + request.strobe_delay = cpu_to_le32(strobe_delay); + request.refclk = cpu_to_le32(refclk); + return gb_operation_sync(connection, + GB_SVC_TYPE_TIMESYNC_ENABLE, + &request, sizeof(request), NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_svc_timesync_enable); + +int gb_svc_timesync_disable(struct gb_svc *svc) +{ + struct gb_connection *connection = svc->connection; + + return gb_operation_sync(connection, + GB_SVC_TYPE_TIMESYNC_DISABLE, + NULL, 0, NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_svc_timesync_disable); + +int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time) +{ + struct gb_connection *connection = svc->connection; + struct gb_svc_timesync_authoritative_response response; + int ret, i; + + ret = gb_operation_sync(connection, + GB_SVC_TYPE_TIMESYNC_AUTHORITATIVE, NULL, 0, + &response, sizeof(response)); + if (ret < 0) + return ret; + + for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) + frame_time[i] = le64_to_cpu(response.frame_time[i]); + return 0; +} +EXPORT_SYMBOL_GPL(gb_svc_timesync_authoritative); + +int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time) +{ + struct gb_connection *connection = svc->connection; + struct gb_svc_timesync_ping_response response; + int ret; + + ret = gb_operation_sync(connection, + GB_SVC_TYPE_TIMESYNC_PING, + NULL, 0, + &response, sizeof(response)); + if (ret < 0) + return ret; + + *frame_time = le64_to_cpu(response.frame_time); + return 0; +} +EXPORT_SYMBOL_GPL(gb_svc_timesync_ping); + +int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask) +{ + struct gb_connection *connection = svc->connection; + struct gb_svc_timesync_wake_pins_acquire_request request; + + request.strobe_mask = cpu_to_le32(strobe_mask); + return gb_operation_sync(connection, + GB_SVC_TYPE_TIMESYNC_WAKE_PINS_ACQUIRE, + &request, sizeof(request), + NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_svc_timesync_wake_pins_acquire); + +int gb_svc_timesync_wake_pins_release(struct gb_svc *svc) +{ + struct gb_connection *connection = svc->connection; + + return gb_operation_sync(connection, + GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE, + NULL, 0, NULL, 0); +} +EXPORT_SYMBOL_GPL(gb_svc_timesync_wake_pins_release); + /* Creates bi-directional routes between the devices */ int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, u8 intf2_id, u8 dev2_id) diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 7268db6e2643..e3e0aa14b08d 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -85,6 +85,13 @@ void gb_svc_watchdog_destroy(struct gb_svc *svc); bool gb_svc_watchdog_enabled(struct gb_svc *svc); int gb_svc_watchdog_enable(struct gb_svc *svc); int gb_svc_watchdog_disable(struct gb_svc *svc); +int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, + u32 strobe_delay, u32 refclk); +int gb_svc_timesync_disable(struct gb_svc *svc); +int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time); +int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time); +int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask); +int gb_svc_timesync_wake_pins_release(struct gb_svc *svc); int gb_svc_protocol_init(void); void gb_svc_protocol_exit(void); -- cgit v1.2.3-59-g8ed1b From fa433b619655bf2d253094074d132004a9fa479c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 12 May 2016 12:43:51 +0100 Subject: greybus: control: Add TimeSync get-last-event logic gb_control_timesync_get_last_event() sends a request asking for the FrameTime at the last SVC strobe event. The responding entity returns the FrameTime in the response phase of the request. Performing this operation to an Interface after previously: 1. Synchronizing time using timesync-enable/timesync-authoritative 2. Sending an SVC_TIMESYNC_PING will return the FrameTime of the responding entity at the SVC-ping. If this command is sent before synchronization has been initiated or successfully completed the responding entity should return an error code. - control.c::gb_control_timesync_get_last_event(u64 *frame_time) Returns the FrameTime at the last SVC_TIMESYNC_PING to the AP Module. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 14 ++++++++++++++ drivers/staging/greybus/control.h | 2 ++ drivers/staging/greybus/greybus_protocols.h | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 13c68f3f01de..7a838068c1ad 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -177,6 +177,20 @@ int gb_control_timesync_disable(struct gb_control *control) NULL, 0); } +int gb_control_timesync_get_last_event(struct gb_control *control, + u64 *frame_time) +{ + struct gb_control_timesync_get_last_event_response response; + int ret; + + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_TIMESYNC_GET_LAST_EVENT, + NULL, 0, &response, sizeof(response)); + if (!ret) + *frame_time = le64_to_cpu(response.frame_time); + return ret; +} + int gb_control_timesync_authoritative(struct gb_control *control, u64 *frame_time, u8 count) { diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 773cdde4dc44..02b583901029 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -43,6 +43,8 @@ int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, int gb_control_timesync_enable(struct gb_control *control, u8 count, u64 frame_time, u32 strobe_delay, u32 refclk); int gb_control_timesync_disable(struct gb_control *control); +int gb_control_timesync_get_last_event(struct gb_control *control, + u64 *frame_time); int gb_control_timesync_authoritative(struct gb_control *control, u64 *frame_time, u8 count); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index ca5eb3b580aa..40bd6c0a4a62 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -123,6 +123,7 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE 0x09 /* Unused 0x0a */ #define GB_CONTROL_TYPE_BUNDLE_VERSION 0x0b +#define GB_CONTROL_TYPE_TIMESYNC_GET_LAST_EVENT 0x0d #define GB_CONTROL_TYPE_MODE_SWITCH 0x0e struct gb_control_version_request { @@ -179,6 +180,11 @@ struct gb_control_timesync_authoritative_request { } __packed; /* timesync authoritative response has no payload */ +/* timesync get_last_event_request has no payload */ +struct gb_control_timesync_get_last_event_response { + __le64 frame_time; +} __packed; + /* APBridge protocol */ /* request APB1 log */ -- cgit v1.2.3-59-g8ed1b From 123e7497237a43068d88d49efaca6fff1f5e29e3 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 12 May 2016 12:43:52 +0100 Subject: greybus: control: Drop unused parameter from timesync_authoritative The count field is redundant and unused. Drop it from the control timesync_authoritative command. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 2 +- drivers/staging/greybus/control.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 7a838068c1ad..b4a1c1476c56 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -192,7 +192,7 @@ int gb_control_timesync_get_last_event(struct gb_control *control, } int gb_control_timesync_authoritative(struct gb_control *control, - u64 *frame_time, u8 count) + u64 *frame_time) { struct gb_control_timesync_authoritative_request request; int i; diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 02b583901029..33e7e2795d1e 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -46,6 +46,6 @@ int gb_control_timesync_disable(struct gb_control *control); int gb_control_timesync_get_last_event(struct gb_control *control, u64 *frame_time); int gb_control_timesync_authoritative(struct gb_control *control, - u64 *frame_time, u8 count); + u64 *frame_time); #endif /* __CONTROL_H */ -- cgit v1.2.3-59-g8ed1b From 90fa0e6c3ea768a649e50f810b83814b0d15ee92 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:04 +0530 Subject: greybus: gpbridge: Export few routines In order to separate protocol specific drivers into their own modules, some of the gpbridge routines need to be exported. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index c8b3822db80c..4cb587fcf4bc 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -163,11 +163,13 @@ int gb_gpbridge_register_driver(struct gpbridge_driver *driver, pr_info("registered new driver %s\n", driver->name); return 0; } +EXPORT_SYMBOL_GPL(gb_gpbridge_register_driver); void gb_gpbridge_deregister_driver(struct gpbridge_driver *driver) { driver_unregister(&driver->driver); } +EXPORT_SYMBOL_GPL(gb_gpbridge_deregister_driver); int gb_gpbridge_get_version(struct gb_connection *connection) { @@ -194,6 +196,7 @@ int gb_gpbridge_get_version(struct gb_connection *connection) return 0; } +EXPORT_SYMBOL_GPL(gb_gpbridge_get_version); static struct gpbridge_device *gb_gpbridge_create_dev(struct gb_bundle *bundle, struct greybus_descriptor_cport *cport_desc) -- cgit v1.2.3-59-g8ed1b From 9b919bdfcc6e9f8b3ea9f813b1604089ea769cfc Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:05 +0530 Subject: greybus: gpbridge: Create module_gpbridge_driver() Create module_gpbridge_driver() for registering gpbridge module drivers. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 1af20d88af4b..cd5ba9820cb0 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -87,5 +87,16 @@ extern void gb_i2c_driver_exit(void); extern int gb_spi_driver_init(void); extern void gb_spi_driver_exit(void); +/** + * module_gpbridge_driver() - Helper macro for registering a gpbridge driver + * @__gpbridge_driver: gpbridge_driver structure + * + * Helper macro for gpbridge drivers to set up proper module init / exit + * functions. Replaces module_init() and module_exit() and keeps people from + * printing pointless things to the kernel log when their driver is loaded. + */ +#define module_gpbridge_driver(__gpbridge_driver) \ + module_driver(__gpbridge_driver, gb_gpbridge_register, gb_gpbridge_deregister) + #endif /* __GPBRIDGE_H */ -- cgit v1.2.3-59-g8ed1b From 7c0925eb57785190be3d9f3d173fefb3d791cba0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:06 +0530 Subject: greybus: gpio: Create separate module Create separate module for gpio gpbridge driver. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 3 ++- drivers/staging/greybus/gpbridge.c | 7 ------- drivers/staging/greybus/gpbridge.h | 3 --- drivers/staging/greybus/gpio.c | 5 ++++- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 43c0c9910de1..e5c784db35b4 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -18,7 +18,6 @@ gb-phy-y := gpbridge.o \ sdio.o \ uart.o \ pwm.o \ - gpio.o \ i2c.o \ spi.o \ usb.o @@ -40,6 +39,7 @@ gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o gb-firmware-y := fw-core.o fw-download.o +gb-gpio-y := gpio.o obj-m += greybus.o obj-m += gb-phy.o @@ -62,6 +62,7 @@ obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o obj-m += gb-audio-manager.o obj-m += gb-firmware.o +obj-m += gb-gpio.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 4cb587fcf4bc..86226775c5a1 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -317,10 +317,6 @@ static int __init gpbridge_init(void) goto error_gpbridge; } - if (gb_gpio_driver_init()) { - pr_err("error initializing gpio driver\n"); - goto error_gpio; - } if (gb_pwm_driver_init()) { pr_err("error initializing pwm driver\n"); goto error_pwm; @@ -359,8 +355,6 @@ error_sdio: error_uart: gb_pwm_driver_exit(); error_pwm: - gb_gpio_driver_exit(); -error_gpio: greybus_deregister(&gb_gpbridge_driver); error_gpbridge: bus_unregister(&gpbridge_bus_type); @@ -377,7 +371,6 @@ static void __exit gpbridge_exit(void) gb_sdio_driver_exit(); gb_uart_driver_exit(); gb_pwm_driver_exit(); - gb_gpio_driver_exit(); greybus_deregister(&gb_gpbridge_driver); bus_unregister(&gpbridge_bus_type); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index cd5ba9820cb0..a346fa20ffd9 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -66,9 +66,6 @@ void gb_##__driver##_exit(void) \ gb_gpbridge_deregister(&__driver); \ } -extern int gb_gpio_driver_init(void); -extern void gb_gpio_driver_exit(void); - extern int gb_pwm_driver_init(void); extern void gb_pwm_driver_exit(void); diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 4f0695b2b20e..adb213fd81b6 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -747,6 +747,7 @@ static const struct gpbridge_device_id gb_gpio_id_table[] = { { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_GPIO) }, { }, }; +MODULE_DEVICE_TABLE(gpbridge, gb_gpio_id_table); static struct gpbridge_driver gpio_driver = { .name = "gpio", @@ -754,4 +755,6 @@ static struct gpbridge_driver gpio_driver = { .remove = gb_gpio_remove, .id_table = gb_gpio_id_table, }; -gb_gpbridge_builtin_driver(gpio_driver); + +module_gpbridge_driver(gpio_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From d6046b44a5663865a316f9d73a44d64c99aa9fb2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:07 +0530 Subject: greybus: i2c : Create separate module Create separate module for i2c gpbridge driver. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 3 ++- drivers/staging/greybus/gpbridge.c | 7 ------- drivers/staging/greybus/gpbridge.h | 3 --- drivers/staging/greybus/i2c.c | 5 ++++- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index e5c784db35b4..b9e4e9663e33 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -18,7 +18,6 @@ gb-phy-y := gpbridge.o \ sdio.o \ uart.o \ pwm.o \ - i2c.o \ spi.o \ usb.o @@ -40,6 +39,7 @@ gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o gb-firmware-y := fw-core.o fw-download.o gb-gpio-y := gpio.o +gb-i2c-y := i2c.o obj-m += greybus.o obj-m += gb-phy.o @@ -63,6 +63,7 @@ obj-m += gb-audio-apbridgea.o obj-m += gb-audio-manager.o obj-m += gb-firmware.o obj-m += gb-gpio.o +obj-m += gb-i2c.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 86226775c5a1..92838d5569c8 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -333,10 +333,6 @@ static int __init gpbridge_init(void) pr_err("error initializing usb driver\n"); goto error_usb; } - if (gb_i2c_driver_init()) { - pr_err("error initializing i2c driver\n"); - goto error_i2c; - } if (gb_spi_driver_init()) { pr_err("error initializing spi driver\n"); goto error_spi; @@ -345,8 +341,6 @@ static int __init gpbridge_init(void) return 0; error_spi: - gb_i2c_driver_exit(); -error_i2c: gb_usb_driver_exit(); error_usb: gb_sdio_driver_exit(); @@ -366,7 +360,6 @@ module_init(gpbridge_init); static void __exit gpbridge_exit(void) { gb_spi_driver_exit(); - gb_i2c_driver_exit(); gb_usb_driver_exit(); gb_sdio_driver_exit(); gb_uart_driver_exit(); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index a346fa20ffd9..3f0340e7347d 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -78,9 +78,6 @@ extern void gb_sdio_driver_exit(void); extern int gb_usb_driver_init(void); extern void gb_usb_driver_exit(void); -extern int gb_i2c_driver_init(void); -extern void gb_i2c_driver_exit(void); - extern int gb_spi_driver_init(void); extern void gb_spi_driver_exit(void); diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index b49e8b455ab1..69d6f07c0822 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -321,6 +321,7 @@ static const struct gpbridge_device_id gb_i2c_id_table[] = { { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_I2C) }, { }, }; +MODULE_DEVICE_TABLE(gpbridge, gb_i2c_id_table); static struct gpbridge_driver i2c_driver = { .name = "i2c", @@ -328,4 +329,6 @@ static struct gpbridge_driver i2c_driver = { .remove = gb_i2c_remove, .id_table = gb_i2c_id_table, }; -gb_gpbridge_builtin_driver(i2c_driver); + +module_gpbridge_driver(i2c_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From dca8060db87bd851d77a64ef70b822ce3a82b3b5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:08 +0530 Subject: greybus: pwm: Create separate module Create separate module for pwm gpbridge driver. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 3 ++- drivers/staging/greybus/gpbridge.c | 7 ------- drivers/staging/greybus/gpbridge.h | 3 --- drivers/staging/greybus/pwm.c | 5 ++++- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index b9e4e9663e33..2bd23b782d13 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -17,7 +17,6 @@ greybus-y := core.o \ gb-phy-y := gpbridge.o \ sdio.o \ uart.o \ - pwm.o \ spi.o \ usb.o @@ -38,6 +37,7 @@ gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o gb-firmware-y := fw-core.o fw-download.o +gb-pwm-y := pwm.o gb-gpio-y := gpio.o gb-i2c-y := i2c.o @@ -62,6 +62,7 @@ obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o obj-m += gb-audio-manager.o obj-m += gb-firmware.o +obj-m += gb-pwm.o obj-m += gb-gpio.o obj-m += gb-i2c.o diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 92838d5569c8..ecff3b1f1861 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -317,10 +317,6 @@ static int __init gpbridge_init(void) goto error_gpbridge; } - if (gb_pwm_driver_init()) { - pr_err("error initializing pwm driver\n"); - goto error_pwm; - } if (gb_uart_driver_init()) { pr_err("error initializing uart driver\n"); goto error_uart; @@ -347,8 +343,6 @@ error_usb: error_sdio: gb_uart_driver_exit(); error_uart: - gb_pwm_driver_exit(); -error_pwm: greybus_deregister(&gb_gpbridge_driver); error_gpbridge: bus_unregister(&gpbridge_bus_type); @@ -363,7 +357,6 @@ static void __exit gpbridge_exit(void) gb_usb_driver_exit(); gb_sdio_driver_exit(); gb_uart_driver_exit(); - gb_pwm_driver_exit(); greybus_deregister(&gb_gpbridge_driver); bus_unregister(&gpbridge_bus_type); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 3f0340e7347d..fe254bd36a5c 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -66,9 +66,6 @@ void gb_##__driver##_exit(void) \ gb_gpbridge_deregister(&__driver); \ } -extern int gb_pwm_driver_init(void); -extern void gb_pwm_driver_exit(void); - extern int gb_uart_driver_init(void); extern void gb_uart_driver_exit(void); diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index b11e77df4a6c..1c4ffb9a0ae1 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -256,6 +256,7 @@ static const struct gpbridge_device_id gb_pwm_id_table[] = { { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_PWM) }, { }, }; +MODULE_DEVICE_TABLE(gpbridge, gb_pwm_id_table); static struct gpbridge_driver pwm_driver = { .name = "pwm", @@ -263,4 +264,6 @@ static struct gpbridge_driver pwm_driver = { .remove = gb_pwm_remove, .id_table = gb_pwm_id_table, }; -gb_gpbridge_builtin_driver(pwm_driver); + +module_gpbridge_driver(pwm_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 3d64730e119480062ffd2d94c49532fbf3dbb217 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:09 +0530 Subject: greybus: sdio: Create separate module Create separate module for sdio gpbridge driver. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 3 ++- drivers/staging/greybus/gpbridge.c | 7 ------- drivers/staging/greybus/gpbridge.h | 3 --- drivers/staging/greybus/sdio.c | 5 ++++- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 2bd23b782d13..bffb1bdb6b6c 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -15,7 +15,6 @@ greybus-y := core.o \ legacy.o gb-phy-y := gpbridge.o \ - sdio.o \ uart.o \ spi.o \ usb.o @@ -37,6 +36,7 @@ gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o gb-firmware-y := fw-core.o fw-download.o +gb-sdio-y := sdio.o gb-pwm-y := pwm.o gb-gpio-y := gpio.o gb-i2c-y := i2c.o @@ -62,6 +62,7 @@ obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o obj-m += gb-audio-manager.o obj-m += gb-firmware.o +obj-m += gb-sdio.o obj-m += gb-pwm.o obj-m += gb-gpio.o obj-m += gb-i2c.o diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index ecff3b1f1861..d5ccdc4c9003 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -321,10 +321,6 @@ static int __init gpbridge_init(void) pr_err("error initializing uart driver\n"); goto error_uart; } - if (gb_sdio_driver_init()) { - pr_err("error initializing sdio driver\n"); - goto error_sdio; - } if (gb_usb_driver_init()) { pr_err("error initializing usb driver\n"); goto error_usb; @@ -339,8 +335,6 @@ static int __init gpbridge_init(void) error_spi: gb_usb_driver_exit(); error_usb: - gb_sdio_driver_exit(); -error_sdio: gb_uart_driver_exit(); error_uart: greybus_deregister(&gb_gpbridge_driver); @@ -355,7 +349,6 @@ static void __exit gpbridge_exit(void) { gb_spi_driver_exit(); gb_usb_driver_exit(); - gb_sdio_driver_exit(); gb_uart_driver_exit(); greybus_deregister(&gb_gpbridge_driver); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index fe254bd36a5c..44d4781a5444 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -69,9 +69,6 @@ void gb_##__driver##_exit(void) \ extern int gb_uart_driver_init(void); extern void gb_uart_driver_exit(void); -extern int gb_sdio_driver_init(void); -extern void gb_sdio_driver_exit(void); - extern int gb_usb_driver_init(void); extern void gb_usb_driver_exit(void); diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 8ee4d4c5e73d..7f063b4e4942 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -827,6 +827,7 @@ static const struct gpbridge_device_id gb_sdio_id_table[] = { { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SDIO) }, { }, }; +MODULE_DEVICE_TABLE(gpbridge, gb_sdio_id_table); static struct gpbridge_driver sdio_driver = { .name = "sdio", @@ -834,4 +835,6 @@ static struct gpbridge_driver sdio_driver = { .remove = gb_sdio_remove, .id_table = gb_sdio_id_table, }; -gb_gpbridge_builtin_driver(sdio_driver); + +module_gpbridge_driver(sdio_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From bebc013bafed3cc42eada99da0a924e46b0cd0c3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:10 +0530 Subject: greybus: spi: Create separate module Create separate module for spi gpbridge driver. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 3 ++- drivers/staging/greybus/gpbridge.c | 7 ------- drivers/staging/greybus/gpbridge.h | 3 --- drivers/staging/greybus/spi.c | 5 ++++- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index bffb1bdb6b6c..61d4ca9d944f 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -16,7 +16,6 @@ greybus-y := core.o \ gb-phy-y := gpbridge.o \ uart.o \ - spi.o \ usb.o # Prefix all modules with gb- @@ -36,6 +35,7 @@ gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o gb-firmware-y := fw-core.o fw-download.o +gb-spi-y := spi.o gb-sdio-y := sdio.o gb-pwm-y := pwm.o gb-gpio-y := gpio.o @@ -62,6 +62,7 @@ obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o obj-m += gb-audio-manager.o obj-m += gb-firmware.o +obj-m += gb-spi.o obj-m += gb-sdio.o obj-m += gb-pwm.o obj-m += gb-gpio.o diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index d5ccdc4c9003..2425df7a9ebb 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -325,15 +325,9 @@ static int __init gpbridge_init(void) pr_err("error initializing usb driver\n"); goto error_usb; } - if (gb_spi_driver_init()) { - pr_err("error initializing spi driver\n"); - goto error_spi; - } return 0; -error_spi: - gb_usb_driver_exit(); error_usb: gb_uart_driver_exit(); error_uart: @@ -347,7 +341,6 @@ module_init(gpbridge_init); static void __exit gpbridge_exit(void) { - gb_spi_driver_exit(); gb_usb_driver_exit(); gb_uart_driver_exit(); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 44d4781a5444..4fff2a6048ff 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -72,9 +72,6 @@ extern void gb_uart_driver_exit(void); extern int gb_usb_driver_init(void); extern void gb_usb_driver_exit(void); -extern int gb_spi_driver_init(void); -extern void gb_spi_driver_exit(void); - /** * module_gpbridge_driver() - Helper macro for registering a gpbridge driver * @__gpbridge_driver: gpbridge_driver structure diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index dc811b142432..6cf18d179a75 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -550,6 +550,7 @@ static const struct gpbridge_device_id gb_spi_id_table[] = { { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, { }, }; +MODULE_DEVICE_TABLE(gpbridge, gb_spi_id_table); static struct gpbridge_driver spi_driver = { .name = "spi", @@ -557,4 +558,6 @@ static struct gpbridge_driver spi_driver = { .remove = gb_spi_remove, .id_table = gb_spi_id_table, }; -gb_gpbridge_builtin_driver(spi_driver); + +module_gpbridge_driver(spi_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 66b9e09e28b0c32d9b071fc9084bbb8880b88d74 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:11 +0530 Subject: greybus: uart: Create separate module Create separate module for uart gpbridge driver. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 3 ++- drivers/staging/greybus/gpbridge.c | 7 ------- drivers/staging/greybus/gpbridge.h | 3 --- drivers/staging/greybus/uart.c | 9 +++++++-- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 61d4ca9d944f..e99a9a83dd8e 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -15,7 +15,6 @@ greybus-y := core.o \ legacy.o gb-phy-y := gpbridge.o \ - uart.o \ usb.o # Prefix all modules with gb- @@ -37,6 +36,7 @@ gb-camera-y := camera.o gb-firmware-y := fw-core.o fw-download.o gb-spi-y := spi.o gb-sdio-y := sdio.o +gb-uart-y := uart.o gb-pwm-y := pwm.o gb-gpio-y := gpio.o gb-i2c-y := i2c.o @@ -64,6 +64,7 @@ obj-m += gb-audio-manager.o obj-m += gb-firmware.o obj-m += gb-spi.o obj-m += gb-sdio.o +obj-m += gb-uart.o obj-m += gb-pwm.o obj-m += gb-gpio.o obj-m += gb-i2c.o diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 2425df7a9ebb..5a65564dc34c 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -317,10 +317,6 @@ static int __init gpbridge_init(void) goto error_gpbridge; } - if (gb_uart_driver_init()) { - pr_err("error initializing uart driver\n"); - goto error_uart; - } if (gb_usb_driver_init()) { pr_err("error initializing usb driver\n"); goto error_usb; @@ -329,8 +325,6 @@ static int __init gpbridge_init(void) return 0; error_usb: - gb_uart_driver_exit(); -error_uart: greybus_deregister(&gb_gpbridge_driver); error_gpbridge: bus_unregister(&gpbridge_bus_type); @@ -342,7 +336,6 @@ module_init(gpbridge_init); static void __exit gpbridge_exit(void) { gb_usb_driver_exit(); - gb_uart_driver_exit(); greybus_deregister(&gb_gpbridge_driver); bus_unregister(&gpbridge_bus_type); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 4fff2a6048ff..532dd5682f02 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -66,9 +66,6 @@ void gb_##__driver##_exit(void) \ gb_gpbridge_deregister(&__driver); \ } -extern int gb_uart_driver_init(void); -extern void gb_uart_driver_exit(void); - extern int gb_usb_driver_init(void); extern void gb_usb_driver_exit(void); diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 0d8fcb55d9aa..aa28ce5c4fb0 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -804,6 +804,7 @@ static const struct gpbridge_device_id gb_uart_id_table[] = { { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_UART) }, { }, }; +MODULE_DEVICE_TABLE(gpbridge, gb_uart_id_table); static struct gpbridge_driver uart_driver = { .name = "uart", @@ -812,7 +813,7 @@ static struct gpbridge_driver uart_driver = { .id_table = gb_uart_id_table, }; -int gb_uart_driver_init(void) +static int gb_uart_driver_init(void) { int ret; @@ -828,9 +829,13 @@ int gb_uart_driver_init(void) return 0; } +module_init(gb_uart_driver_init); -void gb_uart_driver_exit(void) +static void gb_uart_driver_exit(void) { gb_gpbridge_deregister(&uart_driver); gb_tty_exit(); } + +module_exit(gb_uart_driver_exit); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From ea7c47771ba13e143b3c328af8e03a1c45045cf0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:12 +0530 Subject: greybus: usb: Create separate module Create separate module for usb gpbridge driver. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 5 +++-- drivers/staging/greybus/gpbridge.c | 9 --------- drivers/staging/greybus/gpbridge.h | 3 --- drivers/staging/greybus/usb.c | 5 ++++- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index e99a9a83dd8e..592525ce7e8b 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -14,8 +14,7 @@ greybus-y := core.o \ operation.o \ legacy.o -gb-phy-y := gpbridge.o \ - usb.o +gb-phy-y := gpbridge.o # Prefix all modules with gb- gb-vibrator-y := vibrator.o @@ -40,6 +39,7 @@ gb-uart-y := uart.o gb-pwm-y := pwm.o gb-gpio-y := gpio.o gb-i2c-y := i2c.o +gb-usb-y := usb.o obj-m += greybus.o obj-m += gb-phy.o @@ -68,6 +68,7 @@ obj-m += gb-uart.o obj-m += gb-pwm.o obj-m += gb-gpio.o obj-m += gb-i2c.o +obj-m += gb-usb.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 5a65564dc34c..67f18ac9fe37 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -317,15 +317,8 @@ static int __init gpbridge_init(void) goto error_gpbridge; } - if (gb_usb_driver_init()) { - pr_err("error initializing usb driver\n"); - goto error_usb; - } - return 0; -error_usb: - greybus_deregister(&gb_gpbridge_driver); error_gpbridge: bus_unregister(&gpbridge_bus_type); ida_destroy(&gpbridge_id); @@ -335,8 +328,6 @@ module_init(gpbridge_init); static void __exit gpbridge_exit(void) { - gb_usb_driver_exit(); - greybus_deregister(&gb_gpbridge_driver); bus_unregister(&gpbridge_bus_type); ida_destroy(&gpbridge_id); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index 532dd5682f02..d4339e62d8a4 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -66,9 +66,6 @@ void gb_##__driver##_exit(void) \ gb_gpbridge_deregister(&__driver); \ } -extern int gb_usb_driver_init(void); -extern void gb_usb_driver_exit(void); - /** * module_gpbridge_driver() - Helper macro for registering a gpbridge driver * @__gpbridge_driver: gpbridge_driver structure diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 2b4789bde0c2..2f68a1b49e3c 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -242,6 +242,7 @@ static const struct gpbridge_device_id gb_usb_id_table[] = { { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_USB) }, { }, }; +MODULE_DEVICE_TABLE(gpbridge, gb_usb_id_table); static struct gpbridge_driver usb_driver = { .name = "usb", @@ -249,4 +250,6 @@ static struct gpbridge_driver usb_driver = { .remove = gb_usb_remove, .id_table = gb_usb_id_table, }; -gb_gpbridge_builtin_driver(usb_driver); + +module_gpbridge_driver(usb_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From a4b9a098f27a653884d259ad4ca3500d4ad3107f Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:13 +0530 Subject: greybus: gpbridge: Rename gb-phy.ko module Rename gb-phy.ko module as gb-gpbridge.ko. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 592525ce7e8b..2680f7e7dc4a 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -14,7 +14,7 @@ greybus-y := core.o \ operation.o \ legacy.o -gb-phy-y := gpbridge.o +gb-gpbridge-y := gpbridge.o # Prefix all modules with gb- gb-vibrator-y := vibrator.o @@ -42,7 +42,7 @@ gb-i2c-y := i2c.o gb-usb-y := usb.o obj-m += greybus.o -obj-m += gb-phy.o +obj-m += gb-gpbridge.o obj-m += gb-vibrator.o obj-m += gb-power-supply.o obj-m += gb-loopback.o -- cgit v1.2.3-59-g8ed1b From b9662266aa2694936836bace57a90f908a0b7e24 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 9 May 2016 18:15:14 +0530 Subject: greybus: gpbridge: Remove (now) unused macro gb_gpbridge_builtin_driver() gb_gpbridge_builtin_driver() isn't used anymore, remove it. Tested on EVT 1.5 by inserting GP test module, all the devices were enumerated correctly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h index d4339e62d8a4..0cee2eb64237 100644 --- a/drivers/staging/greybus/gpbridge.h +++ b/drivers/staging/greybus/gpbridge.h @@ -56,16 +56,6 @@ void gb_gpbridge_deregister_driver(struct gpbridge_driver *driver); #define gb_gpbridge_deregister(driver) \ gb_gpbridge_deregister_driver(driver) -#define gb_gpbridge_builtin_driver(__driver) \ - int __init gb_##__driver##_init(void) \ -{ \ - return gb_gpbridge_register(&__driver); \ -} \ -void gb_##__driver##_exit(void) \ -{ \ - gb_gpbridge_deregister(&__driver); \ -} - /** * module_gpbridge_driver() - Helper macro for registering a gpbridge driver * @__gpbridge_driver: gpbridge_driver structure -- cgit v1.2.3-59-g8ed1b From dc0754088e53e42fee1f880bc0c2002f624fe26f Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Tue, 10 May 2016 08:50:13 +0530 Subject: greybus: Remove bridge PHY protocol specific classes These protocols are managed under the bridged PHY class and doesn't need protocol specific classes anymore. Remove their entries from gb_gpbridge_id_table array and remove the now unused macro's and mark their values as unused. Tested on EVT 1.5 with generic-test module. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 7 ------- drivers/staging/greybus/greybus_manifest.h | 14 +++++++------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index 67f18ac9fe37..f082658d4d03 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -283,13 +283,6 @@ static int gb_gpbridge_probe(struct gb_bundle *bundle, static const struct greybus_bundle_id gb_gpbridge_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) }, - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, { }, }; MODULE_DEVICE_TABLE(greybus, gb_gpbridge_id_table); diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 5d00f7de5041..4f7019011642 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -55,16 +55,16 @@ enum greybus_protocol { enum greybus_class_type { GREYBUS_CLASS_CONTROL = 0x00, /* 0x01 is unused */ - GREYBUS_CLASS_GPIO = 0x02, - GREYBUS_CLASS_I2C = 0x03, - GREYBUS_CLASS_UART = 0x04, + /* 0x02 is unused */ + /* 0x03 is unused */ + /* 0x04 is unused */ GREYBUS_CLASS_HID = 0x05, - GREYBUS_CLASS_USB = 0x06, - GREYBUS_CLASS_SDIO = 0x07, + /* 0x06 is unused */ + /* 0x07 is unused */ GREYBUS_CLASS_POWER_SUPPLY = 0x08, - GREYBUS_CLASS_PWM = 0x09, + /* 0x09 is unused */ GREYBUS_CLASS_BRIDGED_PHY = 0x0a, - GREYBUS_CLASS_SPI = 0x0b, + /* 0x0b is unused */ GREYBUS_CLASS_DISPLAY = 0x0c, GREYBUS_CLASS_CAMERA = 0x0d, GREYBUS_CLASS_SENSOR = 0x0e, -- cgit v1.2.3-59-g8ed1b From 4c412921c78732f8f803a5906c97746ede5cf77c Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 14 May 2016 23:42:19 +0530 Subject: greybus: spi: Restructure spi.c to share it with other bundle drivers This patch restructures spi.c as spilib core, so that the same logic can be reused for SPI connections implemented as part of different bundle types. This is required for Firmware Management Bundle. Note that the 'struct gb_protocol' and its callback aren't moved to a separate file in this commit to make its reviews easier. That will be done by a following patch. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 4 +- drivers/staging/greybus/spi.c | 563 ------------------------------------- drivers/staging/greybus/spilib.c | 583 +++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/spilib.h | 18 ++ 4 files changed, 603 insertions(+), 565 deletions(-) delete mode 100644 drivers/staging/greybus/spi.c create mode 100644 drivers/staging/greybus/spilib.c create mode 100644 drivers/staging/greybus/spilib.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 2680f7e7dc4a..a9802dacb7f1 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -33,7 +33,7 @@ gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o gb-firmware-y := fw-core.o fw-download.o -gb-spi-y := spi.o +gb-spilib-y := spilib.o gb-sdio-y := sdio.o gb-uart-y := uart.o gb-pwm-y := pwm.o @@ -62,7 +62,7 @@ obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o obj-m += gb-audio-manager.o obj-m += gb-firmware.o -obj-m += gb-spi.o +obj-m += gb-spilib.o obj-m += gb-sdio.o obj-m += gb-uart.o obj-m += gb-pwm.o diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c deleted file mode 100644 index 6cf18d179a75..000000000000 --- a/drivers/staging/greybus/spi.c +++ /dev/null @@ -1,563 +0,0 @@ -/* - * SPI bridge driver for the Greybus "generic" SPI module. - * - * Copyright 2014-2015 Google Inc. - * Copyright 2014-2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include <linux/bitops.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/spi/spi.h> - -#include "greybus.h" -#include "gpbridge.h" - -struct gb_spi { - struct gb_connection *connection; - struct gpbridge_device *gpbdev; - struct spi_transfer *first_xfer; - struct spi_transfer *last_xfer; - u32 rx_xfer_offset; - u32 tx_xfer_offset; - u32 last_xfer_size; - unsigned int op_timeout; - u16 mode; - u16 flags; - u32 bits_per_word_mask; - u8 num_chipselect; - u32 min_speed_hz; - u32 max_speed_hz; -}; - -#define GB_SPI_STATE_MSG_DONE ((void *)0) -#define GB_SPI_STATE_MSG_IDLE ((void *)1) -#define GB_SPI_STATE_MSG_RUNNING ((void *)2) -#define GB_SPI_STATE_OP_READY ((void *)3) -#define GB_SPI_STATE_OP_DONE ((void *)4) -#define GB_SPI_STATE_MSG_ERROR ((void *)-1) - -#define XFER_TIMEOUT_TOLERANCE 200 - -static struct spi_master *get_master_from_spi(struct gb_spi *spi) -{ - return gb_connection_get_data(spi->connection); -} - -static int tx_header_fit_operation(u32 tx_size, u32 count, size_t data_max) -{ - size_t headers_size; - - data_max -= sizeof(struct gb_spi_transfer_request); - headers_size = (count + 1) * sizeof(struct gb_spi_transfer); - - return tx_size + headers_size > data_max ? 0 : 1; -} - -static size_t calc_rx_xfer_size(u32 rx_size, u32 *tx_xfer_size, u32 len, - size_t data_max) -{ - size_t rx_xfer_size; - - data_max -= sizeof(struct gb_spi_transfer_response); - - if (rx_size + len > data_max) - rx_xfer_size = data_max - rx_size; - else - rx_xfer_size = len; - - /* if this is a write_read, for symmetry read the same as write */ - if (*tx_xfer_size && rx_xfer_size > *tx_xfer_size) - rx_xfer_size = *tx_xfer_size; - if (*tx_xfer_size && rx_xfer_size < *tx_xfer_size) - *tx_xfer_size = rx_xfer_size; - - return rx_xfer_size; -} - -static size_t calc_tx_xfer_size(u32 tx_size, u32 count, size_t len, - size_t data_max) -{ - size_t headers_size; - - data_max -= sizeof(struct gb_spi_transfer_request); - headers_size = (count + 1) * sizeof(struct gb_spi_transfer); - - if (tx_size + headers_size + len > data_max) - return data_max - (tx_size + sizeof(struct gb_spi_transfer)); - - return len; -} - -static void clean_xfer_state(struct gb_spi *spi) -{ - spi->first_xfer = NULL; - spi->last_xfer = NULL; - spi->rx_xfer_offset = 0; - spi->tx_xfer_offset = 0; - spi->last_xfer_size = 0; - spi->op_timeout = 0; -} - -static int setup_next_xfer(struct gb_spi *spi, struct spi_message *msg) -{ - struct spi_transfer *last_xfer = spi->last_xfer; - - if (msg->state != GB_SPI_STATE_OP_DONE) - return 0; - - /* - * if we transferred all content of the last transfer, reset values and - * check if this was the last transfer in the message - */ - if ((spi->tx_xfer_offset + spi->last_xfer_size == last_xfer->len) || - (spi->rx_xfer_offset + spi->last_xfer_size == last_xfer->len)) { - spi->tx_xfer_offset = 0; - spi->rx_xfer_offset = 0; - spi->op_timeout = 0; - if (last_xfer == list_last_entry(&msg->transfers, - struct spi_transfer, - transfer_list)) - msg->state = GB_SPI_STATE_MSG_DONE; - else - spi->first_xfer = list_next_entry(last_xfer, - transfer_list); - return 0; - } - - spi->first_xfer = last_xfer; - if (last_xfer->tx_buf) - spi->tx_xfer_offset += spi->last_xfer_size; - - if (last_xfer->rx_buf) - spi->rx_xfer_offset += spi->last_xfer_size; - - return 0; -} - -static struct spi_transfer *get_next_xfer(struct spi_transfer *xfer, - struct spi_message *msg) -{ - if (xfer == list_last_entry(&msg->transfers, struct spi_transfer, - transfer_list)) - return NULL; - - return list_next_entry(xfer, transfer_list); -} - -/* Routines to transfer data */ -static struct gb_operation * -gb_spi_operation_create(struct gb_spi *spi, struct gb_connection *connection, - struct spi_message *msg) -{ - struct gb_spi_transfer_request *request; - struct spi_device *dev = msg->spi; - struct spi_transfer *xfer; - struct gb_spi_transfer *gb_xfer; - struct gb_operation *operation; - u32 tx_size = 0, rx_size = 0, count = 0, xfer_len = 0, request_size; - u32 tx_xfer_size = 0, rx_xfer_size = 0, len; - u32 total_len = 0; - unsigned int xfer_timeout; - size_t data_max; - void *tx_data; - - data_max = gb_operation_get_payload_size_max(connection); - xfer = spi->first_xfer; - - /* Find number of transfers queued and tx/rx length in the message */ - - while (msg->state != GB_SPI_STATE_OP_READY) { - msg->state = GB_SPI_STATE_MSG_RUNNING; - spi->last_xfer = xfer; - - if (!xfer->tx_buf && !xfer->rx_buf) { - dev_err(&spi->gpbdev->dev, - "bufferless transfer, length %u\n", xfer->len); - msg->state = GB_SPI_STATE_MSG_ERROR; - return NULL; - } - - tx_xfer_size = 0; - rx_xfer_size = 0; - - if (xfer->tx_buf) { - len = xfer->len - spi->tx_xfer_offset; - if (!tx_header_fit_operation(tx_size, count, data_max)) - break; - tx_xfer_size = calc_tx_xfer_size(tx_size, count, - len, data_max); - spi->last_xfer_size = tx_xfer_size; - } - - if (xfer->rx_buf) { - len = xfer->len - spi->rx_xfer_offset; - rx_xfer_size = calc_rx_xfer_size(rx_size, &tx_xfer_size, - len, data_max); - spi->last_xfer_size = rx_xfer_size; - } - - tx_size += tx_xfer_size; - rx_size += rx_xfer_size; - - total_len += spi->last_xfer_size; - count++; - - xfer = get_next_xfer(xfer, msg); - if (!xfer || total_len >= data_max) - msg->state = GB_SPI_STATE_OP_READY; - } - - /* - * In addition to space for all message descriptors we need - * to have enough to hold all tx data. - */ - request_size = sizeof(*request); - request_size += count * sizeof(*gb_xfer); - request_size += tx_size; - - /* Response consists only of incoming data */ - operation = gb_operation_create(connection, GB_SPI_TYPE_TRANSFER, - request_size, rx_size, GFP_KERNEL); - if (!operation) - return NULL; - - request = operation->request->payload; - request->count = cpu_to_le16(count); - request->mode = dev->mode; - request->chip_select = dev->chip_select; - - gb_xfer = &request->transfers[0]; - tx_data = gb_xfer + count; /* place tx data after last gb_xfer */ - - /* Fill in the transfers array */ - xfer = spi->first_xfer; - while (msg->state != GB_SPI_STATE_OP_DONE) { - if (xfer == spi->last_xfer) - xfer_len = spi->last_xfer_size; - else - xfer_len = xfer->len; - - /* make sure we do not timeout in a slow transfer */ - xfer_timeout = xfer_len * 8 * MSEC_PER_SEC / xfer->speed_hz; - xfer_timeout += GB_OPERATION_TIMEOUT_DEFAULT; - - if (xfer_timeout > spi->op_timeout) - spi->op_timeout = xfer_timeout; - - gb_xfer->speed_hz = cpu_to_le32(xfer->speed_hz); - gb_xfer->len = cpu_to_le32(xfer_len); - gb_xfer->delay_usecs = cpu_to_le16(xfer->delay_usecs); - gb_xfer->cs_change = xfer->cs_change; - gb_xfer->bits_per_word = xfer->bits_per_word; - - /* Copy tx data */ - if (xfer->tx_buf) { - gb_xfer->rdwr |= GB_SPI_XFER_WRITE; - memcpy(tx_data, xfer->tx_buf + spi->tx_xfer_offset, - xfer_len); - tx_data += xfer_len; - } - - if (xfer->rx_buf) - gb_xfer->rdwr |= GB_SPI_XFER_READ; - - if (xfer == spi->last_xfer) { - msg->state = GB_SPI_STATE_OP_DONE; - continue; - } - - gb_xfer++; - xfer = get_next_xfer(xfer, msg); - } - - msg->actual_length += total_len; - - return operation; -} - -static void gb_spi_decode_response(struct gb_spi *spi, struct spi_message *msg, - struct gb_spi_transfer_response *response) -{ - struct spi_transfer *xfer = spi->first_xfer; - void *rx_data = response->data; - u32 xfer_len; - - while (xfer) { - /* Copy rx data */ - if (xfer->rx_buf) { - if (xfer == spi->first_xfer) - xfer_len = xfer->len - spi->rx_xfer_offset; - else if (xfer == spi->last_xfer) - xfer_len = spi->last_xfer_size; - else - xfer_len = xfer->len; - - memcpy(xfer->rx_buf + spi->rx_xfer_offset, rx_data, - xfer_len); - rx_data += xfer_len; - } - - if (xfer == spi->last_xfer) - break; - - xfer = list_next_entry(xfer, transfer_list); - } -} - -static int gb_spi_transfer_one_message(struct spi_master *master, - struct spi_message *msg) -{ - struct gb_spi *spi = spi_master_get_devdata(master); - struct gb_connection *connection = spi->connection; - struct gb_spi_transfer_response *response; - struct gb_operation *operation; - int ret = 0; - - spi->first_xfer = list_first_entry_or_null(&msg->transfers, - struct spi_transfer, - transfer_list); - if (!spi->first_xfer) { - ret = -ENOMEM; - goto out; - } - - msg->state = GB_SPI_STATE_MSG_IDLE; - - while (msg->state != GB_SPI_STATE_MSG_DONE && - msg->state != GB_SPI_STATE_MSG_ERROR) { - operation = gb_spi_operation_create(spi, connection, msg); - if (!operation) { - msg->state = GB_SPI_STATE_MSG_ERROR; - ret = -EINVAL; - continue; - } - - ret = gb_operation_request_send_sync_timeout(operation, - spi->op_timeout); - if (!ret) { - response = operation->response->payload; - if (response) - gb_spi_decode_response(spi, msg, response); - } else { - dev_err(&spi->gpbdev->dev, - "transfer operation failed: %d\n", ret); - msg->state = GB_SPI_STATE_MSG_ERROR; - } - - gb_operation_put(operation); - setup_next_xfer(spi, msg); - } - -out: - msg->status = ret; - clean_xfer_state(spi); - spi_finalize_current_message(master); - - return ret; -} - -static int gb_spi_setup(struct spi_device *spi) -{ - /* Nothing to do for now */ - return 0; -} - -static void gb_spi_cleanup(struct spi_device *spi) -{ - /* Nothing to do for now */ -} - - -/* Routines to get controller information */ - -/* - * Map Greybus spi mode bits/flags/bpw into Linux ones. - * All bits are same for now and so these macro's return same values. - */ -#define gb_spi_mode_map(mode) mode -#define gb_spi_flags_map(flags) flags - -static int gb_spi_get_master_config(struct gb_spi *spi) -{ - struct gb_spi_master_config_response response; - u16 mode, flags; - int ret; - - ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_MASTER_CONFIG, - NULL, 0, &response, sizeof(response)); - if (ret < 0) - return ret; - - mode = le16_to_cpu(response.mode); - spi->mode = gb_spi_mode_map(mode); - - flags = le16_to_cpu(response.flags); - spi->flags = gb_spi_flags_map(flags); - - spi->bits_per_word_mask = le32_to_cpu(response.bits_per_word_mask); - spi->num_chipselect = response.num_chipselect; - - spi->min_speed_hz = le32_to_cpu(response.min_speed_hz); - spi->max_speed_hz = le32_to_cpu(response.max_speed_hz); - - return 0; -} - -static int gb_spi_setup_device(struct gb_spi *spi, u8 cs) -{ - struct spi_master *master = get_master_from_spi(spi); - struct gb_spi_device_config_request request; - struct gb_spi_device_config_response response; - struct spi_board_info spi_board = { {0} }; - struct spi_device *spidev; - int ret; - u8 dev_type; - - request.chip_select = cs; - - ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_DEVICE_CONFIG, - &request, sizeof(request), - &response, sizeof(response)); - if (ret < 0) - return ret; - - dev_type = response.device_type; - - if (dev_type == GB_SPI_SPI_DEV) - strlcpy(spi_board.modalias, SPI_DEV_MODALIAS, - sizeof(spi_board.modalias)); - else if (dev_type == GB_SPI_SPI_NOR) - strlcpy(spi_board.modalias, SPI_NOR_MODALIAS, - sizeof(spi_board.modalias)); - else if (dev_type == GB_SPI_SPI_MODALIAS) - memcpy(spi_board.modalias, response.name, - sizeof(spi_board.modalias)); - else - return -EINVAL; - - spi_board.mode = le16_to_cpu(response.mode); - spi_board.bus_num = master->bus_num; - spi_board.chip_select = cs; - spi_board.max_speed_hz = le32_to_cpu(response.max_speed_hz); - - spidev = spi_new_device(master, &spi_board); - if (!spidev) - return -EINVAL; - - return 0; -} - -static int gb_spi_probe(struct gpbridge_device *gpbdev, - const struct gpbridge_device_id *id) -{ - struct gb_connection *connection; - struct gb_spi *spi; - struct spi_master *master; - int ret; - u8 i; - - /* Allocate master with space for data */ - master = spi_alloc_master(&gpbdev->dev, sizeof(*spi)); - if (!master) { - dev_err(&gpbdev->dev, "cannot alloc SPI master\n"); - return -ENOMEM; - } - - connection = gb_connection_create(gpbdev->bundle, - le16_to_cpu(gpbdev->cport_desc->id), - NULL); - if (IS_ERR(connection)) { - ret = PTR_ERR(connection); - goto exit_spi_put; - } - - spi = spi_master_get_devdata(master); - spi->connection = connection; - gb_connection_set_data(connection, master); - spi->gpbdev = gpbdev; - gb_gpbridge_set_data(gpbdev, master); - - ret = gb_connection_enable(connection); - if (ret) - goto exit_connection_destroy; - - ret = gb_gpbridge_get_version(connection); - if (ret) - goto exit_connection_disable; - - /* get master configuration */ - ret = gb_spi_get_master_config(spi); - if (ret) - goto exit_connection_disable; - - master->bus_num = -1; /* Allow spi-core to allocate it dynamically */ - master->num_chipselect = spi->num_chipselect; - master->mode_bits = spi->mode; - master->flags = spi->flags; - master->bits_per_word_mask = spi->bits_per_word_mask; - - /* Attach methods */ - master->cleanup = gb_spi_cleanup; - master->setup = gb_spi_setup; - master->transfer_one_message = gb_spi_transfer_one_message; - - ret = spi_register_master(master); - if (ret < 0) - goto exit_connection_disable; - - /* now, fetch the devices configuration */ - for (i = 0; i < spi->num_chipselect; i++) { - ret = gb_spi_setup_device(spi, i); - if (ret < 0) { - dev_err(&gpbdev->dev, - "failed to allocate spi device %d: %d\n", - i, ret); - goto exit_spi_unregister; - } - } - - return ret; - -exit_spi_unregister: - spi_unregister_master(master); -exit_connection_disable: - gb_connection_disable(connection); -exit_connection_destroy: - gb_connection_destroy(connection); -exit_spi_put: - spi_master_put(master); - - return ret; -} - -static void gb_spi_remove(struct gpbridge_device *gpbdev) -{ - struct spi_master *master = gb_gpbridge_get_data(gpbdev); - struct gb_spi *spi = spi_master_get_devdata(master); - struct gb_connection *connection = spi->connection; - - spi_unregister_master(master); - gb_connection_disable(connection); - gb_connection_destroy(connection); - spi_master_put(master); -} - -static const struct gpbridge_device_id gb_spi_id_table[] = { - { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, - { }, -}; -MODULE_DEVICE_TABLE(gpbridge, gb_spi_id_table); - -static struct gpbridge_driver spi_driver = { - .name = "spi", - .probe = gb_spi_probe, - .remove = gb_spi_remove, - .id_table = gb_spi_id_table, -}; - -module_gpbridge_driver(spi_driver); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c new file mode 100644 index 000000000000..c7fe87801187 --- /dev/null +++ b/drivers/staging/greybus/spilib.c @@ -0,0 +1,583 @@ +/* + * Greybus SPI library + * + * Copyright 2014-2016 Google Inc. + * Copyright 2014-2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include <linux/bitops.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include "greybus.h" +#include "gpbridge.h" +#include "spilib.h" + +struct gb_spilib { + struct gb_connection *connection; + struct device *parent; + struct spi_transfer *first_xfer; + struct spi_transfer *last_xfer; + u32 rx_xfer_offset; + u32 tx_xfer_offset; + u32 last_xfer_size; + unsigned int op_timeout; + u16 mode; + u16 flags; + u32 bits_per_word_mask; + u8 num_chipselect; + u32 min_speed_hz; + u32 max_speed_hz; +}; + +#define GB_SPI_STATE_MSG_DONE ((void *)0) +#define GB_SPI_STATE_MSG_IDLE ((void *)1) +#define GB_SPI_STATE_MSG_RUNNING ((void *)2) +#define GB_SPI_STATE_OP_READY ((void *)3) +#define GB_SPI_STATE_OP_DONE ((void *)4) +#define GB_SPI_STATE_MSG_ERROR ((void *)-1) + +#define XFER_TIMEOUT_TOLERANCE 200 + +static struct spi_master *get_master_from_spi(struct gb_spilib *spi) +{ + return gb_connection_get_data(spi->connection); +} + +static int tx_header_fit_operation(u32 tx_size, u32 count, size_t data_max) +{ + size_t headers_size; + + data_max -= sizeof(struct gb_spi_transfer_request); + headers_size = (count + 1) * sizeof(struct gb_spi_transfer); + + return tx_size + headers_size > data_max ? 0 : 1; +} + +static size_t calc_rx_xfer_size(u32 rx_size, u32 *tx_xfer_size, u32 len, + size_t data_max) +{ + size_t rx_xfer_size; + + data_max -= sizeof(struct gb_spi_transfer_response); + + if (rx_size + len > data_max) + rx_xfer_size = data_max - rx_size; + else + rx_xfer_size = len; + + /* if this is a write_read, for symmetry read the same as write */ + if (*tx_xfer_size && rx_xfer_size > *tx_xfer_size) + rx_xfer_size = *tx_xfer_size; + if (*tx_xfer_size && rx_xfer_size < *tx_xfer_size) + *tx_xfer_size = rx_xfer_size; + + return rx_xfer_size; +} + +static size_t calc_tx_xfer_size(u32 tx_size, u32 count, size_t len, + size_t data_max) +{ + size_t headers_size; + + data_max -= sizeof(struct gb_spi_transfer_request); + headers_size = (count + 1) * sizeof(struct gb_spi_transfer); + + if (tx_size + headers_size + len > data_max) + return data_max - (tx_size + sizeof(struct gb_spi_transfer)); + + return len; +} + +static void clean_xfer_state(struct gb_spilib *spi) +{ + spi->first_xfer = NULL; + spi->last_xfer = NULL; + spi->rx_xfer_offset = 0; + spi->tx_xfer_offset = 0; + spi->last_xfer_size = 0; + spi->op_timeout = 0; +} + +static int setup_next_xfer(struct gb_spilib *spi, struct spi_message *msg) +{ + struct spi_transfer *last_xfer = spi->last_xfer; + + if (msg->state != GB_SPI_STATE_OP_DONE) + return 0; + + /* + * if we transferred all content of the last transfer, reset values and + * check if this was the last transfer in the message + */ + if ((spi->tx_xfer_offset + spi->last_xfer_size == last_xfer->len) || + (spi->rx_xfer_offset + spi->last_xfer_size == last_xfer->len)) { + spi->tx_xfer_offset = 0; + spi->rx_xfer_offset = 0; + spi->op_timeout = 0; + if (last_xfer == list_last_entry(&msg->transfers, + struct spi_transfer, + transfer_list)) + msg->state = GB_SPI_STATE_MSG_DONE; + else + spi->first_xfer = list_next_entry(last_xfer, + transfer_list); + return 0; + } + + spi->first_xfer = last_xfer; + if (last_xfer->tx_buf) + spi->tx_xfer_offset += spi->last_xfer_size; + + if (last_xfer->rx_buf) + spi->rx_xfer_offset += spi->last_xfer_size; + + return 0; +} + +static struct spi_transfer *get_next_xfer(struct spi_transfer *xfer, + struct spi_message *msg) +{ + if (xfer == list_last_entry(&msg->transfers, struct spi_transfer, + transfer_list)) + return NULL; + + return list_next_entry(xfer, transfer_list); +} + +/* Routines to transfer data */ +static struct gb_operation *gb_spi_operation_create(struct gb_spilib *spi, + struct gb_connection *connection, struct spi_message *msg) +{ + struct gb_spi_transfer_request *request; + struct spi_device *dev = msg->spi; + struct spi_transfer *xfer; + struct gb_spi_transfer *gb_xfer; + struct gb_operation *operation; + u32 tx_size = 0, rx_size = 0, count = 0, xfer_len = 0, request_size; + u32 tx_xfer_size = 0, rx_xfer_size = 0, len; + u32 total_len = 0; + unsigned int xfer_timeout; + size_t data_max; + void *tx_data; + + data_max = gb_operation_get_payload_size_max(connection); + xfer = spi->first_xfer; + + /* Find number of transfers queued and tx/rx length in the message */ + + while (msg->state != GB_SPI_STATE_OP_READY) { + msg->state = GB_SPI_STATE_MSG_RUNNING; + spi->last_xfer = xfer; + + if (!xfer->tx_buf && !xfer->rx_buf) { + dev_err(spi->parent, + "bufferless transfer, length %u\n", xfer->len); + msg->state = GB_SPI_STATE_MSG_ERROR; + return NULL; + } + + tx_xfer_size = 0; + rx_xfer_size = 0; + + if (xfer->tx_buf) { + len = xfer->len - spi->tx_xfer_offset; + if (!tx_header_fit_operation(tx_size, count, data_max)) + break; + tx_xfer_size = calc_tx_xfer_size(tx_size, count, + len, data_max); + spi->last_xfer_size = tx_xfer_size; + } + + if (xfer->rx_buf) { + len = xfer->len - spi->rx_xfer_offset; + rx_xfer_size = calc_rx_xfer_size(rx_size, &tx_xfer_size, + len, data_max); + spi->last_xfer_size = rx_xfer_size; + } + + tx_size += tx_xfer_size; + rx_size += rx_xfer_size; + + total_len += spi->last_xfer_size; + count++; + + xfer = get_next_xfer(xfer, msg); + if (!xfer || total_len >= data_max) + msg->state = GB_SPI_STATE_OP_READY; + } + + /* + * In addition to space for all message descriptors we need + * to have enough to hold all tx data. + */ + request_size = sizeof(*request); + request_size += count * sizeof(*gb_xfer); + request_size += tx_size; + + /* Response consists only of incoming data */ + operation = gb_operation_create(connection, GB_SPI_TYPE_TRANSFER, + request_size, rx_size, GFP_KERNEL); + if (!operation) + return NULL; + + request = operation->request->payload; + request->count = cpu_to_le16(count); + request->mode = dev->mode; + request->chip_select = dev->chip_select; + + gb_xfer = &request->transfers[0]; + tx_data = gb_xfer + count; /* place tx data after last gb_xfer */ + + /* Fill in the transfers array */ + xfer = spi->first_xfer; + while (msg->state != GB_SPI_STATE_OP_DONE) { + if (xfer == spi->last_xfer) + xfer_len = spi->last_xfer_size; + else + xfer_len = xfer->len; + + /* make sure we do not timeout in a slow transfer */ + xfer_timeout = xfer_len * 8 * MSEC_PER_SEC / xfer->speed_hz; + xfer_timeout += GB_OPERATION_TIMEOUT_DEFAULT; + + if (xfer_timeout > spi->op_timeout) + spi->op_timeout = xfer_timeout; + + gb_xfer->speed_hz = cpu_to_le32(xfer->speed_hz); + gb_xfer->len = cpu_to_le32(xfer_len); + gb_xfer->delay_usecs = cpu_to_le16(xfer->delay_usecs); + gb_xfer->cs_change = xfer->cs_change; + gb_xfer->bits_per_word = xfer->bits_per_word; + + /* Copy tx data */ + if (xfer->tx_buf) { + gb_xfer->rdwr |= GB_SPI_XFER_WRITE; + memcpy(tx_data, xfer->tx_buf + spi->tx_xfer_offset, + xfer_len); + tx_data += xfer_len; + } + + if (xfer->rx_buf) + gb_xfer->rdwr |= GB_SPI_XFER_READ; + + if (xfer == spi->last_xfer) { + msg->state = GB_SPI_STATE_OP_DONE; + continue; + } + + gb_xfer++; + xfer = get_next_xfer(xfer, msg); + } + + msg->actual_length += total_len; + + return operation; +} + +static void gb_spi_decode_response(struct gb_spilib *spi, + struct spi_message *msg, + struct gb_spi_transfer_response *response) +{ + struct spi_transfer *xfer = spi->first_xfer; + void *rx_data = response->data; + u32 xfer_len; + + while (xfer) { + /* Copy rx data */ + if (xfer->rx_buf) { + if (xfer == spi->first_xfer) + xfer_len = xfer->len - spi->rx_xfer_offset; + else if (xfer == spi->last_xfer) + xfer_len = spi->last_xfer_size; + else + xfer_len = xfer->len; + + memcpy(xfer->rx_buf + spi->rx_xfer_offset, rx_data, + xfer_len); + rx_data += xfer_len; + } + + if (xfer == spi->last_xfer) + break; + + xfer = list_next_entry(xfer, transfer_list); + } +} + +static int gb_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct gb_spilib *spi = spi_master_get_devdata(master); + struct gb_connection *connection = spi->connection; + struct gb_spi_transfer_response *response; + struct gb_operation *operation; + int ret = 0; + + spi->first_xfer = list_first_entry_or_null(&msg->transfers, + struct spi_transfer, + transfer_list); + if (!spi->first_xfer) { + ret = -ENOMEM; + goto out; + } + + msg->state = GB_SPI_STATE_MSG_IDLE; + + while (msg->state != GB_SPI_STATE_MSG_DONE && + msg->state != GB_SPI_STATE_MSG_ERROR) { + operation = gb_spi_operation_create(spi, connection, msg); + if (!operation) { + msg->state = GB_SPI_STATE_MSG_ERROR; + ret = -EINVAL; + continue; + } + + ret = gb_operation_request_send_sync_timeout(operation, + spi->op_timeout); + if (!ret) { + response = operation->response->payload; + if (response) + gb_spi_decode_response(spi, msg, response); + } else { + dev_err(spi->parent, + "transfer operation failed: %d\n", ret); + msg->state = GB_SPI_STATE_MSG_ERROR; + } + + gb_operation_put(operation); + setup_next_xfer(spi, msg); + } + +out: + msg->status = ret; + clean_xfer_state(spi); + spi_finalize_current_message(master); + + return ret; +} + +static int gb_spi_setup(struct spi_device *spi) +{ + /* Nothing to do for now */ + return 0; +} + +static void gb_spi_cleanup(struct spi_device *spi) +{ + /* Nothing to do for now */ +} + + +/* Routines to get controller information */ + +/* + * Map Greybus spi mode bits/flags/bpw into Linux ones. + * All bits are same for now and so these macro's return same values. + */ +#define gb_spi_mode_map(mode) mode +#define gb_spi_flags_map(flags) flags + +static int gb_spi_get_master_config(struct gb_spilib *spi) +{ + struct gb_spi_master_config_response response; + u16 mode, flags; + int ret; + + ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_MASTER_CONFIG, + NULL, 0, &response, sizeof(response)); + if (ret < 0) + return ret; + + mode = le16_to_cpu(response.mode); + spi->mode = gb_spi_mode_map(mode); + + flags = le16_to_cpu(response.flags); + spi->flags = gb_spi_flags_map(flags); + + spi->bits_per_word_mask = le32_to_cpu(response.bits_per_word_mask); + spi->num_chipselect = response.num_chipselect; + + spi->min_speed_hz = le32_to_cpu(response.min_speed_hz); + spi->max_speed_hz = le32_to_cpu(response.max_speed_hz); + + return 0; +} + +static int gb_spi_setup_device(struct gb_spilib *spi, u8 cs) +{ + struct spi_master *master = get_master_from_spi(spi); + struct gb_spi_device_config_request request; + struct gb_spi_device_config_response response; + struct spi_board_info spi_board = { {0} }; + struct spi_device *spidev; + int ret; + u8 dev_type; + + request.chip_select = cs; + + ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_DEVICE_CONFIG, + &request, sizeof(request), + &response, sizeof(response)); + if (ret < 0) + return ret; + + dev_type = response.device_type; + + if (dev_type == GB_SPI_SPI_DEV) + strlcpy(spi_board.modalias, SPI_DEV_MODALIAS, + sizeof(spi_board.modalias)); + else if (dev_type == GB_SPI_SPI_NOR) + strlcpy(spi_board.modalias, SPI_NOR_MODALIAS, + sizeof(spi_board.modalias)); + else if (dev_type == GB_SPI_SPI_MODALIAS) + memcpy(spi_board.modalias, response.name, + sizeof(spi_board.modalias)); + else + return -EINVAL; + + spi_board.mode = le16_to_cpu(response.mode); + spi_board.bus_num = master->bus_num; + spi_board.chip_select = cs; + spi_board.max_speed_hz = le32_to_cpu(response.max_speed_hz); + + spidev = spi_new_device(master, &spi_board); + if (!spidev) + return -EINVAL; + + return 0; +} + +int gb_spilib_master_init(struct gb_connection *connection, struct device *dev) +{ + struct gb_spilib *spi; + struct spi_master *master; + int ret; + u8 i; + + /* Allocate master with space for data */ + master = spi_alloc_master(dev, sizeof(*spi)); + if (!master) { + dev_err(dev, "cannot alloc SPI master\n"); + return -ENOMEM; + } + + spi = spi_master_get_devdata(master); + spi->connection = connection; + gb_connection_set_data(connection, master); + spi->parent = dev; + + /* get master configuration */ + ret = gb_spi_get_master_config(spi); + if (ret) + goto exit_spi_put; + + master->bus_num = -1; /* Allow spi-core to allocate it dynamically */ + master->num_chipselect = spi->num_chipselect; + master->mode_bits = spi->mode; + master->flags = spi->flags; + master->bits_per_word_mask = spi->bits_per_word_mask; + + /* Attach methods */ + master->cleanup = gb_spi_cleanup; + master->setup = gb_spi_setup; + master->transfer_one_message = gb_spi_transfer_one_message; + + ret = spi_register_master(master); + if (ret < 0) + goto exit_spi_put; + + /* now, fetch the devices configuration */ + for (i = 0; i < spi->num_chipselect; i++) { + ret = gb_spi_setup_device(spi, i); + if (ret < 0) { + dev_err(dev, "failed to allocate spi device %d: %d\n", + i, ret); + goto exit_spi_unregister; + } + } + + return 0; + +exit_spi_unregister: + spi_unregister_master(master); +exit_spi_put: + spi_master_put(master); + + return ret; +} +EXPORT_SYMBOL_GPL(gb_spilib_master_init); + +void gb_spilib_master_exit(struct gb_connection *connection) +{ + struct spi_master *master = gb_connection_get_data(connection); + + spi_unregister_master(master); + spi_master_put(master); +} +EXPORT_SYMBOL_GPL(gb_spilib_master_exit); + +static int gb_spi_probe(struct gpbridge_device *gpbdev, + const struct gpbridge_device_id *id) +{ + struct gb_connection *connection; + int ret; + + connection = gb_connection_create(gpbdev->bundle, + le16_to_cpu(gpbdev->cport_desc->id), + NULL); + if (IS_ERR(connection)) + return PTR_ERR(connection); + + ret = gb_connection_enable(connection); + if (ret) + goto exit_connection_destroy; + + ret = gb_gpbridge_get_version(connection); + if (ret) + goto exit_connection_disable; + + ret = gb_spilib_master_init(connection, &gpbdev->dev); + if (ret) + goto exit_connection_disable; + + gb_gpbridge_set_data(gpbdev, connection); + + return 0; + +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); + + return ret; +} + +static void gb_spi_remove(struct gpbridge_device *gpbdev) +{ + struct gb_connection *connection = gb_gpbridge_get_data(gpbdev); + + gb_spilib_master_exit(connection); + gb_connection_disable(connection); + gb_connection_destroy(connection); +} + +static const struct gpbridge_device_id gb_spi_id_table[] = { + { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, + { }, +}; +MODULE_DEVICE_TABLE(gpbridge, gb_spi_id_table); + +static struct gpbridge_driver spi_driver = { + .name = "spi", + .probe = gb_spi_probe, + .remove = gb_spi_remove, + .id_table = gb_spi_id_table, +}; + +module_gpbridge_driver(spi_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/spilib.h b/drivers/staging/greybus/spilib.h new file mode 100644 index 000000000000..9be1b3189834 --- /dev/null +++ b/drivers/staging/greybus/spilib.h @@ -0,0 +1,18 @@ +/* + * Greybus SPI library header + * + * copyright 2016 google inc. + * copyright 2016 linaro ltd. + * + * released under the gplv2 only. + */ + +#ifndef __SPILIB_H +#define __SPILIB_H + +struct gb_connection; + +int gb_spilib_master_init(struct gb_connection *connection, struct device *dev); +void gb_spilib_master_exit(struct gb_connection *connection); + +#endif /* __SPILIB_H */ -- cgit v1.2.3-59-g8ed1b From 8888b963743be7aad2a98bc165fb51150db5fd9f Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 14 May 2016 23:42:20 +0530 Subject: greybus: spi: Separate out spilib from spi bridged PHY bundle driver spilib can be used by multiple bridge drivers implementing different bundle classes. Separate out bridged PHY bundle drivers parts. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 2 ++ drivers/staging/greybus/spi.c | 75 ++++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/spilib.c | 60 -------------------------------- 3 files changed, 77 insertions(+), 60 deletions(-) create mode 100644 drivers/staging/greybus/spi.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index a9802dacb7f1..35fc6bff85fc 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -40,6 +40,7 @@ gb-pwm-y := pwm.o gb-gpio-y := gpio.o gb-i2c-y := i2c.o gb-usb-y := usb.o +gb-spi-y := spi.o obj-m += greybus.o obj-m += gb-gpbridge.o @@ -69,6 +70,7 @@ obj-m += gb-pwm.o obj-m += gb-gpio.o obj-m += gb-i2c.o obj-m += gb-usb.o +obj-m += gb-spi.o KERNELVER ?= $(shell uname -r) KERNELDIR ?= /lib/modules/$(KERNELVER)/build diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c new file mode 100644 index 000000000000..1cf5f509363c --- /dev/null +++ b/drivers/staging/greybus/spi.c @@ -0,0 +1,75 @@ +/* + * SPI bridge PHY driver. + * + * Copyright 2014-2016 Google Inc. + * Copyright 2014-2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include <linux/module.h> + +#include "greybus.h" +#include "gpbridge.h" +#include "spilib.h" + +static int gb_spi_probe(struct gpbridge_device *gpbdev, + const struct gpbridge_device_id *id) +{ + struct gb_connection *connection; + int ret; + + connection = gb_connection_create(gpbdev->bundle, + le16_to_cpu(gpbdev->cport_desc->id), + NULL); + if (IS_ERR(connection)) + return PTR_ERR(connection); + + ret = gb_connection_enable(connection); + if (ret) + goto exit_connection_destroy; + + ret = gb_gpbridge_get_version(connection); + if (ret) + goto exit_connection_disable; + + ret = gb_spilib_master_init(connection, &gpbdev->dev); + if (ret) + goto exit_connection_disable; + + gb_gpbridge_set_data(gpbdev, connection); + + return 0; + +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); + + return ret; +} + +static void gb_spi_remove(struct gpbridge_device *gpbdev) +{ + struct gb_connection *connection = gb_gpbridge_get_data(gpbdev); + + gb_spilib_master_exit(connection); + gb_connection_disable(connection); + gb_connection_destroy(connection); +} + +static const struct gpbridge_device_id gb_spi_id_table[] = { + { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, + { }, +}; +MODULE_DEVICE_TABLE(gpbridge, gb_spi_id_table); + +static struct gpbridge_driver spi_driver = { + .name = "spi", + .probe = gb_spi_probe, + .remove = gb_spi_remove, + .id_table = gb_spi_id_table, +}; + +module_gpbridge_driver(spi_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c index c7fe87801187..6ab1c5f77b7a 100644 --- a/drivers/staging/greybus/spilib.c +++ b/drivers/staging/greybus/spilib.c @@ -14,7 +14,6 @@ #include <linux/spi/spi.h> #include "greybus.h" -#include "gpbridge.h" #include "spilib.h" struct gb_spilib { @@ -521,63 +520,4 @@ void gb_spilib_master_exit(struct gb_connection *connection) } EXPORT_SYMBOL_GPL(gb_spilib_master_exit); -static int gb_spi_probe(struct gpbridge_device *gpbdev, - const struct gpbridge_device_id *id) -{ - struct gb_connection *connection; - int ret; - - connection = gb_connection_create(gpbdev->bundle, - le16_to_cpu(gpbdev->cport_desc->id), - NULL); - if (IS_ERR(connection)) - return PTR_ERR(connection); - - ret = gb_connection_enable(connection); - if (ret) - goto exit_connection_destroy; - - ret = gb_gpbridge_get_version(connection); - if (ret) - goto exit_connection_disable; - - ret = gb_spilib_master_init(connection, &gpbdev->dev); - if (ret) - goto exit_connection_disable; - - gb_gpbridge_set_data(gpbdev, connection); - - return 0; - -exit_connection_disable: - gb_connection_disable(connection); -exit_connection_destroy: - gb_connection_destroy(connection); - - return ret; -} - -static void gb_spi_remove(struct gpbridge_device *gpbdev) -{ - struct gb_connection *connection = gb_gpbridge_get_data(gpbdev); - - gb_spilib_master_exit(connection); - gb_connection_disable(connection); - gb_connection_destroy(connection); -} - -static const struct gpbridge_device_id gb_spi_id_table[] = { - { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, - { }, -}; -MODULE_DEVICE_TABLE(gpbridge, gb_spi_id_table); - -static struct gpbridge_driver spi_driver = { - .name = "spi", - .probe = gb_spi_probe, - .remove = gb_spi_remove, - .id_table = gb_spi_id_table, -}; - -module_gpbridge_driver(spi_driver); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 8502eb3b7870c6a0c53f39caaf5b4c01a22f2a25 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Sat, 14 May 2016 19:22:24 +0200 Subject: greybus: make greybus_gpbdev_type static 'make check' correctly complains that this should be static, so make it so. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpbridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c index f082658d4d03..88f7d26121e4 100644 --- a/drivers/staging/greybus/gpbridge.c +++ b/drivers/staging/greybus/gpbridge.c @@ -50,7 +50,7 @@ static void gpbdev_release(struct device *dev) kfree(gpbdev); } -struct device_type greybus_gpbdev_type = { +static struct device_type greybus_gpbdev_type = { .name = "gpbridge_device", .release = gpbdev_release, }; -- cgit v1.2.3-59-g8ed1b From 013e665372733fafd08599f0fc58ff5f450e4694 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 14 May 2016 23:42:21 +0530 Subject: greybus: fw-management: Add firmware-management protocol driver This patch adds Firmware Management Protocol support to firmware core, which allows the AP to manage firmware on an Interface. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Jun Li <li_jun@projectara.com> Tested-by: Karthik Ravi Shankar <karthikrs@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/firmware.h | 8 + drivers/staging/greybus/fw-core.c | 42 ++- drivers/staging/greybus/fw-management.c | 564 ++++++++++++++++++++++++++++ drivers/staging/greybus/greybus_firmware.h | 84 +++++ drivers/staging/greybus/greybus_protocols.h | 73 ++++ 6 files changed, 768 insertions(+), 5 deletions(-) create mode 100644 drivers/staging/greybus/fw-management.c create mode 100644 drivers/staging/greybus/greybus_firmware.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 35fc6bff85fc..608bd51e7c0b 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -32,7 +32,7 @@ gb-audio-apbridgea-y := audio_apbridgea.o gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o gb-camera-y := camera.o -gb-firmware-y := fw-core.o fw-download.o +gb-firmware-y := fw-core.o fw-download.o fw-management.o gb-spilib-y := spilib.o gb-sdio-y := sdio.o gb-uart-y := uart.o diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h index c5736d5f96a2..a82d0203971b 100644 --- a/drivers/staging/greybus/firmware.h +++ b/drivers/staging/greybus/firmware.h @@ -12,6 +12,14 @@ #include "greybus.h" +/* Firmware Management Protocol specific functions */ +int fw_mgmt_init(void); +void fw_mgmt_exit(void); +struct gb_connection *to_fw_mgmt_connection(struct device *dev); +int gb_fw_mgmt_request_handler(struct gb_operation *op); +int gb_fw_mgmt_connection_init(struct gb_connection *connection); +void gb_fw_mgmt_connection_exit(struct gb_connection *connection); + /* Firmware Download Protocol specific functions */ int gb_fw_download_request_handler(struct gb_operation *op); int gb_fw_download_connection_init(struct gb_connection *connection); diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c index a6865276855a..0a456c547c52 100644 --- a/drivers/staging/greybus/fw-core.c +++ b/drivers/staging/greybus/fw-core.c @@ -17,6 +17,13 @@ struct gb_fw_core { struct gb_connection *mgmt_connection; }; +struct gb_connection *to_fw_mgmt_connection(struct device *dev) +{ + struct gb_fw_core *fw_core = dev_get_drvdata(dev); + + return fw_core->mgmt_connection; +} + static int gb_fw_core_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { @@ -48,7 +55,7 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, } connection = gb_connection_create(bundle, cport_id, - NULL); + gb_fw_mgmt_request_handler); if (IS_ERR(connection)) { ret = PTR_ERR(connection); dev_err(&bundle->dev, @@ -102,13 +109,23 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, fw_core->download_connection = NULL; } + ret = gb_fw_mgmt_connection_init(fw_core->mgmt_connection); + if (ret) { + /* We may still be able to work with the Interface */ + dev_err(&bundle->dev, "failed to initialize firmware management connection, disable it (%d)\n", + ret); + goto err_exit_connections; + } + greybus_set_drvdata(bundle, fw_core); return 0; +err_exit_connections: + gb_fw_download_connection_exit(fw_core->download_connection); err_destroy_connections: - gb_connection_destroy(fw_core->download_connection); gb_connection_destroy(fw_core->mgmt_connection); + gb_connection_destroy(fw_core->download_connection); err_free_fw_core: kfree(fw_core); @@ -119,9 +136,11 @@ static void gb_fw_core_disconnect(struct gb_bundle *bundle) { struct gb_fw_core *fw_core = greybus_get_drvdata(bundle); + gb_fw_mgmt_connection_exit(fw_core->mgmt_connection); gb_fw_download_connection_exit(fw_core->download_connection); - gb_connection_destroy(fw_core->download_connection); + gb_connection_destroy(fw_core->mgmt_connection); + gb_connection_destroy(fw_core->download_connection); kfree(fw_core); } @@ -140,13 +159,28 @@ static struct greybus_driver gb_fw_core_driver = { static int fw_core_init(void) { - return greybus_register(&gb_fw_core_driver); + int ret; + + ret = fw_mgmt_init(); + if (ret) { + pr_err("Failed to initialize fw-mgmt core (%d)\n", ret); + return ret; + } + + ret = greybus_register(&gb_fw_core_driver); + if (ret) { + fw_mgmt_exit(); + return ret; + } + + return 0; } module_init(fw_core_init); static void __exit fw_core_exit(void) { greybus_deregister(&gb_fw_core_driver); + fw_mgmt_exit(); } module_exit(fw_core_exit); diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c new file mode 100644 index 000000000000..2db5a11c5a1d --- /dev/null +++ b/drivers/staging/greybus/fw-management.c @@ -0,0 +1,564 @@ +/* + * Greybus Firmware Management Protocol Driver. + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include <linux/cdev.h> +#include <linux/completion.h> +#include <linux/firmware.h> +#include <linux/fs.h> +#include <linux/idr.h> +#include <linux/ioctl.h> +#include <linux/uaccess.h> + +#include "firmware.h" +#include "greybus_firmware.h" +#include "greybus.h" + +#define FW_MGMT_TIMEOUT_MS 1000 + +struct fw_mgmt { + struct device *parent; + struct gb_connection *connection; + /* Common id-map for interface and backend firmware requests */ + struct ida id_map; + struct mutex mutex; + struct completion completion; + struct cdev cdev; + struct device *class_device; + dev_t dev_num; + unsigned int timeout_jiffies; + + /* Interface Firmware specific fields */ + u8 intf_fw_request_id; + u8 intf_fw_status; + u16 intf_fw_major; + u16 intf_fw_minor; + + /* Backend Firmware specific fields */ + u8 backend_fw_request_id; + u8 backend_fw_status; +}; + +/* + * Number of minor devices this driver supports. + * There will be exactly one required per Interface. + */ +#define NUM_MINORS U8_MAX + +static struct class *fw_mgmt_class; +static dev_t fw_mgmt_dev_num; +static DEFINE_IDA(fw_mgmt_minors_map); + +static int fw_mgmt_interface_fw_version_operation(struct fw_mgmt *fw_mgmt, + struct fw_mgmt_ioc_get_fw *fw_info) +{ + struct gb_connection *connection = fw_mgmt->connection; + struct gb_fw_mgmt_interface_fw_version_response response; + int ret; + + ret = gb_operation_sync(connection, + GB_FW_MGMT_TYPE_INTERFACE_FW_VERSION, NULL, 0, + &response, sizeof(response)); + if (ret) { + dev_err(fw_mgmt->parent, + "failed to get interface firmware version (%d)\n", ret); + return ret; + } + + fw_info->major = le16_to_cpu(response.major); + fw_info->minor = le16_to_cpu(response.minor); + + strncpy(fw_info->firmware_tag, response.firmware_tag, + GB_FIRMWARE_TAG_MAX_LEN); + + /* + * The firmware-tag should be NULL terminated, otherwise throw error but + * don't fail. + */ + if (fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] != '\0') { + dev_err(fw_mgmt->parent, + "fw-version: firmware-tag is not NULL terminated\n"); + fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] = '\0'; + } + + return 0; +} + +static int fw_mgmt_load_and_validate_operation(struct fw_mgmt *fw_mgmt, + u8 load_method, const char *tag) +{ + struct gb_fw_mgmt_load_and_validate_fw_request request; + int ret; + + if (load_method != GB_FW_LOAD_METHOD_UNIPRO && + load_method != GB_FW_LOAD_METHOD_INTERNAL) { + dev_err(fw_mgmt->parent, + "invalid load-method (%d)\n", load_method); + return -EINVAL; + } + + request.load_method = load_method; + strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_LEN); + + /* + * The firmware-tag should be NULL terminated, otherwise throw error and + * fail. + */ + if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] != '\0') { + dev_err(fw_mgmt->parent, "load-and-validate: firmware-tag is not NULL terminated\n"); + return -EINVAL; + } + + /* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */ + ret = ida_simple_get(&fw_mgmt->id_map, 1, 256, GFP_KERNEL); + if (ret < 0) { + dev_err(fw_mgmt->parent, "failed to allocate request id (%d)\n", + ret); + return ret; + } + + fw_mgmt->intf_fw_request_id = ret; + request.request_id = ret; + + ret = gb_operation_sync(fw_mgmt->connection, + GB_FW_MGMT_TYPE_LOAD_AND_VALIDATE_FW, &request, + sizeof(request), NULL, 0); + if (ret) { + ida_simple_remove(&fw_mgmt->id_map, + fw_mgmt->intf_fw_request_id); + fw_mgmt->intf_fw_request_id = 0; + dev_err(fw_mgmt->parent, + "load and validate firmware request failed (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int fw_mgmt_interface_fw_loaded_operation(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct fw_mgmt *fw_mgmt = gb_connection_get_data(connection); + struct gb_fw_mgmt_loaded_fw_request *request; + + /* No pending load and validate request ? */ + if (!fw_mgmt->intf_fw_request_id) { + dev_err(fw_mgmt->parent, + "unexpected firmware loaded request received\n"); + return -ENODEV; + } + + if (op->request->payload_size != sizeof(*request)) { + dev_err(fw_mgmt->parent, "illegal size of firmware loaded request (%zu != %zu)\n", + op->request->payload_size, sizeof(*request)); + return -EINVAL; + } + + request = op->request->payload; + + /* Invalid request-id ? */ + if (request->request_id != fw_mgmt->intf_fw_request_id) { + dev_err(fw_mgmt->parent, "invalid request id for firmware loaded request (%02u != %02u)\n", + fw_mgmt->intf_fw_request_id, request->request_id); + return -ENODEV; + } + + ida_simple_remove(&fw_mgmt->id_map, fw_mgmt->intf_fw_request_id); + fw_mgmt->intf_fw_request_id = 0; + fw_mgmt->intf_fw_status = request->status; + fw_mgmt->intf_fw_major = le16_to_cpu(request->major); + fw_mgmt->intf_fw_minor = le16_to_cpu(request->minor); + + if (fw_mgmt->intf_fw_status == GB_FW_LOAD_STATUS_FAILED) + dev_err(fw_mgmt->parent, + "failed to load interface firmware, status:%02x\n", + fw_mgmt->intf_fw_status); + else if (fw_mgmt->intf_fw_status == GB_FW_LOAD_STATUS_VALIDATION_FAILED) + dev_err(fw_mgmt->parent, + "failed to validate interface firmware, status:%02x\n", + fw_mgmt->intf_fw_status); + + complete(&fw_mgmt->completion); + + return 0; +} + +static int fw_mgmt_backend_fw_version_operation(struct fw_mgmt *fw_mgmt, + struct fw_mgmt_ioc_get_fw *fw_info) +{ + struct gb_connection *connection = fw_mgmt->connection; + struct gb_fw_mgmt_backend_fw_version_request request; + struct gb_fw_mgmt_backend_fw_version_response response; + int ret; + + strncpy(request.firmware_tag, fw_info->firmware_tag, + GB_FIRMWARE_TAG_MAX_LEN); + + /* + * The firmware-tag should be NULL terminated, otherwise throw error and + * fail. + */ + if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] != '\0') { + dev_err(fw_mgmt->parent, "backend-version: firmware-tag is not NULL terminated\n"); + return -EINVAL; + } + + ret = gb_operation_sync(connection, + GB_FW_MGMT_TYPE_BACKEND_FW_VERSION, &request, + sizeof(request), &response, sizeof(response)); + if (ret) { + dev_err(fw_mgmt->parent, "failed to get version of %s backend firmware (%d)\n", + fw_info->firmware_tag, ret); + return ret; + } + + fw_info->major = le16_to_cpu(response.major); + fw_info->minor = le16_to_cpu(response.minor); + + return 0; +} + +static int fw_mgmt_backend_fw_update_operation(struct fw_mgmt *fw_mgmt, + char *tag) +{ + struct gb_fw_mgmt_backend_fw_update_request request; + int ret; + + strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_LEN); + + /* + * The firmware-tag should be NULL terminated, otherwise throw error and + * fail. + */ + if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] != '\0') { + dev_err(fw_mgmt->parent, "backend-update: firmware-tag is not NULL terminated\n"); + return -EINVAL; + } + + /* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */ + ret = ida_simple_get(&fw_mgmt->id_map, 1, 256, GFP_KERNEL); + if (ret < 0) { + dev_err(fw_mgmt->parent, "failed to allocate request id (%d)\n", + ret); + return ret; + } + + fw_mgmt->backend_fw_request_id = ret; + request.request_id = ret; + + ret = gb_operation_sync(fw_mgmt->connection, + GB_FW_MGMT_TYPE_BACKEND_FW_UPDATE, &request, + sizeof(request), NULL, 0); + if (ret) { + ida_simple_remove(&fw_mgmt->id_map, + fw_mgmt->backend_fw_request_id); + fw_mgmt->backend_fw_request_id = 0; + dev_err(fw_mgmt->parent, + "backend %s firmware update request failed (%d)\n", tag, + ret); + return ret; + } + + return 0; +} + +static int fw_mgmt_backend_fw_updated_operation(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct fw_mgmt *fw_mgmt = gb_connection_get_data(connection); + struct gb_fw_mgmt_backend_fw_updated_request *request; + + /* No pending load and validate request ? */ + if (!fw_mgmt->backend_fw_request_id) { + dev_err(fw_mgmt->parent, "unexpected backend firmware updated request received\n"); + return -ENODEV; + } + + if (op->request->payload_size != sizeof(*request)) { + dev_err(fw_mgmt->parent, "illegal size of backend firmware updated request (%zu != %zu)\n", + op->request->payload_size, sizeof(*request)); + return -EINVAL; + } + + request = op->request->payload; + + /* Invalid request-id ? */ + if (request->request_id != fw_mgmt->backend_fw_request_id) { + dev_err(fw_mgmt->parent, "invalid request id for backend firmware updated request (%02u != %02u)\n", + fw_mgmt->backend_fw_request_id, request->request_id); + return -ENODEV; + } + + ida_simple_remove(&fw_mgmt->id_map, fw_mgmt->backend_fw_request_id); + fw_mgmt->backend_fw_request_id = 0; + fw_mgmt->backend_fw_status = request->status; + + if (fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_SUCCESS) + dev_err(fw_mgmt->parent, + "failed to backend load firmware, status:%02x\n", + fw_mgmt->backend_fw_status); + + complete(&fw_mgmt->completion); + + return 0; +} + +/* Char device fops */ + +static int fw_mgmt_open(struct inode *inode, struct file *file) +{ + struct fw_mgmt *fw_mgmt = container_of(inode->i_cdev, struct fw_mgmt, + cdev); + + file->private_data = fw_mgmt; + return 0; +} + +static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, + void __user *buf) +{ + struct fw_mgmt_ioc_get_fw fw_info; + struct fw_mgmt_ioc_intf_load_and_validate intf_load; + struct fw_mgmt_ioc_backend_fw_update backend_update; + unsigned int timeout; + int ret; + + switch (cmd) { + case FW_MGMT_IOC_GET_INTF_FW: + ret = fw_mgmt_interface_fw_version_operation(fw_mgmt, &fw_info); + if (ret) + return ret; + + if (copy_to_user(buf, &fw_info, sizeof(fw_info))) + return -EFAULT; + + return 0; + case FW_MGMT_IOC_GET_BACKEND_FW: + if (copy_from_user(&fw_info, buf, sizeof(fw_info))) + return -EFAULT; + + ret = fw_mgmt_backend_fw_version_operation(fw_mgmt, &fw_info); + if (ret) + return ret; + + if (copy_to_user(buf, &fw_info, sizeof(fw_info))) + return -EFAULT; + + return 0; + case FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE: + if (copy_from_user(&intf_load, buf, sizeof(intf_load))) + return -EFAULT; + + ret = fw_mgmt_load_and_validate_operation(fw_mgmt, + intf_load.load_method, intf_load.firmware_tag); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&fw_mgmt->completion, + fw_mgmt->timeout_jiffies)) { + dev_err(fw_mgmt->parent, "timed out waiting for firmware load and validation to finish\n"); + return -ETIMEDOUT; + } + + intf_load.status = fw_mgmt->intf_fw_status; + intf_load.major = cpu_to_le16(fw_mgmt->intf_fw_major); + intf_load.minor = cpu_to_le16(fw_mgmt->intf_fw_minor); + + if (copy_to_user(buf, &intf_load, sizeof(intf_load))) + return -EFAULT; + + return 0; + case FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE: + if (copy_from_user(&backend_update, buf, + sizeof(backend_update))) + return -EFAULT; + + ret = fw_mgmt_backend_fw_update_operation(fw_mgmt, + backend_update.firmware_tag); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&fw_mgmt->completion, + fw_mgmt->timeout_jiffies)) { + dev_err(fw_mgmt->parent, "timed out waiting for backend firmware update to finish\n"); + return -ETIMEDOUT; + } + + backend_update.status = fw_mgmt->backend_fw_status; + + if (copy_to_user(buf, &backend_update, sizeof(backend_update))) + return -EFAULT; + + return 0; + case FW_MGMT_IOC_SET_TIMEOUT_MS: + if (get_user(timeout, (unsigned int __user *)buf)) + return -EFAULT; + + if (!timeout) { + dev_err(fw_mgmt->parent, "timeout can't be zero\n"); + return -EINVAL; + } + + fw_mgmt->timeout_jiffies = msecs_to_jiffies(timeout); + + return 0; + default: + return -ENOTTY; + } +} + +static long fw_mgmt_ioctl_unlocked(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct fw_mgmt *fw_mgmt = file->private_data; + int ret; + + /* + * Serialize ioctls + * + * We don't want the user to do few operations in parallel. For example, + * updating Interface firmware in parallel for the same Interface. There + * is no need to do things in parallel for speed and we can avoid having + * complicated for now. + */ + mutex_lock(&fw_mgmt->mutex); + ret = fw_mgmt_ioctl(fw_mgmt, cmd, (void __user *)arg); + mutex_unlock(&fw_mgmt->mutex); + + return ret; +} + +static const struct file_operations fw_mgmt_fops = { + .owner = THIS_MODULE, + .open = fw_mgmt_open, + .unlocked_ioctl = fw_mgmt_ioctl_unlocked, +}; + +int gb_fw_mgmt_request_handler(struct gb_operation *op) +{ + u8 type = op->type; + + switch (type) { + case GB_FW_MGMT_TYPE_LOADED_FW: + return fw_mgmt_interface_fw_loaded_operation(op); + case GB_FW_MGMT_TYPE_BACKEND_FW_UPDATED: + return fw_mgmt_backend_fw_updated_operation(op); + default: + dev_err(&op->connection->bundle->dev, + "unsupported request: %u\n", type); + return -EINVAL; + } +} + +int gb_fw_mgmt_connection_init(struct gb_connection *connection) +{ + struct fw_mgmt *fw_mgmt; + int ret, minor; + + if (!connection) + return 0; + + fw_mgmt = kzalloc(sizeof(*fw_mgmt), GFP_KERNEL); + if (!fw_mgmt) + return -ENOMEM; + + fw_mgmt->parent = &connection->bundle->dev; + fw_mgmt->timeout_jiffies = msecs_to_jiffies(FW_MGMT_TIMEOUT_MS); + fw_mgmt->connection = connection; + + gb_connection_set_data(connection, fw_mgmt); + init_completion(&fw_mgmt->completion); + ida_init(&fw_mgmt->id_map); + mutex_init(&fw_mgmt->mutex); + + ret = gb_connection_enable(connection); + if (ret) + goto err_destroy_ida; + + minor = ida_simple_get(&fw_mgmt_minors_map, 0, NUM_MINORS, GFP_KERNEL); + if (minor < 0) { + ret = minor; + goto err_connection_disable; + } + + /* Add a char device to allow userspace to interact with fw-mgmt */ + fw_mgmt->dev_num = MKDEV(MAJOR(fw_mgmt_dev_num), minor); + cdev_init(&fw_mgmt->cdev, &fw_mgmt_fops); + + ret = cdev_add(&fw_mgmt->cdev, fw_mgmt->dev_num, 1); + if (ret) + goto err_remove_ida; + + /* Add a soft link to the previously added char-dev within the bundle */ + fw_mgmt->class_device = device_create(fw_mgmt_class, fw_mgmt->parent, + fw_mgmt->dev_num, NULL, + "fw-mgmt-%d", minor); + if (IS_ERR(fw_mgmt->class_device)) { + ret = PTR_ERR(fw_mgmt->class_device); + goto err_del_cdev; + } + + return 0; + +err_del_cdev: + cdev_del(&fw_mgmt->cdev); +err_remove_ida: + ida_simple_remove(&fw_mgmt_minors_map, minor); +err_connection_disable: + gb_connection_disable(connection); +err_destroy_ida: + ida_destroy(&fw_mgmt->id_map); + kfree(fw_mgmt); + + return ret; +} + +void gb_fw_mgmt_connection_exit(struct gb_connection *connection) +{ + struct fw_mgmt *fw_mgmt; + + if (!connection) + return; + + fw_mgmt = gb_connection_get_data(connection); + device_destroy(fw_mgmt_class, fw_mgmt->dev_num); + cdev_del(&fw_mgmt->cdev); + ida_simple_remove(&fw_mgmt_minors_map, MINOR(fw_mgmt->dev_num)); + gb_connection_disable(fw_mgmt->connection); + ida_destroy(&fw_mgmt->id_map); + + kfree(fw_mgmt); +} + +int fw_mgmt_init(void) +{ + int ret; + + fw_mgmt_class = class_create(THIS_MODULE, "gb_fw_mgmt"); + if (IS_ERR(fw_mgmt_class)) + return PTR_ERR(fw_mgmt_class); + + ret = alloc_chrdev_region(&fw_mgmt_dev_num, 0, NUM_MINORS, + "gb_fw_mgmt"); + if (ret) + goto err_remove_class; + + return 0; + +err_remove_class: + class_destroy(fw_mgmt_class); + return ret; +} + +void fw_mgmt_exit(void) +{ + unregister_chrdev_region(fw_mgmt_dev_num, NUM_MINORS); + class_destroy(fw_mgmt_class); + ida_destroy(&fw_mgmt_minors_map); +} diff --git a/drivers/staging/greybus/greybus_firmware.h b/drivers/staging/greybus/greybus_firmware.h new file mode 100644 index 000000000000..9c5ad75438ae --- /dev/null +++ b/drivers/staging/greybus/greybus_firmware.h @@ -0,0 +1,84 @@ +/* + * Greybus Firmware Management User Header + * + * This file is provided under the GPLv2 license. When using or + * redistributing this file, you may do so under that license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program 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 version 2 for more details. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 GOOGLE INC. OR + * LINARO LTD. 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 __GREYBUS_FIRMWARE_USER_H +#define __GREYBUS_FIRMWARE_USER_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +#define GB_FIRMWARE_U_TAG_MAX_LEN 10 + +#define GB_FW_U_LOAD_METHOD_UNIPRO 0x01 +#define GB_FW_U_LOAD_METHOD_INTERNAL 0x02 + +#define GB_FW_U_LOAD_STATUS_FAILED 0x00 +#define GB_FW_U_LOAD_STATUS_UNVALIDATED 0x01 +#define GB_FW_U_LOAD_STATUS_VALIDATED 0x02 +#define GB_FW_U_LOAD_STATUS_VALIDATION_FAILED 0x03 + +#define GB_FW_U_BACKEND_FW_STATUS_SUCCESS 0x01 +#define GB_FW_U_BACKEND_FW_STATUS_FAIL_FIND 0x02 +#define GB_FW_U_BACKEND_FW_STATUS_FAIL_FETCH 0x03 +#define GB_FW_U_BACKEND_FW_STATUS_FAIL_WRITE 0x04 +#define GB_FW_U_BACKEND_FW_STATUS_INT 0x05 + +/* IOCTL support */ +struct fw_mgmt_ioc_get_fw { + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u16 major; + __u16 minor; +} __attribute__ ((__packed__)); + +struct fw_mgmt_ioc_intf_load_and_validate { + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 load_method; + __u8 status; + __u16 major; + __u16 minor; +} __attribute__ ((__packed__)); + +struct fw_mgmt_ioc_backend_fw_update { + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 status; +} __attribute__ ((__packed__)); + +#define FW_MGMT_IOCTL_BASE 'F' +#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_fw) +#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_fw) +#define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate) +#define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update) +#define FW_MGMT_IOC_SET_TIMEOUT_MS _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int) + +#endif /* __GREYBUS_FIRMWARE_USER_H */ + diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 40bd6c0a4a62..e3ad5d70a9b6 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -250,6 +250,79 @@ struct gb_fw_download_release_firmware_request { /* firmware download release firmware response has no payload */ +/* Firmware Management Protocol */ + +/* Request Types */ +#define GB_FW_MGMT_TYPE_INTERFACE_FW_VERSION 0x01 +#define GB_FW_MGMT_TYPE_LOAD_AND_VALIDATE_FW 0x02 +#define GB_FW_MGMT_TYPE_LOADED_FW 0x03 +#define GB_FW_MGMT_TYPE_BACKEND_FW_VERSION 0x04 +#define GB_FW_MGMT_TYPE_BACKEND_FW_UPDATE 0x05 +#define GB_FW_MGMT_TYPE_BACKEND_FW_UPDATED 0x06 + +#define GB_FW_LOAD_METHOD_UNIPRO 0x01 +#define GB_FW_LOAD_METHOD_INTERNAL 0x02 + +#define GB_FW_LOAD_STATUS_FAILED 0x00 +#define GB_FW_LOAD_STATUS_UNVALIDATED 0x01 +#define GB_FW_LOAD_STATUS_VALIDATED 0x02 +#define GB_FW_LOAD_STATUS_VALIDATION_FAILED 0x03 + +#define GB_FW_BACKEND_FW_STATUS_SUCCESS 0x01 +#define GB_FW_BACKEND_FW_STATUS_FAIL_FIND 0x02 +#define GB_FW_BACKEND_FW_STATUS_FAIL_FETCH 0x03 +#define GB_FW_BACKEND_FW_STATUS_FAIL_WRITE 0x04 +#define GB_FW_BACKEND_FW_STATUS_INT 0x05 + +/* firmware management interface firmware version request has no payload */ +struct gb_fw_mgmt_interface_fw_version_response { + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __le16 major; + __le16 minor; +} __packed; + +/* firmware management load and validate firmware request/response */ +struct gb_fw_mgmt_load_and_validate_fw_request { + __u8 request_id; + __u8 load_method; + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; +} __packed; +/* firmware management load and validate firmware response has no payload*/ + +/* firmware management loaded firmware request */ +struct gb_fw_mgmt_loaded_fw_request { + __u8 request_id; + __u8 status; + __le16 major; + __le16 minor; +} __packed; +/* firmware management loaded firmware response has no payload */ + +/* firmware management backend firmware version request/response */ +struct gb_fw_mgmt_backend_fw_version_request { + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; +} __packed; + +struct gb_fw_mgmt_backend_fw_version_response { + __le16 major; + __le16 minor; +} __packed; + +/* firmware management backend firmware update request */ +struct gb_fw_mgmt_backend_fw_update_request { + __u8 request_id; + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; +} __packed; +/* firmware management backend firmware update response has no payload */ + +/* firmware management backend firmware updated request */ +struct gb_fw_mgmt_backend_fw_updated_request { + __u8 request_id; + __u8 status; +} __packed; +/* firmware management backend firmware updated response has no payload */ + + /* Bootrom Protocol */ /* Version of the Greybus bootrom protocol we support */ -- cgit v1.2.3-59-g8ed1b From 04f0e6ebd1a2ca3a1f1a356e9a07ef1797ef1b7c Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 14 May 2016 23:42:22 +0530 Subject: greybus: fw-management: Add ioctl to initiate mode-switch Once the interface firmware is loaded successfully to a module, userspace can ask it to mode switch to the newly loaded firmware. This patch provides a new ioctl to initiate mode switch. Userspace can initiate a mode switch if it has previously loaded the interface firmware successfully, otherwise the firmware core rejects it. Also, once the mode-switch is initiated, disallow any more interactions from the userspace. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Karthik Ravi Shankar <karthikrs@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-management.c | 20 ++++++++++++++++++++ drivers/staging/greybus/greybus_firmware.h | 1 + 2 files changed, 21 insertions(+) diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index 2db5a11c5a1d..7c2226ab5d5c 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -34,6 +34,8 @@ struct fw_mgmt { unsigned int timeout_jiffies; /* Interface Firmware specific fields */ + bool mode_switch_started; + bool intf_fw_loaded; u8 intf_fw_request_id; u8 intf_fw_status; u16 intf_fw_major; @@ -123,6 +125,7 @@ static int fw_mgmt_load_and_validate_operation(struct fw_mgmt *fw_mgmt, } fw_mgmt->intf_fw_request_id = ret; + fw_mgmt->intf_fw_loaded = false; request.request_id = ret; ret = gb_operation_sync(fw_mgmt->connection, @@ -183,6 +186,8 @@ static int fw_mgmt_interface_fw_loaded_operation(struct gb_operation *op) dev_err(fw_mgmt->parent, "failed to validate interface firmware, status:%02x\n", fw_mgmt->intf_fw_status); + else + fw_mgmt->intf_fw_loaded = true; complete(&fw_mgmt->completion); @@ -329,6 +334,10 @@ static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, unsigned int timeout; int ret; + /* Reject any operations after mode-switch has started */ + if (fw_mgmt->mode_switch_started) + return -EBUSY; + switch (cmd) { case FW_MGMT_IOC_GET_INTF_FW: ret = fw_mgmt_interface_fw_version_operation(fw_mgmt, &fw_info); @@ -407,6 +416,17 @@ static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, fw_mgmt->timeout_jiffies = msecs_to_jiffies(timeout); + return 0; + case FW_MGMT_IOC_MODE_SWITCH: + if (!fw_mgmt->intf_fw_loaded) { + dev_err(fw_mgmt->parent, + "Firmware not loaded for mode-switch\n"); + return -EPERM; + } + + fw_mgmt->mode_switch_started = true; + + /* FIXME: Initiate mode-switch from here */ return 0; default: return -ENOTTY; diff --git a/drivers/staging/greybus/greybus_firmware.h b/drivers/staging/greybus/greybus_firmware.h index 9c5ad75438ae..4e194f21d475 100644 --- a/drivers/staging/greybus/greybus_firmware.h +++ b/drivers/staging/greybus/greybus_firmware.h @@ -79,6 +79,7 @@ struct fw_mgmt_ioc_backend_fw_update { #define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate) #define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update) #define FW_MGMT_IOC_SET_TIMEOUT_MS _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int) +#define FW_MGMT_IOC_MODE_SWITCH _IO(FW_MGMT_IOCTL_BASE, 5) #endif /* __GREYBUS_FIRMWARE_USER_H */ -- cgit v1.2.3-59-g8ed1b From 96ba6740099b1f1a2732c86204d2931cda11d638 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 14 May 2016 23:42:23 +0530 Subject: greybus: fw-management: Free fw-mgmt only after all users are gone The fw-management driver rightly destroys the char device on connection-exit, but that doesn't guarantee that all of the users of the device are gone. Userspace may still be holding file-descriptor of the char device and can initiate new ioctl operations. And that *will* lead to kernel crash. To avoid this issue, manage struct users with kref, manage a list of 'struct fw-mgmt' and start using the structure only after getting its kref incremented. The important part is the routine get_fw_mgmt(), which increments the reference to the struct before returning it to the caller. The list of fw-mgmt structs in protected with a mutex to avoid any races around that. The kref is incremented once the char device is opened and dropped only when it is closed. Reported-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-management.c | 118 ++++++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index 7c2226ab5d5c..0c73f1e5cab3 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -24,6 +24,9 @@ struct fw_mgmt { struct device *parent; struct gb_connection *connection; + struct kref kref; + struct list_head node; + /* Common id-map for interface and backend firmware requests */ struct ida id_map; struct mutex mutex; @@ -32,6 +35,7 @@ struct fw_mgmt { struct device *class_device; dev_t dev_num; unsigned int timeout_jiffies; + bool disabled; /* connection getting disabled */ /* Interface Firmware specific fields */ bool mode_switch_started; @@ -55,6 +59,48 @@ struct fw_mgmt { static struct class *fw_mgmt_class; static dev_t fw_mgmt_dev_num; static DEFINE_IDA(fw_mgmt_minors_map); +static LIST_HEAD(fw_mgmt_list); +static DEFINE_MUTEX(list_mutex); + +static void fw_mgmt_kref_release(struct kref *kref) +{ + struct fw_mgmt *fw_mgmt = container_of(kref, struct fw_mgmt, kref); + + ida_destroy(&fw_mgmt->id_map); + kfree(fw_mgmt); +} + +/* + * All users of fw_mgmt take a reference (from within list_mutex lock), before + * they get a pointer to play with. And the structure will be freed only after + * the last user has put the reference to it. + */ +static void put_fw_mgmt(struct fw_mgmt *fw_mgmt) +{ + kref_put(&fw_mgmt->kref, fw_mgmt_kref_release); +} + +/* Caller must call put_fw_mgmt() after using struct fw_mgmt */ +static struct fw_mgmt *get_fw_mgmt(struct cdev *cdev) +{ + struct fw_mgmt *fw_mgmt; + + mutex_lock(&list_mutex); + + list_for_each_entry(fw_mgmt, &fw_mgmt_list, node) { + if (&fw_mgmt->cdev == cdev) { + kref_get(&fw_mgmt->kref); + goto unlock; + } + } + + fw_mgmt = NULL; + +unlock: + mutex_unlock(&list_mutex); + + return fw_mgmt; +} static int fw_mgmt_interface_fw_version_operation(struct fw_mgmt *fw_mgmt, struct fw_mgmt_ioc_get_fw *fw_info) @@ -318,10 +364,22 @@ static int fw_mgmt_backend_fw_updated_operation(struct gb_operation *op) static int fw_mgmt_open(struct inode *inode, struct file *file) { - struct fw_mgmt *fw_mgmt = container_of(inode->i_cdev, struct fw_mgmt, - cdev); + struct fw_mgmt *fw_mgmt = get_fw_mgmt(inode->i_cdev); - file->private_data = fw_mgmt; + /* fw_mgmt structure can't get freed until file descriptor is closed */ + if (fw_mgmt) { + file->private_data = fw_mgmt; + return 0; + } + + return -ENODEV; +} + +static int fw_mgmt_release(struct inode *inode, struct file *file) +{ + struct fw_mgmt *fw_mgmt = file->private_data; + + put_fw_mgmt(fw_mgmt); return 0; } @@ -437,18 +495,23 @@ static long fw_mgmt_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) { struct fw_mgmt *fw_mgmt = file->private_data; - int ret; + int ret = -ENODEV; /* - * Serialize ioctls + * Serialize ioctls. * * We don't want the user to do few operations in parallel. For example, * updating Interface firmware in parallel for the same Interface. There * is no need to do things in parallel for speed and we can avoid having - * complicated for now. + * complicated code for now. + * + * This is also used to protect ->disabled, which is used to check if + * the connection is getting disconnected, so that we don't start any + * new operations. */ mutex_lock(&fw_mgmt->mutex); - ret = fw_mgmt_ioctl(fw_mgmt, cmd, (void __user *)arg); + if (!fw_mgmt->disabled) + ret = fw_mgmt_ioctl(fw_mgmt, cmd, (void __user *)arg); mutex_unlock(&fw_mgmt->mutex); return ret; @@ -457,6 +520,7 @@ static long fw_mgmt_ioctl_unlocked(struct file *file, unsigned int cmd, static const struct file_operations fw_mgmt_fops = { .owner = THIS_MODULE, .open = fw_mgmt_open, + .release = fw_mgmt_release, .unlocked_ioctl = fw_mgmt_ioctl_unlocked, }; @@ -496,10 +560,15 @@ int gb_fw_mgmt_connection_init(struct gb_connection *connection) init_completion(&fw_mgmt->completion); ida_init(&fw_mgmt->id_map); mutex_init(&fw_mgmt->mutex); + kref_init(&fw_mgmt->kref); + + mutex_lock(&list_mutex); + list_add(&fw_mgmt->node, &fw_mgmt_list); + mutex_unlock(&list_mutex); ret = gb_connection_enable(connection); if (ret) - goto err_destroy_ida; + goto err_list_del; minor = ida_simple_get(&fw_mgmt_minors_map, 0, NUM_MINORS, GFP_KERNEL); if (minor < 0) { @@ -532,9 +601,12 @@ err_remove_ida: ida_simple_remove(&fw_mgmt_minors_map, minor); err_connection_disable: gb_connection_disable(connection); -err_destroy_ida: - ida_destroy(&fw_mgmt->id_map); - kfree(fw_mgmt); +err_list_del: + mutex_lock(&list_mutex); + list_del(&fw_mgmt->node); + mutex_unlock(&list_mutex); + + put_fw_mgmt(fw_mgmt); return ret; } @@ -547,13 +619,33 @@ void gb_fw_mgmt_connection_exit(struct gb_connection *connection) return; fw_mgmt = gb_connection_get_data(connection); + device_destroy(fw_mgmt_class, fw_mgmt->dev_num); cdev_del(&fw_mgmt->cdev); ida_simple_remove(&fw_mgmt_minors_map, MINOR(fw_mgmt->dev_num)); + + /* + * Disallow any new ioctl operations on the char device and wait for + * existing ones to finish. + */ + mutex_lock(&fw_mgmt->mutex); + fw_mgmt->disabled = true; + mutex_unlock(&fw_mgmt->mutex); + + /* All pending greybus operations should have finished by now */ gb_connection_disable(fw_mgmt->connection); - ida_destroy(&fw_mgmt->id_map); - kfree(fw_mgmt); + /* Disallow new users to get access to the fw_mgmt structure */ + mutex_lock(&list_mutex); + list_del(&fw_mgmt->node); + mutex_unlock(&list_mutex); + + /* + * All current users of fw_mgmt would have taken a reference to it by + * now, we can drop our reference and wait the last user will get + * fw_mgmt freed. + */ + put_fw_mgmt(fw_mgmt); } int fw_mgmt_init(void) -- cgit v1.2.3-59-g8ed1b From e2386c9327ab30288f995fc23fb97a3f21b08787 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 14 May 2016 23:42:24 +0530 Subject: greybus: firmware: Add SPI protocol support This patch adds SPI Protocol support to firmware core, which allows the AP to access an SPI flash memory present with an Interface. Tested by using the API from fw-management driver and compiling it. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-core.c | 63 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c index 0a456c547c52..90d32227a490 100644 --- a/drivers/staging/greybus/fw-core.c +++ b/drivers/staging/greybus/fw-core.c @@ -11,10 +11,12 @@ #include <linux/firmware.h> #include "firmware.h" #include "greybus.h" +#include "spilib.h" struct gb_fw_core { struct gb_connection *download_connection; struct gb_connection *mgmt_connection; + struct gb_connection *spi_connection; }; struct gb_connection *to_fw_mgmt_connection(struct device *dev) @@ -24,6 +26,35 @@ struct gb_connection *to_fw_mgmt_connection(struct device *dev) return fw_core->mgmt_connection; } +static int gb_fw_spi_connection_init(struct gb_connection *connection) +{ + int ret; + + if (!connection) + return 0; + + ret = gb_connection_enable(connection); + if (ret) + return ret; + + ret = gb_spilib_master_init(connection, &connection->bundle->dev); + if (ret) { + gb_connection_disable(connection); + return ret; + } + + return 0; +} + +static void gb_fw_spi_connection_exit(struct gb_connection *connection) +{ + if (!connection) + return; + + gb_spilib_master_exit(connection); + gb_connection_disable(connection); +} + static int gb_fw_core_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { @@ -84,6 +115,25 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, fw_core->download_connection = connection; } + break; + case GREYBUS_PROTOCOL_SPI: + /* Disallow multiple SPI CPorts */ + if (fw_core->spi_connection) { + dev_err(&bundle->dev, + "multiple SPI CPorts found\n"); + ret = -EINVAL; + goto err_destroy_connections; + } + + connection = gb_connection_create(bundle, cport_id, + NULL); + if (IS_ERR(connection)) { + dev_err(&bundle->dev, "failed to create SPI connection (%ld)\n", + PTR_ERR(connection)); + } else { + fw_core->spi_connection = connection; + } + break; default: dev_err(&bundle->dev, "invalid protocol id (0x%02x)\n", @@ -109,6 +159,15 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, fw_core->download_connection = NULL; } + ret = gb_fw_spi_connection_init(fw_core->spi_connection); + if (ret) { + /* We may still be able to work with the Interface */ + dev_err(&bundle->dev, "failed to initialize SPI connection, disable it (%d)\n", + ret); + gb_connection_destroy(fw_core->spi_connection); + fw_core->spi_connection = NULL; + } + ret = gb_fw_mgmt_connection_init(fw_core->mgmt_connection); if (ret) { /* We may still be able to work with the Interface */ @@ -122,9 +181,11 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, return 0; err_exit_connections: + gb_fw_spi_connection_exit(fw_core->spi_connection); gb_fw_download_connection_exit(fw_core->download_connection); err_destroy_connections: gb_connection_destroy(fw_core->mgmt_connection); + gb_connection_destroy(fw_core->spi_connection); gb_connection_destroy(fw_core->download_connection); err_free_fw_core: kfree(fw_core); @@ -137,9 +198,11 @@ static void gb_fw_core_disconnect(struct gb_bundle *bundle) struct gb_fw_core *fw_core = greybus_get_drvdata(bundle); gb_fw_mgmt_connection_exit(fw_core->mgmt_connection); + gb_fw_spi_connection_exit(fw_core->spi_connection); gb_fw_download_connection_exit(fw_core->download_connection); gb_connection_destroy(fw_core->mgmt_connection); + gb_connection_destroy(fw_core->spi_connection); gb_connection_destroy(fw_core->download_connection); kfree(fw_core); -- cgit v1.2.3-59-g8ed1b From 7bf7fa12fcb24fccb99d7957e44b8be6e0b82986 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 14 May 2016 23:42:25 +0530 Subject: greybus: Documentation: Document firmware-management interfaces This patch adds a new 'firmware' folder in Documentation, which contains two files: - firmware-management: This describes the userspace interface for interacting with firmware-management bundle. - firmware.c: Sample application to test firmware load for Interface Firmware and firmware updates to Backend Interface Firmware. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Jun Li <li_jun@projectara.com> Tested-by: Karthik Ravi Shankar <karthikrs@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../Documentation/firmware/firmware-management | 190 +++++++++++++++++++++ .../greybus/Documentation/firmware/firmware.c | 146 ++++++++++++++++ 2 files changed, 336 insertions(+) create mode 100644 drivers/staging/greybus/Documentation/firmware/firmware-management create mode 100644 drivers/staging/greybus/Documentation/firmware/firmware.c diff --git a/drivers/staging/greybus/Documentation/firmware/firmware-management b/drivers/staging/greybus/Documentation/firmware/firmware-management new file mode 100644 index 000000000000..f70d3cd26be1 --- /dev/null +++ b/drivers/staging/greybus/Documentation/firmware/firmware-management @@ -0,0 +1,190 @@ + +Firmware Management +------------------- + Copyright 2016 Google Inc. + Copyright 2016 Linaro Ltd. + +Interface-Manifest +------------------ + +All firmware packages on the Modules or Interfaces are managed by a special +Firmware Management Protocol. To support Firmware Management by the AP, the +Interface Manifest shall at least contain the Firmware Management Bundle and a +Firmware Management Protocol CPort within it. + +The bundle may contain additional CPorts based on the extra functionality +required to manage firmware packages. + +For example, this is how the Firmware Management part of the Interface Manifest +may look like: + + ; Firmware Management Bundle (Bundle 1): + [bundle-descriptor 1] + class = 0x16 + + ; (Mandatory) Firmware Management Protocol on CPort 1 + [cport-descriptor 2] + bundle = 1 + protocol = 0x18 + + ; (Optional) Firmware Download Protocol on CPort 2 + [cport-descriptor 1] + bundle = 1 + protocol = 0x17 + + ; (Optional) SPI protocol on CPort 3 + [cport-descriptor 3] + bundle = 1 + protocol = 0x0b + + ; (Optional) Component Authentication Protocol (CAP) on CPort 4 + [cport-descriptor 4] + bundle = 1 + protocol = 0xXX //TBD + + + +Sysfs Interfaces - Firmware Management +-------------------------------------- + +The Firmware Management Protocol interacts with Userspace using the character +device interface. The character device will be present in /dev/ directory +and will be named fw-mgmt-<N>. The number <N> is assigned at runtime. + +Identifying the Character Device +================================ + +There can be multiple devices present in /dev/ directory with name fw-mgmt-N and +user first needs to identify the character device used for firmware-management +for a particular interface. + +The Firmware Management core creates a device of class 'gb_fw_mgmt', which shall +be used by the user to identify the right character device for it. The class +device is created within the Bundle directory for a particular Interface. + +For example this is how the class-device can be present: + +/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_fw_mgmt/fw-mgmt-0 + +The last name in this path: fw-mgmt-0 is precisely the name of the char device +and so the device in this case will be: + +/dev/fw-mgmt-0. + +Operations on the Char device +============================= + +The Character device (fw-mgmt-0 in example) can be opened by the userspace +application and it can perform various 'ioctl' operations on the device. The +device doesn't support any read/write operations. + +Following are the IOCTLs and their data structures available to the user: + +/* IOCTL support */ +#define GB_FW_LOAD_METHOD_UNIPRO 0x01 +#define GB_FW_LOAD_METHOD_INTERNAL 0x02 + +#define GB_FW_LOAD_STATUS_FAILED 0x00 +#define GB_FW_LOAD_STATUS_UNVALIDATED 0x01 +#define GB_FW_LOAD_STATUS_VALIDATED 0x02 +#define GB_FW_LOAD_STATUS_VALIDATION_FAILED 0x03 + +#define GB_FW_BACKEND_FW_STATUS_SUCCESS 0x01 +#define GB_FW_BACKEND_FW_STATUS_FAIL_FIND 0x02 +#define GB_FW_BACKEND_FW_STATUS_FAIL_FETCH 0x03 +#define GB_FW_BACKEND_FW_STATUS_FAIL_WRITE 0x04 +#define GB_FW_BACKEND_FW_STATUS_INT 0x05 + +struct fw_mgmt_ioc_get_fw { + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u16 major; + __u16 minor; +} __packed; + +struct fw_mgmt_ioc_intf_load_and_validate { + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u8 load_method; + __u8 status; + __u16 major; + __u16 minor; +} __packed; + +struct fw_mgmt_ioc_backend_fw_update { + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u8 status; +} __packed; + +#define FW_MGMT_IOCTL_BASE 'S' +#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_fw) +#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_fw) +#define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate) +#define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update) +#define FW_MGMT_IOC_SET_TIMEOUT_MS _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int) +#define FW_MGMT_IOC_MODE_SWITCH _IO(FW_MGMT_IOCTL_BASE, 5) + +1. FW_MGMT_IOC_GET_INTF_FW: + + This ioctl shall be used the user to get the version and firmware-tag of the + currently running Interface Firmware. All the fields of the 'struct + fw_mgmt_ioc_get_fw' are filled by the kernel. + +2. FW_MGMT_IOC_GET_BACKEND_FW: + + This ioctl shall be used the user to get the version of a currently running + Backend Interface Firmware identified by a firmware-tag. The user is required + to fill the 'firmware_tag' field of the 'struct fw_mgmt_ioc_get_fw' in this + case. The 'major' and 'minor' fields are set by the kernel in response. + +3. FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE: + + This ioctl shall be used the user to load an Interface Firmware package on an + Interface. The user needs to fill the 'firmware_tag' and 'load_method' fields + of the 'struct fw_mgmt_ioc_intf_load_and_validate'. The 'status', 'major' and + 'minor' fields are set by the kernel in response. + +4. FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE: + + This ioctl shall be used the user to request an Interface to update a Backend + Interface Firmware. The user is required to fill the 'firmware_tag' field of + the 'struct fw_mgmt_ioc_get_fw' in this case. The 'status' field is set by + the kernel in response. + +5. FW_MGMT_IOC_SET_TIMEOUT_MS: + + This ioctl shall be used the user to increase the timeout interval within + which the firmware must get loaded by the Module. The default timeout is 1 + second. The user needs to pass the timeout in milliseconds. + +6. FW_MGMT_IOC_MODE_SWITCH: + + This ioctl shall be used the user to mode-switch the module to the previously + loaded interface firmware. If the interface firmware isn't loaded previously, + or if another unsuccessful FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE operation is + started after loading interface firmware, then the firmware core wouldn't + allow mode-switch. + + +Sysfs Interfaces - Firmware Download +------------------------------------ + +The Firmware Download Protocol uses the existing Linux Kernel's Firmware class +and the interface provided to userspace are described in: +Documentation/firmware_class/. + + +Sysfs Interfaces - SPI Flash +---------------------------- + +The SPI flash is exposed in userspace as a MTD device and is created +within the Bundle directory. For example, this is how the path may look like: + +$ ls /sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/spi_master/spi32766/spi32766.0/mtd +mtd0 mtd0ro + + +Sample Application +------------------ + +The current directly also provides a firmware.c test application, which can be +referenced while developing userspace application to talk to firmware-management +protocol. diff --git a/drivers/staging/greybus/Documentation/firmware/firmware.c b/drivers/staging/greybus/Documentation/firmware/firmware.c new file mode 100644 index 000000000000..e36786013ead --- /dev/null +++ b/drivers/staging/greybus/Documentation/firmware/firmware.c @@ -0,0 +1,146 @@ +/* Sample code to test firmware-management protocol */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "../../greybus_firmware.h" + +static const char *firmware_tag = "03"; /* S3 firmware */ + +static struct fw_mgmt_ioc_get_fw fw_info; +static struct fw_mgmt_ioc_intf_load_and_validate intf_load; +static struct fw_mgmt_ioc_backend_fw_update backend_update; + +int main(int argc, char *argv[]) +{ + unsigned int timeout = 10000; + char *fwdev; + int fd, ret; + + /* Make sure arguments are correct */ + if (argc != 2) { + printf("\nUsage: ./firmware <Path of the fw-mgmt-X dev>\n"); + return 0; + } + + fwdev = argv[1]; + + printf("Opening %s firmware management device\n", fwdev); + + fd = open(fwdev, O_RDWR); + if (fd < 0) { + printf("Failed to open: %s\n", fwdev); + ret = -1; + goto close_fd; + } + + /* Set Timeout */ + printf("Setting timeout to %u ms\n", timeout); + + ret = ioctl(fd, FW_MGMT_IOC_SET_TIMEOUT_MS, &timeout); + if (ret < 0) { + printf("Failed to set timeout: %s (%d)\n", fwdev, ret); + ret = -1; + goto close_fd; + } + + /* Get Interface Firmware Version */ + printf("Get Interface Firmware Version\n"); + + ret = ioctl(fd, FW_MGMT_IOC_GET_INTF_FW, &fw_info); + if (ret < 0) { + printf("Failed to get interface firmware version: %s (%d)\n", + fwdev, ret); + ret = -1; + goto close_fd; + } + + printf("Interface Firmware tag (%s), major (%d), minor (%d)\n", + fw_info.firmware_tag, fw_info.major, fw_info.minor); + + /* Try Interface Firmware load over Unipro */ + printf("Loading Interface Firmware\n"); + + intf_load.load_method = GB_FW_U_LOAD_METHOD_UNIPRO; + intf_load.status = 0; + intf_load.major = 0; + intf_load.minor = 0; + strncpy((char *)&intf_load.firmware_tag, firmware_tag, + GB_FIRMWARE_U_TAG_MAX_LEN); + + ret = ioctl(fd, FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE, &intf_load); + if (ret < 0) { + printf("Failed to load interface firmware: %s (%d)\n", fwdev, + ret); + ret = -1; + goto close_fd; + } + + if (intf_load.status != GB_FW_U_LOAD_STATUS_VALIDATED && + intf_load.status != GB_FW_U_LOAD_STATUS_UNVALIDATED) { + printf("Load status says loading failed: %d\n", + intf_load.status); + ret = -1; + goto close_fd; + } + + printf("Interface Firmware (%s) Load done: major: %d, minor: %d, status: %d\n", + firmware_tag, intf_load.major, intf_load.minor, + intf_load.status); + + /* Get Backend Firmware Version */ + printf("Getting Backend Firmware Version\n"); + + strncpy((char *)&fw_info.firmware_tag, firmware_tag, + GB_FIRMWARE_U_TAG_MAX_LEN); + fw_info.major = 0; + fw_info.minor = 0; + + ret = ioctl(fd, FW_MGMT_IOC_GET_BACKEND_FW, &fw_info); + if (ret < 0) { + printf("Failed to get backend firmware version: %s (%d)\n", + fwdev, ret); + goto mode_switch; + } + + printf("Backend Firmware tag (%s), major (%d), minor (%d)\n", + fw_info.firmware_tag, fw_info.major, fw_info.minor); + + /* Try Backend Firmware Update over Unipro */ + printf("Updating Backend Firmware\n"); + + backend_update.status = 0; + strncpy((char *)&backend_update.firmware_tag, firmware_tag, + GB_FIRMWARE_U_TAG_MAX_LEN); + + ret = ioctl(fd, FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE, &backend_update); + if (ret < 0) { + printf("Failed to load backend firmware: %s (%d)\n", fwdev, ret); + goto mode_switch; + } + + printf("Backend Firmware (%s) Load done: status: %d\n", + firmware_tag, backend_update.status); + + if (backend_update.status != GB_FW_U_BACKEND_FW_STATUS_SUCCESS) { + printf("Load status says loading failed: %d\n", + backend_update.status); + } + +mode_switch: + /* Initiate Mode-switch to the newly loaded firmware */ + printf("Initiate Mode switch\n"); + + ret = ioctl(fd, FW_MGMT_IOC_MODE_SWITCH); + if (ret < 0) + printf("Failed to initiate mode-switch (%d)\n", ret); + +close_fd: + close(fd); + + return ret; +} -- cgit v1.2.3-59-g8ed1b From c8a657ba3f84643d7ef4b13ff6828e141172419a Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Sun, 15 May 2016 19:37:48 +0100 Subject: greybus: hd: Add TimeSync APBridge commands This patch adds a number of USB Vendor commands to es2.c to enable TimeSync in the bridge. Adds: - es2.c::timesync_enable(u8 count, u64 frame_time, u32 strobe_delay, u32 refclk); Commands APBx to enable timers and clocks to track a pulse-train of incoming TIME_SYNC strobes with strobe_delay microseconds between each. Provides the reference clock the AP is using to track FrameTime. It is the responsibility of APBx to adequately track the FrameTime based on the indicated AP refclk. Once this command has succeeded APBx may not transition to a low-power state were FrameTime counters stop. This function is initiated from the timesync worker thread logic when re-synchronizing frame-time throughout the system. TimeSync is at this time enabled for all APBx active in the system i.e. currently APB2 will not receive TimeSync commands until it becomes a registered host-device in Greybus. - es2.c::timesync_disable(void) Commands APBx to discontinue tracking of FrameTime. After this operation completes APBx may transition to a low-power state where timer-clocks stop operating. - es2.c::timesync_authoritative(u64 *frame_time) Provides an authoritative time for each TIME_SYNC strobe to APBx. APBx must align its local FrameTime to the authoritative clock. - es2.c::timesync_get_last_event(u64 *frame_time) Returns the FrameTime at the last SVC_TIMESYNC_PING to the AP Module. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 153 ++++++++++++++++++++++++++-- drivers/staging/greybus/greybus_protocols.h | 26 +++-- drivers/staging/greybus/hd.h | 7 ++ 3 files changed, 165 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 998b41ebc53e..d6abfdad803b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -121,6 +121,29 @@ struct cport_to_ep { __u8 endpoint_out; }; +/** + * timesync_enable_request - Enable timesync in an APBridge + * @count: number of TimeSync Pulses to expect + * @frame_time: the initial FrameTime at the first TimeSync Pulse + * @strobe_delay: the expected delay in microseconds between each TimeSync Pulse + * @refclk: The AP mandated reference clock to run FrameTime at + */ +struct timesync_enable_request { + __u8 count; + __le64 frame_time; + __le32 strobe_delay; + __le32 refclk; +} __packed; + +/** + * timesync_authoritative_request - Transmit authoritative FrameTime to APBridge + * @frame_time: An array of authoritative FrameTimes provided by the SVC + * and relayed to the APBridge by the AP + */ +struct timesync_authoritative_request { + __le64 frame_time[GB_TIMESYNC_MAX_STROBES]; +} __packed; + static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) { return (struct es2_ap_dev *)&hd->hd_priv; @@ -674,18 +697,126 @@ static int cport_features_disable(struct gb_host_device *hd, u16 cport_id) return retval; } +static int timesync_enable(struct gb_host_device *hd, u8 count, + u64 frame_time, u32 strobe_delay, u32 refclk) +{ + int retval; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + struct gb_control_timesync_enable_request *request; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->count = count; + request->frame_time = cpu_to_le64(frame_time); + request->strobe_delay = cpu_to_le32(strobe_delay); + request->refclk = cpu_to_le32(refclk); + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_TIMESYNC_ENABLE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, request, + sizeof(*request), ES2_TIMEOUT); + if (retval < 0) + dev_err(&udev->dev, "Cannot enable timesync %d\n", retval); + + kfree(request); + return retval; +} + +static int timesync_disable(struct gb_host_device *hd) +{ + int retval; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_TIMESYNC_DISABLE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, NULL, + 0, ES2_TIMEOUT); + if (retval < 0) + dev_err(&udev->dev, "Cannot disable timesync %d\n", retval); + + return retval; +} + +static int timesync_authoritative(struct gb_host_device *hd, u64 *frame_time) +{ + int retval, i; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + struct timesync_authoritative_request *request; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) + request->frame_time[i] = cpu_to_le64(frame_time[i]); + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + REQUEST_TIMESYNC_AUTHORITATIVE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, request, + sizeof(*request), ES2_TIMEOUT); + if (retval < 0) + dev_err(&udev->dev, "Cannot timesync authoritative out %d\n", retval); + + kfree(request); + return retval; +} + +static int timesync_get_last_event(struct gb_host_device *hd, u64 *frame_time) +{ + int retval; + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + u64 *response_frame_time; + + response_frame_time = kzalloc(sizeof(*response_frame_time), GFP_KERNEL); + if (!response_frame_time) + return -ENOMEM; + + retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + REQUEST_TIMESYNC_GET_LAST_EVENT, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, 0, 0, response_frame_time, + sizeof(*response_frame_time), ES2_TIMEOUT); + + if (retval != sizeof(*response_frame_time)) { + dev_err(&udev->dev, "Cannot get last TimeSync event: %d\n", + retval); + + if (retval >= 0) + retval = -EIO; + + goto out; + } + *frame_time = le64_to_cpu(*response_frame_time); + retval = 0; +out: + kfree(response_frame_time); + return retval; +} + static struct gb_hd_driver es2_driver = { - .hd_priv_size = sizeof(struct es2_ap_dev), - .message_send = message_send, - .message_cancel = message_cancel, - .cport_allocate = es2_cport_allocate, - .cport_release = es2_cport_release, - .cport_enable = cport_enable, - .latency_tag_enable = latency_tag_enable, - .latency_tag_disable = latency_tag_disable, - .output = output, - .cport_features_enable = cport_features_enable, - .cport_features_disable = cport_features_disable, + .hd_priv_size = sizeof(struct es2_ap_dev), + .message_send = message_send, + .message_cancel = message_cancel, + .cport_allocate = es2_cport_allocate, + .cport_release = es2_cport_release, + .cport_enable = cport_enable, + .latency_tag_enable = latency_tag_enable, + .latency_tag_disable = latency_tag_disable, + .output = output, + .cport_features_enable = cport_features_enable, + .cport_features_disable = cport_features_disable, + .timesync_enable = timesync_enable, + .timesync_disable = timesync_disable, + .timesync_authoritative = timesync_authoritative, + .timesync_get_last_event = timesync_get_last_event, }; /* Common function to report consistent warnings based on URB status */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e3ad5d70a9b6..d379fe36c7a5 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -188,30 +188,36 @@ struct gb_control_timesync_get_last_event_response { /* APBridge protocol */ /* request APB1 log */ -#define GB_APB_REQUEST_LOG 0x02 +#define GB_APB_REQUEST_LOG 0x02 /* request to map a cport to bulk in and bulk out endpoints */ -#define GB_APB_REQUEST_EP_MAPPING 0x03 +#define GB_APB_REQUEST_EP_MAPPING 0x03 /* request to get the number of cports available */ -#define GB_APB_REQUEST_CPORT_COUNT 0x04 +#define GB_APB_REQUEST_CPORT_COUNT 0x04 /* request to reset a cport state */ -#define GB_APB_REQUEST_RESET_CPORT 0x05 +#define GB_APB_REQUEST_RESET_CPORT 0x05 /* request to time the latency of messages on a given cport */ -#define GB_APB_REQUEST_LATENCY_TAG_EN 0x06 -#define GB_APB_REQUEST_LATENCY_TAG_DIS 0x07 +#define GB_APB_REQUEST_LATENCY_TAG_EN 0x06 +#define GB_APB_REQUEST_LATENCY_TAG_DIS 0x07 /* request to control the CSI transmitter */ -#define GB_APB_REQUEST_CSI_TX_CONTROL 0x08 +#define GB_APB_REQUEST_CSI_TX_CONTROL 0x08 /* request to control the CSI transmitter */ -#define GB_APB_REQUEST_AUDIO_CONTROL 0x09 +#define GB_APB_REQUEST_AUDIO_CONTROL 0x09 /* vendor requests to enable/disable CPort features */ -#define GB_APB_REQUEST_CPORT_FEAT_EN 0x0b -#define GB_APB_REQUEST_CPORT_FEAT_DIS 0x0c +#define GB_APB_REQUEST_CPORT_FEAT_EN 0x0b +#define GB_APB_REQUEST_CPORT_FEAT_DIS 0x0c + +/* TimeSync commands */ +#define REQUEST_TIMESYNC_ENABLE 0x0d +#define REQUEST_TIMESYNC_DISABLE 0x0e +#define REQUEST_TIMESYNC_AUTHORITATIVE 0x0f +#define REQUEST_TIMESYNC_GET_LAST_EVENT 0x10 /* Firmware Download Protocol */ diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 80573aed56ef..8510816c1796 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -30,6 +30,13 @@ struct gb_hd_driver { bool async); int (*cport_features_enable)(struct gb_host_device *hd, u16 cport_id); int (*cport_features_disable)(struct gb_host_device *hd, u16 cport_id); + int (*timesync_enable)(struct gb_host_device *hd, u8 count, + u64 frame_time, u32 strobe_delay, u32 refclk); + int (*timesync_disable)(struct gb_host_device *hd); + int (*timesync_authoritative)(struct gb_host_device *hd, + u64 *frame_time); + int (*timesync_get_last_event)(struct gb_host_device *hd, + u64 *frame_time); }; struct gb_host_device { -- cgit v1.2.3-59-g8ed1b From 8c81d4608dc54225005129e32cf5d2035176df5e Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Sun, 15 May 2016 19:37:49 +0100 Subject: greybus: interface: Extract and store Interface feature byte The Interface description in the Greybus specification contains a 'features' field which is currently not implemented on the AP side. The Interface features field provides information on optional attributes of an Interface as a bitmask. Currently only GREYBUS_INTERFACE_FEATURE_TIMESYNC is implemented in the specification but, the expectation is that other feature flags will be added over time. This patch adds support to extract the feature byte communicated in the features field of the Interface Descriptor header and extends struct interface to contain a features field through which any user with a pointer to struct interface may interrogate the features of an Interface. This is a necessary pre-cursor for TimeSync to ensure only Interfaces which declare GREYBUS_INTERFACE_FEATURE_TIMESYNC will be included when we go through the process of FrameTime synchronization. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_manifest.h | 7 ++++++- drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/manifest.c | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 4f7019011642..28bbadd057d1 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -81,6 +81,10 @@ enum greybus_class_type { GREYBUS_CLASS_VENDOR = 0xff, }; +enum { + GREYBUS_INTERFACE_FEATURE_TIMESYNC = BIT(0), +}; + /* * The string in a string descriptor is not NUL-terminated. The * size of the descriptor will be rounded up to a multiple of 4 @@ -99,7 +103,8 @@ struct greybus_descriptor_string { struct greybus_descriptor_interface { __u8 vendor_stringid; __u8 product_stringid; - __u8 pad[2]; + __u8 features; + __u8 pad; } __packed; /* diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 1a25234351e2..51772ccdd467 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -23,6 +23,7 @@ struct gb_interface { struct list_head manifest_descs; u8 interface_id; /* Physical location within the Endo */ u8 device_id; + u8 features; /* Feature flags set in the manifest */ u32 ddbl1_manufacturer_id; u32 ddbl1_product_id; diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 886c5fb91d9f..529a984db992 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -398,6 +398,9 @@ static bool gb_manifest_parse_interface(struct gb_interface *intf, goto out_free_vendor_string; control->product_string = str; + /* Assign feature flags communicated via manifest */ + intf->features = desc_intf->features; + /* Release the interface descriptor, now that we're done with it */ release_manifest_descriptor(interface_desc); -- cgit v1.2.3-59-g8ed1b From 5319a889eafc5c8c13ef51e38fed224c4c3b33f2 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Sun, 15 May 2016 23:34:51 +0100 Subject: greybus: es2.c: Declare local __le64 not u64 The value passed to le64_to_cpu wants to be an __le64 not a u64. Note to self - remember to run "make check" Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reported-by: Greg Kroah-Hartman <gregkh@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index d6abfdad803b..947ebae6ae5a 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -773,7 +773,7 @@ static int timesync_get_last_event(struct gb_host_device *hd, u64 *frame_time) int retval; struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; - u64 *response_frame_time; + __le64 *response_frame_time; response_frame_time = kzalloc(sizeof(*response_frame_time), GFP_KERNEL); if (!response_frame_time) -- cgit v1.2.3-59-g8ed1b From e27f1da93f00f33db2559be69ea5efc9c2f28de4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 16 May 2016 07:49:45 +0530 Subject: greybus: fw-management: Replace double-tabs with space for structures Reformat structures to use a single space instead of multiple tabs. Reported-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_firmware.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/greybus_firmware.h b/drivers/staging/greybus/greybus_firmware.h index 4e194f21d475..02322600968a 100644 --- a/drivers/staging/greybus/greybus_firmware.h +++ b/drivers/staging/greybus/greybus_firmware.h @@ -55,22 +55,22 @@ /* IOCTL support */ struct fw_mgmt_ioc_get_fw { - __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; - __u16 major; - __u16 minor; + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u16 major; + __u16 minor; } __attribute__ ((__packed__)); struct fw_mgmt_ioc_intf_load_and_validate { - __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; - __u8 load_method; - __u8 status; - __u16 major; - __u16 minor; + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 load_method; + __u8 status; + __u16 major; + __u16 minor; } __attribute__ ((__packed__)); struct fw_mgmt_ioc_backend_fw_update { - __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; - __u8 status; + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 status; } __attribute__ ((__packed__)); #define FW_MGMT_IOCTL_BASE 'F' -- cgit v1.2.3-59-g8ed1b From 79b140fe2b6f2db8c4a4a45bde6065fc34b24e77 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 16 May 2016 07:49:43 +0530 Subject: greybus: fw-management: Fix 'make check' warnings Fix below warnings observed with 'make check'. ~/ara/greybus/fw-management.c:438:33: warning: incorrect type in assignment (different base types) ~/ara/greybus/fw-management.c:438:33: expected unsigned short [unsigned] [addressable] [assigned] [usertype] major ~/ara/greybus/fw-management.c:438:33: got restricted __le16 [usertype] <noident> ~/ara/greybus/fw-management.c:439:33: warning: incorrect type in assignment (different base types) ~/ara/greybus/fw-management.c:439:33: expected unsigned short [unsigned] [addressable] [assigned] [usertype] minor ~/ara/greybus/fw-management.c:439:33: got restricted __le16 [usertype] <noident> Reported-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-management.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index 0c73f1e5cab3..96758f417d7b 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -434,8 +434,8 @@ static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, } intf_load.status = fw_mgmt->intf_fw_status; - intf_load.major = cpu_to_le16(fw_mgmt->intf_fw_major); - intf_load.minor = cpu_to_le16(fw_mgmt->intf_fw_minor); + intf_load.major = fw_mgmt->intf_fw_major; + intf_load.minor = fw_mgmt->intf_fw_minor; if (copy_to_user(buf, &intf_load, sizeof(intf_load))) return -EFAULT; -- cgit v1.2.3-59-g8ed1b From 3b077247b6499aa5bf2183968be8cb6ad675171c Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 16 May 2016 20:54:53 +0530 Subject: greybus: fw-management: Update header's license The header should include both BSD and GPLv2 licenses and so should have been a copy of greybus_protocols.h. This file had only the GPLv2 bits earlier, update it to include BSD bits as well. Reported-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_firmware.h | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_firmware.h b/drivers/staging/greybus/greybus_firmware.h index 02322600968a..1b6882186459 100644 --- a/drivers/staging/greybus/greybus_firmware.h +++ b/drivers/staging/greybus/greybus_firmware.h @@ -1,8 +1,8 @@ /* * Greybus Firmware Management User Header * - * This file is provided under the GPLv2 license. When using or - * redistributing this file, you may do so under that license. + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * @@ -18,6 +18,26 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details. * + * BSD LICENSE + * + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. or Linaro Ltd. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- cgit v1.2.3-59-g8ed1b From 22e26a3a13503f3c35ed8bbb6a7e9e33ad2c6987 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Mon, 16 May 2016 10:33:20 +0100 Subject: greybus: spi: rename rdwr field to xfer_flags As more bits will be added to the field, let's make the field more generic and name it accordingly. So, rename it from rdwr to xfer_flags. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 2 +- drivers/staging/greybus/spilib.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index d379fe36c7a5..0cddbb61e894 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -876,7 +876,7 @@ struct gb_spi_transfer { __le16 delay_usecs; __u8 cs_change; __u8 bits_per_word; - __u8 rdwr; + __u8 xfer_flags; #define GB_SPI_XFER_READ 0x01 #define GB_SPI_XFER_WRITE 0x02 } __packed; diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c index 6ab1c5f77b7a..9eecbf391259 100644 --- a/drivers/staging/greybus/spilib.c +++ b/drivers/staging/greybus/spilib.c @@ -255,14 +255,14 @@ static struct gb_operation *gb_spi_operation_create(struct gb_spilib *spi, /* Copy tx data */ if (xfer->tx_buf) { - gb_xfer->rdwr |= GB_SPI_XFER_WRITE; + gb_xfer->xfer_flags |= GB_SPI_XFER_WRITE; memcpy(tx_data, xfer->tx_buf + spi->tx_xfer_offset, xfer_len); tx_data += xfer_len; } if (xfer->rx_buf) - gb_xfer->rdwr |= GB_SPI_XFER_READ; + gb_xfer->xfer_flags |= GB_SPI_XFER_READ; if (xfer == spi->last_xfer) { msg->state = GB_SPI_STATE_OP_DONE; -- cgit v1.2.3-59-g8ed1b From 3a238fc7844f93c799283d8b822178af9638ff0c Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Mon, 16 May 2016 10:33:21 +0100 Subject: greybus: spi: add inprogress bit to xfer_flags When a SPI transfer needs to be split by more than one greybus spi transfer operation, we need to indicate it so the controller can handle the chip select lines correctly. Add a new bit to indicate it, GB_SPI_XFER_INPROGRESS, and create an helper function to calculate when the transfer is done. As we need this information also in other places. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 1 + drivers/staging/greybus/spilib.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 0cddbb61e894..b98f02c93b1a 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -879,6 +879,7 @@ struct gb_spi_transfer { __u8 xfer_flags; #define GB_SPI_XFER_READ 0x01 #define GB_SPI_XFER_WRITE 0x02 +#define GB_SPI_XFER_INPROGRESS 0x04 } __packed; struct gb_spi_transfer_request { diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c index 9eecbf391259..79ae044b78f3 100644 --- a/drivers/staging/greybus/spilib.c +++ b/drivers/staging/greybus/spilib.c @@ -102,6 +102,17 @@ static void clean_xfer_state(struct gb_spilib *spi) spi->op_timeout = 0; } +static bool is_last_xfer_done(struct gb_spilib *spi) +{ + struct spi_transfer *last_xfer = spi->last_xfer; + + if ((spi->tx_xfer_offset + spi->last_xfer_size == last_xfer->len) || + (spi->rx_xfer_offset + spi->last_xfer_size == last_xfer->len)) + return true; + + return false; +} + static int setup_next_xfer(struct gb_spilib *spi, struct spi_message *msg) { struct spi_transfer *last_xfer = spi->last_xfer; @@ -113,8 +124,7 @@ static int setup_next_xfer(struct gb_spilib *spi, struct spi_message *msg) * if we transferred all content of the last transfer, reset values and * check if this was the last transfer in the message */ - if ((spi->tx_xfer_offset + spi->last_xfer_size == last_xfer->len) || - (spi->rx_xfer_offset + spi->last_xfer_size == last_xfer->len)) { + if (is_last_xfer_done(spi)) { spi->tx_xfer_offset = 0; spi->rx_xfer_offset = 0; spi->op_timeout = 0; @@ -265,6 +275,8 @@ static struct gb_operation *gb_spi_operation_create(struct gb_spilib *spi, gb_xfer->xfer_flags |= GB_SPI_XFER_READ; if (xfer == spi->last_xfer) { + if (!is_last_xfer_done(spi)) + gb_xfer->xfer_flags |= GB_SPI_XFER_INPROGRESS; msg->state = GB_SPI_STATE_OP_DONE; continue; } -- cgit v1.2.3-59-g8ed1b From b0e97bce153a416c4de4841078fba57b69de10f5 Mon Sep 17 00:00:00 2001 From: Eli Sennesh <esennesh@leaflabs.com> Date: Fri, 13 May 2016 13:27:40 -0400 Subject: greybus: operation: rate-limit dev_err printing on the receive path When we receive Greybus operations we don't recognize, requests or responses, en masse, we can pile up a lot of dev_err() printk messages. Doing so along the gb_connection_recv() code path can delay receive processing by up to seven milliseconds, starving the system of bulk-IN urbs. Rate limit those printk messages, ensuring that after too many repeated errors at the same place in the code-path, we'll stop printing to the console at all and let the urbs get returned. This will help prevent denial-of-service attacks on the AP through the UniPro network from malicious or malfunctioning modules. Testing Done: 7 msec recv-to-resubmit-urb processing times go down to <20 usecs Signed-off-by: Eli Sennesh <esennesh@leaflabs.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Mitchell Tasman <tasman@leaflabs.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 716627e863c0..31df413f5901 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -891,7 +891,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, size_t message_size; if (!operation_id) { - dev_err(&connection->hd->dev, + dev_err_ratelimited(&connection->hd->dev, "%s: invalid response id 0 received\n", connection->name); return; @@ -899,9 +899,9 @@ static void gb_connection_recv_response(struct gb_connection *connection, operation = gb_operation_find_outgoing(connection, operation_id); if (!operation) { - dev_err(&connection->hd->dev, - "%s: unexpected response id 0x%04x received\n", - connection->name, operation_id); + dev_err_ratelimited(&connection->hd->dev, + "%s: unexpected response id 0x%04x received\n", + connection->name, operation_id); return; } @@ -909,7 +909,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, header = message->header; message_size = sizeof(*header) + message->payload_size; if (!errno && size > message_size) { - dev_err(&connection->hd->dev, + dev_err_ratelimited(&connection->hd->dev, "%s: malformed response 0x%02x received (%zu > %zu)\n", connection->name, header->type, size, message_size); @@ -918,7 +918,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, if (gb_operation_short_response_allowed(operation)) { message->payload_size = size - sizeof(*header); } else { - dev_err(&connection->hd->dev, + dev_err_ratelimited(&connection->hd->dev, "%s: short response 0x%02x received (%zu < %zu)\n", connection->name, header->type, size, message_size); @@ -956,13 +956,14 @@ void gb_connection_recv(struct gb_connection *connection, if ((connection->state != GB_CONNECTION_STATE_ENABLED && connection->state != GB_CONNECTION_STATE_ENABLED_TX) || gb_connection_is_offloaded(connection)) { - dev_warn(dev, "%s: dropping %zu received bytes\n", + dev_warn_ratelimited(dev, "%s: dropping %zu received bytes\n", connection->name, size); return; } if (size < sizeof(header)) { - dev_err(dev, "%s: short message received\n", connection->name); + dev_err_ratelimited(dev, "%s: short message received\n", + connection->name); return; } @@ -970,10 +971,11 @@ void gb_connection_recv(struct gb_connection *connection, memcpy(&header, data, sizeof(header)); msg_size = le16_to_cpu(header.size); if (size < msg_size) { - dev_err(dev, - "%s: incomplete message 0x%04x of type 0x%02x received (%zu < %zu)\n", - connection->name, le16_to_cpu(header.operation_id), - header.type, size, msg_size); + dev_err_ratelimited(dev, + "%s: incomplete message 0x%04x of type 0x%02x received (%zu < %zu)\n", + connection->name, + le16_to_cpu(header.operation_id), + header.type, size, msg_size); return; /* XXX Should still complete operation */ } -- cgit v1.2.3-59-g8ed1b From ef62adae82524c745640850f62ab0dfe0b580c9f Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 18 May 2016 15:47:48 +0200 Subject: greybus: operation: fix broken activation logic An operation should only be added to the connection active list if the connection is in the enabled state, or if it is in the enabled_tx state and the operation is not incoming. This fixes a race where an early or late incoming request could be added to the active list while the connection is being enabled or disabled, something which could lead to use-after-free issues or worse. Note that the early connection-state checks in the receive path limited the impact of this bug. Fixes: e903a2ce7379 ("connection: add unidirectional enabled state") Reported-by: Alex Elder <elder@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 31df413f5901..b7cc59d0a252 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -48,8 +48,8 @@ static int gb_operation_get_active(struct gb_operation *operation) spin_lock_irqsave(&connection->lock, flags); if (connection->state != GB_CONNECTION_STATE_ENABLED && - connection->state != GB_CONNECTION_STATE_ENABLED_TX && - !gb_operation_is_incoming(operation)) { + (connection->state != GB_CONNECTION_STATE_ENABLED_TX || + gb_operation_is_incoming(operation))) { spin_unlock_irqrestore(&connection->lock, flags); return -ENOTCONN; } -- cgit v1.2.3-59-g8ed1b From ac00154a7c6a8f3b2a98c1cf3cef893af2b0abac Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Tue, 17 May 2016 22:29:08 +0530 Subject: greybus: audio: gb_manager: Use valid argument while removing ida allocated id. module->id is used as an argument to ida_simple_remove(). Since module is already dereferenced, module->id might contain invalid data. So fix this. Fixes: da4cc2d0b066 ("audio:gb_manager: Use proper locking around kobject_xxx") Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c index 9def014cd47c..aa6508b44fab 100644 --- a/drivers/staging/greybus/audio_manager.c +++ b/drivers/staging/greybus/audio_manager.c @@ -76,7 +76,7 @@ int gb_audio_manager_remove(int id) list_del(&module->list); kobject_put(&module->kobj); up_write(&modules_rwsem); - ida_simple_remove(&module_id, module->id); + ida_simple_remove(&module_id, id); return 0; } EXPORT_SYMBOL_GPL(gb_audio_manager_remove); -- cgit v1.2.3-59-g8ed1b From 814ae531d161d789496503969657ea9550e286b5 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Tue, 17 May 2016 09:13:16 -0500 Subject: greybus: connection: verify disabled when destroyed A connection must be in DISABLED state before it gets destroyed. Warn if this is ever not the case (and do the disconnect) before proceeding with connection destruction. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f803d40413ac..ac3be2fceade 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -660,6 +660,9 @@ void gb_connection_destroy(struct gb_connection *connection) if (!connection) return; + if (WARN_ON(connection->state != GB_CONNECTION_STATE_DISABLED)) + gb_connection_disable(connection); + mutex_lock(&gb_connection_mutex); spin_lock_irq(&gb_connections_lock); -- cgit v1.2.3-59-g8ed1b From 9bc63b7ff56f7a1cad5dbf062d1d5dd7a81d7d93 Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Mon, 16 May 2016 17:39:32 +0200 Subject: greybus: uart fix missing negation on DTR setting The unset the DTR flag is missing "~" Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index aa28ce5c4fb0..75fa527acf15 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -375,7 +375,7 @@ static void gb_tty_set_termios(struct tty_struct *tty, if (C_BAUD(tty) == B0) { newline.rate = gb_tty->line_coding.rate; - newctrl &= GB_UART_CTRL_DTR; + newctrl &= ~GB_UART_CTRL_DTR; } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { newctrl |= GB_UART_CTRL_DTR; } -- cgit v1.2.3-59-g8ed1b From e16715c135d80aafea867849f938b080d4f4eadb Mon Sep 17 00:00:00 2001 From: Jeffrey Carlyle <jcarlyle@google.com> Date: Wed, 18 May 2016 18:55:13 -0700 Subject: greybus: interface: retry enumeration of UniPro-only modules Greybus modules will sometimes fail to send the mailbox poke and erroneously be enumerated as UniPro-only modules. The root cause for this on the module side is not fully understand, but it seems that this may be due to "the bootrom bug:" a known problem with the bootrom where linkup will occasionally fail because of a race condition. Before the new hotplug code was implemented in the firmware, the SVC would retry enumeration of modules that did not send the mailbox poke; this patch ports that functionality to the AP. Signed-off-by: Jeffrey Carlyle <jcarlyle@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 3 ++- drivers/staging/greybus/module.c | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index a1435e94f5a1..3b5669375ed3 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -447,7 +447,8 @@ static int gb_interface_activate_operation(struct gb_interface *intf) return -ENODEV; case GB_SVC_INTF_TYPE_UNIPRO: dev_err(&intf->dev, "interface type UniPro not supported\n"); - return -ENODEV; + /* FIXME: check if this is a Toshiba bridge before retrying? */ + return -EAGAIN; case GB_SVC_INTF_TYPE_GREYBUS: break; default: diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 5077253037c8..ea5895475181 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -138,13 +138,32 @@ static void gb_module_register_interface(struct gb_interface *intf) struct gb_module *module = intf->module; u8 intf_id = intf->interface_id; int ret; + int retries = 3; mutex_lock(&intf->mutex); - ret = gb_interface_activate(intf); + while (retries--) { + ret = gb_interface_activate(intf); + if (ret != -EAGAIN) + break; + } if (ret) { dev_err(&module->dev, "failed to activate interface %u: %d\n", intf_id, ret); + + /* + * -EAGAIN indicates that the Greybus operation + * interface_activate determined the remote interface to be + * UniPro-only. At present, we assume a UniPro-only module + * to be a Greybus module that failed to send its mailbox + * poke. There is some reason to believe that this is + * because of a bug in the ES3 bootrom. If we exhause our + * retries trying to activate such an interface, convert + * the error code back into a "no device" error. + */ + if (ret == -EAGAIN) + ret = -ENODEV; + gb_interface_add(intf); goto err_unlock; } -- cgit v1.2.3-59-g8ed1b From e54b106dd1be50377fe8365392466e080b659ab6 Mon Sep 17 00:00:00 2001 From: Sandeep Patil <patil_sandeep@projectara.com> Date: Thu, 19 May 2016 08:52:39 -0700 Subject: greybus: gpbridge: rename 'gpbridge' to 'gbphy' everywhere The 'gpbridge' name didn't relaly reflect what the bus is; which is a bus for bridged-phy devices. So, rename all instances of 'gpbridge' to more appropriate 'gbphy' Testing Done: Build and boot tested. 'lsgb' will stop displaying 'GPBridge' devices until I change the library to reflect this change. Signed-off-by: Sandeep Patil <patil_sandeep@projectara.com> Suggested-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 4 +- drivers/staging/greybus/gbphy.c | 330 ++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/gbphy.h | 71 ++++++++ drivers/staging/greybus/gpbridge.c | 330 ------------------------------------ drivers/staging/greybus/gpbridge.h | 71 -------- drivers/staging/greybus/gpio.c | 54 +++--- drivers/staging/greybus/i2c.c | 38 ++--- drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/loopback.c | 26 +-- drivers/staging/greybus/pwm.c | 32 ++-- drivers/staging/greybus/sdio.c | 36 ++-- drivers/staging/greybus/spi.c | 30 ++-- drivers/staging/greybus/uart.c | 48 +++--- drivers/staging/greybus/usb.c | 38 ++--- 14 files changed, 555 insertions(+), 555 deletions(-) create mode 100644 drivers/staging/greybus/gbphy.c create mode 100644 drivers/staging/greybus/gbphy.h delete mode 100644 drivers/staging/greybus/gpbridge.c delete mode 100644 drivers/staging/greybus/gpbridge.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 608bd51e7c0b..ae9a478d3dde 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -14,7 +14,7 @@ greybus-y := core.o \ operation.o \ legacy.o -gb-gpbridge-y := gpbridge.o +gb-gbphy-y := gbphy.o # Prefix all modules with gb- gb-vibrator-y := vibrator.o @@ -43,7 +43,7 @@ gb-usb-y := usb.o gb-spi-y := spi.o obj-m += greybus.o -obj-m += gb-gpbridge.o +obj-m += gb-gbphy.o obj-m += gb-vibrator.o obj-m += gb-power-supply.o obj-m += gb-loopback.o diff --git a/drivers/staging/greybus/gbphy.c b/drivers/staging/greybus/gbphy.c new file mode 100644 index 000000000000..c11a1368d073 --- /dev/null +++ b/drivers/staging/greybus/gbphy.c @@ -0,0 +1,330 @@ +/* + * Greybus Bridged-Phy Bus driver + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/device.h> + +#include "greybus.h" +#include "gbphy.h" + +struct gbphy_host { + struct gb_bundle *bundle; + struct list_head devices; +}; + +static DEFINE_IDA(gbphy_id); + +static ssize_t protocol_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); + + return sprintf(buf, "0x%02x\n", gbphy_dev->cport_desc->protocol_id); +} +static DEVICE_ATTR_RO(protocol_id); + +static struct attribute *gbphy_dev_attrs[] = { + &dev_attr_protocol_id.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(gbphy_dev); + +static void gbphy_dev_release(struct device *dev) +{ + struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); + + ida_simple_remove(&gbphy_id, gbphy_dev->id); + kfree(gbphy_dev); +} + +static struct device_type greybus_gbphy_dev_type = { + .name = "gbphy_device", + .release = gbphy_dev_release, +}; + +static int gbphy_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); + struct greybus_descriptor_cport *cport_desc = gbphy_dev->cport_desc; + struct gb_bundle *bundle = gbphy_dev->bundle; + struct gb_interface *intf = bundle->intf; + struct gb_module *module = intf->module; + struct gb_host_device *hd = intf->hd; + + if (add_uevent_var(env, "BUS=%u", hd->bus_id)) + return -ENOMEM; + if (add_uevent_var(env, "MODULE=%u", module->module_id)) + return -ENOMEM; + if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) + return -ENOMEM; + if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x", + intf->vendor_id, intf->product_id)) + return -ENOMEM; + if (add_uevent_var(env, "BUNDLE=%u", gbphy_dev->bundle->id)) + return -ENOMEM; + if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class)) + return -ENOMEM; + if (add_uevent_var(env, "GBPHY=%u", gbphy_dev->id)) + return -ENOMEM; + if (add_uevent_var(env, "PROTOCOL_ID=%02x", cport_desc->protocol_id)) + return -ENOMEM; + + return 0; +} + +static const struct gbphy_device_id * +gbphy_dev_match_id(struct gbphy_device *gbphy_dev, struct gbphy_driver *gbphy_drv) +{ + const struct gbphy_device_id *id = gbphy_drv->id_table; + + if (!id) + return NULL; + + for (; id->protocol_id; id++) + if (id->protocol_id == gbphy_dev->cport_desc->protocol_id) + return id; + + return NULL; +} + +static int gbphy_dev_match(struct device *dev, struct device_driver *drv) +{ + struct gbphy_driver *gbphy_drv = to_gbphy_driver(drv); + struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); + const struct gbphy_device_id *id; + + id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); + if (id) + return 1; + + return 0; +} + +static int gbphy_dev_probe(struct device *dev) +{ + struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); + struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); + const struct gbphy_device_id *id; + + id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); + if (!id) + return -ENODEV; + + return gbphy_drv->probe(gbphy_dev, id); +} + +static int gbphy_dev_remove(struct device *dev) +{ + struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); + struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); + + gbphy_drv->remove(gbphy_dev); + return 0; +} + +static struct bus_type gbphy_bus_type = { + .name = "gbphy", + .match = gbphy_dev_match, + .probe = gbphy_dev_probe, + .remove = gbphy_dev_remove, + .uevent = gbphy_dev_uevent, +}; + +int gb_gbphy_register_driver(struct gbphy_driver *driver, + struct module *owner, const char *mod_name) +{ + int retval; + + if (greybus_disabled()) + return -ENODEV; + + driver->driver.bus = &gbphy_bus_type; + driver->driver.name = driver->name; + driver->driver.owner = owner; + driver->driver.mod_name = mod_name; + + retval = driver_register(&driver->driver); + if (retval) + return retval; + + pr_info("registered new driver %s\n", driver->name); + return 0; +} +EXPORT_SYMBOL_GPL(gb_gbphy_register_driver); + +void gb_gbphy_deregister_driver(struct gbphy_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(gb_gbphy_deregister_driver); + +int gb_gbphy_get_version(struct gb_connection *connection) +{ + struct gb_protocol_version_request request; + struct gb_protocol_version_response response; + int retval; + + request.major = 1; + request.minor = 0; + + retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, + &request, sizeof(request), &response, + sizeof(response)); + if (retval) + return retval; + + /* FIXME - do proper version negotiation here someday... */ + + connection->module_major = response.major; + connection->module_minor = response.minor; + + dev_dbg(&connection->hd->dev, "%s: v%u.%u\n", connection->name, + response.major, response.minor); + + return 0; +} +EXPORT_SYMBOL_GPL(gb_gbphy_get_version); + +static struct gbphy_device *gb_gbphy_create_dev(struct gb_bundle *bundle, + struct greybus_descriptor_cport *cport_desc) +{ + struct gbphy_device *gbphy_dev; + int retval; + int id; + + id = ida_simple_get(&gbphy_id, 1, 0, GFP_KERNEL); + if (id < 0) + return ERR_PTR(id); + + gbphy_dev = kzalloc(sizeof(*gbphy_dev), GFP_KERNEL); + if (!gbphy_dev) { + ida_simple_remove(&gbphy_id, id); + return ERR_PTR(-ENOMEM); + } + + gbphy_dev->id = id; + gbphy_dev->bundle = bundle; + gbphy_dev->cport_desc = cport_desc; + gbphy_dev->dev.parent = &bundle->dev; + gbphy_dev->dev.bus = &gbphy_bus_type; + gbphy_dev->dev.type = &greybus_gbphy_dev_type; + gbphy_dev->dev.groups = gbphy_dev_groups; + gbphy_dev->dev.dma_mask = bundle->dev.dma_mask; + dev_set_name(&gbphy_dev->dev, "gbphy%d", id); + + retval = device_register(&gbphy_dev->dev); + if (retval) { + put_device(&gbphy_dev->dev); + return ERR_PTR(retval); + } + + return gbphy_dev; +} + +static void gb_gbphy_disconnect(struct gb_bundle *bundle) +{ + struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle); + struct gbphy_device *gbphy_dev, *temp; + + list_for_each_entry_safe(gbphy_dev, temp, &gbphy_host->devices, list) { + list_del(&gbphy_dev->list); + device_unregister(&gbphy_dev->dev); + } + + kfree(gbphy_host); +} + +static int gb_gbphy_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + struct gbphy_host *gbphy_host; + struct gbphy_device *gbphy_dev; + int i; + + if (bundle->num_cports == 0) + return -ENODEV; + + gbphy_host = kzalloc(sizeof(*gbphy_host), GFP_KERNEL); + if (!gbphy_host) + return -ENOMEM; + + gbphy_host->bundle = bundle; + INIT_LIST_HEAD(&gbphy_host->devices); + greybus_set_drvdata(bundle, gbphy_host); + + /* + * Create a bunch of children devices, one per cport, and bind the + * bridged phy drivers to them. + */ + for (i = 0; i < bundle->num_cports; ++i) { + gbphy_dev = gb_gbphy_create_dev(bundle, &bundle->cport_desc[i]); + if (IS_ERR(gbphy_dev)) { + gb_gbphy_disconnect(bundle); + return PTR_ERR(gbphy_dev); + } + list_add(&gbphy_dev->list, &gbphy_host->devices); + } + + return 0; +} + +static const struct greybus_bundle_id gb_gbphy_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, + { }, +}; +MODULE_DEVICE_TABLE(greybus, gb_gbphy_id_table); + +static struct greybus_driver gb_gbphy_driver = { + .name = "gbphy", + .probe = gb_gbphy_probe, + .disconnect = gb_gbphy_disconnect, + .id_table = gb_gbphy_id_table, +}; + +static int __init gbphy_init(void) +{ + int retval; + + retval = bus_register(&gbphy_bus_type); + if (retval) { + pr_err("gbphy bus register failed (%d)\n", retval); + return retval; + } + + retval = greybus_register(&gb_gbphy_driver); + if (retval) { + pr_err("error registering greybus driver\n"); + goto error_gbphy; + } + + return 0; + +error_gbphy: + bus_unregister(&gbphy_bus_type); + ida_destroy(&gbphy_id); + return retval; +} +module_init(gbphy_init); + +static void __exit gbphy_exit(void) +{ + greybus_deregister(&gb_gbphy_driver); + bus_unregister(&gbphy_bus_type); + ida_destroy(&gbphy_id); +} +module_exit(gbphy_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/gbphy.h b/drivers/staging/greybus/gbphy.h new file mode 100644 index 000000000000..79dbf7e1be58 --- /dev/null +++ b/drivers/staging/greybus/gbphy.h @@ -0,0 +1,71 @@ +/* + * Greybus Bridged-Phy Bus driver + * + * Copyright 2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __GBPHY_H +#define __GBPHY_H + +struct gbphy_device { + u32 id; + struct greybus_descriptor_cport *cport_desc; + struct gb_bundle *bundle; + struct list_head list; + struct device dev; +}; +#define to_gbphy_dev(d) container_of(d, struct gbphy_device, dev) + +static inline void *gb_gbphy_get_data(struct gbphy_device *gdev) +{ + return dev_get_drvdata(&gdev->dev); +} + +static inline void gb_gbphy_set_data(struct gbphy_device *gdev, void *data) +{ + dev_set_drvdata(&gdev->dev, data); +} + +struct gbphy_device_id { + __u8 protocol_id; +}; + +#define GBPHY_PROTOCOL(p) \ + .protocol_id = (p), + +struct gbphy_driver { + const char *name; + int (*probe)(struct gbphy_device *, + const struct gbphy_device_id *id); + void (*remove)(struct gbphy_device *); + const struct gbphy_device_id *id_table; + + struct device_driver driver; +}; +#define to_gbphy_driver(d) container_of(d, struct gbphy_driver, driver) + +int gb_gbphy_get_version(struct gb_connection *connection); +int gb_gbphy_register_driver(struct gbphy_driver *driver, + struct module *owner, const char *mod_name); +void gb_gbphy_deregister_driver(struct gbphy_driver *driver); + +#define gb_gbphy_register(driver) \ + gb_gbphy_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) +#define gb_gbphy_deregister(driver) \ + gb_gbphy_deregister_driver(driver) + +/** + * module_gbphy_driver() - Helper macro for registering a gbphy driver + * @__gbphy_driver: gbphy_driver structure + * + * Helper macro for gbphy drivers to set up proper module init / exit + * functions. Replaces module_init() and module_exit() and keeps people from + * printing pointless things to the kernel log when their driver is loaded. + */ +#define module_gbphy_driver(__gbphy_driver) \ + module_driver(__gbphy_driver, gb_gbphy_register, gb_gbphy_deregister) + +#endif /* __GBPHY_H */ + diff --git a/drivers/staging/greybus/gpbridge.c b/drivers/staging/greybus/gpbridge.c deleted file mode 100644 index 88f7d26121e4..000000000000 --- a/drivers/staging/greybus/gpbridge.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Greybus GP Bridge driver - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/types.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/device.h> - -#include "greybus.h" -#include "gpbridge.h" - -struct gpbridge_host { - struct gb_bundle *bundle; - struct list_head devices; -}; - -static DEFINE_IDA(gpbridge_id); - -static ssize_t protocol_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); - - return sprintf(buf, "0x%02x\n", gpbdev->cport_desc->protocol_id); -} -static DEVICE_ATTR_RO(protocol_id); - -static struct attribute *gpbdev_attrs[] = { - &dev_attr_protocol_id.attr, - NULL, -}; - -ATTRIBUTE_GROUPS(gpbdev); - -static void gpbdev_release(struct device *dev) -{ - struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); - - ida_simple_remove(&gpbridge_id, gpbdev->id); - kfree(gpbdev); -} - -static struct device_type greybus_gpbdev_type = { - .name = "gpbridge_device", - .release = gpbdev_release, -}; - -static int gpbdev_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); - struct greybus_descriptor_cport *cport_desc = gpbdev->cport_desc; - struct gb_bundle *bundle = gpbdev->bundle; - struct gb_interface *intf = bundle->intf; - struct gb_module *module = intf->module; - struct gb_host_device *hd = intf->hd; - - if (add_uevent_var(env, "BUS=%u", hd->bus_id)) - return -ENOMEM; - if (add_uevent_var(env, "MODULE=%u", module->module_id)) - return -ENOMEM; - if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) - return -ENOMEM; - if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x", - intf->vendor_id, intf->product_id)) - return -ENOMEM; - if (add_uevent_var(env, "BUNDLE=%u", gpbdev->bundle->id)) - return -ENOMEM; - if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class)) - return -ENOMEM; - if (add_uevent_var(env, "GPBDEV_ID=%u", gpbdev->id)) - return -ENOMEM; - if (add_uevent_var(env, "PROTOCOL_ID=%02x", cport_desc->protocol_id)) - return -ENOMEM; - - return 0; -} - -static const struct gpbridge_device_id * -gpbdev_match_id(struct gpbridge_device *gpbdev, struct gpbridge_driver *gpbdrv) -{ - const struct gpbridge_device_id *id = gpbdrv->id_table; - - if (!id) - return NULL; - - for (; id->protocol_id; id++) - if (id->protocol_id == gpbdev->cport_desc->protocol_id) - return id; - - return NULL; -} - -static int gpbdev_match(struct device *dev, struct device_driver *drv) -{ - struct gpbridge_driver *gpbdrv = to_gpbridge_driver(drv); - struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); - const struct gpbridge_device_id *id; - - id = gpbdev_match_id(gpbdev, gpbdrv); - if (id) - return 1; - - return 0; -} - -static int gpbdev_probe(struct device *dev) -{ - struct gpbridge_driver *gpbdrv = to_gpbridge_driver(dev->driver); - struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); - const struct gpbridge_device_id *id; - - id = gpbdev_match_id(gpbdev, gpbdrv); - if (!id) - return -ENODEV; - - return gpbdrv->probe(gpbdev, id); -} - -static int gpbdev_remove(struct device *dev) -{ - struct gpbridge_driver *gpbdrv = to_gpbridge_driver(dev->driver); - struct gpbridge_device *gpbdev = to_gpbridge_dev(dev); - - gpbdrv->remove(gpbdev); - return 0; -} - -static struct bus_type gpbridge_bus_type = { - .name = "gpbridge", - .match = gpbdev_match, - .probe = gpbdev_probe, - .remove = gpbdev_remove, - .uevent = gpbdev_uevent, -}; - -int gb_gpbridge_register_driver(struct gpbridge_driver *driver, - struct module *owner, const char *mod_name) -{ - int retval; - - if (greybus_disabled()) - return -ENODEV; - - driver->driver.bus = &gpbridge_bus_type; - driver->driver.name = driver->name; - driver->driver.owner = owner; - driver->driver.mod_name = mod_name; - - retval = driver_register(&driver->driver); - if (retval) - return retval; - - pr_info("registered new driver %s\n", driver->name); - return 0; -} -EXPORT_SYMBOL_GPL(gb_gpbridge_register_driver); - -void gb_gpbridge_deregister_driver(struct gpbridge_driver *driver) -{ - driver_unregister(&driver->driver); -} -EXPORT_SYMBOL_GPL(gb_gpbridge_deregister_driver); - -int gb_gpbridge_get_version(struct gb_connection *connection) -{ - struct gb_protocol_version_request request; - struct gb_protocol_version_response response; - int retval; - - request.major = 1; - request.minor = 0; - - retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, - &request, sizeof(request), &response, - sizeof(response)); - if (retval) - return retval; - - /* FIXME - do proper version negotiation here someday... */ - - connection->module_major = response.major; - connection->module_minor = response.minor; - - dev_dbg(&connection->hd->dev, "%s: v%u.%u\n", connection->name, - response.major, response.minor); - - return 0; -} -EXPORT_SYMBOL_GPL(gb_gpbridge_get_version); - -static struct gpbridge_device *gb_gpbridge_create_dev(struct gb_bundle *bundle, - struct greybus_descriptor_cport *cport_desc) -{ - struct gpbridge_device *gpbdev; - int retval; - int id; - - id = ida_simple_get(&gpbridge_id, 1, 0, GFP_KERNEL); - if (id < 0) - return ERR_PTR(id); - - gpbdev = kzalloc(sizeof(*gpbdev), GFP_KERNEL); - if (!gpbdev) { - ida_simple_remove(&gpbridge_id, id); - return ERR_PTR(-ENOMEM); - } - - gpbdev->id = id; - gpbdev->bundle = bundle; - gpbdev->cport_desc = cport_desc; - gpbdev->dev.parent = &bundle->dev; - gpbdev->dev.bus = &gpbridge_bus_type; - gpbdev->dev.type = &greybus_gpbdev_type; - gpbdev->dev.groups = gpbdev_groups; - gpbdev->dev.dma_mask = bundle->dev.dma_mask; - dev_set_name(&gpbdev->dev, "gpb%d", id); - - retval = device_register(&gpbdev->dev); - if (retval) { - put_device(&gpbdev->dev); - return ERR_PTR(retval); - } - - return gpbdev; -} - -static void gb_gpbridge_disconnect(struct gb_bundle *bundle) -{ - struct gpbridge_host *gpb_host = greybus_get_drvdata(bundle); - struct gpbridge_device *gpbdev, *temp; - - list_for_each_entry_safe(gpbdev, temp, &gpb_host->devices, list) { - list_del(&gpbdev->list); - device_unregister(&gpbdev->dev); - } - - kfree(gpb_host); -} - -static int gb_gpbridge_probe(struct gb_bundle *bundle, - const struct greybus_bundle_id *id) -{ - struct gpbridge_host *gpb_host; - struct gpbridge_device *gpbdev; - int i; - - if (bundle->num_cports == 0) - return -ENODEV; - - gpb_host = kzalloc(sizeof(*gpb_host), GFP_KERNEL); - if (!gpb_host) - return -ENOMEM; - - gpb_host->bundle = bundle; - INIT_LIST_HEAD(&gpb_host->devices); - greybus_set_drvdata(bundle, gpb_host); - - /* - * Create a bunch of children devices, one per cport, and bind the - * bridged phy drivers to them. - */ - for (i = 0; i < bundle->num_cports; ++i) { - gpbdev = gb_gpbridge_create_dev(bundle, &bundle->cport_desc[i]); - if (IS_ERR(gpbdev)) { - gb_gpbridge_disconnect(bundle); - return PTR_ERR(gpbdev); - } - list_add(&gpbdev->list, &gpb_host->devices); - } - - return 0; -} - -static const struct greybus_bundle_id gb_gpbridge_id_table[] = { - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, - { }, -}; -MODULE_DEVICE_TABLE(greybus, gb_gpbridge_id_table); - -static struct greybus_driver gb_gpbridge_driver = { - .name = "gpbridge", - .probe = gb_gpbridge_probe, - .disconnect = gb_gpbridge_disconnect, - .id_table = gb_gpbridge_id_table, -}; - -static int __init gpbridge_init(void) -{ - int retval; - - retval = bus_register(&gpbridge_bus_type); - if (retval) { - pr_err("gpbridge bus register failed (%d)\n", retval); - return retval; - } - - retval = greybus_register(&gb_gpbridge_driver); - if (retval) { - pr_err("error registering greybus driver\n"); - goto error_gpbridge; - } - - return 0; - -error_gpbridge: - bus_unregister(&gpbridge_bus_type); - ida_destroy(&gpbridge_id); - return retval; -} -module_init(gpbridge_init); - -static void __exit gpbridge_exit(void) -{ - greybus_deregister(&gb_gpbridge_driver); - bus_unregister(&gpbridge_bus_type); - ida_destroy(&gpbridge_id); -} -module_exit(gpbridge_exit); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/gpbridge.h b/drivers/staging/greybus/gpbridge.h deleted file mode 100644 index 0cee2eb64237..000000000000 --- a/drivers/staging/greybus/gpbridge.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Greybus GPBridge phy driver - * - * Copyright 2016 Google Inc. - * - * Released under the GPLv2 only. - */ - -#ifndef __GPBRIDGE_H -#define __GPBRIDGE_H - -struct gpbridge_device { - u32 id; - struct greybus_descriptor_cport *cport_desc; - struct gb_bundle *bundle; - struct list_head list; - struct device dev; -}; -#define to_gpbridge_dev(d) container_of(d, struct gpbridge_device, dev) - -static inline void *gb_gpbridge_get_data(struct gpbridge_device *gdev) -{ - return dev_get_drvdata(&gdev->dev); -} - -static inline void gb_gpbridge_set_data(struct gpbridge_device *gdev, void *data) -{ - dev_set_drvdata(&gdev->dev, data); -} - -struct gpbridge_device_id { - __u8 protocol_id; -}; - -#define GPBRIDGE_PROTOCOL(p) \ - .protocol_id = (p), - -struct gpbridge_driver { - const char *name; - int (*probe)(struct gpbridge_device *, - const struct gpbridge_device_id *id); - void (*remove)(struct gpbridge_device *); - const struct gpbridge_device_id *id_table; - - struct device_driver driver; -}; -#define to_gpbridge_driver(d) container_of(d, struct gpbridge_driver, driver) - -int gb_gpbridge_get_version(struct gb_connection *connection); -int gb_gpbridge_register_driver(struct gpbridge_driver *driver, - struct module *owner, const char *mod_name); -void gb_gpbridge_deregister_driver(struct gpbridge_driver *driver); - -#define gb_gpbridge_register(driver) \ - gb_gpbridge_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) -#define gb_gpbridge_deregister(driver) \ - gb_gpbridge_deregister_driver(driver) - -/** - * module_gpbridge_driver() - Helper macro for registering a gpbridge driver - * @__gpbridge_driver: gpbridge_driver structure - * - * Helper macro for gpbridge drivers to set up proper module init / exit - * functions. Replaces module_init() and module_exit() and keeps people from - * printing pointless things to the kernel log when their driver is loaded. - */ -#define module_gpbridge_driver(__gpbridge_driver) \ - module_driver(__gpbridge_driver, gb_gpbridge_register, gb_gpbridge_deregister) - -#endif /* __GPBRIDGE_H */ - diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index adb213fd81b6..e1ad6802630a 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -16,7 +16,7 @@ #include <linux/mutex.h> #include "greybus.h" -#include "gpbridge.h" +#include "gbphy.h" struct gb_gpio_line { /* The following has to be an array of line_max entries */ @@ -33,7 +33,7 @@ struct gb_gpio_line { }; struct gb_gpio_controller { - struct gpbridge_device *gpbdev; + struct gbphy_device *gbphy_dev; struct gb_connection *connection; u8 line_max; /* max line number */ struct gb_gpio_line *lines; @@ -79,7 +79,7 @@ static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, u8 which) { - struct device *dev = &ggc->gpbdev->dev; + struct device *dev = &ggc->gbphy_dev->dev; struct gb_gpio_deactivate_request request; int ret; @@ -97,7 +97,7 @@ static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, u8 which) { - struct device *dev = &ggc->gpbdev->dev; + struct device *dev = &ggc->gbphy_dev->dev; struct gb_gpio_get_direction_request request; struct gb_gpio_get_direction_response response; int ret; @@ -151,7 +151,7 @@ static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, u8 which) { - struct device *dev = &ggc->gpbdev->dev; + struct device *dev = &ggc->gbphy_dev->dev; struct gb_gpio_get_value_request request; struct gb_gpio_get_value_response response; int ret; @@ -178,7 +178,7 @@ static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, static void gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, u8 which, bool value_high) { - struct device *dev = &ggc->gpbdev->dev; + struct device *dev = &ggc->gbphy_dev->dev; struct gb_gpio_set_value_request request; int ret; @@ -217,7 +217,7 @@ static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, static void _gb_gpio_irq_mask(struct gb_gpio_controller *ggc, u8 hwirq) { - struct device *dev = &ggc->gpbdev->dev; + struct device *dev = &ggc->gbphy_dev->dev; struct gb_gpio_irq_mask_request request; int ret; @@ -231,7 +231,7 @@ static void _gb_gpio_irq_mask(struct gb_gpio_controller *ggc, u8 hwirq) static void _gb_gpio_irq_unmask(struct gb_gpio_controller *ggc, u8 hwirq) { - struct device *dev = &ggc->gpbdev->dev; + struct device *dev = &ggc->gbphy_dev->dev; struct gb_gpio_irq_unmask_request request; int ret; @@ -246,7 +246,7 @@ static void _gb_gpio_irq_unmask(struct gb_gpio_controller *ggc, u8 hwirq) static void _gb_gpio_irq_set_type(struct gb_gpio_controller *ggc, u8 hwirq, u8 type) { - struct device *dev = &ggc->gpbdev->dev; + struct device *dev = &ggc->gbphy_dev->dev; struct gb_gpio_irq_type_request request; int ret; @@ -285,7 +285,7 @@ static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_chip *chip = irq_data_to_gpio_chip(d); struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); struct gb_gpio_line *line = &ggc->lines[d->hwirq]; - struct device *dev = &ggc->gpbdev->dev; + struct device *dev = &ggc->gbphy_dev->dev; u8 irq_type; switch (type) { @@ -352,7 +352,7 @@ static int gb_gpio_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_gpio_controller *ggc = gb_connection_get_data(connection); - struct device *dev = &ggc->gpbdev->dev; + struct device *dev = &ggc->gbphy_dev->dev; struct gb_message *request; struct gb_gpio_irq_event_request *event; u8 type = op->type; @@ -624,8 +624,8 @@ static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return irq_find_mapping(ggc->irqdomain, offset); } -static int gb_gpio_probe(struct gpbridge_device *gpbdev, - const struct gpbridge_device_id *id) +static int gb_gpio_probe(struct gbphy_device *gbphy_dev, + const struct gbphy_device_id *id) { struct gb_connection *connection; struct gb_gpio_controller *ggc; @@ -637,8 +637,8 @@ static int gb_gpio_probe(struct gpbridge_device *gpbdev, if (!ggc) return -ENOMEM; - connection = gb_connection_create(gpbdev->bundle, - le16_to_cpu(gpbdev->cport_desc->id), + connection = gb_connection_create(gbphy_dev->bundle, + le16_to_cpu(gbphy_dev->cport_desc->id), gb_gpio_request_handler); if (IS_ERR(connection)) { ret = PTR_ERR(connection); @@ -647,14 +647,14 @@ static int gb_gpio_probe(struct gpbridge_device *gpbdev, ggc->connection = connection; gb_connection_set_data(connection, ggc); - ggc->gpbdev = gpbdev; - gb_gpbridge_set_data(gpbdev, ggc); + ggc->gbphy_dev = gbphy_dev; + gb_gbphy_set_data(gbphy_dev, ggc); ret = gb_connection_enable_tx(connection); if (ret) goto exit_connection_destroy; - ret = gb_gpbridge_get_version(connection); + ret = gb_gbphy_get_version(connection); if (ret) goto exit_connection_disable; @@ -676,9 +676,9 @@ static int gb_gpio_probe(struct gpbridge_device *gpbdev, gpio->label = "greybus_gpio"; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) - gpio->parent = &gpbdev->dev; + gpio->parent = &gbphy_dev->dev; #else - gpio->dev = &gpbdev->dev; + gpio->dev = &gbphy_dev->dev; #endif gpio->owner = THIS_MODULE; @@ -729,9 +729,9 @@ exit_ggc_free: return ret; } -static void gb_gpio_remove(struct gpbridge_device *gpbdev) +static void gb_gpio_remove(struct gbphy_device *gbphy_dev) { - struct gb_gpio_controller *ggc = gb_gpbridge_get_data(gpbdev); + struct gb_gpio_controller *ggc = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = ggc->connection; gb_connection_disable_rx(connection); @@ -743,18 +743,18 @@ static void gb_gpio_remove(struct gpbridge_device *gpbdev) kfree(ggc); } -static const struct gpbridge_device_id gb_gpio_id_table[] = { - { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_GPIO) }, +static const struct gbphy_device_id gb_gpio_id_table[] = { + { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_GPIO) }, { }, }; -MODULE_DEVICE_TABLE(gpbridge, gb_gpio_id_table); +MODULE_DEVICE_TABLE(gbphy, gb_gpio_id_table); -static struct gpbridge_driver gpio_driver = { +static struct gbphy_driver gpio_driver = { .name = "gpio", .probe = gb_gpio_probe, .remove = gb_gpio_remove, .id_table = gb_gpio_id_table, }; -module_gpbridge_driver(gpio_driver); +module_gbphy_driver(gpio_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 69d6f07c0822..6c14e6776adf 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -13,11 +13,11 @@ #include <linux/i2c.h> #include "greybus.h" -#include "gpbridge.h" +#include "gbphy.h" struct gb_i2c_device { struct gb_connection *connection; - struct gpbridge_device *gpbdev; + struct gbphy_device *gbphy_dev; u32 functionality; @@ -85,7 +85,7 @@ gb_i2c_operation_create(struct gb_connection *connection, u32 i; if (msg_count > (u32)U16_MAX) { - dev_err(&gb_i2c_dev->gpbdev->dev, "msg_count (%u) too big\n", + dev_err(&gb_i2c_dev->gbphy_dev->dev, "msg_count (%u) too big\n", msg_count); return NULL; } @@ -168,7 +168,7 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, struct i2c_msg *msgs, u32 msg_count) { struct gb_connection *connection = gb_i2c_dev->connection; - struct device *dev = &gb_i2c_dev->gpbdev->dev; + struct device *dev = &gb_i2c_dev->gbphy_dev->dev; struct gb_operation *operation; int ret; @@ -242,8 +242,8 @@ static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) return gb_i2c_functionality_operation(gb_i2c_dev); } -static int gb_i2c_probe(struct gpbridge_device *gpbdev, - const struct gpbridge_device_id *id) +static int gb_i2c_probe(struct gbphy_device *gbphy_dev, + const struct gbphy_device_id *id) { struct gb_connection *connection; struct gb_i2c_device *gb_i2c_dev; @@ -254,8 +254,8 @@ static int gb_i2c_probe(struct gpbridge_device *gpbdev, if (!gb_i2c_dev) return -ENOMEM; - connection = gb_connection_create(gpbdev->bundle, - le16_to_cpu(gpbdev->cport_desc->id), + connection = gb_connection_create(gbphy_dev->bundle, + le16_to_cpu(gbphy_dev->cport_desc->id), NULL); if (IS_ERR(connection)) { ret = PTR_ERR(connection); @@ -264,14 +264,14 @@ static int gb_i2c_probe(struct gpbridge_device *gpbdev, gb_i2c_dev->connection = connection; gb_connection_set_data(connection, gb_i2c_dev); - gb_i2c_dev->gpbdev = gpbdev; - gb_gpbridge_set_data(gpbdev, gb_i2c_dev); + gb_i2c_dev->gbphy_dev = gbphy_dev; + gb_gbphy_set_data(gbphy_dev, gb_i2c_dev); ret = gb_connection_enable(connection); if (ret) goto exit_connection_destroy; - ret = gb_gpbridge_get_version(connection); + ret = gb_gbphy_get_version(connection); if (ret) goto exit_connection_disable; @@ -286,7 +286,7 @@ static int gb_i2c_probe(struct gpbridge_device *gpbdev, adapter->algo = &gb_i2c_algorithm; /* adapter->algo_data = what? */ - adapter->dev.parent = &gpbdev->dev; + adapter->dev.parent = &gbphy_dev->dev; snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); i2c_set_adapdata(adapter, gb_i2c_dev); @@ -306,9 +306,9 @@ exit_i2cdev_free: return ret; } -static void gb_i2c_remove(struct gpbridge_device *gpbdev) +static void gb_i2c_remove(struct gbphy_device *gbphy_dev) { - struct gb_i2c_device *gb_i2c_dev = gb_gpbridge_get_data(gpbdev); + struct gb_i2c_device *gb_i2c_dev = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = gb_i2c_dev->connection; i2c_del_adapter(&gb_i2c_dev->adapter); @@ -317,18 +317,18 @@ static void gb_i2c_remove(struct gpbridge_device *gpbdev) kfree(gb_i2c_dev); } -static const struct gpbridge_device_id gb_i2c_id_table[] = { - { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_I2C) }, +static const struct gbphy_device_id gb_i2c_id_table[] = { + { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_I2C) }, { }, }; -MODULE_DEVICE_TABLE(gpbridge, gb_i2c_id_table); +MODULE_DEVICE_TABLE(gbphy, gb_i2c_id_table); -static struct gpbridge_driver i2c_driver = { +static struct gbphy_driver i2c_driver = { .name = "i2c", .probe = gb_i2c_probe, .remove = gb_i2c_remove, .id_table = gb_i2c_id_table, }; -module_gpbridge_driver(i2c_driver); +module_gbphy_driver(i2c_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 3b5669375ed3..e3e8e16943b5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -32,7 +32,7 @@ #define TOSHIBA_DMID 0x0126 #define TOSHIBA_ES2_BRIDGE_DPID 0x1000 #define TOSHIBA_ES3_APBRIDGE_DPID 0x1001 -#define TOSHIBA_ES3_GPBRIDGE_DPID 0x1002 +#define TOSHIBA_ES3_GBPHY_DPID 0x1002 static int gb_interface_dme_attr_get(struct gb_interface *intf, diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ccb2799c3b0a..702cf069eefd 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -86,7 +86,7 @@ struct gb_loopback { struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; struct gb_loopback_stats apbridge_unipro_latency; - struct gb_loopback_stats gpbridge_firmware_latency; + struct gb_loopback_stats gbphy_firmware_latency; int type; int async; @@ -106,7 +106,7 @@ struct gb_loopback { u32 lbid; u64 elapsed_nsecs; u32 apbridge_latency_ts; - u32 gpbridge_latency_ts; + u32 gbphy_latency_ts; u32 send_count; }; @@ -290,7 +290,7 @@ gb_loopback_stats_attrs(throughput); /* Latency across the UniPro link from APBridge's perspective */ gb_loopback_stats_attrs(apbridge_unipro_latency); /* Firmware induced overhead in the GPBridge */ -gb_loopback_stats_attrs(gpbridge_firmware_latency); +gb_loopback_stats_attrs(gbphy_firmware_latency); /* Number of errors encountered during loop */ gb_loopback_ro_attr(error); @@ -340,9 +340,9 @@ static struct attribute *loopback_attrs[] = { &dev_attr_apbridge_unipro_latency_min.attr, &dev_attr_apbridge_unipro_latency_max.attr, &dev_attr_apbridge_unipro_latency_avg.attr, - &dev_attr_gpbridge_firmware_latency_min.attr, - &dev_attr_gpbridge_firmware_latency_max.attr, - &dev_attr_gpbridge_firmware_latency_avg.attr, + &dev_attr_gbphy_firmware_latency_min.attr, + &dev_attr_gbphy_firmware_latency_max.attr, + &dev_attr_gbphy_firmware_latency_avg.attr, &dev_attr_type.attr, &dev_attr_size.attr, &dev_attr_us_wait.attr, @@ -670,7 +670,7 @@ static int gb_loopback_sync_transfer(struct gb_loopback *gb, u32 len) int retval; gb->apbridge_latency_ts = 0; - gb->gpbridge_latency_ts = 0; + gb->gbphy_latency_ts = 0; request = kmalloc(len + sizeof(*request), GFP_KERNEL); if (!request) @@ -696,7 +696,7 @@ static int gb_loopback_sync_transfer(struct gb_loopback *gb, u32 len) retval = -EREMOTEIO; } gb->apbridge_latency_ts = (u32)__le32_to_cpu(response->reserved0); - gb->gpbridge_latency_ts = (u32)__le32_to_cpu(response->reserved1); + gb->gbphy_latency_ts = (u32)__le32_to_cpu(response->reserved1); gb_error: kfree(request); @@ -752,7 +752,7 @@ static int gb_loopback_async_transfer_complete( } else { gb->apbridge_latency_ts = (u32)__le32_to_cpu(response->reserved0); - gb->gpbridge_latency_ts = + gb->gbphy_latency_ts = (u32)__le32_to_cpu(response->reserved1); } @@ -850,12 +850,12 @@ static void gb_loopback_reset_stats(struct gb_loopback *gb) sizeof(struct gb_loopback_stats)); memcpy(&gb->apbridge_unipro_latency, &reset, sizeof(struct gb_loopback_stats)); - memcpy(&gb->gpbridge_firmware_latency, &reset, + memcpy(&gb->gbphy_firmware_latency, &reset, sizeof(struct gb_loopback_stats)); /* Should be initialized at least once per transaction set */ gb->apbridge_latency_ts = 0; - gb->gpbridge_latency_ts = 0; + gb->gbphy_latency_ts = 0; memset(&gb->ts, 0, sizeof(struct timeval)); } @@ -931,8 +931,8 @@ static void gb_loopback_calculate_latency_stats(struct gb_loopback *gb) /* Log the firmware supplied latency values */ gb_loopback_update_stats(&gb->apbridge_unipro_latency, gb->apbridge_latency_ts); - gb_loopback_update_stats(&gb->gpbridge_firmware_latency, - gb->gpbridge_latency_ts); + gb_loopback_update_stats(&gb->gbphy_firmware_latency, + gb->gbphy_latency_ts); } static void gb_loopback_calculate_stats(struct gb_loopback *gb, bool error) diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 1c4ffb9a0ae1..77b05e895550 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -13,7 +13,7 @@ #include <linux/pwm.h> #include "greybus.h" -#include "gpbridge.h" +#include "gbphy.h" struct gb_pwm_chip { struct gb_connection *connection; @@ -178,8 +178,8 @@ static const struct pwm_ops gb_pwm_ops = { .owner = THIS_MODULE, }; -static int gb_pwm_probe(struct gpbridge_device *gpbdev, - const struct gpbridge_device_id *id) +static int gb_pwm_probe(struct gbphy_device *gbphy_dev, + const struct gbphy_device_id *id) { struct gb_connection *connection; struct gb_pwm_chip *pwmc; @@ -190,8 +190,8 @@ static int gb_pwm_probe(struct gpbridge_device *gpbdev, if (!pwmc) return -ENOMEM; - connection = gb_connection_create(gpbdev->bundle, - le16_to_cpu(gpbdev->cport_desc->id), + connection = gb_connection_create(gbphy_dev->bundle, + le16_to_cpu(gbphy_dev->cport_desc->id), NULL); if (IS_ERR(connection)) { ret = PTR_ERR(connection); @@ -200,13 +200,13 @@ static int gb_pwm_probe(struct gpbridge_device *gpbdev, pwmc->connection = connection; gb_connection_set_data(connection, pwmc); - gb_gpbridge_set_data(gpbdev, pwmc); + gb_gbphy_set_data(gbphy_dev, pwmc); ret = gb_connection_enable(connection); if (ret) goto exit_connection_destroy; - ret = gb_gpbridge_get_version(connection); + ret = gb_gbphy_get_version(connection); if (ret) goto exit_connection_disable; @@ -217,7 +217,7 @@ static int gb_pwm_probe(struct gpbridge_device *gpbdev, pwm = &pwmc->chip; - pwm->dev = &gpbdev->dev; + pwm->dev = &gbphy_dev->dev; pwm->ops = &gb_pwm_ops; pwm->base = -1; /* Allocate base dynamically */ pwm->npwm = pwmc->pwm_max + 1; @@ -225,7 +225,7 @@ static int gb_pwm_probe(struct gpbridge_device *gpbdev, ret = pwmchip_add(pwm); if (ret) { - dev_err(&gpbdev->dev, + dev_err(&gbphy_dev->dev, "failed to register PWM: %d\n", ret); goto exit_connection_disable; } @@ -241,9 +241,9 @@ exit_pwmc_free: return ret; } -static void gb_pwm_remove(struct gpbridge_device *gpbdev) +static void gb_pwm_remove(struct gbphy_device *gbphy_dev) { - struct gb_pwm_chip *pwmc = gb_gpbridge_get_data(gpbdev); + struct gb_pwm_chip *pwmc = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = pwmc->connection; pwmchip_remove(&pwmc->chip); @@ -252,18 +252,18 @@ static void gb_pwm_remove(struct gpbridge_device *gpbdev) kfree(pwmc); } -static const struct gpbridge_device_id gb_pwm_id_table[] = { - { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_PWM) }, +static const struct gbphy_device_id gb_pwm_id_table[] = { + { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_PWM) }, { }, }; -MODULE_DEVICE_TABLE(gpbridge, gb_pwm_id_table); +MODULE_DEVICE_TABLE(gbphy, gb_pwm_id_table); -static struct gpbridge_driver pwm_driver = { +static struct gbphy_driver pwm_driver = { .name = "pwm", .probe = gb_pwm_probe, .remove = gb_pwm_remove, .id_table = gb_pwm_id_table, }; -module_gpbridge_driver(pwm_driver); +module_gbphy_driver(pwm_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 7f063b4e4942..4d4cfdf8edb6 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -15,11 +15,11 @@ #include <linux/workqueue.h> #include "greybus.h" -#include "gpbridge.h" +#include "gbphy.h" struct gb_sdio_host { struct gb_connection *connection; - struct gpbridge_device *gpbdev; + struct gbphy_device *gbphy_dev; struct mmc_host *mmc; struct mmc_request *mrq; struct mutex lock; /* lock for this host */ @@ -708,8 +708,8 @@ static const struct mmc_host_ops gb_sdio_ops = { .get_cd = gb_mmc_get_cd, }; -static int gb_sdio_probe(struct gpbridge_device *gpbdev, - const struct gpbridge_device_id *id) +static int gb_sdio_probe(struct gbphy_device *gbphy_dev, + const struct gbphy_device_id *id) { struct gb_connection *connection; struct mmc_host *mmc; @@ -717,12 +717,12 @@ static int gb_sdio_probe(struct gpbridge_device *gpbdev, size_t max_buffer; int ret = 0; - mmc = mmc_alloc_host(sizeof(*host), &gpbdev->dev); + mmc = mmc_alloc_host(sizeof(*host), &gbphy_dev->dev); if (!mmc) return -ENOMEM; - connection = gb_connection_create(gpbdev->bundle, - le16_to_cpu(gpbdev->cport_desc->id), + connection = gb_connection_create(gbphy_dev->bundle, + le16_to_cpu(gbphy_dev->cport_desc->id), gb_sdio_request_handler); if (IS_ERR(connection)) { ret = PTR_ERR(connection); @@ -735,14 +735,14 @@ static int gb_sdio_probe(struct gpbridge_device *gpbdev, host->connection = connection; gb_connection_set_data(connection, host); - host->gpbdev = gpbdev; - gb_gpbridge_set_data(gpbdev, host); + host->gbphy_dev = gbphy_dev; + gb_gbphy_set_data(gbphy_dev, host); ret = gb_connection_enable_tx(connection); if (ret) goto exit_connection_destroy; - ret = gb_gpbridge_get_version(connection); + ret = gb_gbphy_get_version(connection); if (ret) goto exit_connection_disable; @@ -767,7 +767,7 @@ static int gb_sdio_probe(struct gpbridge_device *gpbdev, mutex_init(&host->lock); spin_lock_init(&host->xfer); host->mrq_workqueue = alloc_workqueue("mmc-%s", 0, 1, - dev_name(&gpbdev->dev)); + dev_name(&gbphy_dev->dev)); if (!host->mrq_workqueue) { ret = -ENOMEM; goto exit_buf_free; @@ -801,9 +801,9 @@ exit_mmc_free: return ret; } -static void gb_sdio_remove(struct gpbridge_device *gpbdev) +static void gb_sdio_remove(struct gbphy_device *gbphy_dev) { - struct gb_sdio_host *host = gb_gpbridge_get_data(gpbdev); + struct gb_sdio_host *host = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = host->connection; struct mmc_host *mmc; @@ -823,18 +823,18 @@ static void gb_sdio_remove(struct gpbridge_device *gpbdev) mmc_free_host(mmc); } -static const struct gpbridge_device_id gb_sdio_id_table[] = { - { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SDIO) }, +static const struct gbphy_device_id gb_sdio_id_table[] = { + { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_SDIO) }, { }, }; -MODULE_DEVICE_TABLE(gpbridge, gb_sdio_id_table); +MODULE_DEVICE_TABLE(gbphy, gb_sdio_id_table); -static struct gpbridge_driver sdio_driver = { +static struct gbphy_driver sdio_driver = { .name = "sdio", .probe = gb_sdio_probe, .remove = gb_sdio_remove, .id_table = gb_sdio_id_table, }; -module_gpbridge_driver(sdio_driver); +module_gbphy_driver(sdio_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 1cf5f509363c..4f13efeeb91e 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -10,17 +10,17 @@ #include <linux/module.h> #include "greybus.h" -#include "gpbridge.h" +#include "gbphy.h" #include "spilib.h" -static int gb_spi_probe(struct gpbridge_device *gpbdev, - const struct gpbridge_device_id *id) +static int gb_spi_probe(struct gbphy_device *gbphy_dev, + const struct gbphy_device_id *id) { struct gb_connection *connection; int ret; - connection = gb_connection_create(gpbdev->bundle, - le16_to_cpu(gpbdev->cport_desc->id), + connection = gb_connection_create(gbphy_dev->bundle, + le16_to_cpu(gbphy_dev->cport_desc->id), NULL); if (IS_ERR(connection)) return PTR_ERR(connection); @@ -29,15 +29,15 @@ static int gb_spi_probe(struct gpbridge_device *gpbdev, if (ret) goto exit_connection_destroy; - ret = gb_gpbridge_get_version(connection); + ret = gb_gbphy_get_version(connection); if (ret) goto exit_connection_disable; - ret = gb_spilib_master_init(connection, &gpbdev->dev); + ret = gb_spilib_master_init(connection, &gbphy_dev->dev); if (ret) goto exit_connection_disable; - gb_gpbridge_set_data(gpbdev, connection); + gb_gbphy_set_data(gbphy_dev, connection); return 0; @@ -49,27 +49,27 @@ exit_connection_destroy: return ret; } -static void gb_spi_remove(struct gpbridge_device *gpbdev) +static void gb_spi_remove(struct gbphy_device *gbphy_dev) { - struct gb_connection *connection = gb_gpbridge_get_data(gpbdev); + struct gb_connection *connection = gb_gbphy_get_data(gbphy_dev); gb_spilib_master_exit(connection); gb_connection_disable(connection); gb_connection_destroy(connection); } -static const struct gpbridge_device_id gb_spi_id_table[] = { - { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, +static const struct gbphy_device_id gb_spi_id_table[] = { + { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, { }, }; -MODULE_DEVICE_TABLE(gpbridge, gb_spi_id_table); +MODULE_DEVICE_TABLE(gbphy, gb_spi_id_table); -static struct gpbridge_driver spi_driver = { +static struct gbphy_driver spi_driver = { .name = "spi", .probe = gb_spi_probe, .remove = gb_spi_remove, .id_table = gb_spi_id_table, }; -module_gpbridge_driver(spi_driver); +module_gbphy_driver(spi_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 75fa527acf15..5d8f4e8095d9 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -29,7 +29,7 @@ #include <linux/kdev_t.h> #include "greybus.h" -#include "gpbridge.h" +#include "gbphy.h" #define GB_NUM_MINORS 16 /* 16 is is more than enough */ #define GB_NAME "ttyGB" @@ -42,7 +42,7 @@ struct gb_tty_line_coding { }; struct gb_tty { - struct gpbridge_device *gpbdev; + struct gbphy_device *gbphy_dev; struct tty_port port; void *buffer; size_t buffer_payload_max; @@ -78,7 +78,7 @@ static int gb_uart_receive_data_handler(struct gb_operation *op) unsigned long tty_flags = TTY_NORMAL; if (request->payload_size < sizeof(*receive_data)) { - dev_err(&gb_tty->gpbdev->dev, + dev_err(&gb_tty->gbphy_dev->dev, "short receive-data request received (%zu < %zu)\n", request->payload_size, sizeof(*receive_data)); return -EINVAL; @@ -88,7 +88,7 @@ static int gb_uart_receive_data_handler(struct gb_operation *op) recv_data_size = le16_to_cpu(receive_data->size); if (recv_data_size != request->payload_size - sizeof(*receive_data)) { - dev_err(&gb_tty->gpbdev->dev, + dev_err(&gb_tty->gbphy_dev->dev, "malformed receive-data request received (%u != %zu)\n", recv_data_size, request->payload_size - sizeof(*receive_data)); @@ -113,7 +113,7 @@ static int gb_uart_receive_data_handler(struct gb_operation *op) count = tty_insert_flip_string_fixed_flag(port, receive_data->data, tty_flags, recv_data_size); if (count != recv_data_size) { - dev_err(&gb_tty->gpbdev->dev, + dev_err(&gb_tty->gbphy_dev->dev, "UART: RX 0x%08x bytes only wrote 0x%08x\n", recv_data_size, count); } @@ -130,7 +130,7 @@ static int gb_uart_serial_state_handler(struct gb_operation *op) struct gb_uart_serial_state_request *serial_state; if (request->payload_size < sizeof(*serial_state)) { - dev_err(&gb_tty->gpbdev->dev, + dev_err(&gb_tty->gbphy_dev->dev, "short serial-state event received (%zu < %zu)\n", request->payload_size, sizeof(*serial_state)); return -EINVAL; @@ -157,7 +157,7 @@ static int gb_uart_request_handler(struct gb_operation *op) ret = gb_uart_serial_state_handler(op); break; default: - dev_err(&gb_tty->gpbdev->dev, + dev_err(&gb_tty->gbphy_dev->dev, "unsupported unsolicited request: 0x%02x\n", type); ret = -EINVAL; } @@ -211,7 +211,7 @@ static int send_break(struct gb_tty *gb_tty, u8 state) struct gb_uart_set_break_request request; if ((state != 0) && (state != 1)) { - dev_err(&gb_tty->gpbdev->dev, + dev_err(&gb_tty->gbphy_dev->dev, "invalid break state of %d\n", state); return -EINVAL; } @@ -619,8 +619,8 @@ static const struct tty_operations gb_ops = { static struct tty_port_operations null_ops = { }; -static int gb_uart_probe(struct gpbridge_device *gpbdev, - const struct gpbridge_device_id *id) +static int gb_uart_probe(struct gbphy_device *gbphy_dev, + const struct gbphy_device_id *id) { struct gb_connection *connection; size_t max_payload; @@ -633,8 +633,8 @@ static int gb_uart_probe(struct gpbridge_device *gpbdev, if (!gb_tty) return -ENOMEM; - connection = gb_connection_create(gpbdev->bundle, - le16_to_cpu(gpbdev->cport_desc->id), + connection = gb_connection_create(gbphy_dev->bundle, + le16_to_cpu(gbphy_dev->cport_desc->id), gb_uart_request_handler); if (IS_ERR(connection)) { retval = PTR_ERR(connection); @@ -678,15 +678,15 @@ static int gb_uart_probe(struct gpbridge_device *gpbdev, gb_tty->port.ops = &null_ops; gb_tty->connection = connection; - gb_tty->gpbdev = gpbdev; + gb_tty->gbphy_dev = gbphy_dev; gb_connection_set_data(connection, gb_tty); - gb_gpbridge_set_data(gpbdev, gb_tty); + gb_gbphy_set_data(gbphy_dev, gb_tty); retval = gb_connection_enable_tx(connection); if (retval) goto exit_release_minor; - retval = gb_gpbridge_get_version(connection); + retval = gb_gbphy_get_version(connection); if (retval) goto exit_connection_disable; @@ -704,7 +704,7 @@ static int gb_uart_probe(struct gpbridge_device *gpbdev, goto exit_connection_disable; tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, - &gpbdev->dev); + &gbphy_dev->dev); if (IS_ERR(tty_dev)) { retval = PTR_ERR(tty_dev); goto exit_connection_disable; @@ -727,9 +727,9 @@ exit_tty_free: return retval; } -static void gb_uart_remove(struct gpbridge_device *gpbdev) +static void gb_uart_remove(struct gbphy_device *gbphy_dev) { - struct gb_tty *gb_tty = gb_gpbridge_get_data(gpbdev); + struct gb_tty *gb_tty = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = gb_tty->connection; struct tty_struct *tty; @@ -800,13 +800,13 @@ static void gb_tty_exit(void) idr_destroy(&tty_minors); } -static const struct gpbridge_device_id gb_uart_id_table[] = { - { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_UART) }, +static const struct gbphy_device_id gb_uart_id_table[] = { + { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_UART) }, { }, }; -MODULE_DEVICE_TABLE(gpbridge, gb_uart_id_table); +MODULE_DEVICE_TABLE(gbphy, gb_uart_id_table); -static struct gpbridge_driver uart_driver = { +static struct gbphy_driver uart_driver = { .name = "uart", .probe = gb_uart_probe, .remove = gb_uart_remove, @@ -821,7 +821,7 @@ static int gb_uart_driver_init(void) if (ret) return ret; - ret = gb_gpbridge_register(&uart_driver); + ret = gb_gbphy_register(&uart_driver); if (ret) { gb_tty_exit(); return ret; @@ -833,7 +833,7 @@ module_init(gb_uart_driver_init); static void gb_uart_driver_exit(void) { - gb_gpbridge_deregister(&uart_driver); + gb_gbphy_deregister(&uart_driver); gb_tty_exit(); } diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 2f68a1b49e3c..7ed6a60c1d56 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -14,7 +14,7 @@ #include <linux/usb/hcd.h> #include "greybus.h" -#include "gpbridge.h" +#include "gbphy.h" /* Version of the Greybus USB protocol we support */ #define GB_USB_VERSION_MAJOR 0x00 @@ -38,7 +38,7 @@ struct gb_usb_hub_control_response { struct gb_usb_device { struct gb_connection *connection; - struct gpbridge_device *gpbdev; + struct gbphy_device *gbphy_dev; }; static inline struct gb_usb_device *to_gb_usb_device(struct usb_hcd *hcd) @@ -59,7 +59,7 @@ static void hcd_stop(struct usb_hcd *hcd) ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, NULL, 0, NULL, 0); if (ret) - dev_err(&dev->gpbdev->dev, "HCD stop failed '%d'\n", ret); + dev_err(&dev->gbphy_dev->dev, "HCD stop failed '%d'\n", ret); } static int hcd_start(struct usb_hcd *hcd) @@ -71,7 +71,7 @@ static int hcd_start(struct usb_hcd *hcd) ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, NULL, 0, NULL, 0); if (ret) { - dev_err(&dev->gpbdev->dev, "HCD start failed '%d'\n", ret); + dev_err(&dev->gbphy_dev->dev, "HCD start failed '%d'\n", ret); return ret; } @@ -161,11 +161,11 @@ static struct hc_driver usb_gb_hc_driver = { .hub_control = hub_control, }; -static int gb_usb_probe(struct gpbridge_device *gpbdev, - const struct gpbridge_device_id *id) +static int gb_usb_probe(struct gbphy_device *gbphy_dev, + const struct gbphy_device_id *id) { struct gb_connection *connection; - struct device *dev = &gpbdev->dev; + struct device *dev = &gbphy_dev->dev; struct gb_usb_device *gb_usb_dev; struct usb_hcd *hcd; int retval; @@ -174,8 +174,8 @@ static int gb_usb_probe(struct gpbridge_device *gpbdev, if (!hcd) return -ENOMEM; - connection = gb_connection_create(gpbdev->bundle, - le16_to_cpu(gpbdev->cport_desc->id), + connection = gb_connection_create(gbphy_dev->bundle, + le16_to_cpu(gbphy_dev->cport_desc->id), NULL); if (IS_ERR(connection)) { retval = PTR_ERR(connection); @@ -185,8 +185,8 @@ static int gb_usb_probe(struct gpbridge_device *gpbdev, gb_usb_dev = to_gb_usb_device(hcd); gb_usb_dev->connection = connection; gb_connection_set_data(connection, gb_usb_dev); - gb_usb_dev->gpbdev = gpbdev; - gb_gpbridge_set_data(gpbdev, gb_usb_dev); + gb_usb_dev->gbphy_dev = gbphy_dev; + gb_gbphy_set_data(gbphy_dev, gb_usb_dev); hcd->has_tt = 1; @@ -194,7 +194,7 @@ static int gb_usb_probe(struct gpbridge_device *gpbdev, if (retval) goto exit_connection_destroy; - retval = gb_gpbridge_get_version(connection); + retval = gb_gbphy_get_version(connection); if (retval) goto exit_connection_disable; @@ -226,9 +226,9 @@ exit_usb_put: return retval; } -static void gb_usb_remove(struct gpbridge_device *gpbdev) +static void gb_usb_remove(struct gbphy_device *gbphy_dev) { - struct gb_usb_device *gb_usb_dev = gb_gpbridge_get_data(gpbdev); + struct gb_usb_device *gb_usb_dev = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = gb_usb_dev->connection; struct usb_hcd *hcd = gb_usb_device_to_hcd(gb_usb_dev); @@ -238,18 +238,18 @@ static void gb_usb_remove(struct gpbridge_device *gpbdev) usb_put_hcd(hcd); } -static const struct gpbridge_device_id gb_usb_id_table[] = { - { GPBRIDGE_PROTOCOL(GREYBUS_PROTOCOL_USB) }, +static const struct gbphy_device_id gb_usb_id_table[] = { + { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_USB) }, { }, }; -MODULE_DEVICE_TABLE(gpbridge, gb_usb_id_table); +MODULE_DEVICE_TABLE(gbphy, gb_usb_id_table); -static struct gpbridge_driver usb_driver = { +static struct gbphy_driver usb_driver = { .name = "usb", .probe = gb_usb_probe, .remove = gb_usb_remove, .id_table = gb_usb_id_table, }; -module_gpbridge_driver(usb_driver); +module_gbphy_driver(usb_driver); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 16fd976c392bf2e9b88531e842d8d255bccddc98 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Tue, 17 May 2016 22:39:46 +0530 Subject: greybus: arche-platform: Do not assert wake/detect signal to SVC With new definition of AP module boot flow (from HotPlug camp), AP is not supposed to send any wake/detect signal to SVC, instead, during boot SVC would straight away send wake_out pulse on wake/detect line. Note that, pin configuration of wake/detect line would be set to active-high by default, so wake/detect line would always stay high, unless SVC drives it. AP module uses wake/detect line strictly in input mode. Testing Done: Tested on EVT1.5 platform. Note: We are yet to decide on PM support for APBx, so we may need to generate/handshake with SVC over wake/detect line in the future. As of now, follow the implementation and add stuff as and when they come. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index e3854349b36c..c02ff58ddefe 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -207,9 +207,6 @@ static int apb_poweroff(struct device *dev, void *data) static void assert_wakedetect(struct arche_platform_drvdata *arche_pdata) { - /* Assert wake/detect = Detect event from AP */ - gpio_direction_output(arche_pdata->wake_detect_gpio, 1); - /* Enable interrupt here, to read event back from SVC */ gpio_direction_input(arche_pdata->wake_detect_gpio); enable_irq(arche_pdata->wake_detect_irq); @@ -371,9 +368,7 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda /* If in fw_flashing mode, then no need to repeate things again */ if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { disable_irq(arche_pdata->wake_detect_irq); - /* Send disconnect/detach event to SVC */ - gpio_direction_output(arche_pdata->wake_detect_gpio, 0); - usleep_range(100, 200); + spin_lock_irqsave(&arche_pdata->wake_lock, flags); arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); @@ -411,9 +406,9 @@ static ssize_t state_store(struct device *dev, if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) goto exit; + assert_wakedetect(arche_pdata); ret = arche_platform_coldboot_seq(arche_pdata); - assert_wakedetect(arche_pdata); } else if (sysfs_streq(buf, "standby")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) goto exit; @@ -484,8 +479,8 @@ static int arche_platform_pm_notifier(struct notifier_block *notifier, arche_platform_poweroff_seq(arche_pdata); break; case PM_POST_SUSPEND: - arche_platform_coldboot_seq(arche_pdata); assert_wakedetect(arche_pdata); + arche_platform_coldboot_seq(arche_pdata); break; default: break; @@ -588,8 +583,7 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->wake_detect_gpio); return ret; } - /* deassert wake detect */ - gpio_direction_output(arche_pdata->wake_detect_gpio, 0); + arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); arche_pdata->dev = &pdev->dev; @@ -608,8 +602,8 @@ static int arche_platform_probe(struct platform_device *pdev) dev_err(dev, "failed to request wake detect IRQ %d\n", ret); return ret; } - /* Enable it only after sending wake/detect event */ - disable_irq(arche_pdata->wake_detect_irq); + + assert_wakedetect(arche_pdata); ret = device_create_file(dev, &dev_attr_state); if (ret) { @@ -630,8 +624,6 @@ static int arche_platform_probe(struct platform_device *pdev) goto err_populate; } - assert_wakedetect(arche_pdata); - arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; ret = register_pm_notifier(&arche_pdata->pm_notifier); mutex_unlock(&arche_pdata->platform_state_mutex); -- cgit v1.2.3-59-g8ed1b From 7ba535ec69ff670976e69adeec24dac2b65560ee Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Tue, 17 May 2016 22:39:47 +0530 Subject: greybus: arche-platform: Rename assert_wakedetect=>arche_platform_wd_irq_en Now, since AP module does not send any signal to SVC, so it automatically restricts the wake/detect gpio to input. So rename assert_wakedetect() fn to arche_platform_wd_irq_en(), as per implementation. Testing Done: Tested on EVT1.5 platform. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index c02ff58ddefe..bc450770771f 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -205,7 +205,7 @@ static int apb_poweroff(struct device *dev, void *data) return 0; } -static void assert_wakedetect(struct arche_platform_drvdata *arche_pdata) +static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata) { /* Enable interrupt here, to read event back from SVC */ gpio_direction_input(arche_pdata->wake_detect_gpio); @@ -406,7 +406,7 @@ static ssize_t state_store(struct device *dev, if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) goto exit; - assert_wakedetect(arche_pdata); + arche_platform_wd_irq_en(arche_pdata); ret = arche_platform_coldboot_seq(arche_pdata); } else if (sysfs_streq(buf, "standby")) { @@ -479,7 +479,7 @@ static int arche_platform_pm_notifier(struct notifier_block *notifier, arche_platform_poweroff_seq(arche_pdata); break; case PM_POST_SUSPEND: - assert_wakedetect(arche_pdata); + arche_platform_wd_irq_en(arche_pdata); arche_platform_coldboot_seq(arche_pdata); break; default: @@ -603,7 +603,7 @@ static int arche_platform_probe(struct platform_device *pdev) return ret; } - assert_wakedetect(arche_pdata); + arche_platform_wd_irq_en(arche_pdata); ret = device_create_file(dev, &dev_attr_state); if (ret) { -- cgit v1.2.3-59-g8ed1b From 0bba4fb53adff8ee1538a654bfb5ac79298cc5dc Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 19 May 2016 16:20:14 +0200 Subject: greybus: svc: fix another pwrmon return value Errno -ENOSYS is reserved for missing syscalls, replace it with -ENOMSG. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 80e8cf04ade9..3e22ed8b691b 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -202,7 +202,7 @@ int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, case GB_SVC_PWRMON_GET_SAMPLE_INVAL: return -EINVAL; case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: - return -ENOSYS; + return -ENOMSG; default: return -EIO; } -- cgit v1.2.3-59-g8ed1b From deda6aaf666706346866b7d828098ba9bacfb742 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 19 May 2016 16:20:15 +0200 Subject: greybus: audio_apbridgea: fix two return values Errno -ENOSYS is reserved for missing syscalls, replace it with -EOPNOTSUPP for the the two stub operations that used it. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_apbridgea.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio_apbridgea.c b/drivers/staging/greybus/audio_apbridgea.c index a6e089d2d1a8..361470788a76 100644 --- a/drivers/staging/greybus/audio_apbridgea.c +++ b/drivers/staging/greybus/audio_apbridgea.c @@ -78,7 +78,7 @@ int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, __u16 i2s_port, __u32 *delay) { /* TODO: implement */ - return -ENOSYS; + return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(gb_audio_apbridgea_get_tx_delay); @@ -152,7 +152,7 @@ int gb_audio_apbridgea_get_rx_delay(struct gb_connection *connection, __u16 i2s_port, __u32 *delay) { /* TODO: implement */ - return -ENOSYS; + return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(gb_audio_apbridgea_get_rx_delay); -- cgit v1.2.3-59-g8ed1b From 4aea5a15ad6d8fbd9d344a84c0396176e7512d3d Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 19 May 2016 16:20:16 +0200 Subject: greybus: svc: use EREMOTEIO for remote errors Return -EREMOTEIO consistently for unspecified remote errors (e.g. a failed DME attribute read due to a remote UniPro error). Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 3e22ed8b691b..78cc0e38828d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -166,7 +166,7 @@ static int gb_svc_pwrmon_sample_get(struct gb_svc *svc, u8 rail_id, case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: return -ENOMSG; default: - return -EIO; + return -EREMOTEIO; } } @@ -204,7 +204,7 @@ int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: return -ENOMSG; default: - return -EIO; + return -EREMOTEIO; } } @@ -376,7 +376,7 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, if (result) { dev_err(&svc->dev, "UniPro error while getting DME attribute (%u 0x%04x %u): %u\n", intf_id, attr, selector, result); - return -EIO; + return -EREMOTEIO; } if (value) @@ -412,7 +412,7 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, if (result) { dev_err(&svc->dev, "UniPro error while setting DME attribute (%u 0x%04x %u %u): %u\n", intf_id, attr, selector, value, result); - return -EIO; + return -EREMOTEIO; } return 0; -- cgit v1.2.3-59-g8ed1b From 825f79ae7620fa98586c4d2504030eca96d55c59 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 19 May 2016 18:27:25 +0530 Subject: greybus: fw-core: destroy connections on error In one of the error cases we aren't destroying the connections created earlier. Fix it. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c index 90d32227a490..33941efe13ea 100644 --- a/drivers/staging/greybus/fw-core.c +++ b/drivers/staging/greybus/fw-core.c @@ -139,7 +139,7 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, dev_err(&bundle->dev, "invalid protocol id (0x%02x)\n", protocol_id); ret = -EINVAL; - goto err_free_fw_core; + goto err_destroy_connections; } } @@ -187,7 +187,6 @@ err_destroy_connections: gb_connection_destroy(fw_core->mgmt_connection); gb_connection_destroy(fw_core->spi_connection); gb_connection_destroy(fw_core->download_connection); -err_free_fw_core: kfree(fw_core); return ret; -- cgit v1.2.3-59-g8ed1b From d72b5810cba5fd6f77f22742df1cb4a3c473cd6b Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 19 May 2016 18:26:27 +0530 Subject: greybus: fw-management: Fix error message's text Just reword it to make it sound better. Compile tested. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-management.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index 96758f417d7b..da36de313a2c 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -352,7 +352,7 @@ static int fw_mgmt_backend_fw_updated_operation(struct gb_operation *op) if (fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_SUCCESS) dev_err(fw_mgmt->parent, - "failed to backend load firmware, status:%02x\n", + "failed to load backend firmware: %02x\n", fw_mgmt->backend_fw_status); complete(&fw_mgmt->completion); -- cgit v1.2.3-59-g8ed1b From 68b66c28010ebc2ebe21d6bad2de2eda1bed01fc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Date: Fri, 20 May 2016 14:33:35 +0300 Subject: greybus: camera: Convert to bundle driver Convert the legacy camera protocol driver to a bundle driver. Modules now can (and must) declare the camera data cport in their manifest as the data connection isn't hardcoded anymore. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 123 ++++++++++++++++++++++++++------------- drivers/staging/greybus/legacy.c | 1 - 2 files changed, 84 insertions(+), 40 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 4831ad652c04..dd482bd94637 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -36,11 +36,11 @@ struct gb_camera_debugfs_buffer { /** * struct gb_camera - A Greybus Camera Device * @connection: the greybus connection for camera control - * @data_connected: whether the data connection has been established * @debugfs: debugfs entries for camera protocol operations testing * @module: Greybus camera module registered to HOST processor. */ struct gb_camera { + struct gb_bundle *bundle; struct gb_connection *connection; struct gb_connection *data_connection; @@ -103,12 +103,9 @@ static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { #define GB_CAMERA_MAX_SETTINGS_SIZE 8192 -#define gcam_dbg(gcam, format...) \ - dev_dbg(&gcam->connection->bundle->dev, format) -#define gcam_info(gcam, format...) \ - dev_info(&gcam->connection->bundle->dev, format) -#define gcam_err(gcam, format...) \ - dev_err(&gcam->connection->bundle->dev, format) +#define gcam_dbg(gcam, format...) dev_dbg(&gcam->bundle->dev, format) +#define gcam_info(gcam, format...) dev_info(&gcam->bundle->dev, format) +#define gcam_err(gcam, format...) dev_err(&gcam->bundle->dev, format) /* ----------------------------------------------------------------------------- * Camera Protocol Operations @@ -383,14 +380,14 @@ static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) return 0; } -static int gb_camera_event_recv(u8 type, struct gb_operation *op) +static int gb_camera_request_handler(struct gb_operation *op) { struct gb_camera *gcam = gb_connection_get_data(op->connection); struct gb_camera_metadata_request *payload; struct gb_message *request; - if (type != GB_CAMERA_TYPE_METADATA) { - gcam_err(gcam, "Unsupported unsolicited event: %u\n", type); + if (op->type != GB_CAMERA_TYPE_METADATA) { + gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type); return -EINVAL; } @@ -849,7 +846,7 @@ static int gb_camera_debugfs_init(struct gb_camera *gcam) * Create root debugfs entry and a file entry for each camera operation. */ snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, - connection->bundle->id); + gcam->bundle->id); gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); if (IS_ERR(gcam->debugfs.root)) { @@ -905,39 +902,84 @@ static void gb_camera_cleanup(struct gb_camera *gcam) gb_connection_destroy(gcam->data_connection); } + if (gcam->connection) { + gb_connection_disable(gcam->connection); + gb_connection_destroy(gcam->connection); + } + kfree(gcam); } -static int gb_camera_connection_init(struct gb_connection *connection) +static int gb_camera_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) { - struct gb_connection *data; + struct gb_connection *conn; struct gb_camera *gcam; - unsigned long data_flags; + u16 mgmt_cport_id = 0; + u16 data_cport_id = 0; + unsigned int i; int ret; + /* + * The camera bundle must contain exactly two CPorts, one for the + * camera management protocol and one for the camera data protocol. + */ + if (bundle->num_cports != 2) + return -ENODEV; + + for (i = 0; i < bundle->num_cports; ++i) { + struct greybus_descriptor_cport *desc = &bundle->cport_desc[i]; + + switch (desc->protocol_id) { + case GREYBUS_PROTOCOL_CAMERA_MGMT: + mgmt_cport_id = le16_to_cpu(desc->id); + break; + case GREYBUS_PROTOCOL_CAMERA_DATA: + data_cport_id = le16_to_cpu(desc->id); + break; + default: + return -ENODEV; + } + } + + if (!mgmt_cport_id || !data_cport_id) + return -ENODEV; + gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); if (!gcam) return -ENOMEM; - gcam->connection = connection; - gb_connection_set_data(connection, gcam); + gcam->bundle = bundle; + + conn = gb_connection_create(bundle, mgmt_cport_id, + gb_camera_request_handler); + if (IS_ERR(conn)) { + ret = PTR_ERR(conn); + goto error; + } + + gcam->connection = conn; + gb_connection_set_data(conn, gcam); + + ret = gb_connection_enable(conn); + if (ret) + goto error; /* - * Create the data connection between camera module CDSI0 and APB CDS1. - * The CPort IDs are hardcoded by the ES2 bridges. + * Create the data connection between the camera module data CPort and + * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. */ - data_flags = GB_CONNECTION_FLAG_NO_FLOWCTRL | GB_CONNECTION_FLAG_CDSI1; - - data = gb_connection_create_offloaded(connection->bundle, - ES2_APB_CDSI0_CPORT, - data_flags); - if (IS_ERR(data)) { - ret = PTR_ERR(data); + conn = gb_connection_create_offloaded(bundle, data_cport_id, + GB_CONNECTION_FLAG_NO_FLOWCTRL | + GB_CONNECTION_FLAG_CDSI1); + if (IS_ERR(conn)) { + ret = PTR_ERR(conn); goto error; } - gcam->data_connection = data; + gcam->data_connection = conn; + gb_connection_set_data(conn, gcam); - ret = gb_connection_enable(data); + ret = gb_connection_enable(conn); if (ret) goto error; @@ -949,6 +991,8 @@ static int gb_camera_connection_init(struct gb_connection *connection) if (ret < 0) goto error; + greybus_set_drvdata(bundle, gcam); + return 0; error: @@ -956,25 +1000,26 @@ error: return ret; } -static void gb_camera_connection_exit(struct gb_connection *connection) +static void gb_camera_disconnect(struct gb_bundle *bundle) { - struct gb_camera *gcam = gb_connection_get_data(connection); + struct gb_camera *gcam = greybus_get_drvdata(bundle); gb_camera_unregister_intf_ops(gcam); - gb_camera_cleanup(gcam); } -static struct gb_protocol camera_protocol = { - .name = "camera", - .id = GREYBUS_PROTOCOL_CAMERA_MGMT, - .major = GB_CAMERA_VERSION_MAJOR, - .minor = GB_CAMERA_VERSION_MINOR, - .connection_init = gb_camera_connection_init, - .connection_exit = gb_camera_connection_exit, - .request_recv = gb_camera_event_recv, +static const struct greybus_bundle_id gb_camera_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, + { }, +}; + +static struct greybus_driver gb_camera_driver = { + .name = "camera", + .probe = gb_camera_probe, + .disconnect = gb_camera_disconnect, + .id_table = gb_camera_id_table, }; -gb_protocol_driver(&camera_protocol); +module_greybus_driver(gb_camera_driver); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c index 06bd85cc4459..19f3d93abfed 100644 --- a/drivers/staging/greybus/legacy.c +++ b/drivers/staging/greybus/legacy.c @@ -236,7 +236,6 @@ static void legacy_disconnect(struct gb_bundle *bundle) } static const struct greybus_bundle_id legacy_id_table[] = { - { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, { } }; MODULE_DEVICE_TABLE(greybus, legacy_id_table); -- cgit v1.2.3-59-g8ed1b From 428888a2b672444dbab9bb5127b7f583ab58ff2f Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Fri, 20 May 2016 14:33:36 +0300 Subject: greybus: legacy: remove legacy driver support This patch removes the greybus legacy driver support Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 3 +- drivers/staging/greybus/connection.h | 1 - drivers/staging/greybus/core.c | 10 -- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/legacy.c | 258 ----------------------------------- drivers/staging/greybus/legacy.h | 16 --- drivers/staging/greybus/protocol.c | 201 --------------------------- drivers/staging/greybus/protocol.h | 75 ---------- 8 files changed, 1 insertion(+), 564 deletions(-) delete mode 100644 drivers/staging/greybus/legacy.c delete mode 100644 drivers/staging/greybus/legacy.h delete mode 100644 drivers/staging/greybus/protocol.c delete mode 100644 drivers/staging/greybus/protocol.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index ae9a478d3dde..6d07b1bcd21b 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -11,8 +11,7 @@ greybus-y := core.o \ svc.o \ svc_watchdog.o \ bootrom.o \ - operation.o \ - legacy.o + operation.o gb-gbphy-y := gbphy.o diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index b152e2340591..53ce28452da4 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -43,7 +43,6 @@ struct gb_connection { gb_request_handler_t handler; unsigned long flags; - struct gb_protocol *protocol; u8 module_major; u8 module_minor; diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index a44846dd5d79..6d6a2bbb591a 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -13,7 +13,6 @@ #include "bootrom.h" #include "greybus.h" #include "greybus_trace.h" -#include "legacy.h" EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_send); EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_recv); @@ -266,16 +265,8 @@ static int __init gb_init(void) goto error_bootrom; } - retval = gb_legacy_init(); - if (retval) { - pr_err("gb_legacy_init failed\n"); - goto error_legacy; - } - return 0; /* Success */ -error_legacy: - gb_bootrom_exit(); error_bootrom: gb_operation_exit(); error_operation: @@ -291,7 +282,6 @@ module_init(gb_init); static void __exit gb_exit(void) { - gb_legacy_exit(); gb_bootrom_exit(); gb_operation_exit(); gb_hd_exit(); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index f5447e7592e9..e0b194c097a8 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -32,7 +32,6 @@ #include "interface.h" #include "bundle.h" #include "connection.h" -#include "protocol.h" #include "operation.h" diff --git a/drivers/staging/greybus/legacy.c b/drivers/staging/greybus/legacy.c deleted file mode 100644 index 19f3d93abfed..000000000000 --- a/drivers/staging/greybus/legacy.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Greybus legacy-protocol driver - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#include "greybus.h" -#include "legacy.h" -#include "protocol.h" - - -struct legacy_connection { - struct gb_connection *connection; - bool initialized; - struct gb_protocol *protocol; -}; - -struct legacy_data { - size_t num_cports; - struct legacy_connection *connections; -}; - - -static int legacy_connection_get_version(struct gb_connection *connection) -{ - int ret; - - ret = gb_protocol_get_version(connection); - if (ret) { - dev_err(&connection->hd->dev, - "%s: failed to get protocol version: %d\n", - connection->name, ret); - return ret; - } - - return 0; -} - -static int legacy_request_handler(struct gb_operation *operation) -{ - struct gb_protocol *protocol = operation->connection->protocol; - - return protocol->request_recv(operation->type, operation); -} - -static int legacy_connection_init(struct legacy_connection *lc) -{ - struct gb_connection *connection = lc->connection; - int ret; - - dev_dbg(&connection->bundle->dev, "%s - %s\n", __func__, - connection->name); - - ret = gb_connection_enable(connection); - if (ret) - return ret; - - ret = legacy_connection_get_version(connection); - if (ret) - goto err_disable; - - ret = connection->protocol->connection_init(connection); - if (ret) - goto err_disable; - - lc->initialized = true; - - return 0; - -err_disable: - gb_connection_disable(connection); - - return ret; -} - -static void legacy_connection_exit(struct legacy_connection *lc) -{ - struct gb_connection *connection = lc->connection; - - if (!lc->initialized) - return; - - gb_connection_disable(connection); - - connection->protocol->connection_exit(connection); - - lc->initialized = false; -} - -static int legacy_connection_create(struct legacy_connection *lc, - struct gb_bundle *bundle, - struct greybus_descriptor_cport *desc) -{ - struct gb_connection *connection; - struct gb_protocol *protocol; - gb_request_handler_t handler; - u8 major, minor; - int ret; - - /* - * The legacy protocols have always been looked up using a hard-coded - * version of 0.1, despite (or perhaps rather, due to) the fact that - * module version negotiation could not take place until after the - * protocol was bound. - */ - major = 0; - minor = 1; - - protocol = gb_protocol_get(desc->protocol_id, major, minor); - if (!protocol) { - dev_err(&bundle->dev, - "protocol 0x%02x version %u.%u not found\n", - desc->protocol_id, major, minor); - return -EPROTONOSUPPORT; - } - - if (protocol->request_recv) - handler = legacy_request_handler; - else - handler = NULL; - - connection = gb_connection_create(bundle, le16_to_cpu(desc->id), - handler); - if (IS_ERR(connection)) { - ret = PTR_ERR(connection); - goto err_protocol_put; - } - - /* - * NOTE: We need to keep a pointer to the protocol in the actual - * connection structure for now. - */ - connection->protocol = protocol; - - lc->connection = connection; - lc->protocol = protocol; - - return 0; - -err_protocol_put: - gb_protocol_put(protocol); - - return ret; -} - -static void legacy_connection_destroy(struct legacy_connection *lc) -{ - if (!lc->connection) - return; - - lc->connection->protocol = NULL; - - gb_connection_destroy(lc->connection); - - gb_protocol_put(lc->protocol); -} - -static int legacy_probe(struct gb_bundle *bundle, - const struct greybus_bundle_id *id) -{ - struct greybus_descriptor_cport *cport_desc; - struct legacy_data *data; - struct legacy_connection *lc; - int i; - int ret; - - dev_dbg(&bundle->dev, - "%s - bundle class = 0x%02x, num_cports = %zu\n", - __func__, bundle->class, bundle->num_cports); - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->num_cports = bundle->num_cports; - data->connections = kcalloc(data->num_cports, - sizeof(*data->connections), - GFP_KERNEL); - if (!data->connections) { - ret = -ENOMEM; - goto err_free_data; - } - - for (i = 0; i < data->num_cports; ++i) { - cport_desc = &bundle->cport_desc[i]; - lc = &data->connections[i]; - - ret = legacy_connection_create(lc, bundle, cport_desc); - if (ret) - goto err_connections_destroy; - } - - greybus_set_drvdata(bundle, data); - - for (i = 0; i < data->num_cports; ++i) { - lc = &data->connections[i]; - - ret = legacy_connection_init(lc); - if (ret) - goto err_connections_disable; - } - - return 0; - -err_connections_disable: - for (--i; i >= 0; --i) - legacy_connection_exit(&data->connections[i]); -err_connections_destroy: - for (i = 0; i < data->num_cports; ++i) - legacy_connection_destroy(&data->connections[i]); - kfree(data->connections); -err_free_data: - kfree(data); - - return ret; -} - -static void legacy_disconnect(struct gb_bundle *bundle) -{ - struct legacy_data *data = greybus_get_drvdata(bundle); - int i; - - dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__, - bundle->class); - - for (i = 0; i < data->num_cports; ++i) { - legacy_connection_exit(&data->connections[i]); - legacy_connection_destroy(&data->connections[i]); - } - - kfree(data->connections); - kfree(data); -} - -static const struct greybus_bundle_id legacy_id_table[] = { - { } -}; -MODULE_DEVICE_TABLE(greybus, legacy_id_table); - -static struct greybus_driver legacy_driver = { - .name = "legacy", - .probe = legacy_probe, - .disconnect = legacy_disconnect, - .id_table = legacy_id_table, -}; - -int gb_legacy_init(void) -{ - return greybus_register(&legacy_driver); -} - -void gb_legacy_exit(void) -{ - greybus_deregister(&legacy_driver); -} diff --git a/drivers/staging/greybus/legacy.h b/drivers/staging/greybus/legacy.h deleted file mode 100644 index 2177f6aaed3c..000000000000 --- a/drivers/staging/greybus/legacy.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Greybus legacy-protocol driver - * - * Copyright 2015 Google Inc. - * Copyright 2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __LEGACY_H -#define __LEGACY_H - -int gb_legacy_init(void); -void gb_legacy_exit(void); - -#endif /* __LEGACY_H */ diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c deleted file mode 100644 index 057ab6057ee6..000000000000 --- a/drivers/staging/greybus/protocol.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Greybus protocol handling - * - * Copyright 2014-2015 Google Inc. - * Copyright 2014-2015 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "greybus.h" - -/* Global list of registered protocols */ -static DEFINE_SPINLOCK(gb_protocols_lock); -static LIST_HEAD(gb_protocols); - -/* Caller must hold gb_protocols_lock */ -static struct gb_protocol *gb_protocol_find(u8 id, u8 major, u8 minor) -{ - struct gb_protocol *protocol; - - list_for_each_entry(protocol, &gb_protocols, links) { - if (protocol->id < id) - continue; - if (protocol->id > id) - break; - - if (protocol->major > major) - continue; - if (protocol->major < major) - break; - - if (protocol->minor > minor) - continue; - if (protocol->minor < minor) - break; - - return protocol; - } - return NULL; -} - -int __gb_protocol_register(struct gb_protocol *protocol, struct module *module) -{ - struct gb_protocol *existing; - u8 id = protocol->id; - u8 major = protocol->major; - u8 minor = protocol->minor; - - protocol->owner = module; - - /* - * The protocols list is sorted first by protocol id (low to - * high), then by major version (high to low), and finally - * by minor version (high to low). Searching only by - * protocol id will produce the newest implemented version - * of the protocol. - */ - spin_lock_irq(&gb_protocols_lock); - - list_for_each_entry(existing, &gb_protocols, links) { - if (existing->id < id) - continue; - if (existing->id > id) - break; - - if (existing->major > major) - continue; - if (existing->major < major) - break; - - if (existing->minor > minor) - continue; - if (existing->minor < minor) - break; - - /* A matching protocol has already been registered */ - spin_unlock_irq(&gb_protocols_lock); - - return -EEXIST; - } - - /* - * We need to insert the protocol here, before the existing one - * (or before the head if we searched the whole list) - */ - list_add_tail(&protocol->links, &existing->links); - spin_unlock_irq(&gb_protocols_lock); - - pr_info("Registered %s protocol.\n", protocol->name); - - return 0; -} -EXPORT_SYMBOL_GPL(__gb_protocol_register); - -/* - * De-register a previously registered protocol. - */ -void gb_protocol_deregister(struct gb_protocol *protocol) -{ - if (!protocol) - return; - - spin_lock_irq(&gb_protocols_lock); - protocol = gb_protocol_find(protocol->id, protocol->major, - protocol->minor); - if (WARN_ON(!protocol || protocol->count)) { - spin_unlock_irq(&gb_protocols_lock); - return; - } - - list_del(&protocol->links); - spin_unlock_irq(&gb_protocols_lock); - - pr_info("Deregistered %s protocol.\n", protocol->name); -} -EXPORT_SYMBOL_GPL(gb_protocol_deregister); - -/* Returns the requested protocol if available, or a null pointer */ -struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) -{ - struct gb_protocol *protocol; - u8 protocol_count; - - spin_lock_irq(&gb_protocols_lock); - protocol = gb_protocol_find(id, major, minor); - if (protocol) { - if (!try_module_get(protocol->owner)) { - protocol = NULL; - } else { - protocol_count = protocol->count; - if (protocol_count != U8_MAX) - protocol->count++; - } - } - spin_unlock_irq(&gb_protocols_lock); - - if (protocol) - WARN_ON(protocol_count == U8_MAX); - - return protocol; -} -EXPORT_SYMBOL_GPL(gb_protocol_get); - -int gb_protocol_get_version(struct gb_connection *connection) -{ - struct gb_protocol_version_request request; - struct gb_protocol_version_response response; - struct gb_protocol *protocol = connection->protocol; - int retval; - - request.major = protocol->major; - request.minor = protocol->minor; - - retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, - &request, sizeof(request), &response, - sizeof(response)); - if (retval) - return retval; - - if (response.major > connection->protocol->major) { - dev_err(&connection->hd->dev, - "%s: unsupported major version (%u > %u)\n", - connection->name, response.major, - connection->protocol->major); - return -ENOTSUPP; - } - - connection->module_major = response.major; - connection->module_minor = response.minor; - - dev_dbg(&connection->hd->dev, - "%s: %s (0x%02x) v%u.%u\n", connection->name, - protocol->name, protocol->id, response.major, response.minor); - - return 0; -} -EXPORT_SYMBOL_GPL(gb_protocol_get_version); - -void gb_protocol_put(struct gb_protocol *protocol) -{ - u8 id; - u8 major; - u8 minor; - - id = protocol->id; - major = protocol->major; - minor = protocol->minor; - - spin_lock_irq(&gb_protocols_lock); - protocol = gb_protocol_find(id, major, minor); - if (WARN_ON(!protocol || !protocol->count)) - goto out; - - protocol->count--; - module_put(protocol->owner); -out: - spin_unlock_irq(&gb_protocols_lock); -} -EXPORT_SYMBOL_GPL(gb_protocol_put); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h deleted file mode 100644 index 38fe14cf46ce..000000000000 --- a/drivers/staging/greybus/protocol.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Greybus protocol handling - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __PROTOCOL_H -#define __PROTOCOL_H - -struct gb_connection; -struct gb_operation; - -typedef int (*gb_connection_init_t)(struct gb_connection *); -typedef void (*gb_connection_exit_t)(struct gb_connection *); -typedef int (*gb_request_recv_t)(u8, struct gb_operation *); - -/* - * Protocols having the same id but different major and/or minor - * version numbers are treated as distinct protocols. If it makes - * sense someday we could group protocols having the same id. - */ -struct gb_protocol { - u8 id; - u8 major; - u8 minor; - u8 count; - - struct list_head links; /* global list */ - - gb_connection_init_t connection_init; - gb_connection_exit_t connection_exit; - gb_request_recv_t request_recv; - struct module *owner; - char *name; -}; - -int __gb_protocol_register(struct gb_protocol *protocol, struct module *module); -void gb_protocol_deregister(struct gb_protocol *protocol); - -#define gb_protocol_register(protocol) \ - __gb_protocol_register(protocol, THIS_MODULE) - -struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); -int gb_protocol_get_version(struct gb_connection *connection); - -void gb_protocol_put(struct gb_protocol *protocol); - -/* __protocol: Pointer to struct gb_protocol */ -#define gb_protocol_driver(__protocol) \ -static int __init protocol_init(void) \ -{ \ - return gb_protocol_register(__protocol); \ -} \ -module_init(protocol_init); \ -static void __exit protocol_exit(void) \ -{ \ - gb_protocol_deregister(__protocol); \ -} \ -module_exit(protocol_exit) - -/* __protocol: string matching name of struct gb_protocol */ -#define gb_builtin_protocol_driver(__protocol) \ -int __init gb_##__protocol##_init(void) \ -{ \ - return gb_protocol_register(&__protocol); \ -} \ -void gb_##__protocol##_exit(void) \ -{ \ - gb_protocol_deregister(&__protocol); \ -} \ - -#endif /* __PROTOCOL_H */ -- cgit v1.2.3-59-g8ed1b From f866e66f368ee58954d83e6e99194ffcd6dfcad5 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 20 May 2016 11:59:57 -0500 Subject: greybus: add operation traces Define a new gb_operation event class, and define and use trace events that record when an operation is created, finally destroyed, and when its active count changes. Signed-off-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 69 +++++++++++++++++++++++++++++++++ drivers/staging/greybus/operation.c | 17 +++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 849cfa095aac..3b4875d91c32 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -15,6 +15,7 @@ #include <linux/tracepoint.h> struct gb_message; +struct gb_operation; struct gb_host_device; #define gb_bundle_name(message) \ @@ -111,6 +112,74 @@ DEFINE_EVENT(gb_message, gb_message_cancel_incoming, TP_ARGS(message) ); +DECLARE_EVENT_CLASS(gb_operation, + + TP_PROTO(struct gb_operation *operation), + + TP_ARGS(operation), + + TP_STRUCT__entry( + __field(u16, cport_id) /* CPort of HD side of connection */ + __field(u16, id) /* Operation ID */ + __field(u8, type) + __field(unsigned long, flags) + __field(int, active) + __field(int, waiters) + __field(int, errno) + ), + + TP_fast_assign( + __entry->cport_id = operation->connection->hd_cport_id; + __entry->id = operation->id; + __entry->type = operation->type; + __entry->flags = operation->flags; + __entry->active = operation->active; + __entry->waiters = atomic_read(&operation->waiters); + __entry->errno = operation->errno; + ), + + TP_printk("id=%04x type=0x%02x cport_id=%04x flags=0x%lx active=%d waiters=%d errno=%d", + __entry->id, __entry->cport_id, __entry->type, __entry->flags, + __entry->active, __entry->waiters, __entry->errno) +); + +#define DEFINE_OPERATION_EVENT(name) \ + DEFINE_EVENT(gb_operation, name, \ + TP_PROTO(struct gb_operation *operation), \ + TP_ARGS(operation)) + +/* + * Occurs after a new operation is created for an outgoing request + * has been successfully created. + */ +DEFINE_OPERATION_EVENT(gb_operation_create); + +/* + * Occurs after a new operation has been created for an incoming + * request has been successfully created and initialized. + */ +DEFINE_OPERATION_EVENT(gb_operation_create_incoming); + +/* + * Occurs when the last reference to an operation has been dropped, + * prior to freeing resources. + */ +DEFINE_OPERATION_EVENT(gb_operation_destroy); + +/* + * Occurs when an operation has been marked active, after updating + * its active count. + */ +DEFINE_OPERATION_EVENT(gb_operation_get_active); + +/* + * Occurs when an operation has been marked active, before updating + * its active count. + */ +DEFINE_OPERATION_EVENT(gb_operation_put_active); + +#undef DEFINE_OPERATION_EVENT + DECLARE_EVENT_CLASS(gb_host_device, TP_PROTO(struct gb_host_device *hd, u16 intf_cport_id, diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b7cc59d0a252..259bd920e715 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -57,6 +57,8 @@ static int gb_operation_get_active(struct gb_operation *operation) if (operation->active++ == 0) list_add_tail(&operation->links, &connection->operations); + trace_gb_operation_get_active(operation); + spin_unlock_irqrestore(&connection->lock, flags); return 0; @@ -69,6 +71,9 @@ static void gb_operation_put_active(struct gb_operation *operation) unsigned long flags; spin_lock_irqsave(&connection->lock, flags); + + trace_gb_operation_get_active(operation); + if (--operation->active == 0) { list_del(&operation->links); if (atomic_read(&operation->waiters)) @@ -536,6 +541,8 @@ gb_operation_create_flags(struct gb_connection *connection, size_t response_size, unsigned long flags, gfp_t gfp) { + struct gb_operation *operation; + if (WARN_ON_ONCE(type == GB_REQUEST_TYPE_INVALID)) return NULL; if (WARN_ON_ONCE(type & GB_MESSAGE_TYPE_RESPONSE)) @@ -544,9 +551,14 @@ gb_operation_create_flags(struct gb_connection *connection, if (WARN_ON_ONCE(flags & ~GB_OPERATION_FLAG_USER_MASK)) flags &= GB_OPERATION_FLAG_USER_MASK; - return gb_operation_create_common(connection, type, + operation = gb_operation_create_common(connection, type, request_size, response_size, flags, gfp); + if (operation) + trace_gb_operation_create(operation); + + return operation; + } EXPORT_SYMBOL_GPL(gb_operation_create_flags); @@ -581,6 +593,7 @@ gb_operation_create_incoming(struct gb_connection *connection, u16 id, operation->id = id; memcpy(operation->request->header, data, size); + trace_gb_operation_create_incoming(operation); return operation; } @@ -603,6 +616,8 @@ static void _gb_operation_destroy(struct kref *kref) operation = container_of(kref, struct gb_operation, kref); + trace_gb_operation_destroy(operation); + if (operation->response) gb_operation_message_free(operation->response); gb_operation_message_free(operation->request); -- cgit v1.2.3-59-g8ed1b From 630175698e9b3a1e24d62dddb9059f5bdcbb7cec Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 20 May 2016 11:44:55 -0500 Subject: greybus: tracing: define events using macros A tracepoint event is defined with TP_PROTO() and TP_ARGS macros that match that of the event's class. A lot of repetition (and opportunity for inadvertent errors) in tracepoint event definitions can be eliminated by using a macro. Define and use class-specific event definition macros for gb_message and gb_host_device class events. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Jeffrey Carlyle <jcarlyle@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 63 +++++++++++---------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 3b4875d91c32..40c24cf9ba7a 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -52,65 +52,47 @@ DECLARE_EVENT_CLASS(gb_message, __entry->hd_cport_id, __entry->payload_size) ); +#define DEFINE_MESSAGE_EVENT(name) \ + DEFINE_EVENT(gb_message, name, \ + TP_PROTO(struct gb_message *message), \ + TP_ARGS(message)) + /* * tracepoint name greybus:gb_message_send * description send a greybus message * location operation.c:gb_message_send */ -DEFINE_EVENT(gb_message, gb_message_send, - - TP_PROTO(struct gb_message *message), - - TP_ARGS(message) -); +DEFINE_MESSAGE_EVENT(gb_message_send); /* * tracepoint name greybus:gb_message_recv_request * description receive a greybus request * location operation.c:gb_connection_recv_request */ -DEFINE_EVENT(gb_message, gb_message_recv_request, - - TP_PROTO(struct gb_message *message), - - TP_ARGS(message) -); +DEFINE_MESSAGE_EVENT(gb_message_recv_request); /* * tracepoint name greybus:gb_message_recv_response * description receive a greybus response * location operation.c:gb_connection_recv_response */ -DEFINE_EVENT(gb_message, gb_message_recv_response, - - TP_PROTO(struct gb_message *message), - - TP_ARGS(message) -); +DEFINE_MESSAGE_EVENT(gb_message_recv_response); /* * tracepoint name greybus:gb_message_cancel_outgoing * description cancel outgoing greybus request * location operation.c:gb_message_cancel */ -DEFINE_EVENT(gb_message, gb_message_cancel_outgoing, - - TP_PROTO(struct gb_message *message), - - TP_ARGS(message) -); +DEFINE_MESSAGE_EVENT(gb_message_cancel_outgoing); /* * tracepoint name greybus:gb_message_cancel_incoming * description cancel incoming greybus request * location operation.c:gb_message_cancel_incoming */ -DEFINE_EVENT(gb_message, gb_message_cancel_incoming, - - TP_PROTO(struct gb_message *message), +DEFINE_MESSAGE_EVENT(gb_message_cancel_incoming); - TP_ARGS(message) -); +#undef DEFINE_MESSAGE_EVENT DECLARE_EVENT_CLASS(gb_operation, @@ -203,31 +185,28 @@ DECLARE_EVENT_CLASS(gb_host_device, __entry->intf_cport_id, __entry->payload_size) ); +#define DEFINE_HD_EVENT(name) \ + DEFINE_EVENT(gb_host_device, name, \ + TP_PROTO(struct gb_host_device *hd, \ + u16 intf_cport_id, \ + size_t payload_size), \ + TP_ARGS(hd, intf_cport_id, payload_size)) + /* * tracepoint name greybus:gb_host_device_send * description tracepoint representing the point data are transmitted * location es2.c:message_send */ -DEFINE_EVENT(gb_host_device, gb_host_device_send, - - TP_PROTO(struct gb_host_device *hd, u16 intf_cport_id, - size_t payload_size), - - TP_ARGS(hd, intf_cport_id, payload_size) -); +DEFINE_HD_EVENT(gb_host_device_send); /* * tracepoint name greybus:gb_host_device_recv * description tracepoint representing the point data are received * location es2.c:cport_in_callback */ -DEFINE_EVENT(gb_host_device, gb_host_device_recv, +DEFINE_HD_EVENT(gb_host_device_recv); - TP_PROTO(struct gb_host_device *hd, u16 intf_cport_id, - size_t payload_size), - - TP_ARGS(hd, intf_cport_id, payload_size) -); +#undef DEFINE_HD_EVENT #endif /* _TRACE_GREYBUS_H */ -- cgit v1.2.3-59-g8ed1b From 58a881376357caec4c992b73779102142096d383 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 20 May 2016 11:44:56 -0500 Subject: greybus: tracing: eliminate "location" comments Each message event has a set of comments preceeding its definition. One of them, "location", indicates where that event is used. I am certain that this comment will become out of date very easily. Hopefully just the name of the event is a good enough suggestion about where it will be used. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Jeffrey Carlyle <jcarlyle@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 40c24cf9ba7a..4fd9269c97d4 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -60,35 +60,30 @@ DECLARE_EVENT_CLASS(gb_message, /* * tracepoint name greybus:gb_message_send * description send a greybus message - * location operation.c:gb_message_send */ DEFINE_MESSAGE_EVENT(gb_message_send); /* * tracepoint name greybus:gb_message_recv_request * description receive a greybus request - * location operation.c:gb_connection_recv_request */ DEFINE_MESSAGE_EVENT(gb_message_recv_request); /* * tracepoint name greybus:gb_message_recv_response * description receive a greybus response - * location operation.c:gb_connection_recv_response */ DEFINE_MESSAGE_EVENT(gb_message_recv_response); /* * tracepoint name greybus:gb_message_cancel_outgoing * description cancel outgoing greybus request - * location operation.c:gb_message_cancel */ DEFINE_MESSAGE_EVENT(gb_message_cancel_outgoing); /* * tracepoint name greybus:gb_message_cancel_incoming * description cancel incoming greybus request - * location operation.c:gb_message_cancel_incoming */ DEFINE_MESSAGE_EVENT(gb_message_cancel_incoming); @@ -195,14 +190,12 @@ DECLARE_EVENT_CLASS(gb_host_device, /* * tracepoint name greybus:gb_host_device_send * description tracepoint representing the point data are transmitted - * location es2.c:message_send */ DEFINE_HD_EVENT(gb_host_device_send); /* * tracepoint name greybus:gb_host_device_recv * description tracepoint representing the point data are received - * location es2.c:cport_in_callback */ DEFINE_HD_EVENT(gb_host_device_recv); -- cgit v1.2.3-59-g8ed1b From 206dc534e99a5bfc10a699442f0c20d372eacf44 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 20 May 2016 11:44:57 -0500 Subject: greybus: tracing: refine comments I believe that duplicating the tracepoint name in comments prior to the tracepoint is redundant, and doesn't add a lot of value. I also believe that we can provide a little more information about what exactly an event means, or when exactly it is called. I don't claim this is a huge improvement, but it's a proposal. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Jeffrey Carlyle <jcarlyle@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 4fd9269c97d4..ba93873ed0e0 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -58,32 +58,31 @@ DECLARE_EVENT_CLASS(gb_message, TP_ARGS(message)) /* - * tracepoint name greybus:gb_message_send - * description send a greybus message + * Occurs immediately before calling a host device's message_send() + * method. */ DEFINE_MESSAGE_EVENT(gb_message_send); /* - * tracepoint name greybus:gb_message_recv_request - * description receive a greybus request + * Occurs after an incoming request message has been received */ DEFINE_MESSAGE_EVENT(gb_message_recv_request); /* - * tracepoint name greybus:gb_message_recv_response - * description receive a greybus response + * Occurs after an incoming response message has been received, + * after its matching request has been found. */ DEFINE_MESSAGE_EVENT(gb_message_recv_response); /* - * tracepoint name greybus:gb_message_cancel_outgoing - * description cancel outgoing greybus request + * Occurs after an operation has been canceled, possibly before the + * cancellation is complete. */ DEFINE_MESSAGE_EVENT(gb_message_cancel_outgoing); /* - * tracepoint name greybus:gb_message_cancel_incoming - * description cancel incoming greybus request + * Occurs when an incoming request is cancelled; if the response has + * been queued for sending, this occurs after it is sent. */ DEFINE_MESSAGE_EVENT(gb_message_cancel_incoming); @@ -188,14 +187,14 @@ DECLARE_EVENT_CLASS(gb_host_device, TP_ARGS(hd, intf_cport_id, payload_size)) /* - * tracepoint name greybus:gb_host_device_send - * description tracepoint representing the point data are transmitted + * Occurs immediately before calling usb_submit_urb() to send a + * message to the UniPro bridge. */ DEFINE_HD_EVENT(gb_host_device_send); /* - * tracepoint name greybus:gb_host_device_recv - * description tracepoint representing the point data are received + * Occurs after receiving a UniPro message via the USB subsystem, + * just prior to handing it to the Greybus core for handling. */ DEFINE_HD_EVENT(gb_host_device_recv); -- cgit v1.2.3-59-g8ed1b From 038405db982987bbe01ddc1fd90fba2dba700dd4 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Fri, 20 May 2016 18:37:02 -0700 Subject: greybus: legacy: remove protocol.o from the makefile Commit 0917cba11 ("legacy: remove legacy driver support") removed protocol.c, however, the corresponding target in the Makefile was not removed therefore broken the build. Testing Done: - Build & boot on EVT1.5 Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 6d07b1bcd21b..f7fe4d512015 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -6,7 +6,6 @@ greybus-y := core.o \ interface.o \ bundle.o \ connection.o \ - protocol.o \ control.o \ svc.o \ svc_watchdog.o \ -- cgit v1.2.3-59-g8ed1b From 37f07c0c974d78bfcdc9f3267438c91163fc3ae5 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Mon, 23 May 2016 23:31:14 +0530 Subject: greybus: audio: Fix memory leak Topology data pointer was mistakenly set to NULL before freeing it. Fix this. Fixes: 64a86d9ba850 ("audio: Add module specific driver") Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 57d3b0292231..53e84f5fafb0 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -368,8 +368,8 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) gbaudio_unregister_module(gbmodule); gbaudio_tplg_release(gbmodule); - gbmodule->topology = NULL; kfree(gbmodule->topology); + gbmodule->topology = NULL; gb_connection_disable(gbmodule->mgmt_connection); list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) { gb_connection_disable(dai->connection); -- cgit v1.2.3-59-g8ed1b From d7be800f7ae9398055509b3a83d0ea3118e9277f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Mon, 23 May 2016 15:01:08 +0100 Subject: greybus: Fix unbalanced irq_enable() backtrace Tip-of-tree is exhibiting a backtrace when loading-up the set of greybus kernel modules due to calling arche_platform_wd_irq_en() directly after a call to devm_request_threaded_irq(). At the point we call arch_platform_wd_irq_en() the relevant IRQ will already be enabled. What we want to do in this situation is configure the GPIO line as an input. This patch fixes the backtrace by supplanting arche_platform_wd_irq_en() with gpio_direction_input(arche_pdata->wake_detect_gpio) in arche_platform_probe(). WARNING: at msm-ara-3.10/kernel/irq/manage.c:457 __enable_irq+0x74/0xc0() Unbalanced enable for IRQ 687 Modules linked in: gb_arche(O+) gb_camera(O) gb_es2(O) gb_vibrator(O) gb_raw(O) gb_power_supply(O) gb_loopback(O) gb_light(O) gb_hid(O) greybus(O) CPU: 0 PID: 415 Comm: insmod Tainted: G W O 3.10.78-g2a4dec8 #65 Call trace: [<ffffffc000206adc>] dump_backtrace+0x0/0x248 [<ffffffc000206d34>] show_stack+0x10/0x1c [<ffffffc000c6c698>] dump_stack+0x1c/0x28 [<ffffffc00021c95c>] warn_slowpath_common+0x74/0x9c [<ffffffc00021c9d0>] warn_slowpath_fmt+0x4c/0x58 [<ffffffc000269d7c>] __enable_irq+0x70/0xc0 [<ffffffc000269e34>] enable_irq+0x68/0x7c [<ffffffbffc0609b4>] arche_platform_probe+0x3b4/0x4f4 [gb_arche] [<ffffffc0005ace30>] platform_drv_probe+0x14/0x20 [<ffffffc0005ab980>] driver_probe_device+0x160/0x374 [<ffffffc0005abc40>] __driver_attach+0x60/0x90 [<ffffffc0005aa768>] bus_for_each_dev+0x74/0x94 [<ffffffc0005ab2c4>] driver_attach+0x1c/0x28 [<ffffffc0005aae74>] bus_add_driver+0x124/0x248 [<ffffffc0005ac270>] driver_register+0x94/0x110 [<ffffffc0005ad3c4>] platform_driver_register+0x58/0x64 [<ffffffbffc065020>] $x+0x20/0x58 [gb_arche] [<ffffffc0002007dc>] do_one_initcall+0xb0/0x14c [<ffffffc00028252c>] load_module+0x19d0/0x1b18 [<ffffffc00028278c>] SyS_init_module+0x118/0x130 Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiermath@linaro.org> Tested-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index bc450770771f..4af34988e197 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -603,7 +603,7 @@ static int arche_platform_probe(struct platform_device *pdev) return ret; } - arche_platform_wd_irq_en(arche_pdata); + gpio_direction_input(arche_pdata->wake_detect_gpio); ret = device_create_file(dev, &dev_attr_state); if (ret) { -- cgit v1.2.3-59-g8ed1b From 00606367141cf518e94d8354a02d298475de67e5 Mon Sep 17 00:00:00 2001 From: Eli Sennesh <esennesh@leaflabs.com> Date: Mon, 16 May 2016 14:55:17 -0400 Subject: greybus: update UniPro Set Interface Power Mode operation to match spec Bring the gb_svc_intf_set_power_mode() up-to-date with the current Greybus specification. This largely involves adding more members to the structure sent across the wire. Also change the camera code to use the new operation properly, with default values passed for the new necessary arguments. The correctness of these default values is confirmed via testing and by asking Rob Johnson. Testing Done: Took a picture with a camera module, received error code when passing deliberately incorrect values for new parameters, got proper -EIO and Greybus result code printed when operation stopped halfway through. Associated Firmware Changes: 6810-6812 on Gerrit for SW-1239, 6870 and 5612-5613 on Gerrit for SW-2945 Signed-off-by: Eli Sennesh <esennesh@leaflabs.com> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 10 ++++++++-- drivers/staging/greybus/greybus_protocols.h | 28 ++++++++++++++++++++++++++++ drivers/staging/greybus/svc.c | 24 +++++++++++++++++++++--- drivers/staging/greybus/svc.h | 5 ++++- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index dd482bd94637..654bfcd852a2 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -121,17 +121,23 @@ static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, ret = gb_svc_intf_set_power_mode(svc, intf_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_SMALL_AMPLITUDE, + GB_SVC_NO_DE_EMPHASIS, GB_SVC_UNIPRO_FAST_MODE, 2, 2, GB_SVC_PWRM_RXTERMINATION | - GB_SVC_PWRM_TXTERMINATION, 0); + GB_SVC_PWRM_TXTERMINATION, 0, + NULL, NULL); else ret = gb_svc_intf_set_power_mode(svc, intf_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, + GB_SVC_SMALL_AMPLITUDE, + GB_SVC_NO_DE_EMPHASIS, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, - 0, 0); + 0, 0, + NULL, NULL); return ret; } diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b98f02c93b1a..ec3570ab9b26 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1136,6 +1136,13 @@ struct gb_svc_timesync_ping_response { #define GB_SVC_UNIPRO_HIBERNATE_MODE 0x11 #define GB_SVC_UNIPRO_OFF_MODE 0x12 +#define GB_SVC_SMALL_AMPLITUDE 0x01 +#define GB_SVC_LARGE_AMPLITUDE 0x02 + +#define GB_SVC_NO_DE_EMPHASIS 0x00 +#define GB_SVC_SMALL_DE_EMPHASIS 0x01 +#define GB_SVC_LARGE_DE_EMPHASIS 0x02 + #define GB_SVC_PWRM_RXTERMINATION 0x01 #define GB_SVC_PWRM_TXTERMINATION 0x02 #define GB_SVC_PWRM_LINE_RESET 0x04 @@ -1146,17 +1153,38 @@ struct gb_svc_timesync_ping_response { #define GB_SVC_UNIPRO_HS_SERIES_A 0x01 #define GB_SVC_UNIPRO_HS_SERIES_B 0x02 +#define GB_SVC_SETPWRM_PWR_OK 0x00 +#define GB_SVC_SETPWRM_PWR_LOCAL 0x01 +#define GB_SVC_SETPWRM_PWR_REMOTE 0x02 +#define GB_SVC_SETPWRM_PWR_BUSY 0x03 +#define GB_SVC_SETPWRM_PWR_ERROR_CAP 0x04 +#define GB_SVC_SETPWRM_PWR_FATAL_ERROR 0x05 + +struct gb_svc_l2_timer_cfg { + __le16 tsb_fc0_protection_timeout; + __le16 tsb_tc0_replay_timeout; + __le16 tsb_afc0_req_timeout; + __le16 tsb_fc1_protection_timeout; + __le16 tsb_tc1_replay_timeout; + __le16 tsb_afc1_req_timeout; + __le16 reserved_for_tc2[3]; + __le16 reserved_for_tc3[3]; +} __packed; + struct gb_svc_intf_set_pwrm_request { __u8 intf_id; __u8 hs_series; __u8 tx_mode; __u8 tx_gear; __u8 tx_nlanes; + __u8 tx_amplitude; + __u8 tx_hs_equalizer; __u8 rx_mode; __u8 rx_gear; __u8 rx_nlanes; __u8 flags; __le32 quirks; + struct gb_svc_l2_timer_cfg local_l2timerdata, remote_l2timerdata; } __packed; struct gb_svc_intf_set_pwrm_response { diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 78cc0e38828d..90094cf1a5eb 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -578,23 +578,33 @@ void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, u8 tx_mode, u8 tx_gear, u8 tx_nlanes, + u8 tx_amplitude, u8 tx_hs_equalizer, u8 rx_mode, u8 rx_gear, u8 rx_nlanes, - u8 flags, u32 quirks) + u8 flags, u32 quirks, + struct gb_svc_l2_timer_cfg *local, + struct gb_svc_l2_timer_cfg *remote) { struct gb_svc_intf_set_pwrm_request request; struct gb_svc_intf_set_pwrm_response response; int ret; + u16 result_code; request.intf_id = intf_id; request.hs_series = hs_series; request.tx_mode = tx_mode; request.tx_gear = tx_gear; request.tx_nlanes = tx_nlanes; + request.tx_amplitude = tx_amplitude; + request.tx_hs_equalizer = tx_hs_equalizer; request.rx_mode = rx_mode; request.rx_gear = rx_gear; request.rx_nlanes = rx_nlanes; request.flags = flags; request.quirks = cpu_to_le32(quirks); + if (local) + request.local_l2timerdata = *local; + if (remote) + request.remote_l2timerdata = *remote; ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, &request, sizeof(request), @@ -602,7 +612,13 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, if (ret < 0) return ret; - return le16_to_cpu(response.result_code); + result_code = le16_to_cpu(response.result_code); + if (result_code != GB_SVC_SETPWRM_PWR_LOCAL) { + dev_err(&svc->dev, "set power mode = %d\n", result_code); + return -EIO; + } + + return 0; } EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); @@ -926,9 +942,11 @@ static void gb_svc_process_hello_deferred(struct gb_operation *operation) GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, + GB_SVC_SMALL_AMPLITUDE, GB_SVC_NO_DE_EMPHASIS, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, - 0, 0); + 0, 0, + NULL, NULL); if (ret) dev_warn(&svc->dev, diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index e3e0aa14b08d..438806d37947 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -77,8 +77,11 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 value); int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, u8 tx_mode, u8 tx_gear, u8 tx_nlanes, + u8 tx_amplitude, u8 tx_hs_equalizer, u8 rx_mode, u8 rx_gear, u8 rx_nlanes, - u8 flags, u32 quirks); + u8 flags, u32 quirks, + struct gb_svc_l2_timer_cfg *local, + struct gb_svc_l2_timer_cfg *remote); int gb_svc_ping(struct gb_svc *svc); int gb_svc_watchdog_create(struct gb_svc *svc); void gb_svc_watchdog_destroy(struct gb_svc *svc); -- cgit v1.2.3-59-g8ed1b From 91a8030f781d27e84661c4a4edf1e108bfc1c594 Mon Sep 17 00:00:00 2001 From: Jeffrey Carlyle <jcarlyle@google.com> Date: Wed, 25 May 2016 16:38:47 -0700 Subject: greybus: Revert "update UniPro Set Interface Power Mode operation to match spec" This reverts commit 29fee8c55b59bb6ac59b99a0563c89c514cba42b. This change and its companion NuttX changes seem to be triggering a storm of POWERMODEIND switch interrupts on the SVC. Signed-off-by: Jeffrey Carlyle <jcarlyle@google.com> Acked-by: Sandeep Patil <sspatil@google.com> --- drivers/staging/greybus/camera.c | 10 ++-------- drivers/staging/greybus/greybus_protocols.h | 28 ---------------------------- drivers/staging/greybus/svc.c | 24 +++--------------------- drivers/staging/greybus/svc.h | 5 +---- 4 files changed, 6 insertions(+), 61 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 654bfcd852a2..dd482bd94637 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -121,23 +121,17 @@ static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, ret = gb_svc_intf_set_power_mode(svc, intf_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_FAST_MODE, 2, 2, - GB_SVC_SMALL_AMPLITUDE, - GB_SVC_NO_DE_EMPHASIS, GB_SVC_UNIPRO_FAST_MODE, 2, 2, GB_SVC_PWRM_RXTERMINATION | - GB_SVC_PWRM_TXTERMINATION, 0, - NULL, NULL); + GB_SVC_PWRM_TXTERMINATION, 0); else ret = gb_svc_intf_set_power_mode(svc, intf_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, - GB_SVC_SMALL_AMPLITUDE, - GB_SVC_NO_DE_EMPHASIS, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, - 0, 0, - NULL, NULL); + 0, 0); return ret; } diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index ec3570ab9b26..b98f02c93b1a 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1136,13 +1136,6 @@ struct gb_svc_timesync_ping_response { #define GB_SVC_UNIPRO_HIBERNATE_MODE 0x11 #define GB_SVC_UNIPRO_OFF_MODE 0x12 -#define GB_SVC_SMALL_AMPLITUDE 0x01 -#define GB_SVC_LARGE_AMPLITUDE 0x02 - -#define GB_SVC_NO_DE_EMPHASIS 0x00 -#define GB_SVC_SMALL_DE_EMPHASIS 0x01 -#define GB_SVC_LARGE_DE_EMPHASIS 0x02 - #define GB_SVC_PWRM_RXTERMINATION 0x01 #define GB_SVC_PWRM_TXTERMINATION 0x02 #define GB_SVC_PWRM_LINE_RESET 0x04 @@ -1153,38 +1146,17 @@ struct gb_svc_timesync_ping_response { #define GB_SVC_UNIPRO_HS_SERIES_A 0x01 #define GB_SVC_UNIPRO_HS_SERIES_B 0x02 -#define GB_SVC_SETPWRM_PWR_OK 0x00 -#define GB_SVC_SETPWRM_PWR_LOCAL 0x01 -#define GB_SVC_SETPWRM_PWR_REMOTE 0x02 -#define GB_SVC_SETPWRM_PWR_BUSY 0x03 -#define GB_SVC_SETPWRM_PWR_ERROR_CAP 0x04 -#define GB_SVC_SETPWRM_PWR_FATAL_ERROR 0x05 - -struct gb_svc_l2_timer_cfg { - __le16 tsb_fc0_protection_timeout; - __le16 tsb_tc0_replay_timeout; - __le16 tsb_afc0_req_timeout; - __le16 tsb_fc1_protection_timeout; - __le16 tsb_tc1_replay_timeout; - __le16 tsb_afc1_req_timeout; - __le16 reserved_for_tc2[3]; - __le16 reserved_for_tc3[3]; -} __packed; - struct gb_svc_intf_set_pwrm_request { __u8 intf_id; __u8 hs_series; __u8 tx_mode; __u8 tx_gear; __u8 tx_nlanes; - __u8 tx_amplitude; - __u8 tx_hs_equalizer; __u8 rx_mode; __u8 rx_gear; __u8 rx_nlanes; __u8 flags; __le32 quirks; - struct gb_svc_l2_timer_cfg local_l2timerdata, remote_l2timerdata; } __packed; struct gb_svc_intf_set_pwrm_response { diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 90094cf1a5eb..78cc0e38828d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -578,33 +578,23 @@ void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, u8 tx_mode, u8 tx_gear, u8 tx_nlanes, - u8 tx_amplitude, u8 tx_hs_equalizer, u8 rx_mode, u8 rx_gear, u8 rx_nlanes, - u8 flags, u32 quirks, - struct gb_svc_l2_timer_cfg *local, - struct gb_svc_l2_timer_cfg *remote) + u8 flags, u32 quirks) { struct gb_svc_intf_set_pwrm_request request; struct gb_svc_intf_set_pwrm_response response; int ret; - u16 result_code; request.intf_id = intf_id; request.hs_series = hs_series; request.tx_mode = tx_mode; request.tx_gear = tx_gear; request.tx_nlanes = tx_nlanes; - request.tx_amplitude = tx_amplitude; - request.tx_hs_equalizer = tx_hs_equalizer; request.rx_mode = rx_mode; request.rx_gear = rx_gear; request.rx_nlanes = rx_nlanes; request.flags = flags; request.quirks = cpu_to_le32(quirks); - if (local) - request.local_l2timerdata = *local; - if (remote) - request.remote_l2timerdata = *remote; ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, &request, sizeof(request), @@ -612,13 +602,7 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, if (ret < 0) return ret; - result_code = le16_to_cpu(response.result_code); - if (result_code != GB_SVC_SETPWRM_PWR_LOCAL) { - dev_err(&svc->dev, "set power mode = %d\n", result_code); - return -EIO; - } - - return 0; + return le16_to_cpu(response.result_code); } EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); @@ -942,11 +926,9 @@ static void gb_svc_process_hello_deferred(struct gb_operation *operation) GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, - GB_SVC_SMALL_AMPLITUDE, GB_SVC_NO_DE_EMPHASIS, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, - 0, 0, - NULL, NULL); + 0, 0); if (ret) dev_warn(&svc->dev, diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 438806d37947..e3e0aa14b08d 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -77,11 +77,8 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 value); int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, u8 tx_mode, u8 tx_gear, u8 tx_nlanes, - u8 tx_amplitude, u8 tx_hs_equalizer, u8 rx_mode, u8 rx_gear, u8 rx_nlanes, - u8 flags, u32 quirks, - struct gb_svc_l2_timer_cfg *local, - struct gb_svc_l2_timer_cfg *remote); + u8 flags, u32 quirks); int gb_svc_ping(struct gb_svc *svc); int gb_svc_watchdog_create(struct gb_svc *svc); void gb_svc_watchdog_destroy(struct gb_svc *svc); -- cgit v1.2.3-59-g8ed1b From c61a8b49846ecc11f7959f573b9548f859bc73a5 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Tue, 24 May 2016 18:32:02 +0530 Subject: greybus: arche-platform: Make fw_flashing_seq() return error Make arche_platform_fw_flashing_seq() return error value, needed later when we add clock enable support for FW flashing. Testing Done: Tested on EVT1.5 platform. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Tested-by: Michael Scott <michael.scott@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 4af34988e197..a8e36e1141fd 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -335,10 +335,10 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat /* * Requires arche_pdata->platform_state_mutex to be held */ -static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) +static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) - return; + return 0; dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); @@ -353,6 +353,7 @@ static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_ arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING); + return 0; } /* @@ -424,7 +425,9 @@ static ssize_t state_store(struct device *dev, arche_platform_poweroff_seq(arche_pdata); - arche_platform_fw_flashing_seq(arche_pdata); + ret = arche_platform_fw_flashing_seq(arche_pdata); + if (ret) + goto exit; device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); } else { -- cgit v1.2.3-59-g8ed1b From 0b1283e33f7a38645c9d060ce2e2b24a44e3b125 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Tue, 24 May 2016 18:32:03 +0530 Subject: greybus: arche-platform: Enter ACTIVE state only from OFF state Make sure that, transition to active state happens only from OFF state. Instead of imposing the restriction to user-space, driver internally switches to OFF state and then to ACTIVE state. Testing Done: Tested on EVT1.5 platform. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Tested-by: Michael Scott <michael.scott@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index a8e36e1141fd..58b370774399 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -407,8 +407,15 @@ static ssize_t state_store(struct device *dev, if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) goto exit; + /* First we want to make sure we power off everything + * and then activate back again */ + device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); + arche_platform_poweroff_seq(arche_pdata); + arche_platform_wd_irq_en(arche_pdata); ret = arche_platform_coldboot_seq(arche_pdata); + if (ret) + goto exit; } else if (sysfs_streq(buf, "standby")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) -- cgit v1.2.3-59-g8ed1b From 7a867d149f0c0cb9184a38f83704d37439a17f3e Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Tue, 24 May 2016 18:32:04 +0530 Subject: greybus: arche-platform: Enable SVC clock during FW_FLASHING state The issue is, as part of kernel-only build we started seeing failures in SVC FW flashing. It was reproducible easily in kernel-only build, but never observed on Android build. During debugging, there were couple of observations, 1. If SVC clock enabled and disables (which is REFCLK_MAIN), then SVC FW flashing works. 2. If we do not switch SVC to HSE (external clock source) it works. Recently, SVC code has been updated to switch HSE clock, so removing it (remove/skip rcc_switch_ara_pll() fn) would use internal clock only. As per STM32 spec, for flashing through USART we do not need to enable HSE, but the above observation contradicts with it. There is still something missing in terms of understanding of how STM32 device functions as far as Flashing is concerned. There is something hidden in HW, which probably still need to identify. So as a interim solution we will enable clock for FW_FLASHING state, which seems to be fixing the issue here. Testing Done: Tested on EVT1.5 with arche6.0 and kernel-only build. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Tested-by: Michael Scott <michael.scott@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 58b370774399..f47ea4670d25 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -337,6 +337,8 @@ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdat */ static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) { + int ret; + if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) return 0; @@ -348,6 +350,14 @@ static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_p gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); usleep_range(100, 200); + + ret = clk_prepare_enable(arche_pdata->svc_ref_clk); + if (ret) { + dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", + ret); + return ret; + } + svc_reset_onoff(arche_pdata->svc_reset_gpio, !arche_pdata->is_reset_act_hi); @@ -374,10 +384,10 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); - - clk_disable_unprepare(arche_pdata->svc_ref_clk); } + clk_disable_unprepare(arche_pdata->svc_ref_clk); + /* As part of exit, put APB back in reset state */ svc_reset_onoff(arche_pdata->svc_reset_gpio, arche_pdata->is_reset_act_hi); -- cgit v1.2.3-59-g8ed1b From b797c43271f77f1dfd25953d72245cb8d0b832fc Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Tue, 24 May 2016 16:21:29 +0200 Subject: greybus: Fix loopback app after rename to gpphy commit 6d94670 gpbridge: rename 'gpbridge' to 'gbphy' everywhere missed renaming the loopback test app. So do it too. Testing done: complie and run loopback test Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/tools/loopback_test.c | 64 +++++++++++++-------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index d8ff1bc0878a..25035f666372 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -59,10 +59,10 @@ struct loopback_results { uint32_t apbridge_unipro_latency_min; uint32_t apbridge_unipro_latency_jitter; - float gpbridge_firmware_latency_avg; - uint32_t gpbridge_firmware_latency_max; - uint32_t gpbridge_firmware_latency_min; - uint32_t gpbridge_firmware_latency_jitter; + float gbphy_firmware_latency_avg; + uint32_t gbphy_firmware_latency_max; + uint32_t gbphy_firmware_latency_min; + uint32_t gbphy_firmware_latency_jitter; uint32_t error; }; @@ -155,17 +155,17 @@ GET_MAX(throughput_max); GET_MAX(request_max); GET_MAX(latency_max); GET_MAX(apbridge_unipro_latency_max); -GET_MAX(gpbridge_firmware_latency_max); +GET_MAX(gbphy_firmware_latency_max); GET_MIN(throughput_min); GET_MIN(request_min); GET_MIN(latency_min); GET_MIN(apbridge_unipro_latency_min); -GET_MIN(gpbridge_firmware_latency_min); +GET_MIN(gbphy_firmware_latency_min); GET_AVG(throughput_avg); GET_AVG(request_avg); GET_AVG(latency_avg); GET_AVG(apbridge_unipro_latency_avg); -GET_AVG(gpbridge_firmware_latency_avg); +GET_AVG(gbphy_firmware_latency_avg); void abort() { @@ -351,20 +351,20 @@ static int get_results(struct loopback_test *t) r->apbridge_unipro_latency_avg = read_sysfs_float(d->sysfs_entry, "apbridge_unipro_latency_avg"); - r->gpbridge_firmware_latency_min = - read_sysfs_int(d->sysfs_entry, "gpbridge_firmware_latency_min"); - r->gpbridge_firmware_latency_max = - read_sysfs_int(d->sysfs_entry, "gpbridge_firmware_latency_max"); - r->gpbridge_firmware_latency_avg = - read_sysfs_float(d->sysfs_entry, "gpbridge_firmware_latency_avg"); + r->gbphy_firmware_latency_min = + read_sysfs_int(d->sysfs_entry, "gbphy_firmware_latency_min"); + r->gbphy_firmware_latency_max = + read_sysfs_int(d->sysfs_entry, "gbphy_firmware_latency_max"); + r->gbphy_firmware_latency_avg = + read_sysfs_float(d->sysfs_entry, "gbphy_firmware_latency_avg"); r->request_jitter = r->request_max - r->request_min; r->latency_jitter = r->latency_max - r->latency_min; r->throughput_jitter = r->throughput_max - r->throughput_min; r->apbridge_unipro_latency_jitter = r->apbridge_unipro_latency_max - r->apbridge_unipro_latency_min; - r->gpbridge_firmware_latency_jitter = - r->gpbridge_firmware_latency_max - r->gpbridge_firmware_latency_min; + r->gbphy_firmware_latency_jitter = + r->gbphy_firmware_latency_max - r->gbphy_firmware_latency_min; } @@ -391,20 +391,20 @@ static int get_results(struct loopback_test *t) r->apbridge_unipro_latency_avg = get_apbridge_unipro_latency_avg_aggregate(t); - r->gpbridge_firmware_latency_min = - get_gpbridge_firmware_latency_min_aggregate(t); - r->gpbridge_firmware_latency_max = - get_gpbridge_firmware_latency_max_aggregate(t); - r->gpbridge_firmware_latency_avg = - get_gpbridge_firmware_latency_avg_aggregate(t); + r->gbphy_firmware_latency_min = + get_gbphy_firmware_latency_min_aggregate(t); + r->gbphy_firmware_latency_max = + get_gbphy_firmware_latency_max_aggregate(t); + r->gbphy_firmware_latency_avg = + get_gbphy_firmware_latency_avg_aggregate(t); r->request_jitter = r->request_max - r->request_min; r->latency_jitter = r->latency_max - r->latency_min; r->throughput_jitter = r->throughput_max - r->throughput_min; r->apbridge_unipro_latency_jitter = r->apbridge_unipro_latency_max - r->apbridge_unipro_latency_min; - r->gpbridge_firmware_latency_jitter = - r->gpbridge_firmware_latency_max - r->gpbridge_firmware_latency_min; + r->gbphy_firmware_latency_jitter = + r->gbphy_firmware_latency_max - r->gbphy_firmware_latency_min; } @@ -467,11 +467,11 @@ int format_output(struct loopback_test *t, r->apbridge_unipro_latency_jitter); len += snprintf(&buf[len], buf_len - len, - " gpbridge-latency usec:\tmin=%u max=%u average=%f jitter=%u\n", - r->gpbridge_firmware_latency_min, - r->gpbridge_firmware_latency_max, - r->gpbridge_firmware_latency_avg, - r->gpbridge_firmware_latency_jitter); + " gbphy-latency usec:\tmin=%u max=%u average=%f jitter=%u\n", + r->gbphy_firmware_latency_min, + r->gbphy_firmware_latency_max, + r->gbphy_firmware_latency_avg, + r->gbphy_firmware_latency_jitter); } else { len += snprintf(&buf[len], buf_len- len, ",%s,%s,%u,%u,%u", @@ -503,10 +503,10 @@ int format_output(struct loopback_test *t, r->apbridge_unipro_latency_jitter); len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", - r->gpbridge_firmware_latency_min, - r->gpbridge_firmware_latency_max, - r->gpbridge_firmware_latency_avg, - r->gpbridge_firmware_latency_jitter); + r->gbphy_firmware_latency_min, + r->gbphy_firmware_latency_max, + r->gbphy_firmware_latency_avg, + r->gbphy_firmware_latency_jitter); } printf("\n%s\n", buf); -- cgit v1.2.3-59-g8ed1b From 71b7a36072cbaf86d3c69ec6473325bfea4f6f87 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 25 May 2016 12:46:24 +0530 Subject: greybus: Documentation/sysfs: Rename gpbridge to gbphy These were left in the earlier renaming series, fix them as well. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore | 1 + .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore | 1 + .../greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore | 1 - .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore | 1 - 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/gpio/gpiochip490/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gpbridge0/i2c-4/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore -- cgit v1.2.3-59-g8ed1b From 7a9a5bf01ebd31171ad747386133b126deb81c15 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 25 May 2016 12:49:26 +0530 Subject: greybus: Documentation/sysfs: gbphy0 is an invalid name The numbering of gbphy devices is going to start from 1 and not 0. Reflect the same in sysfs hierarchy. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore | 1 - .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore | 1 - .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore | 1 + .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore | 1 + 4 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore create mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/gpio/gpiochip490/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy0/i2c-4/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore new file mode 100644 index 000000000000..f935021a8f8a --- /dev/null +++ b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore @@ -0,0 +1 @@ +!.gitignore -- cgit v1.2.3-59-g8ed1b From ae36e81ed93e9fecfede1cc3dde48cfea3a8c908 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 25 May 2016 22:18:14 +0200 Subject: greybus: interface: fix svc-resource error messages The interface svc-resource helper are used to enable as well as disable the corresponding SVC resources so make sure the error messages reflect that. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index e3e8e16943b5..ba7f880c94e8 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -387,7 +387,7 @@ static int gb_interface_vsys_set(struct gb_interface *intf, bool enable) ret = gb_svc_intf_vsys_set(svc, intf->interface_id, enable); if (ret) { - dev_err(&intf->dev, "failed to enable v_sys: %d\n", ret); + dev_err(&intf->dev, "failed to set v_sys: %d\n", ret); return ret; } @@ -403,7 +403,7 @@ static int gb_interface_refclk_set(struct gb_interface *intf, bool enable) ret = gb_svc_intf_refclk_set(svc, intf->interface_id, enable); if (ret) { - dev_err(&intf->dev, "failed to enable refclk: %d\n", ret); + dev_err(&intf->dev, "failed to set refclk: %d\n", ret); return ret; } @@ -419,7 +419,7 @@ static int gb_interface_unipro_set(struct gb_interface *intf, bool enable) ret = gb_svc_intf_unipro_set(svc, intf->interface_id, enable); if (ret) { - dev_err(&intf->dev, "failed to enable UniPro: %d\n", ret); + dev_err(&intf->dev, "failed to set UniPro: %d\n", ret); return ret; } -- cgit v1.2.3-59-g8ed1b From bae7c690354b585d98ae15bd8b1b7b0ba2bb3c42 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 25 May 2016 22:18:15 +0200 Subject: greybus: remove redundant latency-tag sanity checks Core will never call host-device callbacks with invalid arguments (and that would still need to be verified in bridge firmware anyway), so remove the redundant and insufficient sanity check from the callbacks. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 947ebae6ae5a..5bd348f1fea4 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -622,11 +622,6 @@ static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; - if (!cport_id_valid(hd, cport_id)) { - dev_err(&udev->dev, "invalid cport %u\n", cport_id); - return -EINVAL; - } - retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), GB_APB_REQUEST_LATENCY_TAG_EN, USB_DIR_OUT | USB_TYPE_VENDOR | @@ -645,11 +640,6 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; - if (!cport_id_valid(hd, cport_id)) { - dev_err(&udev->dev, "invalid cport %u\n", cport_id); - return -EINVAL; - } - retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), GB_APB_REQUEST_LATENCY_TAG_DIS, USB_DIR_OUT | USB_TYPE_VENDOR | -- cgit v1.2.3-59-g8ed1b From df732546ce127e3bd8d55f4f7073aebd4482438c Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Mon, 23 May 2016 23:05:29 -0500 Subject: greybus: tracing: fix a bad tracepoint In gb_operation_put_active(), the wrong trace point is being called. Fix that. Signed-off-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 259bd920e715..20a8d74473d0 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -72,7 +72,7 @@ static void gb_operation_put_active(struct gb_operation *operation) spin_lock_irqsave(&connection->lock, flags); - trace_gb_operation_get_active(operation); + trace_gb_operation_put_active(operation); if (--operation->active == 0) { list_del(&operation->links); -- cgit v1.2.3-59-g8ed1b From 1f79046bd73abf5cc1dc5ad1f817a7851496ae0c Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Mon, 23 May 2016 23:05:30 -0500 Subject: greybus: tracing: fix hd traces Currently there are two trace points defined for the Greybus host device structure. One records information when a message gets sent, and another when it gets received. Neither of these is really a host device event. We have trace points defined for messages that dump information about all sent and received messages. As a result, the information about sending messages over a host is redundant, and can go away. (Note that the message traces may need a little refinement so they produce all desired information.) Instead of these trace points, define some that are directly related to the host device abstraction: when one is created, added, deleted, or released (destroyed). These do not require a CPort ID or payload size, so eliminate those two parameters from the host device trace point prototype. Change the trace information recorded for a host device to be just a subset of interesting fields in a host device. Signed-off-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/core.c | 6 ++-- drivers/staging/greybus/es2.c | 4 --- drivers/staging/greybus/greybus_trace.h | 52 ++++++++++++++++++++------------- drivers/staging/greybus/hd.c | 10 ++++++- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 6d6a2bbb591a..b1a7b116843a 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -14,8 +14,10 @@ #include "greybus.h" #include "greybus_trace.h" -EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_send); -EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_recv); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_create); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_release); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_add); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_del); /* Allow greybus to be disabled at boot if needed */ static bool nogreybus; diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 5bd348f1fea4..24fef349eb36 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -16,8 +16,6 @@ #include "greybus.h" #include "kernel_ver.h" #include "connection.h" -#include "greybus_trace.h" - /* Fixed CPort numbers */ #define ES2_CPORT_CDSI0 16 @@ -469,7 +467,6 @@ static int message_send(struct gb_host_device *hd, u16 cport_id, message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; - trace_gb_host_device_send(hd, cport_id, buffer_size); retval = usb_submit_urb(urb, gfp_mask); if (retval) { dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); @@ -909,7 +906,6 @@ static void cport_in_callback(struct urb *urb) cport_id = gb_message_cport_unpack(header); if (cport_id_valid(hd, cport_id)) { - trace_gb_host_device_recv(hd, cport_id, urb->actual_length); greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, urb->actual_length); } else { diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index ba93873ed0e0..cbbc9596c78e 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -158,45 +158,55 @@ DEFINE_OPERATION_EVENT(gb_operation_put_active); DECLARE_EVENT_CLASS(gb_host_device, - TP_PROTO(struct gb_host_device *hd, u16 intf_cport_id, - size_t payload_size), + TP_PROTO(struct gb_host_device *hd), - TP_ARGS(hd, intf_cport_id, payload_size), + TP_ARGS(hd), TP_STRUCT__entry( - __string(name, dev_name(&hd->dev)) - __field(u16, intf_cport_id) - __field(size_t, payload_size) + __field(int, bus_id) + __field(u8, num_cports) + __field(size_t, buffer_size_max) ), TP_fast_assign( - __assign_str(name, dev_name(&hd->dev)) - __entry->intf_cport_id = intf_cport_id; - __entry->payload_size = payload_size; + __entry->bus_id = hd->bus_id; + __entry->num_cports = hd->num_cports; + __entry->buffer_size_max = hd->buffer_size_max; ), - TP_printk("greybus:%s if_id=%u l=%zu", __get_str(name), - __entry->intf_cport_id, __entry->payload_size) + TP_printk("greybus: bus_id=%d num_cports=%hu mtu=%zu", + __entry->bus_id, __entry->num_cports, + __entry->buffer_size_max) ); #define DEFINE_HD_EVENT(name) \ DEFINE_EVENT(gb_host_device, name, \ - TP_PROTO(struct gb_host_device *hd, \ - u16 intf_cport_id, \ - size_t payload_size), \ - TP_ARGS(hd, intf_cport_id, payload_size)) + TP_PROTO(struct gb_host_device *hd), \ + TP_ARGS(hd)) + +/* + * Occurs after a new host device is successfully created, before + * its SVC has been set up. + */ +DEFINE_HD_EVENT(gb_hd_create); + +/* + * Occurs after the last reference to a host device has been + * dropped. + */ +DEFINE_HD_EVENT(gb_hd_release); /* - * Occurs immediately before calling usb_submit_urb() to send a - * message to the UniPro bridge. + * Occurs after a new host device has been added, after the + * connection to its SVC has * been enabled. */ -DEFINE_HD_EVENT(gb_host_device_send); +DEFINE_HD_EVENT(gb_hd_add); /* - * Occurs after receiving a UniPro message via the USB subsystem, - * just prior to handing it to the Greybus core for handling. + * Occurs when a host device is being disconnected from the AP USB + * host controller. */ -DEFINE_HD_EVENT(gb_host_device_recv); +DEFINE_HD_EVENT(gb_hd_del); #undef DEFINE_HD_EVENT diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index fba6d766209f..f64b592187c0 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -11,7 +11,7 @@ #include <linux/slab.h> #include "greybus.h" - +#include "greybus_trace.h" static struct ida gb_hd_bus_id_map; @@ -87,6 +87,8 @@ void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id) } ida_simple_remove(&hd->cport_id_map, cport_id); + + trace_gb_hd_release(hd); } static void gb_hd_release(struct device *dev) @@ -168,6 +170,8 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, device_initialize(&hd->dev); dev_set_name(&hd->dev, "greybus%d", hd->bus_id); + trace_gb_hd_create(hd); + hd->svc = gb_svc_create(hd); if (!hd->svc) { dev_err(&hd->dev, "failed to create svc\n"); @@ -193,12 +197,16 @@ int gb_hd_add(struct gb_host_device *hd) return ret; } + trace_gb_hd_add(hd); + return 0; } EXPORT_SYMBOL_GPL(gb_hd_add); void gb_hd_del(struct gb_host_device *hd) { + trace_gb_hd_del(hd); + /* * Tear down the svc and flush any on-going hotplug processing before * removing the remaining interfaces. -- cgit v1.2.3-59-g8ed1b From 5451ea0e99cb7d58cbf90c7785cb50cc322a4434 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Mon, 23 May 2016 23:05:31 -0500 Subject: greybus: tracing: add module traces Define a new gb_module trace point event class, used to trace events associated with the module abstraction. Define four basic trace points for this--creation time, drop of last reference, before registring interfaces and after de-registering them. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 53 +++++++++++++++++++++++++++++++++ drivers/staging/greybus/module.c | 9 ++++++ 2 files changed, 62 insertions(+) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index cbbc9596c78e..ecf225394b20 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -156,6 +156,59 @@ DEFINE_OPERATION_EVENT(gb_operation_put_active); #undef DEFINE_OPERATION_EVENT +DECLARE_EVENT_CLASS(gb_module, + + TP_PROTO(struct gb_module *module), + + TP_ARGS(module), + + TP_STRUCT__entry( + __field(int, hd_bus_id) + __field(u8, module_id) + __field(u8, num_interfaces) + __field(bool, disconnected) + ), + + TP_fast_assign( + __entry->hd_bus_id = module->hd->bus_id; + __entry->module_id = module->module_id; + __entry->disconnected = module->disconnected; + ), + + TP_printk("greybus: hd_bus_id=%d module_id=%hhu disconnected=%u", + __entry->hd_bus_id, __entry->module_id, __entry->disconnected) +); + +#define DEFINE_MODULE_EVENT(name) \ + DEFINE_EVENT(gb_module, name, \ + TP_PROTO(struct gb_module *module), \ + TP_ARGS(module)) + +/* + * Occurs after a new module is successfully created, before + * creating any of its interfaces. + */ +DEFINE_MODULE_EVENT(gb_module_create); + +/* + * Occurs after the last reference to a module has been dropped. + */ +DEFINE_MODULE_EVENT(gb_module_release); + +/* + * Occurs after a module is successfully created, before registering + * any of its interfaces. + */ +DEFINE_MODULE_EVENT(gb_module_add); + +/* + * Occurs when a module is deleted, before deregistering its + * interfaces. + */ +DEFINE_MODULE_EVENT(gb_module_del); + +#undef DEFINE_MODULE_EVENT + DECLARE_EVENT_CLASS(gb_host_device, TP_PROTO(struct gb_host_device *hd), diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index ea5895475181..c56b98870b6f 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -9,6 +9,7 @@ #include "greybus.h" +#include "greybus_trace.h" static ssize_t eject_store(struct device *dev, struct device_attribute *attr, @@ -77,6 +78,8 @@ static void gb_module_release(struct device *dev) { struct gb_module *module = to_gb_module(dev); + trace_gb_module_release(module); + kfree(module); } @@ -109,6 +112,8 @@ struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id, device_initialize(&module->dev); dev_set_name(&module->dev, "%d-%u", hd->bus_id, module_id); + trace_gb_module_create(module); + for (i = 0; i < num_interfaces; ++i) { intf = gb_interface_create(module, module_id + i); if (!intf) { @@ -215,6 +220,8 @@ int gb_module_add(struct gb_module *module) return ret; } + trace_gb_module_add(module); + for (i = 0; i < module->num_interfaces; ++i) gb_module_register_interface(module->interfaces[i]); @@ -229,6 +236,8 @@ void gb_module_del(struct gb_module *module) for (i = 0; i < module->num_interfaces; ++i) gb_module_deregister_interface(module->interfaces[i]); + trace_gb_module_del(module); + device_del(&module->dev); } -- cgit v1.2.3-59-g8ed1b From cb4c8441e5dac4994ab63c1426babda3102beda9 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Mon, 23 May 2016 23:05:32 -0500 Subject: greybus: tracing: define interface traces Define a new gb_module trace point event class, used to trace events associated with the interface abstraction. Define four basic trace points for this--creation time, drop of last reference, before registring interfaces and after de-registering them. In addition, define traces for activating and deactivating, and enabling and disabling an interface. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 80 +++++++++++++++++++++++++++++++++ drivers/staging/greybus/interface.c | 17 +++++++ 2 files changed, 97 insertions(+) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index ecf225394b20..93e04a85506f 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -209,6 +209,86 @@ DEFINE_MODULE_EVENT(gb_module_del); #undef DEFINE_MODULE_EVENT +DECLARE_EVENT_CLASS(gb_interface, + + TP_PROTO(struct gb_interface *intf), + + TP_ARGS(intf), + + TP_STRUCT__entry( + __field(u8, id) /* Interface id */ + __field(u8, module_id) + __field(u8, device_id) + __field(bool, disconnected) + __field(bool, ejected) + __field(bool, active) + __field(bool, enabled) + ), + + TP_fast_assign( + __entry->id = intf->interface_id; + __entry->module_id = intf->module->module_id; + __entry->device_id = intf->device_id; + __entry->disconnected = intf->disconnected; + __entry->ejected = intf->ejected; + __entry->active = intf->active; + __entry->enabled = intf->enabled; + ), + + TP_printk("greybus: intf_id=%hhu device_id=%hhu module_id=%hhu D=%u J=%u A=%u E=%u", + __entry->id, __entry->device_id, __entry->module_id, + __entry->disconnected, __entry->ejected, __entry->active, + __entry->enabled) +); + +#define DEFINE_INTERFACE_EVENT(name) \ + DEFINE_EVENT(gb_interface, name, \ + TP_PROTO(struct gb_interface *intf), \ + TP_ARGS(intf)) + +/* + * Occurs after a new interface is successfully created. + */ +DEFINE_INTERFACE_EVENT(gb_interface_create); + +/* + * Occurs after the last reference to an interface has been dropped. + */ +DEFINE_INTERFACE_EVENT(gb_interface_release); + +/* + * Occurs after an interface been registerd. + */ +DEFINE_INTERFACE_EVENT(gb_interface_add); + +/* + * Occurs when a registered interface gets deregisterd. + */ +DEFINE_INTERFACE_EVENT(gb_interface_del); + +/* + * Occurs when a registered interface has been successfully + * activated. + */ +DEFINE_INTERFACE_EVENT(gb_interface_activate); + +/* + * Occurs when an activated interface is being deactivated. + */ +DEFINE_INTERFACE_EVENT(gb_interface_deactivate); + +/* + * Occurs when an interface has been successfully enabled. + */ +DEFINE_INTERFACE_EVENT(gb_interface_enable); + +/* + * Occurs when an enabled interface is being disabled. + */ +DEFINE_INTERFACE_EVENT(gb_interface_disable); + +#undef DEFINE_INTERFACE_EVENT + DECLARE_EVENT_CLASS(gb_host_device, TP_PROTO(struct gb_host_device *hd), diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index ba7f880c94e8..c19a09cea462 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -9,6 +9,7 @@ #include "greybus.h" +#include "greybus_trace.h" #define GB_INTERFACE_DEVICE_ID_BAD 0xff @@ -326,6 +327,8 @@ static void gb_interface_release(struct device *dev) { struct gb_interface *intf = to_gb_interface(dev); + trace_gb_interface_release(intf); + kfree(intf); } @@ -375,6 +378,8 @@ struct gb_interface *gb_interface_create(struct gb_module *module, dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev), interface_id); + trace_gb_interface_create(intf); + return intf; } @@ -506,6 +511,8 @@ int gb_interface_activate(struct gb_interface *intf) intf->active = true; + trace_gb_interface_activate(intf); + return 0; err_hibernate_link: @@ -530,6 +537,8 @@ void gb_interface_deactivate(struct gb_interface *intf) if (!intf->active) return; + trace_gb_interface_deactivate(intf); + gb_interface_route_destroy(intf); gb_interface_hibernate_link(intf); gb_interface_unipro_set(intf, false); @@ -629,6 +638,8 @@ int gb_interface_enable(struct gb_interface *intf) intf->enabled = true; + trace_gb_interface_enable(intf); + return 0; err_destroy_bundles: @@ -658,6 +669,8 @@ void gb_interface_disable(struct gb_interface *intf) if (!intf->enabled) return; + trace_gb_interface_disable(intf); + /* * Disable the control-connection early to avoid operation timeouts * when the interface is already gone. @@ -687,6 +700,8 @@ int gb_interface_add(struct gb_interface *intf) return ret; } + trace_gb_interface_add(intf); + dev_info(&intf->dev, "Interface added: VID=0x%08x, PID=0x%08x\n", intf->vendor_id, intf->product_id); dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n", @@ -699,6 +714,8 @@ int gb_interface_add(struct gb_interface *intf) void gb_interface_del(struct gb_interface *intf) { if (device_is_registered(&intf->dev)) { + trace_gb_interface_del(intf); + device_del(&intf->dev); dev_info(&intf->dev, "Interface removed\n"); } -- cgit v1.2.3-59-g8ed1b From 29a822bdf77a0c2af5b556506f3cf3175a9a4a98 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Fri, 27 May 2016 10:49:23 +0530 Subject: greybus: hd: Add API to release reserved CPorts It is required to release all unique ids registered via ida_get_simple to avoid any possible memory leak. cport_release() already exists with special handling for ES2_CPORT_CDSI1, i.e. updating in_use flag without removing associated ida. So, added another API to release reserved cports CDSI0 and CDSI1. This is intended to be used only during es2_destroy(). Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/hd.c | 8 ++++++++ drivers/staging/greybus/hd.h | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index f64b592187c0..52388257d3d2 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -54,6 +54,14 @@ int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id) } EXPORT_SYMBOL_GPL(gb_hd_cport_reserve); +void gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id) +{ + struct ida *id_map = &hd->cport_id_map; + + ida_simple_remove(id_map, cport_id); +} +EXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved); + /* Locking: Caller guarantees serialisation */ int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id, unsigned long flags) diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 8510816c1796..ad229622654e 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -61,6 +61,7 @@ struct gb_host_device { #define to_gb_host_device(d) container_of(d, struct gb_host_device, dev) int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id); +void gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id); int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id, unsigned long flags); void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id); -- cgit v1.2.3-59-g8ed1b From 52033fdebc92021490729465e27ffb47cd42b0e0 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Fri, 27 May 2016 10:49:24 +0530 Subject: greybus: es2: Release reserved cports CDSI0 and CDSI1 Unique ids were reserved for CDSI0 and CDSI1 during _probe, however missed to release those ids during disconnect. This causes a memory leak of 128 bytes for each iteration of unipro_reset. Fix this. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 24fef349eb36..fd59c143d0f3 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -869,6 +869,10 @@ static void es2_destroy(struct es2_ap_dev *es2) kfree(es2->cport_to_ep); + /* release reserved CDSI0 and CDSI1 cports */ + gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI1); + gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI0); + udev = es2->usb_dev; gb_hd_put(es2->hd); -- cgit v1.2.3-59-g8ed1b From f9340fc7dd9f42edfcd7a0e332798ae01a6b48ac Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Tue, 24 May 2016 13:34:47 -0500 Subject: greybus: report right error value Running "make coccicheck" on the Greybus code discovered that an error message in gb_camera_debugfs_init() was interpreting the wrong value in reporting the error code. Fix that. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index dd482bd94637..dda871912c10 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -873,7 +873,7 @@ static int gb_camera_debugfs_init(struct gb_camera *gcam) if (IS_ERR(dentry)) { gcam_err(gcam, "debugfs operation %s create failed (%ld)\n", - entry->name, PTR_ERR(gcam->debugfs.root)); + entry->name, PTR_ERR(dentry)); return PTR_ERR(dentry); } } -- cgit v1.2.3-59-g8ed1b From ebc9e3750d3112e3a84fe65386cb0e5deb994e18 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Tue, 24 May 2016 13:34:48 -0500 Subject: greybus: fix unbalanced mutex Running "make coccicheck" on the Greybus code reports that gb_mmc_get_ro() and gb_mmc_get_cd() can return without releasing the mutex it acquired if there's an error. Fix this. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/sdio.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 4d4cfdf8edb6..bdcc86923c54 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -684,9 +684,12 @@ static int gb_mmc_get_ro(struct mmc_host *mmc) struct gb_sdio_host *host = mmc_priv(mmc); mutex_lock(&host->lock); - if (host->removed) + if (host->removed) { + mutex_unlock(&host->lock); return -ESHUTDOWN; + } mutex_unlock(&host->lock); + return host->read_only; } @@ -695,9 +698,12 @@ static int gb_mmc_get_cd(struct mmc_host *mmc) struct gb_sdio_host *host = mmc_priv(mmc); mutex_lock(&host->lock); - if (host->removed) + if (host->removed) { + mutex_unlock(&host->lock); return -ESHUTDOWN; + } mutex_unlock(&host->lock); + return host->card_present; } -- cgit v1.2.3-59-g8ed1b From 66394300c37e9321a913f269021b3d6c92d786ad Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Tue, 24 May 2016 13:34:49 -0500 Subject: greybus: eliminate unneeded null check Coccinelle points out that debugfs_remove_recursive() handles a null argument properly, so there's no need to check for NULL before making the call. I have verified this is true of the kernel we're now working with (arche-6.0). Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index dda871912c10..d5496f8a7b89 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -883,8 +883,7 @@ static int gb_camera_debugfs_init(struct gb_camera *gcam) static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) { - if (gcam->debugfs.root) - debugfs_remove_recursive(gcam->debugfs.root); + debugfs_remove_recursive(gcam->debugfs.root); vfree(gcam->debugfs.buffers); } -- cgit v1.2.3-59-g8ed1b From d97fca12e92d3179554baf4db42611e5fe56aeed Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Tue, 24 May 2016 13:34:50 -0500 Subject: greybus: fix pointless null check Coccinelle points out that a call in gb_lights_channel_free() to flush_work() is passed which is always non-null. Prior to the call, there is an unnecessary check to see if that address is null. Get rid of the test. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/light.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 8b71ed3df318..78fb8a9f6a48 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1019,8 +1019,7 @@ static int gb_lights_light_register(struct gb_light *light) static void gb_lights_channel_free(struct gb_channel *channel) { #ifndef LED_HAVE_SET_BLOCKING - if (&channel->work_brightness_set) - flush_work(&channel->work_brightness_set); + flush_work(&channel->work_brightness_set); #endif kfree(channel->attrs); kfree(channel->attr_group); -- cgit v1.2.3-59-g8ed1b From 95073cc2e53c1352f2c244408d1bd005b07650fd Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Tue, 24 May 2016 13:34:51 -0500 Subject: greybus: use PTR_ERR_OR_ZERO() Coccinelle points out that the macro PTR_ERR_OR_ZERO() handles the frequent case of converting a pointer into either error code (if its value is an encoded error value) or 0 (otherwise). Switch some code in gb_power_supply_register() to use that macro. I have verified this is true of the kernel we're now working with (arche-6.0). Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/power_supply.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 9cae396c6115..8d6570d1bb61 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -547,10 +547,7 @@ static int gb_power_supply_register(struct gb_power_supply *gbpsy) gbpsy->psy = power_supply_register(&connection->bundle->dev, &gbpsy->desc, &cfg); - if (IS_ERR(gbpsy->psy)) - return PTR_ERR(gbpsy->psy); - - return 0; + return PTR_ERR_OR_ZERO(gbpsy->psy); } #endif -- cgit v1.2.3-59-g8ed1b From 898d75f4aa8b1aeb99df8fe275cb3fcfa3dc0688 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Tue, 24 May 2016 13:34:52 -0500 Subject: greybus: drop a bogus semicolon Coccinelle reports that gb_svc_pwrmon_debugfs_init() has a block of a for loop which is followed by an unnecessary semicolon. Get rid of that semicolon. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 78cc0e38828d..2b3b8d98053c 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -779,7 +779,7 @@ static void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) &pwrmon_debugfs_current_fops); debugfs_create_file("power_now", S_IRUGO, dir, rail, &pwrmon_debugfs_power_fops); - }; + } kfree(rail_names); return; -- cgit v1.2.3-59-g8ed1b From 7aefe7918f8e053f9cd8077fcc98182689f1341e Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:22 +0200 Subject: greybus: core: avoid I/O to disconnected interfaces Add new helper to disable connections to interfaces that have already been disconnected (e.g. forcibly removed). The connection tear-down procedure differs enough depending on whether the interface is still present or already gone to warrant a dedicated helper. This will become more obvious with the new tear-down procedure, which involves I/O on the connection being tore down. This also simplifies handling of the legacy bootrom, which does not support the new tear-down operations. Specifically, this allows us to remove the early control-connection tear down during interface disable, and also avoids some error messages currently printed during legacy mode switch (i.e. bootrom boot-over-UniPro) and forcible removal. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 21 +++++++++++++++++++++ drivers/staging/greybus/connection.h | 1 + drivers/staging/greybus/control.c | 5 ++++- drivers/staging/greybus/core.c | 2 +- drivers/staging/greybus/interface.c | 7 ------- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ac3be2fceade..7e07ef832b7c 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -654,6 +654,27 @@ out_unlock: } EXPORT_SYMBOL_GPL(gb_connection_disable); +/* Disable a connection without communicating with the remote end. */ +void gb_connection_disable_forced(struct gb_connection *connection) +{ + mutex_lock(&connection->mutex); + + if (connection->state == GB_CONNECTION_STATE_DISABLED) + goto out_unlock; + + spin_lock_irq(&connection->lock); + connection->state = GB_CONNECTION_STATE_DISABLED; + gb_connection_cancel_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); + + gb_connection_svc_connection_destroy(connection); + gb_connection_hd_cport_disable(connection); + +out_unlock: + mutex_unlock(&connection->mutex); +} +EXPORT_SYMBOL_GPL(gb_connection_disable_forced); + /* Caller must have disabled the connection before destroying it. */ void gb_connection_destroy(struct gb_connection *connection) { diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 53ce28452da4..f1592391acf1 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -80,6 +80,7 @@ int gb_connection_enable(struct gb_connection *connection); int gb_connection_enable_tx(struct gb_connection *connection); void gb_connection_disable_rx(struct gb_connection *connection); void gb_connection_disable(struct gb_connection *connection); +void gb_connection_disable_forced(struct gb_connection *connection); void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length); diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index b4a1c1476c56..a5effcf09f56 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -316,7 +316,10 @@ void gb_control_disable(struct gb_control *control) { dev_dbg(&control->connection->intf->dev, "%s\n", __func__); - gb_connection_disable(control->connection); + if (control->intf->disconnected) + gb_connection_disable_forced(control->connection); + else + gb_connection_disable(control->connection); } int gb_control_add(struct gb_control *control) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index b1a7b116843a..7350c5eba7e9 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -189,7 +189,7 @@ static int greybus_remove(struct device *dev) list_for_each_entry(connection, &bundle->connections, bundle_links) { if (bundle->intf->disconnected) - gb_connection_disable(connection); + gb_connection_disable_forced(connection); else gb_connection_disable_rx(connection); } diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c19a09cea462..d1e2c456462e 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -671,13 +671,6 @@ void gb_interface_disable(struct gb_interface *intf) trace_gb_interface_disable(intf); - /* - * Disable the control-connection early to avoid operation timeouts - * when the interface is already gone. - */ - if (intf->disconnected) - gb_control_disable(intf->control); - list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); -- cgit v1.2.3-59-g8ed1b From a4b08df4e01beac8a5e90f5dfb8a1ffda943d73d Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:23 +0200 Subject: greybus: interface: clean up bootrom quirk handling Clean up bootrom quirk handling in preparation for addition of further flags. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index d1e2c456462e..0f2b5914eae5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -174,6 +174,7 @@ static void gb_interface_route_destroy(struct gb_interface *intf) static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) { struct gb_host_device *hd = intf->hd; + unsigned long bootrom_quirks; int ret; u32 value; u16 attr; @@ -217,16 +218,18 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) init_status = value >> 24; /* - * Check if the interface is executing the quirky ES3 bootrom that - * requires E2EFC, CSD and CSV to be disabled. + * Check if the interface is executing the quirky ES3 bootrom that, + * for example, requires E2EFC, CSD and CSV to be disabled. */ + bootrom_quirks = GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; + switch (init_status) { case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED: case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: - intf->quirks |= GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; + intf->quirks |= bootrom_quirks; break; default: - intf->quirks &= ~GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; + intf->quirks &= ~bootrom_quirks; } /* Clear the init status. */ -- cgit v1.2.3-59-g8ed1b From d9fa3494b78a4e7fe9d8243ddd5ec472668f8fdf Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:24 +0200 Subject: greybus: interface: avoid I/O to bootrom during removal Add an interface quirk flag to suppress I/O during interface disable. This is needed to prevent I/O to the bootrom during controlled connection tear down (e.g. eject or driver unbind). This will be more obvious with the new connection tear-down procedure. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 8 ++++++-- drivers/staging/greybus/interface.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 0f2b5914eae5..2cfb5a46e7d4 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -221,8 +221,8 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) * Check if the interface is executing the quirky ES3 bootrom that, * for example, requires E2EFC, CSD and CSV to be disabled. */ - bootrom_quirks = GB_INTERFACE_QUIRK_NO_CPORT_FEATURES; - + bootrom_quirks = GB_INTERFACE_QUIRK_NO_CPORT_FEATURES | + GB_INTERFACE_QUIRK_FORCED_DISABLE; switch (init_status) { case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED: case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: @@ -674,6 +674,10 @@ void gb_interface_disable(struct gb_interface *intf) trace_gb_interface_disable(intf); + /* Set disconnected flag to avoid I/O during connection tear down. */ + if (intf->quirks & GB_INTERFACE_QUIRK_FORCED_DISABLE) + intf->disconnected = true; + list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 51772ccdd467..e833f7df025d 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -13,6 +13,7 @@ #define GB_INTERFACE_QUIRK_NO_CPORT_FEATURES BIT(0) #define GB_INTERFACE_QUIRK_NO_INIT_STATUS BIT(1) #define GB_INTERFACE_QUIRK_NO_ARA_IDS BIT(2) +#define GB_INTERFACE_QUIRK_FORCED_DISABLE BIT(3) struct gb_interface { struct device dev; -- cgit v1.2.3-59-g8ed1b From aca7aab39aa2d69d0a5b1cfc9319506b7c26b79d Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:25 +0200 Subject: greybus: connection: add control connection flag Add control connection flag which will be set for control connections. Also add a helper function to test if the flag is set. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 15 ++++++++------- drivers/staging/greybus/connection.h | 6 ++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7e07ef832b7c..2da713ca7fea 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -227,7 +227,8 @@ gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id, struct gb_connection * gb_connection_create_control(struct gb_interface *intf) { - return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL, 0); + return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL, + GB_CONNECTION_FLAG_CONTROL); } struct gb_connection * @@ -412,11 +413,11 @@ static int gb_connection_control_connected(struct gb_connection *connection) return 0; } - control = connection->intf->control; - - if (connection == control->connection) + if (gb_connection_is_control(connection)) return 0; + control = connection->intf->control; + ret = gb_control_connected_operation(control, cport_id); if (ret) { dev_err(&connection->bundle->dev, @@ -438,11 +439,11 @@ gb_connection_control_disconnected(struct gb_connection *connection) if (gb_connection_is_static(connection)) return; - control = connection->intf->control; - - if (connection == control->connection) + if (gb_connection_is_control(connection)) return; + control = connection->intf->control; + ret = gb_control_disconnected_operation(control, cport_id); if (ret) { dev_warn(&connection->bundle->dev, diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index f1592391acf1..6120c088648a 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -17,6 +17,7 @@ #define GB_CONNECTION_FLAG_NO_FLOWCTRL BIT(1) #define GB_CONNECTION_FLAG_OFFLOADED BIT(2) #define GB_CONNECTION_FLAG_CDSI1 BIT(3) +#define GB_CONNECTION_FLAG_CONTROL BIT(4) enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, @@ -104,6 +105,11 @@ static inline bool gb_connection_is_offloaded(struct gb_connection *connection) return connection->flags & GB_CONNECTION_FLAG_OFFLOADED; } +static inline bool gb_connection_is_control(struct gb_connection *connection) +{ + return connection->flags & GB_CONNECTION_FLAG_CONTROL; +} + static inline void *gb_connection_get_data(struct gb_connection *connection) { return connection->private; -- cgit v1.2.3-59-g8ed1b From 197616e227b5b562814828df4dac9477e27b0149 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:26 +0200 Subject: greybus: control: implement disconnecting operation Implement the new disconnecting control operation needed for proper connection tear down. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 12 ++++++++++++ drivers/staging/greybus/control.h | 2 ++ drivers/staging/greybus/greybus_protocols.h | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index a5effcf09f56..c5cdf3c8ccf8 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -149,6 +149,18 @@ int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id) sizeof(request), NULL, 0); } +int gb_control_disconnecting_operation(struct gb_control *control, + u16 cport_id) +{ + struct gb_control_disconnecting_request request; + + request.cport_id = cpu_to_le16(cport_id); + + return gb_operation_sync(control->connection, + GB_CONTROL_TYPE_DISCONNECTING, &request, + sizeof(request), NULL, 0); +} + int gb_control_mode_switch_operation(struct gb_control *control) { return gb_operation_unidirectional(control->connection, diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 33e7e2795d1e..9891fbdd1ff7 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -36,6 +36,8 @@ void gb_control_put(struct gb_control *control); int gb_control_get_bundle_versions(struct gb_control *control); int gb_control_connected_operation(struct gb_control *control, u16 cport_id); int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id); +int gb_control_disconnecting_operation(struct gb_control *control, + u16 cport_id); int gb_control_mode_switch_operation(struct gb_control *control); int gb_control_get_manifest_size_operation(struct gb_interface *intf); int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b98f02c93b1a..216a1d1c3481 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -123,6 +123,7 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE 0x09 /* Unused 0x0a */ #define GB_CONTROL_TYPE_BUNDLE_VERSION 0x0b +#define GB_CONTROL_TYPE_DISCONNECTING 0x0c #define GB_CONTROL_TYPE_TIMESYNC_GET_LAST_EVENT 0x0d #define GB_CONTROL_TYPE_MODE_SWITCH 0x0e @@ -160,6 +161,11 @@ struct gb_control_connected_request { __le16 cport_id; } __packed; +struct gb_control_disconnecting_request { + __le16 cport_id; +} __packed; +/* disconnecting response has no payload */ + struct gb_control_disconnected_request { __le16 cport_id; } __packed; -- cgit v1.2.3-59-g8ed1b From 96f5ab00c2a1bfc698bb9a51301f1cc1f83aa8e2 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:27 +0200 Subject: greybus: svc: add stub connection-quiescing operation Add stubbed out connection-quiescing operation that is needed for proper connection tear down. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 10 ++++++++++ drivers/staging/greybus/svc.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 2b3b8d98053c..4176e231b14a 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -438,6 +438,16 @@ int gb_svc_connection_create(struct gb_svc *svc, } EXPORT_SYMBOL_GPL(gb_svc_connection_create); +void gb_svc_connection_quiescing(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id) +{ + /* FIXME: implement */ + + dev_dbg(&svc->dev, "%s - (%u:%u %u:%u)\n", __func__, + intf1_id, cport1_id, intf2_id, cport2_id); +} +EXPORT_SYMBOL_GPL(gb_svc_connection_quiescing); + void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) { diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index e3e0aa14b08d..7a78c5bffbe6 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -64,6 +64,8 @@ int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id, u8 cport_flags); +void gb_svc_connection_quiescing(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, + u8 intf2_id, u16 cport2_id); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id); -- cgit v1.2.3-59-g8ed1b From 3cbe52c2e07ddb2c1f75e0ff9ff4e33a8e478877 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:28 +0200 Subject: greybus: connection: log cport-enable error messages Log failures to disable a host cport, and include the connection name in cport enable/disable error messages. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2da713ca7fea..785b60b09c46 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -278,7 +278,8 @@ static int gb_connection_hd_cport_enable(struct gb_connection *connection) ret = hd->driver->cport_enable(hd, connection->hd_cport_id); if (ret) { dev_err(&hd->dev, - "failed to enable host cport: %d\n", ret); + "%s: failed to enable host cport: %d\n", + connection->name, ret); return ret; } @@ -288,11 +289,17 @@ static int gb_connection_hd_cport_enable(struct gb_connection *connection) static void gb_connection_hd_cport_disable(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; + int ret; if (!hd->driver->cport_disable) return; - hd->driver->cport_disable(hd, connection->hd_cport_id); + ret = hd->driver->cport_disable(hd, connection->hd_cport_id); + if (ret) { + dev_err(&hd->dev, + "%s: failed to disable host cport: %d\n", + connection->name, ret); + } } static int -- cgit v1.2.3-59-g8ed1b From 6adcf44170db6cddcbbf9321244e051a72120555 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:29 +0200 Subject: greybus: es2: reset cports at disable Make sure to reset CPorts at disable rather than enable as per specification. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index fd59c143d0f3..68a8461ea84c 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -602,7 +602,7 @@ static void es2_cport_release(struct gb_host_device *hd, u16 cport_id) ida_simple_remove(&hd->cport_id_map, cport_id); } -static int cport_enable(struct gb_host_device *hd, u16 cport_id) +static int cport_disable(struct gb_host_device *hd, u16 cport_id) { int retval; @@ -794,7 +794,7 @@ static struct gb_hd_driver es2_driver = { .message_cancel = message_cancel, .cport_allocate = es2_cport_allocate, .cport_release = es2_cport_release, - .cport_enable = cport_enable, + .cport_disable = cport_disable, .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, .output = output, -- cgit v1.2.3-59-g8ed1b From e2efe1bbc42896c4c24fec16785be5264a2d7794 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:30 +0200 Subject: greybus: connection: rename an error label Rename an error label to make it more descriptive. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 785b60b09c46..9c7133e3ed4b 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -566,11 +566,11 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) ret = gb_connection_control_connected(connection); if (ret) - goto err_svc_destroy; + goto err_flush_operations; return 0; -err_svc_destroy: +err_flush_operations: spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; gb_connection_cancel_operations(connection, -ESHUTDOWN); -- cgit v1.2.3-59-g8ed1b From 00ad6975e7ca131a446c3c2e6510eec39664646d Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:31 +0200 Subject: greybus: connection: move CPort Buffer configuration out of svc helpers The CPort Buffer configuration in the host-device needs to match the CPort feature flags set by the SVC, but they need not always be configured at the same point in time. This will be used when implementing proper connection tear down. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 9c7133e3ed4b..f3a3915de272 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -345,7 +345,7 @@ gb_connection_svc_connection_create(struct gb_connection *connection) int ret; if (gb_connection_is_static(connection)) - return gb_connection_hd_cport_features_enable(connection); + return 0; intf = connection->intf; @@ -373,23 +373,12 @@ gb_connection_svc_connection_create(struct gb_connection *connection) return ret; } - ret = gb_connection_hd_cport_features_enable(connection); - if (ret) { - gb_svc_connection_destroy(hd->svc, hd->svc->ap_intf_id, - connection->hd_cport_id, - intf->interface_id, - connection->intf_cport_id); - return ret; - } - return 0; } static void gb_connection_svc_connection_destroy(struct gb_connection *connection) { - gb_connection_hd_cport_features_disable(connection); - if (gb_connection_is_static(connection)) return; @@ -557,6 +546,10 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) if (ret) goto err_hd_cport_disable; + ret = gb_connection_hd_cport_features_enable(connection); + if (ret) + goto err_svc_connection_destroy; + spin_lock_irq(&connection->lock); if (connection->handler && rx) connection->state = GB_CONNECTION_STATE_ENABLED; @@ -576,6 +569,8 @@ err_flush_operations: gb_connection_cancel_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); + gb_connection_hd_cport_features_disable(connection); +err_svc_connection_destroy: gb_connection_svc_connection_destroy(connection); err_hd_cport_disable: gb_connection_hd_cport_disable(connection); @@ -654,6 +649,7 @@ void gb_connection_disable(struct gb_connection *connection) gb_connection_cancel_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); + gb_connection_hd_cport_features_disable(connection); gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); @@ -675,6 +671,7 @@ void gb_connection_disable_forced(struct gb_connection *connection) gb_connection_cancel_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); + gb_connection_hd_cport_features_disable(connection); gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); -- cgit v1.2.3-59-g8ed1b From 1430cc920ab374a94f3a31574a2d616dd7f4c4d1 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:32 +0200 Subject: greybus: connection: remove unused invalid state Remove the unused legacy INVALID connection state. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 6120c088648a..9a35e6196b17 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -20,10 +20,9 @@ #define GB_CONNECTION_FLAG_CONTROL BIT(4) enum gb_connection_state { - GB_CONNECTION_STATE_INVALID = 0, - GB_CONNECTION_STATE_DISABLED = 1, - GB_CONNECTION_STATE_ENABLED_TX = 2, - GB_CONNECTION_STATE_ENABLED = 3, + GB_CONNECTION_STATE_DISABLED = 0, + GB_CONNECTION_STATE_ENABLED_TX = 1, + GB_CONNECTION_STATE_ENABLED = 2, }; struct gb_operation; -- cgit v1.2.3-59-g8ed1b From 8890f95710d835ba9f843c7dd849858da1d847d8 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:33 +0200 Subject: greybus: operation: clean up early connection-state check Now that the legacy invalid state is gone, clean up the early connection-state check in the receive path and explicitly drop incoming messages for disabled connection. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 20a8d74473d0..aaa1345b19d9 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -968,8 +968,7 @@ void gb_connection_recv(struct gb_connection *connection, size_t msg_size; u16 operation_id; - if ((connection->state != GB_CONNECTION_STATE_ENABLED && - connection->state != GB_CONNECTION_STATE_ENABLED_TX) || + if (connection->state == GB_CONNECTION_STATE_DISABLED || gb_connection_is_offloaded(connection)) { dev_warn_ratelimited(dev, "%s: dropping %zu received bytes\n", connection->name, size); -- cgit v1.2.3-59-g8ed1b From 77bbbcf6d36d7ef2054a8b0dc8396cc0f092945e Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:34 +0200 Subject: greybus: operation: restructure activation state handling Restructure the operation activation state handling in preparation for a new disconnecting state. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index aaa1345b19d9..75d1231cfde5 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -47,11 +47,15 @@ static int gb_operation_get_active(struct gb_operation *operation) spin_lock_irqsave(&connection->lock, flags); - if (connection->state != GB_CONNECTION_STATE_ENABLED && - (connection->state != GB_CONNECTION_STATE_ENABLED_TX || - gb_operation_is_incoming(operation))) { - spin_unlock_irqrestore(&connection->lock, flags); - return -ENOTCONN; + switch (connection->state) { + case GB_CONNECTION_STATE_ENABLED: + break; + case GB_CONNECTION_STATE_ENABLED_TX: + if (gb_operation_is_incoming(operation)) + goto err_unlock; + break; + default: + goto err_unlock; } if (operation->active++ == 0) @@ -62,6 +66,11 @@ static int gb_operation_get_active(struct gb_operation *operation) spin_unlock_irqrestore(&connection->lock, flags); return 0; + +err_unlock: + spin_unlock_irqrestore(&connection->lock, flags); + + return -ENOTCONN; } /* Caller holds operation reference. */ -- cgit v1.2.3-59-g8ed1b From 18079ece816b3b272b114b770e636f4e5567b028 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:35 +0200 Subject: greybus: operation: add helper for creating core operations Add a new core operation flag and a helper that core can use to create core operations. This will be used to implement the ping operations that core sends as part of connection tear down. Note that a new trace point is also added. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 5 +++++ drivers/staging/greybus/operation.c | 20 ++++++++++++++++++++ drivers/staging/greybus/operation.h | 12 ++++++++++++ 3 files changed, 37 insertions(+) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 93e04a85506f..148ffaf4c6b4 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -130,6 +130,11 @@ DECLARE_EVENT_CLASS(gb_operation, */ DEFINE_OPERATION_EVENT(gb_operation_create); +/* + * Occurs after a new core operation has been created. + */ +DEFINE_OPERATION_EVENT(gb_operation_create_core); + /* * Occurs after a new operation has been created for an incoming * request has been successfully created and initialized. diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 75d1231cfde5..fb4a29e2c09f 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -571,6 +571,26 @@ gb_operation_create_flags(struct gb_connection *connection, } EXPORT_SYMBOL_GPL(gb_operation_create_flags); +struct gb_operation * +gb_operation_create_core(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size, unsigned long flags, + gfp_t gfp) +{ + struct gb_operation *operation; + + flags |= GB_OPERATION_FLAG_CORE; + + operation = gb_operation_create_common(connection, type, + request_size, response_size, + flags, gfp); + if (operation) + trace_gb_operation_create_core(operation); + + return operation; +} +/* Do not export this function. */ + size_t gb_operation_get_payload_size_max(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h index 117d7df2e0bd..de09a2c7de54 100644 --- a/drivers/staging/greybus/operation.h +++ b/drivers/staging/greybus/operation.h @@ -60,6 +60,7 @@ struct gb_message { #define GB_OPERATION_FLAG_INCOMING BIT(0) #define GB_OPERATION_FLAG_UNIDIRECTIONAL BIT(1) #define GB_OPERATION_FLAG_SHORT_RESPONSE BIT(2) +#define GB_OPERATION_FLAG_CORE BIT(3) #define GB_OPERATION_FLAG_USER_MASK (GB_OPERATION_FLAG_SHORT_RESPONSE | \ GB_OPERATION_FLAG_UNIDIRECTIONAL) @@ -123,6 +124,11 @@ gb_operation_short_response_allowed(struct gb_operation *operation) return operation->flags & GB_OPERATION_FLAG_SHORT_RESPONSE; } +static inline bool gb_operation_is_core(struct gb_operation *operation) +{ + return operation->flags & GB_OPERATION_FLAG_CORE; +} + void gb_connection_recv(struct gb_connection *connection, void *data, size_t size); @@ -144,6 +150,12 @@ gb_operation_create(struct gb_connection *connection, response_size, 0, gfp); } +struct gb_operation * +gb_operation_create_core(struct gb_connection *connection, + u8 type, size_t request_size, + size_t response_size, unsigned long flags, + gfp_t gfp); + void gb_operation_get(struct gb_operation *operation); void gb_operation_put(struct gb_operation *operation); -- cgit v1.2.3-59-g8ed1b From 3de5acfafb3c610bc6f7013a1583852cbc2747f1 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:36 +0200 Subject: greybus: connection: implement proper connection closure Implement proper connection closure, which includes sending ping requests on the connection being tore down while coordinating with the remote interface as well as the SVC. This specifically implements the new ping operation, which in case of offloaded connections is handled by the host-device driver in an implementation-defined manner through a new callback. Note that the normal connection tear-down procedure is executed in case of failed connection establishment due to failed connected operation. Specifically, the disconnecting request is sent also in case the connected operation never succeeded. This is needed since the interface may have enabled FCT flow upon receiving the connected request. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 109 ++++++++++++++++++++++++++-- drivers/staging/greybus/connection.h | 7 +- drivers/staging/greybus/greybus_protocols.h | 1 + drivers/staging/greybus/hd.h | 1 + drivers/staging/greybus/operation.c | 5 +- 5 files changed, 112 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f3a3915de272..77067515a28a 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -389,6 +389,21 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection) connection->intf_cport_id); } +static void +gb_connection_svc_connection_quiescing(struct gb_connection *connection) +{ + struct gb_host_device *hd = connection->hd; + + if (gb_connection_is_static(connection)) + return; + + gb_svc_connection_quiescing(hd->svc, + hd->svc->ap_intf_id, + connection->hd_cport_id, + connection->intf->interface_id, + connection->intf_cport_id); +} + /* Inform Interface about active CPorts */ static int gb_connection_control_connected(struct gb_connection *connection) { @@ -424,7 +439,26 @@ static int gb_connection_control_connected(struct gb_connection *connection) return 0; } -/* Inform Interface about inactive CPorts */ +static void +gb_connection_control_disconnecting(struct gb_connection *connection) +{ + struct gb_control *control; + u16 cport_id = connection->intf_cport_id; + int ret; + + if (gb_connection_is_static(connection)) + return; + + control = connection->intf->control; + + ret = gb_control_disconnecting_operation(control, cport_id); + if (ret) { + dev_err(&connection->hd->dev, + "%s: failed to send disconnecting: %d\n", + connection->name, ret); + } +} + static void gb_connection_control_disconnected(struct gb_connection *connection) { @@ -447,10 +481,56 @@ gb_connection_control_disconnected(struct gb_connection *connection) } } +static int gb_connection_ping_operation(struct gb_connection *connection) +{ + struct gb_operation *operation; + int ret; + + operation = gb_operation_create_core(connection, + GB_REQUEST_TYPE_PING, + 0, 0, 0, + GFP_KERNEL); + if (!operation) + return -ENOMEM; + + ret = gb_operation_request_send_sync(operation); + + gb_operation_put(operation); + + return ret; +} + +static int gb_connection_ping(struct gb_connection *connection) +{ + struct gb_host_device *hd = connection->hd; + int ret; + + if (gb_connection_is_static(connection)) + return 0; + + if (gb_connection_is_offloaded(connection)) { + if (!hd->driver->cport_ping) + return 0; + + ret = hd->driver->cport_ping(hd, connection->intf_cport_id); + } else { + ret = gb_connection_ping_operation(connection); + } + + if (ret) { + dev_err(&hd->dev, "%s: failed to send ping: %d\n", + connection->name, ret); + return ret; + } + + return 0; +} + /* * Cancel all active operations on a connection. * - * Locking: Called with connection lock held and state set to DISABLED. + * Locking: Called with connection lock held and state set to DISABLED or + * DISCONNECTING. */ static void gb_connection_cancel_operations(struct gb_connection *connection, int errno) @@ -559,17 +639,24 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) ret = gb_connection_control_connected(connection); if (ret) - goto err_flush_operations; + goto err_control_disconnecting; return 0; -err_flush_operations: +err_control_disconnecting: + gb_connection_control_disconnecting(connection); + spin_lock_irq(&connection->lock); - connection->state = GB_CONNECTION_STATE_DISABLED; + connection->state = GB_CONNECTION_STATE_DISCONNECTING; gb_connection_cancel_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); + gb_connection_ping(connection); gb_connection_hd_cport_features_disable(connection); + gb_connection_svc_connection_quiescing(connection); + gb_connection_ping(connection); + gb_connection_control_disconnected(connection); + connection->state = GB_CONNECTION_STATE_DISABLED; err_svc_connection_destroy: gb_connection_svc_connection_destroy(connection); err_hd_cport_disable: @@ -642,14 +729,22 @@ void gb_connection_disable(struct gb_connection *connection) if (connection->state == GB_CONNECTION_STATE_DISABLED) goto out_unlock; - gb_connection_control_disconnected(connection); + gb_connection_control_disconnecting(connection); spin_lock_irq(&connection->lock); - connection->state = GB_CONNECTION_STATE_DISABLED; + connection->state = GB_CONNECTION_STATE_DISCONNECTING; gb_connection_cancel_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); + gb_connection_ping(connection); gb_connection_hd_cport_features_disable(connection); + gb_connection_svc_connection_quiescing(connection); + gb_connection_ping(connection); + + gb_connection_control_disconnected(connection); + + connection->state = GB_CONNECTION_STATE_DISABLED; + gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 9a35e6196b17..af171f5f0635 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -20,9 +20,10 @@ #define GB_CONNECTION_FLAG_CONTROL BIT(4) enum gb_connection_state { - GB_CONNECTION_STATE_DISABLED = 0, - GB_CONNECTION_STATE_ENABLED_TX = 1, - GB_CONNECTION_STATE_ENABLED = 2, + GB_CONNECTION_STATE_DISABLED = 0, + GB_CONNECTION_STATE_ENABLED_TX = 1, + GB_CONNECTION_STATE_ENABLED = 2, + GB_CONNECTION_STATE_DISCONNECTING = 3, }; struct gb_operation; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 216a1d1c3481..6afd0a6a98cd 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -96,6 +96,7 @@ struct gb_operation_msg_hdr { /* Generic request types */ +#define GB_REQUEST_TYPE_PING 0x00 #define GB_REQUEST_TYPE_PROTOCOL_VERSION 0x01 #define GB_REQUEST_TYPE_INVALID 0x7f diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index ad229622654e..7321cfdd41d7 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -21,6 +21,7 @@ struct gb_hd_driver { void (*cport_release)(struct gb_host_device *hd, u16 cport_id); int (*cport_enable)(struct gb_host_device *hd, u16 cport_id); int (*cport_disable)(struct gb_host_device *hd, u16 cport_id); + int (*cport_ping)(struct gb_host_device *hd, u16 cport_id); int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index fb4a29e2c09f..7906a95899e5 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -46,7 +46,6 @@ static int gb_operation_get_active(struct gb_operation *operation) unsigned long flags; spin_lock_irqsave(&connection->lock, flags); - switch (connection->state) { case GB_CONNECTION_STATE_ENABLED: break; @@ -54,6 +53,10 @@ static int gb_operation_get_active(struct gb_operation *operation) if (gb_operation_is_incoming(operation)) goto err_unlock; break; + case GB_CONNECTION_STATE_DISCONNECTING: + if (!gb_operation_is_core(operation)) + goto err_unlock; + break; default: goto err_unlock; } -- cgit v1.2.3-59-g8ed1b From 800d6c8f4e66cc6ff3737b4de1272c31cd9e1e3c Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:37 +0200 Subject: greybus: connection: add flush host-device callback Add a new host-device callback to flush any host-device queues, including any ongoing transfers, as part of connection tear down. The host-device buffers are flushed after the disconnecting operation have completed and the AP queues have been emptied. This can be used to flush already queued messages which upon reception would have been discarded by the remote end anyway. Note that this does not remove the need to flush any host-device queues as part of CPort disable which needs to make the CPort ready for reuse after deallocating all associated state and resetting the port. Suggested-by: Mitchell Tasman <tasman@leaflabs.com> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 24 ++++++++++++++++++++++++ drivers/staging/greybus/hd.h | 1 + 2 files changed, 25 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 77067515a28a..c1cdfcd830a4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -302,6 +302,24 @@ static void gb_connection_hd_cport_disable(struct gb_connection *connection) } } +static int gb_connection_hd_cport_flush(struct gb_connection *connection) +{ + struct gb_host_device *hd = connection->hd; + int ret; + + if (!hd->driver->cport_flush) + return 0; + + ret = hd->driver->cport_flush(hd, connection->hd_cport_id); + if (ret) { + dev_err(&hd->dev, "%s: failed to flush host cport: %d\n", + connection->name, ret); + return ret; + } + + return 0; +} + static int gb_connection_hd_cport_features_enable(struct gb_connection *connection) { @@ -651,6 +669,9 @@ err_control_disconnecting: gb_connection_cancel_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); + /* Transmit queue should already be empty. */ + gb_connection_hd_cport_flush(connection); + gb_connection_ping(connection); gb_connection_hd_cport_features_disable(connection); gb_connection_svc_connection_quiescing(connection); @@ -736,6 +757,8 @@ void gb_connection_disable(struct gb_connection *connection) gb_connection_cancel_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); + gb_connection_hd_cport_flush(connection); + gb_connection_ping(connection); gb_connection_hd_cport_features_disable(connection); gb_connection_svc_connection_quiescing(connection); @@ -766,6 +789,7 @@ void gb_connection_disable_forced(struct gb_connection *connection) gb_connection_cancel_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); + gb_connection_hd_cport_flush(connection); gb_connection_hd_cport_features_disable(connection); gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 7321cfdd41d7..5136d0c9ecc8 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -21,6 +21,7 @@ struct gb_hd_driver { void (*cport_release)(struct gb_host_device *hd, u16 cport_id); int (*cport_enable)(struct gb_host_device *hd, u16 cport_id); int (*cport_disable)(struct gb_host_device *hd, u16 cport_id); + int (*cport_flush)(struct gb_host_device *hd, u16 cport_id); int (*cport_ping)(struct gb_host_device *hd, u16 cport_id); int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); -- cgit v1.2.3-59-g8ed1b From e3fbe484323a026ed3689c72bac186ab2009dbcb Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:38 +0200 Subject: greybus: kernel_ver: backport reinit_completion to pre-3.13 Backport upstream reinit_completion() to pre-3.13 kernels. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index f5e62ed3d9f5..f6fb3bfcfe7a 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -313,4 +313,19 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev) #define SPI_NOR_MODALIAS "m25p80" #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +/** + * reinit_completion - reinitialize a completion structure + * @x: pointer to completion structure that is to be reinitialized + * + * This inline function should be used to reinitialize a completion structure + * so it can be reused. This is especially important after complete_all() is + * used. + */ +static inline void reinit_completion(struct completion *x) +{ + x->done = 0; +} +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ -- cgit v1.2.3-59-g8ed1b From 08f94352e8d09f7db07b4e894b3c223ee92df5ad Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:39 +0200 Subject: greybus: control: add error message to mode-switch helper Print an informative error message in case sending the mode-switch request fails. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index c5cdf3c8ccf8..3e02bbb5abfe 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -163,9 +163,18 @@ int gb_control_disconnecting_operation(struct gb_control *control, int gb_control_mode_switch_operation(struct gb_control *control) { - return gb_operation_unidirectional(control->connection, + int ret; + + ret = gb_operation_unidirectional(control->connection, GB_CONTROL_TYPE_MODE_SWITCH, NULL, 0); + if (ret) { + dev_err(&control->dev, "failed to send mode switch: %d\n", + ret); + return ret; + } + + return 0; } int gb_control_timesync_enable(struct gb_control *control, u8 count, -- cgit v1.2.3-59-g8ed1b From 55742d2a071a569bf20f90d37b1b5b8a25a3f882 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:40 +0200 Subject: greybus: interface: implement generic mode-switch functionality Add a generic interface for bundle drivers to use to request that a mode switch is carried out on its behalf. Mode switching involves tearing down all connections to an interface, sending a unidirectional mode-switch request, and waiting for a mailbox event that triggers deferred control connection reset and re-enumeration of the interface. In case of a timeout waiting for the interface mailbox event, or on other errors, the interface is powered off. All of this needs to be done by core from work-queue context in order not to block incoming SVC requests and bundle-device tear down. Care must also be taken to serialise against concurrent module removal events and eject requests. Special handling of legacy mode-switching is also added in order to continue to support the ES3 bootrom. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 36 ++++++- drivers/staging/greybus/connection.h | 5 + drivers/staging/greybus/control.c | 17 ++++ drivers/staging/greybus/control.h | 3 + drivers/staging/greybus/interface.c | 180 ++++++++++++++++++++++++++++++++++- drivers/staging/greybus/interface.h | 10 ++ drivers/staging/greybus/svc.c | 55 +---------- 7 files changed, 249 insertions(+), 57 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index c1cdfcd830a4..3be767b9a0de 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -487,10 +487,23 @@ gb_connection_control_disconnected(struct gb_connection *connection) if (gb_connection_is_static(connection)) return; - if (gb_connection_is_control(connection)) + control = connection->intf->control; + + if (gb_connection_is_control(connection)) { + if (connection->mode_switch) { + ret = gb_control_mode_switch_operation(control); + if (ret) { + /* + * Allow mode switch to time out waiting for + * mailbox event. + */ + return; + } + } + return; + } - control = connection->intf->control; ret = gb_control_disconnected_operation(control, cport_id); if (ret) { @@ -743,6 +756,18 @@ out_unlock: } EXPORT_SYMBOL_GPL(gb_connection_disable_rx); +void gb_connection_mode_switch_prepare(struct gb_connection *connection) +{ + connection->mode_switch = true; +} + +void gb_connection_mode_switch_complete(struct gb_connection *connection) +{ + gb_connection_svc_connection_destroy(connection); + gb_connection_hd_cport_disable(connection); + connection->mode_switch = false; +} + void gb_connection_disable(struct gb_connection *connection) { mutex_lock(&connection->mutex); @@ -768,8 +793,11 @@ void gb_connection_disable(struct gb_connection *connection) connection->state = GB_CONNECTION_STATE_DISABLED; - gb_connection_svc_connection_destroy(connection); - gb_connection_hd_cport_disable(connection); + /* control-connection tear down is deferred when mode switching */ + if (!connection->mode_switch) { + gb_connection_svc_connection_destroy(connection); + gb_connection_hd_cport_disable(connection); + } out_unlock: mutex_unlock(&connection->mutex); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index af171f5f0635..9cd0bac9ceb9 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -58,6 +58,8 @@ struct gb_connection { atomic_t op_cycle; void *private; + + bool mode_switch; }; struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, @@ -83,6 +85,9 @@ void gb_connection_disable_rx(struct gb_connection *connection); void gb_connection_disable(struct gb_connection *connection); void gb_connection_disable_forced(struct gb_connection *connection); +void gb_connection_mode_switch_prepare(struct gb_connection *connection); +void gb_connection_mode_switch_complete(struct gb_connection *connection); + void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, u8 *data, size_t length); diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 3e02bbb5abfe..d772c27a01df 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -364,7 +364,24 @@ void gb_control_del(struct gb_control *control) device_del(&control->dev); } +struct gb_control *gb_control_get(struct gb_control *control) +{ + get_device(&control->dev); + + return control; +} + void gb_control_put(struct gb_control *control) { put_device(&control->dev); } + +void gb_control_mode_switch_prepare(struct gb_control *control) +{ + gb_connection_mode_switch_prepare(control->connection); +} + +void gb_control_mode_switch_complete(struct gb_control *control) +{ + gb_connection_mode_switch_complete(control->connection); +} diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 9891fbdd1ff7..b1e5af25352a 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -31,6 +31,7 @@ int gb_control_enable(struct gb_control *control); void gb_control_disable(struct gb_control *control); int gb_control_add(struct gb_control *control); void gb_control_del(struct gb_control *control); +struct gb_control *gb_control_get(struct gb_control *control); void gb_control_put(struct gb_control *control); int gb_control_get_bundle_versions(struct gb_control *control); @@ -39,6 +40,8 @@ int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id); int gb_control_disconnecting_operation(struct gb_control *control, u16 cport_id); int gb_control_mode_switch_operation(struct gb_control *control); +void gb_control_mode_switch_prepare(struct gb_control *control); +void gb_control_mode_switch_complete(struct gb_control *control); int gb_control_get_manifest_size_operation(struct gb_interface *intf); int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, size_t size); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 2cfb5a46e7d4..b5d243b28f9a 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -8,9 +8,10 @@ */ #include "greybus.h" - #include "greybus_trace.h" +#define GB_INTERFACE_MODE_SWITCH_TIMEOUT 1000 + #define GB_INTERFACE_DEVICE_ID_BAD 0xff /* Don't-care selector index */ @@ -163,6 +164,174 @@ static void gb_interface_route_destroy(struct gb_interface *intf) intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; } +/* Locking: Caller holds the interface mutex. */ +static int gb_interface_legacy_mode_switch(struct gb_interface *intf) +{ + int ret; + + dev_info(&intf->dev, "legacy mode switch detected\n"); + + /* Mark as disconnected to prevent I/O during disable. */ + intf->disconnected = true; + gb_interface_disable(intf); + intf->disconnected = false; + + ret = gb_interface_enable(intf); + if (ret) { + dev_err(&intf->dev, "failed to re-enable interface: %d\n", ret); + gb_interface_deactivate(intf); + } + + return ret; +} + +void gb_interface_mailbox_event(struct gb_interface *intf, u16 result, + u32 mailbox) +{ + mutex_lock(&intf->mutex); + + if (result) { + dev_warn(&intf->dev, + "mailbox event with UniPro error: 0x%04x\n", + result); + goto err_disable; + } + + if (mailbox != GB_SVC_INTF_MAILBOX_GREYBUS) { + dev_warn(&intf->dev, + "mailbox event with unexpected value: 0x%08x\n", + mailbox); + goto err_disable; + } + + if (intf->quirks & GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH) { + gb_interface_legacy_mode_switch(intf); + goto out_unlock; + } + + if (!intf->mode_switch) { + dev_warn(&intf->dev, "unexpected mailbox event: 0x%08x\n", + mailbox); + goto err_disable; + } + + dev_info(&intf->dev, "mode switch detected\n"); + + complete(&intf->mode_switch_completion); + +out_unlock: + mutex_unlock(&intf->mutex); + + return; + +err_disable: + gb_interface_disable(intf); + gb_interface_deactivate(intf); + mutex_unlock(&intf->mutex); +} + +static void gb_interface_mode_switch_work(struct work_struct *work) +{ + struct gb_interface *intf; + struct gb_control *control; + unsigned long timeout; + int ret; + + intf = container_of(work, struct gb_interface, mode_switch_work); + + mutex_lock(&intf->mutex); + /* Make sure interface is still enabled. */ + if (!intf->enabled) { + dev_dbg(&intf->dev, "mode switch aborted\n"); + intf->mode_switch = false; + mutex_unlock(&intf->mutex); + goto out_interface_put; + } + + /* + * Prepare the control device for mode switch and make sure to get an + * extra reference before it goes away during interface disable. + */ + control = gb_control_get(intf->control); + gb_control_mode_switch_prepare(control); + gb_interface_disable(intf); + mutex_unlock(&intf->mutex); + + timeout = msecs_to_jiffies(GB_INTERFACE_MODE_SWITCH_TIMEOUT); + ret = wait_for_completion_interruptible_timeout( + &intf->mode_switch_completion, timeout); + + /* Finalise control-connection mode switch. */ + gb_control_mode_switch_complete(control); + gb_control_put(control); + + if (ret < 0) { + dev_err(&intf->dev, "mode switch interrupted\n"); + goto err_deactivate; + } else if (ret == 0) { + dev_err(&intf->dev, "mode switch timed out\n"); + goto err_deactivate; + } + + /* Re-enable (re-enumerate) interface if still active. */ + mutex_lock(&intf->mutex); + intf->mode_switch = false; + if (intf->active) { + ret = gb_interface_enable(intf); + if (ret) { + dev_err(&intf->dev, "failed to re-enable interface: %d\n", + ret); + gb_interface_deactivate(intf); + } + } + mutex_unlock(&intf->mutex); + +out_interface_put: + gb_interface_put(intf); + + return; + +err_deactivate: + mutex_lock(&intf->mutex); + intf->mode_switch = false; + gb_interface_deactivate(intf); + mutex_unlock(&intf->mutex); + + gb_interface_put(intf); +} + +int gb_interface_request_mode_switch(struct gb_interface *intf) +{ + int ret = 0; + + mutex_lock(&intf->mutex); + if (intf->mode_switch) { + ret = -EBUSY; + goto out_unlock; + } + + intf->mode_switch = true; + reinit_completion(&intf->mode_switch_completion); + + /* + * Get a reference to the interface device, which will be put once the + * mode switch is complete. + */ + get_device(&intf->dev); + + if (!queue_work(system_long_wq, &intf->mode_switch_work)) { + put_device(&intf->dev); + ret = -EBUSY; + goto out_unlock; + } + +out_unlock: + mutex_unlock(&intf->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(gb_interface_request_mode_switch); + /* * T_TstSrcIncrement is written by the module on ES2 as a stand-in for the * init-status attribute DME_TOSHIBA_INIT_STATUS. The AP needs to read and @@ -222,7 +391,8 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) * for example, requires E2EFC, CSD and CSV to be disabled. */ bootrom_quirks = GB_INTERFACE_QUIRK_NO_CPORT_FEATURES | - GB_INTERFACE_QUIRK_FORCED_DISABLE; + GB_INTERFACE_QUIRK_FORCED_DISABLE | + GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH; switch (init_status) { case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED: case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: @@ -368,6 +538,8 @@ struct gb_interface *gb_interface_create(struct gb_module *module, INIT_LIST_HEAD(&intf->bundles); INIT_LIST_HEAD(&intf->manifest_descs); mutex_init(&intf->mutex); + INIT_WORK(&intf->mode_switch_work, gb_interface_mode_switch_work); + init_completion(&intf->mode_switch_completion); /* Invalid device id to start with */ intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; @@ -542,6 +714,10 @@ void gb_interface_deactivate(struct gb_interface *intf) trace_gb_interface_deactivate(intf); + /* Abort any ongoing mode switch. */ + if (intf->mode_switch) + complete(&intf->mode_switch_completion); + gb_interface_route_destroy(intf); gb_interface_hibernate_link(intf); gb_interface_unipro_set(intf, false); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index e833f7df025d..603f146dce42 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -14,6 +14,7 @@ #define GB_INTERFACE_QUIRK_NO_INIT_STATUS BIT(1) #define GB_INTERFACE_QUIRK_NO_ARA_IDS BIT(2) #define GB_INTERFACE_QUIRK_FORCED_DISABLE BIT(3) +#define GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH BIT(4) struct gb_interface { struct device dev; @@ -40,9 +41,14 @@ struct gb_interface { struct mutex mutex; bool disconnected; + bool ejected; bool active; bool enabled; + bool mode_switch; + + struct work_struct mode_switch_work; + struct completion mode_switch_completion; }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) @@ -55,5 +61,9 @@ void gb_interface_disable(struct gb_interface *intf); int gb_interface_add(struct gb_interface *intf); void gb_interface_del(struct gb_interface *intf); void gb_interface_put(struct gb_interface *intf); +void gb_interface_mailbox_event(struct gb_interface *intf, u16 result, + u32 mailbox); + +int gb_interface_request_mode_switch(struct gb_interface *intf); #endif /* __INTERFACE_H */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 4176e231b14a..9df3f570eb83 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -895,28 +895,6 @@ static struct gb_module *gb_svc_module_lookup(struct gb_svc *svc, u8 module_id) return NULL; } -static void gb_svc_intf_reenable(struct gb_svc *svc, struct gb_interface *intf) -{ - int ret; - - mutex_lock(&intf->mutex); - - /* Mark as disconnected to prevent I/O during disable. */ - intf->disconnected = true; - gb_interface_disable(intf); - intf->disconnected = false; - - ret = gb_interface_enable(intf); - if (ret) { - dev_err(&svc->dev, "failed to enable interface %u: %d\n", - intf->interface_id, ret); - - gb_interface_deactivate(intf); - } - - mutex_unlock(&intf->mutex); -} - static void gb_svc_process_hello_deferred(struct gb_operation *operation) { struct gb_connection *connection = operation->connection; @@ -965,10 +943,9 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation) /* All modules are considered 1x2 for now */ module = gb_svc_module_lookup(svc, intf_id); if (module) { - dev_info(&svc->dev, "mode switch detected on interface %u\n", - intf_id); - - return gb_svc_intf_reenable(svc, module->interfaces[0]); + /* legacy mode switch */ + return gb_interface_mailbox_event(module->interfaces[0], 0, + GB_SVC_INTF_MAILBOX_GREYBUS); } module = gb_module_create(hd, intf_id, 1); @@ -1115,31 +1092,7 @@ static void gb_svc_process_intf_mailbox_event(struct gb_operation *operation) return; } - if (result_code) { - dev_warn(&svc->dev, - "mailbox event %u with UniPro error: 0x%04x\n", - intf_id, result_code); - goto err_disable_interface; - } - - if (mailbox != GB_SVC_INTF_MAILBOX_GREYBUS) { - dev_warn(&svc->dev, - "mailbox event %u with unexected value: 0x%08x\n", - intf_id, mailbox); - goto err_disable_interface; - } - - dev_info(&svc->dev, "mode switch detected on interface %u\n", intf_id); - - gb_svc_intf_reenable(svc, intf); - - return; - -err_disable_interface: - mutex_lock(&intf->mutex); - gb_interface_disable(intf); - gb_interface_deactivate(intf); - mutex_unlock(&intf->mutex); + gb_interface_mailbox_event(intf, result_code, mailbox); } static void gb_svc_process_deferred_request(struct work_struct *work) -- cgit v1.2.3-59-g8ed1b From 8ccf389b1909a644e5941217e243ca1c663c8160 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 17:26:41 +0200 Subject: greybus: svc: remove deprecated hotplug operations Now that firmware has made the switch to the new interface boot sequence, we can remove support for the deprecated hotplug and hot-unplug operations. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 19 ----- drivers/staging/greybus/svc.c | 122 ---------------------------- 2 files changed, 141 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 6afd0a6a98cd..77ac3f2a48d2 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -908,8 +908,6 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_PROTOCOL_VERSION 0x01 #define GB_SVC_TYPE_SVC_HELLO 0x02 #define GB_SVC_TYPE_INTF_DEVICE_ID 0x03 -#define GB_SVC_TYPE_INTF_HOTPLUG 0x04 -#define GB_SVC_TYPE_INTF_HOT_UNPLUG 0x05 #define GB_SVC_TYPE_INTF_RESET 0x06 #define GB_SVC_TYPE_CONN_CREATE 0x07 #define GB_SVC_TYPE_CONN_DESTROY 0x08 @@ -983,23 +981,6 @@ struct gb_svc_intf_device_id_request { } __packed; /* device id response has no payload */ -struct gb_svc_intf_hotplug_request { - __u8 intf_id; - struct { - __le32 ddbl1_mfr_id; - __le32 ddbl1_prod_id; - __le32 ara_vend_id; - __le32 ara_prod_id; - __le64 serial_number; - } data; -} __packed; -/* hotplug response has no payload */ - -struct gb_svc_intf_hot_unplug_request { - __u8 intf_id; -} __packed; -/* hot unplug response has no payload */ - struct gb_svc_intf_reset_request { __u8 intf_id; } __packed; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 9df3f570eb83..b41d2622d94d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -924,73 +924,6 @@ static void gb_svc_process_hello_deferred(struct gb_operation *operation) ret); } -static void gb_svc_process_intf_hotplug(struct gb_operation *operation) -{ - struct gb_svc_intf_hotplug_request *request; - struct gb_connection *connection = operation->connection; - struct gb_svc *svc = gb_connection_get_data(connection); - struct gb_host_device *hd = connection->hd; - struct gb_module *module; - u8 intf_id; - int ret; - - /* The request message size has already been verified. */ - request = operation->request->payload; - intf_id = request->intf_id; - - dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); - - /* All modules are considered 1x2 for now */ - module = gb_svc_module_lookup(svc, intf_id); - if (module) { - /* legacy mode switch */ - return gb_interface_mailbox_event(module->interfaces[0], 0, - GB_SVC_INTF_MAILBOX_GREYBUS); - } - - module = gb_module_create(hd, intf_id, 1); - if (!module) { - dev_err(&svc->dev, "failed to create module\n"); - return; - } - - ret = gb_module_add(module); - if (ret) { - gb_module_put(module); - return; - } - - list_add(&module->hd_node, &hd->modules); -} - -static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation) -{ - struct gb_svc *svc = gb_connection_get_data(operation->connection); - struct gb_svc_intf_hot_unplug_request *request; - struct gb_module *module; - u8 intf_id; - - /* The request message size has already been verified. */ - request = operation->request->payload; - intf_id = request->intf_id; - - dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id); - - /* All modules are considered 1x2 for now */ - module = gb_svc_module_lookup(svc, intf_id); - if (!module) { - dev_warn(&svc->dev, "could not find hot-unplug interface %u\n", - intf_id); - return; - } - - module->disconnected = true; - - gb_module_del(module); - list_del(&module->hd_node); - gb_module_put(module); -} - static void gb_svc_process_module_inserted(struct gb_operation *operation) { struct gb_svc_module_inserted_request *request; @@ -1111,12 +1044,6 @@ static void gb_svc_process_deferred_request(struct work_struct *work) case GB_SVC_TYPE_SVC_HELLO: gb_svc_process_hello_deferred(operation); break; - case GB_SVC_TYPE_INTF_HOTPLUG: - gb_svc_process_intf_hotplug(operation); - break; - case GB_SVC_TYPE_INTF_HOT_UNPLUG: - gb_svc_process_intf_hot_unplug(operation); - break; case GB_SVC_TYPE_MODULE_INSERTED: gb_svc_process_module_inserted(operation); break; @@ -1153,51 +1080,6 @@ static int gb_svc_queue_deferred_request(struct gb_operation *operation) return 0; } -/* - * Bringing up a module can be time consuming, as that may require lots of - * initialization on the module side. Over that, we may also need to download - * the firmware first and flash that on the module. - * - * In order not to make other svc events wait for all this to finish, - * handle most of module hotplug stuff outside of the hotplug callback, with - * help of a workqueue. - */ -static int gb_svc_intf_hotplug_recv(struct gb_operation *op) -{ - struct gb_svc *svc = gb_connection_get_data(op->connection); - struct gb_svc_intf_hotplug_request *request; - - if (op->request->payload_size < sizeof(*request)) { - dev_warn(&svc->dev, "short hotplug request received (%zu < %zu)\n", - op->request->payload_size, sizeof(*request)); - return -EINVAL; - } - - request = op->request->payload; - - dev_dbg(&svc->dev, "%s - id = %u\n", __func__, request->intf_id); - - return gb_svc_queue_deferred_request(op); -} - -static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op) -{ - struct gb_svc *svc = gb_connection_get_data(op->connection); - struct gb_svc_intf_hot_unplug_request *request; - - if (op->request->payload_size < sizeof(*request)) { - dev_warn(&svc->dev, "short hot unplug request received (%zu < %zu)\n", - op->request->payload_size, sizeof(*request)); - return -EINVAL; - } - - request = op->request->payload; - - dev_dbg(&svc->dev, "%s - id = %u\n", __func__, request->intf_id); - - return gb_svc_queue_deferred_request(op); -} - static int gb_svc_intf_reset_recv(struct gb_operation *op) { struct gb_svc *svc = gb_connection_get_data(op->connection); @@ -1371,10 +1253,6 @@ static int gb_svc_request_handler(struct gb_operation *op) if (!ret) svc->state = GB_SVC_STATE_SVC_HELLO; return ret; - case GB_SVC_TYPE_INTF_HOTPLUG: - return gb_svc_intf_hotplug_recv(op); - case GB_SVC_TYPE_INTF_HOT_UNPLUG: - return gb_svc_intf_hot_unplug_recv(op); case GB_SVC_TYPE_INTF_RESET: return gb_svc_intf_reset_recv(op); case GB_SVC_TYPE_KEY_EVENT: -- cgit v1.2.3-59-g8ed1b From dacf3eb7f3c1c474a55f7c24eef39011784d558c Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 18:23:02 +0200 Subject: greybus: fix tracepoint-related whitespace issues Fix some whitespace issues introduced by the recent tracepoint changes. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/module.c | 2 +- drivers/staging/greybus/operation.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index c56b98870b6f..7f0ed9fc2536 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -8,9 +8,9 @@ */ #include "greybus.h" - #include "greybus_trace.h" + static ssize_t eject_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 7906a95899e5..659e84b10c7f 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -570,7 +570,6 @@ gb_operation_create_flags(struct gb_connection *connection, trace_gb_operation_create(operation); return operation; - } EXPORT_SYMBOL_GPL(gb_operation_create_flags); -- cgit v1.2.3-59-g8ed1b From 12823178332216daa76512d256dbdacb5ba76ea7 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 27 May 2016 18:23:01 +0200 Subject: greybus: hd: fix gb_hd_release tracepoint The recently added gb_hd_release tracepoint was added to the wrong function. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/hd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 52388257d3d2..12ac0b6584d8 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -95,14 +95,14 @@ void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id) } ida_simple_remove(&hd->cport_id_map, cport_id); - - trace_gb_hd_release(hd); } static void gb_hd_release(struct device *dev) { struct gb_host_device *hd = to_gb_host_device(dev); + trace_gb_hd_release(hd); + if (hd->svc) gb_svc_put(hd->svc); ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id); -- cgit v1.2.3-59-g8ed1b From e55c25206d5c99b12443eec37b4832d6817170ba Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Tue, 31 May 2016 14:36:08 +0200 Subject: greybus: uart: Handle CRTSCTS flag in termios Handle the CRTSCTS flag in set_termios, so that auto flow control can be turned off. For this, add a new flag in the line coding request specifically for this purpose. Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 3 +++ drivers/staging/greybus/uart.c | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 77ac3f2a48d2..072e5c99a934 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1304,6 +1304,9 @@ struct gb_uart_set_line_coding_request { #define GB_SERIAL_SPACE_PARITY 4 __u8 data_bits; + + __u8 flow_control; +#define GB_SERIAL_AUTO_RTSCTS_EN 0x1 } __packed; /* output control lines */ diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 5d8f4e8095d9..44d8b9ad183a 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -39,6 +39,7 @@ struct gb_tty_line_coding { __u8 format; __u8 parity; __u8 data_bits; + __u8 flow_control; }; struct gb_tty { @@ -375,9 +376,9 @@ static void gb_tty_set_termios(struct tty_struct *tty, if (C_BAUD(tty) == B0) { newline.rate = gb_tty->line_coding.rate; - newctrl &= ~GB_UART_CTRL_DTR; + newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { - newctrl |= GB_UART_CTRL_DTR; + newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); } if (newctrl != gb_tty->ctrlout) { @@ -385,6 +386,11 @@ static void gb_tty_set_termios(struct tty_struct *tty, send_control(gb_tty, newctrl); } + if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) + newline.flow_control |= GB_SERIAL_AUTO_RTSCTS_EN; + else + newline.flow_control &= ~GB_SERIAL_AUTO_RTSCTS_EN; + if (memcmp(&gb_tty->line_coding, &newline, sizeof(newline))) { memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); send_line_coding(gb_tty); -- cgit v1.2.3-59-g8ed1b From 219ffcf3a51d0e0d8464d42db01e1e745a0d690f Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Tue, 31 May 2016 14:36:09 +0200 Subject: greybus: uart: Implement dtr_rts callback. Dtr_dts allows the tty layer to set the flow control lines to the correct state during open and close. Note that locking for newctrl is missing throughout the driver and will be addressed on a separate patch. Suggested-and-reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/uart.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 44d8b9ad183a..c257fbf40b79 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -604,6 +604,22 @@ static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, return -ENOIOCTLCMD; } +static void gb_tty_dtr_rts(struct tty_port *port, int on) +{ + struct gb_tty *gb_tty; + u8 newctrl; + + gb_tty = container_of(port, struct gb_tty, port); + newctrl = gb_tty->ctrlout; + + if (on) + newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); + else + newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); + + gb_tty->ctrlout = newctrl; + send_control(gb_tty, newctrl); +} static const struct tty_operations gb_ops = { .install = gb_tty_install, @@ -623,7 +639,9 @@ static const struct tty_operations gb_ops = { .tiocmset = gb_tty_tiocmset, }; -static struct tty_port_operations null_ops = { }; +static struct tty_port_operations gb_port_ops = { + .dtr_rts = gb_tty_dtr_rts, +}; static int gb_uart_probe(struct gbphy_device *gbphy_dev, const struct gbphy_device_id *id) @@ -681,7 +699,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, mutex_init(&gb_tty->mutex); tty_port_init(&gb_tty->port); - gb_tty->port.ops = &null_ops; + gb_tty->port.ops = &gb_port_ops; gb_tty->connection = connection; gb_tty->gbphy_dev = gbphy_dev; -- cgit v1.2.3-59-g8ed1b From 8d6fbe9bf9947ee875c222a2e33d5977e2d052f2 Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Tue, 31 May 2016 14:36:10 +0200 Subject: greybus: uart: Use a fifo to send data to the modules The firmware now buffers data instead of blocking while the transfer is sent, and the write operation cannot sleep. Instead of using gb_transfer_sync (which sleeps) in the write callback, buffer data in a fifo and send it from from a work queue. The write_room callback will will report 1 byte less that what is really available in the fifo, to leave space for extra characters that may be added by the tty layer. Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/uart.c | 123 +++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index c257fbf40b79..09fae9ae22bb 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -27,6 +27,8 @@ #include <linux/idr.h> #include <linux/fs.h> #include <linux/kdev_t.h> +#include <linux/kfifo.h> +#include <linux/workqueue.h> #include "greybus.h" #include "gbphy.h" @@ -34,6 +36,9 @@ #define GB_NUM_MINORS 16 /* 16 is is more than enough */ #define GB_NAME "ttyGB" +#define GB_UART_WRITE_FIFO_SIZE PAGE_SIZE +#define GB_UART_WRITE_ROOM_MARGIN 1 /* leave some space in fifo */ + struct gb_tty_line_coding { __le32 rate; __u8 format; @@ -61,6 +66,9 @@ struct gb_tty { u8 ctrlin; /* input control lines */ u8 ctrlout; /* output control lines */ struct gb_tty_line_coding line_coding; + struct work_struct tx_work; + struct kfifo write_fifo; + bool close_pending; }; static struct tty_driver *gb_tty_driver; @@ -166,25 +174,52 @@ static int gb_uart_request_handler(struct gb_operation *op) return ret; } -static int send_data(struct gb_tty *tty, u16 size, const u8 *data) +static void gb_uart_tx_write_work(struct work_struct *work) { struct gb_uart_send_data_request *request; + struct gb_tty *gb_tty; + unsigned long flags; + unsigned int send_size; int ret; - if (!data || !size) - return 0; + gb_tty = container_of(work, struct gb_tty, tx_work); + request = gb_tty->buffer; - if (size > tty->buffer_payload_max) - size = tty->buffer_payload_max; - request = tty->buffer; - request->size = cpu_to_le16(size); - memcpy(&request->data[0], data, size); - ret = gb_operation_sync(tty->connection, GB_UART_TYPE_SEND_DATA, - request, sizeof(*request) + size, NULL, 0); - if (ret) - return ret; - else - return size; + while (1) { + if (gb_tty->close_pending) + break; + + spin_lock_irqsave(&gb_tty->write_lock, flags); + send_size = kfifo_out_peek(&gb_tty->write_fifo, + &request->data[0], + gb_tty->buffer_payload_max); + if (!send_size) { + spin_unlock_irqrestore(&gb_tty->write_lock, flags); + break; + } + + spin_unlock_irqrestore(&gb_tty->write_lock, flags); + + request->size = cpu_to_le16(send_size); + ret = gb_operation_sync(gb_tty->connection, + GB_UART_TYPE_SEND_DATA, + request, sizeof(*request) + send_size, + NULL, 0); + if (ret) { + dev_err(&gb_tty->gbphy_dev->dev, + "send data error: %d\n", ret); + if (!gb_tty->close_pending) + schedule_work(work); + return; + } + + spin_lock_irqsave(&gb_tty->write_lock, flags); + ret = kfifo_out(&gb_tty->write_fifo, &request->data[0], + send_size); + spin_unlock_irqrestore(&gb_tty->write_lock, flags); + + tty_port_tty_wakeup(&gb_tty->port); + } } static int send_line_coding(struct gb_tty *tty) @@ -318,19 +353,42 @@ static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, { struct gb_tty *gb_tty = tty->driver_data; - return send_data(gb_tty, count, buf); + count = kfifo_in_spinlocked(&gb_tty->write_fifo, buf, count, + &gb_tty->write_lock); + if (count && !gb_tty->close_pending) + schedule_work(&gb_tty->tx_work); + + return count; } static int gb_tty_write_room(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; + unsigned long flags; + int room; + + spin_lock_irqsave(&gb_tty->write_lock, flags); + room = kfifo_avail(&gb_tty->write_fifo); + spin_unlock_irqrestore(&gb_tty->write_lock, flags); + + room -= GB_UART_WRITE_ROOM_MARGIN; + if (room < 0) + return 0; - return gb_tty->buffer_payload_max; + return room; } static int gb_tty_chars_in_buffer(struct tty_struct *tty) { - return 0; + struct gb_tty *gb_tty = tty->driver_data; + unsigned long flags; + int chars; + + spin_lock_irqsave(&gb_tty->write_lock, flags); + chars = kfifo_len(&gb_tty->write_fifo); + spin_unlock_irqrestore(&gb_tty->write_lock, flags); + + return chars; } static int gb_tty_break_ctl(struct tty_struct *tty, int state) @@ -621,6 +679,24 @@ static void gb_tty_dtr_rts(struct tty_port *port, int on) send_control(gb_tty, newctrl); } +static void gb_tty_port_shutdown(struct tty_port *port) +{ + struct gb_tty *gb_tty; + unsigned long flags; + + gb_tty = container_of(port, struct gb_tty, port); + + gb_tty->close_pending = true; + + cancel_work_sync(&gb_tty->tx_work); + + spin_lock_irqsave(&gb_tty->write_lock, flags); + kfifo_reset_out(&gb_tty->write_fifo); + spin_unlock_irqrestore(&gb_tty->write_lock, flags); + + gb_tty->close_pending = false; +} + static const struct tty_operations gb_ops = { .install = gb_tty_install, .open = gb_tty_open, @@ -641,6 +717,7 @@ static const struct tty_operations gb_ops = { static struct tty_port_operations gb_port_ops = { .dtr_rts = gb_tty_dtr_rts, + .shutdown = gb_tty_port_shutdown, }; static int gb_uart_probe(struct gbphy_device *gbphy_dev, @@ -680,6 +757,13 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, goto exit_connection_destroy; } + INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work); + + retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE, + GFP_KERNEL); + if (retval) + goto exit_buf_free; + minor = alloc_minor(gb_tty); if (minor < 0) { if (minor == -ENOSPC) { @@ -689,7 +773,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, } else { retval = minor; } - goto exit_buf_free; + goto exit_kfifo_free; } gb_tty->minor = minor; @@ -741,6 +825,8 @@ exit_connection_disable: gb_connection_disable(connection); exit_release_minor: release_minor(gb_tty); +exit_kfifo_free: + kfifo_free(&gb_tty->write_fifo); exit_buf_free: kfree(gb_tty->buffer); exit_connection_destroy: @@ -777,6 +863,7 @@ static void gb_uart_remove(struct gbphy_device *gbphy_dev) gb_connection_disable(connection); tty_port_destroy(&gb_tty->port); gb_connection_destroy(connection); + kfifo_free(&gb_tty->write_fifo); kfree(gb_tty->buffer); kfree(gb_tty); } -- cgit v1.2.3-59-g8ed1b From a8bc00fb88037e0107a0682478f47396a073be5c Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Tue, 31 May 2016 14:36:11 +0200 Subject: greybus: uart: Add credits based tracking for transmit path To avoid polling the firmware for space, a credit based system is implemented. The host will keep track of how many credits (bytes) it has sent to the firmware, and stop sending data when the quota is filled. The host will be informed that the firmware has more room for data when it handles the receive_credits request message from the firmware, and will continue to write data as credits become available. The firmware and the host may implement an algorithm to aggregate credits, and avoid extra greybus traffic. Suggested-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 5 +++ drivers/staging/greybus/uart.c | 66 ++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 072e5c99a934..089751c24117 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1269,6 +1269,7 @@ struct gb_raw_send_request { #define GB_UART_TYPE_SET_CONTROL_LINE_STATE 0x05 #define GB_UART_TYPE_SEND_BREAK 0x06 #define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ +#define GB_UART_TYPE_RECEIVE_CREDITS 0x08 /* Represents data from AP -> Module */ struct gb_uart_send_data_request { @@ -1289,6 +1290,10 @@ struct gb_uart_recv_data_request { __u8 data[0]; } __packed; +struct gb_uart_receive_credits_request { + __le16 count; +} __packed; + struct gb_uart_set_line_coding_request { __le32 rate; __u8 format; diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 09fae9ae22bb..14b3e9d06e9c 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -38,6 +38,7 @@ #define GB_UART_WRITE_FIFO_SIZE PAGE_SIZE #define GB_UART_WRITE_ROOM_MARGIN 1 /* leave some space in fifo */ +#define GB_UART_FIRMWARE_CREDITS 4096 struct gb_tty_line_coding { __le32 rate; @@ -69,6 +70,7 @@ struct gb_tty { struct work_struct tx_work; struct kfifo write_fifo; bool close_pending; + unsigned int credits; }; static struct tty_driver *gb_tty_driver; @@ -151,6 +153,53 @@ static int gb_uart_serial_state_handler(struct gb_operation *op) return 0; } +static int gb_uart_receive_credits_handler(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_tty *gb_tty = gb_connection_get_data(connection); + struct gb_message *request = op->request; + struct gb_uart_receive_credits_request *credit_request; + unsigned long flags; + unsigned int incoming_credits; + int ret = 0; + + if (request->payload_size < sizeof(*credit_request)) { + dev_err(&gb_tty->gbphy_dev->dev, + "short receive_credits event received (%zu < %zu)\n", + request->payload_size, + sizeof(*credit_request)); + return -EINVAL; + } + + credit_request = request->payload; + incoming_credits = le16_to_cpu(credit_request->count); + + spin_lock_irqsave(&gb_tty->write_lock, flags); + gb_tty->credits += incoming_credits; + if (gb_tty->credits > GB_UART_FIRMWARE_CREDITS) { + gb_tty->credits -= incoming_credits; + ret = -EINVAL; + } + spin_unlock_irqrestore(&gb_tty->write_lock, flags); + + if (ret) { + dev_err(&gb_tty->gbphy_dev->dev, + "invalid number of incoming credits: %d\n", + incoming_credits); + return ret; + } + + if (!gb_tty->close_pending) + schedule_work(&gb_tty->tx_work); + + /* + * the port the tty layer may be waiting for credits + */ + tty_port_tty_wakeup(&gb_tty->port); + + return ret; +} + static int gb_uart_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -165,6 +214,9 @@ static int gb_uart_request_handler(struct gb_operation *op) case GB_UART_TYPE_SERIAL_STATE: ret = gb_uart_serial_state_handler(op); break; + case GB_UART_TYPE_RECEIVE_CREDITS: + ret = gb_uart_receive_credits_handler(op); + break; default: dev_err(&gb_tty->gbphy_dev->dev, "unsupported unsolicited request: 0x%02x\n", type); @@ -190,14 +242,19 @@ static void gb_uart_tx_write_work(struct work_struct *work) break; spin_lock_irqsave(&gb_tty->write_lock, flags); + send_size = gb_tty->buffer_payload_max; + if (send_size > gb_tty->credits) + send_size = gb_tty->credits; + send_size = kfifo_out_peek(&gb_tty->write_fifo, &request->data[0], - gb_tty->buffer_payload_max); + send_size); if (!send_size) { spin_unlock_irqrestore(&gb_tty->write_lock, flags); break; } + gb_tty->credits -= send_size; spin_unlock_irqrestore(&gb_tty->write_lock, flags); request->size = cpu_to_le16(send_size); @@ -208,6 +265,9 @@ static void gb_uart_tx_write_work(struct work_struct *work) if (ret) { dev_err(&gb_tty->gbphy_dev->dev, "send data error: %d\n", ret); + spin_lock_irqsave(&gb_tty->write_lock, flags); + gb_tty->credits += send_size; + spin_unlock_irqrestore(&gb_tty->write_lock, flags); if (!gb_tty->close_pending) schedule_work(work); return; @@ -386,6 +446,8 @@ static int gb_tty_chars_in_buffer(struct tty_struct *tty) spin_lock_irqsave(&gb_tty->write_lock, flags); chars = kfifo_len(&gb_tty->write_fifo); + if (gb_tty->credits < GB_UART_FIRMWARE_CREDITS) + chars += GB_UART_FIRMWARE_CREDITS - gb_tty->credits; spin_unlock_irqrestore(&gb_tty->write_lock, flags); return chars; @@ -764,6 +826,8 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, if (retval) goto exit_buf_free; + gb_tty->credits = GB_UART_FIRMWARE_CREDITS; + minor = alloc_minor(gb_tty); if (minor < 0) { if (minor == -ENOSPC) { -- cgit v1.2.3-59-g8ed1b From 2b3b87f056b276b688cb0429faf682cac33dbf7b Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Tue, 31 May 2016 14:36:12 +0200 Subject: greybus: uart: Implement flush_buffer Data may be held pening in the hardware because of flow control mechanisms. When the port is closed, we need to flush all data that was not sent. For this, use the greybus message GB_UART_TYPE_FLUSH_FIFOS which will flush all data queued on the module but not yet sent on the data line. Suggested-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 7 +++++++ drivers/staging/greybus/uart.c | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 089751c24117..ff371c44cdf4 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1270,6 +1270,7 @@ struct gb_raw_send_request { #define GB_UART_TYPE_SEND_BREAK 0x06 #define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ #define GB_UART_TYPE_RECEIVE_CREDITS 0x08 +#define GB_UART_TYPE_FLUSH_FIFOS 0x09 /* Represents data from AP -> Module */ struct gb_uart_send_data_request { @@ -1335,6 +1336,12 @@ struct gb_uart_serial_state_request { __u8 control; } __packed; +struct gb_uart_serial_flush_request { + __u8 flags; +#define GB_SERIAL_FLAG_FLUSH_TRANSMITTER 0x01 +#define GB_SERIAL_FLAG_FLUSH_RECEIVER 0x02 +} __packed; + /* Loopback */ /* Version of the Greybus loopback protocol we support */ diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 14b3e9d06e9c..7dc9f9b873b1 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -317,6 +317,14 @@ static int send_break(struct gb_tty *gb_tty, u8 state) &request, sizeof(request), NULL, 0); } +static int gb_uart_flush(struct gb_tty *gb_tty, u8 flags) +{ + struct gb_uart_serial_flush_request request; + + request.flags = flags; + return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_FLUSH_FIFOS, + &request, sizeof(request), NULL, 0); +} static struct gb_tty *get_gb_by_minor(unsigned minor) { @@ -745,6 +753,7 @@ static void gb_tty_port_shutdown(struct tty_port *port) { struct gb_tty *gb_tty; unsigned long flags; + int ret; gb_tty = container_of(port, struct gb_tty, port); @@ -756,6 +765,12 @@ static void gb_tty_port_shutdown(struct tty_port *port) kfifo_reset_out(&gb_tty->write_fifo); spin_unlock_irqrestore(&gb_tty->write_lock, flags); + ret = gb_uart_flush(gb_tty, GB_SERIAL_FLAG_FLUSH_TRANSMITTER); + if (ret) { + dev_err(&gb_tty->gbphy_dev->dev, + "error flushing transmitter: %d\n", ret); + } + gb_tty->close_pending = false; } -- cgit v1.2.3-59-g8ed1b From 5dad5c314b686ca84aa5477462fcfd2b1abcba46 Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Tue, 31 May 2016 14:36:13 +0200 Subject: greybus: uart: wait for credits on shutdown We should try to wait until all credits are accounted for before returning from shutdown. For this purpose add a helper function that will wait on completion, and call it form the shutdown. This helper will also be useful when "wait until sent" is implemented. Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/uart.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 7dc9f9b873b1..a2fca3b3358e 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -29,6 +29,7 @@ #include <linux/kdev_t.h> #include <linux/kfifo.h> #include <linux/workqueue.h> +#include <linux/completion.h> #include "greybus.h" #include "gbphy.h" @@ -39,6 +40,7 @@ #define GB_UART_WRITE_FIFO_SIZE PAGE_SIZE #define GB_UART_WRITE_ROOM_MARGIN 1 /* leave some space in fifo */ #define GB_UART_FIRMWARE_CREDITS 4096 +#define GB_UART_CREDIT_WAIT_TIMEOUT_MSEC 10000 struct gb_tty_line_coding { __le32 rate; @@ -71,6 +73,7 @@ struct gb_tty { struct kfifo write_fifo; bool close_pending; unsigned int credits; + struct completion credits_complete; }; static struct tty_driver *gb_tty_driver; @@ -197,6 +200,9 @@ static int gb_uart_receive_credits_handler(struct gb_operation *op) */ tty_port_tty_wakeup(&gb_tty->port); + if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) + complete(&gb_tty->credits_complete); + return ret; } @@ -317,6 +323,24 @@ static int send_break(struct gb_tty *gb_tty, u8 state) &request, sizeof(request), NULL, 0); } +static int gb_uart_wait_for_all_credits(struct gb_tty *gb_tty) +{ + int ret; + + if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) + return 0; + + ret = wait_for_completion_timeout(&gb_tty->credits_complete, + msecs_to_jiffies(GB_UART_CREDIT_WAIT_TIMEOUT_MSEC)); + if (!ret) { + dev_err(&gb_tty->gbphy_dev->dev, + "time out waiting for credits\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int gb_uart_flush(struct gb_tty *gb_tty, u8 flags) { struct gb_uart_serial_flush_request request; @@ -765,12 +789,18 @@ static void gb_tty_port_shutdown(struct tty_port *port) kfifo_reset_out(&gb_tty->write_fifo); spin_unlock_irqrestore(&gb_tty->write_lock, flags); + if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) + goto out; + ret = gb_uart_flush(gb_tty, GB_SERIAL_FLAG_FLUSH_TRANSMITTER); if (ret) { dev_err(&gb_tty->gbphy_dev->dev, "error flushing transmitter: %d\n", ret); } + gb_uart_wait_for_all_credits(gb_tty); + +out: gb_tty->close_pending = false; } @@ -842,6 +872,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, goto exit_buf_free; gb_tty->credits = GB_UART_FIRMWARE_CREDITS; + init_completion(&gb_tty->credits_complete); minor = alloc_minor(gb_tty); if (minor < 0) { -- cgit v1.2.3-59-g8ed1b From 5e569115e9b9d4af631589b9d9cc5227a660b008 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 30 May 2016 11:31:52 +0530 Subject: greybus: Remove unused VERSION specific macros We don't use these anymore. Drop them. Note that some macro's specific to bridged PHY devices aren't removed in the patch, as gbsim will break otherwise. They will be removed separately. Compile tested. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 27 --------------------------- drivers/staging/greybus/usb.c | 4 ---- drivers/staging/greybus/vibrator.c | 4 ---- 3 files changed, 35 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index ff371c44cdf4..6550f1744ed8 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -407,10 +407,6 @@ struct gb_bootrom_get_vid_pid_response { /* Power Supply */ -/* Version of the Greybus power supply protocol we support */ -#define GB_POWER_SUPPLY_VERSION_MAJOR 0x00 -#define GB_POWER_SUPPLY_VERSION_MINOR 0x01 - /* Greybus power supply request types */ #define GB_POWER_SUPPLY_TYPE_GET_SUPPLIES 0x02 #define GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION 0x03 @@ -572,10 +568,6 @@ struct gb_power_supply_event_request { /* HID */ -/* Version of the Greybus hid protocol we support */ -#define GB_HID_VERSION_MAJOR 0x00 -#define GB_HID_VERSION_MINOR 0x01 - /* Greybus HID operation types */ #define GB_HID_TYPE_GET_DESC 0x02 #define GB_HID_TYPE_GET_REPORT_DESC 0x03 @@ -1243,10 +1235,6 @@ struct gb_svc_intf_mailbox_event_request { /* RAW */ -/* Version of the Greybus raw protocol we support */ -#define GB_RAW_VERSION_MAJOR 0x00 -#define GB_RAW_VERSION_MINOR 0x01 - /* Greybus raw request types */ #define GB_RAW_TYPE_SEND 0x02 @@ -1344,10 +1332,6 @@ struct gb_uart_serial_flush_request { /* Loopback */ -/* Version of the Greybus loopback protocol we support */ -#define GB_LOOPBACK_VERSION_MAJOR 0x00 -#define GB_LOOPBACK_VERSION_MINOR 0x01 - /* Greybus loopback request types */ #define GB_LOOPBACK_TYPE_PING 0x02 #define GB_LOOPBACK_TYPE_TRANSFER 0x03 @@ -1532,9 +1516,6 @@ struct gb_sdio_event_request { /* Camera */ -#define GB_CAMERA_VERSION_MAJOR 0x00 -#define GB_CAMERA_VERSION_MINOR 0x01 - /* Greybus Camera request types */ #define GB_CAMERA_TYPE_CAPABILITIES 0x02 #define GB_CAMERA_TYPE_CONFIGURE_STREAMS 0x03 @@ -1608,9 +1589,6 @@ struct gb_camera_metadata_request { /* Lights */ -#define GB_LIGHTS_VERSION_MAJOR 0x00 -#define GB_LIGHTS_VERSION_MINOR 0x01 - /* Greybus Lights request types */ #define GB_LIGHTS_TYPE_GET_LIGHTS 0x02 #define GB_LIGHTS_TYPE_GET_LIGHT_CONFIG 0x03 @@ -1782,11 +1760,6 @@ struct gb_lights_get_flash_fault_response { /* Audio */ -/* Version of the Greybus audio protocol we support */ -#define GB_AUDIO_VERSION_MAJOR 0x00 -#define GB_AUDIO_VERSION_MINOR 0x01 - -#define GB_AUDIO_TYPE_PROTOCOL_VERSION 0x01 #define GB_AUDIO_TYPE_GET_TOPOLOGY_SIZE 0x02 #define GB_AUDIO_TYPE_GET_TOPOLOGY 0x03 #define GB_AUDIO_TYPE_GET_CONTROL 0x04 diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index 7ed6a60c1d56..e5ba34ac7643 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -16,10 +16,6 @@ #include "greybus.h" #include "gbphy.h" -/* Version of the Greybus USB protocol we support */ -#define GB_USB_VERSION_MAJOR 0x00 -#define GB_USB_VERSION_MINOR 0x01 - /* Greybus USB request types */ #define GB_USB_TYPE_HCD_START 0x02 #define GB_USB_TYPE_HCD_STOP 0x03 diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 5afcb2784a32..33b2bf9c16c8 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -21,10 +21,6 @@ struct gb_vibrator_device { int minor; /* vibrator minor number */ }; -/* Version of the Greybus vibrator protocol we support */ -#define GB_VIBRATOR_VERSION_MAJOR 0x00 -#define GB_VIBRATOR_VERSION_MINOR 0x01 - /* Greybus Vibrator operation types */ #define GB_VIBRATOR_TYPE_ON 0x02 #define GB_VIBRATOR_TYPE_OFF 0x03 -- cgit v1.2.3-59-g8ed1b From 64060fe95458f22bac7327b2ee8dc5ce9e488d44 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 30 May 2016 11:31:53 +0530 Subject: greybus: gbphy: Remove protocol specific version handling We should be using the generic version handling at bundle level, instead of at protocol level for bridged PHY devices as well. The bundle version handling is already in place, though it is *not* used today as we haven't bumped the version of control protocol yet. Remove protocol specific handling for bridged PHY devices. Tested on EVT 1.5 with gpbridge-test module. No nuttx changes are required with this. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.h | 3 --- drivers/staging/greybus/gbphy.c | 27 --------------------------- drivers/staging/greybus/gbphy.h | 1 - drivers/staging/greybus/gpio.c | 4 ---- drivers/staging/greybus/greybus_protocols.h | 25 ------------------------- drivers/staging/greybus/i2c.c | 4 ---- drivers/staging/greybus/pwm.c | 4 ---- drivers/staging/greybus/sdio.c | 4 ---- drivers/staging/greybus/spi.c | 4 ---- drivers/staging/greybus/uart.c | 4 ---- drivers/staging/greybus/usb.c | 4 ---- 11 files changed, 84 deletions(-) diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 9cd0bac9ceb9..7d0988ef1183 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -44,9 +44,6 @@ struct gb_connection { gb_request_handler_t handler; unsigned long flags; - u8 module_major; - u8 module_minor; - struct mutex mutex; spinlock_t lock; enum gb_connection_state state; diff --git a/drivers/staging/greybus/gbphy.c b/drivers/staging/greybus/gbphy.c index c11a1368d073..2727e4afcf4c 100644 --- a/drivers/staging/greybus/gbphy.c +++ b/drivers/staging/greybus/gbphy.c @@ -171,33 +171,6 @@ void gb_gbphy_deregister_driver(struct gbphy_driver *driver) } EXPORT_SYMBOL_GPL(gb_gbphy_deregister_driver); -int gb_gbphy_get_version(struct gb_connection *connection) -{ - struct gb_protocol_version_request request; - struct gb_protocol_version_response response; - int retval; - - request.major = 1; - request.minor = 0; - - retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, - &request, sizeof(request), &response, - sizeof(response)); - if (retval) - return retval; - - /* FIXME - do proper version negotiation here someday... */ - - connection->module_major = response.major; - connection->module_minor = response.minor; - - dev_dbg(&connection->hd->dev, "%s: v%u.%u\n", connection->name, - response.major, response.minor); - - return 0; -} -EXPORT_SYMBOL_GPL(gb_gbphy_get_version); - static struct gbphy_device *gb_gbphy_create_dev(struct gb_bundle *bundle, struct greybus_descriptor_cport *cport_desc) { diff --git a/drivers/staging/greybus/gbphy.h b/drivers/staging/greybus/gbphy.h index 79dbf7e1be58..68ad51821a60 100644 --- a/drivers/staging/greybus/gbphy.h +++ b/drivers/staging/greybus/gbphy.h @@ -46,7 +46,6 @@ struct gbphy_driver { }; #define to_gbphy_driver(d) container_of(d, struct gbphy_driver, driver) -int gb_gbphy_get_version(struct gb_connection *connection); int gb_gbphy_register_driver(struct gbphy_driver *driver, struct module *owner, const char *mod_name); void gb_gbphy_deregister_driver(struct gbphy_driver *driver); diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index e1ad6802630a..f60cc1da9f05 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -654,10 +654,6 @@ static int gb_gpio_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_connection_destroy; - ret = gb_gbphy_get_version(connection); - if (ret) - goto exit_connection_disable; - ret = gb_gpio_controller_setup(ggc); if (ret) goto exit_connection_disable; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 6550f1744ed8..82075c703f33 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -97,7 +97,6 @@ struct gb_operation_msg_hdr { /* Generic request types */ #define GB_REQUEST_TYPE_PING 0x00 -#define GB_REQUEST_TYPE_PROTOCOL_VERSION 0x01 #define GB_REQUEST_TYPE_INVALID 0x7f struct gb_protocol_version_request { @@ -614,10 +613,6 @@ struct gb_hid_input_report_request { /* I2C */ -/* Version of the Greybus i2c protocol we support */ -#define GB_I2C_VERSION_MAJOR 0x00 -#define GB_I2C_VERSION_MINOR 0x01 - /* Greybus i2c request types */ #define GB_I2C_TYPE_FUNCTIONALITY 0x02 #define GB_I2C_TYPE_TRANSFER 0x05 @@ -654,10 +649,6 @@ struct gb_i2c_transfer_response { /* GPIO */ -/* Version of the Greybus GPIO protocol we support */ -#define GB_GPIO_VERSION_MAJOR 0x00 -#define GB_GPIO_VERSION_MINOR 0x01 - /* Greybus GPIO request types */ #define GB_GPIO_TYPE_LINE_COUNT 0x02 #define GB_GPIO_TYPE_ACTIVATE 0x03 @@ -757,10 +748,6 @@ struct gb_gpio_irq_event_request { /* PWM */ -/* Version of the Greybus PWM protocol we support */ -#define GB_PWM_VERSION_MAJOR 0x00 -#define GB_PWM_VERSION_MINOR 0x01 - /* Greybus PWM operation types */ #define GB_PWM_TYPE_PWM_COUNT 0x02 #define GB_PWM_TYPE_ACTIVATE 0x03 @@ -804,10 +791,6 @@ struct gb_pwm_disable_request { /* SPI */ -/* Version of the Greybus spi protocol we support */ -#define GB_SPI_VERSION_MAJOR 0x00 -#define GB_SPI_VERSION_MINOR 0x01 - /* Should match up with modes in linux/spi/spi.h */ #define GB_SPI_MODE_CPHA 0x01 /* clock phase */ #define GB_SPI_MODE_CPOL 0x02 /* clock polarity */ @@ -1246,10 +1229,6 @@ struct gb_raw_send_request { /* UART */ -/* Version of the Greybus UART protocol we support */ -#define GB_UART_VERSION_MAJOR 0x00 -#define GB_UART_VERSION_MINOR 0x01 - /* Greybus UART operation types */ #define GB_UART_TYPE_SEND_DATA 0x02 #define GB_UART_TYPE_RECEIVE_DATA 0x03 /* Unsolicited data */ @@ -1356,10 +1335,6 @@ struct gb_loopback_transfer_response { } __packed; /* SDIO */ -/* Version of the Greybus sdio protocol we support */ -#define GB_SDIO_VERSION_MAJOR 0x00 -#define GB_SDIO_VERSION_MINOR 0x01 - /* Greybus SDIO operation types */ #define GB_SDIO_TYPE_GET_CAPABILITIES 0x02 #define GB_SDIO_TYPE_SET_IOS 0x03 diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 6c14e6776adf..2541bdbb9dd9 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -271,10 +271,6 @@ static int gb_i2c_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_connection_destroy; - ret = gb_gbphy_get_version(connection); - if (ret) - goto exit_connection_disable; - ret = gb_i2c_device_setup(gb_i2c_dev); if (ret) goto exit_connection_disable; diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 77b05e895550..b941cb5b5063 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -206,10 +206,6 @@ static int gb_pwm_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_connection_destroy; - ret = gb_gbphy_get_version(connection); - if (ret) - goto exit_connection_disable; - /* Query number of pwms present */ ret = gb_pwm_count_operation(pwmc); if (ret) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index bdcc86923c54..a270517c90c8 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -748,10 +748,6 @@ static int gb_sdio_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_connection_destroy; - ret = gb_gbphy_get_version(connection); - if (ret) - goto exit_connection_disable; - ret = gb_sdio_get_caps(host); if (ret < 0) goto exit_connection_disable; diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 4f13efeeb91e..a82337931c0d 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -29,10 +29,6 @@ static int gb_spi_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_connection_destroy; - ret = gb_gbphy_get_version(connection); - if (ret) - goto exit_connection_disable; - ret = gb_spilib_master_init(connection, &gbphy_dev->dev); if (ret) goto exit_connection_disable; diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index a2fca3b3358e..d1611abf8ccd 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -904,10 +904,6 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, if (retval) goto exit_release_minor; - retval = gb_gbphy_get_version(connection); - if (retval) - goto exit_connection_disable; - send_control(gb_tty, gb_tty->ctrlout); /* initialize the uart to be 9600n81 */ diff --git a/drivers/staging/greybus/usb.c b/drivers/staging/greybus/usb.c index e5ba34ac7643..ccadda084b76 100644 --- a/drivers/staging/greybus/usb.c +++ b/drivers/staging/greybus/usb.c @@ -190,10 +190,6 @@ static int gb_usb_probe(struct gbphy_device *gbphy_dev, if (retval) goto exit_connection_destroy; - retval = gb_gbphy_get_version(connection); - if (retval) - goto exit_connection_disable; - /* * FIXME: The USB bridged-PHY protocol driver depends on changes to * USB core which are not yet upstream. -- cgit v1.2.3-59-g8ed1b From c92c1d026b1e13f20fee3350975a4986608ff84a Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 30 May 2016 13:05:08 +0530 Subject: greybus: fw-mgmt: Add hooks to do mode-switch This is the last step to required to finish the mode switch story. That is, call the hook provided by Interface layer to accomplish mode switch. Tested on EVT 1.5 with gpbridge-test module. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Karthik Ravi Shankar <karthikrs@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-management.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index da36de313a2c..22dcad2ebc15 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -484,7 +484,14 @@ static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, fw_mgmt->mode_switch_started = true; - /* FIXME: Initiate mode-switch from here */ + ret = gb_interface_request_mode_switch(fw_mgmt->connection->intf); + if (ret) { + dev_err(fw_mgmt->parent, "Mode-switch failed: %d\n", + ret); + fw_mgmt->mode_switch_started = false; + return ret; + } + return 0; default: return -ENOTTY; -- cgit v1.2.3-59-g8ed1b From 7ff6e0128a7da5d5a8b51abb09fafeb7b8944792 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 30 May 2016 13:05:09 +0530 Subject: greybus: control: Mode-switch is a 'core' operation The operation layer allows only the 'core' operations on a connection, which is in its 'disconnecting' state. Mode switch is sent at the very end of interface-disable sequence, and the control connection is already in its 'disconnecting' state at that time. And so gb_operation_get_active() always fail with error -ENOTCONN. The operation core already has support for such 'core' operations, just that we were missing the needed flag while creating the mode switch operation. Fix that. Tested on EVT 1.5 with gpbridge-test module. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Karthik Ravi Shankar <karthikrs@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index d772c27a01df..88e5718965d6 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -163,18 +163,24 @@ int gb_control_disconnecting_operation(struct gb_control *control, int gb_control_mode_switch_operation(struct gb_control *control) { + struct gb_operation *operation; int ret; - ret = gb_operation_unidirectional(control->connection, - GB_CONTROL_TYPE_MODE_SWITCH, - NULL, 0); + operation = gb_operation_create_core(control->connection, + GB_CONTROL_TYPE_MODE_SWITCH, + 0, 0, GB_OPERATION_FLAG_UNIDIRECTIONAL, + GFP_KERNEL); + if (!operation) + return -ENOMEM; + + ret = gb_operation_request_send_sync(operation); if (ret) { - dev_err(&control->dev, "failed to send mode switch: %d\n", - ret); - return ret; + dev_err(&control->dev, "failed to send mode switch: %d\n", ret); } - return 0; + gb_operation_put(operation); + + return ret; } int gb_control_timesync_enable(struct gb_control *control, u8 count, -- cgit v1.2.3-59-g8ed1b From 7dea1d5f8a9d3141881dab40bb55c1a7ae84d8ab Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 30 May 2016 13:05:10 +0530 Subject: greybus: interface: Mode switch takes over a second Mode switch takes just over a second to complete and the current timeout of one second isn't sufficient. Mode-switch logs from EVT 1.5: [ 56.709082] gb-firmware 1-3.3.1: Requested firmware package 'ara_00000126_00001002_fffe0001_ff980067_03.tftf' [ 57.003968] gb_control_mode_switch_operation: 176 [ 58.041616] greybus 1-3.3: mode switch detected Increase the timeout to two seconds. Tested with EVT 1.5 with gpbridge-test module. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Karthik Ravi Shankar <karthikrs@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index b5d243b28f9a..577d15fb5bd2 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -10,7 +10,7 @@ #include "greybus.h" #include "greybus_trace.h" -#define GB_INTERFACE_MODE_SWITCH_TIMEOUT 1000 +#define GB_INTERFACE_MODE_SWITCH_TIMEOUT 2000 #define GB_INTERFACE_DEVICE_ID_BAD 0xff -- cgit v1.2.3-59-g8ed1b From 68ee90c29ea4068e03ad637af07f6ad7c27da544 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 30 May 2016 14:32:09 +0530 Subject: greybus: Add comment about the mode_switch_started flag Add a comment to describe the purpose of the flag. Suggested-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-management.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index 22dcad2ebc15..c9c28fc15492 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -482,6 +482,11 @@ static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, return -EPERM; } + /* + * Disallow new ioctls as the fw-core bundle driver is going to + * get disconnected soon and the character device will get + * removed. + */ fw_mgmt->mode_switch_started = true; ret = gb_interface_request_mode_switch(fw_mgmt->connection->intf); -- cgit v1.2.3-59-g8ed1b From 07f91c75b490f67b1684a3a4d2b708a19ab93676 Mon Sep 17 00:00:00 2001 From: Evgeniy Borisov <borisov_evgeniy@projectara.com> Date: Tue, 31 May 2016 11:33:09 +0300 Subject: greybus: camera-gb: Add description of interface header Add description for all interface structures in gb-camera.h. Signed-off-by: Evgeniy Borisov <eborisov@mm-sol.com> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gb-camera.h | 67 ++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/gb-camera.h b/drivers/staging/greybus/gb-camera.h index cc9c012249e2..63f8c922fa9a 100644 --- a/drivers/staging/greybus/gb-camera.h +++ b/drivers/staging/greybus/gb-camera.h @@ -15,6 +15,19 @@ /* Output flags returned */ #define GB_CAMERA_OUT_FLAG_ADJUSTED (1 << 0) +/** + * struct gb_camera_stream - Represents greybus camera stream. + * @width: Stream width in pixels. + * @height: Stream height in pixels. + * @pixel_code: Media bus pixel code. + * @vc: MIPI CSI virtual channel. + * @dt: MIPI CSI data types. Most formats use a single data type, in which case + * the second element will be ignored. + * @max_size: Maximum size of a frame in bytes. The camera module guarantees + * that all data between the Frame Start and Frame End packet for + * the associated virtual channel and data type(s) will not exceed + * this size. + */ struct gb_camera_stream { unsigned int width; unsigned int height; @@ -26,10 +39,10 @@ struct gb_camera_stream { /** * struct gb_camera_csi_params - CSI configuration parameters - * @num_lanes: number of CSI data lanes - * @clk_freq: CSI clock frequency in Hz - * @lines_per_second: total number of lines in a second of transmission - * (blanking included) + * @num_lanes: number of CSI data lanes + * @clk_freq: CSI clock frequency in Hz + * @lines_per_second: Total number of lines in a second of transmission + * (blanking included) */ struct gb_camera_csi_params { unsigned int num_lanes; @@ -37,6 +50,46 @@ struct gb_camera_csi_params { unsigned int lines_per_second; }; +/** + * struct gb_camera_ops - Greybus camera operations, used by the Greybus camera + * driver to expose operations to the host camera driver. + * @capabilities: Retrieve camera capabilities and store them in the buffer + * 'buf' capabilities. The buffer maximum size is specified by + * the caller in the 'size' parameter, and the effective + * capabilities size is returned from the function. If the buffer + * size is too small to hold the capabilities an error is + * returned and the buffer is left untouched. + * + * @configure_streams: Negotiate configuration and prepare the module for video + * capture. The caller specifies the number of streams it + * requests in the 'nstreams' argument and the associated + * streams configurations in the 'streams' argument. The + * GB_CAMERA_IN_FLAG_TEST 'flag' can be set to test a + * configuration without applying it, otherwise the + * configuration is applied by the module. The module can + * decide to modify the requested configuration, including + * using a different number of streams. In that case the + * modified configuration won't be applied, the + * GB_CAMERA_OUT_FLAG_ADJUSTED 'flag' will be set upon + * return, and the modified configuration and number of + * streams stored in 'streams' and 'array'. The module + * returns its CSI-2 bus parameters in the 'csi_params' + * structure in all cases. + * + * @capture: Submit a capture request. The supplied 'request_id' must be unique + * and higher than the IDs of all the previously submitted requests. + * The 'streams' argument specifies which streams are affected by the + * request in the form of a bitmask, with bits corresponding to the + * configured streams indexes. If the request contains settings, the + * 'settings' argument points to the settings buffer and its size is + * specified by the 'settings_size' argument. Otherwise the 'settings' + * argument should be set to NULL and 'settings_size' to 0. + * + * @flush: Flush the capture requests queue. Return the ID of the last request + * that will processed by the device before it stops transmitting video + * frames. All queued capture requests with IDs higher than the returned + * ID will be dropped without being processed. + */ struct gb_camera_ops { ssize_t (*capabilities)(void *priv, char *buf, size_t len); int (*configure_streams)(void *priv, unsigned int *nstreams, @@ -48,6 +101,12 @@ struct gb_camera_ops { int (*flush)(void *priv, u32 *request_id); }; +/** + * struct gb_camera_module - Represents greybus camera module. + * @priv: Module private data, passed to all camera operations. + * @ops: Greybus camera operation callbacks. + * @list: List entry in the camera modules list. + */ struct gb_camera_module { void *priv; const struct gb_camera_ops *ops; -- cgit v1.2.3-59-g8ed1b From 6c5ce637db01c33712db996a78a74b25f3a5e0e3 Mon Sep 17 00:00:00 2001 From: Evgeniy Borisov <borisov_evgeniy@projectara.com> Date: Tue, 31 May 2016 11:33:10 +0300 Subject: greybus: camera-gb: Extend gb camera module structure Add additional information in gb camera module. - interface ID - reference count - release handle Signed-off-by: Evgeniy Borisov <eborisov@mm-sol.com> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gb-camera.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/greybus/gb-camera.h b/drivers/staging/greybus/gb-camera.h index 63f8c922fa9a..b8651ed05244 100644 --- a/drivers/staging/greybus/gb-camera.h +++ b/drivers/staging/greybus/gb-camera.h @@ -105,12 +105,18 @@ struct gb_camera_ops { * struct gb_camera_module - Represents greybus camera module. * @priv: Module private data, passed to all camera operations. * @ops: Greybus camera operation callbacks. + * @interface_id: Interface id of the module. + * @refcount: Reference counting object. + * @release: Module release function. * @list: List entry in the camera modules list. */ struct gb_camera_module { void *priv; const struct gb_camera_ops *ops; + unsigned int interface_id; + struct kref refcount; + void (*release)(struct kref *kref); struct list_head list; /* Global list */ }; -- cgit v1.2.3-59-g8ed1b From 17ca677018117deee1bd75b301894dca975e7fc5 Mon Sep 17 00:00:00 2001 From: Evgeniy Borisov <borisov_evgeniy@projectara.com> Date: Tue, 31 May 2016 11:33:11 +0300 Subject: greybus: camera-gb: Implement camera module reference counting as subject. In explanation: The main idea for implementing reference counting is to not block exit until any other modules are in use. Camera responsibility is to handle properly any additional calls after camera exit and that what this patch is doing: 1. Free camera module when reference count is zero. 2. After camera cleanup handle properly any additional ongoing transaction. Return error if connection is destroyed. Signed-off-by: Evgeniy Borisov <eborisov@mm-sol.com> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 97 +++++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index d5496f8a7b89..d7cef2ac5aee 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -35,7 +35,9 @@ struct gb_camera_debugfs_buffer { /** * struct gb_camera - A Greybus Camera Device - * @connection: the greybus connection for camera control + * @connection: the greybus connection for camera management + * @data_connection: the greybus connection for camera data + * @mutex: protects the connection field * @debugfs: debugfs entries for camera protocol operations testing * @module: Greybus camera module registered to HOST processor. */ @@ -43,6 +45,7 @@ struct gb_camera { struct gb_bundle *bundle; struct gb_connection *connection; struct gb_connection *data_connection; + struct mutex mutex; struct { struct dentry *root; @@ -163,15 +166,24 @@ static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) static int gb_camera_capabilities(struct gb_camera *gcam, u8 *capabilities, size_t *size) { - struct gb_operation *op; + struct gb_operation *op = NULL; int ret; + mutex_lock(&gcam->mutex); + + if (!gcam->connection) { + ret = -EINVAL; + goto done; + } + op = gb_operation_create_flags(gcam->connection, GB_CAMERA_TYPE_CAPABILITIES, 0, *size, GB_OPERATION_FLAG_SHORT_RESPONSE, GFP_KERNEL); - if (!op) - return -ENOMEM; + if (!op) { + ret = -ENOMEM; + goto done; + } ret = gb_operation_request_send_sync(op); if (ret) { @@ -183,7 +195,9 @@ static int gb_camera_capabilities(struct gb_camera *gcam, *size = op->response->payload_size; done: - gb_operation_put(op); + mutex_unlock(&gcam->mutex); + if (op) + gb_operation_put(op); return ret; } @@ -222,8 +236,9 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, req = kmalloc(req_size, GFP_KERNEL); resp = kmalloc(resp_size, GFP_KERNEL); if (!req || !resp) { - ret = -ENOMEM; - goto done; + kfree(req); + kfree(resp); + return -ENOMEM; } req->num_streams = nstreams; @@ -239,6 +254,13 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, cfg->padding = 0; } + mutex_lock(&gcam->mutex); + + if (!gcam->connection) { + ret = -EINVAL; + goto done; + } + ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CONFIGURE_STREAMS, req, req_size, resp, resp_size); @@ -329,6 +351,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, ret = 0; done: + mutex_unlock(&gcam->mutex); kfree(req); kfree(resp); return ret; @@ -356,8 +379,17 @@ static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, req->num_frames = cpu_to_le16(num_frames); memcpy(req->settings, settings, settings_size); + mutex_lock(&gcam->mutex); + + if (!gcam->connection) { + ret = -EINVAL; + goto done; + } + ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, - req, req_size, NULL, 0); + req, req_size, NULL, 0); +done: + mutex_unlock(&gcam->mutex); kfree(req); @@ -369,15 +401,26 @@ static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) struct gb_camera_flush_response resp; int ret; + mutex_lock(&gcam->mutex); + + if (!gcam->connection) { + ret = -EINVAL; + goto done; + } + ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, &resp, sizeof(resp)); + if (ret < 0) - return ret; + goto done; if (request_id) *request_id = le32_to_cpu(resp.request_id); - return 0; +done: + mutex_unlock(&gcam->mutex); + + return ret; } static int gb_camera_request_handler(struct gb_operation *op) @@ -527,18 +570,6 @@ static const struct gb_camera_ops gb_cam_ops = { .flush = gb_camera_op_flush, }; -static int gb_camera_register_intf_ops(struct gb_camera *gcam) -{ - gcam->module.priv = gcam; - gcam->module.ops = &gb_cam_ops; - return gb_camera_register(&gcam->module); -} - -static int gb_camera_unregister_intf_ops(struct gb_camera *gcam) -{ - return gb_camera_unregister(&gcam->module); -} - /* ----------------------------------------------------------------------------- * DebugFS */ @@ -899,14 +930,23 @@ static void gb_camera_cleanup(struct gb_camera *gcam) if (gcam->data_connection) { gb_connection_disable(gcam->data_connection); gb_connection_destroy(gcam->data_connection); + gcam->data_connection = NULL; } + mutex_lock(&gcam->mutex); if (gcam->connection) { gb_connection_disable(gcam->connection); gb_connection_destroy(gcam->connection); + gcam->connection = NULL; } + mutex_unlock(&gcam->mutex); +} - kfree(gcam); +static void gb_camera_release_module(struct kref *ref) +{ + struct gb_camera_module *cam_mod = + container_of(ref, struct gb_camera_module, refcount); + kfree(cam_mod->priv); } static int gb_camera_probe(struct gb_bundle *bundle, @@ -964,6 +1004,8 @@ static int gb_camera_probe(struct gb_bundle *bundle, if (ret) goto error; + mutex_init(&gcam->mutex); + /* * Create the data connection between the camera module data CPort and * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. @@ -986,7 +1028,11 @@ static int gb_camera_probe(struct gb_bundle *bundle, if (ret < 0) goto error; - ret = gb_camera_register_intf_ops(gcam); + gcam->module.priv = gcam; + gcam->module.ops = &gb_cam_ops; + gcam->module.interface_id = gcam->connection->intf->interface_id; + gcam->module.release = gb_camera_release_module; + ret = gb_camera_register(&gcam->module); if (ret < 0) goto error; @@ -996,6 +1042,7 @@ static int gb_camera_probe(struct gb_bundle *bundle, error: gb_camera_cleanup(gcam); + kfree(gcam); return ret; } @@ -1003,8 +1050,8 @@ static void gb_camera_disconnect(struct gb_bundle *bundle) { struct gb_camera *gcam = greybus_get_drvdata(bundle); - gb_camera_unregister_intf_ops(gcam); gb_camera_cleanup(gcam); + gb_camera_unregister(&gcam->module); } static const struct greybus_bundle_id gb_camera_id_table[] = { -- cgit v1.2.3-59-g8ed1b From e5f23c45841ff371e4e515e8c5395fc659017930 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:29 -0500 Subject: greybus: tracing: fix "make check" warnings Some of the trace buffer fields were defined as Booleans. This leads to two problems reported by "make check": - the __field() macro (or some descendent macro) performs a sizeof(bool) operation, which results in a warning - The TP_printk() macro, which specifies a printf() style format string, produces a warning when one attempts to format a Boolean as an integer. Fix both problems implicitly converting Boolean values from the data structures into integers in the trace buffer. Signed-off-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 148ffaf4c6b4..52a7ef5c4168 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -171,7 +171,7 @@ DECLARE_EVENT_CLASS(gb_module, __field(int, hd_bus_id) __field(u8, module_id) __field(u8, num_interfaces) - __field(bool, disconnected) + __field(int, disconnected) /* bool */ ), TP_fast_assign( @@ -180,7 +180,7 @@ DECLARE_EVENT_CLASS(gb_module, __entry->disconnected = module->disconnected; ), - TP_printk("greybus: hd_bus_id=%d module_id=%hhu disconnected=%u", + TP_printk("greybus: hd_bus_id=%d module_id=%hhu disconnected=%d", __entry->hd_bus_id, __entry->module_id, __entry->disconnected) ); @@ -224,10 +224,10 @@ DECLARE_EVENT_CLASS(gb_interface, __field(u8, id) /* Interface id */ __field(u8, module_id) __field(u8, device_id) - __field(bool, disconnected) - __field(bool, ejected) - __field(bool, active) - __field(bool, enabled) + __field(int, disconnected) /* bool */ + __field(int, ejected) /* bool */ + __field(int, active) /* bool */ + __field(int, enabled) /* bool */ ), TP_fast_assign( @@ -240,7 +240,7 @@ DECLARE_EVENT_CLASS(gb_interface, __entry->enabled = intf->enabled; ), - TP_printk("greybus: intf_id=%hhu device_id=%hhu module_id=%hhu D=%u J=%u A=%u E=%u", + TP_printk("greybus: intf_id=%hhu device_id=%hhu module_id=%hhu D=%d J=%d A=%d E=%d", __entry->id, __entry->device_id, __entry->module_id, __entry->disconnected, __entry->ejected, __entry->active, __entry->enabled) -- cgit v1.2.3-59-g8ed1b From 76639ef579012a1276043ed765a14170997155b2 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:30 -0500 Subject: greybus: define BUNDLE_ID_NONE Define a bundle ID that means "no bundle". This will be used for tracing connection events during the portion of a connection's lifetime when it has no bundle associated with it. Don't allow any bundle to be created using that ID. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bundle.c | 5 +++++ drivers/staging/greybus/bundle.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 1810b62457bc..e7c00b679636 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -102,6 +102,11 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, { struct gb_bundle *bundle; + if (bundle_id == BUNDLE_ID_NONE) { + dev_err(&intf->dev, "can't use bundle id %u\n", bundle_id); + return NULL; + } + /* * Reject any attempt to reuse a bundle id. We initialize * these serially, so there's no need to worry about keeping diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 2dc61ab7495d..3895f94f43c4 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -12,6 +12,8 @@ #include <linux/list.h> +#define BUNDLE_ID_NONE U8_MAX + /* Greybus "public" definitions" */ struct gb_bundle { struct device dev; -- cgit v1.2.3-59-g8ed1b From c507397455a6b0b7de7393dad9b4c46c6926bc3f Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:31 -0500 Subject: greybus: tracing: fix host device num_cports The type of the gb_host_device num_cports field is size_t. Correct the num_cports data recorded with a host device event so its type matches that; fix the format length modifier used for it as well. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 52a7ef5c4168..7157a6767385 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -302,7 +302,7 @@ DECLARE_EVENT_CLASS(gb_host_device, TP_STRUCT__entry( __field(int, bus_id) - __field(u8, num_cports) + __field(size_t, num_cports) __field(size_t, buffer_size_max) ), @@ -312,7 +312,7 @@ DECLARE_EVENT_CLASS(gb_host_device, __entry->buffer_size_max = hd->buffer_size_max; ), - TP_printk("greybus: bus_id=%d num_cports=%hu mtu=%zu", + TP_printk("greybus: bus_id=%d num_cports=%zu mtu=%zu", __entry->bus_id, __entry->num_cports, __entry->buffer_size_max) ); -- cgit v1.2.3-59-g8ed1b From 1ea3ed54dcc2e623db650cf9592d4d3469c99e65 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:32 -0500 Subject: greybus: tracing: reorder trace definitions Move the definition of the module trace events below those for the interface. We'll define them in an order that represents a sort of layering of the abstractions (note not all of these are defined yet): message operation connection bundle interface module host device Other tracepoints (like perhaps some tied to timesync) will go at the beginning or end. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 106 ++++++++++++++++---------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 7157a6767385..cd930ea75a72 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -161,59 +161,6 @@ DEFINE_OPERATION_EVENT(gb_operation_put_active); #undef DEFINE_OPERATION_EVENT -DECLARE_EVENT_CLASS(gb_module, - - TP_PROTO(struct gb_module *module), - - TP_ARGS(module), - - TP_STRUCT__entry( - __field(int, hd_bus_id) - __field(u8, module_id) - __field(u8, num_interfaces) - __field(int, disconnected) /* bool */ - ), - - TP_fast_assign( - __entry->hd_bus_id = module->hd->bus_id; - __entry->module_id = module->module_id; - __entry->disconnected = module->disconnected; - ), - - TP_printk("greybus: hd_bus_id=%d module_id=%hhu disconnected=%d", - __entry->hd_bus_id, __entry->module_id, __entry->disconnected) -); - -#define DEFINE_MODULE_EVENT(name) \ - DEFINE_EVENT(gb_module, name, \ - TP_PROTO(struct gb_module *module), \ - TP_ARGS(module)) - -/* - * Occurs after a new module is successfully created, before - * creating any of its interfaces. - */ -DEFINE_MODULE_EVENT(gb_module_create); - -/* - * Occurs after the last reference to a module has been dropped. - */ -DEFINE_MODULE_EVENT(gb_module_release); - -/* - * Occurs after a module is successfully created, before registering - * any of its interfaces. - */ -DEFINE_MODULE_EVENT(gb_module_add); - -/* - * Occurs when a module is deleted, before deregistering its - * interfaces. - */ -DEFINE_MODULE_EVENT(gb_module_del); - -#undef DEFINE_MODULE_EVENT - DECLARE_EVENT_CLASS(gb_interface, TP_PROTO(struct gb_interface *intf), @@ -294,6 +241,59 @@ DEFINE_INTERFACE_EVENT(gb_interface_disable); #undef DEFINE_INTERFACE_EVENT +DECLARE_EVENT_CLASS(gb_module, + + TP_PROTO(struct gb_module *module), + + TP_ARGS(module), + + TP_STRUCT__entry( + __field(int, hd_bus_id) + __field(u8, module_id) + __field(u8, num_interfaces) + __field(int, disconnected) /* bool */ + ), + + TP_fast_assign( + __entry->hd_bus_id = module->hd->bus_id; + __entry->module_id = module->module_id; + __entry->disconnected = module->disconnected; + ), + + TP_printk("greybus: hd_bus_id=%d module_id=%hhu disconnected=%d", + __entry->hd_bus_id, __entry->module_id, __entry->disconnected) +); + +#define DEFINE_MODULE_EVENT(name) \ + DEFINE_EVENT(gb_module, name, \ + TP_PROTO(struct gb_module *module), \ + TP_ARGS(module)) + +/* + * Occurs after a new module is successfully created, before + * creating any of its interfaces. + */ +DEFINE_MODULE_EVENT(gb_module_create); + +/* + * Occurs after the last reference to a module has been dropped. + */ +DEFINE_MODULE_EVENT(gb_module_release); + +/* + * Occurs after a module is successfully created, before registering + * any of its interfaces. + */ +DEFINE_MODULE_EVENT(gb_module_add); + +/* + * Occurs when a module is deleted, before deregistering its + * interfaces. + */ +DEFINE_MODULE_EVENT(gb_module_del); + +#undef DEFINE_MODULE_EVENT + DECLARE_EVENT_CLASS(gb_host_device, TP_PROTO(struct gb_host_device *hd), -- cgit v1.2.3-59-g8ed1b From c65fdf031816001dde2bf852560c86d226e54da4 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:33 -0500 Subject: greybus: tracing: fix module num_interfaces A module's num_interfaces field is included in the data to be recorded for tracing, but it's never assigned or reported. Fix its type to be size_t, to match its definition in the gb_module structure. Also correct a format length modifier used for a host device's num_cports field. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index cd930ea75a72..9d38cb31f81f 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -250,18 +250,20 @@ DECLARE_EVENT_CLASS(gb_module, TP_STRUCT__entry( __field(int, hd_bus_id) __field(u8, module_id) - __field(u8, num_interfaces) + __field(size_t, num_interfaces) __field(int, disconnected) /* bool */ ), TP_fast_assign( __entry->hd_bus_id = module->hd->bus_id; __entry->module_id = module->module_id; + __entry->num_interfaces = module->num_interfaces; __entry->disconnected = module->disconnected; ), - TP_printk("greybus: hd_bus_id=%d module_id=%hhu disconnected=%d", - __entry->hd_bus_id, __entry->module_id, __entry->disconnected) + TP_printk("greybus: hd_bus_id=%d module_id=%hhu num_interfaces=%zu disconnected=%d", + __entry->hd_bus_id, __entry->module_id, + __entry->num_interfaces, __entry->disconnected) ); #define DEFINE_MODULE_EVENT(name) \ -- cgit v1.2.3-59-g8ed1b From 14a36ae70429f2872a8f750c60c75d14862761dc Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:34 -0500 Subject: greybus: tracing: assign "parent" id first Most abstractions to be traced will have a sort of "parent" object it is associated with, and an identifier for that parent is stored with the as trace event data. For example, the parent of a message is the operation it's a part of, and the parent of an operation is the connection it uses. We'll arrange to define that parent id first in all events. Most abstractions already do this. Move an interface's module id so it's defined and assigned first. The message traces are going to be changed soon, so leave that one alone. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 9d38cb31f81f..5ecb504f0f65 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -168,8 +168,8 @@ DECLARE_EVENT_CLASS(gb_interface, TP_ARGS(intf), TP_STRUCT__entry( - __field(u8, id) /* Interface id */ __field(u8, module_id) + __field(u8, id) /* Interface id */ __field(u8, device_id) __field(int, disconnected) /* bool */ __field(int, ejected) /* bool */ @@ -178,8 +178,8 @@ DECLARE_EVENT_CLASS(gb_interface, ), TP_fast_assign( - __entry->id = intf->interface_id; __entry->module_id = intf->module->module_id; + __entry->id = intf->interface_id; __entry->device_id = intf->device_id; __entry->disconnected = intf->disconnected; __entry->ejected = intf->ejected; -- cgit v1.2.3-59-g8ed1b From 6879dbf15e866565cde7591337c2fcca7440ab6f Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:35 -0500 Subject: greybus: tracing: add interface mode_switch Add the value of an interface's mode_switch field to the information tracked and reported for tracing. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 5ecb504f0f65..80c428d9d1b1 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -175,6 +175,7 @@ DECLARE_EVENT_CLASS(gb_interface, __field(int, ejected) /* bool */ __field(int, active) /* bool */ __field(int, enabled) /* bool */ + __field(int, mode_switch) /* bool */ ), TP_fast_assign( @@ -185,12 +186,13 @@ DECLARE_EVENT_CLASS(gb_interface, __entry->ejected = intf->ejected; __entry->active = intf->active; __entry->enabled = intf->enabled; + __entry->mode_switch = intf->mode_switch; ), - TP_printk("greybus: intf_id=%hhu device_id=%hhu module_id=%hhu D=%d J=%d A=%d E=%d", + TP_printk("greybus: intf_id=%hhu device_id=%hhu module_id=%hhu D=%d J=%d A=%d E=%d M=%d", __entry->id, __entry->device_id, __entry->module_id, __entry->disconnected, __entry->ejected, __entry->active, - __entry->enabled) + __entry->enabled, __entry->mode_switch) ); #define DEFINE_INTERFACE_EVENT(name) \ -- cgit v1.2.3-59-g8ed1b From 4f9c5c0bbb9fb4715ca01cd28ba04a3117a9be6f Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:36 -0500 Subject: greybus: tracing: define bundle traces Define a new gb_bundle trace point event class, used to trace events associated with the bundle abstraction. Define four basic trace points for this--creation time, drop of last reference, before adding it to its interface and when removed when its interface is destroyed. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bundle.c | 9 ++++++ drivers/staging/greybus/greybus_trace.h | 56 +++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index e7c00b679636..c1c3d67a0279 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -8,6 +8,7 @@ */ #include "greybus.h" +#include "greybus_trace.h" static ssize_t bundle_class_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -82,6 +83,8 @@ static void gb_bundle_release(struct device *dev) { struct gb_bundle *bundle = to_gb_bundle(dev); + trace_gb_bundle_release(bundle); + kfree(bundle->state); kfree(bundle->cport_desc); kfree(bundle); @@ -136,6 +139,8 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, list_add(&bundle->links, &intf->bundles); + trace_gb_bundle_create(bundle); + return bundle; } @@ -149,6 +154,8 @@ int gb_bundle_add(struct gb_bundle *bundle) return ret; } + trace_gb_bundle_add(bundle); + return 0; } @@ -157,6 +164,8 @@ int gb_bundle_add(struct gb_bundle *bundle) */ void gb_bundle_destroy(struct gb_bundle *bundle) { + trace_gb_bundle_destroy(bundle); + if (device_is_registered(&bundle->dev)) device_del(&bundle->dev); diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 80c428d9d1b1..a1dc07038f54 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -16,6 +16,7 @@ struct gb_message; struct gb_operation; +struct gb_bundle; struct gb_host_device; #define gb_bundle_name(message) \ @@ -161,6 +162,61 @@ DEFINE_OPERATION_EVENT(gb_operation_put_active); #undef DEFINE_OPERATION_EVENT +DECLARE_EVENT_CLASS(gb_bundle, + + TP_PROTO(struct gb_bundle *bundle), + + TP_ARGS(bundle), + + TP_STRUCT__entry( + __field(u8, intf_id) + __field(u8, id) + __field(u8, class) + __field(size_t, num_cports) + ), + + TP_fast_assign( + __entry->intf_id = bundle->intf->interface_id; + __entry->id = bundle->id; + __entry->class = bundle->class; + __entry->num_cports = bundle->num_cports; + ), + + TP_printk("intf_id=0x%02x id=%02x class=0x%02x num_cports=%zu", + __entry->intf_id, __entry->id, __entry->class, + __entry->num_cports) +); + +#define DEFINE_BUNDLE_EVENT(name) \ + DEFINE_EVENT(gb_bundle, name, \ + TP_PROTO(struct gb_bundle *bundle), \ + TP_ARGS(bundle)) + +/* + * Occurs after a new bundle is successfully created. + */ +DEFINE_BUNDLE_EVENT(gb_bundle_create); + +/* + * Occurs when the last reference to a bundle has been dropped, + * before its resources are freed. + */ +DEFINE_BUNDLE_EVENT(gb_bundle_release); + +/* + * Occurs when a bundle is added to an interface when the interface + * is enabled. + */ +DEFINE_BUNDLE_EVENT(gb_bundle_add); + +/* + * Occurs when a registered bundle gets destroyed, normally at the + * time an interface is disabled. + */ +DEFINE_BUNDLE_EVENT(gb_bundle_destroy); + +#undef DEFINE_BUNDLE_EVENT + DECLARE_EVENT_CLASS(gb_interface, TP_PROTO(struct gb_interface *intf), -- cgit v1.2.3-59-g8ed1b From 79c8c6494220dafeaedf8dc94b50c9a787e25e5d Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:37 -0500 Subject: greybus: tracing: define connection traces Define a new gb_connection trace point event class, used to trace events associated with the connection abstraction. Define four basic trace events for this--creation, drop of last reference, and when adding or dropping any other reference. There are certainly more events that we might want to add, but aim is to just get the basic framework in place. Signed-off-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 21 +++++++++ drivers/staging/greybus/greybus_trace.h | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3be767b9a0de..d35e95a17c56 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -10,6 +10,7 @@ #include <linux/workqueue.h> #include "greybus.h" +#include "greybus_trace.h" static void gb_connection_kref_release(struct kref *kref); @@ -38,10 +39,14 @@ gb_connection_intf_find(struct gb_interface *intf, u16 cport_id) static void gb_connection_get(struct gb_connection *connection) { kref_get(&connection->kref); + + trace_gb_connection_get(connection); } static void gb_connection_put(struct gb_connection *connection) { + trace_gb_connection_put(connection); + kref_put(&connection->kref, gb_connection_kref_release); } @@ -93,6 +98,8 @@ static void gb_connection_kref_release(struct kref *kref) connection = container_of(kref, struct gb_connection, kref); + trace_gb_connection_release(connection); + kfree(connection); } @@ -204,6 +211,8 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, mutex_unlock(&gb_connection_mutex); + trace_gb_connection_create(connection); + return connection; err_free_connection: @@ -709,6 +718,9 @@ int gb_connection_enable(struct gb_connection *connection) goto out_unlock; ret = _gb_connection_enable(connection, true); + if (!ret) + trace_gb_connection_enable(connection); + out_unlock: mutex_unlock(&connection->mutex); @@ -731,6 +743,9 @@ int gb_connection_enable_tx(struct gb_connection *connection) goto out_unlock; ret = _gb_connection_enable(connection, false); + if (!ret) + trace_gb_connection_enable(connection); + out_unlock: mutex_unlock(&connection->mutex); @@ -751,6 +766,8 @@ void gb_connection_disable_rx(struct gb_connection *connection) gb_connection_flush_incoming_operations(connection, -ESHUTDOWN); spin_unlock_irq(&connection->lock); + trace_gb_connection_disable(connection); + out_unlock: mutex_unlock(&connection->mutex); } @@ -793,6 +810,8 @@ void gb_connection_disable(struct gb_connection *connection) connection->state = GB_CONNECTION_STATE_DISABLED; + trace_gb_connection_disable(connection); + /* control-connection tear down is deferred when mode switching */ if (!connection->mode_switch) { gb_connection_svc_connection_destroy(connection); @@ -822,6 +841,8 @@ void gb_connection_disable_forced(struct gb_connection *connection) gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); + trace_gb_connection_disable(connection); + out_unlock: mutex_unlock(&connection->mutex); } diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index a1dc07038f54..a0757404dc4c 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -16,6 +16,7 @@ struct gb_message; struct gb_operation; +struct gb_connection; struct gb_bundle; struct gb_host_device; @@ -162,6 +163,80 @@ DEFINE_OPERATION_EVENT(gb_operation_put_active); #undef DEFINE_OPERATION_EVENT +DECLARE_EVENT_CLASS(gb_connection, + + TP_PROTO(struct gb_connection *connection), + + TP_ARGS(connection), + + TP_STRUCT__entry( + __field(int, hd_bus_id) + __field(u8, bundle_id) + /* name contains "hd_cport_id/intf_id:cport_id" */ + __dynamic_array(char, name, sizeof(connection->name)) + __field(enum gb_connection_state, state) + __field(unsigned long, flags) + ), + + TP_fast_assign( + __entry->hd_bus_id = connection->hd->bus_id; + __entry->bundle_id = connection->bundle ? + connection->bundle->id : BUNDLE_ID_NONE; + memcpy(__get_str(name), connection->name, + sizeof(connection->name)); + __entry->state = connection->state; + __entry->flags = connection->flags; + ), + + TP_printk("hd_bus_id=%d bundle_id=0x%02x name=\"%s\" state=%u flags=0x%lx", + __entry->hd_bus_id, __entry->bundle_id, __get_str(name), + (unsigned int)__entry->state, __entry->flags) +); + +#define DEFINE_CONNECTION_EVENT(name) \ + DEFINE_EVENT(gb_connection, name, \ + TP_PROTO(struct gb_connection *connection), \ + TP_ARGS(connection)) + +/* + * Occurs after a new connection is successfully created. + */ +DEFINE_CONNECTION_EVENT(gb_connection_create); + +/* + * Occurs when the last reference to a connection has been dropped, + * before its resources are freed. + */ +DEFINE_CONNECTION_EVENT(gb_connection_release); + +/* + * Occurs when a new reference to connection is added, currently + * only when a message over the connection is received. + */ +DEFINE_CONNECTION_EVENT(gb_connection_get); + +/* + * Occurs when a new reference to connection is dropped, after a + * a received message is handled, or when the connection is + * destroyed. + */ +DEFINE_CONNECTION_EVENT(gb_connection_put); + +/* + * Occurs when a request to enable a connection is made, either for + * transmit only, or for both transmit and receive. + */ +DEFINE_CONNECTION_EVENT(gb_connection_enable); + +/* + * Occurs when a request to disable a connection is made, either for + * receive only, or for both transmit and receive. Also occurs when + * a request to forcefully disable a connection is made. + */ +DEFINE_CONNECTION_EVENT(gb_connection_disable); + +#undef DEFINE_CONNECTION_EVENT + DECLARE_EVENT_CLASS(gb_bundle, TP_PROTO(struct gb_bundle *bundle), -- cgit v1.2.3-59-g8ed1b From 495787a792ac498843b25e5569597c24cd026f2b Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:38 -0500 Subject: greybus: tracing: add timing traces Bryan reports he used certain message traces to determine when time sync messages transit the boundary between the Greybus core and the host device. This patch adds two trace events--one a message event for outbound messages (because it indicates its operation and its destination), and one host device event for incoming messages (because message information isn't available as early as desired). These events are being created to allow the same sort of analysis of messages without having to store extra information for every message trace. (The next patch changes the information a message trace records.) Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 2 ++ drivers/staging/greybus/es2.c | 4 ++++ drivers/staging/greybus/greybus_trace.h | 15 ++++++++++++++- drivers/staging/greybus/hd.c | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index d35e95a17c56..7def600ddeb5 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -81,6 +81,8 @@ void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, { struct gb_connection *connection; + trace_gb_hd_in(hd); + connection = gb_connection_hd_find(hd, cport_id); if (!connection) { dev_err(&hd->dev, diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 68a8461ea84c..bdf502493e31 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -14,6 +14,7 @@ #include <asm/unaligned.h> #include "greybus.h" +#include "greybus_trace.h" #include "kernel_ver.h" #include "connection.h" @@ -467,6 +468,9 @@ static int message_send(struct gb_host_device *hd, u16 cport_id, message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; + + trace_gb_message_submit(message); + retval = usb_submit_urb(urb, gfp_mask); if (retval) { dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index a0757404dc4c..62f5803a408d 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -88,6 +88,12 @@ DEFINE_MESSAGE_EVENT(gb_message_cancel_outgoing); */ DEFINE_MESSAGE_EVENT(gb_message_cancel_incoming); +/* + * Occurs in the host driver message_send() function just prior to + * handing off the data to be processed by hardware. + */ +DEFINE_MESSAGE_EVENT(gb_message_submit); + #undef DEFINE_MESSAGE_EVENT DECLARE_EVENT_CLASS(gb_operation, @@ -471,7 +477,7 @@ DEFINE_HD_EVENT(gb_hd_release); /* * Occurs after a new host device has been added, after the - * connection to its SVC has * been enabled. + * connection to its SVC has been enabled. */ DEFINE_HD_EVENT(gb_hd_add); @@ -481,6 +487,13 @@ DEFINE_HD_EVENT(gb_hd_add); */ DEFINE_HD_EVENT(gb_hd_del); +/* + * Occurs when a host device has passed received data to the Greybus + * core, after it has been determined it is destined for a valid + * CPort. + */ +DEFINE_HD_EVENT(gb_hd_in); + #undef DEFINE_HD_EVENT #endif /* _TRACE_GREYBUS_H */ diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 12ac0b6584d8..6d952ba441a6 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -13,6 +13,9 @@ #include "greybus.h" #include "greybus_trace.h" +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_message_submit); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_in); + static struct ida gb_hd_bus_id_map; int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, -- cgit v1.2.3-59-g8ed1b From 0790c09a3bc987a22749b0fb9b3effa313b1ece7 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Fri, 3 Jun 2016 15:55:39 -0500 Subject: greybus: tracing: fix message traces The original message trace events were defined long before the recent tracing updates. It records information that's not really directly related to a message. Change the information recorded and reported for message events to just be the content of the message header. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 62f5803a408d..342f5adb6708 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -20,11 +20,6 @@ struct gb_connection; struct gb_bundle; struct gb_host_device; -#define gb_bundle_name(message) \ - (message->operation->connection->bundle ? \ - dev_name(&message->operation->connection->bundle->dev) : \ - dev_name(&message->operation->connection->hd->svc->dev)) - DECLARE_EVENT_CLASS(gb_message, TP_PROTO(struct gb_message *message), @@ -32,26 +27,23 @@ DECLARE_EVENT_CLASS(gb_message, TP_ARGS(message), TP_STRUCT__entry( - __string(name, gb_bundle_name(message)) - __field(u16, op_id) - __field(u16, intf_cport_id) - __field(u16, hd_cport_id) - __field(size_t, payload_size) + __field(u16, size) + __field(u16, operation_id) + __field(u8, type) + __field(u8, result) ), TP_fast_assign( - __assign_str(name, gb_bundle_name(message)) - __entry->op_id = message->operation->id; - __entry->intf_cport_id = - message->operation->connection->intf_cport_id; - __entry->hd_cport_id = - message->operation->connection->hd_cport_id; - __entry->payload_size = message->payload_size; + __entry->size = le16_to_cpu(message->header->size); + __entry->operation_id = + le16_to_cpu(message->header->operation_id); + __entry->type = message->header->type; + __entry->result = message->header->result; ), - TP_printk("greybus:%s op=%04x if_id=%u hd_id=%u l=%zu", - __get_str(name), __entry->op_id, __entry->intf_cport_id, - __entry->hd_cport_id, __entry->payload_size) + TP_printk("greybus: size=%hu operation_id=0x%04x type=0x%02x result=0x%02x", + __entry->size, __entry->operation_id, + __entry->type, __entry->result) ); #define DEFINE_MESSAGE_EVENT(name) \ -- cgit v1.2.3-59-g8ed1b From 02f1c12cca45ed684a268563919787f06877bef2 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Fri, 3 Jun 2016 17:45:29 +0530 Subject: greybus: audio: Report warning in case module is already removed Added warning message in find_gb_module(). This will help to identify invalid mixer control/widget modification triggered from above layer. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index e423e6d16a25..dfe120baecad 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -47,6 +47,8 @@ static struct gbaudio_module_info *find_gb_module( } } mutex_unlock(&codec->lock); + dev_warn(codec->dev, "%s: module#%d missing in codec list\n", name, + dev_id); return NULL; } -- cgit v1.2.3-59-g8ed1b From 698282f659d7c657aad4e0a72ed33190f2332e04 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Fri, 3 Jun 2016 17:45:30 +0530 Subject: greybus: audio: Report DISCONNECT event after resource cleanup. Reporting DISCONNECT event immediately on module removal causes race condition while re-populating mixer controls by above HAL. The original intent was to avoid any (invalid) mixer control modification request from above layer. Ideally, it should report 'MODULE_NOT_READY' on module plug-out and DISCONNECT after resource cleanup. This would involve changes in GB Audio manager and HAL layer. Since we already have a plan to remove GB Audio manager, I'm making this change in GB codec driver to avoid any race condition. Also, codec driver already ensures mixer control modifcations for disconnected modules are not triggered to AP Bridge audio FW & reported invalid. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 53e84f5fafb0..95d2ddadeeab 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -363,10 +363,12 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) struct gbaudio_data_connection *dai, *_dai; + /* cleanup module related resources first */ + gbaudio_unregister_module(gbmodule); + /* inform uevent to above layers */ gb_audio_manager_remove(gbmodule->manager_id); - gbaudio_unregister_module(gbmodule); gbaudio_tplg_release(gbmodule); kfree(gbmodule->topology); gbmodule->topology = NULL; -- cgit v1.2.3-59-g8ed1b From 970dc85bd95d931def5926ae81b5aa84ef14fb7c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Sun, 5 Jun 2016 14:03:26 +0100 Subject: greybus: timesync: Add timesync core driver This patch adds the core timesync functionality. 0. arche-platform.c/arche-apb-ctrl.c Modifies the platform layer to hook the incoming TIME_SYNC signal up to the timesync strobe IRQ handler. If the arche-platform driver can't satisfy the request for the wake-detect line, it will return -EAGAIN and the calling work-queue must reschedule the attempt to get exclusive access to the wake-detect pin logic. A private data field is added to the arche-platform driver to enable passing of a timesync pointer to the ISR responsible for synchronizing time. 1. timesync.c A new file added which contains all of the logic associated with sending greybus commands to SVC, APBx or Interfaces to enable, disable and disseminate timing information. 2. timesync_platform.c Any platform/arch specific code goes into timesync_platform.c. Originally the idea was to keep the x86 and ARM arch dependencies in a timesync_platform_arch.c file - however with further refinement that's currently not necessary however just-in-case it becomes necessary to resuscitate arch or platform specific methods for accessing timer resources that access shouldn't be part of the core timesync.c logic and so for the moment we access these timer resources through a thin access layer in timesync_platform.c. Expect this to go away long term ideally. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Acked-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 4 +- drivers/staging/greybus/arche-apb-ctrl.c | 16 +- drivers/staging/greybus/arche-platform.c | 71 +- drivers/staging/greybus/arche_platform.h | 9 +- drivers/staging/greybus/greybus.h | 2 +- drivers/staging/greybus/interface.c | 55 ++ drivers/staging/greybus/interface.h | 5 + drivers/staging/greybus/timesync.c | 1021 +++++++++++++++++++++++++++ drivers/staging/greybus/timesync.h | 41 ++ drivers/staging/greybus/timesync_platform.c | 77 ++ 10 files changed, 1288 insertions(+), 13 deletions(-) create mode 100644 drivers/staging/greybus/timesync.c create mode 100644 drivers/staging/greybus/timesync.h create mode 100644 drivers/staging/greybus/timesync_platform.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index f7fe4d512015..981569dc86a7 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -10,7 +10,9 @@ greybus-y := core.o \ svc.o \ svc_watchdog.o \ bootrom.o \ - operation.o + operation.o \ + timesync.o \ + timesync_platform.o gb-gbphy-y := gbphy.o diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index a9d78a85939a..fce6a187ecfe 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -90,7 +90,7 @@ static int coldboot_seq(struct platform_device *pdev) } } - gpio_set_value(apb->boot_ret_gpio, 0); + apb_bootret_deassert(dev); /* On DB3 clock was not mandatory */ if (gpio_is_valid(apb->clk_en_gpio)) @@ -184,6 +184,20 @@ static void poweroff_seq(struct platform_device *pdev) /* TODO: May have to send an event to SVC about this exit */ } +void apb_bootret_assert(struct device *dev) +{ + struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); + + gpio_set_value(apb->boot_ret_gpio, 1); +} + +void apb_bootret_deassert(struct device *dev) +{ + struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); + + gpio_set_value(apb->boot_ret_gpio, 0); +} + int apb_ctrl_coldboot(struct device *dev) { return coldboot_seq(to_platform_device(dev)); diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index f47ea4670d25..31c952454cff 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -22,6 +22,7 @@ #include <linux/suspend.h> #include <linux/time.h> #include "arche_platform.h" +#include "greybus.h" #include <linux/usb/usb3613.h> @@ -34,6 +35,7 @@ enum svc_wakedetect_state { WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ WD_STATE_COLDBOOT_START, /* Cold boot process started */ WD_STATE_STANDBYBOOT_START, /* Not used */ + WD_STATE_TIMESYNC, }; struct arche_platform_drvdata { @@ -57,13 +59,27 @@ struct arche_platform_drvdata { int wake_detect_irq; spinlock_t wake_lock; /* Protect wake_detect_state */ struct mutex platform_state_mutex; /* Protect state */ + wait_queue_head_t wq; /* WQ for arche_pdata->state */ unsigned long wake_detect_start; struct notifier_block pm_notifier; struct device *dev; + struct gb_timesync_svc *timesync_svc_pdata; }; -/* Requires calling context to hold arche_pdata->spinlock */ +static int arche_apb_bootret_assert(struct device *dev, void *data) +{ + apb_bootret_assert(dev); + return 0; +} + +static int arche_apb_bootret_deassert(struct device *dev, void *data) +{ + apb_bootret_deassert(dev); + return 0; +} + +/* Requires calling context to hold arche_pdata->platform_state_mutex */ static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata, enum arche_platform_state state) { @@ -94,7 +110,8 @@ static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata, * satisfy the requested state-transition or -EINVAL for all other * state-transition requests. */ -int arche_platform_change_state(enum arche_platform_state state) +int arche_platform_change_state(enum arche_platform_state state, + struct gb_timesync_svc *timesync_svc_pdata) { struct arche_platform_drvdata *arche_pdata; struct platform_device *pdev; @@ -118,11 +135,6 @@ int arche_platform_change_state(enum arche_platform_state state) mutex_lock(&arche_pdata->platform_state_mutex); spin_lock_irqsave(&arche_pdata->wake_lock, flags); - if (arche_pdata->wake_detect_state != WD_STATE_IDLE) { - dev_err(arche_pdata->dev, - "driver busy with wake/detect line ops\n"); - goto exit; - } if (arche_pdata->state == state) { ret = 0; @@ -131,10 +143,27 @@ int arche_platform_change_state(enum arche_platform_state state) switch (state) { case ARCHE_PLATFORM_STATE_TIME_SYNC: - disable_irq(arche_pdata->wake_detect_irq); + if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { + ret = -EINVAL; + goto exit; + } + if (arche_pdata->wake_detect_state != WD_STATE_IDLE) { + dev_err(arche_pdata->dev, + "driver busy with wake/detect line ops\n"); + goto exit; + } + device_for_each_child(arche_pdata->dev, NULL, + arche_apb_bootret_assert); + arche_pdata->wake_detect_state = WD_STATE_TIMESYNC; break; case ARCHE_PLATFORM_STATE_ACTIVE: - enable_irq(arche_pdata->wake_detect_irq); + if (arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC) { + ret = -EINVAL; + goto exit; + } + device_for_each_child(arche_pdata->dev, NULL, + arche_apb_bootret_deassert); + arche_pdata->wake_detect_state = WD_STATE_IDLE; break; case ARCHE_PLATFORM_STATE_OFF: case ARCHE_PLATFORM_STATE_STANDBY: @@ -147,7 +176,11 @@ int arche_platform_change_state(enum arche_platform_state state) "invalid state transition request\n"); goto exit; } + arche_pdata->timesync_svc_pdata = timesync_svc_pdata; arche_platform_set_state(arche_pdata, state); + if (state == ARCHE_PLATFORM_STATE_ACTIVE) + wake_up(&arche_pdata->wq); + ret = 0; exit: spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); @@ -252,6 +285,11 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) spin_lock_irqsave(&arche_pdata->wake_lock, flags); + if (arche_pdata->wake_detect_state == WD_STATE_TIMESYNC) { + gb_timesync_irq(arche_pdata->timesync_svc_pdata); + goto exit; + } + if (gpio_get_value(arche_pdata->wake_detect_gpio)) { /* wake/detect rising */ @@ -293,6 +331,7 @@ static irqreturn_t arche_platform_wd_irq(int irq, void *devid) } } +exit: spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); return IRQ_HANDLED; @@ -402,7 +441,17 @@ static ssize_t state_store(struct device *dev, struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); int ret = 0; +retry: mutex_lock(&arche_pdata->platform_state_mutex); + if (arche_pdata->state == ARCHE_PLATFORM_STATE_TIME_SYNC) { + mutex_unlock(&arche_pdata->platform_state_mutex); + ret = wait_event_interruptible( + arche_pdata->wq, + arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC); + if (ret) + return ret; + goto retry; + } if (sysfs_streq(buf, "off")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) @@ -610,6 +659,7 @@ static int arche_platform_probe(struct platform_device *pdev) spin_lock_init(&arche_pdata->wake_lock); mutex_init(&arche_pdata->platform_state_mutex); + init_waitqueue_head(&arche_pdata->wq); arche_pdata->wake_detect_irq = gpio_to_irq(arche_pdata->wake_detect_gpio); @@ -653,6 +703,9 @@ static int arche_platform_probe(struct platform_device *pdev) goto err_populate; } + /* Register callback pointer */ + arche_platform_change_state_cb = arche_platform_change_state; + dev_info(dev, "Device registered successfully\n"); return 0; diff --git a/drivers/staging/greybus/arche_platform.h b/drivers/staging/greybus/arche_platform.h index 0f2d29208f7f..bd12345b82a2 100644 --- a/drivers/staging/greybus/arche_platform.h +++ b/drivers/staging/greybus/arche_platform.h @@ -10,6 +10,8 @@ #ifndef __ARCHE_PLATFORM_H #define __ARCHE_PLATFORM_H +#include "timesync.h" + enum arche_platform_state { ARCHE_PLATFORM_STATE_OFF, ARCHE_PLATFORM_STATE_ACTIVE, @@ -18,8 +20,11 @@ enum arche_platform_state { ARCHE_PLATFORM_STATE_TIME_SYNC, }; -int arche_platform_change_state(enum arche_platform_state state); +int arche_platform_change_state(enum arche_platform_state state, + struct gb_timesync_svc *pdata); +extern int (*arche_platform_change_state_cb)(enum arche_platform_state state, + struct gb_timesync_svc *pdata); int __init arche_apb_init(void); void __exit arche_apb_exit(void); @@ -28,5 +33,7 @@ int apb_ctrl_coldboot(struct device *dev); int apb_ctrl_fw_flashing(struct device *dev); int apb_ctrl_standby_boot(struct device *dev); void apb_ctrl_poweroff(struct device *dev); +void apb_bootret_assert(struct device *dev); +void apb_bootret_deassert(struct device *dev); #endif /* __ARCHE_PLATFORM_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index e0b194c097a8..154dfd11e93b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -33,7 +33,7 @@ #include "bundle.h" #include "connection.h" #include "operation.h" - +#include "timesync.h" /* Matches up with the Greybus Protocol specification document */ #define GREYBUS_VERSION_MAJOR 0x00 diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 577d15fb5bd2..3eb8754961f1 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -865,6 +865,61 @@ void gb_interface_disable(struct gb_interface *intf) intf->enabled = false; } +/* + * Enable TimeSync on an Interface control connection. + * + * Locking: Takes and releases the interface mutex. + */ +int gb_interface_timesync_enable(struct gb_interface *intf, u8 count, + u64 frame_time, u32 strobe_delay, u32 refclk) +{ + int ret = -ENODEV; + + mutex_lock(&intf->mutex); + if (intf->enabled) { + ret = gb_control_timesync_enable(intf->control, count, + frame_time, strobe_delay, + refclk); + } + mutex_unlock(&intf->mutex); + return ret; +} + +/* + * Disable TimeSync on an Interface control connection. + * + * Locking: Takes and releases the interface mutex. + */ +int gb_interface_timesync_disable(struct gb_interface *intf) +{ + int ret = -ENODEV; + + mutex_lock(&intf->mutex); + if (intf->enabled) + ret = gb_control_timesync_disable(intf->control); + mutex_unlock(&intf->mutex); + return ret; +} + +/* + * Transmit the Authoritative FrameTime via an Interface control connection. + * + * Locking: Takes and releases the interface mutex. + */ +int gb_interface_timesync_authoritative(struct gb_interface *intf, + u64 *frame_time) +{ + int ret = -ENODEV; + + mutex_lock(&intf->mutex); + if (intf->enabled) { + ret = gb_control_timesync_authoritative(intf->control, + frame_time); + } + mutex_unlock(&intf->mutex); + return ret; +} + /* Register an interface. */ int gb_interface_add(struct gb_interface *intf) { diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 603f146dce42..8796c55b7ffd 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -58,6 +58,11 @@ int gb_interface_activate(struct gb_interface *intf); void gb_interface_deactivate(struct gb_interface *intf); int gb_interface_enable(struct gb_interface *intf); void gb_interface_disable(struct gb_interface *intf); +int gb_interface_timesync_enable(struct gb_interface *intf, u8 count, + u64 frame_time, u32 strobe_delay, u32 refclk); +int gb_interface_timesync_authoritative(struct gb_interface *intf, + u64 *frame_time); +int gb_interface_timesync_disable(struct gb_interface *intf); int gb_interface_add(struct gb_interface *intf); void gb_interface_del(struct gb_interface *intf); void gb_interface_put(struct gb_interface *intf); diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c new file mode 100644 index 000000000000..260b670596bb --- /dev/null +++ b/drivers/staging/greybus/timesync.c @@ -0,0 +1,1021 @@ +/* + * TimeSync API driver. + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ +#include <linux/debugfs.h> +#include "greybus.h" +#include "timesync.h" + +/* + * Minimum inter-strobe value of one millisecond is chosen because it + * just-about fits the common definition of a jiffy. + * + * Maximum value OTOH is constrained by the number of bits the SVC can fit + * into a 16 bit up-counter. The SVC configures the timer in microseconds + * so the maximum allowable value is 65535 microseconds. We clip that value + * to 10000 microseconds for the sake of using nice round base 10 numbers + * and since right-now there's no imaginable use-case requiring anything + * other than a one millisecond inter-strobe time, let alone something + * higher than ten milliseconds. + */ +#define GB_TIMESYNC_STROBE_DELAY_US 1000 +#define GB_TIMESYNC_DEFAULT_OFFSET_US 1000 + +/* Work queue timers long, short and SVC strobe timeout */ +#define GB_TIMESYNC_DELAYED_WORK_LONG msecs_to_jiffies(1000) +#define GB_TIMESYNC_DELAYED_WORK_SHORT msecs_to_jiffies(1) +#define GB_TIMESYNC_MAX_WAIT_SVC msecs_to_jiffies(5000) + +/* Reported nanoseconds per clock */ +static u64 gb_timesync_ns_per_clock; + +/* Reported clock rate */ +static unsigned long gb_timesync_clock_rate; + +/* Workqueue */ +static void gb_timesync_worker(struct work_struct *work); + +/* List of SVCs with one FrameTime per SVC */ +static LIST_HEAD(gb_timesync_svc_list); + +/* Synchronize parallel contexts accessing a valid timesync_svc pointer */ +static DEFINE_MUTEX(gb_timesync_svc_list_mutex); + +struct gb_timesync_svc { + struct list_head list; + struct list_head interface_list; + struct gb_svc *svc; + struct gb_timesync_host_device *timesync_hd; + + spinlock_t spinlock; /* Per SVC spinlock to sync with ISR */ + struct mutex mutex; /* Per SVC mutex for regular synchronization */ + + struct dentry *frame_time_dentry; + struct workqueue_struct *work_queue; + wait_queue_head_t wait_queue; + struct delayed_work delayed_work; + + /* The current local FrameTime */ + u64 frame_time_offset; + u64 strobe_time[GB_TIMESYNC_MAX_STROBES]; + + /* The SVC FrameTime and relative AP FrameTime @ last TIMESYNC_PING */ + u64 svc_ping_frame_time; + u64 ap_ping_frame_time; + + /* Transitory settings */ + u32 strobe_mask; + bool offset_down; + bool print_ping; + bool capture_ping; + int strobe; + + /* Current state */ + int state; +}; + +struct gb_timesync_host_device { + struct list_head list; + struct gb_host_device *hd; + u64 ping_frame_time; +}; + +struct gb_timesync_interface { + struct list_head list; + struct gb_interface *interface; + u64 ping_frame_time; +}; + +enum gb_timesync_state { + GB_TIMESYNC_STATE_INVALID = 0, + GB_TIMESYNC_STATE_INACTIVE = 1, + GB_TIMESYNC_STATE_INIT = 2, + GB_TIMESYNC_STATE_WAIT_SVC = 3, + GB_TIMESYNC_STATE_AUTHORITATIVE = 4, + GB_TIMESYNC_STATE_PING = 5, + GB_TIMESYNC_STATE_ACTIVE = 6, +}; + +static u64 gb_timesync_adjust_count(struct gb_timesync_svc *timesync_svc, + u64 counts) +{ + if (timesync_svc->offset_down) + return counts - timesync_svc->frame_time_offset; + else + return counts + timesync_svc->frame_time_offset; +} + +/* + * This function provides the authoritative FrameTime to a calling function. It + * is designed to be lockless and should remain that way the caller is assumed + * to be state-aware. + */ +static u64 __gb_timesync_get_frame_time(struct gb_timesync_svc *timesync_svc) +{ + u64 clocks = gb_timesync_platform_get_counter(); + + return gb_timesync_adjust_count(timesync_svc, clocks); +} + +static void gb_timesync_schedule_svc_timeout(struct gb_timesync_svc + *timesync_svc) +{ + queue_delayed_work(timesync_svc->work_queue, + ×ync_svc->delayed_work, + GB_TIMESYNC_MAX_WAIT_SVC); +} + +static void gb_timesync_set_state(struct gb_timesync_svc *timesync_svc, + int state) +{ + switch (state) { + case GB_TIMESYNC_STATE_INVALID: + timesync_svc->state = state; + wake_up(×ync_svc->wait_queue); + break; + case GB_TIMESYNC_STATE_INACTIVE: + if (timesync_svc->state != GB_TIMESYNC_STATE_INIT) { + timesync_svc->state = state; + wake_up(×ync_svc->wait_queue); + } + break; + case GB_TIMESYNC_STATE_INIT: + if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) { + timesync_svc->strobe = 0; + timesync_svc->frame_time_offset = 0; + timesync_svc->state = state; + cancel_delayed_work(×ync_svc->delayed_work); + queue_delayed_work(timesync_svc->work_queue, + ×ync_svc->delayed_work, + GB_TIMESYNC_DELAYED_WORK_LONG); + } + break; + case GB_TIMESYNC_STATE_WAIT_SVC: + if (timesync_svc->state == GB_TIMESYNC_STATE_INIT) + timesync_svc->state = state; + break; + case GB_TIMESYNC_STATE_AUTHORITATIVE: + if (timesync_svc->state == GB_TIMESYNC_STATE_WAIT_SVC) { + timesync_svc->state = state; + cancel_delayed_work(×ync_svc->delayed_work); + queue_delayed_work(timesync_svc->work_queue, + ×ync_svc->delayed_work, 0); + } + break; + case GB_TIMESYNC_STATE_PING: + if (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE) { + timesync_svc->state = state; + queue_delayed_work(timesync_svc->work_queue, + ×ync_svc->delayed_work, + GB_TIMESYNC_DELAYED_WORK_SHORT); + } + break; + case GB_TIMESYNC_STATE_ACTIVE: + if (timesync_svc->state == GB_TIMESYNC_STATE_AUTHORITATIVE || + timesync_svc->state == GB_TIMESYNC_STATE_PING) { + timesync_svc->state = state; + wake_up(×ync_svc->wait_queue); + } + break; + } + + if (WARN_ON(timesync_svc->state != state)) { + pr_err("Invalid state transition %d=>%d\n", + timesync_svc->state, state); + } +} + +static void gb_timesync_set_state_atomic(struct gb_timesync_svc *timesync_svc, + int state) +{ + unsigned long flags; + + spin_lock_irqsave(×ync_svc->spinlock, flags); + gb_timesync_set_state(timesync_svc, state); + spin_unlock_irqrestore(×ync_svc->spinlock, flags); +} + +static u64 gb_timesync_diff(u64 x, u64 y) +{ + if (x > y) + return x - y; + else + return y - x; +} + +static void gb_timesync_adjust_to_svc(struct gb_timesync_svc *svc, + u64 svc_frame_time, u64 ap_frame_time) +{ + if (svc_frame_time > ap_frame_time) { + svc->frame_time_offset = svc_frame_time - ap_frame_time; + svc->offset_down = false; + } else { + svc->frame_time_offset = ap_frame_time - svc_frame_time; + svc->offset_down = true; + } +} + +/* + * Find the two pulses that best-match our expected inter-strobe gap and + * then calculate the difference between the SVC time at the second pulse + * to the local time at the second pulse. + */ +static void gb_timesync_collate_frame_time(struct gb_timesync_svc *timesync_svc, + u64 *frame_time) +{ + int i = 0; + u64 delta; + u64 strobe_delay_ns = GB_TIMESYNC_STROBE_DELAY_US * NSEC_PER_USEC; + u64 least = 0; + + for (i = 1; i < GB_TIMESYNC_MAX_STROBES; i++) { + delta = timesync_svc->strobe_time[i] - + timesync_svc->strobe_time[i - 1]; + delta *= gb_timesync_ns_per_clock; + delta = gb_timesync_diff(delta, strobe_delay_ns); + + if (!least || delta < least) { + least = delta; + gb_timesync_adjust_to_svc(timesync_svc, frame_time[i], + timesync_svc->strobe_time[i]); + pr_debug("adjust %s local %llu svc %llu delta %llu\n", + timesync_svc->offset_down ? "down" : "up", + timesync_svc->strobe_time[i], frame_time[i], + delta); + } + } +} + +static void gb_timesync_teardown(struct gb_timesync_svc *timesync_svc) +{ + struct gb_timesync_interface *timesync_interface; + struct gb_svc *svc = timesync_svc->svc; + struct gb_interface *interface; + struct gb_host_device *hd; + int ret; + + list_for_each_entry(timesync_interface, + ×ync_svc->interface_list, list) { + interface = timesync_interface->interface; + ret = gb_interface_timesync_disable(interface); + if (ret) { + dev_err(&interface->dev, + "interface timesync_disable %d\n", ret); + } + } + + hd = timesync_svc->timesync_hd->hd; + ret = hd->driver->timesync_disable(hd); + if (ret < 0) { + dev_err(&hd->dev, "host timesync_disable %d\n", + ret); + } + + gb_svc_timesync_wake_pins_release(svc); + gb_svc_timesync_disable(svc); + gb_timesync_platform_unlock_bus(); + + gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); +} + +static void gb_timesync_platform_lock_bus_fail(struct gb_timesync_svc + *timesync_svc, int ret) +{ + if (ret == -EAGAIN) { + gb_timesync_set_state(timesync_svc, timesync_svc->state); + } else { + pr_err("Failed to lock timesync bus %d\n", ret); + gb_timesync_set_state(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); + } +} + +static void gb_timesync_enable(struct gb_timesync_svc *timesync_svc) +{ + struct gb_svc *svc = timesync_svc->svc; + struct gb_host_device *hd; + struct gb_timesync_interface *timesync_interface; + struct gb_interface *interface; + u64 init_frame_time; + unsigned long clock_rate = gb_timesync_clock_rate; + int ret; + + /* + * Get access to the wake pins in the AP and SVC + * Release these pins either in gb_timesync_teardown() or in + * gb_timesync_authoritative() + */ + ret = gb_timesync_platform_lock_bus(timesync_svc); + if (ret < 0) { + gb_timesync_platform_lock_bus_fail(timesync_svc, ret); + return; + } + ret = gb_svc_timesync_wake_pins_acquire(svc, timesync_svc->strobe_mask); + if (ret) { + dev_err(&svc->dev, + "gb_svc_timesync_wake_pins_acquire %d\n", ret); + gb_timesync_teardown(timesync_svc); + return; + } + + /* Choose an initial time in the future */ + init_frame_time = __gb_timesync_get_frame_time(timesync_svc) + 100000UL; + + /* Send enable command to all relevant participants */ + list_for_each_entry(timesync_interface, ×ync_svc->interface_list, + list) { + interface = timesync_interface->interface; + ret = gb_interface_timesync_enable(interface, + GB_TIMESYNC_MAX_STROBES, + init_frame_time, + GB_TIMESYNC_STROBE_DELAY_US, + clock_rate); + if (ret) { + dev_err(&interface->dev, + "interface timesync_enable %d\n", ret); + } + } + + hd = timesync_svc->timesync_hd->hd; + ret = hd->driver->timesync_enable(hd, GB_TIMESYNC_MAX_STROBES, + init_frame_time, + GB_TIMESYNC_STROBE_DELAY_US, + clock_rate); + if (ret < 0) { + dev_err(&hd->dev, "host timesync_enable %d\n", + ret); + } + + gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_WAIT_SVC); + ret = gb_svc_timesync_enable(svc, GB_TIMESYNC_MAX_STROBES, + init_frame_time, + GB_TIMESYNC_STROBE_DELAY_US, + clock_rate); + if (ret) { + dev_err(&svc->dev, + "gb_svc_timesync_enable %d\n", ret); + gb_timesync_teardown(timesync_svc); + return; + } + + /* Schedule a timeout waiting for SVC to complete strobing */ + gb_timesync_schedule_svc_timeout(timesync_svc); +} + +static void gb_timesync_authoritative(struct gb_timesync_svc *timesync_svc) +{ + struct gb_svc *svc = timesync_svc->svc; + struct gb_host_device *hd; + struct gb_timesync_interface *timesync_interface; + struct gb_interface *interface; + u64 svc_frame_time[GB_TIMESYNC_MAX_STROBES]; + int ret; + + /* Get authoritative time from SVC and adjust local clock */ + ret = gb_svc_timesync_authoritative(svc, svc_frame_time); + if (ret) { + dev_err(&svc->dev, + "gb_svc_timesync_authoritative %d\n", ret); + gb_timesync_teardown(timesync_svc); + return; + } + gb_timesync_collate_frame_time(timesync_svc, svc_frame_time); + + /* Transmit authoritative time to downstream slaves */ + hd = timesync_svc->timesync_hd->hd; + ret = hd->driver->timesync_authoritative(hd, svc_frame_time); + if (ret < 0) + dev_err(&hd->dev, "host timesync_authoritative %d\n", ret); + + list_for_each_entry(timesync_interface, + ×ync_svc->interface_list, list) { + interface = timesync_interface->interface; + ret = gb_interface_timesync_authoritative( + interface, + svc_frame_time); + if (ret) { + dev_err(&interface->dev, + "interface timesync_authoritative %d\n", ret); + } + } + + /* Release wake pins */ + gb_svc_timesync_wake_pins_release(svc); + gb_timesync_platform_unlock_bus(); + + /* Transition to state ACTIVE */ + gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_ACTIVE); + + /* Schedule a ping to verify the synchronized system time */ + timesync_svc->print_ping = true; + gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_PING); +} + +static size_t gb_timesync_log_frame_time(struct gb_timesync_svc *timesync_svc, + char *buf, size_t buflen) +{ + struct gb_svc *svc = timesync_svc->svc; + struct gb_host_device *hd; + struct gb_timesync_interface *timesync_interface; + struct gb_interface *interface; + unsigned int len; + size_t off; + + /* AP/SVC */ + memset(buf, 0x00, buflen); + off = snprintf(buf, buflen, "timesync: ping-time ap=%llu %s=%llu ", + timesync_svc->ap_ping_frame_time, dev_name(&svc->dev), + timesync_svc->svc_ping_frame_time); + len = buflen - off; + + /* APB/GPB */ + if (len < buflen) { + hd = timesync_svc->timesync_hd->hd; + off += snprintf(&buf[off], len, "%s=%llu ", dev_name(&hd->dev), + timesync_svc->timesync_hd->ping_frame_time); + len = buflen - off; + } + + list_for_each_entry(timesync_interface, + ×ync_svc->interface_list, list) { + if (len < buflen) { + interface = timesync_interface->interface; + off += snprintf(&buf[off], len, "%s=%llu ", + dev_name(&interface->dev), + timesync_interface->ping_frame_time); + len = buflen - off; + } + } + if (len < buflen) + off += snprintf(&buf[off], len, "\n"); + return off; +} + +/* + * Send an SVC initiated wake 'ping' to each TimeSync participant. + * Get the FrameTime from each participant associated with the wake + * ping. + */ +static void gb_timesync_ping(struct gb_timesync_svc *timesync_svc) +{ + struct gb_svc *svc = timesync_svc->svc; + struct gb_host_device *hd; + struct gb_timesync_interface *timesync_interface; + struct gb_control *control; + u64 *ping_frame_time; + int ret; + + /* Get access to the wake pins in the AP and SVC */ + ret = gb_timesync_platform_lock_bus(timesync_svc); + if (ret < 0) { + gb_timesync_platform_lock_bus_fail(timesync_svc, ret); + return; + } + ret = gb_svc_timesync_wake_pins_acquire(svc, timesync_svc->strobe_mask); + if (ret) { + dev_err(&svc->dev, + "gb_svc_timesync_wake_pins_acquire %d\n", ret); + gb_timesync_teardown(timesync_svc); + return; + } + + /* Have SVC generate a timesync ping */ + timesync_svc->capture_ping = true; + ret = gb_svc_timesync_ping(svc, ×ync_svc->svc_ping_frame_time); + timesync_svc->capture_ping = false; + if (ret) { + dev_err(&svc->dev, + "gb_svc_timesync_ping %d\n", ret); + gb_timesync_teardown(timesync_svc); + return; + } + + /* Get the ping FrameTime from each APB/GPB */ + hd = timesync_svc->timesync_hd->hd; + ret = hd->driver->timesync_get_last_event(hd, + ×ync_svc->timesync_hd->ping_frame_time); + if (ret) + dev_err(&hd->dev, "host timesync_get_last_event %d\n", ret); + + list_for_each_entry(timesync_interface, + ×ync_svc->interface_list, list) { + control = timesync_interface->interface->control; + ping_frame_time = ×ync_interface->ping_frame_time; + ret = gb_control_timesync_get_last_event(control, + ping_frame_time); + if (ret) { + dev_err(×ync_interface->interface->dev, + "gb_control_timesync_get_last_event %d\n", ret); + } + } + + /* Ping success - move to timesync active */ + gb_svc_timesync_wake_pins_release(svc); + gb_timesync_platform_unlock_bus(); + gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_ACTIVE); +} + +static void gb_timesync_log_ping_time(struct gb_timesync_svc *timesync_svc) +{ + char *buf; + + if (!timesync_svc->print_ping) + return; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf) { + gb_timesync_log_frame_time(timesync_svc, buf, PAGE_SIZE); + pr_info("%s", buf); + kfree(buf); + } +} + +/* + * Perform the actual work of scheduled TimeSync logic. + */ +static void gb_timesync_worker(struct work_struct *work) +{ + struct delayed_work *delayed_work = to_delayed_work(work); + struct gb_timesync_svc *timesync_svc = + container_of(delayed_work, struct gb_timesync_svc, delayed_work); + + mutex_lock(×ync_svc->mutex); + + switch (timesync_svc->state) { + case GB_TIMESYNC_STATE_INIT: + gb_timesync_enable(timesync_svc); + break; + + case GB_TIMESYNC_STATE_WAIT_SVC: + dev_err(×ync_svc->svc->dev, + "timeout SVC strobe completion\n"); + gb_timesync_teardown(timesync_svc); + break; + + case GB_TIMESYNC_STATE_AUTHORITATIVE: + gb_timesync_authoritative(timesync_svc); + break; + + case GB_TIMESYNC_STATE_PING: + gb_timesync_ping(timesync_svc); + gb_timesync_log_ping_time(timesync_svc); + break; + + default: + pr_err("Invalid state %d for delayed work\n", + timesync_svc->state); + break; + } + + mutex_unlock(×ync_svc->mutex); +} + +/* + * Schedule a new TimeSync INIT or PING operation serialized w/r to + * gb_timesync_worker(). + */ +static int gb_timesync_schedule(struct gb_timesync_svc *timesync_svc, int state) +{ + int ret = 0; + + if (state != GB_TIMESYNC_STATE_INIT && state != GB_TIMESYNC_STATE_PING) + return -EINVAL; + + mutex_lock(×ync_svc->mutex); + if (timesync_svc->state == GB_TIMESYNC_STATE_INACTIVE || + timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE) { + gb_timesync_set_state_atomic(timesync_svc, state); + } else { + ret = -ENODEV; + } + mutex_unlock(×ync_svc->mutex); + return ret; +} + +static int __gb_timesync_schedule_synchronous( + struct gb_timesync_svc *timesync_svc, int state) +{ + unsigned long flags; + int ret; + + ret = gb_timesync_schedule(timesync_svc, state); + if (ret) + return ret; + + ret = wait_event_interruptible(timesync_svc->wait_queue, + (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE || + timesync_svc->state == GB_TIMESYNC_STATE_INACTIVE || + timesync_svc->state == GB_TIMESYNC_STATE_INVALID)); + if (ret) + return ret; + + mutex_lock(×ync_svc->mutex); + spin_lock_irqsave(×ync_svc->spinlock, flags); + + switch (timesync_svc->state) { + case GB_TIMESYNC_STATE_INVALID: + case GB_TIMESYNC_STATE_INACTIVE: + ret = -ENODEV; + break; + case GB_TIMESYNC_STATE_INIT: + case GB_TIMESYNC_STATE_WAIT_SVC: + case GB_TIMESYNC_STATE_AUTHORITATIVE: + case GB_TIMESYNC_STATE_PING: + ret = -EAGAIN; + break; + case GB_TIMESYNC_STATE_ACTIVE: + ret = 0; + break; + } + + spin_unlock_irqrestore(×ync_svc->spinlock, flags); + mutex_unlock(×ync_svc->mutex); + + return ret; +} + +static struct gb_timesync_svc *gb_timesync_find_timesync_svc( + struct gb_host_device *hd) +{ + struct gb_timesync_svc *timesync_svc; + + list_for_each_entry(timesync_svc, &gb_timesync_svc_list, list) { + if (timesync_svc->svc == hd->svc) + return timesync_svc; + } + return NULL; +} + +static struct gb_timesync_interface *gb_timesync_find_timesync_interface( + struct gb_timesync_svc *timesync_svc, + struct gb_interface *interface) +{ + struct gb_timesync_interface *timesync_interface; + + list_for_each_entry(timesync_interface, ×ync_svc->interface_list, list) { + if (timesync_interface->interface == interface) + return timesync_interface; + } + return NULL; +} + +int gb_timesync_schedule_synchronous(struct gb_interface *interface) +{ + int ret; + struct gb_timesync_svc *timesync_svc; + + if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) + return 0; + + mutex_lock(&gb_timesync_svc_list_mutex); + timesync_svc = gb_timesync_find_timesync_svc(interface->hd); + if (!timesync_svc) { + ret = -ENODEV; + goto done; + } + + ret = __gb_timesync_schedule_synchronous(timesync_svc, + GB_TIMESYNC_STATE_INIT); +done: + mutex_unlock(&gb_timesync_svc_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(gb_timesync_schedule_synchronous); + +void gb_timesync_schedule_asynchronous(struct gb_interface *interface) +{ + struct gb_timesync_svc *timesync_svc; + + if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) + return; + + mutex_lock(&gb_timesync_svc_list_mutex); + timesync_svc = gb_timesync_find_timesync_svc(interface->hd); + if (!timesync_svc) + goto done; + + gb_timesync_schedule(timesync_svc, GB_TIMESYNC_STATE_INIT); +done: + mutex_unlock(&gb_timesync_svc_list_mutex); + return; +} +EXPORT_SYMBOL_GPL(gb_timesync_schedule_asynchronous); + +static ssize_t gb_timesync_ping_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + struct gb_timesync_svc *timesync_svc = file->f_inode->i_private; + char *pbuf; + ssize_t ret = 0; + + mutex_lock(&gb_timesync_svc_list_mutex); + mutex_lock(×ync_svc->mutex); + if (list_empty(×ync_svc->interface_list)) + ret = -ENODEV; + timesync_svc->print_ping = false; + mutex_unlock(×ync_svc->mutex); + if (ret) + goto done; + + ret = __gb_timesync_schedule_synchronous(timesync_svc, + GB_TIMESYNC_STATE_PING); + if (ret) + goto done; + + pbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!pbuf) { + ret = -ENOMEM; + goto done; + } + + ret = gb_timesync_log_frame_time(timesync_svc, pbuf, PAGE_SIZE); + if (ret > 0) + ret = simple_read_from_buffer(buf, len, offset, pbuf, ret); + kfree(pbuf); +done: + mutex_unlock(&gb_timesync_svc_list_mutex); + return ret; +} + +static const struct file_operations gb_timesync_debugfs_ops = { + .read = gb_timesync_ping_read, +}; + +static int gb_timesync_hd_add(struct gb_timesync_svc *timesync_svc, + struct gb_host_device *hd) +{ + struct gb_timesync_host_device *timesync_hd; + + timesync_hd = kzalloc(sizeof(*timesync_hd), GFP_KERNEL); + if (!timesync_hd) + return -ENOMEM; + + WARN_ON(timesync_svc->timesync_hd); + timesync_hd->hd = hd; + timesync_svc->timesync_hd = timesync_hd; + + return 0; +} + +static void gb_timesync_hd_remove(struct gb_timesync_svc *timesync_svc, + struct gb_host_device *hd) +{ + if (timesync_svc->timesync_hd->hd == hd) { + kfree(timesync_svc->timesync_hd); + timesync_svc->timesync_hd = NULL; + return; + } + WARN_ON(1); +} + +int gb_timesync_svc_add(struct gb_svc *svc) +{ + struct gb_timesync_svc *timesync_svc; + int ret; + + timesync_svc = kzalloc(sizeof(*timesync_svc), GFP_KERNEL); + if (!timesync_svc) + return -ENOMEM; + + timesync_svc->work_queue = + create_singlethread_workqueue("gb-timesync-work_queue"); + + if (!timesync_svc->work_queue) { + kfree(timesync_svc); + return -ENOMEM; + } + + mutex_lock(&gb_timesync_svc_list_mutex); + INIT_LIST_HEAD(×ync_svc->interface_list); + INIT_DELAYED_WORK(×ync_svc->delayed_work, gb_timesync_worker); + mutex_init(×ync_svc->mutex); + spin_lock_init(×ync_svc->spinlock); + init_waitqueue_head(×ync_svc->wait_queue); + + timesync_svc->svc = svc; + timesync_svc->frame_time_offset = 0; + timesync_svc->capture_ping = false; + gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); + timesync_svc->frame_time_dentry = + debugfs_create_file("frame-time", S_IRUGO, svc->debugfs_dentry, + timesync_svc, &gb_timesync_debugfs_ops); + list_add(×ync_svc->list, &gb_timesync_svc_list); + ret = gb_timesync_hd_add(timesync_svc, svc->hd); + if (ret) { + list_del(×ync_svc->list); + debugfs_remove(timesync_svc->frame_time_dentry); + destroy_workqueue(timesync_svc->work_queue); + kfree(timesync_svc); + } + mutex_unlock(&gb_timesync_svc_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(gb_timesync_svc_add); + +void gb_timesync_svc_remove(struct gb_svc *svc) +{ + struct gb_timesync_svc *timesync_svc; + struct gb_timesync_interface *timesync_interface; + struct gb_timesync_interface *next; + + mutex_lock(&gb_timesync_svc_list_mutex); + timesync_svc = gb_timesync_find_timesync_svc(svc->hd); + if (!timesync_svc) + goto done; + + mutex_lock(×ync_svc->mutex); + + gb_timesync_teardown(timesync_svc); + + gb_timesync_hd_remove(timesync_svc, svc->hd); + list_for_each_entry_safe(timesync_interface, next, + ×ync_svc->interface_list, list) { + list_del(×ync_interface->list); + kfree(timesync_interface); + } + gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INVALID); + debugfs_remove(timesync_svc->frame_time_dentry); + cancel_delayed_work_sync(×ync_svc->delayed_work); + destroy_workqueue(timesync_svc->work_queue); + list_del(×ync_svc->list); + + mutex_unlock(×ync_svc->mutex); + + kfree(timesync_svc); +done: + mutex_unlock(&gb_timesync_svc_list_mutex); +} +EXPORT_SYMBOL_GPL(gb_timesync_svc_remove); + +/* + * Add a Greybus Interface to the set of TimeSync Interfaces. + */ +int gb_timesync_interface_add(struct gb_interface *interface) +{ + struct gb_timesync_svc *timesync_svc; + struct gb_timesync_interface *timesync_interface; + int ret = 0; + + if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) + return 0; + + mutex_lock(&gb_timesync_svc_list_mutex); + timesync_svc = gb_timesync_find_timesync_svc(interface->hd); + if (!timesync_svc) { + ret = -ENODEV; + goto done; + } + + timesync_interface = kzalloc(sizeof(*timesync_interface), GFP_KERNEL); + if (!timesync_interface) { + ret = -ENOMEM; + goto done; + } + + mutex_lock(×ync_svc->mutex); + timesync_interface->interface = interface; + list_add(×ync_interface->list, ×ync_svc->interface_list); + timesync_svc->strobe_mask |= 1 << interface->interface_id; + mutex_unlock(×ync_svc->mutex); + +done: + mutex_unlock(&gb_timesync_svc_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(gb_timesync_interface_add); + +/* + * Remove a Greybus Interface from the set of TimeSync Interfaces. + */ +void gb_timesync_interface_remove(struct gb_interface *interface) +{ + struct gb_timesync_svc *timesync_svc; + struct gb_timesync_interface *timesync_interface; + + if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) + return; + + mutex_lock(&gb_timesync_svc_list_mutex); + timesync_svc = gb_timesync_find_timesync_svc(interface->hd); + if (!timesync_svc) + goto done; + + timesync_interface = gb_timesync_find_timesync_interface(timesync_svc, + interface); + if (!timesync_interface) + goto done; + + mutex_lock(×ync_svc->mutex); + timesync_svc->strobe_mask &= ~(1 << interface->interface_id); + list_del(×ync_interface->list); + kfree(timesync_interface); + mutex_unlock(×ync_svc->mutex); +done: + mutex_unlock(&gb_timesync_svc_list_mutex); +} +EXPORT_SYMBOL_GPL(gb_timesync_interface_remove); + +/* + * Give the authoritative FrameTime to the calling function. Returns zero if we + * are not in GB_TIMESYNC_STATE_ACTIVE. + */ +static u64 gb_timesync_get_frame_time(struct gb_timesync_svc *timesync_svc) +{ + unsigned long flags; + u64 ret; + + spin_lock_irqsave(×ync_svc->spinlock, flags); + if (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE) + ret = __gb_timesync_get_frame_time(timesync_svc); + else + ret = 0; + spin_unlock_irqrestore(×ync_svc->spinlock, flags); + return ret; +} + +u64 gb_timesync_get_frame_time_by_interface(struct gb_interface *interface) +{ + struct gb_timesync_svc *timesync_svc; + u64 ret = 0; + + mutex_lock(&gb_timesync_svc_list_mutex); + timesync_svc = gb_timesync_find_timesync_svc(interface->hd); + if (!timesync_svc) + goto done; + + ret = gb_timesync_get_frame_time(timesync_svc); +done: + mutex_unlock(&gb_timesync_svc_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(gb_timesync_get_frame_time_by_interface); + +u64 gb_timesync_get_frame_time_by_svc(struct gb_svc *svc) +{ + struct gb_timesync_svc *timesync_svc; + u64 ret = 0; + + mutex_lock(&gb_timesync_svc_list_mutex); + timesync_svc = gb_timesync_find_timesync_svc(svc->hd); + if (!timesync_svc) + goto done; + + ret = gb_timesync_get_frame_time(timesync_svc); +done: + mutex_unlock(&gb_timesync_svc_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(gb_timesync_get_frame_time_by_svc); + +void gb_timesync_irq(struct gb_timesync_svc *timesync_svc) +{ + unsigned long flags; + u64 strobe_time; + + strobe_time = __gb_timesync_get_frame_time(timesync_svc); + + spin_lock_irqsave(×ync_svc->spinlock, flags); + + if (timesync_svc->state == GB_TIMESYNC_STATE_PING) { + if (timesync_svc->capture_ping) + timesync_svc->ap_ping_frame_time = strobe_time; + goto done; + } else if (timesync_svc->state != GB_TIMESYNC_STATE_WAIT_SVC) { + goto done; + } + + timesync_svc->strobe_time[timesync_svc->strobe] = strobe_time; + + if (++timesync_svc->strobe == GB_TIMESYNC_MAX_STROBES) { + gb_timesync_set_state(timesync_svc, + GB_TIMESYNC_STATE_AUTHORITATIVE); + } +done: + spin_unlock_irqrestore(×ync_svc->spinlock, flags); +} +EXPORT_SYMBOL(gb_timesync_irq); + +int __init gb_timesync_init(void) +{ + int ret = 0; + + ret = gb_timesync_platform_init(); + if (ret) { + pr_err("timesync platform init fail!\n"); + return ret; + } + + gb_timesync_clock_rate = gb_timesync_platform_get_clock_rate(); + gb_timesync_ns_per_clock = NSEC_PER_SEC / gb_timesync_clock_rate; + + pr_info("Time-Sync timer frequency %lu Hz\n", gb_timesync_clock_rate); + return 0; +} + +void gb_timesync_exit(void) +{ + gb_timesync_platform_exit(); +} diff --git a/drivers/staging/greybus/timesync.h b/drivers/staging/greybus/timesync.h new file mode 100644 index 000000000000..d907b0feafde --- /dev/null +++ b/drivers/staging/greybus/timesync.h @@ -0,0 +1,41 @@ +/* + * TimeSync API driver. + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __TIMESYNC_H +#define __TIMESYNC_H + +struct gb_svc; +struct gb_interface; +struct gb_timesync_svc; + +/* Platform */ +u64 gb_timesync_platform_get_counter(void); +u32 gb_timesync_platform_get_clock_rate(void); +int gb_timesync_platform_lock_bus(struct gb_timesync_svc *pdata); +void gb_timesync_platform_unlock_bus(void); + +int gb_timesync_platform_init(void); +void gb_timesync_platform_exit(void); + +/* Core API */ +int gb_timesync_interface_add(struct gb_interface *interface); +void gb_timesync_interface_remove(struct gb_interface *interface); +int gb_timesync_svc_add(struct gb_svc *svc); +void gb_timesync_svc_remove(struct gb_svc *svc); + +u64 gb_timesync_get_frame_time_by_interface(struct gb_interface *interface); +u64 gb_timesync_get_frame_time_by_svc(struct gb_svc *svc); + +int gb_timesync_schedule_synchronous(struct gb_interface *intf); +void gb_timesync_schedule_asynchronous(struct gb_interface *intf); +void gb_timesync_irq(struct gb_timesync_svc *timesync_svc); +int gb_timesync_init(void); +void gb_timesync_exit(void); + +#endif /* __TIMESYNC_H */ diff --git a/drivers/staging/greybus/timesync_platform.c b/drivers/staging/greybus/timesync_platform.c new file mode 100644 index 000000000000..50e8883f932f --- /dev/null +++ b/drivers/staging/greybus/timesync_platform.c @@ -0,0 +1,77 @@ +/* + * TimeSync API driver. + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + * + * This code reads directly from an ARMv7 memory-mapped timer that lives in + * MMIO space. Since this counter lives inside of MMIO space its shared between + * cores and that means we don't have to worry about issues like TSC on x86 + * where each time-stamp-counter (TSC) is local to a particular core. + * + * Register-level access code is based on + * drivers/clocksource/arm_arch_timer.c + */ +#include <linux/cpufreq.h> +#include <linux/of_platform.h> + +#include "greybus.h" +#include "arche_platform.h" + +static u32 gb_timesync_clock_frequency; +int (*arche_platform_change_state_cb)(enum arche_platform_state state, + struct gb_timesync_svc *pdata); +EXPORT_SYMBOL_GPL(arche_platform_change_state_cb); + +u64 gb_timesync_platform_get_counter(void) +{ + return (u64)get_cycles(); +} + +u32 gb_timesync_platform_get_clock_rate(void) +{ + if (unlikely(!gb_timesync_clock_frequency)) + return cpufreq_get(0); + + return gb_timesync_clock_frequency; +} + +int gb_timesync_platform_lock_bus(struct gb_timesync_svc *pdata) +{ + return arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_TIME_SYNC, + pdata); +} + +void gb_timesync_platform_unlock_bus(void) +{ + arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_ACTIVE, NULL); +} + +static const struct of_device_id arch_timer_of_match[] = { + { .compatible = "google,greybus-frame-time-counter", }, + {}, +}; + +int __init gb_timesync_platform_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, arch_timer_of_match); + if (!np) { + /* Tolerate not finding to allow BBB etc to continue */ + pr_warn("Unable to find a compatible ARMv7 timer\n"); + return 0; + } + + if (of_property_read_u32(np, "clock-frequency", + &gb_timesync_clock_frequency)) { + pr_err("Unable to find timer clock-frequency\n"); + return -ENODEV; + } + + return 0; +} + +void gb_timesync_platform_exit(void) {} -- cgit v1.2.3-59-g8ed1b From 4a4484274f7431c68e104a66b497639e1ac9022c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Sun, 5 Jun 2016 14:03:27 +0100 Subject: greybus: timesync: Bind TimeSync into Greybus TimeSync needs to bind into Greybus in a few places. - core.c To initialize its internal state and tear-down its internal state. To schedule a timesync to a newly added Bundle after probe() completes. - svc.c To get access to the SVC and enable/disable timesync as well as extracting the authoritative time from the SVC to subsequently disseminate to other entities in the system. - interface.c To get access to an Interface in order to inform APBx of timesync enable/disable and authoritative operations. This patch adds those bindings into Greybus core. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Acked-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/core.c | 10 ++++++++++ drivers/staging/greybus/interface.c | 7 +++++++ drivers/staging/greybus/svc.c | 18 +++++++++++++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 7350c5eba7e9..c8129068001d 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -178,6 +178,8 @@ static int greybus_probe(struct device *dev) return retval; } + gb_timesync_schedule_asynchronous(bundle->intf); + return 0; } @@ -267,8 +269,15 @@ static int __init gb_init(void) goto error_bootrom; } + retval = gb_timesync_init(); + if (retval) { + pr_err("gb_timesync_init failed\n"); + goto error_timesync; + } return 0; /* Success */ +error_timesync: + gb_bootrom_exit(); error_bootrom: gb_operation_exit(); error_operation: @@ -284,6 +293,7 @@ module_init(gb_init); static void __exit gb_exit(void) { + gb_timesync_exit(); gb_bootrom_exit(); gb_operation_exit(); gb_hd_exit(); diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 3eb8754961f1..9b90209e8fe0 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -805,6 +805,12 @@ int gb_interface_enable(struct gb_interface *intf) if (ret) goto err_destroy_bundles; + ret = gb_timesync_interface_add(intf); + if (ret) { + dev_err(&intf->dev, "failed to add to timesync: %d\n", ret); + goto err_destroy_bundles; + } + list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) { ret = gb_bundle_add(bundle); if (ret) { @@ -857,6 +863,7 @@ void gb_interface_disable(struct gb_interface *intf) list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); + gb_timesync_interface_remove(intf); gb_control_del(intf->control); gb_control_disable(intf->control); gb_control_put(intf->control); diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index b41d2622d94d..d7458c53b889 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -851,14 +851,25 @@ static int gb_svc_hello(struct gb_operation *op) ret = gb_svc_watchdog_create(svc); if (ret) { dev_err(&svc->dev, "failed to create watchdog: %d\n", ret); - input_unregister_device(svc->input); - device_del(&svc->dev); - return ret; + goto err_unregister_device; } gb_svc_debugfs_init(svc); + ret = gb_timesync_svc_add(svc); + if (ret) { + dev_err(&svc->dev, "failed to add SVC to timesync: %d\n", ret); + gb_svc_debugfs_exit(svc); + goto err_unregister_device; + } + return gb_svc_queue_deferred_request(op); + +err_unregister_device: + gb_svc_watchdog_destroy(svc); + input_unregister_device(svc->input); + device_del(&svc->dev); + return ret; } static struct gb_interface *gb_svc_interface_lookup(struct gb_svc *svc, @@ -1404,6 +1415,7 @@ void gb_svc_del(struct gb_svc *svc) * from the request handler. */ if (device_is_registered(&svc->dev)) { + gb_timesync_svc_remove(svc); gb_svc_debugfs_exit(svc); gb_svc_watchdog_destroy(svc); input_unregister_device(svc->input); -- cgit v1.2.3-59-g8ed1b From 6da7c88972a0153db427c14c6c5b2c4e2ccaefb7 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Sun, 5 Jun 2016 14:03:28 +0100 Subject: greybus: tracepoints: Add standard Linux tracepoint for TimeSync event This patch adds a tracepoint to the TimeSync ISR, the purpose of which is to indicate a TimeSync event has happened. This tracepoint can be enabled by issuing the following command: echo 1 > /sys/kernel/debug/tracing/events/greybus/gb_timesync_irq/enable Synchronization looks like this: TIMESTAMP FUNCTION | | 147.865788: gb_timesync_irq: strobe 1/4 frame-time 2910076529 147.866781: gb_timesync_irq: strobe 2/4 frame-time 2910095689 147.867777: gb_timesync_irq: strobe 3/4 frame-time 2910114820 147.868791: gb_timesync_irq: strobe 4/4 frame-time 2910134038 A ping can be triggered like this: cat /sys/kernel/debug/greybus/frame-time And that ping looks like this: TIMESTAMP FUNCTION | | 147.934678: gb_timesync_irq: ping 4/4 frame-time 2911380356 169.280551: gb_timesync_irq: ping 4/4 frame-time 3321221069 Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Acked-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 28 ++++++++++++++++++++++++++++ drivers/staging/greybus/timesync.c | 17 ++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 342f5adb6708..7249b8aba583 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -488,6 +488,34 @@ DEFINE_HD_EVENT(gb_hd_in); #undef DEFINE_HD_EVENT +/* + * Occurs on a TimeSync synchronization event or a TimeSync ping event. + */ +TRACE_EVENT(gb_timesync_irq, + + TP_PROTO(u8 ping, u8 strobe, u8 count, u64 frame_time), + + TP_ARGS(ping, strobe, count, frame_time), + + TP_STRUCT__entry( + __field(u8, ping) + __field(u8, strobe) + __field(u8, count) + __field(u64, frame_time) + ), + + TP_fast_assign( + __entry->ping = ping; + __entry->strobe = strobe; + __entry->count = count; + __entry->frame_time = frame_time; + ), + + TP_printk("%s %d/%d frame-time %llu\n", + __entry->ping ? "ping" : "strobe", __entry->strobe, + __entry->count, __entry->frame_time) +); + #endif /* _TRACE_GREYBUS_H */ /* This part must be outside protection */ diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index 260b670596bb..4bf62adf4f7a 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -9,6 +9,7 @@ #include <linux/debugfs.h> #include "greybus.h" #include "timesync.h" +#include "greybus_trace.h" /* * Minimum inter-strobe value of one millisecond is chosen because it @@ -974,17 +975,19 @@ void gb_timesync_irq(struct gb_timesync_svc *timesync_svc) { unsigned long flags; u64 strobe_time; + bool strobe_is_ping = true; strobe_time = __gb_timesync_get_frame_time(timesync_svc); spin_lock_irqsave(×ync_svc->spinlock, flags); if (timesync_svc->state == GB_TIMESYNC_STATE_PING) { - if (timesync_svc->capture_ping) - timesync_svc->ap_ping_frame_time = strobe_time; - goto done; + if (!timesync_svc->capture_ping) + goto done_nolog; + timesync_svc->ap_ping_frame_time = strobe_time; + goto done_log; } else if (timesync_svc->state != GB_TIMESYNC_STATE_WAIT_SVC) { - goto done; + goto done_nolog; } timesync_svc->strobe_time[timesync_svc->strobe] = strobe_time; @@ -993,7 +996,11 @@ void gb_timesync_irq(struct gb_timesync_svc *timesync_svc) gb_timesync_set_state(timesync_svc, GB_TIMESYNC_STATE_AUTHORITATIVE); } -done: + strobe_is_ping = false; +done_log: + trace_gb_timesync_irq(strobe_is_ping, timesync_svc->strobe, + GB_TIMESYNC_MAX_STROBES, strobe_time); +done_nolog: spin_unlock_irqrestore(×ync_svc->spinlock, flags); } EXPORT_SYMBOL(gb_timesync_irq); -- cgit v1.2.3-59-g8ed1b From 00fdbae1a9d2cecfcbab98fe69e4483f80fb80c4 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Sun, 5 Jun 2016 14:03:29 +0100 Subject: greybus: timesync: Add gb_timesync_frame_time_to_timespec() This patch adds gb_timesync_to_timespec_by_svc() and gb_timesync_to_timespec_by_interface() respectively. These routines will convert from a given FrameTime to a ktime/timespec within an envelope of about 17 seconds. The purpose of this routine is to enable reporting of a FrameTime from a Module such as a Camera Module and to allow the AP to then convert this timestamp into a Linux-native timestamp such as ktime. This is useful and required in the v4l layer. At 19.2MHz the accuracy of this conversion is about .3 femtoseconds per count, which means at a 1 second offset from the reference the cumulative error is about 1.59 nanoseconds. 1.59 nanoseconds is still less than 1 clock's worth of error @ 19.2MHz where each clock is 52.0833~ nanoseconds. We're aiming for a maximum error rate of 30 nanoseconds which means at the clock rate we are running at, the conversion from a FrameTime to a Linux ktime/timespec can be plus-or-minus about 17 seconds from the reference FrameTime/ktime pair before the routine will refuse to convert. A realistic use-case for this routine is envisaged to be - Greybus message received - Some processing takes place - taking milliseconds - Call into this routine is made - Actual time between event in Module and conversion in AP < 1 second - Error rate in conversion at 1.59 nanoseconds is less than 1 clock @ 19.2MHz This routine is not designed to allow for conversions for events with large gaps between the event time and the current reference time for conversion. Since FrameTime can be a very large integer we cannot convert an arbitrarily large FrameTime to ktime, the feeling and objective here is to make an over-provisioned envelope that in practical terms can never be exceeded by expected use-cases. To convert longer gaps more work would have to be done but ultimately some limit needs to be imposed and right now 0.3 femotseconds per clock on MSM8994 is both accurate and generous. Adds: - timesync.c::gb_timesync_frame_time_to_timespec_by_svc( struct gb_svc *, u64 frame_time, struct timespec *ts) - gb_svc is a pointer to a standard greybus SVC data structure - frame_time is a system FrameTime. - ts is an output parameter which represents the converted FrameTime as a CLOCK_MONOTONIC timespec value. - Returns 0 on success or a negative number indicating the type of error on failure. - timesync.c::gb_timesync_frame_time_to_timespec_by_interface( struct gb_interface *, u64 frame_time, struct timespec *ts) - gb_svc is a pointer to a standard greybus Interface data structure - frame_time is a system FrameTime. - ts is an output parameter which represents the converted FrameTime as a CLOCK_MONOTONIC timespec value. - Returns 0 on success or a negative number indicating the type of error on failure. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Acked-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 268 +++++++++++++++++++++++++++++++++---- drivers/staging/greybus/timesync.h | 4 + 2 files changed, 247 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index 4bf62adf4f7a..ebc61ef7e672 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -7,6 +7,7 @@ * Released under the GPLv2 only. */ #include <linux/debugfs.h> +#include <linux/hrtimer.h> #include "greybus.h" #include "timesync.h" #include "greybus_trace.h" @@ -30,9 +31,15 @@ #define GB_TIMESYNC_DELAYED_WORK_LONG msecs_to_jiffies(1000) #define GB_TIMESYNC_DELAYED_WORK_SHORT msecs_to_jiffies(1) #define GB_TIMESYNC_MAX_WAIT_SVC msecs_to_jiffies(5000) +#define GB_TIMESYNC_KTIME_UPDATE msecs_to_jiffies(1000) +#define GB_TIMESYNC_MAX_KTIME_CONVERSION 15 -/* Reported nanoseconds per clock */ +/* Reported nanoseconds/femtoseconds per clock */ static u64 gb_timesync_ns_per_clock; +static u64 gb_timesync_fs_per_clock; + +/* Maximum difference we will accept converting FrameTime to ktime */ +static u32 gb_timesync_max_ktime_diff; /* Reported clock rate */ static unsigned long gb_timesync_clock_rate; @@ -46,6 +53,12 @@ static LIST_HEAD(gb_timesync_svc_list); /* Synchronize parallel contexts accessing a valid timesync_svc pointer */ static DEFINE_MUTEX(gb_timesync_svc_list_mutex); +/* Structure to convert from FrameTime to timespec/ktime */ +struct gb_timesync_frame_time_data { + u64 frame_time; + struct timespec ts; +}; + struct gb_timesync_svc { struct list_head list; struct list_head interface_list; @@ -59,10 +72,12 @@ struct gb_timesync_svc { struct workqueue_struct *work_queue; wait_queue_head_t wait_queue; struct delayed_work delayed_work; + struct timer_list ktime_timer; /* The current local FrameTime */ u64 frame_time_offset; - u64 strobe_time[GB_TIMESYNC_MAX_STROBES]; + struct gb_timesync_frame_time_data strobe_data[GB_TIMESYNC_MAX_STROBES]; + struct gb_timesync_frame_time_data ktime_data; /* The SVC FrameTime and relative AP FrameTime @ last TIMESYNC_PING */ u64 svc_ping_frame_time; @@ -101,6 +116,8 @@ enum gb_timesync_state { GB_TIMESYNC_STATE_ACTIVE = 6, }; +static void gb_timesync_ktime_timer_fn(unsigned long data); + static u64 gb_timesync_adjust_count(struct gb_timesync_svc *timesync_svc, u64 counts) { @@ -220,6 +237,17 @@ static void gb_timesync_adjust_to_svc(struct gb_timesync_svc *svc, } } +/* + * Associate a FrameTime with a ktime timestamp represented as struct timespec + * Requires the calling context to hold timesync_svc->mutex + */ +static void gb_timesync_store_ktime(struct gb_timesync_svc *timesync_svc, + struct timespec ts, u64 frame_time) +{ + timesync_svc->ktime_data.ts = ts; + timesync_svc->ktime_data.frame_time = frame_time; +} + /* * Find the two pulses that best-match our expected inter-strobe gap and * then calculate the difference between the SVC time at the second pulse @@ -229,24 +257,32 @@ static void gb_timesync_collate_frame_time(struct gb_timesync_svc *timesync_svc, u64 *frame_time) { int i = 0; - u64 delta; + u64 delta, ap_frame_time; u64 strobe_delay_ns = GB_TIMESYNC_STROBE_DELAY_US * NSEC_PER_USEC; u64 least = 0; for (i = 1; i < GB_TIMESYNC_MAX_STROBES; i++) { - delta = timesync_svc->strobe_time[i] - - timesync_svc->strobe_time[i - 1]; + delta = timesync_svc->strobe_data[i].frame_time - + timesync_svc->strobe_data[i - 1].frame_time; delta *= gb_timesync_ns_per_clock; delta = gb_timesync_diff(delta, strobe_delay_ns); if (!least || delta < least) { least = delta; gb_timesync_adjust_to_svc(timesync_svc, frame_time[i], - timesync_svc->strobe_time[i]); + timesync_svc->strobe_data[i].frame_time); + + ap_frame_time = timesync_svc->strobe_data[i].frame_time; + ap_frame_time = gb_timesync_adjust_count(timesync_svc, + ap_frame_time); + gb_timesync_store_ktime(timesync_svc, + timesync_svc->strobe_data[i].ts, + ap_frame_time); + pr_debug("adjust %s local %llu svc %llu delta %llu\n", timesync_svc->offset_down ? "down" : "up", - timesync_svc->strobe_time[i], frame_time[i], - delta); + timesync_svc->strobe_data[i].frame_time, + frame_time[i], delta); } } } @@ -415,6 +451,119 @@ static void gb_timesync_authoritative(struct gb_timesync_svc *timesync_svc) gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_PING); } +static int __gb_timesync_get_status(struct gb_timesync_svc *timesync_svc) +{ + int ret = -EINVAL; + + switch (timesync_svc->state) { + case GB_TIMESYNC_STATE_INVALID: + case GB_TIMESYNC_STATE_INACTIVE: + ret = -ENODEV; + break; + case GB_TIMESYNC_STATE_INIT: + case GB_TIMESYNC_STATE_WAIT_SVC: + case GB_TIMESYNC_STATE_AUTHORITATIVE: + case GB_TIMESYNC_STATE_PING: + ret = -EAGAIN; + break; + case GB_TIMESYNC_STATE_ACTIVE: + ret = 0; + break; + } + return ret; +} + +/* + * This routine takes a FrameTime and derives the difference with-respect + * to a reference FrameTime/ktime pair. It then returns the calculated + * ktime based on the difference between the supplied FrameTime and + * the reference FrameTime. + * + * The time difference is calculated to six decimal places. Taking 19.2MHz + * as an example this means we have 52.083333~ nanoseconds per clock or + * 52083333~ femtoseconds per clock. + * + * Naively taking the count difference and converting to + * seconds/nanoseconds would quickly see the 0.0833 component produce + * noticeable errors. For example a time difference of one second would + * loose 19200000 * 0.08333x nanoseconds or 1.59 seconds. + * + * In contrast calculating in femtoseconds the same example of 19200000 * + * 0.000000083333x nanoseconds per count of error is just 1.59 nanoseconds! + * + * Continuing the example of 19.2 MHz we cap the maximum error difference + * at a worst-case 0.3 microseconds over a potential calculation window of + * abount 15 seconds, meaning you can convert a FrameTime that is <= 15 + * seconds older/younger than the reference time with a maximum error of + * 0.2385 useconds. Note 19.2MHz is an example frequency not a requirement. + */ +static int gb_timesync_to_timespec(struct gb_timesync_svc *timesync_svc, + u64 frame_time, struct timespec *ts) +{ + unsigned long flags; + u64 delta_fs, counts; + u32 sec, nsec; + bool add; + int ret = 0; + + mutex_lock(×ync_svc->mutex); + spin_lock_irqsave(×ync_svc->spinlock, flags); + + ret = __gb_timesync_get_status(timesync_svc); + if (ret) + goto done; + + /* Support calculating ktime upwards or downwards from the reference */ + if (frame_time < timesync_svc->ktime_data.frame_time) { + add = false; + counts = timesync_svc->ktime_data.frame_time - frame_time; + } else { + add = true; + counts = frame_time - timesync_svc->ktime_data.frame_time; + } + + /* Enforce the .23 of a usecond boundary @ 19.2MHz */ + if (counts > gb_timesync_max_ktime_diff) { + ret = -EINVAL; + goto done; + } + + /* Determine the time difference in femtoseconds */ + delta_fs = counts * gb_timesync_fs_per_clock; + sec = delta_fs / FSEC_PER_SEC; + nsec = (delta_fs % FSEC_PER_SEC) / 1000000UL; + + if (add) { + /* Add the calculated offset - overflow nanoseconds upwards */ + ts->tv_sec = timesync_svc->ktime_data.ts.tv_sec + sec; + ts->tv_nsec = timesync_svc->ktime_data.ts.tv_nsec + nsec; + if (ts->tv_nsec >= NSEC_PER_SEC) { + ts->tv_sec++; + ts->tv_nsec -= NSEC_PER_SEC; + } + } else { + /* Subtract the difference over/underflow as necessary */ + if (nsec > timesync_svc->ktime_data.ts.tv_nsec) { + sec++; + nsec = nsec + timesync_svc->ktime_data.ts.tv_nsec; + nsec %= NSEC_PER_SEC; + } else { + nsec = timesync_svc->ktime_data.ts.tv_nsec - nsec; + } + /* Cannot return a negative second value */ + if (sec > timesync_svc->ktime_data.ts.tv_sec) { + ret = -EINVAL; + goto done; + } + ts->tv_sec = timesync_svc->ktime_data.ts.tv_sec - sec; + ts->tv_nsec = nsec; + } +done: + spin_unlock_irqrestore(×ync_svc->spinlock, flags); + mutex_unlock(×ync_svc->mutex); + return ret; +} + static size_t gb_timesync_log_frame_time(struct gb_timesync_svc *timesync_svc, char *buf, size_t buflen) { @@ -616,21 +765,7 @@ static int __gb_timesync_schedule_synchronous( mutex_lock(×ync_svc->mutex); spin_lock_irqsave(×ync_svc->spinlock, flags); - switch (timesync_svc->state) { - case GB_TIMESYNC_STATE_INVALID: - case GB_TIMESYNC_STATE_INACTIVE: - ret = -ENODEV; - break; - case GB_TIMESYNC_STATE_INIT: - case GB_TIMESYNC_STATE_WAIT_SVC: - case GB_TIMESYNC_STATE_AUTHORITATIVE: - case GB_TIMESYNC_STATE_PING: - ret = -EAGAIN; - break; - case GB_TIMESYNC_STATE_ACTIVE: - ret = 0; - break; - } + ret = __gb_timesync_get_status(timesync_svc); spin_unlock_irqrestore(×ync_svc->spinlock, flags); mutex_unlock(×ync_svc->mutex); @@ -810,7 +945,15 @@ int gb_timesync_svc_add(struct gb_svc *svc) debugfs_remove(timesync_svc->frame_time_dentry); destroy_workqueue(timesync_svc->work_queue); kfree(timesync_svc); + goto done; } + + init_timer(×ync_svc->ktime_timer); + timesync_svc->ktime_timer.function = gb_timesync_ktime_timer_fn; + timesync_svc->ktime_timer.expires = jiffies + GB_TIMESYNC_KTIME_UPDATE; + timesync_svc->ktime_timer.data = (unsigned long)timesync_svc; + add_timer(×ync_svc->ktime_timer); +done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; } @@ -830,6 +973,7 @@ void gb_timesync_svc_remove(struct gb_svc *svc) mutex_lock(×ync_svc->mutex); gb_timesync_teardown(timesync_svc); + del_timer_sync(×ync_svc->ktime_timer); gb_timesync_hd_remove(timesync_svc, svc->hd); list_for_each_entry_safe(timesync_interface, next, @@ -971,12 +1115,77 @@ done: } EXPORT_SYMBOL_GPL(gb_timesync_get_frame_time_by_svc); +/* Incrementally updates the conversion base from FrameTime to ktime */ +static void gb_timesync_ktime_timer_fn(unsigned long data) +{ + struct gb_timesync_svc *timesync_svc = + (struct gb_timesync_svc *)data; + unsigned long flags; + u64 frame_time; + struct timespec ts; + + spin_lock_irqsave(×ync_svc->spinlock, flags); + + if (timesync_svc->state != GB_TIMESYNC_STATE_ACTIVE) + goto done; + + ktime_get_ts(&ts); + frame_time = __gb_timesync_get_frame_time(timesync_svc); + gb_timesync_store_ktime(timesync_svc, ts, frame_time); + +done: + spin_unlock_irqrestore(×ync_svc->spinlock, flags); + mod_timer(×ync_svc->ktime_timer, + jiffies + GB_TIMESYNC_KTIME_UPDATE); +} + +int gb_timesync_to_timespec_by_svc(struct gb_svc *svc, u64 frame_time, + struct timespec *ts) +{ + struct gb_timesync_svc *timesync_svc; + int ret = 0; + + mutex_lock(&gb_timesync_svc_list_mutex); + timesync_svc = gb_timesync_find_timesync_svc(svc->hd); + if (!timesync_svc) { + ret = -ENODEV; + goto done; + } + ret = gb_timesync_to_timespec(timesync_svc, frame_time, ts); +done: + mutex_unlock(&gb_timesync_svc_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(gb_timesync_to_timespec_by_svc); + +int gb_timesync_to_timespec_by_interface(struct gb_interface *interface, + u64 frame_time, struct timespec *ts) +{ + struct gb_timesync_svc *timesync_svc; + int ret = 0; + + mutex_lock(&gb_timesync_svc_list_mutex); + timesync_svc = gb_timesync_find_timesync_svc(interface->hd); + if (!timesync_svc) { + ret = -ENODEV; + goto done; + } + + ret = gb_timesync_to_timespec(timesync_svc, frame_time, ts); +done: + mutex_unlock(&gb_timesync_svc_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(gb_timesync_to_timespec_by_interface); + void gb_timesync_irq(struct gb_timesync_svc *timesync_svc) { unsigned long flags; u64 strobe_time; bool strobe_is_ping = true; + struct timespec ts; + ktime_get_ts(&ts); strobe_time = __gb_timesync_get_frame_time(timesync_svc); spin_lock_irqsave(×ync_svc->spinlock, flags); @@ -990,7 +1199,8 @@ void gb_timesync_irq(struct gb_timesync_svc *timesync_svc) goto done_nolog; } - timesync_svc->strobe_time[timesync_svc->strobe] = strobe_time; + timesync_svc->strobe_data[timesync_svc->strobe].frame_time = strobe_time; + timesync_svc->strobe_data[timesync_svc->strobe].ts = ts; if (++timesync_svc->strobe == GB_TIMESYNC_MAX_STROBES) { gb_timesync_set_state(timesync_svc, @@ -1016,9 +1226,17 @@ int __init gb_timesync_init(void) } gb_timesync_clock_rate = gb_timesync_platform_get_clock_rate(); + + /* Calculate nanoseconds and femtoseconds per clock */ + gb_timesync_fs_per_clock = FSEC_PER_SEC / gb_timesync_clock_rate; gb_timesync_ns_per_clock = NSEC_PER_SEC / gb_timesync_clock_rate; - pr_info("Time-Sync timer frequency %lu Hz\n", gb_timesync_clock_rate); + /* Calculate the maximum number of clocks we will convert to ktime */ + gb_timesync_max_ktime_diff = + GB_TIMESYNC_MAX_KTIME_CONVERSION * gb_timesync_clock_rate; + + pr_info("Time-Sync @ %lu Hz max ktime conversion +/- %d seconds\n", + gb_timesync_clock_rate, GB_TIMESYNC_MAX_KTIME_CONVERSION); return 0; } diff --git a/drivers/staging/greybus/timesync.h b/drivers/staging/greybus/timesync.h index d907b0feafde..72fc9a35a002 100644 --- a/drivers/staging/greybus/timesync.h +++ b/drivers/staging/greybus/timesync.h @@ -31,6 +31,10 @@ void gb_timesync_svc_remove(struct gb_svc *svc); u64 gb_timesync_get_frame_time_by_interface(struct gb_interface *interface); u64 gb_timesync_get_frame_time_by_svc(struct gb_svc *svc); +int gb_timesync_to_timespec_by_svc(struct gb_svc *svc, u64 frame_time, + struct timespec *ts); +int gb_timesync_to_timespec_by_interface(struct gb_interface *interface, + u64 frame_time, struct timespec *ts); int gb_timesync_schedule_synchronous(struct gb_interface *intf); void gb_timesync_schedule_asynchronous(struct gb_interface *intf); -- cgit v1.2.3-59-g8ed1b From 423042f4b2dc763f16c27b18a19611fa1773ac30 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Sun, 5 Jun 2016 14:03:30 +0100 Subject: greybus: timesync: Add debugfs entry to display frame-ping in ktime This patch makes a debugfs entry in /sys/kernel/debug/greybus/X-svc/frame-ktime that generates a TimeSync ping event to the system and then subsequently presents that data to user-space as a ktime/timespec clock-monotonic value rather than as a raw frame-time, to aid humans in debugging and understanding frame-time and to provide an example of the converting a frame-time to timespec/ktime to other developers. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Acked-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 114 +++++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index ebc61ef7e672..87cbe10a5ae7 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -69,6 +69,7 @@ struct gb_timesync_svc { struct mutex mutex; /* Per SVC mutex for regular synchronization */ struct dentry *frame_time_dentry; + struct dentry *frame_ktime_dentry; struct workqueue_struct *work_queue; wait_queue_head_t wait_queue; struct delayed_work delayed_work; @@ -506,6 +507,7 @@ static int gb_timesync_to_timespec(struct gb_timesync_svc *timesync_svc, bool add; int ret = 0; + memset(ts, 0x00, sizeof(*ts)); mutex_lock(×ync_svc->mutex); spin_lock_irqsave(×ync_svc->spinlock, flags); @@ -575,7 +577,6 @@ static size_t gb_timesync_log_frame_time(struct gb_timesync_svc *timesync_svc, size_t off; /* AP/SVC */ - memset(buf, 0x00, buflen); off = snprintf(buf, buflen, "timesync: ping-time ap=%llu %s=%llu ", timesync_svc->ap_ping_frame_time, dev_name(&svc->dev), timesync_svc->svc_ping_frame_time); @@ -604,6 +605,65 @@ static size_t gb_timesync_log_frame_time(struct gb_timesync_svc *timesync_svc, return off; } +static size_t gb_timesync_log_frame_ktime(struct gb_timesync_svc *timesync_svc, + char *buf, size_t buflen) +{ + struct gb_svc *svc = timesync_svc->svc; + struct gb_host_device *hd; + struct gb_timesync_interface *timesync_interface; + struct gb_interface *interface; + struct timespec ts; + unsigned int len; + size_t off; + + /* AP */ + gb_timesync_to_timespec(timesync_svc, timesync_svc->ap_ping_frame_time, + &ts); + off = snprintf(buf, buflen, "timesync: ping-time ap=%lu.%lu ", + ts.tv_sec, ts.tv_nsec); + len = buflen - off; + if (len >= buflen) + goto done; + + /* SVC */ + gb_timesync_to_timespec(timesync_svc, timesync_svc->svc_ping_frame_time, + &ts); + off += snprintf(&buf[off], len, "%s=%lu.%lu ", dev_name(&svc->dev), + ts.tv_sec, ts.tv_nsec); + len = buflen - off; + if (len >= buflen) + goto done; + + /* APB/GPB */ + hd = timesync_svc->timesync_hd->hd; + gb_timesync_to_timespec(timesync_svc, + timesync_svc->timesync_hd->ping_frame_time, + &ts); + off += snprintf(&buf[off], len, "%s=%lu.%lu ", + dev_name(&hd->dev), + ts.tv_sec, ts.tv_nsec); + len = buflen - off; + if (len >= buflen) + goto done; + + list_for_each_entry(timesync_interface, + ×ync_svc->interface_list, list) { + interface = timesync_interface->interface; + gb_timesync_to_timespec(timesync_svc, + timesync_interface->ping_frame_time, + &ts); + off += snprintf(&buf[off], len, "%s=%lu.%lu ", + dev_name(&interface->dev), + ts.tv_sec, ts.tv_nsec); + len = buflen - off; + if (len >= buflen) + goto done; + } + off += snprintf(&buf[off], len, "\n"); +done: + return off; +} + /* * Send an SVC initiated wake 'ping' to each TimeSync participant. * Get the FrameTime from each participant associated with the wake @@ -840,11 +900,11 @@ done: } EXPORT_SYMBOL_GPL(gb_timesync_schedule_asynchronous); -static ssize_t gb_timesync_ping_read(struct file *file, char __user *buf, - size_t len, loff_t *offset) +static ssize_t gb_timesync_ping_read(struct file *file, char __user *ubuf, + size_t len, loff_t *offset, bool ktime) { struct gb_timesync_svc *timesync_svc = file->f_inode->i_private; - char *pbuf; + char *buf; ssize_t ret = 0; mutex_lock(&gb_timesync_svc_list_mutex); @@ -861,23 +921,44 @@ static ssize_t gb_timesync_ping_read(struct file *file, char __user *buf, if (ret) goto done; - pbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!pbuf) { + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) { ret = -ENOMEM; goto done; } - ret = gb_timesync_log_frame_time(timesync_svc, pbuf, PAGE_SIZE); + if (ktime) + ret = gb_timesync_log_frame_ktime(timesync_svc, buf, PAGE_SIZE); + else + ret = gb_timesync_log_frame_time(timesync_svc, buf, PAGE_SIZE); if (ret > 0) - ret = simple_read_from_buffer(buf, len, offset, pbuf, ret); - kfree(pbuf); + ret = simple_read_from_buffer(ubuf, len, offset, buf, ret); + kfree(buf); done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; } -static const struct file_operations gb_timesync_debugfs_ops = { - .read = gb_timesync_ping_read, +static ssize_t gb_timesync_ping_read_frame_time(struct file *file, + char __user *buf, + size_t len, loff_t *offset) +{ + return gb_timesync_ping_read(file, buf, len, offset, false); +} + +static ssize_t gb_timesync_ping_read_frame_ktime(struct file *file, + char __user *buf, + size_t len, loff_t *offset) +{ + return gb_timesync_ping_read(file, buf, len, offset, true); +} + +static const struct file_operations gb_timesync_debugfs_frame_time_ops = { + .read = gb_timesync_ping_read_frame_time, +}; + +static const struct file_operations gb_timesync_debugfs_frame_ktime_ops = { + .read = gb_timesync_ping_read_frame_ktime, }; static int gb_timesync_hd_add(struct gb_timesync_svc *timesync_svc, @@ -935,13 +1016,21 @@ int gb_timesync_svc_add(struct gb_svc *svc) timesync_svc->frame_time_offset = 0; timesync_svc->capture_ping = false; gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); + timesync_svc->frame_time_dentry = debugfs_create_file("frame-time", S_IRUGO, svc->debugfs_dentry, - timesync_svc, &gb_timesync_debugfs_ops); + timesync_svc, + &gb_timesync_debugfs_frame_time_ops); + timesync_svc->frame_ktime_dentry = + debugfs_create_file("frame-ktime", S_IRUGO, svc->debugfs_dentry, + timesync_svc, + &gb_timesync_debugfs_frame_ktime_ops); + list_add(×ync_svc->list, &gb_timesync_svc_list); ret = gb_timesync_hd_add(timesync_svc, svc->hd); if (ret) { list_del(×ync_svc->list); + debugfs_remove(timesync_svc->frame_ktime_dentry); debugfs_remove(timesync_svc->frame_time_dentry); destroy_workqueue(timesync_svc->work_queue); kfree(timesync_svc); @@ -982,6 +1071,7 @@ void gb_timesync_svc_remove(struct gb_svc *svc) kfree(timesync_interface); } gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INVALID); + debugfs_remove(timesync_svc->frame_ktime_dentry); debugfs_remove(timesync_svc->frame_time_dentry); cancel_delayed_work_sync(×ync_svc->delayed_work); destroy_workqueue(timesync_svc->work_queue); -- cgit v1.2.3-59-g8ed1b From 109fbdfd6a23c122c564a6cd6671b9911e0a24b5 Mon Sep 17 00:00:00 2001 From: Jeffrey Carlyle <jcarlyle@google.com> Date: Tue, 7 Jun 2016 08:22:22 -0700 Subject: greybus: svc: implement connection_quiescing call Implement Greybus remote call to connection_quiescing operation. This operation disables flow contorl for the connection and resets associated attributes. Testing done: tested along with required NuttX firmware changes, booted EVT2, inserted module, removed module, inserted module. Verified module was functioning as expected. Signed-off-by: Jeffrey Carlyle <jcarlyle@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 12 ++++++++++++ drivers/staging/greybus/svc.c | 23 ++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 82075c703f33..8125fb7140d8 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -904,6 +904,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_ACQUIRE 0x18 #define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE 0x19 #define GB_SVC_TYPE_TIMESYNC_PING 0x1a +#define GB_SVC_TYPE_CONN_QUIESCING 0x1e #define GB_SVC_TYPE_MODULE_INSERTED 0x1f #define GB_SVC_TYPE_MODULE_REMOVED 0x20 #define GB_SVC_TYPE_INTF_VSYS_ENABLE 0x21 @@ -1215,6 +1216,17 @@ struct gb_svc_intf_mailbox_event_request { } __packed; /* intf_mailbox_event response has no payload */ +struct gb_svc_conn_quiescing_request { + __u8 intf1_id; + __le16 cport1_id; + __u8 intf2_id; + __le16 cport2_id; +} __packed; + +struct gb_svc_conn_quiescing_response { + __u8 status; +} __packed; + /* RAW */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index d7458c53b889..bfdd0ce37623 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -441,10 +441,31 @@ EXPORT_SYMBOL_GPL(gb_svc_connection_create); void gb_svc_connection_quiescing(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) { - /* FIXME: implement */ + struct gb_svc_conn_quiescing_request request; + struct gb_svc_conn_quiescing_response response; + int ret; dev_dbg(&svc->dev, "%s - (%u:%u %u:%u)\n", __func__, intf1_id, cport1_id, intf2_id, cport2_id); + + request.intf1_id = intf1_id; + request.cport1_id = cpu_to_le16(cport1_id); + request.intf2_id = intf2_id; + request.cport2_id = cpu_to_le16(cport2_id); + + ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_QUIESCING, + &request, sizeof(request), + &response, sizeof(response)); + if (ret < 0) + return; + if (response.status != GB_SVC_OP_SUCCESS) { + dev_err(&svc->dev, "quiescing connection failed (%u:%u %u:%u): %u\n", + intf1_id, cport1_id, intf2_id, cport2_id, + response.status); + return; + } + + return; } EXPORT_SYMBOL_GPL(gb_svc_connection_quiescing); -- cgit v1.2.3-59-g8ed1b From 4d27574cd354bc47758268c2e32a0a47b7aacfc0 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Wed, 8 Jun 2016 20:33:26 +0530 Subject: greybus: audio: Ratelimit err messages In case of audio mgmt connection failure, GB requests would fail giving an error message within the driver and reporting error. However there is no error handling in above HAL and it'll keep on triggering similar request via GB codec driver. This may overflood serial console. In one of the instance it locked CPU for >10sec and caused a watchdog bite. Thus ratelimit those error messages. Testing Done: compile tested Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 23dde708c76f..975d2e86b113 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -92,7 +92,7 @@ static int gbaudio_module_disable(struct gbaudio_codec_info *codec, ret = -EINVAL; } if (ret) { - dev_err(codec->dev, "deactivate for %s failed:%d\n", + dev_err_ratelimited(codec->dev, "deactivate for %s failed:%d\n", module->name, ret); goto func_exit; } @@ -118,7 +118,7 @@ static int gbaudio_module_disable(struct gbaudio_codec_info *codec, ret = -EINVAL; } if (ret) { - dev_err(codec->dev, "unregister_cport for %s failed:%d\n", + dev_err_ratelimited(codec->dev, "unregister_cport for %s failed:%d\n", module->name, ret); goto func_exit; } @@ -195,7 +195,7 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, ret = -EINVAL; } if (ret) { - dev_err(codec->dev, "reg_cport for %s\n", module->name); + dev_err_ratelimited(codec->dev, "reg_cport for %s\n", module->name); goto func_exit; } module_state = GBAUDIO_CODEC_STARTUP; @@ -209,7 +209,7 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, format, rate, channels, sig_bits); if (ret) { - dev_err(codec->dev, "set_pcm for %s\n", module->name); + dev_err_ratelimited(codec->dev, "set_pcm for %s\n", module->name); goto func_exit; } module_state = GBAUDIO_CODEC_HWPARAMS; @@ -226,7 +226,7 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, module->mgmt_connection, data_cport, 192); if (ret) { - dev_err(codec->dev, + dev_err_ratelimited(codec->dev, "set_rx_data_size for %s\n", module->name); goto func_exit; @@ -234,7 +234,7 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, ret = gb_audio_gb_activate_rx(module->mgmt_connection, data_cport); if (ret) { - dev_err(codec->dev, "activate_rx for %s\n", + dev_err_ratelimited(codec->dev, "activate_rx for %s\n", module->name); goto func_exit; } @@ -244,7 +244,7 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, module->mgmt_connection, data_cport, 192); if (ret) { - dev_err(codec->dev, + dev_err_ratelimited(codec->dev, "set_tx_data_size for %s\n", module->name); goto func_exit; @@ -252,7 +252,7 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, ret = gb_audio_gb_activate_tx(module->mgmt_connection, data_cport); if (ret) { - dev_err(codec->dev, "activate_tx for %s\n", + dev_err_ratelimited(codec->dev, "activate_tx for %s\n", module->name); goto func_exit; } @@ -579,7 +579,7 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, format, rate, channels, sig_bits); if (ret) { - dev_err(dai->dev, "%d: Error during set_pcm\n", ret); + dev_err_ratelimited(dai->dev, "%d: Error during set_pcm\n", ret); goto func_exit; } if (state < GBAUDIO_CODEC_HWPARAMS) { @@ -588,7 +588,7 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, AUDIO_APBRIDGEA_PCM_RATE_48000, 6144000); if (ret) { - dev_err(dai->dev, + dev_err_ratelimited(dai->dev, "%d: Error during set_config\n", ret); goto func_exit; } @@ -619,7 +619,7 @@ static int gbmodule_prepare_tx(struct gbaudio_module_info *module, ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection, data_cport, 192); if (ret) { - dev_err(dev, "%d:Error during set_tx_data_size, cport:%d\n", + dev_err_ratelimited(dev, "%d:Error during set_tx_data_size, cport:%d\n", ret, data_cport); return ret; } @@ -627,7 +627,7 @@ static int gbmodule_prepare_tx(struct gbaudio_module_info *module, ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0, 192); if (ret) { - dev_err(dev, + dev_err_ratelimited(dev, "%d:Error during apbridgea set_tx_data_size, cport\n", ret); return ret; @@ -636,7 +636,7 @@ static int gbmodule_prepare_tx(struct gbaudio_module_info *module, ret = gb_audio_gb_activate_tx(module->mgmt_connection, data_cport); if (ret) - dev_err(dev, "%s:Error during activate stream,%d\n", + dev_err_ratelimited(dev, "%s:Error during activate stream,%d\n", module->name, ret); return ret; @@ -654,7 +654,7 @@ static int gbmodule_prepare_rx(struct gbaudio_module_info *module, ret = gb_audio_gb_set_rx_data_size(module->mgmt_connection, data_cport, 192); if (ret) { - dev_err(dev, "%d:Error during set_rx_data_size, cport:%d\n", + dev_err_ratelimited(dev, "%d:Error during set_rx_data_size, cport:%d\n", ret, data_cport); return ret; } @@ -662,7 +662,7 @@ static int gbmodule_prepare_rx(struct gbaudio_module_info *module, ret = gb_audio_apbridgea_set_rx_data_size(data->connection, 0, 192); if (ret) { - dev_err(dev, + dev_err_ratelimited(dev, "%d:Error during apbridgea_set_rx_data_size\n", ret); return ret; @@ -671,7 +671,7 @@ static int gbmodule_prepare_rx(struct gbaudio_module_info *module, ret = gb_audio_gb_activate_rx(module->mgmt_connection, data_cport); if (ret) - dev_err(dev, "%s:Error during activate stream,%d\n", + dev_err_ratelimited(dev, "%s:Error during activate stream,%d\n", module->name, ret); return ret; @@ -820,7 +820,7 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, } else ret = -EINVAL; if (ret) - dev_err(dai->dev, "%s:Error during %s stream:%d\n", + dev_err_ratelimited(dai->dev, "%s:Error during %s stream:%d\n", module->name, start ? "Start" : "Stop", ret); func_exit: -- cgit v1.2.3-59-g8ed1b From 44d6449350b866cbea613aabe5aba4aa640791cd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Tue, 7 Jun 2016 21:33:54 -0700 Subject: greybus: kernel_ver.h: provide pwm_is_enabled() In the 4.7-rc1 kernel release, PWMF_ENABLED is removed and pwm_is_enabled() is the correct way to test if a pwm device is enabled, so provide a version of that function that will work on all older kernels and change the pwm.c driver to use it so that it will work on newer kernels as well. Tested: Tree now builds successfully against 3.14.y, 4.4.y, 4.5.y, 4.6.y, and 4.7-rc2 kernels Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 7 +++++++ drivers/staging/greybus/pwm.c | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index f6fb3bfcfe7a..9819c3e5a2ec 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -328,4 +328,11 @@ static inline void reinit_completion(struct completion *x) } #endif +#ifdef PWMF_ENABLED +static inline bool pwm_is_enabled(const struct pwm_device *pwm) +{ + return test_bit(PWMF_ENABLED, &pwm->flags); +} +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index b941cb5b5063..713123231251 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -132,7 +132,7 @@ static void gb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); - if (test_bit(PWMF_ENABLED, &pwm->flags)) + if (pwm_is_enabled(pwm)) dev_warn(chip->dev, "freeing PWM device without disabling\n"); gb_pwm_deactivate_operation(pwmc, pwm->hwpwm); -- cgit v1.2.3-59-g8ed1b From 8fb76c3cfd5c54cb0d8713436d480a8062217d5c Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Wed, 8 Jun 2016 09:39:00 +0200 Subject: greybus: svc: pwrmon: validate svc protocol op status when getting rail names AP should check for Greybus SVC Protocol Operation Status to determine if the operation was successfully completed by the SVC Testing Done: - Successfully getting the rail names in the pwrmon_dummy sandbox branch Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Bartosz Golaszewski <bgolaszewski@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 1 + drivers/staging/greybus/svc.c | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 8125fb7140d8..3c6415fd8647 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1145,6 +1145,7 @@ struct gb_svc_pwrmon_rail_count_get_response { #define GB_SVC_PWRMON_RAIL_NAME_BUFSIZE 32 struct gb_svc_pwrmon_rail_names_get_response { + __u8 status; __u8 name[0][GB_SVC_PWRMON_RAIL_NAME_BUFSIZE]; } __packed; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index bfdd0ce37623..e2ab94277b4e 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -135,6 +135,13 @@ static int gb_svc_pwrmon_rail_names_get(struct gb_svc *svc, return ret; } + if (response->status != GB_SVC_OP_SUCCESS) { + dev_err(&svc->dev, + "SVC error while getting rail names: %u\n", + response->status); + return -EREMOTEIO; + } + return 0; } @@ -778,7 +785,8 @@ static void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) if (!rail_count || rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT) goto err_pwrmon_debugfs; - bufsize = GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * rail_count; + bufsize = sizeof(*rail_names) + + GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * rail_count; rail_names = kzalloc(bufsize, GFP_KERNEL); if (!rail_names) -- cgit v1.2.3-59-g8ed1b From 365d79fc20232dd730723801b7417cca9766dd79 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Wed, 8 Jun 2016 12:44:22 -0700 Subject: greybus: don't key on an enumerated constant The symbol PWMF_ENABLED is defined via an enum, which is not defined at the time the preprocessor passes through "kernel_ver.h". So we can't use it in an #if statement expression. Use the Linux kernel version instead. Change-Id: Id427224b1dfecfd886fcdae89c4bcf711b616439 Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/kernel_ver.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 9819c3e5a2ec..fd5a6cc42eaa 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -328,7 +328,13 @@ static inline void reinit_completion(struct completion *x) } #endif -#ifdef PWMF_ENABLED +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) +#include <linux/pwm.h> +/* + * pwm_is_enabled() was first defined in 4.2-rc1 (first commit!). + * PWMF_ENABLED was first defined in 3.5-rc2, but our code is + * always newer than that. +*/ static inline bool pwm_is_enabled(const struct pwm_device *pwm) { return test_bit(PWMF_ENABLED, &pwm->flags); -- cgit v1.2.3-59-g8ed1b From c0b06a6d4e07902f7e1eef88d242901c0a1faf03 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Wed, 8 Jun 2016 16:54:01 -0500 Subject: greybus: tracing: drop "greybus" prefix I posted this once before but it got rejected for fear it would not be clear which messages were related to Greybus. Every trace event currently defined for Greybus is recorded in a function whose name begins with "gb_". Every trace event reported in /sys/kernel/debug/tracing/trace includes the name of the function in which the event was recorded. Get rid of the "greybus: " prefix in all of the Greybus trace events. It just takes up precious space and is not actually helpful. Anyone actually enabling individual trace events should know enough about what they're doing to recognize which ones are being enabled. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_trace.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/greybus_trace.h b/drivers/staging/greybus/greybus_trace.h index 7249b8aba583..6f8692da9ec8 100644 --- a/drivers/staging/greybus/greybus_trace.h +++ b/drivers/staging/greybus/greybus_trace.h @@ -41,7 +41,7 @@ DECLARE_EVENT_CLASS(gb_message, __entry->result = message->header->result; ), - TP_printk("greybus: size=%hu operation_id=0x%04x type=0x%02x result=0x%02x", + TP_printk("size=%hu operation_id=0x%04x type=0x%02x result=0x%02x", __entry->size, __entry->operation_id, __entry->type, __entry->result) ); @@ -318,7 +318,7 @@ DECLARE_EVENT_CLASS(gb_interface, __entry->mode_switch = intf->mode_switch; ), - TP_printk("greybus: intf_id=%hhu device_id=%hhu module_id=%hhu D=%d J=%d A=%d E=%d M=%d", + TP_printk("intf_id=%hhu device_id=%hhu module_id=%hhu D=%d J=%d A=%d E=%d M=%d", __entry->id, __entry->device_id, __entry->module_id, __entry->disconnected, __entry->ejected, __entry->active, __entry->enabled, __entry->mode_switch) @@ -392,7 +392,7 @@ DECLARE_EVENT_CLASS(gb_module, __entry->disconnected = module->disconnected; ), - TP_printk("greybus: hd_bus_id=%d module_id=%hhu num_interfaces=%zu disconnected=%d", + TP_printk("hd_bus_id=%d module_id=%hhu num_interfaces=%zu disconnected=%d", __entry->hd_bus_id, __entry->module_id, __entry->num_interfaces, __entry->disconnected) ); @@ -445,7 +445,7 @@ DECLARE_EVENT_CLASS(gb_host_device, __entry->buffer_size_max = hd->buffer_size_max; ), - TP_printk("greybus: bus_id=%d num_cports=%zu mtu=%zu", + TP_printk("bus_id=%d num_cports=%zu mtu=%zu", __entry->bus_id, __entry->num_cports, __entry->buffer_size_max) ); -- cgit v1.2.3-59-g8ed1b From 82278bfea04c9cda1e01ac4fe41938cc3f5f1252 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 9 Jun 2016 18:42:19 +0200 Subject: greybus: fix forced disable of offloaded connections Core disables all connections for bundles whose interface is already gone in order to avoid unnecessary operation timeouts during driver disconnect. This isn't needed for offloaded connections (as the AP can not send requests over such connections), and in fact must not be done since only the bundle driver currently knows how to disable I/O on such connections in a class-specific way (this may eventually be handled by core though). Also add comment about why connection are disabled early on forced disconnect. Testing Done: Tested on EVT2. Reported-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Tested-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index c8129068001d..5811299c96fa 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -189,7 +189,15 @@ static int greybus_remove(struct device *dev) struct gb_bundle *bundle = to_gb_bundle(dev); struct gb_connection *connection; + /* + * Disable (non-offloaded) connections early in case the interface is + * already gone to avoid unceccessary operation timeouts during + * driver disconnect. Otherwise, only disable incoming requests. + */ list_for_each_entry(connection, &bundle->connections, bundle_links) { + if (gb_connection_is_offloaded(connection)) + continue; + if (bundle->intf->disconnected) gb_connection_disable_forced(connection); else -- cgit v1.2.3-59-g8ed1b From cb14e97623d9289a30c3cfab02747581dda22f58 Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos <grosikopulos@mms-0359.ent.mm-sol.com> Date: Thu, 9 Jun 2016 11:53:15 +0300 Subject: greybus: camera: Add debug data format Add support for greybus debug data format. Greybus debug data format id is 0x42. Signed-off-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Acked-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index d7cef2ac5aee..162cb7d294cd 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -98,7 +98,11 @@ static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { { .mbus_code = V4L2_MBUS_FMT_ARA_METADATA_1X8, .gb_format = 0x41, - } + }, + { + .mbus_code = V4L2_MBUS_FMT_ARA_DEBUG_DATA_1X8, + .gb_format = 0x42, + }, }; #define ES2_APB_CDSI0_CPORT 16 -- cgit v1.2.3-59-g8ed1b From 12e6895d1e343512990eec5ae8747b5968a8f8b8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 9 Jun 2016 16:34:34 +0530 Subject: greybus: pwm: Fix compilation with v4.3 kernel pwm_is_enabled() wasn't enabled by v4.2-rc1, but it was based of v4.2-rc1. It was actually first included in v4.3. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index fd5a6cc42eaa..2a541fa5f5bc 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -328,10 +328,10 @@ static inline void reinit_completion(struct completion *x) } #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) #include <linux/pwm.h> /* - * pwm_is_enabled() was first defined in 4.2-rc1 (first commit!). + * pwm_is_enabled() was first defined in 4.3. * PWMF_ENABLED was first defined in 3.5-rc2, but our code is * always newer than that. */ -- cgit v1.2.3-59-g8ed1b From 9e50e987f55dc16364786525abdb4c7eac50e00b Mon Sep 17 00:00:00 2001 From: Marti Bolivar <mbolivar@leaflabs.com> Date: Thu, 9 Jun 2016 17:46:47 -0400 Subject: greybus: remove obsolete SVC result codes The "busy" SVC result codes are gone from the spec. Delete them. Testing Done: compile. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 3c6415fd8647..8b5563ede00e 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1036,7 +1036,7 @@ struct gb_svc_intf_vsys_request { struct gb_svc_intf_vsys_response { __u8 result_code; #define GB_SVC_INTF_VSYS_OK 0x00 -#define GB_SVC_INTF_VSYS_BUSY 0x01 + /* 0x01 is reserved */ #define GB_SVC_INTF_VSYS_FAIL 0x02 } __packed; @@ -1048,7 +1048,7 @@ struct gb_svc_intf_refclk_request { struct gb_svc_intf_refclk_response { __u8 result_code; #define GB_SVC_INTF_REFCLK_OK 0x00 -#define GB_SVC_INTF_REFCLK_BUSY 0x01 + /* 0x01 is reserved */ #define GB_SVC_INTF_REFCLK_FAIL 0x02 } __packed; @@ -1060,7 +1060,7 @@ struct gb_svc_intf_unipro_request { struct gb_svc_intf_unipro_response { __u8 result_code; #define GB_SVC_INTF_UNIPRO_OK 0x00 -#define GB_SVC_INTF_UNIPRO_BUSY 0x01 + /* 0x01 is reserved */ #define GB_SVC_INTF_UNIPRO_FAIL 0x02 #define GB_SVC_INTF_UNIPRO_NOT_OFF 0x03 } __packed; -- cgit v1.2.3-59-g8ed1b From aa62b5e49a596cad71a816fc36a4da6ad0c0cd0f Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 9 Jun 2016 16:34:35 +0530 Subject: greybus: bootrom: Compile as a separate module User space doesn't break anymore with new greybus modules and its time to make bootrom a separate module. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 3 ++- drivers/staging/greybus/bootrom.c | 11 ++--------- drivers/staging/greybus/bootrom.h | 16 ---------------- drivers/staging/greybus/core.c | 10 ---------- 4 files changed, 4 insertions(+), 36 deletions(-) delete mode 100644 drivers/staging/greybus/bootrom.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 981569dc86a7..40325b32c5f7 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -9,7 +9,6 @@ greybus-y := core.o \ control.o \ svc.o \ svc_watchdog.o \ - bootrom.o \ operation.o \ timesync.o \ timesync_platform.o @@ -31,6 +30,7 @@ gb-audio-gb-y := audio_gb.o gb-audio-apbridgea-y := audio_apbridgea.o gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o +gb-bootrom-y := bootrom.o gb-camera-y := camera.o gb-firmware-y := fw-core.o fw-download.o fw-management.o gb-spilib-y := spilib.o @@ -62,6 +62,7 @@ endif obj-m += gb-audio-gb.o obj-m += gb-audio-apbridgea.o obj-m += gb-audio-manager.o +obj-m += gb-bootrom.o obj-m += gb-firmware.o obj-m += gb-spilib.o obj-m += gb-sdio.o diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index f375a879242c..2cebffab8d9d 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -12,7 +12,6 @@ #include <linux/mutex.h> #include <linux/workqueue.h> -#include "bootrom.h" #include "greybus.h" /* Timeout, in jiffies, within which the next request must be received */ @@ -455,12 +454,6 @@ static struct greybus_driver gb_bootrom_driver = { .id_table = gb_bootrom_id_table, }; -int gb_bootrom_init(void) -{ - return greybus_register(&gb_bootrom_driver); -} +module_greybus_driver(gb_bootrom_driver); -void gb_bootrom_exit(void) -{ - greybus_deregister(&gb_bootrom_driver); -} +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/greybus/bootrom.h b/drivers/staging/greybus/bootrom.h deleted file mode 100644 index fd2d1938ef21..000000000000 --- a/drivers/staging/greybus/bootrom.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Greybus bootrom code - * - * Copyright 2016 Google Inc. - * Copyright 2016 Linaro Ltd. - * - * Released under the GPLv2 only. - */ - -#ifndef __BOOTROM_H -#define __BOOTROM_H - -int gb_bootrom_init(void); -void gb_bootrom_exit(void); - -#endif /* __BOOTROM_H */ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 5811299c96fa..75bc6081d21a 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -10,7 +10,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define CREATE_TRACE_POINTS -#include "bootrom.h" #include "greybus.h" #include "greybus_trace.h" @@ -271,12 +270,6 @@ static int __init gb_init(void) goto error_operation; } - retval = gb_bootrom_init(); - if (retval) { - pr_err("gb_bootrom_init failed\n"); - goto error_bootrom; - } - retval = gb_timesync_init(); if (retval) { pr_err("gb_timesync_init failed\n"); @@ -285,8 +278,6 @@ static int __init gb_init(void) return 0; /* Success */ error_timesync: - gb_bootrom_exit(); -error_bootrom: gb_operation_exit(); error_operation: gb_hd_exit(); @@ -302,7 +293,6 @@ module_init(gb_init); static void __exit gb_exit(void) { gb_timesync_exit(); - gb_bootrom_exit(); gb_operation_exit(); gb_hd_exit(); bus_unregister(&greybus_bus_type); -- cgit v1.2.3-59-g8ed1b From 129a6fbe7648987b23a87d5d9c4fbf0780c528ed Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 9 Jun 2016 16:34:36 +0530 Subject: greybus: core: Make greybus_match_one_id() return bool This routine always returns 0 or 1 and a return type of 'bool' suits it the best. Update it. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 75bc6081d21a..a3837b2c8e89 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -31,22 +31,22 @@ int greybus_disabled(void) } EXPORT_SYMBOL_GPL(greybus_disabled); -static int greybus_match_one_id(struct gb_bundle *bundle, +static bool greybus_match_one_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && (id->vendor != bundle->intf->vendor_id)) - return 0; + return false; if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && (id->product != bundle->intf->product_id)) - return 0; + return false; if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) && (id->class != bundle->class)) - return 0; + return false; - return 1; + return true; } static const struct greybus_bundle_id * -- cgit v1.2.3-59-g8ed1b From 8a285fed1af3b9aae5e63ff3135fb9a0bf5d2c3c Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 9 Jun 2016 16:34:38 +0530 Subject: greybus: Use BIT(2) for GREYBUS_ID_MATCH_CLASS Bit 2 was left unused, use it. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_id.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_id.h b/drivers/staging/greybus/greybus_id.h index c91e7be0451c..4bb1fc1b811d 100644 --- a/drivers/staging/greybus/greybus_id.h +++ b/drivers/staging/greybus/greybus_id.h @@ -21,6 +21,6 @@ struct greybus_bundle_id { /* Used to match the greybus_bundle_id */ #define GREYBUS_ID_MATCH_VENDOR BIT(0) #define GREYBUS_ID_MATCH_PRODUCT BIT(1) -#define GREYBUS_ID_MATCH_CLASS BIT(3) +#define GREYBUS_ID_MATCH_CLASS BIT(2) #endif /* __LINUX_GREYBUS_ID_H */ -- cgit v1.2.3-59-g8ed1b From c05d471b2d6f4b9775674a3ada32a08079b1d24b Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 9 Jun 2016 16:34:39 +0530 Subject: greybus: Remove legacy suspend/resume callbacks We should be using the PM hooks available within the 'struct device_driver', instead of adding legacy suspend/resume callbacks. Remove them. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 154dfd11e93b..dbc9be05afc6 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -62,9 +62,6 @@ struct greybus_driver { const struct greybus_bundle_id *id); void (*disconnect)(struct gb_bundle *bundle); - int (*suspend)(struct gb_bundle *bundle, pm_message_t message); - int (*resume)(struct gb_bundle *bundle); - const struct greybus_bundle_id *id_table; struct device_driver driver; -- cgit v1.2.3-59-g8ed1b From 56278c7384a3c0242fe2f7ba91473c978ab500ae Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 9 Jun 2016 16:34:40 +0530 Subject: greybus: hd: Export host device tracepoint from hd.c There is no point keeping this code in core.c, while its only used by hd.c. Relocate it. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/core.c | 5 ----- drivers/staging/greybus/hd.c | 6 +++++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index a3837b2c8e89..dacc49c55253 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -13,11 +13,6 @@ #include "greybus.h" #include "greybus_trace.h" -EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_create); -EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_release); -EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_add); -EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_del); - /* Allow greybus to be disabled at boot if needed */ static bool nogreybus; #ifdef MODULE diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 6d952ba441a6..8ef849a8300f 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -13,8 +13,12 @@ #include "greybus.h" #include "greybus_trace.h" -EXPORT_TRACEPOINT_SYMBOL_GPL(gb_message_submit); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_create); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_release); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_add); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_del); EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_in); +EXPORT_TRACEPOINT_SYMBOL_GPL(gb_message_submit); static struct ida gb_hd_bus_id_map; -- cgit v1.2.3-59-g8ed1b From 62e04623530c9a4312817438dea4859c549d01c1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 9 Jun 2016 16:34:42 +0530 Subject: greybus: connection: Avoid unnecessary line breaks Some line breaks weren't required as we never crossed 80 columns, remove them. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7def600ddeb5..e0ee82f45de1 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -288,8 +288,7 @@ static int gb_connection_hd_cport_enable(struct gb_connection *connection) ret = hd->driver->cport_enable(hd, connection->hd_cport_id); if (ret) { - dev_err(&hd->dev, - "%s: failed to enable host cport: %d\n", + dev_err(&hd->dev, "%s: failed to enable host cport: %d\n", connection->name, ret); return ret; } @@ -307,8 +306,7 @@ static void gb_connection_hd_cport_disable(struct gb_connection *connection) ret = hd->driver->cport_disable(hd, connection->hd_cport_id); if (ret) { - dev_err(&hd->dev, - "%s: failed to disable host cport: %d\n", + dev_err(&hd->dev, "%s: failed to disable host cport: %d\n", connection->name, ret); } } -- cgit v1.2.3-59-g8ed1b From 8c2522d87ab20ba245ddf92e4e8b19a76e5760fd Mon Sep 17 00:00:00 2001 From: Eli Sennesh <esennesh@leaflabs.com> Date: Fri, 3 Jun 2016 11:24:44 -0400 Subject: greybus: update UniPro Set Interface Power Mode operation to match spec Bring the gb_svc_intf_set_power_mode() up-to-date with the current Greybus specification. This largely involves adding more members to the structure sent across the wire. Also change the camera code to use the new operation properly, with default values passed for the new necessary arguments. The correctness of these default values is confirmed via testing and by asking Rob Johnson. We must make sure to zero the request structure sent across the wire, lest bite us most cruelly, and we fix by changing the Set Power Mode Response structure to use a __u8 rather than a __le16. Testing Done: Took a picture with a camera module, received error code when passing deliberately incorrect values for new parameters, got proper -EIO and Greybus result code printed when operation stopped halfway through. Could induce error by initializing request struct with random nonsense, and can stop it by initializing request struct with zeroes. Associated Firmware Changes: 1669-1671 on Android N Gerrit for SW-2945 Signed-off-by: Eli Sennesh <esennesh@leaflabs.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 10 ++++++++-- drivers/staging/greybus/greybus_protocols.h | 30 ++++++++++++++++++++++++++++- drivers/staging/greybus/svc.c | 26 ++++++++++++++++++++++--- drivers/staging/greybus/svc.h | 5 ++++- 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 162cb7d294cd..ca1f4989197f 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -128,17 +128,23 @@ static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, ret = gb_svc_intf_set_power_mode(svc, intf_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_FAST_MODE, 2, 2, + GB_SVC_SMALL_AMPLITUDE, + GB_SVC_NO_DE_EMPHASIS, GB_SVC_UNIPRO_FAST_MODE, 2, 2, GB_SVC_PWRM_RXTERMINATION | - GB_SVC_PWRM_TXTERMINATION, 0); + GB_SVC_PWRM_TXTERMINATION, 0, + NULL, NULL); else ret = gb_svc_intf_set_power_mode(svc, intf_id, GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, + GB_SVC_SMALL_AMPLITUDE, + GB_SVC_NO_DE_EMPHASIS, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, - 0, 0); + 0, 0, + NULL, NULL); return ret; } diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 8b5563ede00e..63dd2041fdeb 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1100,6 +1100,13 @@ struct gb_svc_timesync_ping_response { #define GB_SVC_UNIPRO_HIBERNATE_MODE 0x11 #define GB_SVC_UNIPRO_OFF_MODE 0x12 +#define GB_SVC_SMALL_AMPLITUDE 0x01 +#define GB_SVC_LARGE_AMPLITUDE 0x02 + +#define GB_SVC_NO_DE_EMPHASIS 0x00 +#define GB_SVC_SMALL_DE_EMPHASIS 0x01 +#define GB_SVC_LARGE_DE_EMPHASIS 0x02 + #define GB_SVC_PWRM_RXTERMINATION 0x01 #define GB_SVC_PWRM_TXTERMINATION 0x02 #define GB_SVC_PWRM_LINE_RESET 0x04 @@ -1110,21 +1117,42 @@ struct gb_svc_timesync_ping_response { #define GB_SVC_UNIPRO_HS_SERIES_A 0x01 #define GB_SVC_UNIPRO_HS_SERIES_B 0x02 +#define GB_SVC_SETPWRM_PWR_OK 0x00 +#define GB_SVC_SETPWRM_PWR_LOCAL 0x01 +#define GB_SVC_SETPWRM_PWR_REMOTE 0x02 +#define GB_SVC_SETPWRM_PWR_BUSY 0x03 +#define GB_SVC_SETPWRM_PWR_ERROR_CAP 0x04 +#define GB_SVC_SETPWRM_PWR_FATAL_ERROR 0x05 + +struct gb_svc_l2_timer_cfg { + __le16 tsb_fc0_protection_timeout; + __le16 tsb_tc0_replay_timeout; + __le16 tsb_afc0_req_timeout; + __le16 tsb_fc1_protection_timeout; + __le16 tsb_tc1_replay_timeout; + __le16 tsb_afc1_req_timeout; + __le16 reserved_for_tc2[3]; + __le16 reserved_for_tc3[3]; +} __packed; + struct gb_svc_intf_set_pwrm_request { __u8 intf_id; __u8 hs_series; __u8 tx_mode; __u8 tx_gear; __u8 tx_nlanes; + __u8 tx_amplitude; + __u8 tx_hs_equalizer; __u8 rx_mode; __u8 rx_gear; __u8 rx_nlanes; __u8 flags; __le32 quirks; + struct gb_svc_l2_timer_cfg local_l2timerdata, remote_l2timerdata; } __packed; struct gb_svc_intf_set_pwrm_response { - __le16 result_code; + __u8 result_code; } __packed; struct gb_svc_key_event_request { diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index e2ab94277b4e..cee058fe5188 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -616,23 +616,35 @@ void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, u8 tx_mode, u8 tx_gear, u8 tx_nlanes, + u8 tx_amplitude, u8 tx_hs_equalizer, u8 rx_mode, u8 rx_gear, u8 rx_nlanes, - u8 flags, u32 quirks) + u8 flags, u32 quirks, + struct gb_svc_l2_timer_cfg *local, + struct gb_svc_l2_timer_cfg *remote) { struct gb_svc_intf_set_pwrm_request request; struct gb_svc_intf_set_pwrm_response response; int ret; + u16 result_code; + + memset(&request, 0, sizeof(request)); request.intf_id = intf_id; request.hs_series = hs_series; request.tx_mode = tx_mode; request.tx_gear = tx_gear; request.tx_nlanes = tx_nlanes; + request.tx_amplitude = tx_amplitude; + request.tx_hs_equalizer = tx_hs_equalizer; request.rx_mode = rx_mode; request.rx_gear = rx_gear; request.rx_nlanes = rx_nlanes; request.flags = flags; request.quirks = cpu_to_le32(quirks); + if (local) + request.local_l2timerdata = *local; + if (remote) + request.remote_l2timerdata = *remote; ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, &request, sizeof(request), @@ -640,7 +652,13 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, if (ret < 0) return ret; - return le16_to_cpu(response.result_code); + result_code = response.result_code; + if (result_code != GB_SVC_SETPWRM_PWR_LOCAL) { + dev_err(&svc->dev, "set power mode = %d\n", result_code); + return -EIO; + } + + return 0; } EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); @@ -954,9 +972,11 @@ static void gb_svc_process_hello_deferred(struct gb_operation *operation) GB_SVC_UNIPRO_HS_SERIES_A, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, + GB_SVC_SMALL_AMPLITUDE, GB_SVC_NO_DE_EMPHASIS, GB_SVC_UNIPRO_SLOW_AUTO_MODE, 2, 1, - 0, 0); + 0, 0, + NULL, NULL); if (ret) dev_warn(&svc->dev, diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 7a78c5bffbe6..dab8fbab2380 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -79,8 +79,11 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 value); int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, u8 tx_mode, u8 tx_gear, u8 tx_nlanes, + u8 tx_amplitude, u8 tx_hs_equalizer, u8 rx_mode, u8 rx_gear, u8 rx_nlanes, - u8 flags, u32 quirks); + u8 flags, u32 quirks, + struct gb_svc_l2_timer_cfg *local, + struct gb_svc_l2_timer_cfg *remote); int gb_svc_ping(struct gb_svc *svc); int gb_svc_watchdog_create(struct gb_svc *svc); void gb_svc_watchdog_destroy(struct gb_svc *svc); -- cgit v1.2.3-59-g8ed1b From 27c243cf5fcf4b3bb525f1f3f15ed8db91199507 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 9 Jun 2016 09:30:39 +0530 Subject: greybus: Use mute_stream callback to initiate GB data xfer Currently trigger callback is used to start/stop greybus tx/rx path. This works well for almost all scenario except few specially handled usecases by Android Audio subsystem. In case of Music playback followed by Incoming ringtone, above layer tries to trigger_pause from one FE dailink and start a fresh playback via different FE dailink. Since, same BE dailink is used for both cases, an invalid state transition is requested i.e. from PAUSE->START. This fails & thus causes ringtone playback failure. With built-in codec, trigger callback is not required to initiate data xfer unlike gb-codec driver. This state transition should be handled in Android layer, but since it can lead to multiple side effects for various usecase we are trying to avoid trigger callback in gbcodec driver as well. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 77 +++++++++-------------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 975d2e86b113..334469345f87 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -731,54 +731,24 @@ func_exit: return ret; } -static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) +static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) { int ret; - int tx, rx, start, stop; struct gbaudio_data_connection *data; struct gbaudio_module_info *module; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); + + dev_dbg(dai->dev, "Mute:%d, Direction:%s\n", mute, + stream ? "CAPTURE":"PLAYBACK"); + mutex_lock(&codec->lock); if (list_empty(&codec->module_list)) { dev_err(codec->dev, "No codec module available\n"); mutex_unlock(&codec->lock); - if (cmd == SNDRV_PCM_TRIGGER_STOP) - return 0; return -ENODEV; } - tx = rx = start = stop = 0; - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - start = 1; - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - stop = 1; - break; - default: - dev_err(dai->dev, "Invalid tigger cmd:%d\n", cmd); - ret = -EINVAL; - goto func_exit; - } - - switch (substream->stream) { - case SNDRV_PCM_STREAM_CAPTURE: - rx = 1; - break; - case SNDRV_PCM_STREAM_PLAYBACK: - tx = 1; - break; - default: - dev_err(dai->dev, "Invalid stream type:%d\n", - substream->stream); - ret = -EINVAL; - goto func_exit; - } - list_for_each_entry(module, &codec->module_list, list) { /* find the dai */ data = find_data(module, dai->name); @@ -791,50 +761,43 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, ret = -ENODEV; goto func_exit; } - if (start && tx) { + + if (!mute && !stream) {/* start playback */ ret = gb_audio_apbridgea_prepare_tx(data->connection, 0); if (!ret) ret = gb_audio_apbridgea_start_tx(data->connection, 0, 0); - codec->stream[substream->stream].state = GBAUDIO_CODEC_START; - } else if (start && rx) { + codec->stream[stream].state = GBAUDIO_CODEC_START; + } else if (!mute && stream) {/* start capture */ ret = gb_audio_apbridgea_prepare_rx(data->connection, 0); if (!ret) ret = gb_audio_apbridgea_start_rx(data->connection, 0); - codec->stream[substream->stream].state = GBAUDIO_CODEC_START; - } else if (stop && tx) { + codec->stream[stream].state = GBAUDIO_CODEC_START; + } else if (mute && !stream) {/* stop playback */ ret = gb_audio_apbridgea_stop_tx(data->connection, 0); if (!ret) ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0); - codec->stream[substream->stream].state = GBAUDIO_CODEC_STOP; - } else if (stop && rx) { + codec->stream[stream].state = GBAUDIO_CODEC_STOP; + } else if (mute && stream) {/* stop capture */ ret = gb_audio_apbridgea_stop_rx(data->connection, 0); if (!ret) ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0); - codec->stream[substream->stream].state = GBAUDIO_CODEC_STOP; + codec->stream[stream].state = GBAUDIO_CODEC_STOP; } else ret = -EINVAL; if (ret) - dev_err_ratelimited(dai->dev, "%s:Error during %s stream:%d\n", - module->name, start ? "Start" : "Stop", ret); + dev_err_ratelimited(dai->dev, + "%s:Error during %s %s stream:%d\n", + module->name, mute ? "Mute" : "Unmute", + stream ? "Capture" : "Playback", ret); func_exit: mutex_unlock(&codec->lock); - return ret; -} - -static int gbcodec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - return 0; -} - -static int gbcodec_digital_mute(struct snd_soc_dai *dai, int mute) -{ return 0; } @@ -842,10 +805,8 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { .startup = gbcodec_startup, .shutdown = gbcodec_shutdown, .hw_params = gbcodec_hw_params, - .trigger = gbcodec_trigger, .prepare = gbcodec_prepare, - .set_fmt = gbcodec_set_dai_fmt, - .digital_mute = gbcodec_digital_mute, + .mute_stream = gbcodec_mute_stream, }; static int gbaudio_init_jack(struct gbaudio_module_info *module, -- cgit v1.2.3-59-g8ed1b From f2b6303d4505b5b046691a936c346a0e376cd9f3 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 9 Jun 2016 09:30:40 +0530 Subject: greybus: Remove apbridgea_shutdown_xx sequence if already done While reordering gb_deactivate sequence to avoid protocol error this was mistakenly added even during shutdown_tx/rx. It is supposed to be done immediately after stop_tx and only once. Fixes: 739f25d5f490 ("audio: Reorder gb_deactivate sequence to avoid protocol error") Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 334469345f87..2feac6beea57 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -396,12 +396,6 @@ static int gbmodule_shutdown_tx(struct gbaudio_module_info *module, return 0; } - if (codec_state == GBAUDIO_CODEC_STOP) { - ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0); - if (ret) - return ret; - } - /* deactivate */ cportid = data->connection->intf_cport_id; if (module_state >= GBAUDIO_CODEC_PREPARE) { @@ -435,12 +429,6 @@ static int gbmodule_shutdown_rx(struct gbaudio_module_info *module, return 0; } - if (codec_state == GBAUDIO_CODEC_STOP) { - ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0); - if (ret) - return ret; - } - /* deactivate */ cportid = data->connection->intf_cport_id; if (module_state >= GBAUDIO_CODEC_PREPARE) { -- cgit v1.2.3-59-g8ed1b From c6722ab5d3c3d23021dd32bd1ae569665ee2263b Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Fri, 10 Jun 2016 12:59:11 +0530 Subject: greybus: audio: Ratelimit err messages in bundle, topology driver Earlier I have shared a patch to rate limit err messages in audio_codec driver. However, missed to include suggestion from Mark to do similar changes in audio bundle & topology parser as well. Doing it now. Testing Done: Compile tested Fixes: 4cb3d109e5fc ("audio: Ratelimit err messages") Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 25 +++++++++++++++---------- drivers/staging/greybus/audio_topology.c | 23 ++++++++++++----------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 95d2ddadeeab..81001329aad2 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -23,8 +23,9 @@ static int gbaudio_request_jack(struct gbaudio_module_info *module, { int report, button_status; - dev_warn(module->dev, "Jack Event received: type: %u, event: %u\n", - req->jack_attribute, req->event); + dev_warn_ratelimited(module->dev, + "Jack Event received: type: %u, event: %u\n", + req->jack_attribute, req->event); if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) { module->jack_type = 0; @@ -42,8 +43,9 @@ static int gbaudio_request_jack(struct gbaudio_module_info *module, report &= ~GBCODEC_JACK_MASK; report |= req->jack_attribute & GBCODEC_JACK_MASK; if (module->jack_type) - dev_warn(module->dev, "Modifying jack from %d to %d\n", - module->jack_type, report); + dev_warn_ratelimited(module->dev, + "Modifying jack from %d to %d\n", + module->jack_type, report); module->jack_type = report; snd_soc_jack_report(&module->headset_jack, report, GBCODEC_JACK_MASK); @@ -56,12 +58,14 @@ static int gbaudio_request_button(struct gbaudio_module_info *module, { int soc_button_id, report; - dev_warn(module->dev, "Button Event received: id: %u, event: %u\n", - req->button_id, req->event); + dev_warn_ratelimited(module->dev, + "Button Event received: id: %u, event: %u\n", + req->button_id, req->event); /* currently supports 4 buttons only */ if (!module->jack_type) { - dev_err(module->dev, "Jack not present. Bogus event!!\n"); + dev_err_ratelimited(module->dev, + "Jack not present. Bogus event!!\n"); return -EINVAL; } @@ -84,7 +88,8 @@ static int gbaudio_request_button(struct gbaudio_module_info *module, soc_button_id = SND_JACK_BTN_3; break; default: - dev_err(module->dev, "Invalid button request received\n"); + dev_err_ratelimited(module->dev, + "Invalid button request received\n"); return -EINVAL; } @@ -138,8 +143,8 @@ static int gbaudio_codec_request_handler(struct gb_operation *op) break; default: - dev_err(&connection->bundle->dev, - "Invalid Audio Event received\n"); + dev_err_ratelimited(&connection->bundle->dev, + "Invalid Audio Event received\n"); return -EINVAL; } diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index dfe120baecad..1e0768670b69 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -173,8 +173,8 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); if (ret) { - dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, - kcontrol->id.name); + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); return ret; } @@ -253,8 +253,8 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); if (ret) { - dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, - kcontrol->id.name); + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); } return ret; @@ -335,8 +335,8 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); if (ret) { - dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, - kcontrol->id.name); + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); return ret; } /* update ucontrol */ @@ -393,9 +393,9 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); if (ret) { - dev_err(codec->dev, - "%d:Error in %s for %s\n", ret, __func__, - kcontrol->id.name); + dev_err_ratelimited(codec->dev, + "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); } } @@ -600,8 +600,9 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, break; } if (ret) - dev_err(codec->dev, "%d: widget, event:%d failed:%d\n", wid, - event, ret); + dev_err_ratelimited(codec->dev, + "%d: widget, event:%d failed:%d\n", wid, + event, ret); return ret; } -- cgit v1.2.3-59-g8ed1b From 6db9cc68d7f9cea440293af4225a7b7f22d1d86d Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Fri, 10 Jun 2016 17:33:03 +0100 Subject: greybus: uart: Fix minor number leak On the gb_uart_remove() path we are forgetting to do a release_minor() leading to a minor number leak. This is a simple one-line fix. Tested on EVT 2.0 Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index d1611abf8ccd..7460bdbd0a1a 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -969,6 +969,7 @@ static void gb_uart_remove(struct gbphy_device *gbphy_dev) gb_connection_disable(connection); tty_port_destroy(&gb_tty->port); gb_connection_destroy(connection); + release_minor(gb_tty); kfifo_free(&gb_tty->write_fifo); kfree(gb_tty->buffer); kfree(gb_tty); -- cgit v1.2.3-59-g8ed1b From 4c2f8a48e989c323e4fb378f95ceaa8091760832 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Fri, 10 Jun 2016 16:10:12 +0100 Subject: greybus: timesync: Do 64 bit divisions in a 32 friendly way We need to use 'do_div()' when doing 64 bit division or modulo division since the kernel will not pull in the gcc builtins __aeabi_ldivmod and __aeabi_uldivmod on 32 bit builds. Reported-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index 87cbe10a5ae7..a029fa085af6 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -502,8 +502,7 @@ static int gb_timesync_to_timespec(struct gb_timesync_svc *timesync_svc, u64 frame_time, struct timespec *ts) { unsigned long flags; - u64 delta_fs, counts; - u32 sec, nsec; + u64 delta_fs, counts, sec, nsec; bool add; int ret = 0; @@ -532,8 +531,15 @@ static int gb_timesync_to_timespec(struct gb_timesync_svc *timesync_svc, /* Determine the time difference in femtoseconds */ delta_fs = counts * gb_timesync_fs_per_clock; - sec = delta_fs / FSEC_PER_SEC; - nsec = (delta_fs % FSEC_PER_SEC) / 1000000UL; + + /* Convert to seconds */ + sec = delta_fs; + do_div(sec, NSEC_PER_SEC); + do_div(sec, 1000000UL); + + /* Get the nanosecond remainder */ + nsec = do_div(delta_fs, sec); + do_div(nsec, 1000000UL); if (add) { /* Add the calculated offset - overflow nanoseconds upwards */ @@ -548,7 +554,7 @@ static int gb_timesync_to_timespec(struct gb_timesync_svc *timesync_svc, if (nsec > timesync_svc->ktime_data.ts.tv_nsec) { sec++; nsec = nsec + timesync_svc->ktime_data.ts.tv_nsec; - nsec %= NSEC_PER_SEC; + nsec = do_div(nsec, NSEC_PER_SEC); } else { nsec = timesync_svc->ktime_data.ts.tv_nsec - nsec; } @@ -1318,8 +1324,10 @@ int __init gb_timesync_init(void) gb_timesync_clock_rate = gb_timesync_platform_get_clock_rate(); /* Calculate nanoseconds and femtoseconds per clock */ - gb_timesync_fs_per_clock = FSEC_PER_SEC / gb_timesync_clock_rate; - gb_timesync_ns_per_clock = NSEC_PER_SEC / gb_timesync_clock_rate; + gb_timesync_fs_per_clock = FSEC_PER_SEC; + do_div(gb_timesync_fs_per_clock, gb_timesync_clock_rate); + gb_timesync_ns_per_clock = NSEC_PER_SEC; + do_div(gb_timesync_ns_per_clock, gb_timesync_clock_rate); /* Calculate the maximum number of clocks we will convert to ktime */ gb_timesync_max_ktime_diff = -- cgit v1.2.3-59-g8ed1b From 6554efa28edc09029012f8217c5481ff320b7fca Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Fri, 10 Jun 2016 11:37:54 +0530 Subject: greybus: firmware: Fix spelling mistake s/directly/directory :) Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Documentation/firmware/firmware-management | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Documentation/firmware/firmware-management b/drivers/staging/greybus/Documentation/firmware/firmware-management index f70d3cd26be1..bafe700f9901 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware-management +++ b/drivers/staging/greybus/Documentation/firmware/firmware-management @@ -185,6 +185,6 @@ mtd0 mtd0ro Sample Application ------------------ -The current directly also provides a firmware.c test application, which can be +The current directory also provides a firmware.c test application, which can be referenced while developing userspace application to talk to firmware-management protocol. -- cgit v1.2.3-59-g8ed1b From 60fb3405c73ea0bbab942c699496123f9e987c04 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Fri, 10 Jun 2016 14:59:07 +0530 Subject: greybus: Remove extra blank lines This patch removes few blank lines across the repository at places where two blank lines were present together or when a blank line is present at the start or end of a routine. Note that this doesn't remove most of them from greybus_protocols.h as they were added on purpose. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 1 - drivers/staging/greybus/audio_module.c | 2 -- drivers/staging/greybus/audio_topology.c | 1 - drivers/staging/greybus/bundle.c | 1 - drivers/staging/greybus/connection.c | 1 - drivers/staging/greybus/gpio.c | 1 - drivers/staging/greybus/hid.c | 1 - drivers/staging/greybus/power_supply.c | 3 --- drivers/staging/greybus/pwm.c | 1 - drivers/staging/greybus/spilib.c | 1 - drivers/staging/greybus/svc.c | 1 - drivers/staging/greybus/uart.c | 3 --- 12 files changed, 17 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 2feac6beea57..45e12b1d8627 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -1164,7 +1164,6 @@ static struct snd_soc_codec_driver soc_codec_dev_gbaudio = { .ignore_pmdown_time = 1, }; - #ifdef CONFIG_PM static int gbaudio_codec_suspend(struct device *dev) { diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 81001329aad2..d87b9985a0e2 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -221,7 +221,6 @@ static int gb_audio_probe(struct gb_bundle *bundle, int ret, i; struct gb_audio_topology *topology; - /* There should be at least one Management and one Data cport */ if (bundle->num_cports < 2) return -ENODEV; @@ -367,7 +366,6 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); struct gbaudio_data_connection *dai, *_dai; - /* cleanup module related resources first */ gbaudio_unregister_module(gbmodule); diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 1e0768670b69..1f9e8b6178dd 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -872,7 +872,6 @@ static int gbaudio_tplg_process_routes(struct gbaudio_module_info *module, if (!dapm_routes) return -ENOMEM; - module->dapm_routes = dapm_routes; curr = routes; diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index c1c3d67a0279..80f54c977509 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -56,7 +56,6 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(state); - static struct attribute *bundle_attrs[] = { &dev_attr_bundle_class.attr, &dev_attr_bundle_id.attr, diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index e0ee82f45de1..28076d014177 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -513,7 +513,6 @@ gb_connection_control_disconnected(struct gb_connection *connection) return; } - ret = gb_control_disconnected_operation(control, cport_id); if (ret) { dev_warn(&connection->bundle->dev, diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index f60cc1da9f05..deae2658c7a8 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -556,7 +556,6 @@ static void gb_gpio_irqchip_remove(struct gb_gpio_controller *ggc) } } - /** * gb_gpio_irqchip_add() - adds an irqchip to a gpio chip * @chip: the gpio chip to add the irqchip to diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index fd4a7e096450..ca0c94949c6b 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -116,7 +116,6 @@ static int gb_hid_request_handler(struct gb_operation *op) return 0; } - static int gb_hid_report_len(struct hid_report *report) { return ((report->size - 1) >> 3) + 1 + diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 8d6570d1bb61..e96c24da007b 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -290,7 +290,6 @@ static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy) goto out_put_operation; } - /* Store available properties */ for (i = 0; i < gbpsy->properties_count; i++) { gbpsy->props[i].prop = resp->props[i].property; @@ -512,7 +511,6 @@ static int property_is_writeable(struct power_supply *b, return is_psy_prop_writeable(gbpsy, psp); } - #ifndef CORE_OWNS_PSY_STRUCT static int gb_power_supply_register(struct gb_power_supply *gbpsy) { @@ -562,7 +560,6 @@ static void _gb_power_supply_free(struct gb_power_supply *gbpsy) static void _gb_power_supply_release(struct gb_power_supply *gbpsy) { - gbpsy->update_interval = 0; cancel_delayed_work_sync(&gbpsy->work); diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 713123231251..1438b2e12cee 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -80,7 +80,6 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, &request, sizeof(request), NULL, 0); } - static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, u8 which, u8 polarity) { diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c index 79ae044b78f3..527909b26d79 100644 --- a/drivers/staging/greybus/spilib.c +++ b/drivers/staging/greybus/spilib.c @@ -383,7 +383,6 @@ static void gb_spi_cleanup(struct spi_device *spi) /* Nothing to do for now */ } - /* Routines to get controller information */ /* diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index cee058fe5188..d467ceb0916d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -44,7 +44,6 @@ static ssize_t ap_intf_id_show(struct device *dev, } static DEVICE_ATTR_RO(ap_intf_id); - // FIXME // This is a hack, we need to do this "right" and clean the interface up // properly, not just forcibly yank the thing out of the system and hope for the diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 7460bdbd0a1a..6260569b2f25 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -597,7 +597,6 @@ static void gb_tty_throttle(struct tty_struct *tty) gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; retval = send_control(gb_tty, gb_tty->ctrlout); } - } static void gb_tty_unthrottle(struct tty_struct *tty) @@ -710,7 +709,6 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) } while (!retval); return retval; - } static int get_serial_usage(struct gb_tty *gb_tty, @@ -924,7 +922,6 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, goto exit_connection_disable; } - return 0; exit_connection_disable: -- cgit v1.2.3-59-g8ed1b From 54131222e6dc3a26de5cb036e599307666e58fd9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Fri, 10 Jun 2016 14:59:09 +0530 Subject: greybus: connection: Return bool from gb_connection_intf_find() This is used only to check if an existing connection already uses the cport_id or not and doesn't really need to return pointer to the connection. While at it, also rename it to suit its purpose. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 28076d014177..3cfc4dd134d8 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -21,8 +21,7 @@ static DEFINE_MUTEX(gb_connection_mutex); /* Caller holds gb_connection_mutex. */ -static struct gb_connection * -gb_connection_intf_find(struct gb_interface *intf, u16 cport_id) +static bool gb_connection_cport_in_use(struct gb_interface *intf, u16 cport_id) { struct gb_host_device *hd = intf->hd; struct gb_connection *connection; @@ -30,10 +29,10 @@ gb_connection_intf_find(struct gb_interface *intf, u16 cport_id) list_for_each_entry(connection, &hd->connections, hd_links) { if (connection->intf == intf && connection->intf_cport_id == cport_id) - return connection; + return true; } - return NULL; + return false; } static void gb_connection_get(struct gb_connection *connection) @@ -155,7 +154,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, mutex_lock(&gb_connection_mutex); - if (intf && gb_connection_intf_find(intf, cport_id)) { + if (intf && gb_connection_cport_in_use(intf, cport_id)) { dev_err(&intf->dev, "cport %u already in use\n", cport_id); ret = -EBUSY; goto err_unlock; -- cgit v1.2.3-59-g8ed1b From d9e4c4ee12e490aca06a0a70d534c60648e2f43a Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 13 Jun 2016 15:08:01 +0530 Subject: greybus: camera: Initialize mutex before using it We are using the mutex from gb_camera_cleanup(), which can get called even before the mutex is initialized. Fix it by initializing the mutex early enough. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index ca1f4989197f..0d20c6b8b015 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -999,6 +999,7 @@ static int gb_camera_probe(struct gb_bundle *bundle, return -ENOMEM; gcam->bundle = bundle; + mutex_init(&gcam->mutex); conn = gb_connection_create(bundle, mgmt_cport_id, gb_camera_request_handler); @@ -1014,8 +1015,6 @@ static int gb_camera_probe(struct gb_bundle *bundle, if (ret) goto error; - mutex_init(&gcam->mutex); - /* * Create the data connection between the camera module data CPort and * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. -- cgit v1.2.3-59-g8ed1b From d1a8c36ec10446c3584033b06beaa51bad188144 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 13 Jun 2016 21:48:28 +0530 Subject: greybus: svc: disable connection after all its users are gone gb_svc_del() can be called during removal of gb-es2.ko module as well, and in that case we would like to properly shutdown all modules and interfaces as USB is still alive. This requires that we don't disable the svc connection, at least for tx, as that will be used while removing modules and interfaces. Disable only rx to begin with, as we shouldn't be handling any requests from the SVC. Disable tx only after all the users of svc connection are gone. Tested on EVT 2.0 by remove gb-es2.ko module. There are still few errors, specially while quiescing the connections (-22), but not that many. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index d467ceb0916d..e316f08ab756 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -1456,7 +1456,7 @@ static void gb_svc_remove_modules(struct gb_svc *svc) void gb_svc_del(struct gb_svc *svc) { - gb_connection_disable(svc->connection); + gb_connection_disable_rx(svc->connection); /* * The SVC device and input device may have been registered @@ -1473,6 +1473,8 @@ void gb_svc_del(struct gb_svc *svc) flush_workqueue(svc->wq); gb_svc_remove_modules(svc); + + gb_connection_disable(svc->connection); } void gb_svc_put(struct gb_svc *svc) -- cgit v1.2.3-59-g8ed1b From f3d5f6613f0da3ba1135f6e06b9692202fb9920b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Date: Tue, 14 Jun 2016 15:59:23 +0300 Subject: greybus: camera: Clean up on stream configuration failure When the camera pipeline can't be configured due to a failure of one of the components (failure to start the CSI transmitter for instance), components that have already been setup for video streaming need to be set back to a quiescient state. This is especially important to ensure that a stream configuration failure won't keep the UniPro links in high speed mode forever. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Tested-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 141 +++++++++++++++++++++++++-------------- 1 file changed, 91 insertions(+), 50 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 0d20c6b8b015..bbf54d7f52a1 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -115,7 +115,7 @@ static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { #define gcam_err(gcam, format...) dev_err(&gcam->bundle->dev, format) /* ----------------------------------------------------------------------------- - * Camera Protocol Operations + * Hardware Configuration */ static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, @@ -173,6 +173,87 @@ static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) return 0; } +struct ap_csi_config_request { + __u8 csi_id; + __u8 flags; +#define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01 + __u8 num_lanes; + __u8 padding; + __le32 bus_freq; + __le32 lines_per_second; +} __packed; + +static int gb_camera_setup_data_connection(struct gb_camera *gcam, + const struct gb_camera_configure_streams_response *resp, + struct gb_camera_csi_params *csi_params) +{ + struct ap_csi_config_request csi_cfg; + int ret; + + /* Set the UniPro link to high speed mode. */ + ret = gb_camera_set_power_mode(gcam, true); + if (ret < 0) + return ret; + + /* + * Configure the APB1 CSI transmitter using the lines count reported by + * the camera module, but with hard-coded bus frequency and lanes number. + * + * TODO: use the clocking and size informations reported by camera module + * to compute the required CSI bandwidth, and configure the CSI receiver + * on AP side, and the CSI transmitter on APB1 side accordingly. + */ + memset(&csi_cfg, 0, sizeof(csi_cfg)); + csi_cfg.csi_id = 1; + csi_cfg.flags = 0; + csi_cfg.num_lanes = resp->num_lanes; + csi_cfg.bus_freq = cpu_to_le32(960000000); + csi_cfg.lines_per_second = resp->lines_per_second; + + ret = gb_hd_output(gcam->connection->hd, &csi_cfg, + sizeof(csi_cfg), + GB_APB_REQUEST_CSI_TX_CONTROL, false); + + if (ret < 0) { + gcam_err(gcam, "failed to start the CSI transmitter\n"); + gb_camera_set_power_mode(gcam, false); + return ret; + } + + if (csi_params) { + csi_params->num_lanes = csi_cfg.num_lanes; + /* Transmitting two bits per cycle. (DDR clock) */ + csi_params->clk_freq = csi_cfg.bus_freq / 2; + csi_params->lines_per_second = csi_cfg.lines_per_second; + } + + return 0; +} + +static void gb_camera_teardown_data_connection(struct gb_camera *gcam) +{ + struct ap_csi_config_request csi_cfg; + int ret; + + /* Stop the APB1 CSI transmitter. */ + memset(&csi_cfg, 0, sizeof(csi_cfg)); + csi_cfg.csi_id = 1; + + ret = gb_hd_output(gcam->connection->hd, &csi_cfg, + sizeof(csi_cfg), + GB_APB_REQUEST_CSI_TX_CONTROL, false); + + if (ret < 0) + gcam_err(gcam, "failed to stop the CSI transmitter\n"); + + /* Set the UniPro link to low speed mode. */ + gb_camera_set_power_mode(gcam, false); +} + +/* ----------------------------------------------------------------------------- + * Camera Protocol Operations + */ + static int gb_camera_capabilities(struct gb_camera *gcam, u8 *capabilities, size_t *size) { @@ -211,16 +292,6 @@ done: return ret; } -struct ap_csi_config_request { - __u8 csi_id; - __u8 flags; -#define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01 - __u8 num_lanes; - __u8 padding; - __le32 bus_freq; - __le32 lines_per_second; -} __packed; - static int gb_camera_configure_streams(struct gb_camera *gcam, unsigned int *num_streams, unsigned int *flags, @@ -229,7 +300,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, { struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; - struct ap_csi_config_request csi_cfg; unsigned int nstreams = *num_streams; unsigned int i; @@ -315,50 +385,21 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto done; } - /* Setup unipro link speed. */ - ret = gb_camera_set_power_mode(gcam, nstreams != 0); - if (ret < 0) - goto done; - - /* - * Configure the APB1 CSI transmitter using the lines count reported by - * the camera module, but with hard-coded bus frequency and lanes number. - * - * TODO: use the clocking and size informations reported by camera module - * to compute the required CSI bandwidth, and configure the CSI receiver - * on AP side, and the CSI transmitter on APB1 side accordingly. - */ - memset(&csi_cfg, 0, sizeof(csi_cfg)); - - if (nstreams) { - csi_cfg.csi_id = 1; - csi_cfg.flags = 0; - csi_cfg.num_lanes = resp->num_lanes; - csi_cfg.bus_freq = cpu_to_le32(960000000); - csi_cfg.lines_per_second = resp->lines_per_second; - ret = gb_hd_output(gcam->connection->hd, &csi_cfg, - sizeof(csi_cfg), - GB_APB_REQUEST_CSI_TX_CONTROL, false); - if (csi_params) { - csi_params->num_lanes = csi_cfg.num_lanes; - /* Transmitting two bits per cycle. (DDR clock) */ - csi_params->clk_freq = csi_cfg.bus_freq / 2; - csi_params->lines_per_second = csi_cfg.lines_per_second; + if (resp->num_streams) { + ret = gb_camera_setup_data_connection(gcam, resp, csi_params); + if (ret < 0) { + memset(req, 0, sizeof(*req)); + gb_operation_sync(gcam->connection, + GB_CAMERA_TYPE_CONFIGURE_STREAMS, + req, req_size, resp, resp_size); + goto done; } } else { - csi_cfg.csi_id = 1; - ret = gb_hd_output(gcam->connection->hd, &csi_cfg, - sizeof(csi_cfg), - GB_APB_REQUEST_CSI_TX_CONTROL, false); + gb_camera_teardown_data_connection(gcam); } - if (ret < 0) - gcam_err(gcam, "failed to %s the CSI transmitter\n", - nstreams ? "start" : "stop"); - *flags = resp->flags; *num_streams = resp->num_streams; - ret = 0; done: mutex_unlock(&gcam->mutex); -- cgit v1.2.3-59-g8ed1b From 3b8ebfeb32daf13ef5fccb7ec3d163783e6a01a2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Date: Tue, 14 Jun 2016 15:59:24 +0300 Subject: greybus: camera: Fix data connection setup When the module is in the configured state, an attempt to change the configuration must first tear down the data connection to update its parameters, as the APB1 bridge doesn't support modifying the CSI transmitter configuration when it is already started. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Tested-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index bbf54d7f52a1..db7cdcee1c53 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -33,11 +33,17 @@ struct gb_camera_debugfs_buffer { size_t length; }; +enum gb_camera_state { + GB_CAMERA_STATE_UNCONFIGURED, + GB_CAMERA_STATE_CONFIGURED, +}; + /** * struct gb_camera - A Greybus Camera Device * @connection: the greybus connection for camera management * @data_connection: the greybus connection for camera data - * @mutex: protects the connection field + * @mutex: protects the connection and state fields + * @state: the current module state * @debugfs: debugfs entries for camera protocol operations testing * @module: Greybus camera module registered to HOST processor. */ @@ -45,7 +51,9 @@ struct gb_camera { struct gb_bundle *bundle; struct gb_connection *connection; struct gb_connection *data_connection; + struct mutex mutex; + enum gb_camera_state state; struct { struct dentry *root; @@ -300,7 +308,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, { struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; - unsigned int nstreams = *num_streams; unsigned int i; size_t req_size; @@ -385,6 +392,11 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto done; } + if (gcam->state == GB_CAMERA_STATE_CONFIGURED) { + gb_camera_teardown_data_connection(gcam); + gcam->state = GB_CAMERA_STATE_UNCONFIGURED; + } + if (resp->num_streams) { ret = gb_camera_setup_data_connection(gcam, resp, csi_params); if (ret < 0) { @@ -394,8 +406,8 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, req, req_size, resp, resp_size); goto done; } - } else { - gb_camera_teardown_data_connection(gcam); + + gcam->state = GB_CAMERA_STATE_CONFIGURED; } *flags = resp->flags; @@ -1039,9 +1051,11 @@ static int gb_camera_probe(struct gb_bundle *bundle, if (!gcam) return -ENOMEM; - gcam->bundle = bundle; mutex_init(&gcam->mutex); + gcam->bundle = bundle; + gcam->state = GB_CAMERA_STATE_UNCONFIGURED; + conn = gb_connection_create(bundle, mgmt_cport_id, gb_camera_request_handler); if (IS_ERR(conn)) { -- cgit v1.2.3-59-g8ed1b From 9120b9060bca4528d13de5c4e6522e6a78110704 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Date: Tue, 14 Jun 2016 15:59:25 +0300 Subject: greybus: camera: Create and destroy data connection on demand Creating the data connection at probe time makes it impossible to support multiple inserted camera modules as they would all try to establish a data connection to the same CPort on the AP side. Create and destroy the data connection when configuring the streams instead, as a single module can be streaming at a time. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Tested-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 60 +++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index db7cdcee1c53..0062f483fc2e 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -42,6 +42,7 @@ enum gb_camera_state { * struct gb_camera - A Greybus Camera Device * @connection: the greybus connection for camera management * @data_connection: the greybus connection for camera data + * @data_cport_id: the data CPort ID on the module side * @mutex: protects the connection and state fields * @state: the current module state * @debugfs: debugfs entries for camera protocol operations testing @@ -51,6 +52,7 @@ struct gb_camera { struct gb_bundle *bundle; struct gb_connection *connection; struct gb_connection *data_connection; + u16 data_cport_id; struct mutex mutex; enum gb_camera_state state; @@ -196,12 +198,30 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam, struct gb_camera_csi_params *csi_params) { struct ap_csi_config_request csi_cfg; + struct gb_connection *conn; int ret; + /* + * Create the data connection between the camera module data CPort and + * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. + */ + conn = gb_connection_create_offloaded(gcam->bundle, gcam->data_cport_id, + GB_CONNECTION_FLAG_NO_FLOWCTRL | + GB_CONNECTION_FLAG_CDSI1); + if (IS_ERR(conn)) + return PTR_ERR(conn); + + gcam->data_connection = conn; + gb_connection_set_data(conn, gcam); + + ret = gb_connection_enable(conn); + if (ret) + goto error_conn_destroy; + /* Set the UniPro link to high speed mode. */ ret = gb_camera_set_power_mode(gcam, true); if (ret < 0) - return ret; + goto error_conn_disable; /* * Configure the APB1 CSI transmitter using the lines count reported by @@ -224,8 +244,7 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam, if (ret < 0) { gcam_err(gcam, "failed to start the CSI transmitter\n"); - gb_camera_set_power_mode(gcam, false); - return ret; + goto error_power; } if (csi_params) { @@ -236,6 +255,15 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam, } return 0; + +error_power: + gb_camera_set_power_mode(gcam, false); +error_conn_disable: + gb_connection_disable(gcam->data_connection); +error_conn_destroy: + gb_connection_destroy(gcam->data_connection); + gcam->data_connection = NULL; + return ret; } static void gb_camera_teardown_data_connection(struct gb_camera *gcam) @@ -256,6 +284,11 @@ static void gb_camera_teardown_data_connection(struct gb_camera *gcam) /* Set the UniPro link to low speed mode. */ gb_camera_set_power_mode(gcam, false); + + /* Destroy the data connection. */ + gb_connection_disable(gcam->data_connection); + gb_connection_destroy(gcam->data_connection); + gcam->data_connection = NULL; } /* ----------------------------------------------------------------------------- @@ -990,13 +1023,13 @@ static void gb_camera_cleanup(struct gb_camera *gcam) { gb_camera_debugfs_cleanup(gcam); + mutex_lock(&gcam->mutex); if (gcam->data_connection) { gb_connection_disable(gcam->data_connection); gb_connection_destroy(gcam->data_connection); gcam->data_connection = NULL; } - mutex_lock(&gcam->mutex); if (gcam->connection) { gb_connection_disable(gcam->connection); gb_connection_destroy(gcam->connection); @@ -1055,6 +1088,7 @@ static int gb_camera_probe(struct gb_bundle *bundle, gcam->bundle = bundle; gcam->state = GB_CAMERA_STATE_UNCONFIGURED; + gcam->data_cport_id = data_cport_id; conn = gb_connection_create(bundle, mgmt_cport_id, gb_camera_request_handler); @@ -1066,24 +1100,6 @@ static int gb_camera_probe(struct gb_bundle *bundle, gcam->connection = conn; gb_connection_set_data(conn, gcam); - ret = gb_connection_enable(conn); - if (ret) - goto error; - - /* - * Create the data connection between the camera module data CPort and - * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. - */ - conn = gb_connection_create_offloaded(bundle, data_cport_id, - GB_CONNECTION_FLAG_NO_FLOWCTRL | - GB_CONNECTION_FLAG_CDSI1); - if (IS_ERR(conn)) { - ret = PTR_ERR(conn); - goto error; - } - gcam->data_connection = conn; - gb_connection_set_data(conn, gcam); - ret = gb_connection_enable(conn); if (ret) goto error; -- cgit v1.2.3-59-g8ed1b From 880bc0a4afa66e36f62e5687bef464a751781d55 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <hiremath_vaibhav@projectara.com> Date: Wed, 15 Jun 2016 10:23:23 +0530 Subject: greybus: svc: Pass the correct pointer to input_free_device() In gb_svc_input_create() fn, on failure, wrong pointer was being passed to input_free_device(). Correct it. svc->input gets initialized only on successful return of this fn, so it is absolutely wrong to pass svc->input to input_free_device(). Testing Done: Tested on EVT2.0 platform. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index e316f08ab756..48d07a91f01f 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -1352,7 +1352,7 @@ static struct input_dev *gb_svc_input_create(struct gb_svc *svc) return input_dev; err_free_input: - input_free_device(svc->input); + input_free_device(input_dev); return ERR_PTR(-ENOMEM); } -- cgit v1.2.3-59-g8ed1b From 42830f7f63c7db6422467d6c5c4480b2948b63ef Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 11 Jun 2016 08:01:01 +0530 Subject: greybus: manifest: Disallow reuse of control cport We should be checking if any of the bundles contains a CPort with its id set to the special value of '0', which is reserved for control CPort. Discard the bundle in that case. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/manifest.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/greybus/manifest.c b/drivers/staging/greybus/manifest.c index 529a984db992..3d1592fc94ea 100644 --- a/drivers/staging/greybus/manifest.c +++ b/drivers/staging/greybus/manifest.c @@ -250,6 +250,13 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) if (cport_id > CPORT_ID_MAX) goto exit; + /* Nothing else should have its cport_id as control cport id */ + if (cport_id == GB_CONTROL_CPORT_ID) { + dev_err(&bundle->dev, "invalid cport id found (%02u)\n", + cport_id); + goto exit; + } + /* * Found one, move it to our temporary list after checking for * duplicates. -- cgit v1.2.3-59-g8ed1b From df124299d1d3e64175c79f83b4ecf2710d202e49 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 16 Jun 2016 10:03:57 +0530 Subject: greybus: firmware: Add license and copyright header to application Add license and copyright header to the firmware.c test application. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/firmware/firmware.c | 53 +++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Documentation/firmware/firmware.c b/drivers/staging/greybus/Documentation/firmware/firmware.c index e36786013ead..4a3782e07d71 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware.c +++ b/drivers/staging/greybus/Documentation/firmware/firmware.c @@ -1,4 +1,55 @@ -/* Sample code to test firmware-management protocol */ +/* + * Sample code to test firmware-management protocol + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program 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 version 2 for more details. + * + * BSD LICENSE + * + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. or Linaro Ltd. 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 COPYRIGHT HOLDERS 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 GOOGLE INC. OR + * LINARO LTD. 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 <stdio.h> #include <string.h> -- cgit v1.2.3-59-g8ed1b From c9e8f893eeef4f0e2d10333aed2d175e50a56dab Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 16 Jun 2016 13:42:14 +0100 Subject: greybus: timesync: Do not hold mutex on cancel_delayed_work_sync There is a scenario where gb_timesync_svc_remove() can run, attain a mutex and call cancel_delayed_work_sync(). In the meantime a worker may already be running and trying to attain the same mutex but will never do so as the gb_timesync_svc_remove() path is holding the mutex and waiting on the delayed_work_sync() to complete - leading to deadlock. This patch addresses by calling the cancel_delayed_work_sync() before locking the relevant mutex. Reported-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Tested-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index a029fa085af6..a9b62026a201 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -1065,6 +1065,8 @@ void gb_timesync_svc_remove(struct gb_svc *svc) if (!timesync_svc) goto done; + cancel_delayed_work_sync(×ync_svc->delayed_work); + mutex_lock(×ync_svc->mutex); gb_timesync_teardown(timesync_svc); @@ -1079,7 +1081,6 @@ void gb_timesync_svc_remove(struct gb_svc *svc) gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INVALID); debugfs_remove(timesync_svc->frame_ktime_dentry); debugfs_remove(timesync_svc->frame_time_dentry); - cancel_delayed_work_sync(×ync_svc->delayed_work); destroy_workqueue(timesync_svc->work_queue); list_del(×ync_svc->list); -- cgit v1.2.3-59-g8ed1b From 0cc3bc6930d02ff5083b7b7bc18ca53299bb383a Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 16 Jun 2016 13:42:15 +0100 Subject: greybus: timesync: Fix transitions to the INACTIVE state Analysing a backtrace associated with the current EHCI runtime suspend code has highlighted several places where its perfectly valid to make a transition to GB_TIMESYNC_STATE_INACTIVE when not already in the GB_TIMESYNC_STATE_INIT state, for example failure to issue a TimeSync enable command to the SVC can and should legitimately call gb_timesync_teardown() - at this point the state will be GB_TIMESYNC_STATE_WAIT_SVC and it's legitimate and desirable to transition to the INACTIVE state in this case. This patch fixes by removing the restrictive and incorrect restriction on the transition to INACTIVE only being valid when state == GB_TIMESYNC_STATE_INIT. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Tested-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index a9b62026a201..cc08b090f1b3 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -157,10 +157,8 @@ static void gb_timesync_set_state(struct gb_timesync_svc *timesync_svc, wake_up(×ync_svc->wait_queue); break; case GB_TIMESYNC_STATE_INACTIVE: - if (timesync_svc->state != GB_TIMESYNC_STATE_INIT) { - timesync_svc->state = state; - wake_up(×ync_svc->wait_queue); - } + timesync_svc->state = state; + wake_up(×ync_svc->wait_queue); break; case GB_TIMESYNC_STATE_INIT: if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) { -- cgit v1.2.3-59-g8ed1b From 93dbb97a3e2455cdb5efee17a497758ece4b8ed7 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 16 Jun 2016 13:42:16 +0100 Subject: greybus: timesync: Rework timesync removal serialization logic We need to make sure we adequately cancel and quiesce any scheduled TimeSync synchronization operations in the case of greybus.ko being yanked out of memory i.e. when doing an EHCI runtime suspend or just a plain rmmod. The scenario is a new TimeSync sync operation has been scheduled. Next gb_timesync_svc_remove() runs. In this case we should terminate any scheduled work, terminate our ktime tracking timer and state transition to GB_TIMESYNC_STATE_INVALID to ensure no other context may schedule any new TimeSync operations. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index cc08b090f1b3..b9b29f5f5c8f 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -1067,8 +1067,9 @@ void gb_timesync_svc_remove(struct gb_svc *svc) mutex_lock(×ync_svc->mutex); - gb_timesync_teardown(timesync_svc); + gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INVALID); del_timer_sync(×ync_svc->ktime_timer); + gb_timesync_teardown(timesync_svc); gb_timesync_hd_remove(timesync_svc, svc->hd); list_for_each_entry_safe(timesync_interface, next, @@ -1076,7 +1077,6 @@ void gb_timesync_svc_remove(struct gb_svc *svc) list_del(×ync_interface->list); kfree(timesync_interface); } - gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INVALID); debugfs_remove(timesync_svc->frame_ktime_dentry); debugfs_remove(timesync_svc->frame_time_dentry); destroy_workqueue(timesync_svc->work_queue); -- cgit v1.2.3-59-g8ed1b From 4ee48a5ecb7a3af272ac2c6cd6a0865b95a3d5f2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 15 Jun 2016 15:58:22 +0530 Subject: greybus: firmware: prefix char-device with 'gb-' This will make it consistent with any other character devices we have for greybus and let us identify greybus character devices easily. Compiled tested only. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/firmware/firmware-management | 18 +++++++++--------- .../staging/greybus/Documentation/firmware/firmware.c | 2 +- drivers/staging/greybus/fw-management.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/greybus/Documentation/firmware/firmware-management b/drivers/staging/greybus/Documentation/firmware/firmware-management index bafe700f9901..79977396ef00 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware-management +++ b/drivers/staging/greybus/Documentation/firmware/firmware-management @@ -49,14 +49,14 @@ Sysfs Interfaces - Firmware Management The Firmware Management Protocol interacts with Userspace using the character device interface. The character device will be present in /dev/ directory -and will be named fw-mgmt-<N>. The number <N> is assigned at runtime. +and will be named gb-fw-mgmt-<N>. The number <N> is assigned at runtime. Identifying the Character Device ================================ -There can be multiple devices present in /dev/ directory with name fw-mgmt-N and -user first needs to identify the character device used for firmware-management -for a particular interface. +There can be multiple devices present in /dev/ directory with name gb-fw-mgmt-N +and user first needs to identify the character device used for +firmware-management for a particular interface. The Firmware Management core creates a device of class 'gb_fw_mgmt', which shall be used by the user to identify the right character device for it. The class @@ -64,17 +64,17 @@ device is created within the Bundle directory for a particular Interface. For example this is how the class-device can be present: -/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_fw_mgmt/fw-mgmt-0 +/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_fw_mgmt/gb-fw-mgmt-0 -The last name in this path: fw-mgmt-0 is precisely the name of the char device -and so the device in this case will be: +The last name in this path: gb-fw-mgmt-0 is precisely the name of the char +device and so the device in this case will be: -/dev/fw-mgmt-0. +/dev/gb-fw-mgmt-0. Operations on the Char device ============================= -The Character device (fw-mgmt-0 in example) can be opened by the userspace +The Character device (gb-fw-mgmt-0 in example) can be opened by the userspace application and it can perform various 'ioctl' operations on the device. The device doesn't support any read/write operations. diff --git a/drivers/staging/greybus/Documentation/firmware/firmware.c b/drivers/staging/greybus/Documentation/firmware/firmware.c index 4a3782e07d71..3c305f774939 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware.c +++ b/drivers/staging/greybus/Documentation/firmware/firmware.c @@ -74,7 +74,7 @@ int main(int argc, char *argv[]) /* Make sure arguments are correct */ if (argc != 2) { - printf("\nUsage: ./firmware <Path of the fw-mgmt-X dev>\n"); + printf("\nUsage: ./firmware <Path of the gb-fw-mgmt-X dev>\n"); return 0; } diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index c9c28fc15492..ccd9d7c91416 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -599,7 +599,7 @@ int gb_fw_mgmt_connection_init(struct gb_connection *connection) /* Add a soft link to the previously added char-dev within the bundle */ fw_mgmt->class_device = device_create(fw_mgmt_class, fw_mgmt->parent, fw_mgmt->dev_num, NULL, - "fw-mgmt-%d", minor); + "gb-fw-mgmt-%d", minor); if (IS_ERR(fw_mgmt->class_device)) { ret = PTR_ERR(fw_mgmt->class_device); goto err_del_cdev; -- cgit v1.2.3-59-g8ed1b From 0698be0281e915ac42235b711f35465e8e14785b Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 15 Jun 2016 08:25:56 +0530 Subject: greybus: connection: add trace events before disabling connection This is what we are doing elsewhere: - Send enable/create trace events after enabling/creating stuff. - Send disable/remove trace events before disabling/removing stuff. This wasn't followed in a same way while disabling connections. Fix it. Compile tested. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3cfc4dd134d8..3a17db91a167 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -790,6 +790,8 @@ void gb_connection_disable(struct gb_connection *connection) if (connection->state == GB_CONNECTION_STATE_DISABLED) goto out_unlock; + trace_gb_connection_disable(connection); + gb_connection_control_disconnecting(connection); spin_lock_irq(&connection->lock); @@ -808,8 +810,6 @@ void gb_connection_disable(struct gb_connection *connection) connection->state = GB_CONNECTION_STATE_DISABLED; - trace_gb_connection_disable(connection); - /* control-connection tear down is deferred when mode switching */ if (!connection->mode_switch) { gb_connection_svc_connection_destroy(connection); @@ -829,6 +829,8 @@ void gb_connection_disable_forced(struct gb_connection *connection) if (connection->state == GB_CONNECTION_STATE_DISABLED) goto out_unlock; + trace_gb_connection_disable(connection); + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; gb_connection_cancel_operations(connection, -ESHUTDOWN); @@ -839,8 +841,6 @@ void gb_connection_disable_forced(struct gb_connection *connection) gb_connection_svc_connection_destroy(connection); gb_connection_hd_cport_disable(connection); - trace_gb_connection_disable(connection); - out_unlock: mutex_unlock(&connection->mutex); } -- cgit v1.2.3-59-g8ed1b From e3b090ea954ed6f8de295511b4e533e2ef425a0f Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Tue, 21 Jun 2016 16:56:16 +0100 Subject: greybus: sdio: avoid extra memory operation at data transfer Right now greybus sdio uses the greybus operation_sync to transfer data, this will imply an extra memcpy at greybus core that we can avoid. Also with this change we remove the need for an extra buffer to store intermediate copy. So, let us create the operation and do the memory operations in the sdio driver. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/sdio.c | 80 ++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index a270517c90c8..8d2de7d96eb4 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -24,7 +24,6 @@ struct gb_sdio_host { struct mmc_request *mrq; struct mutex lock; /* lock for this host */ size_t data_max; - void *xfer_buffer; spinlock_t xfer; /* lock to cancel ongoing transfer */ bool xfer_stop; struct workqueue_struct *mrq_workqueue; @@ -245,7 +244,8 @@ static int _gb_sdio_send(struct gb_sdio_host *host, struct mmc_data *data, size_t len, u16 nblocks, off_t skip) { struct gb_sdio_transfer_request *request; - struct gb_sdio_transfer_response response; + struct gb_sdio_transfer_response *response; + struct gb_operation *operation; struct scatterlist *sg = data->sg; unsigned int sg_len = data->sg_len; size_t copied; @@ -255,39 +255,51 @@ static int _gb_sdio_send(struct gb_sdio_host *host, struct mmc_data *data, WARN_ON(len > host->data_max); - request = host->xfer_buffer; + operation = gb_operation_create(host->connection, GB_SDIO_TYPE_TRANSFER, + len + sizeof(*request), + sizeof(*response), GFP_KERNEL); + if (!operation) + return -ENOMEM; + + request = operation->request->payload; request->data_flags = (data->flags >> 8); request->data_blocks = cpu_to_le16(nblocks); request->data_blksz = cpu_to_le16(data->blksz); copied = sg_pcopy_to_buffer(sg, sg_len, &request->data[0], len, skip); - if (copied != len) - return -EINVAL; + if (copied != len) { + ret = -EINVAL; + goto err_put_operation; + } - ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_TRANSFER, - request, len + sizeof(*request), - &response, sizeof(response)); + ret = gb_operation_request_send_sync(operation); if (ret < 0) - return ret; + goto err_put_operation; - send_blocks = le16_to_cpu(response.data_blocks); - send_blksz = le16_to_cpu(response.data_blksz); + response = operation->response->payload; + + send_blocks = le16_to_cpu(response->data_blocks); + send_blksz = le16_to_cpu(response->data_blksz); if (len != send_blksz * send_blocks) { dev_err(mmc_dev(host->mmc), "send: size received: %zu != %d\n", len, send_blksz * send_blocks); - return -EINVAL; + ret = -EINVAL; } +err_put_operation: + gb_operation_put(operation); + return ret; } static int _gb_sdio_recv(struct gb_sdio_host *host, struct mmc_data *data, size_t len, u16 nblocks, off_t skip) { - struct gb_sdio_transfer_request request; + struct gb_sdio_transfer_request *request; struct gb_sdio_transfer_response *response; + struct gb_operation *operation; struct scatterlist *sg = data->sg; unsigned int sg_len = data->sg_len; size_t copied; @@ -297,33 +309,41 @@ static int _gb_sdio_recv(struct gb_sdio_host *host, struct mmc_data *data, WARN_ON(len > host->data_max); - request.data_flags = (data->flags >> 8); - request.data_blocks = cpu_to_le16(nblocks); - request.data_blksz = cpu_to_le16(data->blksz); + operation = gb_operation_create(host->connection, GB_SDIO_TYPE_TRANSFER, + sizeof(*request), + len + sizeof(*response), GFP_KERNEL); + if (!operation) + return -ENOMEM; - response = host->xfer_buffer; + request = operation->request->payload; + request->data_flags = (data->flags >> 8); + request->data_blocks = cpu_to_le16(nblocks); + request->data_blksz = cpu_to_le16(data->blksz); - ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_TRANSFER, - &request, sizeof(request), response, len + - sizeof(*response)); + ret = gb_operation_request_send_sync(operation); if (ret < 0) - return ret; + goto err_put_operation; + response = operation->response->payload; recv_blocks = le16_to_cpu(response->data_blocks); recv_blksz = le16_to_cpu(response->data_blksz); if (len != recv_blksz * recv_blocks) { dev_err(mmc_dev(host->mmc), "recv: size received: %d != %zu\n", recv_blksz * recv_blocks, len); - return -EINVAL; + ret = -EINVAL; + goto err_put_operation; } copied = sg_pcopy_from_buffer(sg, sg_len, &response->data[0], len, skip); if (copied != len) - return -EINVAL; + ret = -EINVAL; - return 0; +err_put_operation: + gb_operation_put(operation); + + return ret; } static int gb_sdio_transfer(struct gb_sdio_host *host, struct mmc_data *data) @@ -720,7 +740,6 @@ static int gb_sdio_probe(struct gbphy_device *gbphy_dev, struct gb_connection *connection; struct mmc_host *mmc; struct gb_sdio_host *host; - size_t max_buffer; int ret = 0; mmc = mmc_alloc_host(sizeof(*host), &gbphy_dev->dev); @@ -760,19 +779,13 @@ static int gb_sdio_probe(struct gbphy_device *gbphy_dev, mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - max_buffer = gb_operation_get_payload_size_max(host->connection); - host->xfer_buffer = kzalloc(max_buffer, GFP_KERNEL); - if (!host->xfer_buffer) { - ret = -ENOMEM; - goto exit_connection_disable; - } mutex_init(&host->lock); spin_lock_init(&host->xfer); host->mrq_workqueue = alloc_workqueue("mmc-%s", 0, 1, dev_name(&gbphy_dev->dev)); if (!host->mrq_workqueue) { ret = -ENOMEM; - goto exit_buf_free; + goto exit_connection_disable; } INIT_WORK(&host->mrqwork, gb_sdio_mrq_work); @@ -791,8 +804,6 @@ static int gb_sdio_probe(struct gbphy_device *gbphy_dev, exit_wq_destroy: destroy_workqueue(host->mrq_workqueue); -exit_buf_free: - kfree(host->xfer_buffer); exit_connection_disable: gb_connection_disable(connection); exit_connection_destroy: @@ -821,7 +832,6 @@ static void gb_sdio_remove(struct gbphy_device *gbphy_dev) mmc_remove_host(mmc); gb_connection_disable(connection); gb_connection_destroy(connection); - kfree(host->xfer_buffer); mmc_free_host(mmc); } -- cgit v1.2.3-59-g8ed1b From 2d533cf16bffc579b03f8e376b2f01f09a092aeb Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Tue, 21 Jun 2016 23:28:02 +0530 Subject: greybus: ratelimit errors usually seen on unipro_reset It is believed that excessive serial messages from greybus on suspend/resume is leading to watchdog bite. There is still discussion going on whether ratelimiting prints would really fix anything, except it may reduce traffic on serial console and probably bring out real issues in the front. So in order to meet the alpha requirement, we all decided to get ratelimit change "as a TEMP fix" and decide later whether we should revert back once we get proper suspend/resume implementation. Please follow the discussion on Jira card SW-6261. Testing Done: Build tested against ara/main branch. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Konstantin Buhchev <buhchev_konstantin@projectara.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 2 +- drivers/staging/greybus/operation.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index bdf502493e31..426f83e1fda6 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -473,7 +473,7 @@ static int message_send(struct gb_host_device *hd, u16 cport_id, retval = usb_submit_urb(urb, gfp_mask); if (retval) { - dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); + dev_err_ratelimited(&udev->dev, "failed to submit out-urb: %d\n", retval); spin_lock_irqsave(&es2->cport_out_urb_lock, flags); message->hcpriv = NULL; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 659e84b10c7f..136707002b90 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -1127,7 +1127,7 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { - dev_err(&connection->hd->dev, + dev_err_ratelimited(&connection->hd->dev, "%s: synchronous operation of type 0x%02x failed: %d\n", connection->name, type, ret); } else { -- cgit v1.2.3-59-g8ed1b From c13a206f7e44d8bb207512dfe58677674e514833 Mon Sep 17 00:00:00 2001 From: Michael Scott <michael.scott@linaro.org> Date: Tue, 21 Jun 2016 16:41:46 -0700 Subject: greybus: adjust kernel version check for led_sysfs_is_disabled When building greybus against a 3.18 kernel the following error is generated: light.c:205:9: error: implicit declaration of function 'led_sysfs_is_disabled' [-Werror=implicit-function-declaration] led_sysfs_is_disabled was not added until 3.19 kernel cycle Verification: https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/include/linux/leds.h?h=linux-3.18.y (no function led_sysfs_is_disabled defined here) Testing Done: - Successfully built greybus for 3.18 kernel Signed-off-by: Michael Scott <michael.scott@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 2a541fa5f5bc..98e3179cc00d 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -276,7 +276,7 @@ static inline size_t sg_pcopy_from_buffer(struct scatterlist *sgl, #define LED_HAVE_SET_BLOCKING #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) /* * From this version upper it was introduced the possibility to disable led * sysfs entries to handle control of the led device to v4l2, which was -- cgit v1.2.3-59-g8ed1b From 05e3095563670ab9188eab2c38808e9fd1f3a99d Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 23 Jun 2016 23:22:15 +0530 Subject: greybus: Revert "greybus: ratelimit errors usually seen on unipro_reset" This reverts commit 9b891f4fda8dfd6c1d8dc16479c5f6d418a9ccc7. We discussed this over the other thread, [PATCH 0/2] Improve watchdog's implementation a bit, and decided that we shouldn't be trying to hide the watchdog reboot problem by using such patches, rather we should make sure they occur consistently so that the real problem can be fixed. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 2 +- drivers/staging/greybus/operation.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 426f83e1fda6..bdf502493e31 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -473,7 +473,7 @@ static int message_send(struct gb_host_device *hd, u16 cport_id, retval = usb_submit_urb(urb, gfp_mask); if (retval) { - dev_err_ratelimited(&udev->dev, "failed to submit out-urb: %d\n", retval); + dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); spin_lock_irqsave(&es2->cport_out_urb_lock, flags); message->hcpriv = NULL; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 136707002b90..659e84b10c7f 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -1127,7 +1127,7 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { - dev_err_ratelimited(&connection->hd->dev, + dev_err(&connection->hd->dev, "%s: synchronous operation of type 0x%02x failed: %d\n", connection->name, type, ret); } else { -- cgit v1.2.3-59-g8ed1b From 5e2b63915cb6f1e86f9515342cfcfb28cd82c5b0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 23 Jun 2016 23:23:06 +0530 Subject: greybus: don't use spin_lock_irq() spin_[un]lock_irq() routines should be used carefully as they things can go wrong, if they are mixed with spin_lock_irqsave() or other variants. The main problem is that spin_[un]lock_irq() routines doesn't check if the IRQs are already disabled/enabled on the local CPU and so spin_unlock_irq() will forcefully enable interrupts for example. This may not work well, if some other code was relying on interrupts being disabled. Use spin_lock_irqsave() and spin_unlock_restore() instead. This patch doesn't claim that it fixes the JIRA completely, but the issue was harder to reproduce for some iterations after this, which was quite easy to reproduce earlier on. Tested on EVT 2.0 with lots of debug patches to kernel and greybus. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Jeffrey Carlyle <jcarlyle@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 64 +++++++++++++++++++++--------------- drivers/staging/greybus/es2.c | 9 ++--- drivers/staging/greybus/uart.c | 5 +-- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3a17db91a167..0132e36c5109 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -150,6 +150,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, unsigned long flags) { struct gb_connection *connection; + unsigned long irqflags; int ret; mutex_lock(&gb_connection_mutex); @@ -200,7 +201,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, gb_connection_init_name(connection); - spin_lock_irq(&gb_connections_lock); + spin_lock_irqsave(&gb_connections_lock, irqflags); list_add(&connection->hd_links, &hd->connections); if (bundle) @@ -208,7 +209,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, else INIT_LIST_HEAD(&connection->bundle_links); - spin_unlock_irq(&gb_connections_lock); + spin_unlock_irqrestore(&gb_connections_lock, irqflags); mutex_unlock(&gb_connection_mutex); @@ -571,7 +572,7 @@ static int gb_connection_ping(struct gb_connection *connection) * DISCONNECTING. */ static void gb_connection_cancel_operations(struct gb_connection *connection, - int errno) + int errno, unsigned long flags) __must_hold(&connection->lock) { struct gb_operation *operation; @@ -580,7 +581,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, operation = list_last_entry(&connection->operations, struct gb_operation, links); gb_operation_get(operation); - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, flags); if (gb_operation_is_incoming(operation)) gb_operation_cancel_incoming(operation, errno); @@ -589,7 +590,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, gb_operation_put(operation); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); } } @@ -600,7 +601,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, */ static void gb_connection_flush_incoming_operations(struct gb_connection *connection, - int errno) + int errno, unsigned long flags) __must_hold(&connection->lock) { struct gb_operation *operation; @@ -620,13 +621,13 @@ gb_connection_flush_incoming_operations(struct gb_connection *connection, if (!incoming) break; - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, flags); /* FIXME: flush, not cancel? */ gb_operation_cancel_incoming(operation, errno); gb_operation_put(operation); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); } } @@ -642,6 +643,7 @@ gb_connection_flush_incoming_operations(struct gb_connection *connection, */ static int _gb_connection_enable(struct gb_connection *connection, bool rx) { + unsigned long flags; int ret; /* Handle ENABLED_TX -> ENABLED transitions. */ @@ -649,9 +651,9 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) if (!(connection->handler && rx)) return 0; - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); connection->state = GB_CONNECTION_STATE_ENABLED; - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, flags); return 0; } @@ -668,12 +670,12 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) if (ret) goto err_svc_connection_destroy; - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); if (connection->handler && rx) connection->state = GB_CONNECTION_STATE_ENABLED; else connection->state = GB_CONNECTION_STATE_ENABLED_TX; - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, flags); ret = gb_connection_control_connected(connection); if (ret) @@ -684,10 +686,10 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) err_control_disconnecting: gb_connection_control_disconnecting(connection); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); connection->state = GB_CONNECTION_STATE_DISCONNECTING; - gb_connection_cancel_operations(connection, -ESHUTDOWN); - spin_unlock_irq(&connection->lock); + gb_connection_cancel_operations(connection, -ESHUTDOWN, flags); + spin_unlock_irqrestore(&connection->lock, flags); /* Transmit queue should already be empty. */ gb_connection_hd_cport_flush(connection); @@ -753,16 +755,18 @@ EXPORT_SYMBOL_GPL(gb_connection_enable_tx); void gb_connection_disable_rx(struct gb_connection *connection) { + unsigned long flags; + mutex_lock(&connection->mutex); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); if (connection->state != GB_CONNECTION_STATE_ENABLED) { - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, flags); goto out_unlock; } connection->state = GB_CONNECTION_STATE_ENABLED_TX; - gb_connection_flush_incoming_operations(connection, -ESHUTDOWN); - spin_unlock_irq(&connection->lock); + gb_connection_flush_incoming_operations(connection, -ESHUTDOWN, flags); + spin_unlock_irqrestore(&connection->lock, flags); trace_gb_connection_disable(connection); @@ -785,6 +789,8 @@ void gb_connection_mode_switch_complete(struct gb_connection *connection) void gb_connection_disable(struct gb_connection *connection) { + unsigned long flags; + mutex_lock(&connection->mutex); if (connection->state == GB_CONNECTION_STATE_DISABLED) @@ -794,10 +800,10 @@ void gb_connection_disable(struct gb_connection *connection) gb_connection_control_disconnecting(connection); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); connection->state = GB_CONNECTION_STATE_DISCONNECTING; - gb_connection_cancel_operations(connection, -ESHUTDOWN); - spin_unlock_irq(&connection->lock); + gb_connection_cancel_operations(connection, -ESHUTDOWN, flags); + spin_unlock_irqrestore(&connection->lock, flags); gb_connection_hd_cport_flush(connection); @@ -824,6 +830,8 @@ EXPORT_SYMBOL_GPL(gb_connection_disable); /* Disable a connection without communicating with the remote end. */ void gb_connection_disable_forced(struct gb_connection *connection) { + unsigned long flags; + mutex_lock(&connection->mutex); if (connection->state == GB_CONNECTION_STATE_DISABLED) @@ -831,10 +839,10 @@ void gb_connection_disable_forced(struct gb_connection *connection) trace_gb_connection_disable(connection); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); connection->state = GB_CONNECTION_STATE_DISABLED; - gb_connection_cancel_operations(connection, -ESHUTDOWN); - spin_unlock_irq(&connection->lock); + gb_connection_cancel_operations(connection, -ESHUTDOWN, flags); + spin_unlock_irqrestore(&connection->lock, flags); gb_connection_hd_cport_flush(connection); gb_connection_hd_cport_features_disable(connection); @@ -849,6 +857,8 @@ EXPORT_SYMBOL_GPL(gb_connection_disable_forced); /* Caller must have disabled the connection before destroying it. */ void gb_connection_destroy(struct gb_connection *connection) { + unsigned long flags; + if (!connection) return; @@ -857,10 +867,10 @@ void gb_connection_destroy(struct gb_connection *connection) mutex_lock(&gb_connection_mutex); - spin_lock_irq(&gb_connections_lock); + spin_lock_irqsave(&gb_connections_lock, flags); list_del(&connection->bundle_links); list_del(&connection->hd_links); - spin_unlock_irq(&gb_connections_lock); + spin_unlock_irqrestore(&gb_connections_lock, flags); destroy_workqueue(connection->wq); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index bdf502493e31..89fe7641cd24 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -496,11 +496,12 @@ static void message_cancel(struct gb_message *message) struct gb_host_device *hd = message->operation->connection->hd; struct es2_ap_dev *es2 = hd_to_es2(hd); struct urb *urb; + unsigned long flags; int i; might_sleep(); - spin_lock_irq(&es2->cport_out_urb_lock); + spin_lock_irqsave(&es2->cport_out_urb_lock, flags); urb = message->hcpriv; /* Prevent dynamically allocated urb from being deallocated. */ @@ -513,14 +514,14 @@ static void message_cancel(struct gb_message *message) break; } } - spin_unlock_irq(&es2->cport_out_urb_lock); + spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); usb_kill_urb(urb); if (i < NUM_CPORT_OUT_URB) { - spin_lock_irq(&es2->cport_out_urb_lock); + spin_lock_irqsave(&es2->cport_out_urb_lock, flags); es2->cport_out_urb_cancelled[i] = false; - spin_unlock_irq(&es2->cport_out_urb_lock); + spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); } usb_free_urb(urb); diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 6260569b2f25..80896385c1cb 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -674,6 +674,7 @@ static int set_serial_info(struct gb_tty *gb_tty, static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) { int retval = 0; + unsigned long flags; DECLARE_WAITQUEUE(wait, current); struct async_icount old; struct async_icount new; @@ -682,11 +683,11 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) return -EINVAL; do { - spin_lock_irq(&gb_tty->read_lock); + spin_lock_irqsave(&gb_tty->read_lock, flags); old = gb_tty->oldcount; new = gb_tty->iocount; gb_tty->oldcount = new; - spin_unlock_irq(&gb_tty->read_lock); + spin_unlock_irqrestore(&gb_tty->read_lock, flags); if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) break; -- cgit v1.2.3-59-g8ed1b From 1211915127c152a86e68ea35770c9e2524020d4f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Thu, 23 Jun 2016 16:26:00 +0100 Subject: greybus: timesync: Enforce TimeSync locks as subordinate to Interface locks gb_timesync_svc_teardown() is called from gb_timesync_svc_del() and issues a command to a remote Interface to switch off its timers. The lock ordering is TimeSync => Interface in this case. However gb_module_del() takes an Interface lock then calls gb_interface_del() => gb_timesync_svc_del() in this case the lock ordering is Interface => TimeSync. This patch fixes by removing the taking of the Interface mutex in gb_interface_timesync_do_something(). If an Interface is present in the TimeSync linked-list - it is by definition intf->enabled. Reported-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 50 +++++++------------------------------ 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 9b90209e8fe0..05d0020d3a8d 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -872,59 +872,27 @@ void gb_interface_disable(struct gb_interface *intf) intf->enabled = false; } -/* - * Enable TimeSync on an Interface control connection. - * - * Locking: Takes and releases the interface mutex. - */ +/* Enable TimeSync on an Interface control connection. */ int gb_interface_timesync_enable(struct gb_interface *intf, u8 count, u64 frame_time, u32 strobe_delay, u32 refclk) { - int ret = -ENODEV; - - mutex_lock(&intf->mutex); - if (intf->enabled) { - ret = gb_control_timesync_enable(intf->control, count, - frame_time, strobe_delay, - refclk); - } - mutex_unlock(&intf->mutex); - return ret; + return gb_control_timesync_enable(intf->control, count, + frame_time, strobe_delay, + refclk); } -/* - * Disable TimeSync on an Interface control connection. - * - * Locking: Takes and releases the interface mutex. - */ +/* Disable TimeSync on an Interface control connection. */ int gb_interface_timesync_disable(struct gb_interface *intf) { - int ret = -ENODEV; - - mutex_lock(&intf->mutex); - if (intf->enabled) - ret = gb_control_timesync_disable(intf->control); - mutex_unlock(&intf->mutex); - return ret; + return gb_control_timesync_disable(intf->control); } -/* - * Transmit the Authoritative FrameTime via an Interface control connection. - * - * Locking: Takes and releases the interface mutex. - */ +/* Transmit the Authoritative FrameTime via an Interface control connection. */ int gb_interface_timesync_authoritative(struct gb_interface *intf, u64 *frame_time) { - int ret = -ENODEV; - - mutex_lock(&intf->mutex); - if (intf->enabled) { - ret = gb_control_timesync_authoritative(intf->control, - frame_time); - } - mutex_unlock(&intf->mutex); - return ret; + return gb_control_timesync_authoritative(intf->control, + frame_time); } /* Register an interface. */ -- cgit v1.2.3-59-g8ed1b From 19cdabcf0ba92ddd87bdb86f8e3ceaae2dd6f8bb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 23 Jun 2016 14:20:02 -0700 Subject: greybus: Revert "greybus: don't use spin_lock_irq()" This reverts commit 469fbe5da0229edcb42aa08bef8e10feaa37e6d7. It isn't correct in places. Reported-by: Gjorgji Rosikopulos <rosikopulos_gjorgji@projectara.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 64 +++++++++++++++--------------------- drivers/staging/greybus/es2.c | 9 +++-- drivers/staging/greybus/uart.c | 5 ++- 3 files changed, 33 insertions(+), 45 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 0132e36c5109..3a17db91a167 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -150,7 +150,6 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, unsigned long flags) { struct gb_connection *connection; - unsigned long irqflags; int ret; mutex_lock(&gb_connection_mutex); @@ -201,7 +200,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, gb_connection_init_name(connection); - spin_lock_irqsave(&gb_connections_lock, irqflags); + spin_lock_irq(&gb_connections_lock); list_add(&connection->hd_links, &hd->connections); if (bundle) @@ -209,7 +208,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, else INIT_LIST_HEAD(&connection->bundle_links); - spin_unlock_irqrestore(&gb_connections_lock, irqflags); + spin_unlock_irq(&gb_connections_lock); mutex_unlock(&gb_connection_mutex); @@ -572,7 +571,7 @@ static int gb_connection_ping(struct gb_connection *connection) * DISCONNECTING. */ static void gb_connection_cancel_operations(struct gb_connection *connection, - int errno, unsigned long flags) + int errno) __must_hold(&connection->lock) { struct gb_operation *operation; @@ -581,7 +580,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, operation = list_last_entry(&connection->operations, struct gb_operation, links); gb_operation_get(operation); - spin_unlock_irqrestore(&connection->lock, flags); + spin_unlock_irq(&connection->lock); if (gb_operation_is_incoming(operation)) gb_operation_cancel_incoming(operation, errno); @@ -590,7 +589,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, gb_operation_put(operation); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); } } @@ -601,7 +600,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, */ static void gb_connection_flush_incoming_operations(struct gb_connection *connection, - int errno, unsigned long flags) + int errno) __must_hold(&connection->lock) { struct gb_operation *operation; @@ -621,13 +620,13 @@ gb_connection_flush_incoming_operations(struct gb_connection *connection, if (!incoming) break; - spin_unlock_irqrestore(&connection->lock, flags); + spin_unlock_irq(&connection->lock); /* FIXME: flush, not cancel? */ gb_operation_cancel_incoming(operation, errno); gb_operation_put(operation); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); } } @@ -643,7 +642,6 @@ gb_connection_flush_incoming_operations(struct gb_connection *connection, */ static int _gb_connection_enable(struct gb_connection *connection, bool rx) { - unsigned long flags; int ret; /* Handle ENABLED_TX -> ENABLED transitions. */ @@ -651,9 +649,9 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) if (!(connection->handler && rx)) return 0; - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_ENABLED; - spin_unlock_irqrestore(&connection->lock, flags); + spin_unlock_irq(&connection->lock); return 0; } @@ -670,12 +668,12 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) if (ret) goto err_svc_connection_destroy; - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); if (connection->handler && rx) connection->state = GB_CONNECTION_STATE_ENABLED; else connection->state = GB_CONNECTION_STATE_ENABLED_TX; - spin_unlock_irqrestore(&connection->lock, flags); + spin_unlock_irq(&connection->lock); ret = gb_connection_control_connected(connection); if (ret) @@ -686,10 +684,10 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) err_control_disconnecting: gb_connection_control_disconnecting(connection); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISCONNECTING; - gb_connection_cancel_operations(connection, -ESHUTDOWN, flags); - spin_unlock_irqrestore(&connection->lock, flags); + gb_connection_cancel_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); /* Transmit queue should already be empty. */ gb_connection_hd_cport_flush(connection); @@ -755,18 +753,16 @@ EXPORT_SYMBOL_GPL(gb_connection_enable_tx); void gb_connection_disable_rx(struct gb_connection *connection) { - unsigned long flags; - mutex_lock(&connection->mutex); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); if (connection->state != GB_CONNECTION_STATE_ENABLED) { - spin_unlock_irqrestore(&connection->lock, flags); + spin_unlock_irq(&connection->lock); goto out_unlock; } connection->state = GB_CONNECTION_STATE_ENABLED_TX; - gb_connection_flush_incoming_operations(connection, -ESHUTDOWN, flags); - spin_unlock_irqrestore(&connection->lock, flags); + gb_connection_flush_incoming_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); trace_gb_connection_disable(connection); @@ -789,8 +785,6 @@ void gb_connection_mode_switch_complete(struct gb_connection *connection) void gb_connection_disable(struct gb_connection *connection) { - unsigned long flags; - mutex_lock(&connection->mutex); if (connection->state == GB_CONNECTION_STATE_DISABLED) @@ -800,10 +794,10 @@ void gb_connection_disable(struct gb_connection *connection) gb_connection_control_disconnecting(connection); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISCONNECTING; - gb_connection_cancel_operations(connection, -ESHUTDOWN, flags); - spin_unlock_irqrestore(&connection->lock, flags); + gb_connection_cancel_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); gb_connection_hd_cport_flush(connection); @@ -830,8 +824,6 @@ EXPORT_SYMBOL_GPL(gb_connection_disable); /* Disable a connection without communicating with the remote end. */ void gb_connection_disable_forced(struct gb_connection *connection) { - unsigned long flags; - mutex_lock(&connection->mutex); if (connection->state == GB_CONNECTION_STATE_DISABLED) @@ -839,10 +831,10 @@ void gb_connection_disable_forced(struct gb_connection *connection) trace_gb_connection_disable(connection); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; - gb_connection_cancel_operations(connection, -ESHUTDOWN, flags); - spin_unlock_irqrestore(&connection->lock, flags); + gb_connection_cancel_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); gb_connection_hd_cport_flush(connection); gb_connection_hd_cport_features_disable(connection); @@ -857,8 +849,6 @@ EXPORT_SYMBOL_GPL(gb_connection_disable_forced); /* Caller must have disabled the connection before destroying it. */ void gb_connection_destroy(struct gb_connection *connection) { - unsigned long flags; - if (!connection) return; @@ -867,10 +857,10 @@ void gb_connection_destroy(struct gb_connection *connection) mutex_lock(&gb_connection_mutex); - spin_lock_irqsave(&gb_connections_lock, flags); + spin_lock_irq(&gb_connections_lock); list_del(&connection->bundle_links); list_del(&connection->hd_links); - spin_unlock_irqrestore(&gb_connections_lock, flags); + spin_unlock_irq(&gb_connections_lock); destroy_workqueue(connection->wq); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 89fe7641cd24..bdf502493e31 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -496,12 +496,11 @@ static void message_cancel(struct gb_message *message) struct gb_host_device *hd = message->operation->connection->hd; struct es2_ap_dev *es2 = hd_to_es2(hd); struct urb *urb; - unsigned long flags; int i; might_sleep(); - spin_lock_irqsave(&es2->cport_out_urb_lock, flags); + spin_lock_irq(&es2->cport_out_urb_lock); urb = message->hcpriv; /* Prevent dynamically allocated urb from being deallocated. */ @@ -514,14 +513,14 @@ static void message_cancel(struct gb_message *message) break; } } - spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); + spin_unlock_irq(&es2->cport_out_urb_lock); usb_kill_urb(urb); if (i < NUM_CPORT_OUT_URB) { - spin_lock_irqsave(&es2->cport_out_urb_lock, flags); + spin_lock_irq(&es2->cport_out_urb_lock); es2->cport_out_urb_cancelled[i] = false; - spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); + spin_unlock_irq(&es2->cport_out_urb_lock); } usb_free_urb(urb); diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 80896385c1cb..6260569b2f25 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -674,7 +674,6 @@ static int set_serial_info(struct gb_tty *gb_tty, static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) { int retval = 0; - unsigned long flags; DECLARE_WAITQUEUE(wait, current); struct async_icount old; struct async_icount new; @@ -683,11 +682,11 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) return -EINVAL; do { - spin_lock_irqsave(&gb_tty->read_lock, flags); + spin_lock_irq(&gb_tty->read_lock); old = gb_tty->oldcount; new = gb_tty->iocount; gb_tty->oldcount = new; - spin_unlock_irqrestore(&gb_tty->read_lock, flags); + spin_unlock_irq(&gb_tty->read_lock); if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) break; -- cgit v1.2.3-59-g8ed1b From e9f80f3363542b645a5ceb0509273eda1d0e4eab Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 23 Jun 2016 23:23:06 +0530 Subject: greybus: uart: don't use spin_lock_irq() spin_[un]lock_irq() routines should be used carefully as they things can go wrong, if they are mixed with spin_lock_irqsave() or other variants. The main problem is that spin_[un]lock_irq() routines doesn't check if the IRQs are already disabled/enabled on the local CPU and so spin_unlock_irq() will forcefully enable interrupts for example. This may not work well, if some other code was relying on interrupts being disabled. Use spin_lock_irqsave() and spin_unlock_restore() instead. This patch doesn't claim that it fixes the JIRA completely, but the issue was harder to reproduce for some iterations after this, which was quite easy to reproduce earlier on. Tested on EVT 2.0 with lots of debug patches to kernel and greybus. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/uart.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 6260569b2f25..80896385c1cb 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -674,6 +674,7 @@ static int set_serial_info(struct gb_tty *gb_tty, static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) { int retval = 0; + unsigned long flags; DECLARE_WAITQUEUE(wait, current); struct async_icount old; struct async_icount new; @@ -682,11 +683,11 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) return -EINVAL; do { - spin_lock_irq(&gb_tty->read_lock); + spin_lock_irqsave(&gb_tty->read_lock, flags); old = gb_tty->oldcount; new = gb_tty->iocount; gb_tty->oldcount = new; - spin_unlock_irq(&gb_tty->read_lock); + spin_unlock_irqrestore(&gb_tty->read_lock, flags); if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) break; -- cgit v1.2.3-59-g8ed1b From fe9054155d831fee7a8e737ff3898c4bc2e5fc4d Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 23 Jun 2016 23:23:06 +0530 Subject: greybus: es2.c: don't use spin_lock_irq() spin_[un]lock_irq() routines should be used carefully as they things can go wrong, if they are mixed with spin_lock_irqsave() or other variants. The main problem is that spin_[un]lock_irq() routines doesn't check if the IRQs are already disabled/enabled on the local CPU and so spin_unlock_irq() will forcefully enable interrupts for example. This may not work well, if some other code was relying on interrupts being disabled. Use spin_lock_irqsave() and spin_unlock_restore() instead. This patch doesn't claim that it fixes the JIRA completely, but the issue was harder to reproduce for some iterations after this, which was quite easy to reproduce earlier on. Tested on EVT 2.0 with lots of debug patches to kernel and greybus. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index bdf502493e31..89fe7641cd24 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -496,11 +496,12 @@ static void message_cancel(struct gb_message *message) struct gb_host_device *hd = message->operation->connection->hd; struct es2_ap_dev *es2 = hd_to_es2(hd); struct urb *urb; + unsigned long flags; int i; might_sleep(); - spin_lock_irq(&es2->cport_out_urb_lock); + spin_lock_irqsave(&es2->cport_out_urb_lock, flags); urb = message->hcpriv; /* Prevent dynamically allocated urb from being deallocated. */ @@ -513,14 +514,14 @@ static void message_cancel(struct gb_message *message) break; } } - spin_unlock_irq(&es2->cport_out_urb_lock); + spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); usb_kill_urb(urb); if (i < NUM_CPORT_OUT_URB) { - spin_lock_irq(&es2->cport_out_urb_lock); + spin_lock_irqsave(&es2->cport_out_urb_lock, flags); es2->cport_out_urb_cancelled[i] = false; - spin_unlock_irq(&es2->cport_out_urb_lock); + spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); } usb_free_urb(urb); -- cgit v1.2.3-59-g8ed1b From 6f7f2ae5df786bf9ced3247fda51a0a7aeb9cd0c Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 23 Jun 2016 23:23:06 +0530 Subject: greybus: gb_connections_lock: don't use spin_lock_irq() spin_[un]lock_irq() routines should be used carefully as they things can go wrong, if they are mixed with spin_lock_irqsave() or other variants. The main problem is that spin_[un]lock_irq() routines doesn't check if the IRQs are already disabled/enabled on the local CPU and so spin_unlock_irq() will forcefully enable interrupts for example. This may not work well, if some other code was relying on interrupts being disabled. Use spin_lock_irqsave() and spin_unlock_restore() instead. This patch doesn't claim that it fixes the JIRA completely, but the issue was harder to reproduce for some iterations after this, which was quite easy to reproduce earlier on. Tested on EVT 2.0 with lots of debug patches to kernel and greybus. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3a17db91a167..810c61807c0a 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -150,6 +150,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, unsigned long flags) { struct gb_connection *connection; + unsigned long irqflags; int ret; mutex_lock(&gb_connection_mutex); @@ -200,7 +201,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, gb_connection_init_name(connection); - spin_lock_irq(&gb_connections_lock); + spin_lock_irqsave(&gb_connections_lock, irqflags); list_add(&connection->hd_links, &hd->connections); if (bundle) @@ -208,7 +209,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, else INIT_LIST_HEAD(&connection->bundle_links); - spin_unlock_irq(&gb_connections_lock); + spin_unlock_irqrestore(&gb_connections_lock, irqflags); mutex_unlock(&gb_connection_mutex); @@ -849,6 +850,8 @@ EXPORT_SYMBOL_GPL(gb_connection_disable_forced); /* Caller must have disabled the connection before destroying it. */ void gb_connection_destroy(struct gb_connection *connection) { + unsigned long flags; + if (!connection) return; @@ -857,10 +860,10 @@ void gb_connection_destroy(struct gb_connection *connection) mutex_lock(&gb_connection_mutex); - spin_lock_irq(&gb_connections_lock); + spin_lock_irqsave(&gb_connections_lock, flags); list_del(&connection->bundle_links); list_del(&connection->hd_links); - spin_unlock_irq(&gb_connections_lock); + spin_unlock_irqrestore(&gb_connections_lock, flags); destroy_workqueue(connection->wq); -- cgit v1.2.3-59-g8ed1b From c0e65d026c297c53eeb5412c31a5410317225945 Mon Sep 17 00:00:00 2001 From: Joel Porquet <porquet_joel@projectara.com> Date: Fri, 24 Jun 2016 14:41:36 -0700 Subject: greybus: add support for the log protocol Add support for the new Log class/protocol. This protocol allows modules to send their internal logging messages to the AP in order to make module debugging easier. The protocol is, for now, composed a single module-initiated request. This request contains a message and associated length. The message is integrated in the kernel log with dev_dbg(). In order to be displayed with 'dmesg', the following command needs to be entered first: $ echo "file log.c +p" > /sys/kernel/debug/dynamic_debug/control The major portion of this file was initially written by Greg KH. Signed-off-by: Joel Porquet <porquet_joel@projectara.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 2 + drivers/staging/greybus/greybus_manifest.h | 2 + drivers/staging/greybus/greybus_protocols.h | 14 +++ drivers/staging/greybus/log.c | 132 ++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 drivers/staging/greybus/log.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 40325b32c5f7..bd9967ca46c6 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -18,6 +18,7 @@ gb-gbphy-y := gbphy.o # Prefix all modules with gb- gb-vibrator-y := vibrator.o gb-power-supply-y := power_supply.o +gb-log-y := log.o gb-loopback-y := loopback.o gb-light-y := light.o gb-raw-y := raw.o @@ -46,6 +47,7 @@ obj-m += greybus.o obj-m += gb-gbphy.o obj-m += gb-vibrator.o obj-m += gb-power-supply.o +obj-m += gb-log.o obj-m += gb-loopback.o obj-m += gb-light.o obj-m += gb-hid.o diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 28bbadd057d1..2ae39ad92368 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -47,6 +47,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_CAMERA_DATA = 0x16, GREYBUS_PROTOCOL_FW_DOWNLOAD = 0x17, GREYBUS_PROTOCOL_FW_MANAGEMENT = 0x18, + GREYBUS_PROTOCOL_LOG = 0x1a, /* ... */ GREYBUS_PROTOCOL_RAW = 0xfe, GREYBUS_PROTOCOL_VENDOR = 0xff, @@ -76,6 +77,7 @@ enum greybus_class_type { /* 0x14 is unused */ GREYBUS_CLASS_BOOTROM = 0x15, GREYBUS_CLASS_FW_MANAGEMENT = 0x16, + GREYBUS_CLASS_LOG = 0x17, /* ... */ GREYBUS_CLASS_RAW = 0xfe, GREYBUS_CLASS_VENDOR = 0xff, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 63dd2041fdeb..203bc46151e7 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -2136,5 +2136,19 @@ struct gb_audio_send_data_request { __u8 data[0]; } __packed; + +/* Log */ + +/* operations */ +#define GB_LOG_TYPE_SEND_LOG 0x02 + +/* length */ +#define GB_LOG_MAX_LEN 1024 + +struct gb_log_send_log_request { + __le16 len; + __u8 msg[0]; +} __packed; + #endif /* __GREYBUS_PROTOCOLS_H */ diff --git a/drivers/staging/greybus/log.c b/drivers/staging/greybus/log.c new file mode 100644 index 000000000000..70dd9e5a1cf2 --- /dev/null +++ b/drivers/staging/greybus/log.c @@ -0,0 +1,132 @@ +/* + * Greybus driver for the log protocol + * + * Copyright 2016 Google Inc. + * + * Released under the GPLv2 only. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/sizes.h> +#include <linux/uaccess.h> + +#include "greybus.h" + +struct gb_log { + struct gb_connection *connection; +}; + +static int gb_log_request_handler(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct device *dev = &connection->bundle->dev; + struct gb_log_send_log_request *receive; + u16 len; + + if (op->type != GB_LOG_TYPE_SEND_LOG) { + dev_err(dev, "unknown request type 0x%02x\n", op->type); + return -EINVAL; + } + + /* Verify size of payload */ + if (op->request->payload_size < sizeof(*receive)) { + dev_err(dev, "log request too small (%zu < %zu)\n", + op->request->payload_size, sizeof(*receive)); + return -EINVAL; + } + receive = op->request->payload; + len = le16_to_cpu(receive->len); + if (len != (int)(op->request->payload_size - sizeof(*receive))) { + dev_err(dev, "log request wrong size %d vs %d\n", len, + (int)(op->request->payload_size - sizeof(*receive))); + return -EINVAL; + } + if (len == 0) { + dev_err(dev, "log request of 0 bytes?\n"); + return -EINVAL; + } + + if (len > GB_LOG_MAX_LEN) { + dev_err(dev, "log request too big: %d\n", len); + return -EINVAL; + } + + /* Ensure the buffer is 0 terminated */ + receive->msg[len - 1] = '\0'; + + /* Print with dev_dbg() so that it can be easily turned off using + * dynamic debugging (and prevent any DoS) */ + dev_dbg(dev, "%s", receive->msg); + + return 0; +} + +static int gb_log_probe(struct gb_bundle *bundle, + const struct greybus_bundle_id *id) +{ + struct greybus_descriptor_cport *cport_desc; + struct gb_connection *connection; + struct gb_log *log; + int retval; + + if (bundle->num_cports != 1) + return -ENODEV; + + cport_desc = &bundle->cport_desc[0]; + if (cport_desc->protocol_id != GREYBUS_PROTOCOL_LOG) + return -ENODEV; + + log = kzalloc(sizeof(*log), GFP_KERNEL); + if (!log) + return -ENOMEM; + + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), + gb_log_request_handler); + if (IS_ERR(connection)) { + retval = PTR_ERR(connection); + goto error_free; + } + + log->connection = connection; + greybus_set_drvdata(bundle, log); + + retval = gb_connection_enable(connection); + if (retval) + goto error_connection_destroy; + + return 0; + +error_connection_destroy: + gb_connection_destroy(connection); +error_free: + kfree(log); + return retval; +} + +static void gb_log_disconnect(struct gb_bundle *bundle) +{ + struct gb_log *log = greybus_get_drvdata(bundle); + struct gb_connection *connection = log->connection; + + gb_connection_disable(connection); + gb_connection_destroy(connection); + + kfree(log); +} + +static const struct greybus_bundle_id gb_log_id_table[] = { + { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOG) }, + { } +}; +MODULE_DEVICE_TABLE(greybus, gb_log_id_table); + +static struct greybus_driver gb_log_driver = { + .name = "log", + .probe = gb_log_probe, + .disconnect = gb_log_disconnect, + .id_table = gb_log_id_table, +}; +module_greybus_driver(gb_log_driver); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 50687f360958722de8610998a3cc886d9c9ba0b5 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Wed, 22 Jun 2016 14:58:43 +0100 Subject: greybus: timesync: Initialize the timesync ping fields to zero Remember to initialize the TimeSync ping fields to zero so that if a timesync_get_last_event() returns an error - we display a FrameTime that is obviously incorrect. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index b9b29f5f5c8f..3391febedc63 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -698,6 +698,7 @@ static void gb_timesync_ping(struct gb_timesync_svc *timesync_svc) /* Have SVC generate a timesync ping */ timesync_svc->capture_ping = true; + timesync_svc->svc_ping_frame_time = 0; ret = gb_svc_timesync_ping(svc, ×ync_svc->svc_ping_frame_time); timesync_svc->capture_ping = false; if (ret) { @@ -709,6 +710,7 @@ static void gb_timesync_ping(struct gb_timesync_svc *timesync_svc) /* Get the ping FrameTime from each APB/GPB */ hd = timesync_svc->timesync_hd->hd; + timesync_svc->timesync_hd->ping_frame_time = 0; ret = hd->driver->timesync_get_last_event(hd, ×ync_svc->timesync_hd->ping_frame_time); if (ret) @@ -717,6 +719,7 @@ static void gb_timesync_ping(struct gb_timesync_svc *timesync_svc) list_for_each_entry(timesync_interface, ×ync_svc->interface_list, list) { control = timesync_interface->interface->control; + timesync_interface->ping_frame_time = 0; ping_frame_time = ×ync_interface->ping_frame_time; ret = gb_control_timesync_get_last_event(control, ping_frame_time); -- cgit v1.2.3-59-g8ed1b From fd6d6f61d83c8217a052ba494f5b44abcec7ff43 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Wed, 22 Jun 2016 14:58:44 +0100 Subject: greybus: timesync: Make printout consitent with other greybus messages The current printout on a TimeSync ping doesn't actually contain the word greybus and uses the word ping-time in the print string, something that appears nowhere in our official documentation on this feature. This patch changes the format string to contain 'greybus' and 'frametime' to bring the TimeSync printout more in-line with other greybus kernel log strings. before: timesync ping-time: ap=8632564 1-svc=8630712 greybus1=8633031 1-8.8=8633026 after: greybus frametime: ap=8632564 1-svc=8630712 greybus1=8633031 1-8.8=8633026 Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index 3391febedc63..14531edb824c 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -581,7 +581,8 @@ static size_t gb_timesync_log_frame_time(struct gb_timesync_svc *timesync_svc, size_t off; /* AP/SVC */ - off = snprintf(buf, buflen, "timesync: ping-time ap=%llu %s=%llu ", + off = snprintf(buf, buflen, "%s frametime: ap=%llu %s=%llu ", + greybus_bus_type.name, timesync_svc->ap_ping_frame_time, dev_name(&svc->dev), timesync_svc->svc_ping_frame_time); len = buflen - off; @@ -623,8 +624,8 @@ static size_t gb_timesync_log_frame_ktime(struct gb_timesync_svc *timesync_svc, /* AP */ gb_timesync_to_timespec(timesync_svc, timesync_svc->ap_ping_frame_time, &ts); - off = snprintf(buf, buflen, "timesync: ping-time ap=%lu.%lu ", - ts.tv_sec, ts.tv_nsec); + off = snprintf(buf, buflen, "%s frametime: ap=%lu.%lu ", + greybus_bus_type.name, ts.tv_sec, ts.tv_nsec); len = buflen - off; if (len >= buflen) goto done; -- cgit v1.2.3-59-g8ed1b From a4293e1d4e6416477976ee3bd248589d3fc4bb19 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 22 Jun 2016 14:46:39 +0530 Subject: greybus: bootrom: Enhance timeout error message The timeout message is very generic today and there are several requests we can be timing out waiting for. Update bootrom driver to also track the next expected request and enhance the error message based on that to confirm the request we timed out waiting for. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 54 +++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index 2cebffab8d9d..6d79bc6388df 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -17,11 +17,19 @@ /* Timeout, in jiffies, within which the next request must be received */ #define NEXT_REQ_TIMEOUT_J msecs_to_jiffies(1000) +enum next_request_type { + NEXT_REQ_FIRMWARE_SIZE, + NEXT_REQ_GET_FIRMWARE, + NEXT_REQ_READY_TO_BOOT, + NEXT_REQ_MODE_SWITCH, +}; + struct gb_bootrom { struct gb_connection *connection; const struct firmware *fw; u8 protocol_major; u8 protocol_minor; + enum next_request_type next_request; struct delayed_work dwork; struct mutex mutex; /* Protects bootrom->fw */ }; @@ -40,8 +48,28 @@ static void gb_bootrom_timedout(struct work_struct *work) struct delayed_work *dwork = to_delayed_work(work); struct gb_bootrom *bootrom = container_of(dwork, struct gb_bootrom, dwork); struct device *dev = &bootrom->connection->bundle->dev; + const char *reason; + + switch (bootrom->next_request) { + case NEXT_REQ_FIRMWARE_SIZE: + reason = "Firmware Size Request"; + break; + case NEXT_REQ_GET_FIRMWARE: + reason = "Get Firmware Request"; + break; + case NEXT_REQ_READY_TO_BOOT: + reason = "Ready to Boot Request"; + break; + case NEXT_REQ_MODE_SWITCH: + reason = "Interface Mode Switch"; + break; + default: + reason = NULL; + dev_err(dev, "Invalid next-request: %u", bootrom->next_request); + break; + } - dev_err(dev, "Timed out waiting for request from the Module\n"); + dev_err(dev, "Timed out waiting for %s from the Module\n", reason); mutex_lock(&bootrom->mutex); free_firmware(bootrom); @@ -50,6 +78,13 @@ static void gb_bootrom_timedout(struct work_struct *work) /* TODO: Power-off Module ? */ } +static void gb_bootrom_set_timeout(struct gb_bootrom *bootrom, + enum next_request_type next, unsigned long timeout) +{ + bootrom->next_request = next; + schedule_delayed_work(&bootrom->dwork, timeout); +} + /* * The es2 chip doesn't have VID/PID programmed into the hardware and we need to * hack that up to distinguish different modules and their firmware blobs. @@ -175,7 +210,8 @@ unlock: queue_work: /* Refresh timeout */ - schedule_delayed_work(&bootrom->dwork, NEXT_REQ_TIMEOUT_J); + gb_bootrom_set_timeout(bootrom, NEXT_REQ_GET_FIRMWARE, + NEXT_REQ_TIMEOUT_J); return ret; } @@ -188,6 +224,7 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) struct gb_bootrom_get_firmware_response *firmware_response; struct device *dev = &op->connection->bundle->dev; unsigned int offset, size; + enum next_request_type next_request; int ret = 0; /* Disable timeouts */ @@ -239,7 +276,12 @@ unlock: queue_work: /* Refresh timeout */ - schedule_delayed_work(&bootrom->dwork, NEXT_REQ_TIMEOUT_J); + if (!ret && (offset + size == fw->size)) + next_request = NEXT_REQ_READY_TO_BOOT; + else + next_request = NEXT_REQ_GET_FIRMWARE; + + gb_bootrom_set_timeout(bootrom, next_request, NEXT_REQ_TIMEOUT_J); return ret; } @@ -284,7 +326,8 @@ queue_work: * send a new hotplug request, which shall get rid of the bootrom * connection. As that can take some time, increase the timeout a bit. */ - schedule_delayed_work(&bootrom->dwork, 5 * NEXT_REQ_TIMEOUT_J); + gb_bootrom_set_timeout(bootrom, NEXT_REQ_MODE_SWITCH, + 5 * NEXT_REQ_TIMEOUT_J); return ret; } @@ -403,7 +446,8 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, } /* Refresh timeout */ - schedule_delayed_work(&bootrom->dwork, NEXT_REQ_TIMEOUT_J); + gb_bootrom_set_timeout(bootrom, NEXT_REQ_FIRMWARE_SIZE, + NEXT_REQ_TIMEOUT_J); dev_dbg(&bundle->dev, "AP_READY sent\n"); -- cgit v1.2.3-59-g8ed1b From dbb8cfeba9cc22e091eaec3dfa34ae4c7dc4d241 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 22 Jun 2016 14:46:40 +0530 Subject: greybus: bootrom: send timeout in milliseconds to gb_bootrom_set_timeout() Rename NEXT_REQ_TIMEOUT_J to NEXT_REQ_TIMEOUT_MS and store the timeout in milliseconds instead of jiffies. Suggested-by: Alex Elder <alex.elder@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index 6d79bc6388df..70785d4b64a9 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -15,7 +15,7 @@ #include "greybus.h" /* Timeout, in jiffies, within which the next request must be received */ -#define NEXT_REQ_TIMEOUT_J msecs_to_jiffies(1000) +#define NEXT_REQ_TIMEOUT_MS 1000 enum next_request_type { NEXT_REQ_FIRMWARE_SIZE, @@ -82,7 +82,7 @@ static void gb_bootrom_set_timeout(struct gb_bootrom *bootrom, enum next_request_type next, unsigned long timeout) { bootrom->next_request = next; - schedule_delayed_work(&bootrom->dwork, timeout); + schedule_delayed_work(&bootrom->dwork, msecs_to_jiffies(timeout)); } /* @@ -211,7 +211,7 @@ unlock: queue_work: /* Refresh timeout */ gb_bootrom_set_timeout(bootrom, NEXT_REQ_GET_FIRMWARE, - NEXT_REQ_TIMEOUT_J); + NEXT_REQ_TIMEOUT_MS); return ret; } @@ -281,7 +281,7 @@ queue_work: else next_request = NEXT_REQ_GET_FIRMWARE; - gb_bootrom_set_timeout(bootrom, next_request, NEXT_REQ_TIMEOUT_J); + gb_bootrom_set_timeout(bootrom, next_request, NEXT_REQ_TIMEOUT_MS); return ret; } @@ -327,7 +327,7 @@ queue_work: * connection. As that can take some time, increase the timeout a bit. */ gb_bootrom_set_timeout(bootrom, NEXT_REQ_MODE_SWITCH, - 5 * NEXT_REQ_TIMEOUT_J); + 5 * NEXT_REQ_TIMEOUT_MS); return ret; } @@ -447,7 +447,7 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, /* Refresh timeout */ gb_bootrom_set_timeout(bootrom, NEXT_REQ_FIRMWARE_SIZE, - NEXT_REQ_TIMEOUT_J); + NEXT_REQ_TIMEOUT_MS); dev_dbg(&bundle->dev, "AP_READY sent\n"); -- cgit v1.2.3-59-g8ed1b From 326f98ac3168cf415f4e1681b3684614bcd79fd3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 22 Jun 2016 14:46:41 +0530 Subject: greybus: bootrom: Wait for 10 seconds for mode-switch The greybus SVC core handles events from the SVC serially today. In some cases the SVC operations may take too long, for example trying to activate a dummy interface. If another interface receives a mode-switch mailbox event in that time, the SVC core wouldn't be able to process it in quickly enough and bootrom driver will print following error: bootrom 1-3.3.1: Timed out waiting for Interface Mode Switch from the Module This can be reproduced easily by attaching a 2x2 module along with any other normal module like camera or speaker, and doing a unipro_reset from userspace. The logs suggest this time to be around 6-7 seconds in most of the cases. Attaching multiple modules with dummy interfaces may make this worst. Lets increase the timeout from 5 to 10 seconds for now, also add a FIXME for the same. Testing Done: Tested on EVT 2.0 with camera and a 2x2 module. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index 70785d4b64a9..84537a07ab7f 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -17,6 +17,12 @@ /* Timeout, in jiffies, within which the next request must be received */ #define NEXT_REQ_TIMEOUT_MS 1000 +/* + * FIXME: Reduce this timeout once svc core handles parallel processing of + * events from the SVC, which are handled sequentially today. + */ +#define MODE_SWITCH_TIMEOUT_MS 10000 + enum next_request_type { NEXT_REQ_FIRMWARE_SIZE, NEXT_REQ_GET_FIRMWARE, @@ -327,7 +333,7 @@ queue_work: * connection. As that can take some time, increase the timeout a bit. */ gb_bootrom_set_timeout(bootrom, NEXT_REQ_MODE_SWITCH, - 5 * NEXT_REQ_TIMEOUT_MS); + MODE_SWITCH_TIMEOUT_MS); return ret; } -- cgit v1.2.3-59-g8ed1b From 2d466c23c64f7556d0a184a1f02b2c8a23edaf5e Mon Sep 17 00:00:00 2001 From: Jeffrey Carlyle <jcarlyle@google.com> Date: Fri, 24 Jun 2016 08:58:56 +0530 Subject: greybus: connection: switch to using spin_lock_irqsave/spin_lock_irqrestore excluisvely We know that it is a bad idea to explicitly enable IRQs when we don't don't know if they were already off before we disabled, so switch to the save _irqsave and _irqrestore functions. Ultimately, we need to review places in the Greybus drivers where IRQs are disabled and remove unnecessary instances. This is only an interim step. This code will never run from hard irq context, as it is already taking mutex in the path. Testing done: booted EVT2.0, ran suspend/resume test app with a period of 20s for a few dozen cycles. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Jeffrey Carlyle <jcarlyle@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 53 ++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 810c61807c0a..20a87b9ee674 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -572,7 +572,7 @@ static int gb_connection_ping(struct gb_connection *connection) * DISCONNECTING. */ static void gb_connection_cancel_operations(struct gb_connection *connection, - int errno) + int errno, unsigned long *flags) __must_hold(&connection->lock) { struct gb_operation *operation; @@ -581,7 +581,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, operation = list_last_entry(&connection->operations, struct gb_operation, links); gb_operation_get(operation); - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, *flags); if (gb_operation_is_incoming(operation)) gb_operation_cancel_incoming(operation, errno); @@ -590,7 +590,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, gb_operation_put(operation); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, *flags); } } @@ -601,7 +601,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, */ static void gb_connection_flush_incoming_operations(struct gb_connection *connection, - int errno) + int errno, unsigned long *flags) __must_hold(&connection->lock) { struct gb_operation *operation; @@ -621,13 +621,13 @@ gb_connection_flush_incoming_operations(struct gb_connection *connection, if (!incoming) break; - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, *flags); /* FIXME: flush, not cancel? */ gb_operation_cancel_incoming(operation, errno); gb_operation_put(operation); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, *flags); } } @@ -644,15 +644,16 @@ gb_connection_flush_incoming_operations(struct gb_connection *connection, static int _gb_connection_enable(struct gb_connection *connection, bool rx) { int ret; + unsigned long flags; /* Handle ENABLED_TX -> ENABLED transitions. */ if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) { if (!(connection->handler && rx)) return 0; - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); connection->state = GB_CONNECTION_STATE_ENABLED; - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, flags); return 0; } @@ -669,12 +670,12 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) if (ret) goto err_svc_connection_destroy; - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); if (connection->handler && rx) connection->state = GB_CONNECTION_STATE_ENABLED; else connection->state = GB_CONNECTION_STATE_ENABLED_TX; - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, flags); ret = gb_connection_control_connected(connection); if (ret) @@ -685,10 +686,10 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) err_control_disconnecting: gb_connection_control_disconnecting(connection); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); connection->state = GB_CONNECTION_STATE_DISCONNECTING; - gb_connection_cancel_operations(connection, -ESHUTDOWN); - spin_unlock_irq(&connection->lock); + gb_connection_cancel_operations(connection, -ESHUTDOWN, &flags); + spin_unlock_irqrestore(&connection->lock, flags); /* Transmit queue should already be empty. */ gb_connection_hd_cport_flush(connection); @@ -754,16 +755,18 @@ EXPORT_SYMBOL_GPL(gb_connection_enable_tx); void gb_connection_disable_rx(struct gb_connection *connection) { + unsigned long flags; + mutex_lock(&connection->mutex); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); if (connection->state != GB_CONNECTION_STATE_ENABLED) { - spin_unlock_irq(&connection->lock); + spin_unlock_irqrestore(&connection->lock, flags); goto out_unlock; } connection->state = GB_CONNECTION_STATE_ENABLED_TX; - gb_connection_flush_incoming_operations(connection, -ESHUTDOWN); - spin_unlock_irq(&connection->lock); + gb_connection_flush_incoming_operations(connection, -ESHUTDOWN, &flags); + spin_unlock_irqrestore(&connection->lock, flags); trace_gb_connection_disable(connection); @@ -786,6 +789,8 @@ void gb_connection_mode_switch_complete(struct gb_connection *connection) void gb_connection_disable(struct gb_connection *connection) { + unsigned long flags; + mutex_lock(&connection->mutex); if (connection->state == GB_CONNECTION_STATE_DISABLED) @@ -795,10 +800,10 @@ void gb_connection_disable(struct gb_connection *connection) gb_connection_control_disconnecting(connection); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); connection->state = GB_CONNECTION_STATE_DISCONNECTING; - gb_connection_cancel_operations(connection, -ESHUTDOWN); - spin_unlock_irq(&connection->lock); + gb_connection_cancel_operations(connection, -ESHUTDOWN, &flags); + spin_unlock_irqrestore(&connection->lock, flags); gb_connection_hd_cport_flush(connection); @@ -825,6 +830,8 @@ EXPORT_SYMBOL_GPL(gb_connection_disable); /* Disable a connection without communicating with the remote end. */ void gb_connection_disable_forced(struct gb_connection *connection) { + unsigned long flags; + mutex_lock(&connection->mutex); if (connection->state == GB_CONNECTION_STATE_DISABLED) @@ -832,10 +839,10 @@ void gb_connection_disable_forced(struct gb_connection *connection) trace_gb_connection_disable(connection); - spin_lock_irq(&connection->lock); + spin_lock_irqsave(&connection->lock, flags); connection->state = GB_CONNECTION_STATE_DISABLED; - gb_connection_cancel_operations(connection, -ESHUTDOWN); - spin_unlock_irq(&connection->lock); + gb_connection_cancel_operations(connection, -ESHUTDOWN, &flags); + spin_unlock_irqrestore(&connection->lock, flags); gb_connection_hd_cport_flush(connection); gb_connection_hd_cport_features_disable(connection); -- cgit v1.2.3-59-g8ed1b From 9e138dd479cb1f924a3d78e13e6ba5668db73acc Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Fri, 24 Jun 2016 13:52:43 -0700 Subject: greybus: audio: remove the unnecessary return statement The return statement immediately after the BUG_ON of the gbcodec_write() call is added by mistake. It's not causing any errors right now due to that gbcodec_reg is currently not being used. Testing Done: - Audio playback on EVT2 Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 45e12b1d8627..3c5a5aeeef8a 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -1099,7 +1099,6 @@ static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, return 0; BUG_ON(reg >= GBCODEC_REG_COUNT); - return 0; gbcodec_reg[reg] = value; dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value); -- cgit v1.2.3-59-g8ed1b From caad3090ab37d47e21505b613ad611ad6d20358d Mon Sep 17 00:00:00 2001 From: Evgeniy Borisov <borisov_evgeniy@projectara.com> Date: Thu, 23 Jun 2016 12:39:18 +0300 Subject: greybus: camera: Add RAW data format Add support for greybus RAW data format. Greybus RAW data formats starts from 0x80. Signed-off-by: Evgeniy Borisov <eborisov@mm-sol.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 0062f483fc2e..c47e4244a132 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -113,6 +113,38 @@ static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { .mbus_code = V4L2_MBUS_FMT_ARA_DEBUG_DATA_1X8, .gb_format = 0x42, }, + { + .mbus_code = V4L2_MBUS_FMT_SBGGR10_1X10, + .gb_format = 0x80, + }, + { + .mbus_code = V4L2_MBUS_FMT_SGBRG10_1X10, + .gb_format = 0x81, + }, + { + .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, + .gb_format = 0x82, + }, + { + .mbus_code = V4L2_MBUS_FMT_SRGGB10_1X10, + .gb_format = 0x83, + }, + { + .mbus_code = V4L2_MBUS_FMT_SBGGR12_1X12, + .gb_format = 0x84, + }, + { + .mbus_code = V4L2_MBUS_FMT_SGBRG12_1X12, + .gb_format = 0x85, + }, + { + .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, + .gb_format = 0x86, + }, + { + .mbus_code = V4L2_MBUS_FMT_SRGGB12_1X12, + .gb_format = 0x87, + }, }; #define ES2_APB_CDSI0_CPORT 16 -- cgit v1.2.3-59-g8ed1b From 6910fa2dd6dc0d81c6ec4a2f36d6e21bd27b4479 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 22 Jun 2016 11:42:02 +0200 Subject: greybus: hd: add flag argument to cport_enable callback Add a flag argument to the host-device cport_enable callback that can be used to provide hints about the connection being created (e.g. connection priority). Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 3 ++- drivers/staging/greybus/hd.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 20a87b9ee674..232af8d13111 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -286,7 +286,8 @@ static int gb_connection_hd_cport_enable(struct gb_connection *connection) if (!hd->driver->cport_enable) return 0; - ret = hd->driver->cport_enable(hd, connection->hd_cport_id); + ret = hd->driver->cport_enable(hd, connection->hd_cport_id, + connection->flags); if (ret) { dev_err(&hd->dev, "%s: failed to enable host cport: %d\n", connection->name, ret); diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 5136d0c9ecc8..d5d8c67551f2 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -19,7 +19,8 @@ struct gb_hd_driver { int (*cport_allocate)(struct gb_host_device *hd, int cport_id, unsigned long flags); void (*cport_release)(struct gb_host_device *hd, u16 cport_id); - int (*cport_enable)(struct gb_host_device *hd, u16 cport_id); + int (*cport_enable)(struct gb_host_device *hd, u16 cport_id, + unsigned long flags); int (*cport_disable)(struct gb_host_device *hd, u16 cport_id); int (*cport_flush)(struct gb_host_device *hd, u16 cport_id); int (*cport_ping)(struct gb_host_device *hd, u16 cport_id); -- cgit v1.2.3-59-g8ed1b From 1ba30c33096ced9d5c5814acdc46f53f3864f4e6 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 22 Jun 2016 11:42:03 +0200 Subject: greybus: connection: prevent drivers from specifying core flags Prevent drivers from specifying core flags (currently only GB_CONNECTION_FLAG_CONTROL) when creating connections. Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 8 ++++---- drivers/staging/greybus/connection.h | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 232af8d13111..7b2d6358f432 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -260,6 +260,9 @@ gb_connection_create_flags(struct gb_bundle *bundle, u16 cport_id, { struct gb_interface *intf = bundle->intf; + if (WARN_ON_ONCE(flags & GB_CONNECTION_FLAG_CORE_MASK)) + flags &= ~GB_CONNECTION_FLAG_CORE_MASK; + return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, handler, flags); } @@ -269,12 +272,9 @@ struct gb_connection * gb_connection_create_offloaded(struct gb_bundle *bundle, u16 cport_id, unsigned long flags) { - struct gb_interface *intf = bundle->intf; - flags |= GB_CONNECTION_FLAG_OFFLOADED; - return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, - NULL, flags); + return gb_connection_create_flags(bundle, cport_id, NULL, flags); } EXPORT_SYMBOL_GPL(gb_connection_create_offloaded); diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 7d0988ef1183..0ea8ac8fbb8f 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -19,6 +19,8 @@ #define GB_CONNECTION_FLAG_CDSI1 BIT(3) #define GB_CONNECTION_FLAG_CONTROL BIT(4) +#define GB_CONNECTION_FLAG_CORE_MASK GB_CONNECTION_FLAG_CONTROL + enum gb_connection_state { GB_CONNECTION_STATE_DISABLED = 0, GB_CONNECTION_STATE_ENABLED_TX = 1, -- cgit v1.2.3-59-g8ed1b From 3094f9477301ec477fed2a43fef68c43e70e72ce Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 22 Jun 2016 11:42:04 +0200 Subject: greybus: connection: add support for high-priority connections Add connection flag to indicate that a connection has high priority. For the SVC and control connections this may be used by the host-device driver as a hint to allocate dedicated DMA channels or to use polling to avoid message congestion. We also allow drivers to set this flag on their connections, even though we currently have no use case for this (and the host-device driver is again free to ignore it). Note that this mechanism can be used to indicate also high-bandwidth connections (e.g. wanting larger buffers or dedicated endpoints), but that that should be done using a separate high-bandwidth flag rather than overloading the high-priority flag. Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 5 +++-- drivers/staging/greybus/connection.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 7b2d6358f432..9eb177ea30de 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -232,14 +232,15 @@ gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id, gb_request_handler_t handler) { return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, handler, - 0); + GB_CONNECTION_FLAG_HIGH_PRIO); } struct gb_connection * gb_connection_create_control(struct gb_interface *intf) { return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL, - GB_CONNECTION_FLAG_CONTROL); + GB_CONNECTION_FLAG_CONTROL | + GB_CONNECTION_FLAG_HIGH_PRIO); } struct gb_connection * diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 0ea8ac8fbb8f..4d9f4c64176c 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -18,6 +18,7 @@ #define GB_CONNECTION_FLAG_OFFLOADED BIT(2) #define GB_CONNECTION_FLAG_CDSI1 BIT(3) #define GB_CONNECTION_FLAG_CONTROL BIT(4) +#define GB_CONNECTION_FLAG_HIGH_PRIO BIT(5) #define GB_CONNECTION_FLAG_CORE_MASK GB_CONNECTION_FLAG_CONTROL -- cgit v1.2.3-59-g8ed1b From 74ec7598b51fb347a1273f8deea42883ae725e6e Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 22 Jun 2016 11:42:05 +0200 Subject: greybus: es2: add support for greybus cport flags Add support for Greybus CPort flags that are sent to the bridge through a new USB vendor request when enabling a CPort as part of connection establishment. Currently the Greybus control and high-priority connection flags are recognised and forwarded to APBA. Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 42 +++++++++++++++++++++++++++++ drivers/staging/greybus/greybus_protocols.h | 10 +++++++ 2 files changed, 52 insertions(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 89fe7641cd24..22174450a8b3 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -607,6 +607,47 @@ static void es2_cport_release(struct gb_host_device *hd, u16 cport_id) ida_simple_remove(&hd->cport_id_map, cport_id); } +static int cport_enable(struct gb_host_device *hd, u16 cport_id, + unsigned long flags) +{ + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct usb_device *udev = es2->usb_dev; + struct gb_apb_request_cport_flags *req; + int ret; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + if (flags & GB_CONNECTION_FLAG_CONTROL) + req->flags |= GB_APB_CPORT_FLAG_CONTROL; + if (flags & GB_CONNECTION_FLAG_HIGH_PRIO) + req->flags |= GB_APB_CPORT_FLAG_HIGH_PRIO; + + dev_dbg(&hd->dev, "%s - cport = %u, flags = %02x\n", __func__, + cport_id, req->flags); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + GB_APB_REQUEST_CPORT_FLAGS, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, cport_id, 0, + req, sizeof(*req), ES2_TIMEOUT); + if (ret != sizeof(*req)) { + dev_err(&udev->dev, "failed to set cport flags for port %d\n", + cport_id); + if (ret >= 0) + ret = -EIO; + + goto out; + } + + ret = 0; +out: + kfree(req); + + return ret; +} + static int cport_disable(struct gb_host_device *hd, u16 cport_id) { int retval; @@ -799,6 +840,7 @@ static struct gb_hd_driver es2_driver = { .message_cancel = message_cancel, .cport_allocate = es2_cport_allocate, .cport_release = es2_cport_release, + .cport_enable = cport_enable, .cport_disable = cport_disable, .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 203bc46151e7..327d01f3679a 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -225,6 +225,16 @@ struct gb_control_timesync_get_last_event_response { #define REQUEST_TIMESYNC_AUTHORITATIVE 0x0f #define REQUEST_TIMESYNC_GET_LAST_EVENT 0x10 +/* requests to set Greybus CPort flags */ +#define GB_APB_REQUEST_CPORT_FLAGS 0x11 + +struct gb_apb_request_cport_flags { + u32 flags; +#define GB_APB_CPORT_FLAG_CONTROL 0x01 +#define GB_APB_CPORT_FLAG_HIGH_PRIO 0x02 +} __packed; + + /* Firmware Download Protocol */ /* Request Types */ -- cgit v1.2.3-59-g8ed1b From 0ef5be18852d0b044f916fd6194bb228f36e392a Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 27 Jun 2016 11:19:23 +0530 Subject: greybus: kernel_ver: Add kstrtobool() It was added in 4.6 and is required for one of the use case, copy it. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 48 ++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 98e3179cc00d..80ed27c9a650 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -341,4 +341,52 @@ static inline bool pwm_is_enabled(const struct pwm_device *pwm) } #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +/** + * kstrtobool - convert common user inputs into boolean values + * @s: input string + * @res: result + * + * This routine returns 0 iff the first character is one of 'Yy1Nn0', or + * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value + * pointed to by res is updated upon finding a match. + */ +static inline int kstrtobool(const char *s, bool *res) +{ + if (!s) + return -EINVAL; + + switch (s[0]) { + case 'y': + case 'Y': + case '1': + *res = true; + return 0; + case 'n': + case 'N': + case '0': + *res = false; + return 0; + case 'o': + case 'O': + switch (s[1]) { + case 'n': + case 'N': + *res = true; + return 0; + case 'f': + case 'F': + *res = false; + return 0; + default: + break; + } + default: + break; + } + + return -EINVAL; +} +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ -- cgit v1.2.3-59-g8ed1b From 1beb1fae808d82a409b1c6ad4575f890b946f0ca Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Tue, 28 Jun 2016 12:10:14 -0700 Subject: greybus: svc: remove input device handling in SVC protocol The input device was added before to handle the key(s) connected directly to SVC, which is not the case anymore. So, this change removes the gb_svc_key_event() operation and the corresponding input device code with it. Testing Done: Boot tested and tried module insert/removal through AraManager Change-Id: Iaa541d4aefb5c0ed16caaa39450029de35d7c228 Signed-off-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 104 +----------------------------------------- drivers/staging/greybus/svc.h | 2 - 2 files changed, 2 insertions(+), 104 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 48d07a91f01f..63d2a53b624f 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -8,13 +8,10 @@ */ #include <linux/debugfs.h> -#include <linux/input.h> #include <linux/workqueue.h> #include "greybus.h" -#define SVC_KEY_ARA_BUTTON KEY_A - #define SVC_INTF_EJECT_TIMEOUT 9000 #define SVC_INTF_ACTIVATE_TIMEOUT 6000 @@ -887,13 +884,6 @@ static int gb_svc_hello(struct gb_operation *op) return ret; } - ret = input_register_device(svc->input); - if (ret) { - dev_err(&svc->dev, "failed to register input: %d\n", ret); - device_del(&svc->dev); - return ret; - } - ret = gb_svc_watchdog_create(svc); if (ret) { dev_err(&svc->dev, "failed to create watchdog: %d\n", ret); @@ -913,7 +903,6 @@ static int gb_svc_hello(struct gb_operation *op) err_unregister_device: gb_svc_watchdog_destroy(svc); - input_unregister_device(svc->input); device_del(&svc->dev); return ret; } @@ -1160,53 +1149,6 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) return 0; } -static int gb_svc_key_code_map(struct gb_svc *svc, u16 key_code, u16 *code) -{ - switch (key_code) { - case GB_KEYCODE_ARA: - *code = SVC_KEY_ARA_BUTTON; - break; - default: - dev_warn(&svc->dev, "unknown keycode received: %u\n", key_code); - return -EINVAL; - } - - return 0; -} - -static int gb_svc_key_event_recv(struct gb_operation *op) -{ - struct gb_svc *svc = gb_connection_get_data(op->connection); - struct gb_message *request = op->request; - struct gb_svc_key_event_request *key; - u16 code; - u8 event; - int ret; - - if (request->payload_size < sizeof(*key)) { - dev_warn(&svc->dev, "short key request received (%zu < %zu)\n", - request->payload_size, sizeof(*key)); - return -EINVAL; - } - - key = request->payload; - - ret = gb_svc_key_code_map(svc, le16_to_cpu(key->key_code), &code); - if (ret < 0) - return ret; - - event = key->key_event; - if ((event != GB_SVC_KEY_PRESSED) && (event != GB_SVC_KEY_RELEASED)) { - dev_warn(&svc->dev, "unknown key event received: %u\n", event); - return -EINVAL; - } - - input_report_key(svc->input, code, (event == GB_SVC_KEY_PRESSED)); - input_sync(svc->input); - - return 0; -} - static int gb_svc_module_inserted_recv(struct gb_operation *op) { struct gb_svc *svc = gb_connection_get_data(op->connection); @@ -1314,8 +1256,6 @@ static int gb_svc_request_handler(struct gb_operation *op) return ret; case GB_SVC_TYPE_INTF_RESET: return gb_svc_intf_reset_recv(op); - case GB_SVC_TYPE_KEY_EVENT: - return gb_svc_key_event_recv(op); case GB_SVC_TYPE_MODULE_INSERTED: return gb_svc_module_inserted_recv(op); case GB_SVC_TYPE_MODULE_REMOVED: @@ -1328,34 +1268,6 @@ static int gb_svc_request_handler(struct gb_operation *op) } } -static struct input_dev *gb_svc_input_create(struct gb_svc *svc) -{ - struct input_dev *input_dev; - - input_dev = input_allocate_device(); - if (!input_dev) - return ERR_PTR(-ENOMEM); - - input_dev->name = dev_name(&svc->dev); - svc->input_phys = kasprintf(GFP_KERNEL, "greybus-%s/input0", - input_dev->name); - if (!svc->input_phys) - goto err_free_input; - - input_dev->phys = svc->input_phys; - input_dev->dev.parent = &svc->dev; - - input_set_drvdata(input_dev, svc); - - input_set_capability(input_dev, EV_KEY, SVC_KEY_ARA_BUTTON); - - return input_dev; - -err_free_input: - input_free_device(input_dev); - return ERR_PTR(-ENOMEM); -} - static void gb_svc_release(struct device *dev) { struct gb_svc *svc = to_gb_svc(dev); @@ -1364,7 +1276,6 @@ static void gb_svc_release(struct device *dev) gb_connection_destroy(svc->connection); ida_destroy(&svc->device_id_map); destroy_workqueue(svc->wq); - kfree(svc->input_phys); kfree(svc); } @@ -1400,27 +1311,18 @@ struct gb_svc *gb_svc_create(struct gb_host_device *hd) svc->state = GB_SVC_STATE_RESET; svc->hd = hd; - svc->input = gb_svc_input_create(svc); - if (IS_ERR(svc->input)) { - dev_err(&svc->dev, "failed to create input device: %ld\n", - PTR_ERR(svc->input)); - goto err_put_device; - } - svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, gb_svc_request_handler); if (IS_ERR(svc->connection)) { dev_err(&svc->dev, "failed to create connection: %ld\n", PTR_ERR(svc->connection)); - goto err_free_input; + goto err_put_device; } gb_connection_set_data(svc->connection, svc); return svc; -err_free_input: - input_free_device(svc->input); err_put_device: put_device(&svc->dev); return NULL; @@ -1459,14 +1361,12 @@ void gb_svc_del(struct gb_svc *svc) gb_connection_disable_rx(svc->connection); /* - * The SVC device and input device may have been registered - * from the request handler. + * The SVC device may have been registered from the request handler. */ if (device_is_registered(&svc->dev)) { gb_timesync_svc_remove(svc); gb_svc_debugfs_exit(svc); gb_svc_watchdog_destroy(svc); - input_unregister_device(svc->input); device_del(&svc->dev); } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index dab8fbab2380..7f5e534238bc 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -42,8 +42,6 @@ struct gb_svc { u8 protocol_major; u8 protocol_minor; - struct input_dev *input; - char *input_phys; struct gb_svc_watchdog *watchdog; struct dentry *debugfs_dentry; -- cgit v1.2.3-59-g8ed1b From a29bac623f015045385411eb9b3eced95aa96686 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Mon, 27 Jun 2016 20:07:09 +0200 Subject: greybus: Revert "connection: switch to using spin_lock_irqsave/spin_lock_irqrestore excluisvely" This reverts commit 426237c515b42b9f06d9a2b1021a6d2c4d440c51. Someone decided that all use of spin_lock_irq was to be considered a bug and went on a search-and-replace type "bug-fixing" spree last week. This is however just plain wrong. Using spin_lock_irq is perfectly fine in paths were interrupts have not been disabled, and this is in fact even preferred over the lazy approach of always using spin_lock_irqsave instead of understanding the code that is being written or modified. All current uses of spin_lock_irq have already been vetted in this respect. Also note that it is only used in functions that may sleep, that is, in functions that must not be called with interrupts disabled in the first place. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 53 ++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 9eb177ea30de..f26146840ab5 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -574,7 +574,7 @@ static int gb_connection_ping(struct gb_connection *connection) * DISCONNECTING. */ static void gb_connection_cancel_operations(struct gb_connection *connection, - int errno, unsigned long *flags) + int errno) __must_hold(&connection->lock) { struct gb_operation *operation; @@ -583,7 +583,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, operation = list_last_entry(&connection->operations, struct gb_operation, links); gb_operation_get(operation); - spin_unlock_irqrestore(&connection->lock, *flags); + spin_unlock_irq(&connection->lock); if (gb_operation_is_incoming(operation)) gb_operation_cancel_incoming(operation, errno); @@ -592,7 +592,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, gb_operation_put(operation); - spin_lock_irqsave(&connection->lock, *flags); + spin_lock_irq(&connection->lock); } } @@ -603,7 +603,7 @@ static void gb_connection_cancel_operations(struct gb_connection *connection, */ static void gb_connection_flush_incoming_operations(struct gb_connection *connection, - int errno, unsigned long *flags) + int errno) __must_hold(&connection->lock) { struct gb_operation *operation; @@ -623,13 +623,13 @@ gb_connection_flush_incoming_operations(struct gb_connection *connection, if (!incoming) break; - spin_unlock_irqrestore(&connection->lock, *flags); + spin_unlock_irq(&connection->lock); /* FIXME: flush, not cancel? */ gb_operation_cancel_incoming(operation, errno); gb_operation_put(operation); - spin_lock_irqsave(&connection->lock, *flags); + spin_lock_irq(&connection->lock); } } @@ -646,16 +646,15 @@ gb_connection_flush_incoming_operations(struct gb_connection *connection, static int _gb_connection_enable(struct gb_connection *connection, bool rx) { int ret; - unsigned long flags; /* Handle ENABLED_TX -> ENABLED transitions. */ if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) { if (!(connection->handler && rx)) return 0; - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_ENABLED; - spin_unlock_irqrestore(&connection->lock, flags); + spin_unlock_irq(&connection->lock); return 0; } @@ -672,12 +671,12 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) if (ret) goto err_svc_connection_destroy; - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); if (connection->handler && rx) connection->state = GB_CONNECTION_STATE_ENABLED; else connection->state = GB_CONNECTION_STATE_ENABLED_TX; - spin_unlock_irqrestore(&connection->lock, flags); + spin_unlock_irq(&connection->lock); ret = gb_connection_control_connected(connection); if (ret) @@ -688,10 +687,10 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) err_control_disconnecting: gb_connection_control_disconnecting(connection); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISCONNECTING; - gb_connection_cancel_operations(connection, -ESHUTDOWN, &flags); - spin_unlock_irqrestore(&connection->lock, flags); + gb_connection_cancel_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); /* Transmit queue should already be empty. */ gb_connection_hd_cport_flush(connection); @@ -757,18 +756,16 @@ EXPORT_SYMBOL_GPL(gb_connection_enable_tx); void gb_connection_disable_rx(struct gb_connection *connection) { - unsigned long flags; - mutex_lock(&connection->mutex); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); if (connection->state != GB_CONNECTION_STATE_ENABLED) { - spin_unlock_irqrestore(&connection->lock, flags); + spin_unlock_irq(&connection->lock); goto out_unlock; } connection->state = GB_CONNECTION_STATE_ENABLED_TX; - gb_connection_flush_incoming_operations(connection, -ESHUTDOWN, &flags); - spin_unlock_irqrestore(&connection->lock, flags); + gb_connection_flush_incoming_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); trace_gb_connection_disable(connection); @@ -791,8 +788,6 @@ void gb_connection_mode_switch_complete(struct gb_connection *connection) void gb_connection_disable(struct gb_connection *connection) { - unsigned long flags; - mutex_lock(&connection->mutex); if (connection->state == GB_CONNECTION_STATE_DISABLED) @@ -802,10 +797,10 @@ void gb_connection_disable(struct gb_connection *connection) gb_connection_control_disconnecting(connection); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISCONNECTING; - gb_connection_cancel_operations(connection, -ESHUTDOWN, &flags); - spin_unlock_irqrestore(&connection->lock, flags); + gb_connection_cancel_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); gb_connection_hd_cport_flush(connection); @@ -832,8 +827,6 @@ EXPORT_SYMBOL_GPL(gb_connection_disable); /* Disable a connection without communicating with the remote end. */ void gb_connection_disable_forced(struct gb_connection *connection) { - unsigned long flags; - mutex_lock(&connection->mutex); if (connection->state == GB_CONNECTION_STATE_DISABLED) @@ -841,10 +834,10 @@ void gb_connection_disable_forced(struct gb_connection *connection) trace_gb_connection_disable(connection); - spin_lock_irqsave(&connection->lock, flags); + spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISABLED; - gb_connection_cancel_operations(connection, -ESHUTDOWN, &flags); - spin_unlock_irqrestore(&connection->lock, flags); + gb_connection_cancel_operations(connection, -ESHUTDOWN); + spin_unlock_irq(&connection->lock); gb_connection_hd_cport_flush(connection); gb_connection_hd_cport_features_disable(connection); -- cgit v1.2.3-59-g8ed1b From 0b1d262354214daab1a3b7df763dc51c355ec353 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Mon, 27 Jun 2016 20:07:10 +0200 Subject: greybus: Revert "greybus: gb_connections_lock: don't use spin_lock_irq()" This reverts commit b022fd95108e8b9d202532a74d39e86152bc8f7f. Someone decided that all use of spin_lock_irq was to be considered a bug and went on a search-and-replace type "bug-fixing" spree last week. This is however just plain wrong. Using spin_lock_irq is perfectly fine in paths were interrupts have not been disabled, and this is in fact even preferred over the lazy approach of always using spin_lock_irqsave instead of understanding the code that is being written or modified. All current uses of spin_lock_irq have already been vetted in this respect. Also note that it is only used in functions that may sleep, that is, in functions that must not be called with interrupts disabled in the first place. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index f26146840ab5..632c8be43cf0 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -150,7 +150,6 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, unsigned long flags) { struct gb_connection *connection; - unsigned long irqflags; int ret; mutex_lock(&gb_connection_mutex); @@ -201,7 +200,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, gb_connection_init_name(connection); - spin_lock_irqsave(&gb_connections_lock, irqflags); + spin_lock_irq(&gb_connections_lock); list_add(&connection->hd_links, &hd->connections); if (bundle) @@ -209,7 +208,7 @@ _gb_connection_create(struct gb_host_device *hd, int hd_cport_id, else INIT_LIST_HEAD(&connection->bundle_links); - spin_unlock_irqrestore(&gb_connections_lock, irqflags); + spin_unlock_irq(&gb_connections_lock); mutex_unlock(&gb_connection_mutex); @@ -852,8 +851,6 @@ EXPORT_SYMBOL_GPL(gb_connection_disable_forced); /* Caller must have disabled the connection before destroying it. */ void gb_connection_destroy(struct gb_connection *connection) { - unsigned long flags; - if (!connection) return; @@ -862,10 +859,10 @@ void gb_connection_destroy(struct gb_connection *connection) mutex_lock(&gb_connection_mutex); - spin_lock_irqsave(&gb_connections_lock, flags); + spin_lock_irq(&gb_connections_lock); list_del(&connection->bundle_links); list_del(&connection->hd_links); - spin_unlock_irqrestore(&gb_connections_lock, flags); + spin_unlock_irq(&gb_connections_lock); destroy_workqueue(connection->wq); -- cgit v1.2.3-59-g8ed1b From 07af934094ed7d79442dcec82305001e3a66ad6c Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Mon, 27 Jun 2016 20:07:11 +0200 Subject: greybus: Revert "greybus: es2.c: don't use spin_lock_irq()" This reverts commit b44c3b5b0307788750eb4c462ed5982236876a8b. Someone decided that all use of spin_lock_irq was to be considered a bug and went on a search-and-replace type "bug-fixing" spree last week. This is however just plain wrong. Using spin_lock_irq is perfectly fine in paths were interrupts have not been disabled, and this is in fact even preferred over the lazy approach of always using spin_lock_irqsave instead of understanding the code that is being written or modified. All current uses of spin_lock_irq have already been vetted in this respect. Also note that it is only used in functions that may sleep, that is, in functions that must not be called with interrupts disabled in the first place. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 22174450a8b3..5bdf2e1298ad 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -496,12 +496,11 @@ static void message_cancel(struct gb_message *message) struct gb_host_device *hd = message->operation->connection->hd; struct es2_ap_dev *es2 = hd_to_es2(hd); struct urb *urb; - unsigned long flags; int i; might_sleep(); - spin_lock_irqsave(&es2->cport_out_urb_lock, flags); + spin_lock_irq(&es2->cport_out_urb_lock); urb = message->hcpriv; /* Prevent dynamically allocated urb from being deallocated. */ @@ -514,14 +513,14 @@ static void message_cancel(struct gb_message *message) break; } } - spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); + spin_unlock_irq(&es2->cport_out_urb_lock); usb_kill_urb(urb); if (i < NUM_CPORT_OUT_URB) { - spin_lock_irqsave(&es2->cport_out_urb_lock, flags); + spin_lock_irq(&es2->cport_out_urb_lock); es2->cport_out_urb_cancelled[i] = false; - spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); + spin_unlock_irq(&es2->cport_out_urb_lock); } usb_free_urb(urb); -- cgit v1.2.3-59-g8ed1b From 2aae92bdc25325cfd782cc76842a4302540958f2 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Mon, 27 Jun 2016 20:07:12 +0200 Subject: greybus: Revert "greybus: uart: don't use spin_lock_irq()" This reverts commit bd3c4aa99dc23449699432e0744bcb5af7afa98c. Someone decided that all use of spin_lock_irq was to be considered a bug and went on a search-and-replace type "bug-fixing" spree last week. This is however just plain wrong. Using spin_lock_irq is perfectly fine in paths were interrupts have not been disabled, and this is in fact even preferred over the lazy approach of always using spin_lock_irqsave instead of understanding the code that is being written or modified. All current uses of spin_lock_irq have already been vetted in this respect. Also note that it is only used in functions that may sleep, that is, in functions that must not be called with interrupts disabled in the first place. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/uart.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 80896385c1cb..6260569b2f25 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -674,7 +674,6 @@ static int set_serial_info(struct gb_tty *gb_tty, static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) { int retval = 0; - unsigned long flags; DECLARE_WAITQUEUE(wait, current); struct async_icount old; struct async_icount new; @@ -683,11 +682,11 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) return -EINVAL; do { - spin_lock_irqsave(&gb_tty->read_lock, flags); + spin_lock_irq(&gb_tty->read_lock); old = gb_tty->oldcount; new = gb_tty->iocount; gb_tty->oldcount = new; - spin_unlock_irqrestore(&gb_tty->read_lock, flags); + spin_unlock_irq(&gb_tty->read_lock); if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) break; -- cgit v1.2.3-59-g8ed1b From 957ccca08a990888e2a8f057dee646b8f37b1660 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 29 Jun 2016 13:42:26 -0700 Subject: greybus: light: Initialize mutex before using it Light protocol driver is suffering from the same issue that was fixed in camera driver earlier (commit a7c3b0c3c8da). Big cleanup function is used instead of fine grained control in the error path, and in one of the cases the mutex was found uninitialized and so the oops seen in SW-6752. Initialize the mutex before any code can access it. Compile tested only. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/light.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 78fb8a9f6a48..3d42a5dafee9 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1221,6 +1221,8 @@ static int gb_lights_probe(struct gb_bundle *bundle, if (!glights) return -ENOMEM; + mutex_init(&glights->lights_lock); + connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), gb_lights_request_handler); if (IS_ERR(connection)) { @@ -1231,8 +1233,6 @@ static int gb_lights_probe(struct gb_bundle *bundle, glights->connection = connection; gb_connection_set_data(connection, glights); - mutex_init(&glights->lights_lock); - greybus_set_drvdata(bundle, glights); /* We aren't ready to receive an incoming request yet */ -- cgit v1.2.3-59-g8ed1b From 0c15a9e0f3f7174718351fcb1c16be944f3b8a57 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Tue, 5 Jul 2016 17:09:20 -0500 Subject: greybus: audio: topology: Add helper API to map controlid with widget name This API is used by enumerated controls .get/.set callback functions to fetch control id associated with a widget. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mark.greer@animalcreek.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/audio_codec.h | 1 + drivers/staging/greybus/audio_topology.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 5a19467c1623..f4936f18647b 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -123,6 +123,7 @@ struct gbaudio_widget { struct gbaudio_control { __u8 id; char *name; + char *wname; const char * const *texts; struct list_head list; }; diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 1f9e8b6178dd..1bc987449180 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -77,6 +77,20 @@ static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, return NULL; } +static int gbaudio_map_wcontrolname(struct gbaudio_module_info *module, + const char *name) +{ + struct gbaudio_control *control; + + list_for_each_entry(control, &module->widget_ctl_list, list) { + if (!strncmp(control->wname, name, NAME_SIZE)) + return control->id; + } + dev_warn(module->dev, "%s: missing in modules controls list\n", name); + + return -EINVAL; +} + static int gbaudio_map_widgetname(struct gbaudio_module_info *module, const char *name) { @@ -652,6 +666,8 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, } control->id = curr->id; control->name = curr->name; + control->wname = w->name; + if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) control->texts = (const char * const *) curr->info.value.enumerated.names; -- cgit v1.2.3-59-g8ed1b From bb296b48038010dee1c55aa24264f3205ec949fd Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 30 Jun 2016 18:45:35 +0530 Subject: greybus: added warning message in case of missing widget Additional warning message added to notify in case above layer tries to access widget that is already removed from the list. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mark.greer@animalcreek.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/audio_topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 1bc987449180..92f2ada60be8 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -99,6 +99,8 @@ static int gbaudio_map_widgetname(struct gbaudio_module_info *module, if (!strncmp(widget->name, name, NAME_SIZE)) return widget->id; } + dev_warn(module->dev, "%s: missing in modules widgets list\n", name); + return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From d4cd9daa49b2dc46497d40d0251b27d0d685754e Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Tue, 5 Jul 2016 17:09:20 -0500 Subject: greybus: audio: topology: Use csize while computing next_ptr in parser Size of control elements vary in case of enumerated controls. As a preparation to enable enumerated control in topology parser, this patch uses csize while parsing controls & wsize while parsing widgets & its control to update next pointer. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mark.greer@animalcreek.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/audio_topology.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 92f2ada60be8..130548313454 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -624,9 +624,9 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, struct snd_soc_dapm_widget *dw, - struct gb_audio_widget *w) + struct gb_audio_widget *w, int *w_size) { - int i, ret; + int i, ret, csize; struct snd_kcontrol_new *widget_kctls; struct gb_audio_control *curr; struct gbaudio_control *control, *_control; @@ -648,9 +648,11 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, return -ENOMEM; } + *w_size = sizeof(struct gb_audio_widget); + /* create relevant kcontrols */ + curr = w->ctl; for (i = 0; i < w->ncontrols; i++) { - curr = &w->ctl[i]; ret = gbaudio_tplg_create_wcontrol(module, &widget_kctls[i], curr); if (ret) { @@ -673,6 +675,9 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) control->texts = (const char * const *) curr->info.value.enumerated.names; + csize = sizeof(struct gb_audio_control); + *w_size += csize; + curr = (void *)curr + csize; list_add(&control->list, &module->widget_ctl_list); dev_dbg(module->dev, "%s: control of type %d created\n", widget_kctls[i].name, widget_kctls[i].iface); @@ -771,7 +776,7 @@ error: static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module, struct gb_audio_control *controls) { - int i, ret; + int i, csize, ret; struct snd_kcontrol_new *dapm_kctls; struct gb_audio_control *curr; struct gbaudio_control *control, *_control; @@ -808,10 +813,12 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module, if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) control->texts = (const char * const *) curr->info.value.enumerated.names; + csize = sizeof(struct gb_audio_control); + list_add(&control->list, &module->ctl_list); dev_dbg(module->dev, "%d:%s created of type %d\n", curr->id, curr->name, curr->info.type); - curr++; + curr = (void *)curr + csize; } module->controls = dapm_kctls; @@ -829,7 +836,7 @@ error: static int gbaudio_tplg_process_widgets(struct gbaudio_module_info *module, struct gb_audio_widget *widgets) { - int i, ret, ncontrols; + int i, ret, w_size; struct snd_soc_dapm_widget *dapm_widgets; struct gb_audio_widget *curr; struct gbaudio_widget *widget, *_widget; @@ -843,7 +850,7 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_module_info *module, curr = widgets; for (i = 0; i < module->num_dapm_widgets; i++) { ret = gbaudio_tplg_create_widget(module, &dapm_widgets[i], - curr); + curr, &w_size); if (ret) { dev_err(module->dev, "%s:%d type not supported\n", curr->name, curr->type); @@ -859,9 +866,7 @@ static int gbaudio_tplg_process_widgets(struct gbaudio_module_info *module, widget->id = curr->id; widget->name = curr->name; list_add(&widget->list, &module->widget_list); - ncontrols = curr->ncontrols; - curr++; - curr = (void *)curr + ncontrols*sizeof(struct gb_audio_control); + curr = (void *)curr + w_size; } module->dapm_widgets = dapm_widgets; -- cgit v1.2.3-59-g8ed1b From e65579e335da0a65b5a76a343ddff6a6f3c77dd1 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 30 Jun 2016 18:45:37 +0530 Subject: greybus: audio: topology: Enable enumerated control support Added .get/.set callback and relevant changes in parser to enable enumerated control support for kcontrols and DAPM widget controls. Currently, it is limited to enumerated strings only. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mark.greer@animalcreek.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/audio_topology.c | 358 +++++++++++++++++++++++++++---- 1 file changed, 319 insertions(+), 39 deletions(-) diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 130548313454..e0779ca64388 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -53,7 +53,7 @@ static struct gbaudio_module_info *find_gb_module( } static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, - __u8 control_id, __u8 index) + __u8 control_id, __u8 index) { struct gbaudio_control *control; @@ -77,8 +77,23 @@ static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, return NULL; } +static int gbaudio_map_controlname(struct gbaudio_module_info *module, + const char *name) +{ + struct gbaudio_control *control; + + list_for_each_entry(control, &module->ctl_list, list) { + if (!strncmp(control->name, name, NAME_SIZE)) + return control->id; + } + + dev_warn(module->dev, "%s: missing in modules controls list\n", name); + + return -EINVAL; +} + static int gbaudio_map_wcontrolname(struct gbaudio_module_info *module, - const char *name) + const char *name) { struct gbaudio_control *control; @@ -92,7 +107,7 @@ static int gbaudio_map_wcontrolname(struct gbaudio_module_info *module, } static int gbaudio_map_widgetname(struct gbaudio_module_info *module, - const char *name) + const char *name) { struct gbaudio_widget *widget; list_for_each_entry(widget, &module->widget_list, list) { @@ -105,7 +120,7 @@ static int gbaudio_map_widgetname(struct gbaudio_module_info *module, } static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module, - __u8 widget_id) + __u8 widget_id) { struct gbaudio_widget *widget; @@ -116,6 +131,27 @@ static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module, return NULL; } +const char **gb_generate_enum_strings(struct gbaudio_module_info *gb, + struct gb_audio_enumerated *gbenum) +{ + const char **strings; + int i; + __u8 *data; + + strings = devm_kzalloc(gb->dev, sizeof(char *) * gbenum->items, + GFP_KERNEL); + data = gbenum->names; + + for (i = 0; i < gbenum->items; i++) { + strings[i] = (const char *)data; + while (*data != '\0') + data++; + data++; + } + + return strings; +} + static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -473,60 +509,288 @@ static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w) return ret; } +static int gbcodec_enum_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, ctl_id; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; + + ctl_id = gbaudio_map_controlname(module, kcontrol->id.name); + if (ctl_id < 0) + return -EINVAL; + + ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + return ret; + } + + ucontrol->value.enumerated.item[0] = gbvalue.value.enumerated_item[0]; + if (e->shift_l != e->shift_r) + ucontrol->value.enumerated.item[1] = + gbvalue.value.enumerated_item[1]; + + return 0; +} + +static int gbcodec_enum_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, ctl_id; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; + + ctl_id = gbaudio_map_controlname(module, kcontrol->id.name); + if (ctl_id < 0) + return -EINVAL; + + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + gbvalue.value.enumerated_item[0] = ucontrol->value.enumerated.item[0]; + + if (e->shift_l != e->shift_r) { + if (ucontrol->value.enumerated.item[1] > e->max - 1) + return -EINVAL; + gbvalue.value.enumerated_item[1] = + ucontrol->value.enumerated.item[1]; + } + + ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + } + + return ret; +} + +static int gbaudio_tplg_create_enum_kctl(struct gbaudio_module_info *gb, + struct snd_kcontrol_new *kctl, + struct gb_audio_control *ctl) +{ + struct soc_enum *gbe; + struct gb_audio_enumerated *gb_enum; + int i; + + gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL); + if (!gbe) + return -ENOMEM; + + gb_enum = &ctl->info.value.enumerated; + + /* since count=1, and reg is dummy */ + gbe->max = gb_enum->items; + gbe->texts = gb_generate_enum_strings(gb, gb_enum); + + /* debug enum info */ + dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gb_enum->items, + gb_enum->names_length); + for (i = 0; i < gb_enum->items; i++) + dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]); + + *kctl = (struct snd_kcontrol_new) + SOC_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_ctl_get, + gbcodec_enum_ctl_put); + return 0; +} + static int gbaudio_tplg_create_kcontrol(struct gbaudio_module_info *gb, struct snd_kcontrol_new *kctl, struct gb_audio_control *ctl) { + int ret = 0; struct gbaudio_ctl_pvt *ctldata; switch (ctl->iface) { case SNDRV_CTL_ELEM_IFACE_MIXER: - ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt), - GFP_KERNEL); - if (!ctldata) - return -ENOMEM; - ctldata->ctl_id = ctl->id; - ctldata->data_cport = ctl->data_cport; - ctldata->access = ctl->access; - ctldata->vcount = ctl->count_values; - ctldata->info = &ctl->info; - *kctl = (struct snd_kcontrol_new) - SOC_MIXER_GB(ctl->name, ctl->count, ctldata); - ctldata = NULL; + switch (ctl->info.type) { + case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: + ret = gbaudio_tplg_create_enum_kctl(gb, kctl, ctl); + break; + default: + ctldata = devm_kzalloc(gb->dev, + sizeof(struct gbaudio_ctl_pvt), + GFP_KERNEL); + if (!ctldata) + return -ENOMEM; + ctldata->ctl_id = ctl->id; + ctldata->data_cport = ctl->data_cport; + ctldata->access = ctl->access; + ctldata->vcount = ctl->count_values; + ctldata->info = &ctl->info; + *kctl = (struct snd_kcontrol_new) + SOC_MIXER_GB(ctl->name, ctl->count, ctldata); + ctldata = NULL; + break; + } break; default: return -EINVAL; } dev_dbg(gb->dev, "%s:%d control created\n", ctl->name, ctl->id); + return ret; +} + +static int gbcodec_enum_dapm_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, ctl_id; + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct gbaudio_module_info *module; + struct gb_audio_ctl_elem_value gbvalue; + struct snd_soc_codec *codec = widget->codec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; + + ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name); + if (ctl_id < 0) + return -EINVAL; + + ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + return ret; + } + + ucontrol->value.enumerated.item[0] = gbvalue.value.enumerated_item[0]; + if (e->shift_l != e->shift_r) + ucontrol->value.enumerated.item[1] = + gbvalue.value.enumerated_item[1]; + return 0; } -static const char * const gbtexts[] = {"Stereo", "Left", "Right"}; +static int gbcodec_enum_dapm_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, wi, ctl_id; + unsigned int val, mux, change; + unsigned int mask; + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct gb_audio_ctl_elem_value gbvalue; + struct gbaudio_module_info *module; + struct snd_soc_codec *codec = widget->codec; + struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + + module = find_gb_module(gb, kcontrol->id.name); + if (!module) + return -EINVAL; + + ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name); + if (ctl_id < 0) + return -EINVAL; + + change = 0; + ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + return ret; + } -static const SOC_ENUM_SINGLE_DECL( - module_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbtexts); + mux = ucontrol->value.enumerated.item[0]; + val = mux << e->shift_l; + mask = e->mask << e->shift_l; -static const SOC_ENUM_SINGLE_DECL( - module_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbtexts); + if (gbvalue.value.enumerated_item[0] != + ucontrol->value.enumerated.item[0]) { + change = 1; + gbvalue.value.enumerated_item[0] = + ucontrol->value.enumerated.item[0]; + } + + if (e->shift_l != e->shift_r) { + if (ucontrol->value.enumerated.item[1] > e->max - 1) + return -EINVAL; + val |= ucontrol->value.enumerated.item[1] << e->shift_r; + mask |= e->mask << e->shift_r; + if (gbvalue.value.enumerated_item[1] != + ucontrol->value.enumerated.item[1]) { + change = 1; + gbvalue.value.enumerated_item[1] = + ucontrol->value.enumerated.item[1]; + } + } + + if (change) { + ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, + GB_AUDIO_INVALID_INDEX, &gbvalue); + if (ret) { + dev_err_ratelimited(codec->dev, + "%d:Error in %s for %s\n", ret, + __func__, kcontrol->id.name); + } + for (wi = 0; wi < wlist->num_widgets; wi++) { + widget = wlist->widgets[wi]; + + widget->value = val; + widget->dapm->update = NULL; + snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e); + } + } + + return change; +} static int gbaudio_tplg_create_enum_ctl(struct gbaudio_module_info *gb, struct snd_kcontrol_new *kctl, struct gb_audio_control *ctl) { - switch (ctl->id) { - case 8: - *kctl = (struct snd_kcontrol_new) - SOC_DAPM_ENUM(ctl->name, module_apb1_rx_enum); - break; - case 9: - *kctl = (struct snd_kcontrol_new) - SOC_DAPM_ENUM(ctl->name, module_mic_enum); - break; - default: - return -EINVAL; - } + struct soc_enum *gbe; + struct gb_audio_enumerated *gb_enum; + int i; + + gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL); + if (!gbe) + return -ENOMEM; + gb_enum = &ctl->info.value.enumerated; + + /* since count=1, and reg is dummy */ + gbe->max = gb_enum->items; + gbe->texts = gb_generate_enum_strings(gb, gb_enum); + + /* debug enum info */ + dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gb_enum->items, + gb_enum->names_length); + for (i = 0; i < gb_enum->items; i++) + dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]); + + *kctl = (struct snd_kcontrol_new) + SOC_DAPM_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_dapm_ctl_get, + gbcodec_enum_dapm_ctl_put); return 0; } @@ -672,10 +936,18 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, control->name = curr->name; control->wname = w->name; - if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) + if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) { + struct gb_audio_enumerated *gbenum = + &curr->info.value.enumerated; + + csize = offsetof(struct gb_audio_control, info); + csize += offsetof(struct gb_audio_ctl_elem_info, value); + csize += offsetof(struct gb_audio_enumerated, names); + csize += gbenum->names_length; control->texts = (const char * const *) - curr->info.value.enumerated.names; - csize = sizeof(struct gb_audio_control); + gb_generate_enum_strings(module, gbenum); + } else + csize = sizeof(struct gb_audio_control); *w_size += csize; curr = (void *)curr + csize; list_add(&control->list, &module->widget_ctl_list); @@ -810,10 +1082,18 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module, snprintf(curr->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name); control->name = curr->name; - if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) + if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) { + struct gb_audio_enumerated *gbenum = + &curr->info.value.enumerated; + + csize = offsetof(struct gb_audio_control, info); + csize += offsetof(struct gb_audio_ctl_elem_info, value); + csize += offsetof(struct gb_audio_enumerated, names); + csize += gbenum->names_length; control->texts = (const char * const *) - curr->info.value.enumerated.names; - csize = sizeof(struct gb_audio_control); + gb_generate_enum_strings(module, gbenum); + } else + csize = sizeof(struct gb_audio_control); list_add(&control->list, &module->ctl_list); dev_dbg(module->dev, "%d:%s created of type %d\n", curr->id, -- cgit v1.2.3-59-g8ed1b From 7f93eab7c705527b0579b1bfd4d6e9f6acc9e5ca Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Thu, 30 Jun 2016 09:18:00 -0500 Subject: greybus: camera: Early update num_streams and flags In configure_streams Operation, when returning from the greybus operation call, we can assign the num_streams and flags variable earlier so that if the following stream configuration inspection fails, the caller receives the right number of configured streams anyway Testing Done: White camera module preview and capture. Triggering failure during stream configuration inspection makes configuration fails, but avoid segfaulting as was previously happening Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/camera.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index c47e4244a132..584f85e7a02d 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -432,6 +432,9 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto done; } + *flags = resp->flags; + *num_streams = resp->num_streams; + for (i = 0; i < nstreams; ++i) { struct gb_camera_stream_config_response *cfg = &resp->config[i]; @@ -451,11 +454,8 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, } if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) || - (*flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) { - *flags = resp->flags; - *num_streams = resp->num_streams; + (req->flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) goto done; - } if (gcam->state == GB_CAMERA_STATE_CONFIGURED) { gb_camera_teardown_data_connection(gcam); @@ -469,15 +469,14 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CONFIGURE_STREAMS, req, req_size, resp, resp_size); + *flags = 0; + *num_streams = 0; goto done; } gcam->state = GB_CAMERA_STATE_CONFIGURED; } - *flags = resp->flags; - *num_streams = resp->num_streams; - done: mutex_unlock(&gcam->mutex); kfree(req); -- cgit v1.2.3-59-g8ed1b From 36ab1108b60eb649c7c0f0653f854afc1234cfcb Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Thu, 30 Jun 2016 09:18:00 -0500 Subject: greybus: camera: Fix number of configured streams Camera Module may report a lower number of configured streams than the one requested by the AP. All the non-supported stream configuration are zeroed. Make the stream configuration inspection loop take only the valid stream into account, to avoid unnecessarily accessing zeroed memory areas. So far, inspecting non valid streams configuration has prove to be harmless, but as we'll need to inspect stream characteristics as reported image sizes and format, we have to take only valid configurations into account. Testing Done: White Camera Module preview and capture. Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 584f85e7a02d..41891b24ef05 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -435,7 +435,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, *flags = resp->flags; *num_streams = resp->num_streams; - for (i = 0; i < nstreams; ++i) { + for (i = 0; i < resp->num_streams; ++i) { struct gb_camera_stream_config_response *cfg = &resp->config[i]; streams[i].width = le16_to_cpu(cfg->width); -- cgit v1.2.3-59-g8ed1b From 80b3982b8d1ca463963b9d2d406098c18add2baf Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Thu, 30 Jun 2016 09:18:00 -0500 Subject: greybus: camera: Fix size of configure_streams(0) When APB-A CSI-Tx configuration fails, it is necessary to unconfigure the camera module issuesing a 0 stream configuration request. Fix size of request and response to avoid Greybus core complain about Response size differences. Testing Done: Triggering the error condition after APB-A CSI-tx configuration does not make Greybus core complain anymore Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/camera.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 41891b24ef05..ce0ff759f3ce 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -468,7 +468,8 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, memset(req, 0, sizeof(*req)); gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CONFIGURE_STREAMS, - req, req_size, resp, resp_size); + req, sizeof(*req), + resp, sizeof(*resp)); *flags = 0; *num_streams = 0; goto done; -- cgit v1.2.3-59-g8ed1b From e3eda54d0b5fef23957cc4f586f4b44fd223c881 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 30 Jun 2016 10:54:00 -0500 Subject: greybus: Add Component Authentication Protocol support This patch adds Component Authentication Protocol support in greybus. The purpose of the CAP protocol is to authenticate the Module hardware, and it can only be used when it is present as part of the firmware-management bundle, on a separate CPort. Compile tested only. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Jun Li <li_jun@projectara.com> Tested-by: Jun Li <li_jun@projectara.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/authentication.c | 437 +++++++++++++++++++++++ drivers/staging/greybus/firmware.h | 6 + drivers/staging/greybus/fw-core.c | 51 ++- drivers/staging/greybus/greybus_authentication.h | 120 +++++++ drivers/staging/greybus/greybus_manifest.h | 1 + drivers/staging/greybus/greybus_protocols.h | 37 ++ 7 files changed, 650 insertions(+), 4 deletions(-) create mode 100644 drivers/staging/greybus/authentication.c create mode 100644 drivers/staging/greybus/greybus_authentication.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index bd9967ca46c6..fa5aaf363c7b 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -33,7 +33,7 @@ gb-audio-manager-y += audio_manager.o gb-audio-manager-y += audio_manager_module.o gb-bootrom-y := bootrom.o gb-camera-y := camera.o -gb-firmware-y := fw-core.o fw-download.o fw-management.o +gb-firmware-y := fw-core.o fw-download.o fw-management.o authentication.o gb-spilib-y := spilib.o gb-sdio-y := sdio.o gb-uart-y := uart.o diff --git a/drivers/staging/greybus/authentication.c b/drivers/staging/greybus/authentication.c new file mode 100644 index 000000000000..e4697805530a --- /dev/null +++ b/drivers/staging/greybus/authentication.c @@ -0,0 +1,437 @@ +/* + * Greybus Component Authentication Protocol (CAP) Driver. + * + * Copyright 2016 Google Inc. + * Copyright 2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/uaccess.h> + +#include "greybus_authentication.h" +#include "firmware.h" +#include "greybus.h" + +#define CAP_TIMEOUT_MS 1000 + +/* + * Number of minor devices this driver supports. + * There will be exactly one required per Interface. + */ +#define NUM_MINORS U8_MAX + +struct gb_cap { + struct device *parent; + struct gb_connection *connection; + struct kref kref; + struct list_head node; + bool disabled; /* connection getting disabled */ + + struct mutex mutex; + struct cdev cdev; + struct device *class_device; + dev_t dev_num; +}; + +static struct class *cap_class; +static dev_t cap_dev_num; +static DEFINE_IDA(cap_minors_map); +static LIST_HEAD(cap_list); +static DEFINE_MUTEX(list_mutex); + +static void cap_kref_release(struct kref *kref) +{ + struct gb_cap *cap = container_of(kref, struct gb_cap, kref); + + kfree(cap); +} + +/* + * All users of cap take a reference (from within list_mutex lock), before + * they get a pointer to play with. And the structure will be freed only after + * the last user has put the reference to it. + */ +static void put_cap(struct gb_cap *cap) +{ + kref_put(&cap->kref, cap_kref_release); +} + +/* Caller must call put_cap() after using struct gb_cap */ +static struct gb_cap *get_cap(struct cdev *cdev) +{ + struct gb_cap *cap; + + mutex_lock(&list_mutex); + + list_for_each_entry(cap, &cap_list, node) { + if (&cap->cdev == cdev) { + kref_get(&cap->kref); + goto unlock; + } + } + + cap = NULL; + +unlock: + mutex_unlock(&list_mutex); + + return cap; +} + +static int cap_get_endpoint_uid(struct gb_cap *cap, u8 *euid) +{ + struct gb_connection *connection = cap->connection; + struct gb_cap_get_endpoint_uid_response response; + int ret; + + ret = gb_operation_sync(connection, GB_CAP_TYPE_GET_ENDPOINT_UID, NULL, + 0, &response, sizeof(response)); + if (ret) { + dev_err(cap->parent, "failed to get endpoint uid (%d)\n", ret); + return ret; + } + + memcpy(euid, response.uid, sizeof(response.uid)); + + return 0; +} + +static int cap_get_ims_certificate(struct gb_cap *cap, u32 class, u32 id, + u8 *certificate, u32 *size, u8 *result) +{ + struct gb_connection *connection = cap->connection; + struct gb_cap_get_ims_certificate_request *request; + struct gb_cap_get_ims_certificate_response *response; + size_t max_size = gb_operation_get_payload_size_max(connection); + struct gb_operation *op; + int ret; + + op = gb_operation_create_flags(connection, + GB_CAP_TYPE_GET_IMS_CERTIFICATE, + sizeof(*request), max_size, + GB_OPERATION_FLAG_SHORT_RESPONSE, + GFP_KERNEL); + if (!op) + return -ENOMEM; + + request = op->request->payload; + request->certificate_class = cpu_to_le32(class); + request->certificate_id = cpu_to_le32(id); + + ret = gb_operation_request_send_sync(op); + if (ret) { + dev_err(cap->parent, "failed to get certificate (%d)\n", ret); + goto done; + } + + response = op->response->payload; + *result = response->result_code; + *size = op->response->payload_size - sizeof(*response); + memcpy(certificate, response->certificate, *size); + +done: + gb_operation_put(op); + return ret; +} + +static int cap_authenticate(struct gb_cap *cap, u32 auth_type, u8 *uid, + u8 *challenge, u8 *result, u8 *auth_response, + u32 *signature_size, u8 *signature) +{ + struct gb_connection *connection = cap->connection; + struct gb_cap_authenticate_request *request; + struct gb_cap_authenticate_response *response; + size_t max_size = gb_operation_get_payload_size_max(connection); + struct gb_operation *op; + int ret; + + op = gb_operation_create_flags(connection, GB_CAP_TYPE_AUTHENTICATE, + sizeof(*request), max_size, + GB_OPERATION_FLAG_SHORT_RESPONSE, + GFP_KERNEL); + if (!op) + return -ENOMEM; + + request = op->request->payload; + request->auth_type = cpu_to_le32(auth_type); + memcpy(request->uid, uid, sizeof(request->uid)); + memcpy(request->challenge, challenge, sizeof(request->challenge)); + + ret = gb_operation_request_send_sync(op); + if (ret) { + dev_err(cap->parent, "failed to authenticate (%d)\n", ret); + goto done; + } + + response = op->response->payload; + *result = response->result_code; + *signature_size = op->response->payload_size - sizeof(*response); + memcpy(auth_response, response->response, sizeof(response->response)); + memcpy(signature, response->signature, *signature_size); + +done: + gb_operation_put(op); + return ret; +} + +/* Char device fops */ + +static int cap_open(struct inode *inode, struct file *file) +{ + struct gb_cap *cap = get_cap(inode->i_cdev); + + /* cap structure can't get freed until file descriptor is closed */ + if (cap) { + file->private_data = cap; + return 0; + } + + return -ENODEV; +} + +static int cap_release(struct inode *inode, struct file *file) +{ + struct gb_cap *cap = file->private_data; + + put_cap(cap); + return 0; +} + +static int cap_ioctl(struct gb_cap *cap, unsigned int cmd, + void __user *buf) +{ + struct cap_ioc_get_endpoint_uid endpoint_uid; + struct cap_ioc_get_ims_certificate *ims_cert; + struct cap_ioc_authenticate *authenticate; + int ret; + + switch (cmd) { + case CAP_IOC_GET_ENDPOINT_UID: + ret = cap_get_endpoint_uid(cap, endpoint_uid.uid); + if (ret) + return ret; + + if (copy_to_user(buf, &endpoint_uid, sizeof(endpoint_uid))) + return -EFAULT; + + return 0; + case CAP_IOC_GET_IMS_CERTIFICATE: + ims_cert = kzalloc(sizeof(*ims_cert), GFP_KERNEL); + if (!ims_cert) + return -ENOMEM; + + if (copy_from_user(ims_cert, buf, sizeof(*ims_cert))) { + ret = -EFAULT; + goto free_ims_cert; + } + + ret = cap_get_ims_certificate(cap, ims_cert->certificate_class, + ims_cert->certificate_id, + ims_cert->certificate, + &ims_cert->cert_size, + &ims_cert->result_code); + if (ret) + goto free_ims_cert; + + if (copy_to_user(buf, ims_cert, sizeof(*ims_cert))) + ret = -EFAULT; + +free_ims_cert: + kfree(ims_cert); + return ret; + case CAP_IOC_AUTHENTICATE: + authenticate = kzalloc(sizeof(*authenticate), GFP_KERNEL); + if (!authenticate) + return -ENOMEM; + + if (copy_from_user(authenticate, buf, sizeof(*authenticate))) { + ret = -EFAULT; + goto free_authenticate; + } + + ret = cap_authenticate(cap, authenticate->auth_type, + authenticate->uid, + authenticate->challenge, + &authenticate->result_code, + authenticate->response, + &authenticate->signature_size, + authenticate->signature); + if (ret) + goto free_authenticate; + + if (copy_to_user(buf, authenticate, sizeof(*authenticate))) + ret = -EFAULT; +free_authenticate: + kfree(authenticate); + return ret; + default: + return -ENOTTY; + } +} + +static long cap_ioctl_unlocked(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct gb_cap *cap = file->private_data; + int ret = -ENODEV; + + /* + * Serialize ioctls. + * + * We don't want the user to do multiple authentication operations in + * parallel. + * + * This is also used to protect ->disabled, which is used to check if + * the connection is getting disconnected, so that we don't start any + * new operations. + */ + mutex_lock(&cap->mutex); + if (!cap->disabled) + ret = cap_ioctl(cap, cmd, (void __user *)arg); + mutex_unlock(&cap->mutex); + + return ret; +} + +static const struct file_operations cap_fops = { + .owner = THIS_MODULE, + .open = cap_open, + .release = cap_release, + .unlocked_ioctl = cap_ioctl_unlocked, +}; + +int gb_cap_connection_init(struct gb_connection *connection) +{ + struct gb_cap *cap; + int ret, minor; + + if (!connection) + return 0; + + cap = kzalloc(sizeof(*cap), GFP_KERNEL); + if (!cap) + return -ENOMEM; + + cap->parent = &connection->bundle->dev; + cap->connection = connection; + mutex_init(&cap->mutex); + gb_connection_set_data(connection, cap); + kref_init(&cap->kref); + + mutex_lock(&list_mutex); + list_add(&cap->node, &cap_list); + mutex_unlock(&list_mutex); + + ret = gb_connection_enable(connection); + if (ret) + goto err_list_del; + + minor = ida_simple_get(&cap_minors_map, 0, NUM_MINORS, GFP_KERNEL); + if (minor < 0) { + ret = minor; + goto err_connection_disable; + } + + /* Add a char device to allow userspace to interact with cap */ + cap->dev_num = MKDEV(MAJOR(cap_dev_num), minor); + cdev_init(&cap->cdev, &cap_fops); + + ret = cdev_add(&cap->cdev, cap->dev_num, 1); + if (ret) + goto err_remove_ida; + + /* Add a soft link to the previously added char-dev within the bundle */ + cap->class_device = device_create(cap_class, cap->parent, cap->dev_num, + NULL, "gb-authenticate-%d", minor); + if (IS_ERR(cap->class_device)) { + ret = PTR_ERR(cap->class_device); + goto err_del_cdev; + } + + return 0; + +err_del_cdev: + cdev_del(&cap->cdev); +err_remove_ida: + ida_simple_remove(&cap_minors_map, minor); +err_connection_disable: + gb_connection_disable(connection); +err_list_del: + mutex_lock(&list_mutex); + list_del(&cap->node); + mutex_unlock(&list_mutex); + + put_cap(cap); + + return ret; +} + +void gb_cap_connection_exit(struct gb_connection *connection) +{ + struct gb_cap *cap; + + if (!connection) + return; + + cap = gb_connection_get_data(connection); + + device_destroy(cap_class, cap->dev_num); + cdev_del(&cap->cdev); + ida_simple_remove(&cap_minors_map, MINOR(cap->dev_num)); + + /* + * Disallow any new ioctl operations on the char device and wait for + * existing ones to finish. + */ + mutex_lock(&cap->mutex); + cap->disabled = true; + mutex_unlock(&cap->mutex); + + /* All pending greybus operations should have finished by now */ + gb_connection_disable(cap->connection); + + /* Disallow new users to get access to the cap structure */ + mutex_lock(&list_mutex); + list_del(&cap->node); + mutex_unlock(&list_mutex); + + /* + * All current users of cap would have taken a reference to it by + * now, we can drop our reference and wait the last user will get + * cap freed. + */ + put_cap(cap); +} + +int cap_init(void) +{ + int ret; + + cap_class = class_create(THIS_MODULE, "gb_authenticate"); + if (IS_ERR(cap_class)) + return PTR_ERR(cap_class); + + ret = alloc_chrdev_region(&cap_dev_num, 0, NUM_MINORS, + "gb_authenticate"); + if (ret) + goto err_remove_class; + + return 0; + +err_remove_class: + class_destroy(cap_class); + return ret; +} + +void cap_exit(void) +{ + unregister_chrdev_region(cap_dev_num, NUM_MINORS); + class_destroy(cap_class); + ida_destroy(&cap_minors_map); +} diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h index a82d0203971b..fb955379cfe4 100644 --- a/drivers/staging/greybus/firmware.h +++ b/drivers/staging/greybus/firmware.h @@ -25,4 +25,10 @@ int gb_fw_download_request_handler(struct gb_operation *op); int gb_fw_download_connection_init(struct gb_connection *connection); void gb_fw_download_connection_exit(struct gb_connection *connection); +/* CAP Protocol specific functions */ +int cap_init(void); +void cap_exit(void); +int gb_cap_connection_init(struct gb_connection *connection); +void gb_cap_connection_exit(struct gb_connection *connection); + #endif /* __FIRMWARE_H */ diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c index 33941efe13ea..963973242be0 100644 --- a/drivers/staging/greybus/fw-core.c +++ b/drivers/staging/greybus/fw-core.c @@ -17,6 +17,7 @@ struct gb_fw_core { struct gb_connection *download_connection; struct gb_connection *mgmt_connection; struct gb_connection *spi_connection; + struct gb_connection *cap_connection; }; struct gb_connection *to_fw_mgmt_connection(struct device *dev) @@ -134,6 +135,24 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, fw_core->spi_connection = connection; } + break; + case GREYBUS_PROTOCOL_AUTHENTICATION: + /* Disallow multiple CAP CPorts */ + if (fw_core->cap_connection) { + dev_err(&bundle->dev, "multiple Authentication CPorts found\n"); + ret = -EINVAL; + goto err_destroy_connections; + } + + connection = gb_connection_create(bundle, cport_id, + NULL); + if (IS_ERR(connection)) { + dev_err(&bundle->dev, "failed to create Authentication connection (%ld)\n", + PTR_ERR(connection)); + } else { + fw_core->cap_connection = connection; + } + break; default: dev_err(&bundle->dev, "invalid protocol id (0x%02x)\n", @@ -168,6 +187,15 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, fw_core->spi_connection = NULL; } + ret = gb_cap_connection_init(fw_core->cap_connection); + if (ret) { + /* We may still be able to work with the Interface */ + dev_err(&bundle->dev, "failed to initialize CAP connection, disable it (%d)\n", + ret); + gb_connection_destroy(fw_core->cap_connection); + fw_core->cap_connection = NULL; + } + ret = gb_fw_mgmt_connection_init(fw_core->mgmt_connection); if (ret) { /* We may still be able to work with the Interface */ @@ -181,10 +209,12 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, return 0; err_exit_connections: + gb_cap_connection_exit(fw_core->cap_connection); gb_fw_spi_connection_exit(fw_core->spi_connection); gb_fw_download_connection_exit(fw_core->download_connection); err_destroy_connections: gb_connection_destroy(fw_core->mgmt_connection); + gb_connection_destroy(fw_core->cap_connection); gb_connection_destroy(fw_core->spi_connection); gb_connection_destroy(fw_core->download_connection); kfree(fw_core); @@ -197,10 +227,12 @@ static void gb_fw_core_disconnect(struct gb_bundle *bundle) struct gb_fw_core *fw_core = greybus_get_drvdata(bundle); gb_fw_mgmt_connection_exit(fw_core->mgmt_connection); + gb_cap_connection_exit(fw_core->cap_connection); gb_fw_spi_connection_exit(fw_core->spi_connection); gb_fw_download_connection_exit(fw_core->download_connection); gb_connection_destroy(fw_core->mgmt_connection); + gb_connection_destroy(fw_core->cap_connection); gb_connection_destroy(fw_core->spi_connection); gb_connection_destroy(fw_core->download_connection); @@ -229,19 +261,32 @@ static int fw_core_init(void) return ret; } - ret = greybus_register(&gb_fw_core_driver); + ret = cap_init(); if (ret) { - fw_mgmt_exit(); - return ret; + pr_err("Failed to initialize component authentication core (%d)\n", + ret); + goto fw_mgmt_exit; } + ret = greybus_register(&gb_fw_core_driver); + if (ret) + goto cap_exit; + return 0; + +cap_exit: + cap_exit(); +fw_mgmt_exit: + fw_mgmt_exit(); + + return ret; } module_init(fw_core_init); static void __exit fw_core_exit(void) { greybus_deregister(&gb_fw_core_driver); + cap_exit(); fw_mgmt_exit(); } module_exit(fw_core_exit); diff --git a/drivers/staging/greybus/greybus_authentication.h b/drivers/staging/greybus/greybus_authentication.h new file mode 100644 index 000000000000..4784ed98e8a3 --- /dev/null +++ b/drivers/staging/greybus/greybus_authentication.h @@ -0,0 +1,120 @@ +/* + * Greybus Component Authentication User Header + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program 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 version 2 for more details. + * + * BSD LICENSE + * + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. or Linaro Ltd. 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 COPYRIGHT HOLDERS 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 GOOGLE INC. OR + * LINARO LTD. 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 __GREYBUS_AUTHENTICATION_USER_H +#define __GREYBUS_AUTHENTICATION_USER_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +#define CAP_CERTIFICATE_MAX_SIZE 1600 +#define CAP_SIGNATURE_MAX_SIZE 320 + +/* Certificate class types */ +#define CAP_CERT_IMS_EAPC 0x00000001 +#define CAP_CERT_IMS_EASC 0x00000002 +#define CAP_CERT_IMS_EARC 0x00000003 +#define CAP_CERT_IMS_IAPC 0x00000004 +#define CAP_CERT_IMS_IASC 0x00000005 +#define CAP_CERT_IMS_IARC 0x00000006 + +/* IMS Certificate response result codes */ +#define CAP_IMS_RESULT_CERT_FOUND 0x00 +#define CAP_IMS_RESULT_CERT_CLASS_INVAL 0x01 +#define CAP_IMS_RESULT_CERT_CORRUPT 0x02 +#define CAP_IMS_RESULT_CERT_NOT_FOUND 0x03 + +/* Authentication types */ +#define CAP_AUTH_IMS_PRI 0x00000001 +#define CAP_AUTH_IMS_SEC 0x00000002 +#define CAP_AUTH_IMS_RSA 0x00000003 + +/* Authenticate response result codes */ +#define CAP_AUTH_RESULT_CR_SUCCESS 0x00 +#define CAP_AUTH_RESULT_CR_BAD_TYPE 0x01 +#define CAP_AUTH_RESULT_CR_WRONG_EP 0x02 +#define CAP_AUTH_RESULT_CR_NO_KEY 0x03 +#define CAP_AUTH_RESULT_CR_SIG_FAIL 0x04 + + +/* IOCTL support */ +struct cap_ioc_get_endpoint_uid { + __u8 uid[8]; +} __attribute__ ((__packed__)); + +struct cap_ioc_get_ims_certificate { + __u32 certificate_class; + __u32 certificate_id; + + __u8 result_code; + __u32 cert_size; + __u8 certificate[CAP_CERTIFICATE_MAX_SIZE]; +} __attribute__ ((__packed__)); + +struct cap_ioc_authenticate { + __u32 auth_type; + __u8 uid[8]; + __u8 challenge[32]; + + __u8 result_code; + __u8 response[64]; + __u32 signature_size; + __u8 signature[CAP_SIGNATURE_MAX_SIZE]; +} __attribute__ ((__packed__)); + +#define CAP_IOCTL_BASE 'C' +#define CAP_IOC_GET_ENDPOINT_UID _IOR(CAP_IOCTL_BASE, 0, struct cap_ioc_get_endpoint_uid) +#define CAP_IOC_GET_IMS_CERTIFICATE _IOWR(CAP_IOCTL_BASE, 1, struct cap_ioc_get_ims_certificate) +#define CAP_IOC_AUTHENTICATE _IOWR(CAP_IOCTL_BASE, 2, struct cap_ioc_authenticate) + +#endif /* __GREYBUS_AUTHENTICATION_USER_H */ diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 2ae39ad92368..d135945cefe1 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -47,6 +47,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_CAMERA_DATA = 0x16, GREYBUS_PROTOCOL_FW_DOWNLOAD = 0x17, GREYBUS_PROTOCOL_FW_MANAGEMENT = 0x18, + GREYBUS_PROTOCOL_AUTHENTICATION = 0x19, GREYBUS_PROTOCOL_LOG = 0x1a, /* ... */ GREYBUS_PROTOCOL_RAW = 0xfe, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 327d01f3679a..0043b912f720 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -345,6 +345,43 @@ struct gb_fw_mgmt_backend_fw_updated_request { /* firmware management backend firmware updated response has no payload */ +/* Component Authentication Protocol (CAP) */ + +/* Request Types */ +#define GB_CAP_TYPE_GET_ENDPOINT_UID 0x01 +#define GB_CAP_TYPE_GET_IMS_CERTIFICATE 0x02 +#define GB_CAP_TYPE_AUTHENTICATE 0x03 + +/* CAP get endpoint uid request has no payload */ +struct gb_cap_get_endpoint_uid_response { + __u8 uid[8]; +} __packed; + +/* CAP get endpoint ims certificate request/response */ +struct gb_cap_get_ims_certificate_request { + __le32 certificate_class; + __le32 certificate_id; +} __packed; + +struct gb_cap_get_ims_certificate_response { + __u8 result_code; + __u8 certificate[0]; +} __packed; + +/* CAP authenticate request/response */ +struct gb_cap_authenticate_request { + __le32 auth_type; + __u8 uid[8]; + __u8 challenge[32]; +} __packed; + +struct gb_cap_authenticate_response { + __u8 result_code; + __u8 response[64]; + __u8 signature[0]; +} __packed; + + /* Bootrom Protocol */ /* Version of the Greybus bootrom protocol we support */ -- cgit v1.2.3-59-g8ed1b From 6f4219dd7df0b834caad344db05d9d06d71658cd Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 30 Jun 2016 10:54:00 -0500 Subject: greybus: Documentation: Document Authentication interfaces This patch defined userspace interface of the CAP protocol. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Jun Li <li_jun@projectara.com> Tested-by: Jun Li <li_jun@projectara.com> Signed-off-by: Alex Elder <elder@linaro.org> --- .../greybus/Documentation/firmware/authenticate.c | 139 +++++++++++++++++++++ .../Documentation/firmware/firmware-management | 134 +++++++++++++++++++- 2 files changed, 269 insertions(+), 4 deletions(-) create mode 100644 drivers/staging/greybus/Documentation/firmware/authenticate.c diff --git a/drivers/staging/greybus/Documentation/firmware/authenticate.c b/drivers/staging/greybus/Documentation/firmware/authenticate.c new file mode 100644 index 000000000000..ab0688ad1e37 --- /dev/null +++ b/drivers/staging/greybus/Documentation/firmware/authenticate.c @@ -0,0 +1,139 @@ +/* + * Sample code to test CAP protocol + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program 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 version 2 for more details. + * + * BSD LICENSE + * + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. or Linaro Ltd. 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 COPYRIGHT HOLDERS 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 GOOGLE INC. OR + * LINARO LTD. 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 <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "../../greybus_authentication.h" + +struct cap_ioc_get_endpoint_uid uid; +struct cap_ioc_get_ims_certificate cert = { + .certificate_class = 0, + .certificate_id = 0, +}; + +struct cap_ioc_authenticate authenticate = { + .auth_type = 0, + .challenge = {0}, +}; + +int main(int argc, char *argv[]) +{ + unsigned int timeout = 10000; + char *capdev; + int fd, ret; + + /* Make sure arguments are correct */ + if (argc != 2) { + printf("\nUsage: ./firmware <Path of the gb-cap-X dev>\n"); + return 0; + } + + capdev = argv[1]; + + printf("Opening %s authentication device\n", capdev); + + fd = open(capdev, O_RDWR); + if (fd < 0) { + printf("Failed to open: %s\n", capdev); + return -1; + } + + /* Get UID */ + printf("Get UID\n"); + + ret = ioctl(fd, CAP_IOC_GET_ENDPOINT_UID, &uid); + if (ret < 0) { + printf("Failed to get UID: %s (%d)\n", capdev, ret); + ret = -1; + goto close_fd; + } + + printf("UID received: 0x%llx\n", *(long long unsigned int *)(uid.uid)); + + /* Get certificate */ + printf("Get IMS certificate\n"); + + ret = ioctl(fd, CAP_IOC_GET_IMS_CERTIFICATE, &cert); + if (ret < 0) { + printf("Failed to get IMS certificate: %s (%d)\n", capdev, ret); + ret = -1; + goto close_fd; + } + + printf("IMS Certificate size: %d\n", cert.cert_size); + + /* Authenticate */ + printf("Authenticate module\n"); + + memcpy(authenticate.uid, uid.uid, 8); + + ret = ioctl(fd, CAP_IOC_AUTHENTICATE, &authenticate); + if (ret < 0) { + printf("Failed to authenticate module: %s (%d)\n", capdev, ret); + ret = -1; + goto close_fd; + } + + printf("Authenticated, result (%02x), sig-size (%02x)\n", + authenticate.result_code, authenticate.signature_size); + +close_fd: + close(fd); + + return ret; +} diff --git a/drivers/staging/greybus/Documentation/firmware/firmware-management b/drivers/staging/greybus/Documentation/firmware/firmware-management index 79977396ef00..6b7b6d814507 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware-management +++ b/drivers/staging/greybus/Documentation/firmware/firmware-management @@ -40,8 +40,7 @@ may look like: ; (Optional) Component Authentication Protocol (CAP) on CPort 4 [cport-descriptor 4] bundle = 1 - protocol = 0xXX //TBD - + protocol = 0x19 Sysfs Interfaces - Firmware Management @@ -164,6 +163,129 @@ struct fw_mgmt_ioc_backend_fw_update { allow mode-switch. +Sysfs Interfaces - Authentication +--------------------------------- + +The Component Authentication Protocol interacts with Userspace using the +character device interface. The character device will be present in /dev/ +directory and will be named gb-authenticate-<N>. The number <N> is assigned at +runtime. + +Identifying the Character Device +================================ + +There can be multiple devices present in /dev/ directory with name +gb-authenticate-N and user first needs to identify the character device used for +authentication a of particular interface. + +The Authentication core creates a device of class 'gb_authenticate', which shall +be used by the user to identify the right character device for it. The class +device is created within the Bundle directory for a particular Interface. + +For example this is how the class-device can be present: + +/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_authenticate/gb-authenticate-0 + +The last name in this path: gb-authenticate-0 is precisely the name of the char +device and so the device in this case will be: + +/dev/gb-authenticate-0. + +Operations on the Char device +============================= + +The Character device (/dev/gb-authenticate-0 in above example) can be opened by +the userspace application and it can perform various 'ioctl' operations on the +device. The device doesn't support any read/write operations. + +Following are the IOCTLs and their data structures available to the user: + +#define CAP_CERTIFICATE_MAX_SIZE 1600 +#define CAP_SIGNATURE_MAX_SIZE 320 + +/* Certificate class types */ +#define CAP_CERT_IMS_EAPC 0x00000001 +#define CAP_CERT_IMS_EASC 0x00000002 +#define CAP_CERT_IMS_EARC 0x00000003 +#define CAP_CERT_IMS_IAPC 0x00000004 +#define CAP_CERT_IMS_IASC 0x00000005 +#define CAP_CERT_IMS_IARC 0x00000006 + +/* IMS Certificate response result codes */ +#define CAP_IMS_RESULT_CERT_FOUND 0x00 +#define CAP_IMS_RESULT_CERT_CLASS_INVAL 0x01 +#define CAP_IMS_RESULT_CERT_CORRUPT 0x02 +#define CAP_IMS_RESULT_CERT_NOT_FOUND 0x03 + +/* Authentication types */ +#define CAP_AUTH_IMS_PRI 0x00000001 +#define CAP_AUTH_IMS_SEC 0x00000002 +#define CAP_AUTH_IMS_RSA 0x00000003 + +/* Authenticate response result codes */ +#define CAP_AUTH_RESULT_CR_SUCCESS 0x00 +#define CAP_AUTH_RESULT_CR_BAD_TYPE 0x01 +#define CAP_AUTH_RESULT_CR_WRONG_EP 0x02 +#define CAP_AUTH_RESULT_CR_NO_KEY 0x03 +#define CAP_AUTH_RESULT_CR_SIG_FAIL 0x04 + + +/* IOCTL support */ +struct cap_ioc_get_endpoint_uid { + __u8 uid[8]; +} __attribute__ ((__packed__)); + +struct cap_ioc_get_ims_certificate { + __u32 certificate_class; + __u32 certificate_id; + + __u8 result_code; + __u32 cert_size; + __u8 certificate[CAP_CERTIFICATE_MAX_SIZE]; +} __attribute__ ((__packed__)); + +struct cap_ioc_authenticate { + __u32 auth_type; + __u8 uid[8]; + __u8 challenge[32]; + + __u8 result_code; + __u8 response[64]; + __u32 signature_size; + __u8 signature[CAP_SIGNATURE_MAX_SIZE]; +} __attribute__ ((__packed__)); + +#define CAP_IOCTL_BASE 'C' +#define CAP_IOC_GET_ENDPOINT_UID _IOR(CAP_IOCTL_BASE, 0, struct cap_ioc_get_endpoint_uid) +#define CAP_IOC_GET_IMS_CERTIFICATE _IOWR(CAP_IOCTL_BASE, 1, struct cap_ioc_get_ims_certificate) +#define CAP_IOC_AUTHENTICATE _IOWR(CAP_IOCTL_BASE, 2, struct cap_ioc_authenticate) + + +1. CAP_IOC_GET_ENDPOINT_UID: + + This ioctl shall be used the user to get the endpoint UID associated with the + Interface. All the fields of the 'struct cap_ioc_get_endpoint_uid' are + filled by the kernel. + +2. CAP_IOC_GET_IMS_CERTIFICATE: + + This ioctl shall be used the user to retrieve one of the available + cryptographic certificates held by the Interface for use in Component + Authentication. The user is required to fill the 'certificate_class' and + 'certificate_id' field of the 'struct cap_ioc_get_ims_certificate' in this + case. The other fields will be set by the kernel in response. The first + 'cert_size' bytes of the 'certificate' shall be read by the user and others + must be discarded. + +3. CAP_IOC_AUTHENTICATE: + + This ioctl shall be used the user to authenticate the Module attached to an + Interface. The user needs to fill the 'auth_type', 'uid', and 'challenge' + fields of the 'struct cap_ioc_authenticate'. The other fields will be set by + the kernel in response. The first 'signature_size' bytes of the 'signature' + shall be read by the user and others must be discarded. + + Sysfs Interfaces - Firmware Download ------------------------------------ @@ -182,9 +304,13 @@ $ ls /sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/spi_master/spi32766/spi32766.0/m mtd0 mtd0ro -Sample Application ------------------- +Sample Applications +------------------- The current directory also provides a firmware.c test application, which can be referenced while developing userspace application to talk to firmware-management protocol. + +The current directory also provides a authenticate.c test application, which can +be referenced while developing userspace application to talk to +component authentication protocol. -- cgit v1.2.3-59-g8ed1b From 776165481d8ed956ccb92465f97ba727eed53e4c Mon Sep 17 00:00:00 2001 From: Fabien Parent <fparent@baylibre.com> Date: Wed, 6 Jul 2016 10:51:00 -0500 Subject: greybus: connection: remove CDSI1 hack This hack is not necessary anymore since the firmware is now able to handle correctly (dis)connect{ed,ing} operations on the CDSI CPort. Testing Done: Checked that I could start the camera app several time and got the preview each time. Signed-off-by: Fabien Parent <fparent@baylibre.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/connection.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 632c8be43cf0..174a52d5f524 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -442,16 +442,6 @@ static int gb_connection_control_connected(struct gb_connection *connection) if (gb_connection_is_static(connection)) return 0; - /* - * HACK: Suppress connected request for the offloaded camera - * connection as it is currently not supported by firmware. Note that - * the corresponding non-fatal disconnected event is still sent. - */ - if (gb_connection_is_offloaded(connection) && - connection->flags & GB_CONNECTION_FLAG_CDSI1) { - return 0; - } - if (gb_connection_is_control(connection)) return 0; -- cgit v1.2.3-59-g8ed1b From 2c8e8841e3b8cea90cc9b7172eebfdf90b06038a Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: control: add bundle suspend and resume preparations Add the AP implementation for the Greybus Control Bundle Suspend Operation. This Operation is used to request a Bundle to enter the BUNDLE_SUSPENDED state, all Connections associated with this Bundle must be closed before issuing this operation. Add the AP implementation for the Greybus Control Bundle Resume Operation. This operation request a specific Bundle to transition from the BUNDLE_SUSPENDED state to the BUNDLE_ACTIVE state. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/control.c | 65 +++++++++++++++++++++++++++++ drivers/staging/greybus/control.h | 3 +- drivers/staging/greybus/greybus_protocols.h | 21 ++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 88e5718965d6..a95c776f17a1 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -233,6 +233,71 @@ int gb_control_timesync_authoritative(struct gb_control *control, NULL, 0); } +static int gb_control_bundle_pm_status_map(u8 status) +{ + switch (status) { + case GB_CONTROL_BUNDLE_PM_INVAL: + return -EINVAL; + case GB_CONTROL_BUNDLE_PM_BUSY: + return -EBUSY; + case GB_CONTROL_BUNDLE_PM_NA: + return -ENOMSG; + case GB_CONTROL_BUNDLE_PM_FAIL: + default: + return -EREMOTEIO; + } +} + +int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id) +{ + struct gb_control_bundle_pm_request request; + struct gb_control_bundle_pm_response response; + int ret; + + request.bundle_id = bundle_id; + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_BUNDLE_SUSPEND, &request, + sizeof(request), &response, sizeof(response)); + if (ret) { + dev_err(&control->dev, + "failed to send bundle suspend: %d\n", ret); + return ret; + } + + if (response.status != GB_CONTROL_BUNDLE_PM_OK) { + dev_err(&control->dev, + "bundle error while suspending: %d\n", response.status); + return gb_control_bundle_pm_status_map(response.status); + } + + return 0; +} + +int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id) +{ + struct gb_control_bundle_pm_request request; + struct gb_control_bundle_pm_response response; + int ret; + + request.bundle_id = bundle_id; + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_BUNDLE_RESUME, &request, + sizeof(request), &response, sizeof(response)); + if (ret) { + dev_err(&control->dev, + "failed to send bundle resume: %d\n", ret); + return ret; + } + + if (response.status != GB_CONTROL_BUNDLE_PM_OK) { + dev_err(&control->dev, + "bundle error while resuming: %d\n", response.status); + return gb_control_bundle_pm_status_map(response.status); + } + + return 0; +} + static ssize_t vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index b1e5af25352a..c7f34635ea92 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -52,5 +52,6 @@ int gb_control_timesync_get_last_event(struct gb_control *control, u64 *frame_time); int gb_control_timesync_authoritative(struct gb_control *control, u64 *frame_time); - +int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id); +int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id); #endif /* __CONTROL_H */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 0043b912f720..3b6fd0268529 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -126,6 +126,8 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_DISCONNECTING 0x0c #define GB_CONTROL_TYPE_TIMESYNC_GET_LAST_EVENT 0x0d #define GB_CONTROL_TYPE_MODE_SWITCH 0x0e +#define GB_CONTROL_TYPE_BUNDLE_SUSPEND 0x0f +#define GB_CONTROL_TYPE_BUNDLE_RESUME 0x10 struct gb_control_version_request { __u8 major; @@ -191,6 +193,25 @@ struct gb_control_timesync_get_last_event_response { __le64 frame_time; } __packed; +/* + * All Bundle power management operations use the same request and response + * layout and status codes. + */ + +#define GB_CONTROL_BUNDLE_PM_OK 0x00 +#define GB_CONTROL_BUNDLE_PM_INVAL 0x01 +#define GB_CONTROL_BUNDLE_PM_BUSY 0x02 +#define GB_CONTROL_BUNDLE_PM_FAIL 0x03 +#define GB_CONTROL_BUNDLE_PM_NA 0x04 + +struct gb_control_bundle_pm_request { + __u8 bundle_id; +} __packed; + +struct gb_control_bundle_pm_response { + __u8 status; +} __packed; + /* APBridge protocol */ /* request APB1 log */ -- cgit v1.2.3-59-g8ed1b From f61908f94a9fc550d4619224263fff16cddcc830 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: control: add bundle deactivate and activate operation Add the AP implementation for the Greybus Control Bundle Deactivate Operation. This operation requests a Bundle to enter the BUNDLE_OFF state. All Connections associated with the Bundle must be closed prior sending this operation. Add the AP implementation for the Greybus Control Bundle Activate Operation. This operation requests a specific Bundle to transition from the BUNDLE_OFF state to the BUNDLE_ACTIVE state. [elder@linaro.org: fixed a typo pointed out by Johan] Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/control.c | 50 +++++++++++++++++++++++++++++ drivers/staging/greybus/control.h | 2 ++ drivers/staging/greybus/greybus_protocols.h | 2 ++ 3 files changed, 54 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index a95c776f17a1..a53fa3d68280 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -298,6 +298,56 @@ int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id) return 0; } +int gb_control_bundle_deactivate(struct gb_control *control, u8 bundle_id) +{ + struct gb_control_bundle_pm_request request; + struct gb_control_bundle_pm_response response; + int ret; + + request.bundle_id = bundle_id; + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_BUNDLE_DEACTIVATE, &request, + sizeof(request), &response, sizeof(response)); + if (ret) { + dev_err(&control->dev, + "failed to send bundle deactivate: %d\n", ret); + return ret; + } + + if (response.status != GB_CONTROL_BUNDLE_PM_OK) { + dev_err(&control->dev, + "bundle error while deactivating: %d\n", response.status); + return gb_control_bundle_pm_status_map(response.status); + } + + return 0; +} + +int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id) +{ + struct gb_control_bundle_pm_request request; + struct gb_control_bundle_pm_response response; + int ret; + + request.bundle_id = bundle_id; + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_BUNDLE_ACTIVATE, &request, + sizeof(request), &response, sizeof(response)); + if (ret) { + dev_err(&control->dev, + "failed to send bundle activate: %d\n", ret); + return ret; + } + + if (response.status != GB_CONTROL_BUNDLE_PM_OK) { + dev_err(&control->dev, + "bundle error while activating: %d\n", response.status); + return gb_control_bundle_pm_status_map(response.status); + } + + return 0; +} + static ssize_t vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index c7f34635ea92..5ddf0138c6c5 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -54,4 +54,6 @@ int gb_control_timesync_authoritative(struct gb_control *control, u64 *frame_time); int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id); int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id); +int gb_control_bundle_deactivate(struct gb_control *control, u8 bundle_id); +int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id); #endif /* __CONTROL_H */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 3b6fd0268529..1ae335040139 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -128,6 +128,8 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_MODE_SWITCH 0x0e #define GB_CONTROL_TYPE_BUNDLE_SUSPEND 0x0f #define GB_CONTROL_TYPE_BUNDLE_RESUME 0x10 +#define GB_CONTROL_TYPE_BUNDLE_DEACTIVATE 0x11 +#define GB_CONTROL_TYPE_BUNDLE_ACTIVATE 0x12 struct gb_control_version_request { __u8 major; -- cgit v1.2.3-59-g8ed1b From f53be0eaf0603324ea90eb352df0ffa18dde646a Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: control: add interface suspend prepare operation Add the AP implementation for the Greybus Control Interface Suspend Prepare Operation. AP uses this Operation during the Suspend transition to request the Interface to enter a low-power mode after it detects a subsequent UniPro link hibernation. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/control.c | 35 +++++++++++++++++++++++++++++ drivers/staging/greybus/control.h | 1 + drivers/staging/greybus/greybus_protocols.h | 15 +++++++++++++ 3 files changed, 51 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index a53fa3d68280..252352ce3664 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -348,6 +348,41 @@ int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id) return 0; } +static int gb_control_interface_pm_status_map(u8 status) +{ + switch (status) { + case GB_CONTROL_INTF_PM_BUSY: + return -EBUSY; + case GB_CONTROL_INTF_PM_NA: + return -ENOMSG; + default: + return -EREMOTEIO; + } +} + +int gb_control_interface_suspend_prepare(struct gb_control *control) +{ + struct gb_control_intf_pm_response response; + int ret; + + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_INTF_SUSPEND_PREPARE, NULL, 0, + &response, sizeof(response)); + if (ret) { + dev_err(&control->dev, + "failed to send interface suspend prepare: %d\n", ret); + return ret; + } + + if (response.status != GB_CONTROL_INTF_PM_OK) { + dev_err(&control->dev, "interface error while preparing suspend: %d\n", + response.status); + return gb_control_interface_pm_status_map(response.status); + } + + return 0; +} + static ssize_t vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 5ddf0138c6c5..bc32ca78d225 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -56,4 +56,5 @@ int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id); int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id); int gb_control_bundle_deactivate(struct gb_control *control, u8 bundle_id); int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id); +int gb_control_interface_suspend_prepare(struct gb_control *control); #endif /* __CONTROL_H */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 1ae335040139..cd64a99debfa 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -130,6 +130,7 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_BUNDLE_RESUME 0x10 #define GB_CONTROL_TYPE_BUNDLE_DEACTIVATE 0x11 #define GB_CONTROL_TYPE_BUNDLE_ACTIVATE 0x12 +#define GB_CONTROL_TYPE_INTF_SUSPEND_PREPARE 0x13 struct gb_control_version_request { __u8 major; @@ -214,6 +215,20 @@ struct gb_control_bundle_pm_response { __u8 status; } __packed; +/* + * Interface Suspend Prepare and Deactivate Prepare operations use the same + * response layout and error codes. Define a single response structure and reuse + * it. Both operations have no payload. + */ + +#define GB_CONTROL_INTF_PM_OK 0x00 +#define GB_CONTROL_INTF_PM_BUSY 0x01 +#define GB_CONTROL_INTF_PM_NA 0x02 + +struct gb_control_intf_pm_response { + __u8 status; +} __packed; + /* APBridge protocol */ /* request APB1 log */ -- cgit v1.2.3-59-g8ed1b From 30ea26bdd403396c62d6c9fa26477ca1f7af5676 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: control: add interface deactivate prepare operation Add the AP implementation for the Greybus Control Interface Deactivate Prepare Operation. AP uses this Operation during the Power Down transition to request the bridge to power down after it detects a subsequent UniPro link hibernation. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/control.c | 23 +++++++++++++++++++++++ drivers/staging/greybus/control.h | 1 + drivers/staging/greybus/greybus_protocols.h | 1 + 3 files changed, 25 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 252352ce3664..a1c78fc68509 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -383,6 +383,29 @@ int gb_control_interface_suspend_prepare(struct gb_control *control) return 0; } +int gb_control_interface_deactivate_prepare(struct gb_control *control) +{ + struct gb_control_intf_pm_response response; + int ret; + + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_INTF_DEACTIVATE_PREPARE, NULL, + 0, &response, sizeof(response)); + if (ret) { + dev_err(&control->dev, "failed to send interface deactivate prepare: %d\n", + ret); + return ret; + } + + if (response.status != GB_CONTROL_INTF_PM_OK) { + dev_err(&control->dev, "interface error while preparing deactivate: %d\n", + response.status); + return gb_control_interface_pm_status_map(response.status); + } + + return 0; +} + static ssize_t vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index bc32ca78d225..2f76ae48b2d8 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -57,4 +57,5 @@ int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id); int gb_control_bundle_deactivate(struct gb_control *control, u8 bundle_id); int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id); int gb_control_interface_suspend_prepare(struct gb_control *control); +int gb_control_interface_deactivate_prepare(struct gb_control *control); #endif /* __CONTROL_H */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index cd64a99debfa..10209ae61e1b 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -131,6 +131,7 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_BUNDLE_DEACTIVATE 0x11 #define GB_CONTROL_TYPE_BUNDLE_ACTIVATE 0x12 #define GB_CONTROL_TYPE_INTF_SUSPEND_PREPARE 0x13 +#define GB_CONTROL_TYPE_INTF_DEACTIVATE_PREPARE 0x14 struct gb_control_version_request { __u8 major; -- cgit v1.2.3-59-g8ed1b From 385227fce67959a1ab2094fdc9afb78ac19a0dfe Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: control: add interface hibernate abort operation Add the AP implementation for the Greybus Control Hibernate Abort Operation. AP may use this Operation to abort a previous Control Interface Suspend or Control Interface Deactivate Prepare Operation. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/control.c | 24 ++++++++++++++++++++++++ drivers/staging/greybus/control.h | 1 + drivers/staging/greybus/greybus_protocols.h | 1 + 3 files changed, 26 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index a1c78fc68509..55bb465e16f1 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -406,6 +406,30 @@ int gb_control_interface_deactivate_prepare(struct gb_control *control) return 0; } +int gb_control_interface_hibernate_abort(struct gb_control *control) +{ + struct gb_control_intf_pm_response response; + int ret; + + ret = gb_operation_sync(control->connection, + GB_CONTROL_TYPE_INTF_HIBERNATE_ABORT, NULL, 0, + &response, sizeof(response)); + if (ret) { + dev_err(&control->dev, + "failed to send interface aborting hibernate: %d\n", + ret); + return ret; + } + + if (response.status != GB_CONTROL_INTF_PM_OK) { + dev_err(&control->dev, "interface error while aborting hibernate: %d\n", + response.status); + return gb_control_interface_pm_status_map(response.status); + } + + return 0; +} + static ssize_t vendor_string_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 2f76ae48b2d8..86a4902604b6 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -58,4 +58,5 @@ int gb_control_bundle_deactivate(struct gb_control *control, u8 bundle_id); int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id); int gb_control_interface_suspend_prepare(struct gb_control *control); int gb_control_interface_deactivate_prepare(struct gb_control *control); +int gb_control_interface_hibernate_abort(struct gb_control *control); #endif /* __CONTROL_H */ diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 10209ae61e1b..dfee73716df7 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -132,6 +132,7 @@ struct gb_protocol_version_response { #define GB_CONTROL_TYPE_BUNDLE_ACTIVATE 0x12 #define GB_CONTROL_TYPE_INTF_SUSPEND_PREPARE 0x13 #define GB_CONTROL_TYPE_INTF_DEACTIVATE_PREPARE 0x14 +#define GB_CONTROL_TYPE_INTF_HIBERNATE_ABORT 0x15 struct gb_control_version_request { __u8 major; -- cgit v1.2.3-59-g8ed1b From fc8a4027135252b49650a4dbfcf2a8fb434a6e6a Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: svc: add interface resume operation Add the AP implementation for the Greybus SVC Interface Resume Operation. This operation allows the AP to request the SVC to resume an Interface which was previously SUSPENDED, allowing it to later be ENUMERATED. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/greybus_protocols.h | 9 +++++++++ drivers/staging/greybus/svc.c | 29 +++++++++++++++++++++++++++++ drivers/staging/greybus/svc.h | 2 ++ 3 files changed, 40 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index dfee73716df7..1a12531944e9 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1001,6 +1001,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_UNIPRO_ENABLE 0x25 #define GB_SVC_TYPE_INTF_UNIPRO_DISABLE 0x26 #define GB_SVC_TYPE_INTF_ACTIVATE 0x27 +#define GB_SVC_TYPE_INTF_RESUME 0x28 #define GB_SVC_TYPE_INTF_MAILBOX_EVENT 0x29 /* Greybus SVC protocol status values */ @@ -1321,6 +1322,14 @@ struct gb_svc_intf_activate_response { __u8 intf_type; } __packed; +struct gb_svc_intf_resume_request { + __u8 intf_id; +} __packed; + +struct gb_svc_intf_resume_response { + __u8 status; +} __packed; + #define GB_SVC_INTF_MAILBOX_NONE 0x00 #define GB_SVC_INTF_MAILBOX_AP 0x01 #define GB_SVC_INTF_MAILBOX_GREYBUS 0x02 diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 63d2a53b624f..a46d7fb0139b 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -14,6 +14,7 @@ #define SVC_INTF_EJECT_TIMEOUT 9000 #define SVC_INTF_ACTIVATE_TIMEOUT 6000 +#define SVC_INTF_RESUME_TIMEOUT 3000 struct gb_svc_deferred_request { struct work_struct work; @@ -354,6 +355,34 @@ int gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type) return 0; } +int gb_svc_intf_resume(struct gb_svc *svc, u8 intf_id) +{ + struct gb_svc_intf_resume_request request; + struct gb_svc_intf_resume_response response; + int ret; + + request.intf_id = intf_id; + + ret = gb_operation_sync_timeout(svc->connection, + GB_SVC_TYPE_INTF_RESUME, + &request, sizeof(request), + &response, sizeof(response), + SVC_INTF_RESUME_TIMEOUT); + if (ret < 0) { + dev_err(&svc->dev, "failed to send interface resume %u: %d\n", + intf_id, ret); + return ret; + } + + if (response.status != GB_SVC_OP_SUCCESS) { + dev_err(&svc->dev, "failed to resume interface %u: %u\n", + intf_id, response.status); + return -EREMOTEIO; + } + + return 0; +} + int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value) { diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 7f5e534238bc..750eaff7d0cd 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -71,6 +71,8 @@ int gb_svc_intf_vsys_set(struct gb_svc *svc, u8 intf_id, bool enable); int gb_svc_intf_refclk_set(struct gb_svc *svc, u8 intf_id, bool enable); int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable); int gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type); +int gb_svc_intf_resume(struct gb_svc *svc, u8 intf_id); + int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 *value); int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, -- cgit v1.2.3-59-g8ed1b From c7dc28ff2b47d6dc4efd420b6f1325554b6f8287 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: svc: add power mode call for link hibernation Due to when using set_power_mode to hibernate a link, it won't trigger a POWERMODEIND event, hence the hard-coded GB_SVC_SETPWRM_PWR_OK would be returned and it should also be considered as successful result code for link hibernation. Therefore, adding this set_power_mode_hibernate function to separate the two calls in order to check with the correct result code. Testing Done: - Suspend an Interface and observe no set power mode error. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/svc.c | 35 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/svc.h | 1 + 2 files changed, 36 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index a46d7fb0139b..14bada965ddd 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -687,6 +687,41 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, } EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); +int gb_svc_intf_set_power_mode_hibernate(struct gb_svc *svc, u8 intf_id) +{ + struct gb_svc_intf_set_pwrm_request request; + struct gb_svc_intf_set_pwrm_response response; + int ret; + u16 result_code; + + memset(&request, 0, sizeof(request)); + + request.intf_id = intf_id; + request.hs_series = GB_SVC_UNIPRO_HS_SERIES_A; + request.tx_mode = GB_SVC_UNIPRO_HIBERNATE_MODE; + request.rx_mode = GB_SVC_UNIPRO_HIBERNATE_MODE; + + ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, + &request, sizeof(request), + &response, sizeof(response)); + if (ret < 0) { + dev_err(&svc->dev, + "failed to send set power mode operation to interface %u: %d\n", + intf_id, ret); + return ret; + } + + result_code = response.result_code; + if (result_code != GB_SVC_SETPWRM_PWR_OK) { + dev_err(&svc->dev, + "failed to hibernate the link for interface %u: %u\n", + intf_id, result_code); + return -EIO; + } + + return 0; +} + int gb_svc_ping(struct gb_svc *svc) { return gb_operation_sync_timeout(svc->connection, GB_SVC_TYPE_PING, diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 750eaff7d0cd..4ab066b90a5b 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -84,6 +84,7 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, u8 flags, u32 quirks, struct gb_svc_l2_timer_cfg *local, struct gb_svc_l2_timer_cfg *remote); +int gb_svc_intf_set_power_mode_hibernate(struct gb_svc *svc, u8 intf_id); int gb_svc_ping(struct gb_svc *svc); int gb_svc_watchdog_create(struct gb_svc *svc); void gb_svc_watchdog_destroy(struct gb_svc *svc); -- cgit v1.2.3-59-g8ed1b From cc28c2c2283d5e1b8d2fc0a1cf2bc45783fc7f71 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: interface: implement unipro link hibernate call Adds AP implementation of unipro link hibernation set power mode call needed for proper cport closure and interface suspend and power off transition. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/interface.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 05d0020d3a8d..2290a9c3ff9a 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -641,11 +641,9 @@ static int gb_interface_activate_operation(struct gb_interface *intf) static int gb_interface_hibernate_link(struct gb_interface *intf) { - dev_dbg(&intf->dev, "%s\n", __func__); - - /* FIXME: implement */ + struct gb_svc *svc = intf->hd->svc; - return 0; + return gb_svc_intf_set_power_mode_hibernate(svc, intf->interface_id); } /* -- cgit v1.2.3-59-g8ed1b From 707a5c42ce55938de599e4461384efafd6950c37 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: control: add connection suspend and resume calls Adds function calls for handling control connection suspend and resume, for now all they do is disable and enable control connection. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/control.c | 21 +++++++++++++++++++++ drivers/staging/greybus/control.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 55bb465e16f1..37a30b3075ce 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -546,6 +546,27 @@ void gb_control_disable(struct gb_control *control) gb_connection_disable(control->connection); } +int gb_control_suspend(struct gb_control *control) +{ + gb_connection_disable(control->connection); + + return 0; +} + +int gb_control_resume(struct gb_control *control) +{ + int ret; + + ret = gb_connection_enable_tx(control->connection); + if (ret) { + dev_err(&control->connection->intf->dev, + "failed to enable control connection: %d\n", ret); + return ret; + } + + return 0; +} + int gb_control_add(struct gb_control *control) { int ret; diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index 86a4902604b6..f73ec3e297ba 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -29,6 +29,8 @@ struct gb_control { struct gb_control *gb_control_create(struct gb_interface *intf); int gb_control_enable(struct gb_control *control); void gb_control_disable(struct gb_control *control); +int gb_control_suspend(struct gb_control *control); +int gb_control_resume(struct gb_control *control); int gb_control_add(struct gb_control *control); void gb_control_del(struct gb_control *control); struct gb_control *gb_control_get(struct gb_control *control); -- cgit v1.2.3-59-g8ed1b From 576bffb5a84f205c9ada777bc201b95cd8c0b868 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 7 Jul 2016 22:07:00 -0500 Subject: greybus: interface: send deactivate prepare when interface is disabled The AP Interface shall exchange a Greybus Control Interface Deactivate Prepare Operation with the Interface being powered down. Testing Done: - Check for the return code after sending the deactivate prepare operation Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/interface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 2290a9c3ff9a..e7efc541895a 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -861,6 +861,9 @@ void gb_interface_disable(struct gb_interface *intf) list_for_each_entry_safe(bundle, next, &intf->bundles, links) gb_bundle_destroy(bundle); + if (!intf->mode_switch && !intf->disconnected) + gb_control_interface_deactivate_prepare(intf->control); + gb_timesync_interface_remove(intf); gb_control_del(intf->control); gb_control_disable(intf->control); -- cgit v1.2.3-59-g8ed1b From 9d9d3777a9db5c0773d270e51b65c1252856d95e Mon Sep 17 00:00:00 2001 From: Alexandre Bailon <abailon@baylibre.com> Date: Thu, 7 Jul 2016 07:41:00 -0500 Subject: greybus: es2: Add a new bulk in endpoint for APBridgeA RPC Add a new bulk in endpoint in order to get RPC status from APbridgeA without to have to poll on control endpoint. So the new endpoint layout is: EP0: control EP EP1 and EP2: muxed endpoints (bulk in and out) EP3 to EP14: direct muxed endpoints (bulk in and out) EP15: ARPC bulk in endpoint Note: This patch is allocating ARPC URBs but does nothing with them. The following patch will use them. Testing Done: Tested with an APBridgeA enumerating 16 endpoints. Kernel doesn't print Not enough endpoints found in device, aborting! Signed-off-by: Alexandre Bailon <abailon@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/es2.c | 140 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 5bdf2e1298ad..8ebbd704f3b8 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -25,6 +25,9 @@ /* Memory sizes for the buffers sent to/from the ES2 controller */ #define ES2_GBUF_MSG_SIZE_MAX 2048 +/* Memory sizes for the ARPC buffers */ +#define ARPC_IN_SIZE_MAX 128 + static const struct usb_device_id id_table[] = { { USB_DEVICE(0x18d1, 0x1eaf) }, { }, @@ -36,6 +39,12 @@ MODULE_DEVICE_TABLE(usb, id_table); /* Number of bulk in and bulk out couple */ #define NUM_BULKS 7 +/* Expected number of bulk out endpoints */ +#define NUM_BULKS_OUT NUM_BULKS + +/* Expected number of bulk in endpoints (including ARPC endpoint) */ +#define NUM_BULKS_IN (NUM_BULKS + 1) + /* * Number of CPort IN urbs in flight at any point in time. * Adjust if we are having stalls in the USB buffer due to not enough urbs in @@ -48,6 +57,11 @@ MODULE_DEVICE_TABLE(usb, id_table); */ #define NUM_CPORT_OUT_URB (8 * NUM_BULKS) +/* + * Number of ARPC in urbs in flight at any point in time. + */ +#define NUM_ARPC_IN_URB 2 + /* * @endpoint: bulk in endpoint for CPort data * @urb: array of urbs for the CPort in messages @@ -85,6 +99,9 @@ struct es2_cport_out { * @apb_log_dentry: file system entry for the log file interface * @apb_log_enable_dentry: file system entry for enabling logging * @apb_log_fifo: kernel FIFO to carry logged data + * @arpc_urb: array of urbs for the ARPC in messages + * @arpc_buffer: array of buffers for the @arpc_urb urbs + * @arpc_endpoint_in: bulk in endpoint for APBridgeA RPC */ struct es2_ap_dev { struct usb_device *usb_dev; @@ -106,6 +123,10 @@ struct es2_ap_dev { struct dentry *apb_log_dentry; struct dentry *apb_log_enable_dentry; DECLARE_KFIFO(apb_log_fifo, char, APB1_LOG_SIZE); + + __u8 arpc_endpoint_in; + struct urb *arpc_urb[NUM_ARPC_IN_URB]; + u8 *arpc_buffer[NUM_ARPC_IN_URB]; }; /** @@ -344,6 +365,45 @@ static void es2_cport_in_disable(struct es2_ap_dev *es2, } } +static int es2_arpc_in_enable(struct es2_ap_dev *es2) +{ + struct urb *urb; + int ret; + int i; + + for (i = 0; i < NUM_ARPC_IN_URB; ++i) { + urb = es2->arpc_urb[i]; + + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + dev_err(&es2->usb_dev->dev, + "failed to submit arpc in-urb: %d\n", ret); + goto err_kill_urbs; + } + } + + return 0; + +err_kill_urbs: + for (--i; i >= 0; --i) { + urb = es2->arpc_urb[i]; + usb_kill_urb(urb); + } + + return ret; +} + +static void es2_arpc_in_disable(struct es2_ap_dev *es2) +{ + struct urb *urb; + int i; + + for (i = 0; i < NUM_ARPC_IN_URB; ++i) { + urb = es2->arpc_urb[i]; + usb_kill_urb(urb); + } +} + static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask) { struct urb *urb = NULL; @@ -899,6 +959,16 @@ static void es2_destroy(struct es2_ap_dev *es2) es2->cport_out_urb_busy[i] = false; /* just to be anal */ } + for (i = 0; i < NUM_ARPC_IN_URB; ++i) { + struct urb *urb = es2->arpc_urb[i]; + + if (!urb) + break; + usb_free_urb(urb); + kfree(es2->arpc_buffer[i]); + es2->arpc_buffer[i] = NULL; + } + for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { struct es2_cport_in *cport_in = &es2->cport_in[bulk_in]; @@ -991,6 +1061,31 @@ static void cport_out_callback(struct urb *urb) free_urb(es2, urb); } +static void arpc_in_callback(struct urb *urb) +{ + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + + /* The urb is being unlinked */ + if (status == -ENOENT || status == -ESHUTDOWN) + return; + + dev_err(dev, "arpc in-urb error %d (dropped)\n", status); + return; + } + +exit: + /* put our urb back in the request pool */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "failed to resubmit arpc in-urb: %d\n", retval); +} + #define APB1_LOG_MSG_SIZE 64 static void apb_log_get(struct es2_ap_dev *es2, char *buf) { @@ -1225,8 +1320,13 @@ static int ap_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[i].desc; if (usb_endpoint_is_bulk_in(endpoint)) { - es2->cport_in[bulk_in++].endpoint = - endpoint->bEndpointAddress; + if (bulk_in < NUM_BULKS) + es2->cport_in[bulk_in].endpoint = + endpoint->bEndpointAddress; + else + es2->arpc_endpoint_in = + endpoint->bEndpointAddress; + bulk_in++; } else if (usb_endpoint_is_bulk_out(endpoint)) { es2->cport_out[bulk_out++].endpoint = endpoint->bEndpointAddress; @@ -1236,7 +1336,7 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) { + if (bulk_in != NUM_BULKS_IN || bulk_out != NUM_BULKS_OUT) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); retval = -ENODEV; goto error; @@ -1271,6 +1371,32 @@ static int ap_probe(struct usb_interface *interface, } } + /* Allocate buffers for ARPC in messages */ + for (i = 0; i < NUM_ARPC_IN_URB; ++i) { + struct urb *urb; + u8 *buffer; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + retval = -ENOMEM; + goto error; + } + buffer = kmalloc(ARPC_IN_SIZE_MAX, GFP_KERNEL); + if (!buffer) { + retval = -ENOMEM; + goto error; + } + + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, + es2->arpc_endpoint_in), + buffer, ARPC_IN_SIZE_MAX, + arpc_in_callback, es2); + + es2->arpc_urb[i] = urb; + es2->arpc_buffer[i] = buffer; + } + /* Allocate urbs for our CPort OUT messages */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { struct urb *urb; @@ -1291,9 +1417,12 @@ static int ap_probe(struct usb_interface *interface, gb_debugfs_get(), es2, &apb_log_enable_fops); + if (es2_arpc_in_enable(es2)) + goto error; + retval = gb_hd_add(hd); if (retval) - goto error; + goto err_disable_arpc_in; for (i = 0; i < NUM_BULKS; ++i) { retval = es2_cport_in_enable(es2, &es2->cport_in[i]); @@ -1307,6 +1436,8 @@ err_disable_cport_in: for (--i; i >= 0; --i) es2_cport_in_disable(es2, &es2->cport_in[i]); gb_hd_del(hd); +err_disable_arpc_in: + es2_arpc_in_disable(es2); error: es2_destroy(es2); @@ -1322,6 +1453,7 @@ static void ap_disconnect(struct usb_interface *interface) for (i = 0; i < NUM_BULKS; ++i) es2_cport_in_disable(es2, &es2->cport_in[i]); + es2_arpc_in_disable(es2); es2_destroy(es2); } -- cgit v1.2.3-59-g8ed1b From c14118a8411c4d7ad1dd6dd501beb33ae1268b08 Mon Sep 17 00:00:00 2001 From: Alexandre Bailon <abailon@baylibre.com> Date: Thu, 7 Jul 2016 07:41:00 -0500 Subject: greybus: es2: Implement APBridgeA RPC (ARPC) Implement ARPC. In first time, we are going to use it to implement new request but the goal is to update all existing vendor request to use ARPC. In addition, Convert the current USB Vendor request for CPort Reset to ARPC so that we can be sure that the port has been fully reset by the time the request completes. Testing Done: AP can reset APBA Cports by using the ARPC command. In addition, tested with a hacked firmware that cause error during the Cport reset, and Greybus printed the error "failed to reset cport". Signed-off-by: Alexandre Bailon <abailon@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/es2.c | 203 ++++++++++++++++++++++++++-- drivers/staging/greybus/greybus_protocols.h | 32 +++++ 2 files changed, 226 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 8ebbd704f3b8..b763b27ce9ef 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -11,6 +11,7 @@ #include <linux/usb.h> #include <linux/kfifo.h> #include <linux/debugfs.h> +#include <linux/list.h> #include <asm/unaligned.h> #include "greybus.h" @@ -26,6 +27,7 @@ #define ES2_GBUF_MSG_SIZE_MAX 2048 /* Memory sizes for the ARPC buffers */ +#define ARPC_OUT_SIZE_MAX U16_MAX #define ARPC_IN_SIZE_MAX 128 static const struct usb_device_id id_table[] = { @@ -102,6 +104,9 @@ struct es2_cport_out { * @arpc_urb: array of urbs for the ARPC in messages * @arpc_buffer: array of buffers for the @arpc_urb urbs * @arpc_endpoint_in: bulk in endpoint for APBridgeA RPC + * @arpc_id_cycle: gives an unique id to ARPC + * @arpc_lock: locks ARPC list + * @arpcs: list of in progress ARPCs */ struct es2_ap_dev { struct usb_device *usb_dev; @@ -127,6 +132,10 @@ struct es2_ap_dev { __u8 arpc_endpoint_in; struct urb *arpc_urb[NUM_ARPC_IN_URB]; u8 *arpc_buffer[NUM_ARPC_IN_URB]; + + int arpc_id_cycle; + spinlock_t arpc_lock; + struct list_head arpcs; }; /** @@ -164,6 +173,14 @@ struct timesync_authoritative_request { __le64 frame_time[GB_TIMESYNC_MAX_STROBES]; } __packed; +struct arpc { + struct list_head list; + struct arpc_request_message *req; + struct arpc_response_message *resp; + struct completion response_received; + bool active; +}; + static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) { return (struct es2_ap_dev *)&hd->hd_priv; @@ -172,6 +189,8 @@ static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) static void cport_out_callback(struct urb *urb); static void usb_log_enable(struct es2_ap_dev *es2); static void usb_log_disable(struct es2_ap_dev *es2); +static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, + size_t size, int *result, unsigned int timeout); /* Get the endpoints pair mapped to the cport */ static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id) @@ -590,7 +609,9 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) { struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; + struct arpc_cport_reset req; int retval; + int result; switch (cport_id) { case GB_SVC_CPORT_ID: @@ -599,18 +620,15 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) return 0; } - retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - GB_APB_REQUEST_RESET_CPORT, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, cport_id, 0, - NULL, 0, ES2_TIMEOUT); - if (retval < 0) { + req.cport_id = cpu_to_le16(cport_id); + retval = arpc_sync(es2, ARPC_CPORT_RESET, &req, sizeof(req), + &result, ES2_TIMEOUT); + if (retval == -EREMOTEIO) { dev_err(&udev->dev, "failed to reset cport %u: %d\n", cport_id, - retval); - return retval; + result); } - return 0; + return retval; } static int es2_cport_allocate(struct gb_host_device *hd, int cport_id, @@ -1061,10 +1079,154 @@ static void cport_out_callback(struct urb *urb) free_urb(es2, urb); } +static struct arpc *arpc_alloc(void *payload, u16 size, u8 type) +{ + struct arpc *rpc; + + if (size + sizeof(*rpc->req) > ARPC_OUT_SIZE_MAX) + return NULL; + + rpc = kzalloc(sizeof(*rpc), GFP_KERNEL); + if (!rpc) + return NULL; + + INIT_LIST_HEAD(&rpc->list); + rpc->req = kzalloc(sizeof(*rpc->req) + size, GFP_KERNEL); + if (!rpc->req) + goto err_free_rpc; + + rpc->resp = kzalloc(sizeof(*rpc->resp), GFP_KERNEL); + if (!rpc->req) + goto err_free_req; + + rpc->req->type = type; + rpc->req->size = cpu_to_le16(sizeof(rpc->req) + size); + memcpy(rpc->req->data, payload, size); + + init_completion(&rpc->response_received); + + return rpc; + +err_free_req: + kfree(rpc->req); +err_free_rpc: + kfree(rpc); + + return NULL; +} + +static void arpc_free(struct arpc *rpc) +{ + kfree(rpc->req); + kfree(rpc->resp); + kfree(rpc); +} + +static struct arpc *arpc_find(struct es2_ap_dev *es2, u8 id) +{ + struct arpc *rpc; + + list_for_each_entry(rpc, &es2->arpcs, list) { + if (rpc->req->id == id) + return rpc; + } + + return NULL; +} + +static void arpc_add(struct es2_ap_dev *es2, struct arpc *rpc) +{ + rpc->active = true; + rpc->req->id = (u16)(es2->arpc_id_cycle++); + list_add_tail(&es2->arpcs, &rpc->list); +} + +static void arpc_del(struct es2_ap_dev *es2, struct arpc *rpc) +{ + if (rpc->active) { + rpc->active = false; + list_del(&rpc->list); + } +} + +static int arpc_send(struct es2_ap_dev *es2, struct arpc *rpc, int timeout) +{ + struct usb_device *udev = es2->usb_dev; + int retval; + + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + APBA_REQUEST_ARPC_RUN, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE, + 0, 0, + rpc->req, rpc->req->size, + ES2_TIMEOUT); + if (retval != rpc->req->size) { + dev_err(&udev->dev, + "failed to send ARPC request %d: %d\n", + rpc->req->type, retval); + if (retval > 0) + retval = -EIO; + return retval; + } + + return 0; +} + +static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, + size_t size, int *result, unsigned int timeout) +{ + struct arpc *rpc; + unsigned long flags; + int retval; + + rpc = arpc_alloc(payload, size, type); + if (!rpc) + return -ENOMEM; + + spin_lock_irqsave(&es2->arpc_lock, flags); + arpc_add(es2, rpc); + spin_unlock_irqrestore(&es2->arpc_lock, flags); + + retval = arpc_send(es2, rpc, timeout); + if (retval) + goto out_arpc_del; + + retval = wait_for_completion_interruptible_timeout( + &rpc->response_received, + msecs_to_jiffies(timeout)); + if (retval <= 0) { + if (!retval) + retval = -ETIMEDOUT; + goto out_arpc_del; + } + + *result = rpc->resp->result; + if (*result) + retval = -EREMOTEIO; + +out_arpc_del: + spin_lock_irqsave(&es2->arpc_lock, flags); + arpc_del(es2, rpc); + spin_unlock_irqrestore(&es2->arpc_lock, flags); + arpc_free(rpc); + + if (retval < 0 && retval != -EREMOTEIO) { + dev_err(&es2->usb_dev->dev, + "failed to execute ARPC: %d\n", retval); + } + + return retval; +} + static void arpc_in_callback(struct urb *urb) { + struct es2_ap_dev *es2 = urb->context; struct device *dev = &urb->dev->dev; int status = check_urb_status(urb); + struct arpc *rpc; + struct arpc_response_message *resp; + unsigned long flags; int retval; if (status) { @@ -1079,6 +1241,26 @@ static void arpc_in_callback(struct urb *urb) return; } + if (urb->actual_length < sizeof(*resp)) { + dev_err(dev, "short aprc response received\n"); + goto exit; + } + + resp = urb->transfer_buffer; + spin_lock_irqsave(&es2->arpc_lock, flags); + rpc = arpc_find(es2, resp->id); + if (!rpc) { + dev_err(dev, "invalid arpc response id received: %d\n", + resp->id); + spin_unlock_irqrestore(&es2->arpc_lock, flags); + goto exit; + } + + arpc_del(es2, rpc); + memcpy(rpc->resp, resp, sizeof(*resp)); + complete(&rpc->response_received); + spin_unlock_irqrestore(&es2->arpc_lock, flags); + exit: /* put our urb back in the request pool */ retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -1417,6 +1599,9 @@ static int ap_probe(struct usb_interface *interface, gb_debugfs_get(), es2, &apb_log_enable_fops); + INIT_LIST_HEAD(&es2->arpcs); + spin_lock_init(&es2->arpc_lock); + if (es2_arpc_in_enable(es2)) goto error; diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 1a12531944e9..e9f3d2cfd973 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -268,12 +268,44 @@ struct gb_control_intf_pm_response { /* requests to set Greybus CPort flags */ #define GB_APB_REQUEST_CPORT_FLAGS 0x11 +/* ARPC command */ +#define APBA_REQUEST_ARPC_RUN 0x12 + struct gb_apb_request_cport_flags { u32 flags; #define GB_APB_CPORT_FLAG_CONTROL 0x01 #define GB_APB_CPORT_FLAG_HIGH_PRIO 0x02 } __packed; +/* APBridgeA RPC (ARPC) */ + +enum arpc_result { + ARPC_SUCCESS = 0x00, + ARPC_NO_MEMORY = 0x01, + ARPC_INVALID = 0x02, + ARPC_TIMEOUT = 0x03, + ARPC_UNKNOWN_ERROR = 0xff, +}; + +/* ARPC request */ +struct arpc_request_message { + __le16 id; /* RPC unique id */ + __le16 size; /* Size in bytes of header + payload */ + __u8 type; /* RPC type */ + __u8 data[0]; /* ARPC data */ +} __packed; + +/* ARPC response */ +struct arpc_response_message { + __le16 id; /* RPC unique id */ + __u8 result; /* Result of RPC */ +} __packed; + +#define ARPC_CPORT_RESET 0x00 + +struct arpc_cport_reset { + __le16 cport_id; +} __packed; /* Firmware Download Protocol */ -- cgit v1.2.3-59-g8ed1b From 34873949885ad66f9bc6ad6baff78aed4846c092 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 13 Jul 2016 09:34:00 -0500 Subject: greybus: es2: fix arpc return value ARPC should return 0 on success, but instead was returning the number of jiffies left until the timeout. This caused cport_reset() to report an error and an incorrect error message to be printed when disabling a connection. Reported-by: Alex Elder <elder@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Tested-by: Alex Elder <elder@linaro.org> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/es2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index b763b27ce9ef..6ada1ee128fc 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1204,6 +1204,8 @@ static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, *result = rpc->resp->result; if (*result) retval = -EREMOTEIO; + else + retval = 0; out_arpc_del: spin_lock_irqsave(&es2->arpc_lock, flags); -- cgit v1.2.3-59-g8ed1b From cf1caac6cd7c1fa25de304925f6b239380797a46 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Tue, 12 Jul 2016 04:56:00 -0500 Subject: greybus: audio: Fix incorrect codec state modification In case module is removed dynamically with ongoing playback, during module cleanup codec state is mistakenly modified. State should be modified for module only. Fix this. Fixes: 76414cb499b7 ("audio: Use single codec driver registration") Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/audio_codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 3c5a5aeeef8a..5e05375bb71e 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -971,7 +971,7 @@ static void gbaudio_codec_cleanup(struct gbaudio_module_info *module) ret = gb_audio_apbridgea_unregister_cport(data->connection, i2s_port, cportid, AUDIO_APBRIDGEA_DIRECTION_TX); - gbcodec->stream[0].state = GBAUDIO_CODEC_SHUTDOWN; + module->ctrlstate[0] = GBAUDIO_CODEC_SHUTDOWN; } if (cap_state == GBAUDIO_CODEC_START) { @@ -996,7 +996,7 @@ static void gbaudio_codec_cleanup(struct gbaudio_module_info *module) ret = gb_audio_apbridgea_unregister_cport(data->connection, i2s_port, cportid, AUDIO_APBRIDGEA_DIRECTION_RX); - gbcodec->stream[1].state = GBAUDIO_CODEC_SHUTDOWN; + module->ctrlstate[1] = GBAUDIO_CODEC_SHUTDOWN; } } -- cgit v1.2.3-59-g8ed1b From 12ce523147e485fc605599f7ec94fbbc6be0fc01 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Tue, 12 Jul 2016 04:56:00 -0500 Subject: greybus: audio: Maintain proper codec state during shutdown sequence. During shutdown sequence, in case all modules are already removed, codec state is not updated. Though it's not causing any harm for now, but it's good to maintain proper codec state. Fix this. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/audio_codec.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 5e05375bb71e..9c7bec737875 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -460,6 +460,8 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, if (list_empty(&codec->module_list)) { dev_err(codec->dev, "No codec module available\n"); + codec->stream[substream->stream].state = GBAUDIO_CODEC_SHUTDOWN; + codec->stream[substream->stream].dai_name = NULL; mutex_unlock(&codec->lock); pm_relax(dai->dev); return; @@ -733,8 +735,14 @@ static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) mutex_lock(&codec->lock); if (list_empty(&codec->module_list)) { dev_err(codec->dev, "No codec module available\n"); + if (mute) { + codec->stream[stream].state = GBAUDIO_CODEC_STOP; + ret = 0; + } else { + ret = -ENODEV; + } mutex_unlock(&codec->lock); - return -ENODEV; + return ret; } list_for_each_entry(module, &codec->module_list, list) { -- cgit v1.2.3-59-g8ed1b From e818027c0b4e2573e2e63ca04480ce7972596eb2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 14 Jul 2016 12:18:00 -0500 Subject: greybus: es2: Fix 'make check' warnings with arpc This is what I got: greybus/es2.c:1130:29: warning: restricted __le16 degrades to integer greybus/es2.c:1140:22: warning: incorrect type in assignment (different base types) greybus/es2.c:1140:22: expected restricted __le16 [usertype] id greybus/es2.c:1140:22: got unsigned short [unsigned] [usertype] <noident> greybus/es2.c:1162:52: warning: incorrect type in argument 8 (different base types) greybus/es2.c:1162:52: expected unsigned short [unsigned] [usertype] size greybus/es2.c:1162:52: got restricted __le16 [usertype] size greybus/es2.c:1164:31: warning: restricted __le16 degrades to integer greybus/es2.c:1253:34: warning: incorrect type in argument 2 (different base types) greybus/es2.c:1253:34: expected unsigned char [unsigned] [usertype] id greybus/es2.c:1253:34: got restricted __le16 [usertype] id Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/es2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 6ada1ee128fc..7c294c413365 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1127,7 +1127,7 @@ static struct arpc *arpc_find(struct es2_ap_dev *es2, u8 id) struct arpc *rpc; list_for_each_entry(rpc, &es2->arpcs, list) { - if (rpc->req->id == id) + if (le16_to_cpu(rpc->req->id) == id) return rpc; } @@ -1137,7 +1137,7 @@ static struct arpc *arpc_find(struct es2_ap_dev *es2, u8 id) static void arpc_add(struct es2_ap_dev *es2, struct arpc *rpc) { rpc->active = true; - rpc->req->id = (u16)(es2->arpc_id_cycle++); + rpc->req->id = cpu_to_le16(es2->arpc_id_cycle++); list_add_tail(&es2->arpcs, &rpc->list); } @@ -1159,9 +1159,9 @@ static int arpc_send(struct es2_ap_dev *es2, struct arpc *rpc, int timeout) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, - rpc->req, rpc->req->size, + rpc->req, le16_to_cpu(rpc->req->size), ES2_TIMEOUT); - if (retval != rpc->req->size) { + if (retval != le16_to_cpu(rpc->req->size)) { dev_err(&udev->dev, "failed to send ARPC request %d: %d\n", rpc->req->type, retval); @@ -1250,7 +1250,7 @@ static void arpc_in_callback(struct urb *urb) resp = urb->transfer_buffer; spin_lock_irqsave(&es2->arpc_lock, flags); - rpc = arpc_find(es2, resp->id); + rpc = arpc_find(es2, le16_to_cpu(resp->id)); if (!rpc) { dev_err(dev, "invalid arpc response id received: %d\n", resp->id); -- cgit v1.2.3-59-g8ed1b From 165a74ab1401ac3e2b6eb5b7084607b588734e09 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 14 Jul 2016 12:55:00 -0500 Subject: greybus: es2: fix arpc response lookups Fix arpc response lookups that were truncating the 16-bit response id to 8-bit, something which would have lead to all arpc calls timing out after the 256th request. Testing done: Enumerated and ejected a module on EVT2. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/es2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 7c294c413365..7961622103be 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1122,12 +1122,12 @@ static void arpc_free(struct arpc *rpc) kfree(rpc); } -static struct arpc *arpc_find(struct es2_ap_dev *es2, u8 id) +static struct arpc *arpc_find(struct es2_ap_dev *es2, __le16 id) { struct arpc *rpc; list_for_each_entry(rpc, &es2->arpcs, list) { - if (le16_to_cpu(rpc->req->id) == id) + if (rpc->req->id == id) return rpc; } @@ -1250,7 +1250,7 @@ static void arpc_in_callback(struct urb *urb) resp = urb->transfer_buffer; spin_lock_irqsave(&es2->arpc_lock, flags); - rpc = arpc_find(es2, le16_to_cpu(resp->id)); + rpc = arpc_find(es2, resp->id); if (!rpc) { dev_err(dev, "invalid arpc response id received: %d\n", resp->id); -- cgit v1.2.3-59-g8ed1b From 2d48b5b4a8ca12aeff6551796357add7c05ce8cf Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: bundle: add activate and deactivate AP shall send the Bundle Activate Operation to power on a bundle, and send the Bundle Deactivate Request after closing all the associated connections for power down. Testing Done: - Check for the return code of the bundle activate and deactivate operation sent Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index dacc49c55253..ca3bad910aae 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -162,6 +162,13 @@ static int greybus_probe(struct device *dev) if (!id) return -ENODEV; + /* + * FIXME: We need to perform error handling on bundle activate call + * below when firmware is ready. We just allow the activate operation to + * fail for now since bundle may be in active already. + */ + gb_control_bundle_activate(bundle->intf->control, bundle->id); + retval = driver->probe(bundle, id); if (retval) { /* @@ -169,6 +176,8 @@ static int greybus_probe(struct device *dev) */ WARN_ON(!list_empty(&bundle->connections)); + gb_control_bundle_deactivate(bundle->intf->control, bundle->id); + return retval; } @@ -203,6 +212,9 @@ static int greybus_remove(struct device *dev) /* Catch buggy drivers that fail to destroy their connections. */ WARN_ON(!list_empty(&bundle->connections)); + if (!bundle->intf->disconnected) + gb_control_bundle_deactivate(bundle->intf->control, bundle->id); + return 0; } -- cgit v1.2.3-59-g8ed1b From 30a3bf7b30d86b94ad4fbdcf9cdce1dcf5037c58 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: interface: add runtime pm support Configure and enable runtime pm support for the Interface. Refer to the 12.2. The Interface Lifecycle of the Greybus specification for details on the requirements for transitioning from ENUMERATED to SUSPEND and vice versa. All the Bundles for the Interface have to be either OFF or SUSPENDED before the Interface can be autosuspended. Testing Done: - Check the runtime_status of an interface driver and validate the suspend current of a module. Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/interface.c | 109 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/kernel_ver.h | 13 +++++ 3 files changed, 123 insertions(+) diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index dbc9be05afc6..3e32028832c1 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/device.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #include <linux/idr.h> #include "kernel_ver.h" diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index e7efc541895a..16e268f1b109 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -7,6 +7,8 @@ * Released under the GPLv2 only. */ +#include <linux/delay.h> + #include "greybus.h" #include "greybus_trace.h" @@ -14,6 +16,11 @@ #define GB_INTERFACE_DEVICE_ID_BAD 0xff +#define GB_INTERFACE_AUTOSUSPEND_MS 3000 + +/* Time required for interface to enter standby before disabling REFCLK */ +#define GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS 20 + /* Don't-care selector index */ #define DME_SELECTOR_INDEX_NULL 0 @@ -36,6 +43,8 @@ #define TOSHIBA_ES3_APBRIDGE_DPID 0x1001 #define TOSHIBA_ES3_GBPHY_DPID 0x1002 +static int gb_interface_hibernate_link(struct gb_interface *intf); +static int gb_interface_refclk_set(struct gb_interface *intf, bool enable); static int gb_interface_dme_attr_get(struct gb_interface *intf, u16 attr, u32 *val) @@ -505,9 +514,92 @@ static void gb_interface_release(struct device *dev) kfree(intf); } +#ifdef CONFIG_PM_RUNTIME +static int gb_interface_suspend(struct device *dev) +{ + struct gb_interface *intf = to_gb_interface(dev); + int ret, timesync_ret; + + ret = gb_control_interface_suspend_prepare(intf->control); + if (ret) + return ret; + + gb_timesync_interface_remove(intf); + + ret = gb_control_suspend(intf->control); + if (ret) + goto err_hibernate_abort; + + ret = gb_interface_hibernate_link(intf); + if (ret) + return ret; + + /* Delay to allow interface to enter standby before disabling refclk */ + msleep(GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS); + + ret = gb_interface_refclk_set(intf, false); + if (ret) + return ret; + + return 0; + +err_hibernate_abort: + gb_control_interface_hibernate_abort(intf->control); + + timesync_ret = gb_timesync_interface_add(intf); + if (timesync_ret) { + dev_err(dev, "failed to add to timesync: %d\n", timesync_ret); + return timesync_ret; + } + + return ret; +} + +static int gb_interface_resume(struct device *dev) +{ + struct gb_interface *intf = to_gb_interface(dev); + struct gb_svc *svc = intf->hd->svc; + int ret; + + ret = gb_interface_refclk_set(intf, true); + if (ret) + return ret; + + ret = gb_svc_intf_resume(svc, intf->interface_id); + if (ret) + return ret; + + ret = gb_control_resume(intf->control); + if (ret) + return ret; + + ret = gb_timesync_interface_add(intf); + if (ret) { + dev_err(dev, "failed to add to timesync: %d\n", ret); + return ret; + } + + return 0; +} + +static int gb_interface_runtime_idle(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + pm_request_autosuspend(dev); + + return 0; +} +#endif + +static const struct dev_pm_ops gb_interface_pm_ops = { + SET_RUNTIME_PM_OPS(gb_interface_suspend, gb_interface_resume, + gb_interface_runtime_idle) +}; + struct device_type greybus_interface_type = { .name = "greybus_interface", .release = gb_interface_release, + .pm = &gb_interface_pm_ops, }; /* @@ -553,6 +645,9 @@ struct gb_interface *gb_interface_create(struct gb_module *module, dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev), interface_id); + pm_runtime_set_autosuspend_delay(&intf->dev, + GB_INTERFACE_AUTOSUSPEND_MS); + trace_gb_interface_create(intf); return intf; @@ -809,6 +904,11 @@ int gb_interface_enable(struct gb_interface *intf) goto err_destroy_bundles; } + pm_runtime_use_autosuspend(&intf->dev); + pm_runtime_get_noresume(&intf->dev); + pm_runtime_set_active(&intf->dev); + pm_runtime_enable(&intf->dev); + list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) { ret = gb_bundle_add(bundle); if (ret) { @@ -821,6 +921,8 @@ int gb_interface_enable(struct gb_interface *intf) intf->enabled = true; + pm_runtime_put(&intf->dev); + trace_gb_interface_enable(intf); return 0; @@ -854,6 +956,8 @@ void gb_interface_disable(struct gb_interface *intf) trace_gb_interface_disable(intf); + pm_runtime_get_sync(&intf->dev); + /* Set disconnected flag to avoid I/O during connection tear down. */ if (intf->quirks & GB_INTERFACE_QUIRK_FORCED_DISABLE) intf->disconnected = true; @@ -871,6 +975,11 @@ void gb_interface_disable(struct gb_interface *intf) intf->control = NULL; intf->enabled = false; + + pm_runtime_disable(&intf->dev); + pm_runtime_set_suspended(&intf->dev); + pm_runtime_dont_use_autosuspend(&intf->dev); + pm_runtime_put_noidle(&intf->dev); } /* Enable TimeSync on an Interface control connection. */ diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 80ed27c9a650..84beb2f6334c 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -389,4 +389,17 @@ static inline int kstrtobool(const char *s, bool *res) } #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +/* + * After commit b2b49ccbdd54 (PM: Kconfig: Set PM_RUNTIME if PM_SLEEP is + * selected) PM_RUNTIME is always set if PM is set, so files that are build + * conditionally if CONFIG_PM_RUNTIME is set may now be build if CONFIG_PM is + * set. + */ + +#ifdef CONFIG_PM +#define CONFIG_PM_RUNTIME +#endif /* CONFIG_PM */ +#endif + #endif /* __GREYBUS_KERNEL_VER_H */ -- cgit v1.2.3-59-g8ed1b From 61e13db9cc8945d53f72d4021594ee3be214e667 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: bundle: add runtime pm support This patch adds runtime pm support for the bundle core. Unbound bundle devices are always deactivated. During probe, Runtime PM status is set to enabled and active and the usage count is incremented. If the driver supports runtime PM, it should call pm_runtime_put() in its probe routine and pm_runtime_get_sync() in remove routine as bundle needs to be resume before it can be deactivated. Testing Done: - Check runtime_status of the bundle driver when bundle goes to suspend Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/bundle.c | 80 ++++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/bundle.h | 47 +++++++++++++++++++++++ drivers/staging/greybus/core.c | 40 ++++++++++++++++++++ 3 files changed, 167 insertions(+) diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 80f54c977509..5bd7731237f5 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -89,9 +89,89 @@ static void gb_bundle_release(struct device *dev) kfree(bundle); } +#ifdef CONFIG_PM_RUNTIME + +static void gb_bundle_disable_all_connections(struct gb_bundle *bundle) +{ + struct gb_connection *connection; + + list_for_each_entry(connection, &bundle->connections, bundle_links) + gb_connection_disable(connection); +} + +static void gb_bundle_enable_all_connections(struct gb_bundle *bundle) +{ + struct gb_connection *connection; + + list_for_each_entry(connection, &bundle->connections, bundle_links) + gb_connection_enable(connection); +} + +static int gb_bundle_suspend(struct device *dev) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + const struct dev_pm_ops *pm = dev->driver->pm; + int ret; + + if (pm && pm->runtime_suspend) { + ret = pm->runtime_suspend(&bundle->dev); + if (ret) + return ret; + } else { + gb_bundle_disable_all_connections(bundle); + } + + ret = gb_control_bundle_suspend(bundle->intf->control, bundle->id); + if (ret) { + if (pm && pm->runtime_resume) + ret = pm->runtime_resume(dev); + else + gb_bundle_enable_all_connections(bundle); + + return ret; + } + + return 0; +} + +static int gb_bundle_resume(struct device *dev) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + const struct dev_pm_ops *pm = dev->driver->pm; + int ret; + + ret = gb_control_bundle_resume(bundle->intf->control, bundle->id); + if (ret) + return ret; + + if (pm && pm->runtime_resume) { + ret = pm->runtime_resume(dev); + if (ret) + return ret; + } else { + gb_bundle_enable_all_connections(bundle); + } + + return 0; +} + +static int gb_bundle_idle(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + pm_request_autosuspend(dev); + + return 0; +} +#endif + +static const struct dev_pm_ops gb_bundle_pm_ops = { + SET_RUNTIME_PM_OPS(gb_bundle_suspend, gb_bundle_resume, gb_bundle_idle) +}; + struct device_type greybus_bundle_type = { .name = "greybus_bundle", .release = gb_bundle_release, + .pm = &gb_bundle_pm_ops, }; /* diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 3895f94f43c4..349845ee893c 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -40,4 +40,51 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, int gb_bundle_add(struct gb_bundle *bundle); void gb_bundle_destroy(struct gb_bundle *bundle); +/* Bundle Runtime PM wrappers */ +#ifdef CONFIG_PM_RUNTIME +static inline int gb_pm_runtime_get_sync(struct gb_bundle *bundle) +{ + int retval; + + retval = pm_runtime_get_sync(&bundle->dev); + if (retval < 0) { + dev_err(&bundle->dev, + "pm_runtime_get_sync failed: %d\n", retval); + pm_runtime_put_noidle(&bundle->dev); + return retval; + } + + return 0; +} + +static inline int gb_pm_runtime_put_autosuspend(struct gb_bundle *bundle) +{ + int retval; + + pm_runtime_mark_last_busy(&bundle->dev); + retval = pm_runtime_put_autosuspend(&bundle->dev); + + return retval; +} + +static inline void gb_pm_runtime_get_noresume(struct gb_bundle *bundle) +{ + pm_runtime_get_noresume(&bundle->dev); +} + +static inline void gb_pm_runtime_put_noidle(struct gb_bundle *bundle) +{ + pm_runtime_put_noidle(&bundle->dev); +} + +#else +static inline int gb_pm_runtime_get_sync(struct gb_bundle *bundle) +{ return 0; } +static inline int gb_pm_runtime_put_autosuspend(struct gb_bundle *bundle) +{ return 0; } + +static inline void gb_pm_runtime_get_noresume(struct gb_bundle *bundle) {} +static inline void gb_pm_runtime_put_noidle(struct gb_bundle *bundle) {} +#endif + #endif /* __BUNDLE_H */ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index ca3bad910aae..53d9ba151aeb 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -13,6 +13,8 @@ #include "greybus.h" #include "greybus_trace.h" +#define GB_BUNDLE_AUTOSUSPEND_MS 3000 + /* Allow greybus to be disabled at boot if needed */ static bool nogreybus; #ifdef MODULE @@ -162,6 +164,12 @@ static int greybus_probe(struct device *dev) if (!id) return -ENODEV; + retval = pm_runtime_get_sync(&bundle->intf->dev); + if (retval < 0) { + pm_runtime_put_noidle(&bundle->intf->dev); + return retval; + } + /* * FIXME: We need to perform error handling on bundle activate call * below when firmware is ready. We just allow the activate operation to @@ -169,6 +177,19 @@ static int greybus_probe(struct device *dev) */ gb_control_bundle_activate(bundle->intf->control, bundle->id); + /* + * Unbound bundle devices are always deactivated. During probe, the + * Runtime PM is set to enabled and active and the usage count is + * incremented. If the driver supports runtime PM, it should call + * pm_runtime_put() in its probe routine and pm_runtime_get_sync() + * in remove routine. + */ + pm_runtime_set_autosuspend_delay(dev, GB_BUNDLE_AUTOSUSPEND_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + retval = driver->probe(bundle, id); if (retval) { /* @@ -178,11 +199,19 @@ static int greybus_probe(struct device *dev) gb_control_bundle_deactivate(bundle->intf->control, bundle->id); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_put(&bundle->intf->dev); + return retval; } gb_timesync_schedule_asynchronous(bundle->intf); + pm_runtime_put(&bundle->intf->dev); + return 0; } @@ -191,6 +220,11 @@ static int greybus_remove(struct device *dev) struct greybus_driver *driver = to_greybus_driver(dev->driver); struct gb_bundle *bundle = to_gb_bundle(dev); struct gb_connection *connection; + int retval; + + retval = pm_runtime_get_sync(dev); + if (retval < 0) + dev_err(dev, "failed to resume bundle: %d\n", retval); /* * Disable (non-offloaded) connections early in case the interface is @@ -215,6 +249,12 @@ static int greybus_remove(struct device *dev) if (!bundle->intf->disconnected) gb_control_bundle_deactivate(bundle->intf->control, bundle->id); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_put_noidle(dev); + return 0; } -- cgit v1.2.3-59-g8ed1b From 6ba7fad430d6300b966800bc5d2c782e2baf6f1d Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: audio: add runtime pm support Add runtime pm support to audio protocol device class driver. Testing Done: - Use white speaker module and check the interface is autosuspended when it's idle and resumed when playback audio Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/audio_apbridgea.c | 14 +++++++-- drivers/staging/greybus/audio_module.c | 51 +++++++++++++++++++++++++++++++ drivers/staging/greybus/audio_topology.c | 47 ++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio_apbridgea.c b/drivers/staging/greybus/audio_apbridgea.c index 361470788a76..45d3522789a8 100644 --- a/drivers/staging/greybus/audio_apbridgea.c +++ b/drivers/staging/greybus/audio_apbridgea.c @@ -33,12 +33,17 @@ int gb_audio_apbridgea_register_cport(struct gb_connection *connection, __u8 direction) { struct audio_apbridgea_register_cport_request req; + int ret; req.hdr.type = AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT; req.hdr.i2s_port = cpu_to_le16(i2s_port); req.cport = cpu_to_le16(cportid); req.direction = direction; + ret = gb_pm_runtime_get_sync(connection->bundle); + if (ret) + return ret; + return gb_hd_output(connection->hd, &req, sizeof(req), GB_APB_REQUEST_AUDIO_CONTROL, true); } @@ -49,14 +54,19 @@ int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, __u8 direction) { struct audio_apbridgea_unregister_cport_request req; + int ret; req.hdr.type = AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT; req.hdr.i2s_port = cpu_to_le16(i2s_port); req.cport = cpu_to_le16(cportid); req.direction = direction; - return gb_hd_output(connection->hd, &req, sizeof(req), - GB_APB_REQUEST_AUDIO_CONTROL, true); + ret = gb_hd_output(connection->hd, &req, sizeof(req), + GB_APB_REQUEST_AUDIO_CONTROL, true); + + gb_pm_runtime_put_autosuspend(connection->bundle); + + return ret; } EXPORT_SYMBOL_GPL(gb_audio_apbridgea_unregister_cport); diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index d87b9985a0e2..7cf523e7f995 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -332,6 +332,8 @@ static int gb_audio_probe(struct gb_bundle *bundle, dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name); + gb_pm_runtime_put_autosuspend(bundle); + return 0; disable_data_connection: @@ -366,6 +368,8 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); struct gbaudio_data_connection *dai, *_dai; + gb_pm_runtime_get_sync(bundle); + /* cleanup module related resources first */ gbaudio_unregister_module(gbmodule); @@ -394,11 +398,58 @@ static const struct greybus_bundle_id gb_audio_id_table[] = { }; MODULE_DEVICE_TABLE(greybus, gb_audio_id_table); +#ifdef CONFIG_PM_RUNTIME +static int gb_audio_suspend(struct device *dev) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); + struct gbaudio_data_connection *dai; + + list_for_each_entry(dai, &gbmodule->data_list, list) + gb_connection_disable(dai->connection); + + gb_connection_disable(gbmodule->mgmt_connection); + + return 0; +} + +static int gb_audio_resume(struct device *dev) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); + struct gbaudio_data_connection *dai; + int ret; + + ret = gb_connection_enable(gbmodule->mgmt_connection); + if (ret) { + dev_err(dev, "%d:Error while enabling mgmt connection\n", ret); + return ret; + } + + list_for_each_entry(dai, &gbmodule->data_list, list) { + ret = gb_connection_enable(dai->connection); + if (ret) { + dev_err(dev, + "%d:Error while enabling %d:data connection\n", + ret, dai->data_cport); + return ret; + } + } + + return 0; +} +#endif + +static const struct dev_pm_ops gb_audio_pm_ops = { + SET_RUNTIME_PM_OPS(gb_audio_suspend, gb_audio_resume, NULL) +}; + static struct greybus_driver gb_audio_driver = { .name = "gb-audio", .probe = gb_audio_probe, .disconnect = gb_audio_disconnect, .id_table = gb_audio_id_table, + .driver.pm = &gb_audio_pm_ops, }; module_greybus_driver(gb_audio_driver); diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index e0779ca64388..487f74455a1c 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -213,6 +213,7 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, struct gbaudio_module_info *module; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_bundle *bundle; dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); module = find_gb_module(gb, kcontrol->id.name); @@ -221,9 +222,17 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; + bundle = to_gb_bundle(module->dev); + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); + + gb_pm_runtime_put_autosuspend(bundle); + if (ret) { dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, __func__, kcontrol->id.name); @@ -266,6 +275,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, struct gbaudio_module_info *module; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_bundle *bundle; dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); module = find_gb_module(gb, kcontrol->id.name); @@ -274,6 +284,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; + bundle = to_gb_bundle(module->dev); /* update ucontrol */ switch (info->type) { @@ -299,11 +310,18 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, break; } + if (ret) + return ret; + + ret = gb_pm_runtime_get_sync(bundle); if (ret) return ret; ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); + + gb_pm_runtime_put_autosuspend(bundle); + if (ret) { dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, __func__, kcontrol->id.name); @@ -370,6 +388,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_bundle *bundle; dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); module = find_gb_module(gb, kcontrol->id.name); @@ -378,14 +397,22 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; + bundle = to_gb_bundle(module->dev); if (data->vcount == 2) dev_warn(widget->dapm->dev, "GB: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); + + gb_pm_runtime_put_autosuspend(bundle); + if (ret) { dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, __func__, kcontrol->id.name); @@ -410,6 +437,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_bundle *bundle; dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); module = find_gb_module(gb, kcontrol->id.name); @@ -418,6 +446,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; info = (struct gb_audio_ctl_elem_info *)data->info; + bundle = to_gb_bundle(module->dev); if (data->vcount == 2) dev_warn(widget->dapm->dev, @@ -441,9 +470,17 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, } gbvalue.value.integer_value[0] = ucontrol->value.integer.value[0]; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); + + gb_pm_runtime_put_autosuspend(bundle); + if (ret) { dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, @@ -850,6 +887,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); struct gbaudio_module_info *module; + struct gb_bundle *bundle; dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); @@ -865,6 +903,12 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, return -EINVAL; } + bundle = to_gb_bundle(module->dev); + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + switch (event) { case SND_SOC_DAPM_PRE_PMU: ret = gb_audio_gb_enable_widget(module->mgmt_connection, wid); @@ -883,6 +927,9 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, dev_err_ratelimited(codec->dev, "%d: widget, event:%d failed:%d\n", wid, event, ret); + + gb_pm_runtime_put_autosuspend(bundle); + return ret; } -- cgit v1.2.3-59-g8ed1b From 211634f2ca165947e37ba8b259c046a2e07b5f3c Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: camera: add runtime pm support Add runtime pm support to camera protocol device class driver. Testing Done: - Make sure white camera module is able to runtime suspend and resume when the camera is being used Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/camera.c | 81 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index ce0ff759f3ce..bdceb77200c6 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -333,6 +333,10 @@ static int gb_camera_capabilities(struct gb_camera *gcam, struct gb_operation *op = NULL; int ret; + ret = gb_pm_runtime_get_sync(gcam->bundle); + if (ret) + return ret; + mutex_lock(&gcam->mutex); if (!gcam->connection) { @@ -362,6 +366,9 @@ done: mutex_unlock(&gcam->mutex); if (op) gb_operation_put(op); + + gb_pm_runtime_put_autosuspend(gcam->bundle); + return ret; } @@ -408,6 +415,10 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, mutex_lock(&gcam->mutex); + ret = gb_pm_runtime_get_sync(gcam->bundle); + if (ret) + goto done_skip_pm_put; + if (!gcam->connection) { ret = -EINVAL; goto done; @@ -460,9 +471,24 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, if (gcam->state == GB_CAMERA_STATE_CONFIGURED) { gb_camera_teardown_data_connection(gcam); gcam->state = GB_CAMERA_STATE_UNCONFIGURED; + + /* + * When unconfiguring streams release the PM runtime reference + * that was acquired when streams were configured. The bundle + * won't be suspended until the PM runtime reference acquired at + * the beginning of this function gets released right before + * returning. + */ + gb_pm_runtime_put_noidle(gcam->bundle); } if (resp->num_streams) { + /* + * Make sure the bundle won't be suspended until streams get + * unconfigured after the stream is configured successfully + */ + gb_pm_runtime_get_noresume(gcam->bundle); + ret = gb_camera_setup_data_connection(gcam, resp, csi_params); if (ret < 0) { memset(req, 0, sizeof(*req)); @@ -472,6 +498,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, resp, sizeof(*resp)); *flags = 0; *num_streams = 0; + gb_pm_runtime_put_noidle(gcam->bundle); goto done; } @@ -479,6 +506,9 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, } done: + gb_pm_runtime_put_autosuspend(gcam->bundle); + +done_skip_pm_put: mutex_unlock(&gcam->mutex); kfree(req); kfree(resp); @@ -1150,6 +1180,8 @@ static int gb_camera_probe(struct gb_bundle *bundle, greybus_set_drvdata(bundle, gcam); + gb_pm_runtime_put_autosuspend(gcam->bundle); + return 0; error: @@ -1161,6 +1193,11 @@ error: static void gb_camera_disconnect(struct gb_bundle *bundle) { struct gb_camera *gcam = greybus_get_drvdata(bundle); + int ret; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + gb_pm_runtime_get_noresume(bundle); gb_camera_cleanup(gcam); gb_camera_unregister(&gcam->module); @@ -1171,11 +1208,55 @@ static const struct greybus_bundle_id gb_camera_id_table[] = { { }, }; +#ifdef CONFIG_PM_RUNTIME +static int gb_camera_suspend(struct device *dev) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + struct gb_camera *gcam = greybus_get_drvdata(bundle); + + if (gcam->data_connection) + gb_connection_disable(gcam->data_connection); + + gb_connection_disable(gcam->connection); + + return 0; +} + +static int gb_camera_resume(struct device *dev) +{ + struct gb_bundle *bundle = to_gb_bundle(dev); + struct gb_camera *gcam = greybus_get_drvdata(bundle); + int ret; + + ret = gb_connection_enable(gcam->connection); + if (ret) { + gcam_err(gcam, "failed to enable connection: %d\n", ret); + return ret; + } + + if (gcam->data_connection) { + ret = gb_connection_enable(gcam->data_connection); + if (ret) { + gcam_err(gcam, + "failed to enable data connection: %d\n", ret); + return ret; + } + } + + return 0; +} +#endif + +static const struct dev_pm_ops gb_camera_pm_ops = { + SET_RUNTIME_PM_OPS(gb_camera_suspend, gb_camera_resume, NULL) +}; + static struct greybus_driver gb_camera_driver = { .name = "camera", .probe = gb_camera_probe, .disconnect = gb_camera_disconnect, .id_table = gb_camera_id_table, + .driver.pm = &gb_camera_pm_ops, }; module_greybus_driver(gb_camera_driver); -- cgit v1.2.3-59-g8ed1b From af5dc7f8c0f4b71fa53db2e8d6f18c26048a182f Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: gbphy: add gbphy runtime pm support Since GBphy is a child of the Bundle device driver, for those runtime pm settings that are common to all the protocol drivers need to go in to the GBphy bus driver. Testing Done: - Check gbphy driver can be autosuspended Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/gbphy.c | 59 ++++++++++++++++++++++++++++++++++++++++- drivers/staging/greybus/gbphy.h | 40 ++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/gbphy.c b/drivers/staging/greybus/gbphy.c index 2727e4afcf4c..478c162d8ff0 100644 --- a/drivers/staging/greybus/gbphy.c +++ b/drivers/staging/greybus/gbphy.c @@ -19,6 +19,8 @@ #include "greybus.h" #include "gbphy.h" +#define GB_GBPHY_AUTOSUSPEND_MS 3000 + struct gbphy_host { struct gb_bundle *bundle; struct list_head devices; @@ -50,9 +52,25 @@ static void gbphy_dev_release(struct device *dev) kfree(gbphy_dev); } +#ifdef CONFIG_PM_RUNTIME +static int gb_gbphy_idle(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + pm_request_autosuspend(dev); + return 0; +} +#endif + +static const struct dev_pm_ops gb_gbphy_pm_ops = { + SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, + pm_generic_runtime_resume, + gb_gbphy_idle) +}; + static struct device_type greybus_gbphy_dev_type = { .name = "gbphy_device", .release = gbphy_dev_release, + .pm = &gb_gbphy_pm_ops, }; static int gbphy_dev_uevent(struct device *dev, struct kobj_uevent_env *env) @@ -118,12 +136,38 @@ static int gbphy_dev_probe(struct device *dev) struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); const struct gbphy_device_id *id; + int ret; id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); if (!id) return -ENODEV; - return gbphy_drv->probe(gbphy_dev, id); + /* for old kernels we need get_sync to resume parent devices */ + ret = gb_pm_runtime_get_sync(gbphy_dev->bundle); + if (ret < 0) + return ret; + + pm_runtime_set_autosuspend_delay(dev, GB_GBPHY_AUTOSUSPEND_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* + * Drivers should call put on the gbphy dev before returning + * from probe if they support runtime pm. + */ + ret = gbphy_drv->probe(gbphy_dev, id); + if (ret) { + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + } + + gb_pm_runtime_put_autosuspend(gbphy_dev->bundle); + + return ret; } static int gbphy_dev_remove(struct device *dev) @@ -132,6 +176,12 @@ static int gbphy_dev_remove(struct device *dev) struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); gbphy_drv->remove(gbphy_dev); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + return 0; } @@ -211,6 +261,11 @@ static void gb_gbphy_disconnect(struct gb_bundle *bundle) { struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle); struct gbphy_device *gbphy_dev, *temp; + int ret; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret < 0) + gb_pm_runtime_get_noresume(bundle); list_for_each_entry_safe(gbphy_dev, temp, &gbphy_host->devices, list) { list_del(&gbphy_dev->list); @@ -251,6 +306,8 @@ static int gb_gbphy_probe(struct gb_bundle *bundle, list_add(&gbphy_dev->list, &gbphy_host->devices); } + gb_pm_runtime_put_autosuspend(bundle); + return 0; } diff --git a/drivers/staging/greybus/gbphy.h b/drivers/staging/greybus/gbphy.h index 68ad51821a60..57c6f65aeb7f 100644 --- a/drivers/staging/greybus/gbphy.h +++ b/drivers/staging/greybus/gbphy.h @@ -66,5 +66,45 @@ void gb_gbphy_deregister_driver(struct gbphy_driver *driver); #define module_gbphy_driver(__gbphy_driver) \ module_driver(__gbphy_driver, gb_gbphy_register, gb_gbphy_deregister) +#ifdef CONFIG_PM_RUNTIME +static inline int gbphy_runtime_get_sync(struct gbphy_device *gbphy_dev) +{ + struct device *dev = &gbphy_dev->dev; + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync failed: %d\n", ret); + pm_runtime_put_noidle(dev); + return ret; + } + + return 0; +} + +static inline void gbphy_runtime_put_autosuspend(struct gbphy_device *gbphy_dev) +{ + struct device *dev = &gbphy_dev->dev; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +static inline void gbphy_runtime_get_noresume(struct gbphy_device *gbphy_dev) +{ + pm_runtime_get_noresume(&gbphy_dev->dev); +} + +static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev) +{ + pm_runtime_put_noidle(&gbphy_dev->dev); +} +#else +static inline int gbphy_runtime_get_sync(struct device *dev) { return 0; } +static inline void gbphy_runtime_put_autosuspend(struct device *dev) {} +static inline void gbphy_runtime_get_noresume(struct gbphy_device *gbphy_dev) {} +static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev) {} +#endif + #endif /* __GBPHY_H */ -- cgit v1.2.3-59-g8ed1b From c0e72f6af7363de8868e2847450a828f27e96c81 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: i2c: add runtime pm support Add runtime pm support to camera i2c bridged phy device class driver Testing Done: - Passed #gb_test.sh -v -t i2c Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Axel Haslam <haslam_axel@projectara.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/i2c.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index 2541bdbb9dd9..c2a50087000c 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -176,6 +176,10 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, if (!operation) return -ENOMEM; + ret = gbphy_runtime_get_sync(gb_i2c_dev->gbphy_dev); + if (ret) + goto exit_operation_put; + ret = gb_operation_request_send_sync(operation); if (!ret) { struct gb_i2c_transfer_response *response; @@ -187,6 +191,9 @@ static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, dev_err(dev, "transfer operation failed (%d)\n", ret); } + gbphy_runtime_put_autosuspend(gb_i2c_dev->gbphy_dev); + +exit_operation_put: gb_operation_put(operation); return ret; @@ -290,6 +297,7 @@ static int gb_i2c_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_connection_disable; + gbphy_runtime_put_autosuspend(gbphy_dev); return 0; exit_connection_disable: @@ -306,6 +314,11 @@ static void gb_i2c_remove(struct gbphy_device *gbphy_dev) { struct gb_i2c_device *gb_i2c_dev = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = gb_i2c_dev->connection; + int ret; + + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + gbphy_runtime_get_noresume(gbphy_dev); i2c_del_adapter(&gb_i2c_dev->adapter); gb_connection_disable(connection); -- cgit v1.2.3-59-g8ed1b From 22b8708725c20ce57d79e51a7e98628b382420a4 Mon Sep 17 00:00:00 2001 From: Axel Haslam <haslam_axel@projectara.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: uart: Add runtime pm support This adds runtime pm support for the uart driver. Testing Done: Using the test daughter board, let the gpb bridge enter standby and do a uart transfer operation. Signed-off-by: Axel Haslam <haslam_axel@projectara.com> Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/uart.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 6260569b2f25..01aeed1b3a9c 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -771,6 +771,16 @@ static void gb_tty_dtr_rts(struct tty_port *port, int on) send_control(gb_tty, newctrl); } +static int gb_tty_port_activate(struct tty_port *port, + struct tty_struct *tty) +{ + struct gb_tty *gb_tty; + + gb_tty = container_of(port, struct gb_tty, port); + + return gbphy_runtime_get_sync(gb_tty->gbphy_dev); +} + static void gb_tty_port_shutdown(struct tty_port *port) { struct gb_tty *gb_tty; @@ -800,6 +810,8 @@ static void gb_tty_port_shutdown(struct tty_port *port) out: gb_tty->close_pending = false; + + gbphy_runtime_put_autosuspend(gb_tty->gbphy_dev); } static const struct tty_operations gb_ops = { @@ -822,6 +834,7 @@ static const struct tty_operations gb_ops = { static struct tty_port_operations gb_port_ops = { .dtr_rts = gb_tty_dtr_rts, + .activate = gb_tty_port_activate, .shutdown = gb_tty_port_shutdown, }; @@ -922,6 +935,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev, goto exit_connection_disable; } + gbphy_runtime_put_autosuspend(gbphy_dev); return 0; exit_connection_disable: @@ -945,6 +959,11 @@ static void gb_uart_remove(struct gbphy_device *gbphy_dev) struct gb_tty *gb_tty = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = gb_tty->connection; struct tty_struct *tty; + int ret; + + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + gbphy_runtime_get_noresume(gbphy_dev); mutex_lock(&gb_tty->mutex); gb_tty->disconnected = true; -- cgit v1.2.3-59-g8ed1b From 993dc992f2e415adf9ddc69476ebaefda6af1043 Mon Sep 17 00:00:00 2001 From: Axel Haslam <haslam_axel@projectara.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: gpio: Add runtime_pm suppourt Add runtime pm support for the gpio driver. Since there is no remote wakeup support, the module will not suspend as long as a gpio is requested. Maybe an optimization could be made, to allow suspend if all the requested gpios are in output mode, since the bridge should maintain the state of the gpio during suspend. Testing Done: using the test board, let the gpbrige enter standby and request a gpio. Signed-off-by: Axel Haslam <haslam_axel@projectara.com> Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/gpio.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index deae2658c7a8..8fa9998b4c3d 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -66,20 +66,31 @@ static int gb_gpio_line_count_operation(struct gb_gpio_controller *ggc) static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) { struct gb_gpio_activate_request request; + struct gbphy_device *gbphy_dev = ggc->gbphy_dev; int ret; + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + return ret; + request.which = which; ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_ACTIVATE, &request, sizeof(request), NULL, 0); - if (!ret) - ggc->lines[which].active = true; - return ret; + if (ret) { + gbphy_runtime_put_autosuspend(gbphy_dev); + return ret; + } + + ggc->lines[which].active = true; + + return 0; } static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, u8 which) { - struct device *dev = &ggc->gbphy_dev->dev; + struct gbphy_device *gbphy_dev = ggc->gbphy_dev; + struct device *dev = &gbphy_dev->dev; struct gb_gpio_deactivate_request request; int ret; @@ -88,10 +99,13 @@ static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, &request, sizeof(request), NULL, 0); if (ret) { dev_err(dev, "failed to deactivate gpio %u\n", which); - return; + goto out_pm_put; } ggc->lines[which].active = false; + +out_pm_put: + gbphy_runtime_put_autosuspend(gbphy_dev); } static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, @@ -709,6 +723,7 @@ static int gb_gpio_probe(struct gbphy_device *gbphy_dev, goto exit_gpiochip_remove; } + gbphy_runtime_put_autosuspend(gbphy_dev); return 0; exit_gpiochip_remove: @@ -728,6 +743,11 @@ static void gb_gpio_remove(struct gbphy_device *gbphy_dev) { struct gb_gpio_controller *ggc = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = ggc->connection; + int ret; + + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + gbphy_runtime_get_noresume(gbphy_dev); gb_connection_disable_rx(connection); gb_gpio_irqchip_remove(ggc); -- cgit v1.2.3-59-g8ed1b From afa807b8ba23952b471abb70716076bb3c98657c Mon Sep 17 00:00:00 2001 From: Axel Haslam <haslam_axel@projectara.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: pwm: Add runtime_pm support Add runtime pm support for the pmw driver. Testing Done: Set the parameters of pwm0, and enable. Disable pwm0 and let the module enter standby. Enable pwm0, and observe that with an oscilloscope that the wave form is the same as before. Signed-off-by: Axel Haslam <haslam_axel@projectara.com> Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/pwm.c | 98 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 1438b2e12cee..c4bf3298ba07 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -43,32 +43,58 @@ static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, u8 which) { struct gb_pwm_activate_request request; + struct gbphy_device *gbphy_dev; + int ret; if (which > pwmc->pwm_max) return -EINVAL; request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ACTIVATE, - &request, sizeof(request), NULL, 0); + + gbphy_dev = to_gbphy_dev(pwmc->chip.dev); + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + return ret; + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ACTIVATE, + &request, sizeof(request), NULL, 0); + + gbphy_runtime_put_autosuspend(gbphy_dev); + + return ret; } static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, u8 which) { struct gb_pwm_deactivate_request request; + struct gbphy_device *gbphy_dev; + int ret; if (which > pwmc->pwm_max) return -EINVAL; request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DEACTIVATE, - &request, sizeof(request), NULL, 0); + + gbphy_dev = to_gbphy_dev(pwmc->chip.dev); + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + return ret; + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DEACTIVATE, + &request, sizeof(request), NULL, 0); + + gbphy_runtime_put_autosuspend(gbphy_dev); + + return ret; } static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, u8 which, u32 duty, u32 period) { struct gb_pwm_config_request request; + struct gbphy_device *gbphy_dev; + int ret; if (which > pwmc->pwm_max) return -EINVAL; @@ -76,48 +102,90 @@ static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, request.which = which; request.duty = cpu_to_le32(duty); request.period = cpu_to_le32(period); - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, - &request, sizeof(request), NULL, 0); + + gbphy_dev = to_gbphy_dev(pwmc->chip.dev); + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + return ret; + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, + &request, sizeof(request), NULL, 0); + + gbphy_runtime_put_autosuspend(gbphy_dev); + + return ret; } static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, u8 which, u8 polarity) { struct gb_pwm_polarity_request request; + struct gbphy_device *gbphy_dev; + int ret; if (which > pwmc->pwm_max) return -EINVAL; request.which = which; request.polarity = polarity; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_POLARITY, - &request, sizeof(request), NULL, 0); + + gbphy_dev = to_gbphy_dev(pwmc->chip.dev); + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + return ret; + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_POLARITY, + &request, sizeof(request), NULL, 0); + + gbphy_runtime_put_autosuspend(gbphy_dev); + + return ret; } static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, u8 which) { struct gb_pwm_enable_request request; + struct gbphy_device *gbphy_dev; + int ret; if (which > pwmc->pwm_max) return -EINVAL; request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ENABLE, - &request, sizeof(request), NULL, 0); + + gbphy_dev = to_gbphy_dev(pwmc->chip.dev); + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + return ret; + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ENABLE, + &request, sizeof(request), NULL, 0); + if (ret) + gbphy_runtime_put_autosuspend(gbphy_dev); + + return ret; } static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, u8 which) { struct gb_pwm_disable_request request; + struct gbphy_device *gbphy_dev; + int ret; if (which > pwmc->pwm_max) return -EINVAL; request.which = which; - return gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DISABLE, - &request, sizeof(request), NULL, 0); + + ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DISABLE, + &request, sizeof(request), NULL, 0); + + gbphy_dev = to_gbphy_dev(pwmc->chip.dev); + gbphy_runtime_put_autosuspend(gbphy_dev); + + return ret; } static int gb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -225,6 +293,7 @@ static int gb_pwm_probe(struct gbphy_device *gbphy_dev, goto exit_connection_disable; } + gbphy_runtime_put_autosuspend(gbphy_dev); return 0; exit_connection_disable: @@ -240,6 +309,11 @@ static void gb_pwm_remove(struct gbphy_device *gbphy_dev) { struct gb_pwm_chip *pwmc = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = pwmc->connection; + int ret; + + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + gbphy_runtime_get_noresume(gbphy_dev); pwmchip_remove(&pwmc->chip); gb_connection_disable(connection); -- cgit v1.2.3-59-g8ed1b From 4c615dcc6a9d910f8f68e4b57889b628e80165d4 Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: spi: Add runtime_pm support Add runtime operations to the spi driver so that the module is woken up when an spi transfer is started and put back to sleep when the transfer is done. Testing Done: Let the module enter standby and initiate an spi operation. The operation wakes up the module and succeeds. Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/kernel_ver.h | 5 +++++ drivers/staging/greybus/spi.c | 6 ++++++ drivers/staging/greybus/spilib.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 84beb2f6334c..24d5bcd2e0ff 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -313,6 +313,11 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev) #define SPI_NOR_MODALIAS "m25p80" #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +/* Starting from this version, the spi core handles runtime pm automatically */ +#define SPI_CORE_SUPPORT_PM +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) /** * reinit_completion - reinitialize a completion structure diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index a82337931c0d..2e6e328bae9e 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -35,6 +35,7 @@ static int gb_spi_probe(struct gbphy_device *gbphy_dev, gb_gbphy_set_data(gbphy_dev, connection); + gbphy_runtime_put_autosuspend(gbphy_dev); return 0; exit_connection_disable: @@ -48,6 +49,11 @@ exit_connection_destroy: static void gb_spi_remove(struct gbphy_device *gbphy_dev) { struct gb_connection *connection = gb_gbphy_get_data(gbphy_dev); + int ret; + + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + gbphy_runtime_get_noresume(gbphy_dev); gb_spilib_master_exit(connection); gb_connection_disable(connection); diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c index 527909b26d79..e4c82e0a322b 100644 --- a/drivers/staging/greybus/spilib.c +++ b/drivers/staging/greybus/spilib.c @@ -15,6 +15,7 @@ #include "greybus.h" #include "spilib.h" +#include "gbphy.h" struct gb_spilib { struct gb_connection *connection; @@ -372,6 +373,26 @@ out: return ret; } +#ifndef SPI_CORE_SUPPORT_PM +static int gb_spi_prepare_transfer_hardware(struct spi_master *master) +{ + struct gb_spilib *spi = spi_master_get_devdata(master); + struct gbphy_device *gbphy_dev = to_gbphy_dev(spi->parent); + + return gbphy_runtime_get_sync(gbphy_dev); +} + +static int gb_spi_unprepare_transfer_hardware(struct spi_master *master) +{ + struct gb_spilib *spi = spi_master_get_devdata(master); + struct gbphy_device *gbphy_dev = to_gbphy_dev(spi->parent); + + gbphy_runtime_put_autosuspend(gbphy_dev); + + return 0; +} +#endif + static int gb_spi_setup(struct spi_device *spi) { /* Nothing to do for now */ @@ -497,6 +518,14 @@ int gb_spilib_master_init(struct gb_connection *connection, struct device *dev) master->setup = gb_spi_setup; master->transfer_one_message = gb_spi_transfer_one_message; +#ifndef SPI_CORE_SUPPORT_PM + master->prepare_transfer_hardware = gb_spi_prepare_transfer_hardware; + master->unprepare_transfer_hardware = + gb_spi_unprepare_transfer_hardware; +#else + master->auto_runtime_pm = true; +#endif + ret = spi_register_master(master); if (ret < 0) goto exit_spi_put; -- cgit v1.2.3-59-g8ed1b From e854ff58ed7011f73c7cdfcb7966ffa9c103571e Mon Sep 17 00:00:00 2001 From: Axel Haslam <haslam_axel@projectara.com> Date: Thu, 14 Jul 2016 15:13:00 -0500 Subject: greybus: loopback: add runtime pm support Add runtime pm to the loopback driver so that the module wakes up from suspend while a test is executed. Testing Done: Let the module enter standby and execute a loopback test. Signed-off-by: Axel Haslam <haslam_axel@projectara.com> Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org> --- drivers/staging/greybus/loopback.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 702cf069eefd..8b0d0dc2ed8b 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -25,6 +25,7 @@ #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/atomic.h> +#include <linux/pm_runtime.h> #include <asm/div64.h> @@ -976,14 +977,26 @@ static int gb_loopback_fn(void *data) int error = 0; int us_wait = 0; int type; + int ret; u32 size; struct gb_loopback *gb = data; + struct gb_bundle *bundle = gb->connection->bundle; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; while (1) { - if (!gb->type) + if (!gb->type) { + gb_pm_runtime_put_autosuspend(bundle); wait_event_interruptible(gb->wq, gb->type || kthread_should_stop()); + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + } + if (kthread_should_stop()) break; @@ -1042,6 +1055,9 @@ static int gb_loopback_fn(void *data) if (us_wait) udelay(us_wait); } + + gb_pm_runtime_put_autosuspend(bundle); + return 0; } @@ -1233,6 +1249,9 @@ static int gb_loopback_probe(struct gb_bundle *bundle, spin_unlock_irqrestore(&gb_dev.lock, flags); gb_connection_latency_tag_enable(connection); + + gb_pm_runtime_put_autosuspend(bundle); + return 0; out_kfifo1: @@ -1259,6 +1278,11 @@ static void gb_loopback_disconnect(struct gb_bundle *bundle) { struct gb_loopback *gb = greybus_get_drvdata(bundle); unsigned long flags; + int ret; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + gb_pm_runtime_get_noresume(bundle); gb_connection_disable(gb->connection); -- cgit v1.2.3-59-g8ed1b From 99ade1766dbd11652905ec91219a643992cb3c18 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Thu, 14 Jul 2016 14:24:18 -0500 Subject: greybus: get rid of a compile warning The compiler has no way of knowing whether a called function will actually assign something to the object whose address is passed as an argument. So it must assume it won't happen, and this leads to a compile warning. Fix this. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 7961622103be..f64dbcb5e554 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -611,7 +611,7 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) struct usb_device *udev = es2->usb_dev; struct arpc_cport_reset req; int retval; - int result; + int result = 0; switch (cport_id) { case GB_SVC_CPORT_ID: -- cgit v1.2.3-59-g8ed1b From fc0c38b3d1d6648bfef8ed2478cd2505f6a97475 Mon Sep 17 00:00:00 2001 From: Alex Elder <elder@linaro.org> Date: Thu, 14 Jul 2016 14:24:19 -0500 Subject: greybus: use memdup_user() Coccinelle reports that there are two opportunities to use memdup_user() in "authentication.c". This patch simplifies the code in cap_ioctl() by taking advantage of that. Make use of a local variable "size" to improve readability. Signed-off-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/authentication.c | 40 +++++++++++--------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/authentication.c b/drivers/staging/greybus/authentication.c index e4697805530a..a4ac3bbdcb86 100644 --- a/drivers/staging/greybus/authentication.c +++ b/drivers/staging/greybus/authentication.c @@ -209,6 +209,7 @@ static int cap_ioctl(struct gb_cap *cap, unsigned int cmd, struct cap_ioc_get_endpoint_uid endpoint_uid; struct cap_ioc_get_ims_certificate *ims_cert; struct cap_ioc_authenticate *authenticate; + size_t size; int ret; switch (cmd) { @@ -222,38 +223,26 @@ static int cap_ioctl(struct gb_cap *cap, unsigned int cmd, return 0; case CAP_IOC_GET_IMS_CERTIFICATE: - ims_cert = kzalloc(sizeof(*ims_cert), GFP_KERNEL); - if (!ims_cert) - return -ENOMEM; - - if (copy_from_user(ims_cert, buf, sizeof(*ims_cert))) { - ret = -EFAULT; - goto free_ims_cert; - } + size = sizeof(*ims_cert); + ims_cert = memdup_user(buf, size); + if (IS_ERR(ims_cert)) + return PTR_ERR(ims_cert); ret = cap_get_ims_certificate(cap, ims_cert->certificate_class, ims_cert->certificate_id, ims_cert->certificate, &ims_cert->cert_size, &ims_cert->result_code); - if (ret) - goto free_ims_cert; - - if (copy_to_user(buf, ims_cert, sizeof(*ims_cert))) + if (!ret && copy_to_user(buf, ims_cert, size)) ret = -EFAULT; - -free_ims_cert: kfree(ims_cert); + return ret; case CAP_IOC_AUTHENTICATE: - authenticate = kzalloc(sizeof(*authenticate), GFP_KERNEL); - if (!authenticate) - return -ENOMEM; - - if (copy_from_user(authenticate, buf, sizeof(*authenticate))) { - ret = -EFAULT; - goto free_authenticate; - } + size = sizeof(*authenticate); + authenticate = memdup_user(buf, size); + if (IS_ERR(authenticate)) + return PTR_ERR(authenticate); ret = cap_authenticate(cap, authenticate->auth_type, authenticate->uid, @@ -262,13 +251,10 @@ free_ims_cert: authenticate->response, &authenticate->signature_size, authenticate->signature); - if (ret) - goto free_authenticate; - - if (copy_to_user(buf, authenticate, sizeof(*authenticate))) + if (!ret && copy_to_user(buf, authenticate, size)) ret = -EFAULT; -free_authenticate: kfree(authenticate); + return ret; default: return -ENOTTY; -- cgit v1.2.3-59-g8ed1b From 6e720c277ad126de2052b90dd4114953f2d00f79 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Wed, 13 Jul 2016 14:11:19 +0100 Subject: greybus: power_supply: change property values to integer To align with power supply core values type (integer) move the val and previous_val to integer also. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/power_supply.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index e96c24da007b..f905c3c5c2ac 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -18,8 +18,8 @@ struct gb_power_supply_prop { enum power_supply_property prop; - u32 val; - u32 previous_val; + int val; + int previous_val; bool is_writeable; }; @@ -142,8 +142,8 @@ static void check_changed(struct gb_power_supply *gbpsy, struct gb_power_supply_prop *prop) { const struct gb_power_supply_changes *psyc; - u32 val = prop->val; - u32 prev_val = prop->previous_val; + int val = prop->val; + int prev_val = prop->previous_val; int i; for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) { @@ -317,7 +317,7 @@ static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy, struct gb_power_supply_prop *prop; struct gb_power_supply_get_property_request req; struct gb_power_supply_get_property_response resp; - u32 val; + int val; int ret; prop = get_psy_prop(gbpsy, psp); @@ -481,6 +481,7 @@ static int gb_power_supply_property_set(struct gb_power_supply *gbpsy, req.psy_id = gbpsy->id; req.property = (u8)psp; req.prop_val = cpu_to_le32(val); + req.prop_val = cpu_to_le32((s32)val); ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY, &req, sizeof(req), NULL, 0); -- cgit v1.2.3-59-g8ed1b From 47becc556d4e8dc5bcd370100edebd6ad749b702 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Wed, 13 Jul 2016 14:11:20 +0100 Subject: greybus: power_supply: convert greybus properties to power supply properties We need to translate greybus properties to power supply core properties, for that when parsing the properties that were fetch during configuration we check if there is a match to the running kernel version and store it in a new field (gb_prop) in the gb_power_supply_prop struct. If the corresponding property does not exist in the running kernel (because of version or vendor specific property) we drop that property. A collection of properties that, at this time, may diverge are defined in the kernel_ver.h to avoid breakage at build time and run time if this properties are not supported by the kernel. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 14 ++ drivers/staging/greybus/power_supply.c | 241 +++++++++++++++++++++++++++++++-- 2 files changed, 246 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 24d5bcd2e0ff..5d13e36008d3 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -305,6 +305,20 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev) #define PSY_HAVE_PUT #endif +/* + * General power supply properties that could be absent from various reasons, + * like kernel versions or vendor specific versions + */ +#ifndef POWER_SUPPLY_PROP_VOLTAGE_BOOT + #define POWER_SUPPLY_PROP_VOLTAGE_BOOT -1 +#endif +#ifndef POWER_SUPPLY_PROP_CURRENT_BOOT + #define POWER_SUPPLY_PROP_CURRENT_BOOT -1 +#endif +#ifndef POWER_SUPPLY_PROP_CALIBRATE + #define POWER_SUPPLY_PROP_CALIBRATE -1 +#endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) #define SPI_DEV_MODALIAS "spidev" #define SPI_NOR_MODALIAS "spi-nor" diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index f905c3c5c2ac..058fd3c60bd4 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -18,6 +18,7 @@ struct gb_power_supply_prop { enum power_supply_property prop; + u8 gb_prop; int val; int previous_val; bool is_writeable; @@ -83,6 +84,210 @@ static const struct gb_power_supply_changes psy_props_changes[] = { }, }; +static int get_psp_from_gb_prop(int gb_prop, enum power_supply_property *psp) +{ + int prop; + + switch (gb_prop) { + case GB_POWER_SUPPLY_PROP_STATUS: + prop = POWER_SUPPLY_PROP_STATUS; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_TYPE: + prop = POWER_SUPPLY_PROP_CHARGE_TYPE; + break; + case GB_POWER_SUPPLY_PROP_HEALTH: + prop = POWER_SUPPLY_PROP_HEALTH; + break; + case GB_POWER_SUPPLY_PROP_PRESENT: + prop = POWER_SUPPLY_PROP_PRESENT; + break; + case GB_POWER_SUPPLY_PROP_ONLINE: + prop = POWER_SUPPLY_PROP_ONLINE; + break; + case GB_POWER_SUPPLY_PROP_AUTHENTIC: + prop = POWER_SUPPLY_PROP_AUTHENTIC; + break; + case GB_POWER_SUPPLY_PROP_TECHNOLOGY: + prop = POWER_SUPPLY_PROP_TECHNOLOGY; + break; + case GB_POWER_SUPPLY_PROP_CYCLE_COUNT: + prop = POWER_SUPPLY_PROP_CYCLE_COUNT; + break; + case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX: + prop = POWER_SUPPLY_PROP_VOLTAGE_MAX; + break; + case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN: + prop = POWER_SUPPLY_PROP_VOLTAGE_MIN; + break; + case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN; + break; + case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN; + break; + case GB_POWER_SUPPLY_PROP_VOLTAGE_NOW: + prop = POWER_SUPPLY_PROP_VOLTAGE_NOW; + break; + case GB_POWER_SUPPLY_PROP_VOLTAGE_AVG: + prop = POWER_SUPPLY_PROP_VOLTAGE_AVG; + break; + case GB_POWER_SUPPLY_PROP_VOLTAGE_OCV: + prop = POWER_SUPPLY_PROP_VOLTAGE_OCV; + break; + case GB_POWER_SUPPLY_PROP_VOLTAGE_BOOT: + prop = POWER_SUPPLY_PROP_VOLTAGE_BOOT; + break; + case GB_POWER_SUPPLY_PROP_CURRENT_MAX: + prop = POWER_SUPPLY_PROP_CURRENT_MAX; + break; + case GB_POWER_SUPPLY_PROP_CURRENT_NOW: + prop = POWER_SUPPLY_PROP_CURRENT_NOW; + break; + case GB_POWER_SUPPLY_PROP_CURRENT_AVG: + prop = POWER_SUPPLY_PROP_CURRENT_AVG; + break; + case GB_POWER_SUPPLY_PROP_CURRENT_BOOT: + prop = POWER_SUPPLY_PROP_CURRENT_BOOT; + break; + case GB_POWER_SUPPLY_PROP_POWER_NOW: + prop = POWER_SUPPLY_PROP_POWER_NOW; + break; + case GB_POWER_SUPPLY_PROP_POWER_AVG: + prop = POWER_SUPPLY_PROP_POWER_AVG; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: + prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_FULL: + prop = POWER_SUPPLY_PROP_CHARGE_FULL; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY: + prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_NOW: + prop = POWER_SUPPLY_PROP_CHARGE_NOW; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_AVG: + prop = POWER_SUPPLY_PROP_CHARGE_AVG; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_COUNTER: + prop = POWER_SUPPLY_PROP_CHARGE_COUNTER; + break; + case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT; + break; + case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; + break; + case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE; + break; + case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX; + break; + case GB_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + prop = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; + break; + case GB_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; + break; + case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN: + prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; + break; + case GB_POWER_SUPPLY_PROP_ENERGY_FULL: + prop = POWER_SUPPLY_PROP_ENERGY_FULL; + break; + case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY: + prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; + break; + case GB_POWER_SUPPLY_PROP_ENERGY_NOW: + prop = POWER_SUPPLY_PROP_ENERGY_NOW; + break; + case GB_POWER_SUPPLY_PROP_ENERGY_AVG: + prop = POWER_SUPPLY_PROP_ENERGY_AVG; + break; + case GB_POWER_SUPPLY_PROP_CAPACITY: + prop = POWER_SUPPLY_PROP_CAPACITY; + break; + case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN; + break; + case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX: + prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX; + break; + case GB_POWER_SUPPLY_PROP_CAPACITY_LEVEL: + prop = POWER_SUPPLY_PROP_CAPACITY_LEVEL; + break; + case GB_POWER_SUPPLY_PROP_TEMP: + prop = POWER_SUPPLY_PROP_TEMP; + break; + case GB_POWER_SUPPLY_PROP_TEMP_MAX: + prop = POWER_SUPPLY_PROP_TEMP_MAX; + break; + case GB_POWER_SUPPLY_PROP_TEMP_MIN: + prop = POWER_SUPPLY_PROP_TEMP_MIN; + break; + case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + prop = POWER_SUPPLY_PROP_TEMP_ALERT_MIN; + break; + case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + prop = POWER_SUPPLY_PROP_TEMP_ALERT_MAX; + break; + case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT: + prop = POWER_SUPPLY_PROP_TEMP_AMBIENT; + break; + case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN: + prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN; + break; + case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX: + prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX; + break; + case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW; + break; + case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG; + break; + case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + prop = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW; + break; + case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + prop = POWER_SUPPLY_PROP_TIME_TO_FULL_AVG; + break; + case GB_POWER_SUPPLY_PROP_TYPE: + prop = POWER_SUPPLY_PROP_TYPE; + break; + case GB_POWER_SUPPLY_PROP_SCOPE: + prop = POWER_SUPPLY_PROP_SCOPE; + break; + case GB_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + prop = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT; + break; + case GB_POWER_SUPPLY_PROP_CALIBRATE: + prop = POWER_SUPPLY_PROP_CALIBRATE; + break; + default: + prop = -1; + break; + } + + if (prop < 0) + return prop; + + *psp = (enum power_supply_property)prop; + + return 0; +} + static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy) { return gbpsy->supplies->connection; @@ -253,8 +458,9 @@ static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy) struct gb_power_supply_get_property_descriptors_response *resp; struct gb_operation *op; u8 props_count = gbpsy->properties_count; + enum power_supply_property psp; int ret; - int i; + int i, r = 0; if (props_count == 0) return 0; @@ -276,6 +482,17 @@ static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy) resp = op->response->payload; + /* validate received properties */ + for (i = 0; i < props_count; i++) { + ret = get_psp_from_gb_prop(resp->props[i].property, &psp); + if (ret < 0) { + dev_warn(&connection->bundle->dev, + "greybus property %u it is not supported by this kernel, dropped\n", + resp->props[i].property); + gbpsy->properties_count--; + } + } + gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props), GFP_KERNEL); if (!gbpsy->props) { @@ -290,12 +507,18 @@ static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy) goto out_put_operation; } - /* Store available properties */ - for (i = 0; i < gbpsy->properties_count; i++) { - gbpsy->props[i].prop = resp->props[i].property; - gbpsy->props_raw[i] = resp->props[i].property; + /* Store available properties, skip the ones we do not support */ + for (i = 0; i < props_count; i++) { + ret = get_psp_from_gb_prop(resp->props[i].property, &psp); + if (ret < 0) { + r++; + continue; + } + gbpsy->props[i - r].prop = psp; + gbpsy->props[i - r].gb_prop = resp->props[i].property; + gbpsy->props_raw[i - r] = psp; if (resp->props[i].is_writeable) - gbpsy->props[i].is_writeable = true; + gbpsy->props[i - r].is_writeable = true; } /* @@ -304,6 +527,7 @@ static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy) */ _gb_power_supply_append_props(gbpsy); + ret = 0; out_put_operation: gb_operation_put(op); @@ -324,7 +548,7 @@ static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy, if (!prop) return -EINVAL; req.psy_id = gbpsy->id; - req.property = (u8)psp; + req.property = prop->gb_prop; ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY, &req, sizeof(req), &resp, sizeof(resp)); @@ -479,8 +703,7 @@ static int gb_power_supply_property_set(struct gb_power_supply *gbpsy, if (!prop) return -EINVAL; req.psy_id = gbpsy->id; - req.property = (u8)psp; - req.prop_val = cpu_to_le32(val); + req.property = prop->gb_prop; req.prop_val = cpu_to_le32((s32)val); ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY, -- cgit v1.2.3-59-g8ed1b From 1f77b363bef417e28d8556a11c9a82f3e6cf24f0 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Tue, 12 Jul 2016 17:41:21 -0700 Subject: greybus: hd: arche-platform: implement greybus shutdown Implement platform driver shutdown callback to perform proper greybus shutdown so that the userspace unipro_shutdown service that shuts down the APB/SVC abruptly can be removed. The shutdown callback in arche-platform will first remove SVC so that all the Interface can be Deactivated in a sequence according to the spec before powering off the APB: Before: -> Arche/APB power off -> SoC power off After this patch: -> HD shutdown -> SVC shutdown -> Module shutdown -> Interface shutdown -> Bundle shutdown -> Arche/APB power off -> SoC power off Testing Done: - Observe all Interfaces are deactivated in the log during shutdown - Measure power off current and make sure no regression Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-apb-ctrl.c | 6 ++++++ drivers/staging/greybus/arche-platform.c | 10 ++++++++++ drivers/staging/greybus/core.c | 11 +++++++++++ drivers/staging/greybus/hd.c | 6 ++++++ drivers/staging/greybus/hd.h | 1 + 5 files changed, 34 insertions(+) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index fce6a187ecfe..cae56fc0a6e5 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -444,6 +444,11 @@ static int arche_apb_ctrl_resume(struct device *dev) return 0; } +static void arche_apb_ctrl_shutdown(struct platform_device *pdev) +{ + apb_ctrl_poweroff(&pdev->dev); +} + static SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, arche_apb_ctrl_suspend, arche_apb_ctrl_resume); @@ -455,6 +460,7 @@ static struct of_device_id arche_apb_ctrl_of_match[] = { static struct platform_driver arche_apb_ctrl_device_driver = { .probe = arche_apb_ctrl_probe, .remove = arche_apb_ctrl_remove, + .shutdown = arche_apb_ctrl_shutdown, .driver = { .name = "arche-apb-ctrl", .pm = &arche_apb_ctrl_pm_ops, diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 31c952454cff..af784204973f 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -770,6 +770,15 @@ static int arche_platform_resume(struct device *dev) return 0; } +static void arche_platform_shutdown(struct platform_device *pdev) +{ + struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); + + arche_platform_poweroff_seq(arche_pdata); + + usb3613_hub_mode_ctrl(false); +} + static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, arche_platform_suspend, arche_platform_resume); @@ -789,6 +798,7 @@ MODULE_DEVICE_TABLE(of, arche_combined_id); static struct platform_driver arche_platform_device_driver = { .probe = arche_platform_probe, .remove = arche_platform_remove, + .shutdown = arche_platform_shutdown, .driver = { .name = "arche-platform-ctrl", .pm = &arche_platform_pm_ops, diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 53d9ba151aeb..2c94bbb1748f 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -146,10 +146,21 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } +static void greybus_shutdown(struct device *dev) +{ + if (is_gb_host_device(dev)) { + struct gb_host_device *hd; + + hd = to_gb_host_device(dev); + gb_hd_shutdown(hd); + } +} + struct bus_type greybus_bus_type = { .name = "greybus", .match = greybus_match_device, .uevent = greybus_uevent, + .shutdown = greybus_shutdown, }; static int greybus_probe(struct device *dev) diff --git a/drivers/staging/greybus/hd.c b/drivers/staging/greybus/hd.c index 8ef849a8300f..185ae3fa10fd 100644 --- a/drivers/staging/greybus/hd.c +++ b/drivers/staging/greybus/hd.c @@ -232,6 +232,12 @@ void gb_hd_del(struct gb_host_device *hd) } EXPORT_SYMBOL_GPL(gb_hd_del); +void gb_hd_shutdown(struct gb_host_device *hd) +{ + gb_svc_del(hd->svc); +} +EXPORT_SYMBOL_GPL(gb_hd_shutdown); + void gb_hd_put(struct gb_host_device *hd) { put_device(&hd->dev); diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index d5d8c67551f2..6ea5e28d9eb9 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -75,6 +75,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, size_t num_cports); int gb_hd_add(struct gb_host_device *hd); void gb_hd_del(struct gb_host_device *hd); +void gb_hd_shutdown(struct gb_host_device *hd); void gb_hd_put(struct gb_host_device *hd); int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, bool in_irq); -- cgit v1.2.3-59-g8ed1b From fddd7eafc746e5ad6ea0f980e85a3fdc87091e81 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Fri, 15 Jul 2016 18:01:49 +0100 Subject: greybus: timesync/pm: Make synchronous call to restore FrameTime When we discussed and agreed a serialized way to-do PM runtime suspend/resume we omitted the necessity to restore the FrameTime on resume. This patch restores the FrameTime synchronously such that when an Interface PM resume callback completes we have either successfully restored the FrameTime including the new Interface or we've produced a result code to indicate what went wrong when trying. Suggested-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Tested-by: David Lin <dtwlin@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 16e268f1b109..b3bd96e43128 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -579,6 +579,12 @@ static int gb_interface_resume(struct device *dev) return ret; } + ret = gb_timesync_schedule_synchronous(intf); + if (ret) { + dev_err(dev, "failed to synchronize FrameTime: %d\n", ret); + return ret; + } + return 0; } -- cgit v1.2.3-59-g8ed1b From c10b4ea3b23fdd1d7db531b316ba75fd2578fc2a Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Fri, 15 Jul 2016 12:16:54 +0100 Subject: greybus: power_supply: remove references to kernel header In Greybus power supply macro definitions we have a comment that point to a kernel header, remove it since we have a specification for that. As at it, align the status values to the upper values. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e9f3d2cfd973..18c527c4d443 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -533,7 +533,7 @@ struct gb_bootrom_get_vid_pid_response { #define GB_POWER_SUPPLY_TYPE_SET_PROPERTY 0x06 #define GB_POWER_SUPPLY_TYPE_EVENT 0x07 -/* Should match up with battery technologies in linux/power_supply.h */ +/* Greybus power supply battery technologies types */ #define GB_POWER_SUPPLY_TECH_UNKNOWN 0x0000 #define GB_POWER_SUPPLY_TECH_NiMH 0x0001 #define GB_POWER_SUPPLY_TECH_LION 0x0002 @@ -542,7 +542,7 @@ struct gb_bootrom_get_vid_pid_response { #define GB_POWER_SUPPLY_TECH_NiCd 0x0005 #define GB_POWER_SUPPLY_TECH_LiMn 0x0006 -/* Should match up with power supply types in linux/power_supply.h */ +/* Greybus power supply types */ #define GB_POWER_SUPPLY_UNKNOWN_TYPE 0x0000 #define GB_POWER_SUPPLY_BATTERY_TYPE 0x0001 #define GB_POWER_SUPPLY_UPS_TYPE 0x0002 @@ -552,7 +552,7 @@ struct gb_bootrom_get_vid_pid_response { #define GB_POWER_SUPPLY_USB_CDP_TYPE 0x0006 #define GB_POWER_SUPPLY_USB_ACA_TYPE 0x0007 -/* Should match up with power supply health in linux/power_supply.h */ +/* Greybus power supply health values */ #define GB_POWER_SUPPLY_HEALTH_UNKNOWN 0x0000 #define GB_POWER_SUPPLY_HEALTH_GOOD 0x0001 #define GB_POWER_SUPPLY_HEALTH_OVERHEAT 0x0002 @@ -563,12 +563,12 @@ struct gb_bootrom_get_vid_pid_response { #define GB_POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE 0x0007 #define GB_POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE 0x0008 -/* Should match up with battery status in linux/power_supply.h */ -#define GB_POWER_SUPPLY_STATUS_UNKNOWN 0x0000 -#define GB_POWER_SUPPLY_STATUS_CHARGING 0x0001 -#define GB_POWER_SUPPLY_STATUS_DISCHARGING 0x0002 -#define GB_POWER_SUPPLY_STATUS_NOT_CHARGING 0x0003 -#define GB_POWER_SUPPLY_STATUS_FULL 0x0004 +/* Greybus power supply status values */ +#define GB_POWER_SUPPLY_STATUS_UNKNOWN 0x0000 +#define GB_POWER_SUPPLY_STATUS_CHARGING 0x0001 +#define GB_POWER_SUPPLY_STATUS_DISCHARGING 0x0002 +#define GB_POWER_SUPPLY_STATUS_NOT_CHARGING 0x0003 +#define GB_POWER_SUPPLY_STATUS_FULL 0x0004 struct gb_power_supply_get_supplies_response { __u8 supplies_count; -- cgit v1.2.3-59-g8ed1b From 7a0d4eae83e7dbabd02c744ec3aa0ed542ed6181 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Fri, 15 Jul 2016 12:16:55 +0100 Subject: greybus: power_supply: add missing defines present in the specification Some of the Greybus properties values (capacity level and scope) even though they are defined in the specification were missing from the protocol header. They still match the kernel ones, but they should be present in there for protocol sake. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 18c527c4d443..21669093daf5 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -570,6 +570,19 @@ struct gb_bootrom_get_vid_pid_response { #define GB_POWER_SUPPLY_STATUS_NOT_CHARGING 0x0003 #define GB_POWER_SUPPLY_STATUS_FULL 0x0004 +/* Greybus power supply capacity level values */ +#define GB_POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN 0x0000 +#define GB_POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL 0x0001 +#define GB_POWER_SUPPLY_CAPACITY_LEVEL_LOW 0x0002 +#define GB_POWER_SUPPLY_CAPACITY_LEVEL_NORMAL 0x0003 +#define GB_POWER_SUPPLY_CAPACITY_LEVEL_HIGH 0x0004 +#define GB_POWER_SUPPLY_CAPACITY_LEVEL_FULL 0x0005 + +/* Greybus power supply scope values */ +#define GB_POWER_SUPPLY_SCOPE_UNKNOWN 0x0000 +#define GB_POWER_SUPPLY_SCOPE_SYSTEM 0x0001 +#define GB_POWER_SUPPLY_SCOPE_DEVICE 0x0002 + struct gb_power_supply_get_supplies_response { __u8 supplies_count; } __packed; -- cgit v1.2.3-59-g8ed1b From a0de502ed39663d47b568de27f46971e41f0abdf Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 16 Jul 2016 09:44:28 -0700 Subject: greybus: audio_topology: Fix compile warning Fix following compile warning by staticizing gb_generate_enum_strings(). greybus/audio_topology.c:134:12: warning: symbol 'gb_generate_enum_strings' was not declared. Should it be static? Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_topology.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 487f74455a1c..17ba0a028426 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -131,8 +131,8 @@ static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module, return NULL; } -const char **gb_generate_enum_strings(struct gbaudio_module_info *gb, - struct gb_audio_enumerated *gbenum) +static const char **gb_generate_enum_strings(struct gbaudio_module_info *gb, + struct gb_audio_enumerated *gbenum) { const char **strings; int i; -- cgit v1.2.3-59-g8ed1b From 6cc270483c550f1d3f7098bb7fb0de20537947a2 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Fri, 15 Jul 2016 11:03:41 +0200 Subject: greybus: camera: Add bpp to Greybus Format description Add the bytes per pixel value to the structure describing a Greybus Protocol Image Format. The bpp information will be used to compute the length in bytes of a line of pixel data Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Reviewed-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index bdceb77200c6..7835ed78570a 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -74,76 +74,93 @@ struct gb_camera_stream_config { unsigned int max_size; }; -struct gb_camera_fmt_map { +struct gb_camera_fmt_info { enum v4l2_mbus_pixelcode mbus_code; unsigned int gb_format; + unsigned int bpp; }; /* GB format to media code map */ -static const struct gb_camera_fmt_map mbus_to_gbus_format[] = { +static const struct gb_camera_fmt_info gb_fmt_info[] = { { .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, .gb_format = 0x01, + .bpp = 16, }, { .mbus_code = V4L2_MBUS_FMT_NV12_1x8, .gb_format = 0x12, + .bpp = 12, }, { .mbus_code = V4L2_MBUS_FMT_NV21_1x8, .gb_format = 0x13, + .bpp = 12, }, { .mbus_code = V4L2_MBUS_FMT_YU12_1x8, .gb_format = 0x16, + .bpp = 12, }, { .mbus_code = V4L2_MBUS_FMT_YV12_1x8, .gb_format = 0x17, + .bpp = 12, }, { .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, .gb_format = 0x40, + .bpp = 0, }, { .mbus_code = V4L2_MBUS_FMT_ARA_METADATA_1X8, .gb_format = 0x41, + .bpp = 0, }, { .mbus_code = V4L2_MBUS_FMT_ARA_DEBUG_DATA_1X8, .gb_format = 0x42, + .bpp = 0, }, { .mbus_code = V4L2_MBUS_FMT_SBGGR10_1X10, .gb_format = 0x80, + .bpp = 10, }, { .mbus_code = V4L2_MBUS_FMT_SGBRG10_1X10, .gb_format = 0x81, + .bpp = 10, }, { .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, .gb_format = 0x82, + .bpp = 10, }, { .mbus_code = V4L2_MBUS_FMT_SRGGB10_1X10, .gb_format = 0x83, + .bpp = 10, }, { .mbus_code = V4L2_MBUS_FMT_SBGGR12_1X12, .gb_format = 0x84, + .bpp = 12, }, { .mbus_code = V4L2_MBUS_FMT_SGBRG12_1X12, .gb_format = 0x85, + .bpp = 12, }, { .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, .gb_format = 0x86, + .bpp = 12, }, { .mbus_code = V4L2_MBUS_FMT_SRGGB12_1X12, .gb_format = 0x87, + .bpp = 12, }, }; @@ -615,22 +632,22 @@ static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { - if (mbus_to_gbus_format[i].mbus_code == mbus_code) - return mbus_to_gbus_format[i].gb_format; + for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { + if (gb_fmt_info[i].mbus_code == mbus_code) + return gb_fmt_info[i].gb_format; } - return mbus_to_gbus_format[0].gb_format; + return gb_fmt_info[0].gb_format; } static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(mbus_to_gbus_format); i++) { - if (mbus_to_gbus_format[i].gb_format == gb_fmt) - return mbus_to_gbus_format[i].mbus_code; + for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { + if (gb_fmt_info[i].gb_format == gb_fmt) + return gb_fmt_info[i].mbus_code; } - return mbus_to_gbus_format[0].mbus_code; + return gb_fmt_info[0].mbus_code; } static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len) -- cgit v1.2.3-59-g8ed1b From fdf73c00c8872f6f59730955d54b2cdc963e2485 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Fri, 15 Jul 2016 11:03:42 +0200 Subject: greybus: camera: Add wrapper for sync operation with flags The greybus operation core synchronous operation call doesn't support operation flags. Create a new synchronous operation wrapper with flags, and modify the capabilities operation implementation to use it. The code could be later moved to the greybus core if other drivers have a similar need. Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 60 +++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 7835ed78570a..9d9746f434e4 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -173,6 +173,41 @@ static const struct gb_camera_fmt_info gb_fmt_info[] = { #define gcam_info(gcam, format...) dev_info(&gcam->bundle->dev, format) #define gcam_err(gcam, format...) dev_err(&gcam->bundle->dev, format) +static int gb_camera_operation_sync_flags(struct gb_connection *connection, + int type, unsigned int flags, + void *request, size_t request_size, + void *response, size_t *response_size) +{ + struct gb_operation *operation; + int ret; + + operation = gb_operation_create_flags(connection, type, request_size, + *response_size, flags, + GFP_KERNEL); + if (!operation) + return -ENOMEM; + + if (request_size) + memcpy(operation->request->payload, request, request_size); + + ret = gb_operation_request_send_sync(operation); + if (ret) { + dev_err(&connection->hd->dev, + "%s: synchronous operation of type 0x%02x failed: %d\n", + connection->name, type, ret); + } else { + *response_size = operation->response->payload_size; + + if (operation->response->payload_size) + memcpy(response, operation->response->payload, + operation->response->payload_size); + } + + gb_operation_put(operation); + + return ret; +} + /* ----------------------------------------------------------------------------- * Hardware Configuration */ @@ -347,7 +382,6 @@ static void gb_camera_teardown_data_connection(struct gb_camera *gcam) static int gb_camera_capabilities(struct gb_camera *gcam, u8 *capabilities, size_t *size) { - struct gb_operation *op = NULL; int ret; ret = gb_pm_runtime_get_sync(gcam->bundle); @@ -361,28 +395,16 @@ static int gb_camera_capabilities(struct gb_camera *gcam, goto done; } - op = gb_operation_create_flags(gcam->connection, - GB_CAMERA_TYPE_CAPABILITIES, 0, *size, - GB_OPERATION_FLAG_SHORT_RESPONSE, - GFP_KERNEL); - if (!op) { - ret = -ENOMEM; - goto done; - } - - ret = gb_operation_request_send_sync(op); - if (ret) { + ret = gb_camera_operation_sync_flags(gcam->connection, + GB_CAMERA_TYPE_CAPABILITIES, + GB_OPERATION_FLAG_SHORT_RESPONSE, + NULL, 0, + (void *)capabilities, size); + if (ret) gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret); - goto done; - } - - memcpy(capabilities, op->response->payload, op->response->payload_size); - *size = op->response->payload_size; done: mutex_unlock(&gcam->mutex); - if (op) - gb_operation_put(op); gb_pm_runtime_put_autosuspend(gcam->bundle); -- cgit v1.2.3-59-g8ed1b From 24f9a6e4942d7a8b15a1bac9a96a605a433b9478 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Date: Fri, 15 Jul 2016 11:03:43 +0200 Subject: greybus: camera: Hardcode the APB-A CSI-2 TX parameters Camera modules will stop reporting the number of lines per second, hardcode the parameter in the driver until the APB-A CSI-2 TX configuration protocol gets updated as well to use a more appropriate form of bandwidth information. The number of data lanes is also hardcoded as it should not depend on the module's CSI-2 bus configuration. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 9d9746f434e4..ceffe14201d1 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -277,6 +277,13 @@ struct ap_csi_config_request { __le32 lines_per_second; } __packed; +/* + * TODO: Compute the number of lanes dynamically based on bandwidth + * requirements. + */ +#define GB_CAMERA_CSI_NUM_DATA_LANES 4 +#define GB_CAMERA_LINES_PER_SECOND (1280 * 30) + static int gb_camera_setup_data_connection(struct gb_camera *gcam, const struct gb_camera_configure_streams_response *resp, struct gb_camera_csi_params *csi_params) @@ -308,8 +315,8 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam, goto error_conn_disable; /* - * Configure the APB1 CSI transmitter using the lines count reported by - * the camera module, but with hard-coded bus frequency and lanes number. + * Configure the APB1 CSI transmitter with hard-coded bus frequency, + * lanes number and lines per second. * * TODO: use the clocking and size informations reported by camera module * to compute the required CSI bandwidth, and configure the CSI receiver @@ -318,9 +325,9 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam, memset(&csi_cfg, 0, sizeof(csi_cfg)); csi_cfg.csi_id = 1; csi_cfg.flags = 0; - csi_cfg.num_lanes = resp->num_lanes; + csi_cfg.num_lanes = GB_CAMERA_CSI_NUM_DATA_LANES; csi_cfg.bus_freq = cpu_to_le32(960000000); - csi_cfg.lines_per_second = resp->lines_per_second; + csi_cfg.lines_per_second = GB_CAMERA_LINES_PER_SECOND; ret = gb_hd_output(gcam->connection->hd, &csi_cfg, sizeof(csi_cfg), @@ -335,7 +342,6 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam, csi_params->num_lanes = csi_cfg.num_lanes; /* Transmitting two bits per cycle. (DDR clock) */ csi_params->clk_freq = csi_cfg.bus_freq / 2; - csi_params->lines_per_second = csi_cfg.lines_per_second; } return 0; -- cgit v1.2.3-59-g8ed1b From d165a618a14c8871f8e14090791298e1b2ef8231 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Fri, 15 Jul 2016 11:03:44 +0200 Subject: greybus: camera: Update Configure Streams Response As camera specification gets updated, and Configure Stream Response payload modified, define here the new response structure. In order to not break non up-to-date camera modules, keep the existing structure and add the _deprecated suffix to it. Add the size of both new and old structure in order to discriminate dynamically which version of Camera Specification the camera module implements and translate deprecated version of configure_streams response in the new one. In order not to break camera functionalities, for testing purposes, hard-code values the APB-A CSI Tx driver still requires for proper interface configuration (lines_per_second and num_lanes) Testing Done: Preview, capture and video recording with white camera module and APB-A with legacy firmware implementations Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 186 ++++++++++++++++++++++++---- drivers/staging/greybus/greybus_protocols.h | 17 ++- 2 files changed, 176 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index ceffe14201d1..9cf0d4dcc9f2 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -164,6 +164,18 @@ static const struct gb_camera_fmt_info gb_fmt_info[] = { }, }; +static const struct gb_camera_fmt_info *gb_camera_get_format_info(u16 gb_fmt) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { + if (gb_fmt_info[i].gb_format == gb_fmt) + return &gb_fmt_info[i]; + } + + return NULL; +} + #define ES2_APB_CDSI0_CPORT 16 #define ES2_APB_CDSI1_CPORT 17 @@ -208,6 +220,130 @@ static int gb_camera_operation_sync_flags(struct gb_connection *connection, return ret; } +/* + * Temporary support for camera modules implementing legacy version + * of camera specifications + */ +static int gb_camera_configure_stream_translate_deprecated( + struct gb_camera *gcam, + void *module_resp, + struct gb_camera_configure_streams_response *resp) +{ + unsigned int i; + struct gb_camera_configure_streams_response_deprecated *dresp = + (struct gb_camera_configure_streams_response_deprecated *) + module_resp; + + if (dresp->padding != 0) { + gcam_err(gcam, "legacy response padding != 0\n"); + return -EIO; + } + + resp->num_streams = dresp->num_streams; + resp->flags = dresp->flags; + resp->data_rate = dresp->bus_freq; + + for (i = 0; i < dresp->num_streams; i++) { + const struct gb_camera_fmt_info *fmt_info; + struct gb_camera_stream_config_response *cfg; + + if (dresp->config[i].padding || + dresp->config[i].max_pkt_size) { + gcam_err(gcam, "legacy stream #%u padding != 0\n", i); + return -EIO; + } + + resp->config[i] = dresp->config[i]; + cfg = &resp->config[i]; + + /* + * As implementations of legacy version of camera protocol do + * not provide the max_pkt_size attribute, re-calculate it on + * AP side. + */ + fmt_info = gb_camera_get_format_info(cfg->format); + if (!fmt_info) { + gcam_err(gcam, "unsupported greybus image format %d\n", + cfg->format); + return -EIO; + } + + if (fmt_info->bpp == 0) { + cfg->max_pkt_size = cpu_to_le16(4096); + } else if (fmt_info->bpp > 0) { + unsigned int width = le16_to_cpu(cfg->width); + + cfg->max_pkt_size = cpu_to_le32(width * fmt_info->bpp / 8); + } + } + + return 0; +} + +/* + * Validate the stream configuration response verifying padding is correctly + * set and the returned number of streams is supported + * + * FIXME: The function also checks which protocol version the camera module + * implements and if it supports or not the new bandwidth requirements + * definition parameters. + * In case the camera implements the legacy version of protocol + * specifications, it gets translated to the new one. + */ +static int gb_camera_configure_streams_validate_response( + struct gb_camera *gcam, + void *module_resp, + struct gb_camera_configure_streams_response *ap_resp, + unsigned int resp_size, + unsigned int nstreams) +{ + struct gb_camera_configure_streams_response *resp; + unsigned int i; + unsigned int module_resp_size = + resp_size - + sizeof(struct gb_camera_stream_config_response) * + nstreams; + + /* TODO: remove support for legacy camera modules */ + if (module_resp_size == GB_CAMERA_CONFIGURE_STREAMS_DEPRECATED_SIZE) + return gb_camera_configure_stream_translate_deprecated(gcam, + module_resp, ap_resp); + + if (module_resp_size != GB_CAMERA_CONFIGURE_STREAMS_SIZE) { + gcam_err(gcam, "unrecognized protocol version %u\n", + module_resp_size); + return -EIO; + } + + resp = (struct gb_camera_configure_streams_response *) module_resp; + *ap_resp = *resp; + + /* Validate the returned response structure */ + if (ap_resp->padding[0] || ap_resp->padding[1]) { + gcam_err(gcam, "response padding != 0\n"); + return -EIO; + } + + if (ap_resp->num_streams > nstreams) { + gcam_err(gcam, "got #streams %u > request %u\n", + resp->num_streams, nstreams); + return -EIO; + } + + for (i = 0; i < ap_resp->num_streams; i++) { + struct gb_camera_stream_config_response *cfg = &resp->config[i]; + + if (cfg->padding) { + gcam_err(gcam, "stream #%u padding != 0\n", i); + return -EIO; + } + + ap_resp->config[i] = *cfg; + } + + return 0; +} + /* ----------------------------------------------------------------------------- * Hardware Configuration */ @@ -318,9 +454,10 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam, * Configure the APB1 CSI transmitter with hard-coded bus frequency, * lanes number and lines per second. * - * TODO: use the clocking and size informations reported by camera module - * to compute the required CSI bandwidth, and configure the CSI receiver - * on AP side, and the CSI transmitter on APB1 side accordingly. + * TODO: Use the data rate and size information reported by camera + * module to compute the required CSI bandwidth, and configure the + * CSI receiver on AP side, and the CSI transmitter on APB1 side + * accordingly. */ memset(&csi_cfg, 0, sizeof(csi_cfg)); csi_cfg.csi_id = 1; @@ -425,10 +562,12 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, { struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; + void *module_resp; unsigned int nstreams = *num_streams; unsigned int i; size_t req_size; size_t resp_size; + size_t module_resp_size; int ret; if (nstreams > GB_CAMERA_MAX_STREAMS) @@ -437,11 +576,21 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); + /* + * FIXME: Reserve enough space for the deprecated version of the + * configure_stream response, as it is bigger than the + * newly defined one + */ + module_resp_size = GB_CAMERA_CONFIGURE_STREAMS_DEPRECATED_SIZE + + nstreams * sizeof(resp->config[0]); + req = kmalloc(req_size, GFP_KERNEL); resp = kmalloc(resp_size, GFP_KERNEL); - if (!req || !resp) { + module_resp = kmalloc(module_resp_size, GFP_KERNEL); + if (!req || !resp || !module_resp) { kfree(req); kfree(resp); + kfree(module_resp); return -ENOMEM; } @@ -469,24 +618,18 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, goto done; } - ret = gb_operation_sync(gcam->connection, - GB_CAMERA_TYPE_CONFIGURE_STREAMS, - req, req_size, resp, resp_size); + ret = gb_camera_operation_sync_flags(gcam->connection, + GB_CAMERA_TYPE_CONFIGURE_STREAMS, + GB_OPERATION_FLAG_SHORT_RESPONSE, + req, req_size, + module_resp, &module_resp_size); if (ret < 0) goto done; - if (resp->num_streams > nstreams) { - gcam_dbg(gcam, "got #streams %u > request %u\n", - resp->num_streams, nstreams); - ret = -EIO; - goto done; - } - - if (resp->padding != 0) { - gcam_dbg(gcam, "response padding != 0"); - ret = -EIO; + ret = gb_camera_configure_streams_validate_response(gcam, + module_resp, resp, module_resp_size, nstreams); + if (ret < 0) goto done; - } *flags = resp->flags; *num_streams = resp->num_streams; @@ -501,12 +644,6 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, streams[i].dt[0] = cfg->data_type[0]; streams[i].dt[1] = cfg->data_type[1]; streams[i].max_size = le32_to_cpu(cfg->max_size); - - if (cfg->padding[0] || cfg->padding[1] || cfg->padding[2]) { - gcam_dbg(gcam, "stream #%u padding != 0", i); - ret = -EIO; - goto done; - } } if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) || @@ -557,6 +694,7 @@ done_skip_pm_put: mutex_unlock(&gcam->mutex); kfree(req); kfree(resp); + kfree(module_resp); return ret; } diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 21669093daf5..178ec891e9ad 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1706,20 +1706,31 @@ struct gb_camera_stream_config_response { __le16 format; __u8 virtual_channel; __u8 data_type[2]; - __u8 padding[3]; + __le16 max_pkt_size; + __u8 padding; __le32 max_size; } __packed; -struct gb_camera_configure_streams_response { +struct gb_camera_configure_streams_response_deprecated { __u8 num_streams; __u8 flags; -#define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED 0x01 __u8 num_lanes; __u8 padding; __le32 bus_freq; __le32 lines_per_second; struct gb_camera_stream_config_response config[0]; } __packed; +#define GB_CAMERA_CONFIGURE_STREAMS_DEPRECATED_SIZE 12 + +struct gb_camera_configure_streams_response { + __u8 num_streams; +#define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED 0x01 + __u8 flags; + __u8 padding[2]; + __le32 data_rate; + struct gb_camera_stream_config_response config[0]; +}; +#define GB_CAMERA_CONFIGURE_STREAMS_SIZE 8 /* Greybus Camera Capture request payload - response has no payload */ struct gb_camera_capture_request { -- cgit v1.2.3-59-g8ed1b From f88b94ec2a3b37e8f9fd20f8fb17d9e0a104fe1f Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Fri, 15 Jul 2016 11:03:45 +0200 Subject: greybus: camera: Configure APB-A with new params Update the configuration supplied to APB-A to use new parameters, while keeping compatibility with legacy camera modules. Substitute hard-coded clock frequency with information provided by camera module, and retrieve the maximum CSI Long Packet length from the returned stream configuration. This patch requires APB-A csi-tx driver to be updated to comply with newly defined bandwidth requirements. Testing Done: preview, capture and video recording with updated csi-tx driver on APB-A side, and legacy white camera module firmware Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 126 ++++++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 35 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 9cf0d4dcc9f2..e0719e2db4e6 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -220,6 +220,50 @@ static int gb_camera_operation_sync_flags(struct gb_connection *connection, return ret; } +static int gb_camera_get_max_pkt_size(struct gb_camera *gcam, + struct gb_camera_configure_streams_response *resp) +{ + unsigned int max_pkt_size = 0; + unsigned int i; + + for (i = 0; i < resp->num_streams; i++) { + struct gb_camera_stream_config_response *cfg = &resp->config[i]; + const struct gb_camera_fmt_info *fmt_info; + unsigned int pkt_size; + + fmt_info = gb_camera_get_format_info(cfg->format); + if (!fmt_info) { + gcam_err(gcam, "unsupported greybus image format: %d\n", + cfg->format); + return -EIO; + } + + if (fmt_info->bpp == 0) { + pkt_size = le32_to_cpu(cfg->max_pkt_size); + + if (pkt_size == 0) { + gcam_err(gcam, + "Stream %u: invalid zero maximum packet size\n", + i); + return -EIO; + } + } else { + pkt_size = le16_to_cpu(cfg->width) * fmt_info->bpp / 8; + + if (pkt_size != le32_to_cpu(cfg->max_pkt_size)) { + gcam_err(gcam, + "Stream %u: maximum packet size mismatch (%u/%u)\n", + i, pkt_size, cfg->max_pkt_size); + return -EIO; + } + } + + max_pkt_size = max(pkt_size, max_pkt_size); + } + + return max_pkt_size; +} + /* * Temporary support for camera modules implementing legacy version * of camera specifications @@ -409,8 +453,8 @@ struct ap_csi_config_request { #define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01 __u8 num_lanes; __u8 padding; - __le32 bus_freq; - __le32 lines_per_second; + __le32 csi_clk_freq; + __le32 max_pkt_size; } __packed; /* @@ -418,14 +462,18 @@ struct ap_csi_config_request { * requirements. */ #define GB_CAMERA_CSI_NUM_DATA_LANES 4 -#define GB_CAMERA_LINES_PER_SECOND (1280 * 30) + +#define GB_CAMERA_CSI_CLK_FREQ_MAX 999000000U +#define GB_CAMERA_CSI_CLK_FREQ_MIN 100000000U +#define GB_CAMERA_CSI_CLK_FREQ_MARGIN 150000000U static int gb_camera_setup_data_connection(struct gb_camera *gcam, - const struct gb_camera_configure_streams_response *resp, + struct gb_camera_configure_streams_response *resp, struct gb_camera_csi_params *csi_params) { struct ap_csi_config_request csi_cfg; struct gb_connection *conn; + unsigned int clk_freq; int ret; /* @@ -451,34 +499,40 @@ static int gb_camera_setup_data_connection(struct gb_camera *gcam, goto error_conn_disable; /* - * Configure the APB1 CSI transmitter with hard-coded bus frequency, - * lanes number and lines per second. + * Configure the APB-A CSI-2 transmitter. * - * TODO: Use the data rate and size information reported by camera - * module to compute the required CSI bandwidth, and configure the - * CSI receiver on AP side, and the CSI transmitter on APB1 side - * accordingly. + * Hardcode the number of lanes to 4 and compute the bus clock frequency + * based on the module bandwidth requirements with a safety margin. */ memset(&csi_cfg, 0, sizeof(csi_cfg)); csi_cfg.csi_id = 1; csi_cfg.flags = 0; csi_cfg.num_lanes = GB_CAMERA_CSI_NUM_DATA_LANES; - csi_cfg.bus_freq = cpu_to_le32(960000000); - csi_cfg.lines_per_second = GB_CAMERA_LINES_PER_SECOND; + + clk_freq = resp->data_rate / 2 / GB_CAMERA_CSI_NUM_DATA_LANES; + clk_freq = clamp(clk_freq + GB_CAMERA_CSI_CLK_FREQ_MARGIN, + GB_CAMERA_CSI_CLK_FREQ_MIN, + GB_CAMERA_CSI_CLK_FREQ_MAX); + csi_cfg.csi_clk_freq = clk_freq; + + ret = gb_camera_get_max_pkt_size(gcam, resp); + if (ret < 0) { + ret = -EIO; + goto error_power; + } + csi_cfg.max_pkt_size = ret; ret = gb_hd_output(gcam->connection->hd, &csi_cfg, sizeof(csi_cfg), GB_APB_REQUEST_CSI_TX_CONTROL, false); - if (ret < 0) { gcam_err(gcam, "failed to start the CSI transmitter\n"); goto error_power; } if (csi_params) { + csi_params->clk_freq = csi_cfg.csi_clk_freq; csi_params->num_lanes = csi_cfg.num_lanes; - /* Transmitting two bits per cycle. (DDR clock) */ - csi_params->clk_freq = csi_cfg.bus_freq / 2; } return 0; @@ -664,29 +718,31 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, gb_pm_runtime_put_noidle(gcam->bundle); } - if (resp->num_streams) { - /* - * Make sure the bundle won't be suspended until streams get - * unconfigured after the stream is configured successfully - */ - gb_pm_runtime_get_noresume(gcam->bundle); - - ret = gb_camera_setup_data_connection(gcam, resp, csi_params); - if (ret < 0) { - memset(req, 0, sizeof(*req)); - gb_operation_sync(gcam->connection, - GB_CAMERA_TYPE_CONFIGURE_STREAMS, - req, sizeof(*req), - resp, sizeof(*resp)); - *flags = 0; - *num_streams = 0; - gb_pm_runtime_put_noidle(gcam->bundle); - goto done; - } + if (resp->num_streams == 0) + goto done; + + /* + * Make sure the bundle won't be suspended until streams get + * unconfigured after the stream is configured successfully + */ + gb_pm_runtime_get_noresume(gcam->bundle); - gcam->state = GB_CAMERA_STATE_CONFIGURED; + /* Setup CSI-2 connection from APB-A to AP */ + ret = gb_camera_setup_data_connection(gcam, resp, csi_params); + if (ret < 0) { + memset(req, 0, sizeof(*req)); + gb_operation_sync(gcam->connection, + GB_CAMERA_TYPE_CONFIGURE_STREAMS, + req, sizeof(*req), + resp, sizeof(*resp)); + *flags = 0; + *num_streams = 0; + gb_pm_runtime_put_noidle(gcam->bundle); + goto done; } + gcam->state = GB_CAMERA_STATE_CONFIGURED; + done: gb_pm_runtime_put_autosuspend(gcam->bundle); -- cgit v1.2.3-59-g8ed1b From 433aa123f51b85b5ad8fb2f088a6365d54e2763b Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Fri, 15 Jul 2016 11:03:46 +0200 Subject: greybus: camera: Update CSI config parameters Remove lines_per_second parameter from csi configuration structure as VFE driver dropped dependency on it Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gb-camera.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/greybus/gb-camera.h b/drivers/staging/greybus/gb-camera.h index b8651ed05244..d45dabc5b367 100644 --- a/drivers/staging/greybus/gb-camera.h +++ b/drivers/staging/greybus/gb-camera.h @@ -41,13 +41,10 @@ struct gb_camera_stream { * struct gb_camera_csi_params - CSI configuration parameters * @num_lanes: number of CSI data lanes * @clk_freq: CSI clock frequency in Hz - * @lines_per_second: Total number of lines in a second of transmission - * (blanking included) */ struct gb_camera_csi_params { unsigned int num_lanes; unsigned int clk_freq; - unsigned int lines_per_second; }; /** -- cgit v1.2.3-59-g8ed1b From 6a1d29595ed7af5aebbcdb8f4cc045ae7893dc6c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Mon, 18 Jul 2016 14:59:36 +0100 Subject: greybus: greybus_protocols.h/es2: Ensure __le32 is used not u32 There is a dangling u32 in es2/greybus_protocols.h that is only obvious when you try to compile up gbsim. We need to do a cpu_to_le32 and declare the 32-bit int in the header as __le32. This patch does that and splits out the assignment of the req->flags parameter to a temporary variable to allow for later printing. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Suggested-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 10 +++++++--- drivers/staging/greybus/greybus_protocols.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index f64dbcb5e554..afc2d6c8ec5c 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -690,19 +690,23 @@ static int cport_enable(struct gb_host_device *hd, u16 cport_id, struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; struct gb_apb_request_cport_flags *req; + u32 connection_flags; int ret; req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; + connection_flags = 0; if (flags & GB_CONNECTION_FLAG_CONTROL) - req->flags |= GB_APB_CPORT_FLAG_CONTROL; + connection_flags |= GB_APB_CPORT_FLAG_CONTROL; if (flags & GB_CONNECTION_FLAG_HIGH_PRIO) - req->flags |= GB_APB_CPORT_FLAG_HIGH_PRIO; + connection_flags |= GB_APB_CPORT_FLAG_HIGH_PRIO; + + req->flags = cpu_to_le32(connection_flags); dev_dbg(&hd->dev, "%s - cport = %u, flags = %02x\n", __func__, - cport_id, req->flags); + cport_id, connection_flags); ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), GB_APB_REQUEST_CPORT_FLAGS, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 178ec891e9ad..d39a580a3a55 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -272,7 +272,7 @@ struct gb_control_intf_pm_response { #define APBA_REQUEST_ARPC_RUN 0x12 struct gb_apb_request_cport_flags { - u32 flags; + __le32 flags; #define GB_APB_CPORT_FLAG_CONTROL 0x01 #define GB_APB_CPORT_FLAG_HIGH_PRIO 0x02 } __packed; -- cgit v1.2.3-59-g8ed1b From 1455db9e1ba3f602084854fa9c222e9b68e8576c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Mon, 18 Jul 2016 14:59:37 +0100 Subject: greybus: greybus_protocols.h: convert __u32 to __le32 All 32 bit declarations associated with data interchange to the greybus network need to be declared explicitly as little-endian since that is the byte order we use on the greybus network. struct gb_audio_topology is declaring its variables as u32. Fortunately this structure isn't currently used so we can do a conversion from u32 to __le32 without any ancillary code-churn. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index d39a580a3a55..1966136d0649 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -2161,10 +2161,10 @@ struct gb_audio_topology { __u8 num_controls; __u8 num_widgets; __u8 num_routes; - __u32 size_dais; - __u32 size_controls; - __u32 size_widgets; - __u32 size_routes; + __le32 size_dais; + __le32 size_controls; + __le32 size_widgets; + __le32 size_routes; /* * struct gb_audio_dai dai[num_dais]; * struct gb_audio_control controls[num_controls]; -- cgit v1.2.3-59-g8ed1b From 52764bd8aac77dd8ddbdaf55ae896a55ab2a9f5e Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 19 Jul 2016 01:56:49 +0100 Subject: greybus: timesync: Bugfix ping should not result in -EAGAIN gb_timesync_schedule_synchronous() is currently making a synchronous FrameTime synchronization happen. It does a wait_event_interruptible() and then goes to check the status of the state-machine. Occasionally the state indicates PING - which is a completely valid state and should result in a result code of 0 not -EAGAIN. This patch fixes by making __gb_timesync_get_status() return 0 instead of -EAGAIN for the PING state. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Tested-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index 14531edb824c..ddbdf2beb48d 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -462,9 +462,9 @@ static int __gb_timesync_get_status(struct gb_timesync_svc *timesync_svc) case GB_TIMESYNC_STATE_INIT: case GB_TIMESYNC_STATE_WAIT_SVC: case GB_TIMESYNC_STATE_AUTHORITATIVE: - case GB_TIMESYNC_STATE_PING: ret = -EAGAIN; break; + case GB_TIMESYNC_STATE_PING: case GB_TIMESYNC_STATE_ACTIVE: ret = 0; break; -- cgit v1.2.3-59-g8ed1b From 56c78715eaaeba41317a82dc91a037cbbea16736 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Sat, 16 Jul 2016 09:57:30 -0700 Subject: greybus: bootrom: Use "s2l" instead of boot stage in package name The S3 firmware and S3-BFU firmware images will be named like this going forward: S3: ara_00000126_00001001_fffe0001_ffe70018_s3f.tftf S3-BFU: ara_00000126_00001001_fffe0001_ffe70018_s3_bfu.tftf But the current naming for S2 loader image is: ara_00000126_00001001_fffe0001_ffe70018_02.tftf It makes sense to use similar encoding for all three packages and so it should be named as: ara_00000126_00001001_fffe0001_ffe70018_s2l.tftf Because the boot stage is passed from ES3 bootrom, we can't change its value now. But the string created to match the package name is created in bootrom.c and that is the only string we create from bootrom.c. Update bootrom.c to use "s2l" instead of "02" in the package name. Compile Tested only. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Karthik Ravi Shankar <karthikrs@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index 84537a07ab7f..688184181e0c 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -138,21 +138,28 @@ static int download_firmware(struct gb_bootrom *bootrom, u8 stage) { struct gb_connection *connection = bootrom->connection; struct gb_interface *intf = connection->bundle->intf; - char firmware_name[48]; + char firmware_name[49]; int rc; /* Already have a firmware, free it */ free_firmware(bootrom); + /* Bootrom protocol is only supported for loading Stage 2 firmware */ + if (stage != 2) { + dev_err(&connection->bundle->dev, "Invalid boot stage: %u\n", + stage); + return -EINVAL; + } + /* * Create firmware name * * XXX Name it properly.. */ snprintf(firmware_name, sizeof(firmware_name), - "ara_%08x_%08x_%08x_%08x_%02x.tftf", + "ara_%08x_%08x_%08x_%08x_s2l.tftf", intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, - intf->vendor_id, intf->product_id, stage); + intf->vendor_id, intf->product_id); // FIXME: // Turn to dev_dbg later after everyone has valid bootloaders with good -- cgit v1.2.3-59-g8ed1b From 484a4d667751148654277a3ec93b8f0a327da29d Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Tue, 19 Jul 2016 15:41:29 +0100 Subject: greybus: sdio: convert vdd kernel values to greybus We need to convert vdd kernel values to greybus ones. And we get this by shifting the kernel values by 8. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/sdio.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 8d2de7d96eb4..5e5de04047c1 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -43,6 +43,9 @@ struct gb_sdio_host { #define GB_SDIO_RSP_R1B (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ GB_SDIO_RSP_OPCODE | GB_SDIO_RSP_BUSY) +/* kernel vdd starts at 0x80 and we need to translate to greybus ones 0x01 */ +#define GB_SDIO_VDD_SHIFT 8 + static inline bool single_op(struct mmc_command *cmd) { uint32_t opcode = cmd->opcode; @@ -574,10 +577,14 @@ static void gb_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) u8 timing; u8 signal_voltage; u8 drv_type; + u32 vdd = 0; mutex_lock(&host->lock); request.clock = cpu_to_le32(ios->clock); - request.vdd = cpu_to_le32(1 << ios->vdd); + + if (ios->vdd) + vdd = 1 << (ios->vdd - GB_SDIO_VDD_SHIFT); + request.vdd = cpu_to_le32(vdd); request.bus_mode = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN ? GB_SDIO_BUSMODE_OPENDRAIN : -- cgit v1.2.3-59-g8ed1b From d53cc1c37e5a4006fb23df8e54dc4a50e01732d9 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Tue, 19 Jul 2016 15:41:30 +0100 Subject: greybus: sdio: add switch voltage operation Core sd/mmc needs the start_signal_voltage_switch operation to be defined to issue a voltage switch command. So, we define it here even though we do not need to take in action on it, since all voltage control is done in the Module. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/sdio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 5e5de04047c1..ad448c6388bb 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -734,11 +734,17 @@ static int gb_mmc_get_cd(struct mmc_host *mmc) return host->card_present; } +static int gb_mmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +{ + return 0; +} + static const struct mmc_host_ops gb_sdio_ops = { .request = gb_mmc_request, .set_ios = gb_mmc_set_ios, .get_ro = gb_mmc_get_ro, .get_cd = gb_mmc_get_cd, + .start_signal_voltage_switch = gb_mmc_switch_voltage, }; static int gb_sdio_probe(struct gbphy_device *gbphy_dev, -- cgit v1.2.3-59-g8ed1b From 042fd749abab492ae02452cf675f7bc146814955 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Tue, 19 Jul 2016 17:21:27 +0200 Subject: greybus: connection: drop the svc quiescing operation Connection tear down is being reworked, and the SVC quiescing operation is going away. Let's remove this operation now along with the coupled second ping from our intermediate tear down implementation. This both avoids unnecessary noise in the logs resulting from the fact that the SVC side of the quiescing operation was never merged, and speeds up connection tear down slightly. Testing done: Tested on EVT2 using runtime PM. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 19 ------------------ drivers/staging/greybus/greybus_protocols.h | 12 ----------- drivers/staging/greybus/svc.c | 31 ----------------------------- drivers/staging/greybus/svc.h | 2 -- 4 files changed, 64 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 174a52d5f524..634c8b1e92f3 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -417,21 +417,6 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection) connection->intf_cport_id); } -static void -gb_connection_svc_connection_quiescing(struct gb_connection *connection) -{ - struct gb_host_device *hd = connection->hd; - - if (gb_connection_is_static(connection)) - return; - - gb_svc_connection_quiescing(hd->svc, - hd->svc->ap_intf_id, - connection->hd_cport_id, - connection->intf->interface_id, - connection->intf_cport_id); -} - /* Inform Interface about active CPorts */ static int gb_connection_control_connected(struct gb_connection *connection) { @@ -686,8 +671,6 @@ err_control_disconnecting: gb_connection_ping(connection); gb_connection_hd_cport_features_disable(connection); - gb_connection_svc_connection_quiescing(connection); - gb_connection_ping(connection); gb_connection_control_disconnected(connection); connection->state = GB_CONNECTION_STATE_DISABLED; err_svc_connection_destroy: @@ -795,8 +778,6 @@ void gb_connection_disable(struct gb_connection *connection) gb_connection_ping(connection); gb_connection_hd_cport_features_disable(connection); - gb_connection_svc_connection_quiescing(connection); - gb_connection_ping(connection); gb_connection_control_disconnected(connection); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 1966136d0649..c7fcb85892fd 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1036,7 +1036,6 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_ACQUIRE 0x18 #define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE 0x19 #define GB_SVC_TYPE_TIMESYNC_PING 0x1a -#define GB_SVC_TYPE_CONN_QUIESCING 0x1e #define GB_SVC_TYPE_MODULE_INSERTED 0x1f #define GB_SVC_TYPE_MODULE_REMOVED 0x20 #define GB_SVC_TYPE_INTF_VSYS_ENABLE 0x21 @@ -1386,17 +1385,6 @@ struct gb_svc_intf_mailbox_event_request { } __packed; /* intf_mailbox_event response has no payload */ -struct gb_svc_conn_quiescing_request { - __u8 intf1_id; - __le16 cport1_id; - __u8 intf2_id; - __le16 cport2_id; -} __packed; - -struct gb_svc_conn_quiescing_response { - __u8 status; -} __packed; - /* RAW */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 14bada965ddd..da9bb1f182f7 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -470,37 +470,6 @@ int gb_svc_connection_create(struct gb_svc *svc, } EXPORT_SYMBOL_GPL(gb_svc_connection_create); -void gb_svc_connection_quiescing(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id) -{ - struct gb_svc_conn_quiescing_request request; - struct gb_svc_conn_quiescing_response response; - int ret; - - dev_dbg(&svc->dev, "%s - (%u:%u %u:%u)\n", __func__, - intf1_id, cport1_id, intf2_id, cport2_id); - - request.intf1_id = intf1_id; - request.cport1_id = cpu_to_le16(cport1_id); - request.intf2_id = intf2_id; - request.cport2_id = cpu_to_le16(cport2_id); - - ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_QUIESCING, - &request, sizeof(request), - &response, sizeof(response)); - if (ret < 0) - return; - if (response.status != GB_SVC_OP_SUCCESS) { - dev_err(&svc->dev, "quiescing connection failed (%u:%u %u:%u): %u\n", - intf1_id, cport1_id, intf2_id, cport2_id, - response.status); - return; - } - - return; -} -EXPORT_SYMBOL_GPL(gb_svc_connection_quiescing); - void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) { diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index 4ab066b90a5b..f632790a54f4 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -62,8 +62,6 @@ int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id, u8 cport_flags); -void gb_svc_connection_quiescing(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, - u8 intf2_id, u16 cport2_id); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id); int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id); -- cgit v1.2.3-59-g8ed1b From d29b67d44a7cf6d02e7319c0e5a4b729a0aa00e7 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Tue, 19 Jul 2016 13:31:46 +0530 Subject: greybus: arche-platform: Add support for init-off feature Disable wake_detect interrupt after request As part of SW-4344/SW-7061, now we are enabling FW flashing to all builds. That means check for need of FW upgrade is going to be present in all builds, and moving to FW_FlASHING mode from active is heavy operation; so the idea here is simplify this process and save the boot time due to switching back-n-forth between ACTIVE<=>FW_FLASHING modes. So we decided to put unipro into OFF state by default on boot, which can be changed through DT property. If arche-platform device node has "arche,init-off" property set, then unipro will be in OFF state on boot. User can bring it back by # echo active > /sys/devices/arche_platform*/state And to simply the exit code of probe() fn the arche_platform_coldboot_seq() has been shifted to the bottom of the _probe() fn. Testing Done: Tested on EVT2 platform, with and without "arche,init-off" property, multiple times. Note: I am seeing SW-7128, which is not related to these changes. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Tested-by: Michael Scott <michael.scott@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index af784204973f..cbe8bdcabceb 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -672,8 +672,7 @@ static int arche_platform_probe(struct platform_device *pdev) dev_err(dev, "failed to request wake detect IRQ %d\n", ret); return ret; } - - gpio_direction_input(arche_pdata->wake_detect_gpio); + disable_irq(arche_pdata->wake_detect_irq); ret = device_create_file(dev, &dev_attr_state); if (ret) { @@ -681,38 +680,41 @@ static int arche_platform_probe(struct platform_device *pdev) return ret; } - mutex_lock(&arche_pdata->platform_state_mutex); - ret = arche_platform_coldboot_seq(arche_pdata); - if (ret) { - dev_err(dev, "Failed to cold boot svc %d\n", ret); - goto err_coldboot; - } - ret = of_platform_populate(np, NULL, NULL, dev); if (ret) { dev_err(dev, "failed to populate child nodes %d\n", ret); - goto err_populate; + goto err_device_remove; } arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; ret = register_pm_notifier(&arche_pdata->pm_notifier); - mutex_unlock(&arche_pdata->platform_state_mutex); if (ret) { dev_err(dev, "failed to register pm notifier %d\n", ret); - goto err_populate; + goto err_device_remove; } /* Register callback pointer */ arche_platform_change_state_cb = arche_platform_change_state; + /* Explicitly power off if requested */ + if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) { + mutex_lock(&arche_pdata->platform_state_mutex); + ret = arche_platform_coldboot_seq(arche_pdata); + if (ret) { + dev_err(dev, "Failed to cold boot svc %d\n", ret); + goto err_coldboot; + } + arche_platform_wd_irq_en(arche_pdata); + mutex_unlock(&arche_pdata->platform_state_mutex); + } + dev_info(dev, "Device registered successfully\n"); return 0; -err_populate: - arche_platform_poweroff_seq(arche_pdata); err_coldboot: mutex_unlock(&arche_pdata->platform_state_mutex); +err_device_remove: device_remove_file(&pdev->dev, &dev_attr_state); return ret; } -- cgit v1.2.3-59-g8ed1b From 1bb61840c4db64e27f0077dcfc5389e662022d40 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Tue, 19 Jul 2016 15:24:46 +0200 Subject: greybus: interface: add interface-type attribute Add an interface-type string attribute that represents the detected interface type as either "dummy", "unipro", "greybus", or "unknown". Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 8 +++++++ drivers/staging/greybus/interface.c | 25 ++++++++++++++++++++++ drivers/staging/greybus/interface.h | 1 + 3 files changed, 34 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 8605d705b617..721010e22b25 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -82,6 +82,14 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The ID of a Greybus interface. +What: /sys/bus/greybus/devices/N-M.I/interface_type +Date: June 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + The type of a Greybus interface; "dummy", "unipro", "greybus", + or "unknown". + What: /sys/bus/greybus/devices/N-M.I/power_now Date: March 2016 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index b3bd96e43128..1d50877bb212 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -491,6 +491,27 @@ static ssize_t power_now_show(struct device *dev, } static DEVICE_ATTR_RO(power_now); +static const char *gb_interface_type_string(struct gb_interface *intf) +{ + static const char * const types[] = { + [GB_SVC_INTF_TYPE_UNKNOWN] = "unknown", + [GB_SVC_INTF_TYPE_DUMMY] = "dummy", + [GB_SVC_INTF_TYPE_UNIPRO] = "unipro", + [GB_SVC_INTF_TYPE_GREYBUS] = "greybus", + }; + + return types[intf->type]; +} + +static ssize_t interface_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + + return sprintf(buf, "%s\n", gb_interface_type_string(intf)); +} +static DEVICE_ATTR_RO(interface_type); + static struct attribute *interface_attrs[] = { &dev_attr_ddbl1_manufacturer_id.attr, &dev_attr_ddbl1_product_id.attr, @@ -501,6 +522,7 @@ static struct attribute *interface_attrs[] = { &dev_attr_voltage_now.attr, &dev_attr_current_now.attr, &dev_attr_power_now.attr, + &dev_attr_interface_type.attr, NULL, }; ATTRIBUTE_GROUPS(interface); @@ -721,6 +743,8 @@ static int gb_interface_activate_operation(struct gb_interface *intf) return ret; } + intf->type = type; + switch (type) { case GB_SVC_INTF_TYPE_DUMMY: dev_info(&intf->dev, "dummy interface detected\n"); @@ -734,6 +758,7 @@ static int gb_interface_activate_operation(struct gb_interface *intf) break; default: dev_err(&intf->dev, "unknown interface type: %u\n", type); + intf->type = GB_SVC_INTF_TYPE_UNKNOWN; return -ENODEV; } diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 8796c55b7ffd..05a3909b475f 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -26,6 +26,7 @@ struct gb_interface { u8 interface_id; /* Physical location within the Endo */ u8 device_id; u8 features; /* Feature flags set in the manifest */ + u8 type; u32 ddbl1_manufacturer_id; u32 ddbl1_product_id; -- cgit v1.2.3-59-g8ed1b From 441ac1fa9ecdccf9d54803e2548464ca83ad8514 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Tue, 19 Jul 2016 15:24:47 +0200 Subject: greybus: interface: partition attribute group Partition the current attribute group into four groups for UniPro, Greybus, power and common attributes. This is a step in refining the interface-type handling as attributes are type dependent. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 41 ++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 1d50877bb212..1d50bd4bbe3a 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -512,20 +512,55 @@ static ssize_t interface_type_show(struct device *dev, } static DEVICE_ATTR_RO(interface_type); -static struct attribute *interface_attrs[] = { +static struct attribute *interface_unipro_attrs[] = { &dev_attr_ddbl1_manufacturer_id.attr, &dev_attr_ddbl1_product_id.attr, + NULL +}; + +static struct attribute *interface_greybus_attrs[] = { &dev_attr_interface_id.attr, &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, &dev_attr_serial_number.attr, + NULL +}; + +static struct attribute *interface_power_attrs[] = { &dev_attr_voltage_now.attr, &dev_attr_current_now.attr, &dev_attr_power_now.attr, + NULL +}; + +static struct attribute *interface_common_attrs[] = { &dev_attr_interface_type.attr, - NULL, + NULL +}; + +static const struct attribute_group interface_unipro_group = { + .attrs = interface_unipro_attrs, +}; + +static const struct attribute_group interface_greybus_group = { + .attrs = interface_greybus_attrs, +}; + +static const struct attribute_group interface_power_group = { + .attrs = interface_power_attrs, +}; + +static const struct attribute_group interface_common_group = { + .attrs = interface_common_attrs, +}; + +static const struct attribute_group *interface_groups[] = { + &interface_unipro_group, + &interface_greybus_group, + &interface_power_group, + &interface_common_group, + NULL }; -ATTRIBUTE_GROUPS(interface); static void gb_interface_release(struct device *dev) { -- cgit v1.2.3-59-g8ed1b From 835642526e9310d435cc718cdb69f2fcd7a53b84 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Tue, 19 Jul 2016 15:24:48 +0200 Subject: greybus: interface: make attributes type dependent Make most interface attributes type dependent (e.g only UniPro and Greybus interfaces should have a DDBL1 Manufacturer ID attribute). Note that the power attributes (e.g. current_now) will only be visible for UniPro- and Greybus-type interfaces (i.e. interfaces that can draw power). Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 1d50bd4bbe3a..c27b18b3b50d 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -538,15 +538,62 @@ static struct attribute *interface_common_attrs[] = { NULL }; +static umode_t interface_unipro_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct gb_interface *intf = to_gb_interface(dev); + + switch (intf->type) { + case GB_SVC_INTF_TYPE_UNIPRO: + case GB_SVC_INTF_TYPE_GREYBUS: + return attr->mode; + default: + return 0; + } +} + +static umode_t interface_greybus_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct gb_interface *intf = to_gb_interface(dev); + + switch (intf->type) { + case GB_SVC_INTF_TYPE_GREYBUS: + return attr->mode; + default: + return 0; + } +} + +static umode_t interface_power_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct gb_interface *intf = to_gb_interface(dev); + + switch (intf->type) { + case GB_SVC_INTF_TYPE_UNIPRO: + case GB_SVC_INTF_TYPE_GREYBUS: + return attr->mode; + default: + return 0; + } +} + static const struct attribute_group interface_unipro_group = { + .is_visible = interface_unipro_is_visible, .attrs = interface_unipro_attrs, }; static const struct attribute_group interface_greybus_group = { + .is_visible = interface_greybus_is_visible, .attrs = interface_greybus_attrs, }; static const struct attribute_group interface_power_group = { + .is_visible = interface_power_is_visible, .attrs = interface_power_attrs, }; -- cgit v1.2.3-59-g8ed1b From c80a982fc1f8b2b1546c4b2acc08ffd543f6f6c5 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Tue, 19 Jul 2016 15:24:49 +0200 Subject: greybus: interface: amend interface registration message Amend the interface registration message with the detected interface type, and only print the Ara VID/PID and DDBL1 attributes for the types for which they exist. Also drop the now redundant message about a detected dummy interface from the activate operation helper. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c27b18b3b50d..22b734202442 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -829,7 +829,6 @@ static int gb_interface_activate_operation(struct gb_interface *intf) switch (type) { case GB_SVC_INTF_TYPE_DUMMY: - dev_info(&intf->dev, "dummy interface detected\n"); /* FIXME: handle as an error for now */ return -ENODEV; case GB_SVC_INTF_TYPE_UNIPRO: @@ -1131,10 +1130,20 @@ int gb_interface_add(struct gb_interface *intf) trace_gb_interface_add(intf); - dev_info(&intf->dev, "Interface added: VID=0x%08x, PID=0x%08x\n", - intf->vendor_id, intf->product_id); - dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n", - intf->ddbl1_manufacturer_id, intf->ddbl1_product_id); + dev_info(&intf->dev, "Interface added (%s)\n", + gb_interface_type_string(intf)); + + switch (intf->type) { + case GB_SVC_INTF_TYPE_GREYBUS: + dev_info(&intf->dev, "Ara VID=0x%08x, PID=0x%08x\n", + intf->vendor_id, intf->product_id); + /* fall-through */ + case GB_SVC_INTF_TYPE_UNIPRO: + dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n", + intf->ddbl1_manufacturer_id, + intf->ddbl1_product_id); + break; + } return 0; } -- cgit v1.2.3-59-g8ed1b From 6633d80afbeecd91d5d786d6fbb32cdb8bc0a567 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Tue, 19 Jul 2016 15:24:50 +0200 Subject: greybus: module: suppress activation error message for dummy interfaces We currently handle dummy interfaces by deactivating them using activation error paths, but we don't want the corresponding module_inserted error message to be printed. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/module.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 7f0ed9fc2536..242be493cb53 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -153,8 +153,11 @@ static void gb_module_register_interface(struct gb_interface *intf) break; } if (ret) { - dev_err(&module->dev, "failed to activate interface %u: %d\n", - intf_id, ret); + if (intf->type != GB_SVC_INTF_TYPE_DUMMY) { + dev_err(&module->dev, + "failed to activate interface %u: %d\n", + intf_id, ret); + } /* * -EAGAIN indicates that the Greybus operation -- cgit v1.2.3-59-g8ed1b From a212b7586712208804d3d51efa9fdf6e23b2480a Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 20 Jul 2016 16:40:20 +0200 Subject: greybus: interface: use an enum for interface type Use an enum for the interface type instead of using the SVC protocol values directly. Suggested-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Patrick Titiano <ptitiano@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 32 ++++++++++++++++++-------------- drivers/staging/greybus/interface.h | 11 ++++++++++- drivers/staging/greybus/module.c | 2 +- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 22b734202442..e21491b81a7d 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -494,10 +494,11 @@ static DEVICE_ATTR_RO(power_now); static const char *gb_interface_type_string(struct gb_interface *intf) { static const char * const types[] = { - [GB_SVC_INTF_TYPE_UNKNOWN] = "unknown", - [GB_SVC_INTF_TYPE_DUMMY] = "dummy", - [GB_SVC_INTF_TYPE_UNIPRO] = "unipro", - [GB_SVC_INTF_TYPE_GREYBUS] = "greybus", + [GB_INTERFACE_TYPE_INVALID] = "invalid", + [GB_INTERFACE_TYPE_UNKNOWN] = "unknown", + [GB_INTERFACE_TYPE_DUMMY] = "dummy", + [GB_INTERFACE_TYPE_UNIPRO] = "unipro", + [GB_INTERFACE_TYPE_GREYBUS] = "greybus", }; return types[intf->type]; @@ -545,8 +546,8 @@ static umode_t interface_unipro_is_visible(struct kobject *kobj, struct gb_interface *intf = to_gb_interface(dev); switch (intf->type) { - case GB_SVC_INTF_TYPE_UNIPRO: - case GB_SVC_INTF_TYPE_GREYBUS: + case GB_INTERFACE_TYPE_UNIPRO: + case GB_INTERFACE_TYPE_GREYBUS: return attr->mode; default: return 0; @@ -560,7 +561,7 @@ static umode_t interface_greybus_is_visible(struct kobject *kobj, struct gb_interface *intf = to_gb_interface(dev); switch (intf->type) { - case GB_SVC_INTF_TYPE_GREYBUS: + case GB_INTERFACE_TYPE_GREYBUS: return attr->mode; default: return 0; @@ -574,8 +575,8 @@ static umode_t interface_power_is_visible(struct kobject *kobj, struct gb_interface *intf = to_gb_interface(dev); switch (intf->type) { - case GB_SVC_INTF_TYPE_UNIPRO: - case GB_SVC_INTF_TYPE_GREYBUS: + case GB_INTERFACE_TYPE_UNIPRO: + case GB_INTERFACE_TYPE_GREYBUS: return attr->mode; default: return 0; @@ -825,21 +826,22 @@ static int gb_interface_activate_operation(struct gb_interface *intf) return ret; } - intf->type = type; - switch (type) { case GB_SVC_INTF_TYPE_DUMMY: + intf->type = GB_INTERFACE_TYPE_DUMMY; /* FIXME: handle as an error for now */ return -ENODEV; case GB_SVC_INTF_TYPE_UNIPRO: + intf->type = GB_INTERFACE_TYPE_UNIPRO; dev_err(&intf->dev, "interface type UniPro not supported\n"); /* FIXME: check if this is a Toshiba bridge before retrying? */ return -EAGAIN; case GB_SVC_INTF_TYPE_GREYBUS: + intf->type = GB_INTERFACE_TYPE_GREYBUS; break; default: dev_err(&intf->dev, "unknown interface type: %u\n", type); - intf->type = GB_SVC_INTF_TYPE_UNKNOWN; + intf->type = GB_INTERFACE_TYPE_UNKNOWN; return -ENODEV; } @@ -1134,15 +1136,17 @@ int gb_interface_add(struct gb_interface *intf) gb_interface_type_string(intf)); switch (intf->type) { - case GB_SVC_INTF_TYPE_GREYBUS: + case GB_INTERFACE_TYPE_GREYBUS: dev_info(&intf->dev, "Ara VID=0x%08x, PID=0x%08x\n", intf->vendor_id, intf->product_id); /* fall-through */ - case GB_SVC_INTF_TYPE_UNIPRO: + case GB_INTERFACE_TYPE_UNIPRO: dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n", intf->ddbl1_manufacturer_id, intf->ddbl1_product_id); break; + default: + break; } return 0; diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 05a3909b475f..bba1881aee56 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -10,6 +10,14 @@ #ifndef __INTERFACE_H #define __INTERFACE_H +enum gb_interface_type { + GB_INTERFACE_TYPE_INVALID = 0, + GB_INTERFACE_TYPE_UNKNOWN, + GB_INTERFACE_TYPE_DUMMY, + GB_INTERFACE_TYPE_UNIPRO, + GB_INTERFACE_TYPE_GREYBUS, +}; + #define GB_INTERFACE_QUIRK_NO_CPORT_FEATURES BIT(0) #define GB_INTERFACE_QUIRK_NO_INIT_STATUS BIT(1) #define GB_INTERFACE_QUIRK_NO_ARA_IDS BIT(2) @@ -26,7 +34,8 @@ struct gb_interface { u8 interface_id; /* Physical location within the Endo */ u8 device_id; u8 features; /* Feature flags set in the manifest */ - u8 type; + + enum gb_interface_type type; u32 ddbl1_manufacturer_id; u32 ddbl1_product_id; diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 242be493cb53..3ae58768cc87 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -153,7 +153,7 @@ static void gb_module_register_interface(struct gb_interface *intf) break; } if (ret) { - if (intf->type != GB_SVC_INTF_TYPE_DUMMY) { + if (intf->type != GB_INTERFACE_TYPE_DUMMY) { dev_err(&module->dev, "failed to activate interface %u: %d\n", intf_id, ret); -- cgit v1.2.3-59-g8ed1b From 27b9e257dd57fba0d63d996bf050fcbd94d998d7 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 20 Jul 2016 16:40:21 +0200 Subject: greybus: interface: do not read DME during reactivation It will soon be possible to reactivate an interface that has been powered down after already having been activated. In that case there's no need to re-read the DME attributes as part of activation as the values are already cached. Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Patrick Titiano <ptitiano@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 12 +++++++++++- drivers/staging/greybus/interface.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index e21491b81a7d..9fe6764a6ac2 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -95,6 +95,10 @@ static int gb_interface_read_dme(struct gb_interface *intf) { int ret; + /* DME attributes have already been read */ + if (intf->dme_read) + return 0; + ret = gb_interface_dme_attr_get(intf, DME_DDBL1_MANUFACTURERID, &intf->ddbl1_manufacturer_id); if (ret) @@ -111,7 +115,13 @@ static int gb_interface_read_dme(struct gb_interface *intf) intf->quirks |= GB_INTERFACE_QUIRK_NO_INIT_STATUS; } - return gb_interface_read_ara_dme(intf); + ret = gb_interface_read_ara_dme(intf); + if (ret) + return ret; + + intf->dme_read = true; + + return 0; } static int gb_interface_route_create(struct gb_interface *intf) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index bba1881aee56..f52dfd09bb16 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -56,6 +56,7 @@ struct gb_interface { bool active; bool enabled; bool mode_switch; + bool dme_read; struct work_struct mode_switch_work; struct completion mode_switch_completion; -- cgit v1.2.3-59-g8ed1b From 3e93cb6abbc023aebf311618481263e000bf26fb Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 20 Jul 2016 16:40:22 +0200 Subject: greybus: interface: clean up ES3 activation-retry hack Clean up the ES3 activation retry-hack and isolate it in the interface code. This way the retry hack can be reused when we soon start allowing interfaces to be reactivated after having been powered down. Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Patrick Titiano <ptitiano@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 40 +++++++++++++++++++++++++++++-------- drivers/staging/greybus/module.c | 20 +------------------ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 9fe6764a6ac2..8e1b6c017c81 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -844,8 +844,8 @@ static int gb_interface_activate_operation(struct gb_interface *intf) case GB_SVC_INTF_TYPE_UNIPRO: intf->type = GB_INTERFACE_TYPE_UNIPRO; dev_err(&intf->dev, "interface type UniPro not supported\n"); - /* FIXME: check if this is a Toshiba bridge before retrying? */ - return -EAGAIN; + /* FIXME: handle as an error for now */ + return -ENODEV; case GB_SVC_INTF_TYPE_GREYBUS: intf->type = GB_INTERFACE_TYPE_GREYBUS; break; @@ -865,12 +865,7 @@ static int gb_interface_hibernate_link(struct gb_interface *intf) return gb_svc_intf_set_power_mode_hibernate(svc, intf->interface_id); } -/* - * Activate an interface. - * - * Locking: Caller holds the interface mutex. - */ -int gb_interface_activate(struct gb_interface *intf) +static int _gb_interface_activate(struct gb_interface *intf) { int ret; @@ -919,6 +914,35 @@ err_vsys_disable: return ret; } +/* + * Activate an interface. + * + * Locking: Caller holds the interface mutex. + */ +int gb_interface_activate(struct gb_interface *intf) +{ + int retries = 3; + int ret; + + /* + * At present, we assume a UniPro-only module + * to be a Greybus module that failed to send its mailbox + * poke. There is some reason to believe that this is + * because of a bug in the ES3 bootrom. + * + * FIXME: Check if this is a Toshiba bridge before retrying? + */ + while (retries--) { + ret = _gb_interface_activate(intf); + if (ret == -ENODEV && intf->type == GB_SVC_INTF_TYPE_UNIPRO) + continue; + + break; + } + + return ret; +} + /* * Deactivate an interface. * diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index 3ae58768cc87..d506fa0b3272 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -143,15 +143,10 @@ static void gb_module_register_interface(struct gb_interface *intf) struct gb_module *module = intf->module; u8 intf_id = intf->interface_id; int ret; - int retries = 3; mutex_lock(&intf->mutex); - while (retries--) { - ret = gb_interface_activate(intf); - if (ret != -EAGAIN) - break; - } + ret = gb_interface_activate(intf); if (ret) { if (intf->type != GB_INTERFACE_TYPE_DUMMY) { dev_err(&module->dev, @@ -159,19 +154,6 @@ static void gb_module_register_interface(struct gb_interface *intf) intf_id, ret); } - /* - * -EAGAIN indicates that the Greybus operation - * interface_activate determined the remote interface to be - * UniPro-only. At present, we assume a UniPro-only module - * to be a Greybus module that failed to send its mailbox - * poke. There is some reason to believe that this is - * because of a bug in the ES3 bootrom. If we exhause our - * retries trying to activate such an interface, convert - * the error code back into a "no device" error. - */ - if (ret == -EAGAIN) - ret = -ENODEV; - gb_interface_add(intf); goto err_unlock; } -- cgit v1.2.3-59-g8ed1b From 62491622db9c9b6a51630f4c8c653f59c2c834f9 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 20 Jul 2016 16:40:23 +0200 Subject: greybus: interface: make sure type is invariant during reactivation An interface is not expected to change its type after a power down and reactivation so make sure to treat that as a fatal error. This is complicated by the current Toshiba ES3 hack which requires us to retry activation as Greybus interfaces are sometimes misdetected as UniPro interfaces. Handle that by only retrying activation the first time an interface is activated, and for interfaces already detected as having Greybus type. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Patrick Titiano <ptitiano@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 75 +++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 8e1b6c017c81..01cefced7b69 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -822,7 +822,8 @@ static int gb_interface_unipro_set(struct gb_interface *intf, bool enable) return 0; } -static int gb_interface_activate_operation(struct gb_interface *intf) +static int gb_interface_activate_operation(struct gb_interface *intf, + enum gb_interface_type *intf_type) { struct gb_svc *svc = intf->hd->svc; u8 type; @@ -838,20 +839,20 @@ static int gb_interface_activate_operation(struct gb_interface *intf) switch (type) { case GB_SVC_INTF_TYPE_DUMMY: - intf->type = GB_INTERFACE_TYPE_DUMMY; + *intf_type = GB_INTERFACE_TYPE_DUMMY; /* FIXME: handle as an error for now */ return -ENODEV; case GB_SVC_INTF_TYPE_UNIPRO: - intf->type = GB_INTERFACE_TYPE_UNIPRO; + *intf_type = GB_INTERFACE_TYPE_UNIPRO; dev_err(&intf->dev, "interface type UniPro not supported\n"); /* FIXME: handle as an error for now */ return -ENODEV; case GB_SVC_INTF_TYPE_GREYBUS: - intf->type = GB_INTERFACE_TYPE_GREYBUS; + *intf_type = GB_INTERFACE_TYPE_GREYBUS; break; default: dev_err(&intf->dev, "unknown interface type: %u\n", type); - intf->type = GB_INTERFACE_TYPE_UNKNOWN; + *intf_type = GB_INTERFACE_TYPE_UNKNOWN; return -ENODEV; } @@ -865,10 +866,13 @@ static int gb_interface_hibernate_link(struct gb_interface *intf) return gb_svc_intf_set_power_mode_hibernate(svc, intf->interface_id); } -static int _gb_interface_activate(struct gb_interface *intf) +static int _gb_interface_activate(struct gb_interface *intf, + enum gb_interface_type *type) { int ret; + *type = GB_INTERFACE_TYPE_UNKNOWN; + if (intf->ejected) return -ENODEV; @@ -884,7 +888,7 @@ static int _gb_interface_activate(struct gb_interface *intf) if (ret) goto err_refclk_disable; - ret = gb_interface_activate_operation(intf); + ret = gb_interface_activate_operation(intf, type); if (ret) goto err_unipro_disable; @@ -915,26 +919,21 @@ err_vsys_disable: } /* - * Activate an interface. + * At present, we assume a UniPro-only module to be a Greybus module that + * failed to send its mailbox poke. There is some reason to believe that this + * is because of a bug in the ES3 bootrom. * - * Locking: Caller holds the interface mutex. + * FIXME: Check if this is a Toshiba bridge before retrying? */ -int gb_interface_activate(struct gb_interface *intf) +static int _gb_interface_activate_es3_hack(struct gb_interface *intf, + enum gb_interface_type *type) { int retries = 3; int ret; - /* - * At present, we assume a UniPro-only module - * to be a Greybus module that failed to send its mailbox - * poke. There is some reason to believe that this is - * because of a bug in the ES3 bootrom. - * - * FIXME: Check if this is a Toshiba bridge before retrying? - */ while (retries--) { - ret = _gb_interface_activate(intf); - if (ret == -ENODEV && intf->type == GB_SVC_INTF_TYPE_UNIPRO) + ret = _gb_interface_activate(intf, type); + if (ret == -ENODEV && *type == GB_INTERFACE_TYPE_UNIPRO) continue; break; @@ -943,6 +942,42 @@ int gb_interface_activate(struct gb_interface *intf) return ret; } +/* + * Activate an interface. + * + * Locking: Caller holds the interface mutex. + */ +int gb_interface_activate(struct gb_interface *intf) +{ + enum gb_interface_type type; + int ret; + + switch (intf->type) { + case GB_INTERFACE_TYPE_INVALID: + case GB_INTERFACE_TYPE_GREYBUS: + ret = _gb_interface_activate_es3_hack(intf, &type); + break; + default: + ret = _gb_interface_activate(intf, &type); + } + + /* Make sure type is detected correctly during reactivation. */ + if (intf->type != GB_INTERFACE_TYPE_INVALID) { + if (type != intf->type) { + dev_err(&intf->dev, "failed to detect interface type\n"); + + if (!ret) + gb_interface_deactivate(intf); + + return -EIO; + } + } else { + intf->type = type; + } + + return ret; +} + /* * Deactivate an interface. * -- cgit v1.2.3-59-g8ed1b From 12169bc9143ed24ad7a5ea12a7c28d1dba891131 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 20 Jul 2016 16:40:24 +0200 Subject: greybus: interface: prevent reactivation during removal Make sure to prevent an interface that is going away from being reactivated. This is needed to preemptively close a race between the upcoming feature to reactivate a powered-down interface and physical removal (i.e. module_removed event processing) as well as logical removal (e.g. the current system-suspend hack). Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Patrick Titiano <ptitiano@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/interface.h | 1 + drivers/staging/greybus/module.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 01cefced7b69..3ad1c757d7e7 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -873,7 +873,7 @@ static int _gb_interface_activate(struct gb_interface *intf, *type = GB_INTERFACE_TYPE_UNKNOWN; - if (intf->ejected) + if (intf->ejected || intf->removed) return -ENODEV; ret = gb_interface_vsys_set(intf, true); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index f52dfd09bb16..daa9759149a5 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -53,6 +53,7 @@ struct gb_interface { bool disconnected; bool ejected; + bool removed; bool active; bool enabled; bool mode_switch; diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c index d506fa0b3272..69f67ddbd4a3 100644 --- a/drivers/staging/greybus/module.c +++ b/drivers/staging/greybus/module.c @@ -186,6 +186,7 @@ static void gb_module_deregister_interface(struct gb_interface *intf) intf->disconnected = true; mutex_lock(&intf->mutex); + intf->removed = true; gb_interface_disable(intf); gb_interface_deactivate(intf); mutex_unlock(&intf->mutex); -- cgit v1.2.3-59-g8ed1b From 93e29c8530881766837e62088d54ffb733f8bc2a Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 20 Jul 2016 16:40:25 +0200 Subject: greybus: interface: add power_state attribute User space needs the capability of powering ON or OFF an Interface for multiple use cases. For example, userspace may want an Interface currently in its S3 boot stage, to boot into its S2 Loader stage to update the bridge SPI flash. Or the Interface is running its S2 Loader stage and updated the SPI flash with the new S2 Loader firmware and wants to boot into the new S2 Loader firmware. Another use case can be, Android wants to disable (not eject) a misbehaving module. Add a 'power_state' sysfs file within the interface directory. It can be read to know the current power state of the Interface and can be written to power ON or power OFF an Interface. Possible values that can be written or read from it are: "on" and "off". Testing Done: Tested by enabling/disabling camera module on EVT 2.0. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> CC: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> [johan: drop es3-quirk duplication, add to power attribute group, fix return value, drop tags ] Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Patrick Titiano <ptitiano@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 17 +++++++ drivers/staging/greybus/interface.c | 58 ++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 721010e22b25..62d03afe9c85 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -97,6 +97,23 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: Power measurement of the interface in microwatts (uW) +What: /sys/bus/greybus/devices/N-M.I/power_state +Date: March 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + This reflects the power state of a Greybus Interface. If the + value read from it is "on", then power is currently supplied to + the Interface. Otherwise this will read "off" and the power is + currently not supplied to the Interface. + + If the value read is "off", then writing "on" (or '1', 'y', + 'Y') to it will enable power to the Interface. If the value + read is "on", then writing "off" (or '0', 'n', 'N') to it will + disable power to the Interface. + + Writing the currently read value to it has no effect. + What: /sys/bus/greybus/devices/N-M.I/product_id Date: October 2015 KernelVersion: 4.XX diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 3ad1c757d7e7..f5ed79c687d2 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -501,6 +501,63 @@ static ssize_t power_now_show(struct device *dev, } static DEVICE_ATTR_RO(power_now); +static ssize_t power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_interface *intf = to_gb_interface(dev); + + if (intf->active) + return scnprintf(buf, PAGE_SIZE, "on\n"); + else + return scnprintf(buf, PAGE_SIZE, "off\n"); +} + +static ssize_t power_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct gb_interface *intf = to_gb_interface(dev); + bool activate; + int ret = 0; + + if (kstrtobool(buf, &activate)) + return -EINVAL; + + mutex_lock(&intf->mutex); + + if (activate == intf->active) + goto unlock; + + if (activate) { + ret = gb_interface_activate(intf); + if (ret) { + dev_err(&intf->dev, + "failed to activate interface: %d\n", ret); + goto unlock; + } + + ret = gb_interface_enable(intf); + if (ret) { + dev_err(&intf->dev, + "failed to enable interface: %d\n", ret); + gb_interface_deactivate(intf); + goto unlock; + } + } else { + gb_interface_disable(intf); + gb_interface_deactivate(intf); + } + +unlock: + mutex_unlock(&intf->mutex); + + if (ret) + return ret; + + return len; +} +static DEVICE_ATTR_RW(power_state); + static const char *gb_interface_type_string(struct gb_interface *intf) { static const char * const types[] = { @@ -541,6 +598,7 @@ static struct attribute *interface_power_attrs[] = { &dev_attr_voltage_now.attr, &dev_attr_current_now.attr, &dev_attr_power_now.attr, + &dev_attr_power_state.attr, NULL }; -- cgit v1.2.3-59-g8ed1b From e21e6bad3f425e8bc74186f3ab1e21cd40f7a208 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 20 Jul 2016 16:40:26 +0200 Subject: greybus: interface: fix power_state documentation Reword the power_state attribute documentation to make it clear that an attempt to boot and possibly enumerate an interface is made when writing "on" to the file, and that on errors the interface will again be powered down. Drop the incorrect claim that writing the currently read value has no effect, since no such guarantees can be made (e.g. several writers may be sleeping on the interface mutex). Also fix some minor language issues. Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Patrick Titiano <ptitiano@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index 62d03afe9c85..e202eac1f001 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -102,17 +102,18 @@ Date: March 2016 KernelVersion: 4.XX Contact: Greg Kroah-Hartman <greg@kroah.com> Description: - This reflects the power state of a Greybus Interface. If the - value read from it is "on", then power is currently supplied to - the Interface. Otherwise this will read "off" and the power is - currently not supplied to the Interface. + This file reflects the power state of a Greybus interface. If + the value read from it is "on", then power is currently + supplied to the interface. Otherwise it will read "off" and + power is currently not supplied to the interface. If the value read is "off", then writing "on" (or '1', 'y', - 'Y') to it will enable power to the Interface. If the value - read is "on", then writing "off" (or '0', 'n', 'N') to it will - disable power to the Interface. + 'Y') to this file will enable power to the interface and an + attempt to boot and possibly enumerate it will be made. Note + that on errors, the interface will again be powered down. - Writing the currently read value to it has no effect. + If the value read is "on", then writing "off" (or '0', 'n', + 'N') to this file will power down the interface. What: /sys/bus/greybus/devices/N-M.I/product_id Date: October 2015 -- cgit v1.2.3-59-g8ed1b From 4f7e413b2d2d2825dbb36b73f06e69514022b132 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 20 Jul 2016 16:40:27 +0200 Subject: greybus: interface: hibernate UniPro link in activation error path To be well-behaved, we should hibernate the link before disabling UniPro in case the link has already been established (i.e. when the interface type has been detected as UniPro or Greybus). Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Patrick Titiano <ptitiano@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index f5ed79c687d2..919eaa0a46c7 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -947,8 +947,15 @@ static int _gb_interface_activate(struct gb_interface *intf, goto err_refclk_disable; ret = gb_interface_activate_operation(intf, type); - if (ret) - goto err_unipro_disable; + if (ret) { + switch (*type) { + case GB_INTERFACE_TYPE_UNIPRO: + case GB_INTERFACE_TYPE_GREYBUS: + goto err_hibernate_link; + default: + goto err_unipro_disable; + } + } ret = gb_interface_read_dme(intf); if (ret) -- cgit v1.2.3-59-g8ed1b From 8a704565ebda9603c294aacebb34e47cd9d9a3a0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Wed, 20 Jul 2016 10:11:37 -0700 Subject: greybus: move all firmware prefix strings to one place The firmware prefix is hard-coded in a few different places. Put it all in one handy #define, for when/if we ever decide to change it in the future... Testing: 'strings gb-firmware.ko gb-bootrom.ko | grep ara_' produced the same output before and after this patch. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 3 ++- drivers/staging/greybus/firmware.h | 5 +++++ drivers/staging/greybus/fw-download.c | 4 +--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index 688184181e0c..309e0a7c865a 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -13,6 +13,7 @@ #include <linux/workqueue.h> #include "greybus.h" +#include "firmware.h" /* Timeout, in jiffies, within which the next request must be received */ #define NEXT_REQ_TIMEOUT_MS 1000 @@ -157,7 +158,7 @@ static int download_firmware(struct gb_bootrom *bootrom, u8 stage) * XXX Name it properly.. */ snprintf(firmware_name, sizeof(firmware_name), - "ara_%08x_%08x_%08x_%08x_s2l.tftf", + FW_NAME_PREFIX "%08x_%08x_%08x_%08x_s2l.tftf", intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, intf->vendor_id, intf->product_id); diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h index fb955379cfe4..2fbb263895d2 100644 --- a/drivers/staging/greybus/firmware.h +++ b/drivers/staging/greybus/firmware.h @@ -12,6 +12,11 @@ #include "greybus.h" +#define FW_NAME_PREFIX "ara_" + +/* Length of the string in format: "FW_NAME_PREFIX""%08x_%08x_%08x_%08x_%s.tftf" */ +#define FW_NAME_LEN 56 + /* Firmware Management Protocol specific functions */ int fw_mgmt_init(void); void fw_mgmt_exit(void); diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 42cbbf4ac077..41a45ecb62b6 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -14,8 +14,6 @@ #include "firmware.h" #include "greybus.h" -/* Length of the string in format: ara_%08x_%08x_%08x_%08x_%s.tftf */ -#define FW_NAME_LEN 56 /* Estimated minimum buffer size, actual size can be smaller than this */ #define MIN_FETCH_SIZE 512 /* Timeout, in jiffies, within which fetch or release firmware must be called */ @@ -182,7 +180,7 @@ static struct fw_request *find_firmware(struct fw_download *fw_download, fw_req->firmware_id = ret; snprintf(fw_req->name, sizeof(fw_req->name), - "ara_%08x_%08x_%08x_%08x_%s.tftf", + FW_NAME_PREFIX "%08x_%08x_%08x_%08x_%s.tftf", intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, intf->vendor_id, intf->product_id, tag); -- cgit v1.2.3-59-g8ed1b From 93a738894346d01687c8979bca129d7eaaf7d35f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 19 Jul 2016 15:04:37 +0100 Subject: greybus: timesync: reduce initial startup time Currently we have a long initial delay when initiating the first operation to synchronize the FrameTime. That made sense during development of the feature since we did synchronization asynchronously and wanted to give some grace time for new Interfaces to be added before initiating a new synchronization. On the PM runtime resume path though we do a synchronous restoration of FrameTime and in this case waiting for 1 second before initiating the first operation is clearly wrong. This patch reduces the initial operation time bringing the synchronous call time down by 990 milliseconds. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index ddbdf2beb48d..23b209118f2a 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -28,7 +28,7 @@ #define GB_TIMESYNC_DEFAULT_OFFSET_US 1000 /* Work queue timers long, short and SVC strobe timeout */ -#define GB_TIMESYNC_DELAYED_WORK_LONG msecs_to_jiffies(1000) +#define GB_TIMESYNC_DELAYED_WORK_LONG msecs_to_jiffies(10) #define GB_TIMESYNC_DELAYED_WORK_SHORT msecs_to_jiffies(1) #define GB_TIMESYNC_MAX_WAIT_SVC msecs_to_jiffies(5000) #define GB_TIMESYNC_KTIME_UPDATE msecs_to_jiffies(1000) -- cgit v1.2.3-59-g8ed1b From 2861e2079081a156b0b731e1f6acb2aaf5605c5e Mon Sep 17 00:00:00 2001 From: Ryan Lim <limryan@google.com> Date: Wed, 20 Jul 2016 08:14:02 -0700 Subject: greybus: loopback_test: Cancel only the tests running on selected devices When starting a loopback test, it cancels all currently running tests on all loopback devices. When -m argument is given, which runs the test on specific loopback devices, only the tests running on the selected devices need to be cancelled before starting new tests. Signed-off-by: Ryan Lim <limryan@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/tools/loopback_test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index 25035f666372..ec448920b8e9 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -780,7 +780,8 @@ static void prepare_devices(struct loopback_test *t) /* Cancel any running tests */ for (i = 0; i < t->device_count; i++) - write_sysfs_val(t->devices[i].sysfs_entry, "type", 0); + if (device_enabled(t, i)) + write_sysfs_val(t->devices[i].sysfs_entry, "type", 0); for (i = 0; i < t->device_count; i++) { -- cgit v1.2.3-59-g8ed1b From bf32654a24441230fac32479b6a5ba8bbfc3b731 Mon Sep 17 00:00:00 2001 From: Ryan Lim <limryan@google.com> Date: Wed, 20 Jul 2016 08:14:03 -0700 Subject: greybus: loopback_test: Fix -z argument bug The -z argument was missing the break statement needed in the switch block and was not in the getopt list. Signed-off-by: Ryan Lim <limryan@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/tools/loopback_test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index ec448920b8e9..412e6dca0d08 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -901,7 +901,7 @@ int main(int argc, char *argv[]) memset(&t, 0, sizeof(t)); while ((o = getopt(argc, argv, - "t:s:i:S:D:m:v::d::r::p::a::l::x::o:c:w:O:")) != -1) { + "t:s:i:S:D:m:v::d::r::p::a::l::x::o:O:c:w:z::")) != -1) { switch (o) { case 't': snprintf(t.test_name, MAX_STR_LEN, "%s", optarg); @@ -956,6 +956,7 @@ int main(int argc, char *argv[]) break; case 'z': t.file_output = 1; + break; default: usage(); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 346bae656e4e1a471771590f1f4e16e9f9332d1d Mon Sep 17 00:00:00 2001 From: Ryan Lim <limryan@google.com> Date: Wed, 20 Jul 2016 08:14:04 -0700 Subject: greybus: loopback_test: Add -f argument to cancel all tests To make sure tests on all devices, enabled or not, are cancelled, specify -f when starting new loopback test. Signed-off-by: Ryan Lim <limryan@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/tools/loopback_test.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c index 412e6dca0d08..f7f4cd6fb55b 100644 --- a/drivers/staging/greybus/tools/loopback_test.c +++ b/drivers/staging/greybus/tools/loopback_test.c @@ -91,6 +91,7 @@ struct loopback_test { int async_outstanding_operations; int us_wait; int file_output; + int stop_all; int poll_count; char test_name[MAX_STR_LEN]; char sysfs_prefix[MAX_SYSFS_PATH]; @@ -207,6 +208,7 @@ void usage(void) " -c Max number of outstanding operations for async operations\n" " -w Wait in uSec between operations\n" " -z Enable output to a CSV file (incompatible with -p)\n" + " -f When starting new loopback test, stop currently running tests on all devices\n" "Examples:\n" " Send 10000 transfers with a packet size of 128 bytes to all active connections\n" " loopback_test -t transfer -s 128 -i 10000 -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n" @@ -778,9 +780,11 @@ static void prepare_devices(struct loopback_test *t) { int i; - /* Cancel any running tests */ + /* Cancel any running tests on enabled devices. If + * stop_all option is given, stop test on all devices. + */ for (i = 0; i < t->device_count; i++) - if (device_enabled(t, i)) + if (t->stop_all || device_enabled(t, i)) write_sysfs_val(t->devices[i].sysfs_entry, "type", 0); @@ -901,7 +905,7 @@ int main(int argc, char *argv[]) memset(&t, 0, sizeof(t)); while ((o = getopt(argc, argv, - "t:s:i:S:D:m:v::d::r::p::a::l::x::o:O:c:w:z::")) != -1) { + "t:s:i:S:D:m:v::d::r::p::a::l::x::o:O:c:w:z::f::")) != -1) { switch (o) { case 't': snprintf(t.test_name, MAX_STR_LEN, "%s", optarg); @@ -957,6 +961,9 @@ int main(int argc, char *argv[]) case 'z': t.file_output = 1; break; + case 'f': + t.stop_all = 1; + break; default: usage(); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 10a24b7c0911d63c7342dfb860e939221b4a9465 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Wed, 20 Jul 2016 19:31:18 +0100 Subject: greybus: timesync: probe shouldn't complete until FrameTime sync does Currently the probe() function contains the asynchronous() variant of FrameTime synchronization. This patch converts to the synchronous() version of synchronization. This is required for two reasons first a probe() cannot reasonably be considered to be complete without successfully completing a time synchronization for Interfaces that care about that sync. Secondly scheduling the operation asynchronously means its possible the PM-runtime suspend() path can execute before the async timesync operation completes. For both reasons we want to run synchronization - synchronously. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 2c94bbb1748f..8c77d6cb3919 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -219,7 +219,7 @@ static int greybus_probe(struct device *dev) return retval; } - gb_timesync_schedule_asynchronous(bundle->intf); + gb_timesync_schedule_synchronous(bundle->intf); pm_runtime_put(&bundle->intf->dev); -- cgit v1.2.3-59-g8ed1b From b5d1d1282bbb318a67fdcfc8ef2615e93d1d1724 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 21 Jul 2016 12:48:57 +0200 Subject: greybus: interface: fix interface_id attribute group All interfaces, regardless of type, should have an interface_id attribute reflecting its position on the frame. This has been reported to cause an assertion failure in libmoduleutil for dummy modules. Testing done: Verified that the attribute is present for registered dummy interfaces. Reported-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Tested-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Reviewed-by: Patrick Titiano <ptitiano@baylibre.com> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 919eaa0a46c7..6abe1311058f 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -587,7 +587,6 @@ static struct attribute *interface_unipro_attrs[] = { }; static struct attribute *interface_greybus_attrs[] = { - &dev_attr_interface_id.attr, &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, &dev_attr_serial_number.attr, @@ -603,6 +602,7 @@ static struct attribute *interface_power_attrs[] = { }; static struct attribute *interface_common_attrs[] = { + &dev_attr_interface_id.attr, &dev_attr_interface_type.attr, NULL }; -- cgit v1.2.3-59-g8ed1b From 350e3ac2ceb696435efbbe688e6e912801a4b8c4 Mon Sep 17 00:00:00 2001 From: Joel Porquet <porquet_joel@projectara.com> Date: Thu, 21 Jul 2016 15:57:38 -0700 Subject: greybus: firmware.h: rename TFTF firmware filenames Replace prefix "ara_" with prefix "gmp_" for TFTF firmware filenames. Testing Done: compiled and flashed EVT2 (bootimage) and tested that "gmp_" prefixed tftf files are expected. Pushed one from FDK and checked that it's properly loaded. Signed-off-by: Joel Porquet <porquet_joel@projectara.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/firmware.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h index 2fbb263895d2..841a0d532288 100644 --- a/drivers/staging/greybus/firmware.h +++ b/drivers/staging/greybus/firmware.h @@ -12,7 +12,7 @@ #include "greybus.h" -#define FW_NAME_PREFIX "ara_" +#define FW_NAME_PREFIX "gmp_" /* Length of the string in format: "FW_NAME_PREFIX""%08x_%08x_%08x_%08x_%s.tftf" */ #define FW_NAME_LEN 56 -- cgit v1.2.3-59-g8ed1b From 2358024b67fccc07b95c5d8e637927acdb8e30fa Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 21 Jul 2016 14:24:11 +0200 Subject: greybus: control: suppress bundle_activate() for bootrom We always knew backward compatibility with the ES3 bootrom, which was finalised about a year ago, would be a pain. Here we go again. The bootrom does not support control requests added after it was burnt into ROM for obvious reasons. This means that we need to suppress sending the new bundle_activate() operation to any interface executing the legacy bootrom. Do so by adding a new NO_PM interface quirk (we can use the control-protocol version for this later once we bump it). Note that the interface-disable path (e.g. for power down) is already handled by the FORCED_DISABLE quirk, and that the suspend/resume paths are currently avoided by making sure that the bootrom bundle never suspends. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Bartosz Golaszewski <bgolaszewski@baylibre.com> Tested-by: Bartosz Golaszewski <bgolaszewski@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 7 +++++++ drivers/staging/greybus/control.h | 1 + drivers/staging/greybus/interface.c | 3 ++- drivers/staging/greybus/interface.h | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 37a30b3075ce..a08a79da45d7 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -329,6 +329,9 @@ int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id) struct gb_control_bundle_pm_response response; int ret; + if (!control->has_bundle_activate) + return 0; + request.bundle_id = bundle_id; ret = gb_operation_sync(control->connection, GB_CONTROL_TYPE_BUNDLE_ACTIVATE, &request, @@ -528,6 +531,10 @@ int gb_control_enable(struct gb_control *control) if (control->protocol_major > 0 || control->protocol_minor > 1) control->has_bundle_version = true; + /* FIXME: use protocol version instead */ + if (!(control->intf->quirks & GB_INTERFACE_QUIRK_NO_PM)) + control->has_bundle_activate = true; + return 0; err_disable_connection: diff --git a/drivers/staging/greybus/control.h b/drivers/staging/greybus/control.h index f73ec3e297ba..f9a60daf9a72 100644 --- a/drivers/staging/greybus/control.h +++ b/drivers/staging/greybus/control.h @@ -19,6 +19,7 @@ struct gb_control { u8 protocol_major; u8 protocol_minor; + bool has_bundle_activate; bool has_bundle_version; char *vendor_string; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 6abe1311058f..74fa298f5fe7 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -411,7 +411,8 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) */ bootrom_quirks = GB_INTERFACE_QUIRK_NO_CPORT_FEATURES | GB_INTERFACE_QUIRK_FORCED_DISABLE | - GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH; + GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH | + GB_INTERFACE_QUIRK_NO_PM; switch (init_status) { case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED: case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index daa9759149a5..a08d10480654 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -23,6 +23,7 @@ enum gb_interface_type { #define GB_INTERFACE_QUIRK_NO_ARA_IDS BIT(2) #define GB_INTERFACE_QUIRK_FORCED_DISABLE BIT(3) #define GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH BIT(4) +#define GB_INTERFACE_QUIRK_NO_PM BIT(5) struct gb_interface { struct device dev; -- cgit v1.2.3-59-g8ed1b From f5c93dea8fb7daf5ec26176caa791719895cd28e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski <bgolaszewski@baylibre.com> Date: Thu, 21 Jul 2016 18:09:34 +0200 Subject: greybus: pm: add error handling to bundle activation The firmware now keeps the underlying hardware disabled until receiving the first Bundle Activate request. Additionally: requesting transition to a state the bundle is already in is no longer an error. We can now add proper error handling to the bundle activate call. Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/core.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 8c77d6cb3919..1049e9c0edb0 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -181,12 +181,11 @@ static int greybus_probe(struct device *dev) return retval; } - /* - * FIXME: We need to perform error handling on bundle activate call - * below when firmware is ready. We just allow the activate operation to - * fail for now since bundle may be in active already. - */ - gb_control_bundle_activate(bundle->intf->control, bundle->id); + retval = gb_control_bundle_activate(bundle->intf->control, bundle->id); + if (retval) { + pm_runtime_put(&bundle->intf->dev); + return retval; + } /* * Unbound bundle devices are always deactivated. During probe, the -- cgit v1.2.3-59-g8ed1b From db2951835b9b11abf3075302279a41cd345db312 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Wed, 20 Jul 2016 17:18:39 -0700 Subject: greybus: timesync: do not print frametime by default Currently frametime are being printed whenever an Interface is runtime-resumed. This is unnecessarily chatty. This patch moves the frametime print from using pr_info() to dev_dbg(). Testing Done: - $ echo "module greybus +p" > /d/dynamic_debug/control [ 97.699395] greybus 1-svc: greybus frametime: ap=2042778707 1-svc=2042775185 greybus1=2042778397 1-6.6=2042778390 Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Jeffrey Carlyle <jcarlyle@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index 23b209118f2a..ec0a51d6378d 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -746,7 +746,7 @@ static void gb_timesync_log_ping_time(struct gb_timesync_svc *timesync_svc) buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (buf) { gb_timesync_log_frame_time(timesync_svc, buf, PAGE_SIZE); - pr_info("%s", buf); + dev_dbg(×ync_svc->svc->dev, "%s", buf); kfree(buf); } } -- cgit v1.2.3-59-g8ed1b From adac4b95941189ca301759a67da4a132fc2804d5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 20 Jul 2016 16:39:00 -0700 Subject: greybus: firmware: Improve test application It can take arguments not and we can perform all the operations with a single binary, sorry for missing that initially. Usage: ./firmware <gb-fw-mgmt-X> <type: interface/backend> <firmware-tag> <timeout> And all of them have default values, etc. Tested with a semco 13 MP module. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/firmware/firmware.c | 146 ++++++++++++++------- 1 file changed, 96 insertions(+), 50 deletions(-) diff --git a/drivers/staging/greybus/Documentation/firmware/firmware.c b/drivers/staging/greybus/Documentation/firmware/firmware.c index 3c305f774939..c557bf1ec493 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware.c +++ b/drivers/staging/greybus/Documentation/firmware/firmware.c @@ -60,44 +60,29 @@ #include "../../greybus_firmware.h" -static const char *firmware_tag = "03"; /* S3 firmware */ +#define FW_DEV_DEFAULT "/dev/gb-fw-mgmt-0" +#define FW_TAG_INT_DEFAULT "s3f" +#define FW_TAG_BCND_DEFAULT "bf_01" +#define FW_UPDATE_TYPE_DEFAULT 0 +#define FW_TIMEOUT_DEFAULT 10000; + +static const char *firmware_tag; +static const char *fwdev = FW_DEV_DEFAULT; +static int fw_update_type = FW_UPDATE_TYPE_DEFAULT; +static int fw_timeout = FW_TIMEOUT_DEFAULT; static struct fw_mgmt_ioc_get_fw fw_info; static struct fw_mgmt_ioc_intf_load_and_validate intf_load; static struct fw_mgmt_ioc_backend_fw_update backend_update; -int main(int argc, char *argv[]) +static void usage(void) { - unsigned int timeout = 10000; - char *fwdev; - int fd, ret; - - /* Make sure arguments are correct */ - if (argc != 2) { - printf("\nUsage: ./firmware <Path of the gb-fw-mgmt-X dev>\n"); - return 0; - } - - fwdev = argv[1]; - - printf("Opening %s firmware management device\n", fwdev); - - fd = open(fwdev, O_RDWR); - if (fd < 0) { - printf("Failed to open: %s\n", fwdev); - ret = -1; - goto close_fd; - } - - /* Set Timeout */ - printf("Setting timeout to %u ms\n", timeout); + printf("\nUsage: ./firmware <gb-fw-mgmt-X (default: gb-fw-mgmt-0)> <interface: 0, backend: 1 (default: 0)> <firmware-tag> (default: \"s3f\"/\"bf_01\") <timeout (default: 10000 ms)>\n"); +} - ret = ioctl(fd, FW_MGMT_IOC_SET_TIMEOUT_MS, &timeout); - if (ret < 0) { - printf("Failed to set timeout: %s (%d)\n", fwdev, ret); - ret = -1; - goto close_fd; - } +static int update_intf_firmware(int fd) +{ + int ret; /* Get Interface Firmware Version */ printf("Get Interface Firmware Version\n"); @@ -106,8 +91,7 @@ int main(int argc, char *argv[]) if (ret < 0) { printf("Failed to get interface firmware version: %s (%d)\n", fwdev, ret); - ret = -1; - goto close_fd; + return -1; } printf("Interface Firmware tag (%s), major (%d), minor (%d)\n", @@ -120,6 +104,7 @@ int main(int argc, char *argv[]) intf_load.status = 0; intf_load.major = 0; intf_load.minor = 0; + strncpy((char *)&intf_load.firmware_tag, firmware_tag, GB_FIRMWARE_U_TAG_MAX_LEN); @@ -127,35 +112,47 @@ int main(int argc, char *argv[]) if (ret < 0) { printf("Failed to load interface firmware: %s (%d)\n", fwdev, ret); - ret = -1; - goto close_fd; + return -1; } if (intf_load.status != GB_FW_U_LOAD_STATUS_VALIDATED && intf_load.status != GB_FW_U_LOAD_STATUS_UNVALIDATED) { printf("Load status says loading failed: %d\n", intf_load.status); - ret = -1; - goto close_fd; + return -1; } printf("Interface Firmware (%s) Load done: major: %d, minor: %d, status: %d\n", firmware_tag, intf_load.major, intf_load.minor, intf_load.status); + /* Initiate Mode-switch to the newly loaded firmware */ + printf("Initiate Mode switch\n"); + + ret = ioctl(fd, FW_MGMT_IOC_MODE_SWITCH); + if (ret < 0) + printf("Failed to initiate mode-switch (%d)\n", ret); + + return ret; +} + +static int update_backend_firmware(int fd) +{ + int ret; + /* Get Backend Firmware Version */ printf("Getting Backend Firmware Version\n"); - strncpy((char *)&fw_info.firmware_tag, firmware_tag, - GB_FIRMWARE_U_TAG_MAX_LEN); fw_info.major = 0; fw_info.minor = 0; + strncpy((char *)&fw_info.firmware_tag, firmware_tag, + GB_FIRMWARE_U_TAG_MAX_LEN); ret = ioctl(fd, FW_MGMT_IOC_GET_BACKEND_FW, &fw_info); if (ret < 0) { printf("Failed to get backend firmware version: %s (%d)\n", fwdev, ret); - goto mode_switch; + return -1; } printf("Backend Firmware tag (%s), major (%d), minor (%d)\n", @@ -171,24 +168,73 @@ int main(int argc, char *argv[]) ret = ioctl(fd, FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE, &backend_update); if (ret < 0) { printf("Failed to load backend firmware: %s (%d)\n", fwdev, ret); - goto mode_switch; + return -1; } - printf("Backend Firmware (%s) Load done: status: %d\n", - firmware_tag, backend_update.status); - if (backend_update.status != GB_FW_U_BACKEND_FW_STATUS_SUCCESS) { printf("Load status says loading failed: %d\n", backend_update.status); + } else { + printf("Backend Firmware (%s) Load done: status: %d\n", + firmware_tag, backend_update.status); } -mode_switch: - /* Initiate Mode-switch to the newly loaded firmware */ - printf("Initiate Mode switch\n"); + return 0; +} - ret = ioctl(fd, FW_MGMT_IOC_MODE_SWITCH); - if (ret < 0) - printf("Failed to initiate mode-switch (%d)\n", ret); +int main(int argc, char *argv[]) +{ + int fd, ret; + + if (argc > 1 && + (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { + usage(); + return -1; + } + + if (argc > 1) + fwdev = argv[1]; + + if (argc > 2) + sscanf(argv[2], "%u", &fw_update_type); + + if (argc > 3) { + firmware_tag = argv[3]; + } else if (!fw_update_type) { + firmware_tag = FW_TAG_INT_DEFAULT; + } else { + firmware_tag = FW_TAG_BCND_DEFAULT; + } + + if (argc > 4) + sscanf(argv[4], "%u", &fw_timeout); + + printf("Trying Firmware update: fwdev: %s, type: %s, tag: %s, timeout: %d\n", + fwdev, fw_update_type == 0 ? "interface" : "backend", + firmware_tag, fw_timeout); + + printf("Opening %s firmware management device\n", fwdev); + + fd = open(fwdev, O_RDWR); + if (fd < 0) { + printf("Failed to open: %s\n", fwdev); + return -1; + } + + /* Set Timeout */ + printf("Setting timeout to %u ms\n", fw_timeout); + + ret = ioctl(fd, FW_MGMT_IOC_SET_TIMEOUT_MS, &fw_timeout); + if (ret < 0) { + printf("Failed to set timeout: %s (%d)\n", fwdev, ret); + ret = -1; + goto close_fd; + } + + if (!fw_update_type) + ret = update_intf_firmware(fd); + else + ret = update_backend_firmware(fd); close_fd: close(fd); -- cgit v1.2.3-59-g8ed1b From 92bcaddea3197e477bb37439805fcb5a2f9942d7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 21 Jul 2016 14:45:06 -0700 Subject: greybus: spilib: make spilib independent of gbphy spilib is used by multiple users currently (spi.c and fw-core.c) but commit aa52b62a0556 broke that hierarchy and introduced gbphy dependent code in spilib. This may have unreliable consequences as we are doing following operation unconditionally now: gbphy_dev = to_gbphy_dev(spi->parent); gbphy_runtime_get_sync(gbphy_dev); which may not go well when the parent is of type &bundle->dev (fw-core.c). This patch introduces spilib_ops and lets the users of the core register them. This shall have no functional change for the spi.c usecase and shall fix the unreliable results for the fw-core.c usecase. Tested by writing to mtd0 dev and verifying (with print messages) that the below routines are getting called for a gpbridge-test module. Fixes: aa52b62a0556 ("spi: Add runtime_pm support") Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-core.c | 2 +- drivers/staging/greybus/spi.c | 23 ++++++++++++++++++++++- drivers/staging/greybus/spilib.c | 28 ++++++++++++++++------------ drivers/staging/greybus/spilib.h | 8 +++++++- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c index 963973242be0..19f92fb0331b 100644 --- a/drivers/staging/greybus/fw-core.c +++ b/drivers/staging/greybus/fw-core.c @@ -38,7 +38,7 @@ static int gb_fw_spi_connection_init(struct gb_connection *connection) if (ret) return ret; - ret = gb_spilib_master_init(connection, &connection->bundle->dev); + ret = gb_spilib_master_init(connection, &connection->bundle->dev, NULL); if (ret) { gb_connection_disable(connection); return ret; diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index 2e6e328bae9e..bb76b3c0118d 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -13,6 +13,27 @@ #include "gbphy.h" #include "spilib.h" +#ifndef SPI_CORE_SUPPORT_PM +static int gbphy_spi_prepare_transfer_hardware(struct device *dev) +{ + return gbphy_runtime_get_sync(to_gbphy_dev(dev)); +} + +static void gbphy_spi_unprepare_transfer_hardware(struct device *dev) +{ + gbphy_runtime_put_autosuspend(to_gbphy_dev(dev)); +} + +static struct spilib_ops __spilib_ops = { + .prepare_transfer_hardware = gbphy_spi_prepare_transfer_hardware, + .unprepare_transfer_hardware = gbphy_spi_unprepare_transfer_hardware, +}; + +static struct spilib_ops *spilib_ops = &__spilib_ops; +#else +static struct spilib_ops *spilib_ops = NULL; +#endif + static int gb_spi_probe(struct gbphy_device *gbphy_dev, const struct gbphy_device_id *id) { @@ -29,7 +50,7 @@ static int gb_spi_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_connection_destroy; - ret = gb_spilib_master_init(connection, &gbphy_dev->dev); + ret = gb_spilib_master_init(connection, &gbphy_dev->dev, spilib_ops); if (ret) goto exit_connection_disable; diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c index e4c82e0a322b..9427c313dd4e 100644 --- a/drivers/staging/greybus/spilib.c +++ b/drivers/staging/greybus/spilib.c @@ -15,13 +15,13 @@ #include "greybus.h" #include "spilib.h" -#include "gbphy.h" struct gb_spilib { struct gb_connection *connection; struct device *parent; struct spi_transfer *first_xfer; struct spi_transfer *last_xfer; + struct spilib_ops *ops; u32 rx_xfer_offset; u32 tx_xfer_offset; u32 last_xfer_size; @@ -373,25 +373,21 @@ out: return ret; } -#ifndef SPI_CORE_SUPPORT_PM static int gb_spi_prepare_transfer_hardware(struct spi_master *master) { struct gb_spilib *spi = spi_master_get_devdata(master); - struct gbphy_device *gbphy_dev = to_gbphy_dev(spi->parent); - return gbphy_runtime_get_sync(gbphy_dev); + return spi->ops->prepare_transfer_hardware(spi->parent); } static int gb_spi_unprepare_transfer_hardware(struct spi_master *master) { struct gb_spilib *spi = spi_master_get_devdata(master); - struct gbphy_device *gbphy_dev = to_gbphy_dev(spi->parent); - gbphy_runtime_put_autosuspend(gbphy_dev); + spi->ops->unprepare_transfer_hardware(spi->parent); return 0; } -#endif static int gb_spi_setup(struct spi_device *spi) { @@ -483,7 +479,8 @@ static int gb_spi_setup_device(struct gb_spilib *spi, u8 cs) return 0; } -int gb_spilib_master_init(struct gb_connection *connection, struct device *dev) +int gb_spilib_master_init(struct gb_connection *connection, struct device *dev, + struct spilib_ops *ops) { struct gb_spilib *spi; struct spi_master *master; @@ -501,6 +498,7 @@ int gb_spilib_master_init(struct gb_connection *connection, struct device *dev) spi->connection = connection; gb_connection_set_data(connection, master); spi->parent = dev; + spi->ops = ops; /* get master configuration */ ret = gb_spi_get_master_config(spi); @@ -518,11 +516,17 @@ int gb_spilib_master_init(struct gb_connection *connection, struct device *dev) master->setup = gb_spi_setup; master->transfer_one_message = gb_spi_transfer_one_message; -#ifndef SPI_CORE_SUPPORT_PM - master->prepare_transfer_hardware = gb_spi_prepare_transfer_hardware; - master->unprepare_transfer_hardware = + if (ops && ops->prepare_transfer_hardware) { + master->prepare_transfer_hardware = + gb_spi_prepare_transfer_hardware; + } + + if (ops && ops->unprepare_transfer_hardware) { + master->unprepare_transfer_hardware = gb_spi_unprepare_transfer_hardware; -#else + } + +#ifdef SPI_CORE_SUPPORT_PM master->auto_runtime_pm = true; #endif diff --git a/drivers/staging/greybus/spilib.h b/drivers/staging/greybus/spilib.h index 9be1b3189834..566d0dde7f79 100644 --- a/drivers/staging/greybus/spilib.h +++ b/drivers/staging/greybus/spilib.h @@ -10,9 +10,15 @@ #ifndef __SPILIB_H #define __SPILIB_H +struct device; struct gb_connection; -int gb_spilib_master_init(struct gb_connection *connection, struct device *dev); +struct spilib_ops { + int (*prepare_transfer_hardware)(struct device *dev); + void (*unprepare_transfer_hardware)(struct device *dev); +}; + +int gb_spilib_master_init(struct gb_connection *connection, struct device *dev, struct spilib_ops *ops); void gb_spilib_master_exit(struct gb_connection *connection); #endif /* __SPILIB_H */ -- cgit v1.2.3-59-g8ed1b From 098dfaf45e76442d032207d2023930c685ab7c2e Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Fri, 22 Jul 2016 09:41:30 +0530 Subject: greybus: audio: Avoid using ARA keyword It is suggested to avoid using ARA keyword externally. So we need to update GB codec driver. Also, codec name is currently set to 'gb-codec'. However, it makes more sense to name it as apb-dummy-codec, since it is used to control various audio modules connected to APB via greybus. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 9c7bec737875..d7679e6bc364 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -1,5 +1,5 @@ /* - * audio codec driver + * APBridge ALSA SoC dummy codec driver * Copyright 2016 Google Inc. * Copyright 2016 Linaro Ltd. * @@ -1203,13 +1203,13 @@ static int gbaudio_codec_remove(struct platform_device *pdev) } static const struct of_device_id greybus_asoc_machine_of_match[] = { - { .compatible = "qcom,ara-codec", }, + { .compatible = "toshiba,apb-dummy-codec", }, {}, }; static struct platform_driver gbaudio_codec_driver = { .driver = { - .name = "gb-codec", + .name = "apb-dummy-codec", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &gbaudio_codec_pm_ops, @@ -1221,7 +1221,7 @@ static struct platform_driver gbaudio_codec_driver = { }; module_platform_driver(gbaudio_codec_driver); -MODULE_DESCRIPTION("Greybus codec driver"); +MODULE_DESCRIPTION("APBridge ALSA SoC dummy codec driver"); MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:gbaudio-codec"); +MODULE_ALIAS("platform:apb-dummy-codec"); -- cgit v1.2.3-59-g8ed1b From e514dec730284da080286bdf7229d21c2be68b24 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Fri, 22 Jul 2016 13:46:25 -0700 Subject: greybus: operation: print id when synchronous operation timeout In case of a synchronous operation timeout error, it's helpful for purpose of debugging to print the operation id in the error message, so that we know if the response is received at a later time after operation time out. Testing Done: - Observe the error message below when response comes later after operation timeout: [ 792.973978] greybus greybus1: 0/0:0: synchronous operation id 0x0005 of type 0x21 failed: -110 [ 800.646694] greybus greybus1: 0/0:0: unexpected response id 0x0005 received Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 659e84b10c7f..7475ec79b36a 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -1128,8 +1128,8 @@ int gb_operation_sync_timeout(struct gb_connection *connection, int type, ret = gb_operation_request_send_sync_timeout(operation, timeout); if (ret) { dev_err(&connection->hd->dev, - "%s: synchronous operation of type 0x%02x failed: %d\n", - connection->name, type, ret); + "%s: synchronous operation id 0x%04x of type 0x%02x failed: %d\n", + connection->name, operation->id, type, ret); } else { if (response_size) { memcpy(response, operation->response->payload, -- cgit v1.2.3-59-g8ed1b From 553cba82b1ada07c8d29405c4900a66891c06052 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 21 Jul 2016 22:41:05 -0700 Subject: greybus: control: Print bundle-id in print messages The new power management specific operations added to the control protocol do not print the bundle-id in the error messages and it is not possible to identify which bundle-id the operation failed for. Fix that and do minor rewriting of the print messages to make them more readable. Tested on EVT 2.0 with gpbridge-test module. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index a08a79da45d7..d7fd378026ec 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -259,14 +259,14 @@ int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id) GB_CONTROL_TYPE_BUNDLE_SUSPEND, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&control->dev, - "failed to send bundle suspend: %d\n", ret); + dev_err(&control->dev, "failed to send bundle %u suspend: %d\n", + bundle_id, ret); return ret; } if (response.status != GB_CONTROL_BUNDLE_PM_OK) { - dev_err(&control->dev, - "bundle error while suspending: %d\n", response.status); + dev_err(&control->dev, "failed to suspend bundle %u: %d\n", + bundle_id, response.status); return gb_control_bundle_pm_status_map(response.status); } @@ -284,14 +284,14 @@ int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id) GB_CONTROL_TYPE_BUNDLE_RESUME, &request, sizeof(request), &response, sizeof(response)); if (ret) { - dev_err(&control->dev, - "failed to send bundle resume: %d\n", ret); + dev_err(&control->dev, "failed to send bundle %u resume: %d\n", + bundle_id, ret); return ret; } if (response.status != GB_CONTROL_BUNDLE_PM_OK) { - dev_err(&control->dev, - "bundle error while resuming: %d\n", response.status); + dev_err(&control->dev, "failed to resume bundle %u: %d\n", + bundle_id, response.status); return gb_control_bundle_pm_status_map(response.status); } @@ -310,13 +310,14 @@ int gb_control_bundle_deactivate(struct gb_control *control, u8 bundle_id) sizeof(request), &response, sizeof(response)); if (ret) { dev_err(&control->dev, - "failed to send bundle deactivate: %d\n", ret); + "failed to send bundle %u deactivate: %d\n", bundle_id, + ret); return ret; } if (response.status != GB_CONTROL_BUNDLE_PM_OK) { - dev_err(&control->dev, - "bundle error while deactivating: %d\n", response.status); + dev_err(&control->dev, "failed to deactivate bundle %u: %d\n", + bundle_id, response.status); return gb_control_bundle_pm_status_map(response.status); } @@ -338,13 +339,14 @@ int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id) sizeof(request), &response, sizeof(response)); if (ret) { dev_err(&control->dev, - "failed to send bundle activate: %d\n", ret); + "failed to send bundle %u activate: %d\n", bundle_id, + ret); return ret; } if (response.status != GB_CONTROL_BUNDLE_PM_OK) { - dev_err(&control->dev, - "bundle error while activating: %d\n", response.status); + dev_err(&control->dev, "failed to activate bundle %u: %d\n", + bundle_id, response.status); return gb_control_bundle_pm_status_map(response.status); } -- cgit v1.2.3-59-g8ed1b From 633e45eaac40406739baae960d2c8abac40dbb83 Mon Sep 17 00:00:00 2001 From: Ann Chen <chen_ann@projectara.com> Date: Fri, 22 Jul 2016 15:33:55 +0800 Subject: greybus: vibrator: integrate runtime pm Integrate greybus drivers with the Linux Kernel RuntimePM framework for vibrator driver. Testing Done: AP side (kernel) can control the vibrator driver with suspend and resume. Signed-off-by: Ann Chen <chen_ann@projectara.com> Reviewed-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/vibrator.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 33b2bf9c16c8..db5583962101 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -13,6 +13,8 @@ #include <linux/device.h> #include <linux/kdev_t.h> #include <linux/idr.h> +#include <linux/pm_runtime.h> + #include "greybus.h" struct gb_vibrator_device { @@ -32,16 +34,37 @@ struct gb_vibrator_on_request { static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) { struct gb_vibrator_on_request request; + struct gb_bundle *bundle = vib->connection->bundle; + int ret; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; request.timeout_ms = cpu_to_le16(timeout_ms); - return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, - &request, sizeof(request), NULL, 0); + ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, + &request, sizeof(request), NULL, 0); + + gb_pm_runtime_put_autosuspend(bundle); + + return ret; } static int turn_off(struct gb_vibrator_device *vib) { - return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, - NULL, 0, NULL, 0); + struct gb_bundle *bundle = vib->connection->bundle; + int ret; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + + ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, + NULL, 0, NULL, 0); + + gb_pm_runtime_put_autosuspend(bundle); + + return ret; } static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, @@ -151,6 +174,8 @@ static int gb_vibrator_probe(struct gb_bundle *bundle, } #endif + gb_pm_runtime_put_autosuspend(bundle); + return 0; err_ida_remove: @@ -168,6 +193,11 @@ err_free_vib: static void gb_vibrator_disconnect(struct gb_bundle *bundle) { struct gb_vibrator_device *vib = greybus_get_drvdata(bundle); + int ret; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + gb_pm_runtime_get_noresume(bundle); #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); -- cgit v1.2.3-59-g8ed1b From cc90d6fce858cea167102380e57a63af2f75ec8b Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Fri, 22 Jul 2016 14:13:40 -0700 Subject: greybus: bootrom: Skip setting timeout in failure path of size request The currently set value of next_request_type in the error path of gb_bootrom_firmware_size_request() is not correct, as it should have been NEXT_REQ_FIRMWARE_SIZE for the failure case (as we should be waiting for another similar request). But, if an error occurs in gb_bootrom_firmware_size_request(), then the ES3 bootrom will never be able to recover from it and send another request. And so there is no point waiting for another request and timing out. Skip doing that in error path. Reported-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index 309e0a7c865a..d16f13e0cb8d 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -223,9 +223,11 @@ unlock: mutex_unlock(&bootrom->mutex); queue_work: - /* Refresh timeout */ - gb_bootrom_set_timeout(bootrom, NEXT_REQ_GET_FIRMWARE, - NEXT_REQ_TIMEOUT_MS); + if (!ret) { + /* Refresh timeout */ + gb_bootrom_set_timeout(bootrom, NEXT_REQ_GET_FIRMWARE, + NEXT_REQ_TIMEOUT_MS); + } return ret; } -- cgit v1.2.3-59-g8ed1b From 2d6f1c29988c0f5b51949bb674fe9dcdb923c4a6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Fri, 22 Jul 2016 14:13:41 -0700 Subject: greybus: bootrom: Don't print duplicate error messages On failing to find a firmware image, we get these today: bootrom 1-3.3.1: Firmware request for ara_00000126_00001001_fffe0001_ffe5001a_s2l.tftf has failed : -12 bootrom 1-3.3.1: gb_bootrom_firmware_size_request: failed to download firmware (-12) Which are more or less duplicate, as they print error for the same root cause. With this patch this is all we get now: bootrom 1-3.3.1: failed to download ara_00000126_00001001_fffe0001_ffe5001a_s2l.tftf firmware (-12) Reported-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index d16f13e0cb8d..c5b397145145 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -171,10 +171,11 @@ static int download_firmware(struct gb_bootrom *bootrom, u8 stage) rc = request_firmware(&bootrom->fw, firmware_name, &connection->bundle->dev); - if (rc) - dev_err(&connection->bundle->dev, - "Firmware request for %s has failed : %d", + if (rc) { + dev_err(&connection->bundle->dev, "failed to download %s firmware (%d)\n", firmware_name, rc); + } + return rc; } @@ -200,11 +201,8 @@ static int gb_bootrom_firmware_size_request(struct gb_operation *op) mutex_lock(&bootrom->mutex); ret = download_firmware(bootrom, size_request->stage); - if (ret) { - dev_err(dev, "%s: failed to download firmware (%d)\n", __func__, - ret); + if (ret) goto unlock; - } if (!gb_operation_response_alloc(op, sizeof(*size_response), GFP_KERNEL)) { -- cgit v1.2.3-59-g8ed1b From 6d9e6ffca4226007672800a6681c42cc877aab60 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Fri, 22 Jul 2016 14:13:42 -0700 Subject: greybus: bootrom: Set timeout before sending AP_READY A special race is observed in the case where the phone boots with modules attached to it. In that case, the AP processes a later FIRMWARE_SIZE request from the bootrom, before processing the response to the earlier AP_READY request. And because of that, we set the timeout from gb_bootrom_probe() after trying to cancel it from gb_bootrom_firmware_size_request(). And with that, if the firmware package isn't available for the kernel to directly read from, then we get a timeout print message like below: [ 23.669764] bootrom 1-3.3.1: Firmware file 'ara_00000126_00001001_fffe0001_ffe5001a_s2l.tftf' requested [ 24.680528] bootrom 1-3.3.1: Timed out waiting for Firmware Size Request from the Module Note that this doesn't happen if the module is inserted into the frame, after the phone is already booted. This behavior is the result of an earlier commit cdd1cb3f4943 ("operation: add completion work queue"). This perhaps happens only at boot time because the UNBOUND wq (connection->wq) gets a chance to run on other CPUs, while the BOUND wq (gb_operation_completion_wq) doesn't at that stage (24 seconds since boot). Setting the timeout before sending the AP_READY request fixes it for now. Reported-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index c5b397145145..7cc4d034c421 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -450,23 +450,25 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, if (ret) goto err_connection_disable; + /* Refresh timeout */ + gb_bootrom_set_timeout(bootrom, NEXT_REQ_FIRMWARE_SIZE, + NEXT_REQ_TIMEOUT_MS); + /* Tell bootrom we're ready. */ ret = gb_operation_sync(connection, GB_BOOTROM_TYPE_AP_READY, NULL, 0, NULL, 0); if (ret) { dev_err(&connection->bundle->dev, "failed to send AP READY: %d\n", ret); - goto err_connection_disable; + goto err_cancel_timeout; } - /* Refresh timeout */ - gb_bootrom_set_timeout(bootrom, NEXT_REQ_FIRMWARE_SIZE, - NEXT_REQ_TIMEOUT_MS); - dev_dbg(&bundle->dev, "AP_READY sent\n"); return 0; +err_cancel_timeout: + cancel_delayed_work_sync(&bootrom->dwork); err_connection_disable: gb_connection_disable(connection); err_connection_destroy: -- cgit v1.2.3-59-g8ed1b From 40d276ede92e85e4f414ac655c217e0bf5292cbb Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Fri, 22 Jul 2016 14:13:43 -0700 Subject: greybus: bootrom: Create gb_bootrom_cancel_timeout() We set timeouts using gb_bootrom_set_timeout(), which hides the internal implementation, i.e. workqueues. While canceling timeouts, we do cancel_delayed_work_sync(), which exposes the internal implementation and doesn't look that clean. Create gb_bootrom_cancel_timeout() to hide the internal implementation here as well. Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index 7cc4d034c421..b90b25cadf5d 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -92,6 +92,11 @@ static void gb_bootrom_set_timeout(struct gb_bootrom *bootrom, schedule_delayed_work(&bootrom->dwork, msecs_to_jiffies(timeout)); } +static void gb_bootrom_cancel_timeout(struct gb_bootrom *bootrom) +{ + cancel_delayed_work_sync(&bootrom->dwork); +} + /* * The es2 chip doesn't have VID/PID programmed into the hardware and we need to * hack that up to distinguish different modules and their firmware blobs. @@ -188,7 +193,7 @@ static int gb_bootrom_firmware_size_request(struct gb_operation *op) int ret; /* Disable timeouts */ - cancel_delayed_work_sync(&bootrom->dwork); + gb_bootrom_cancel_timeout(bootrom); if (op->request->payload_size != sizeof(*size_request)) { dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", @@ -242,7 +247,7 @@ static int gb_bootrom_get_firmware(struct gb_operation *op) int ret = 0; /* Disable timeouts */ - cancel_delayed_work_sync(&bootrom->dwork); + gb_bootrom_cancel_timeout(bootrom); if (op->request->payload_size != sizeof(*firmware_request)) { dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", @@ -310,7 +315,7 @@ static int gb_bootrom_ready_to_boot(struct gb_operation *op) int ret = 0; /* Disable timeouts */ - cancel_delayed_work_sync(&bootrom->dwork); + gb_bootrom_cancel_timeout(bootrom); if (op->request->payload_size != sizeof(*rtb_request)) { dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", @@ -468,7 +473,7 @@ static int gb_bootrom_probe(struct gb_bundle *bundle, return 0; err_cancel_timeout: - cancel_delayed_work_sync(&bootrom->dwork); + gb_bootrom_cancel_timeout(bootrom); err_connection_disable: gb_connection_disable(connection); err_connection_destroy: @@ -488,7 +493,7 @@ static void gb_bootrom_disconnect(struct gb_bundle *bundle) gb_connection_disable(bootrom->connection); /* Disable timeouts */ - cancel_delayed_work_sync(&bootrom->dwork); + gb_bootrom_cancel_timeout(bootrom); /* * Release firmware: -- cgit v1.2.3-59-g8ed1b From 68793c4c8824a0b1e0cb89737c5919a0b10d70cf Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Fri, 22 Jul 2016 14:13:44 -0700 Subject: greybus: bootrom: Rename download_firmware() as find_firmware() The download_firmware() function isn't downloading the firmware but just finding if one is available or not. The same applies to the error message printed by it. Replace 'download' with 'find' to make it sound better. Reported-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index b90b25cadf5d..e7cfa80b6b0b 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -140,7 +140,7 @@ static void bootrom_es2_fixup_vid_pid(struct gb_bootrom *bootrom) } /* This returns path of the firmware blob on the disk */ -static int download_firmware(struct gb_bootrom *bootrom, u8 stage) +static int find_firmware(struct gb_bootrom *bootrom, u8 stage) { struct gb_connection *connection = bootrom->connection; struct gb_interface *intf = connection->bundle->intf; @@ -177,8 +177,8 @@ static int download_firmware(struct gb_bootrom *bootrom, u8 stage) rc = request_firmware(&bootrom->fw, firmware_name, &connection->bundle->dev); if (rc) { - dev_err(&connection->bundle->dev, "failed to download %s firmware (%d)\n", - firmware_name, rc); + dev_err(&connection->bundle->dev, + "failed to find %s firmware (%d)\n", firmware_name, rc); } return rc; @@ -205,7 +205,7 @@ static int gb_bootrom_firmware_size_request(struct gb_operation *op) mutex_lock(&bootrom->mutex); - ret = download_firmware(bootrom, size_request->stage); + ret = find_firmware(bootrom, size_request->stage); if (ret) goto unlock; -- cgit v1.2.3-59-g8ed1b From 43c85a09b12cd3e782ae237be9903fef4559cc0d Mon Sep 17 00:00:00 2001 From: Philip Yang <yang_philip@projectara.com> Date: Sat, 23 Jul 2016 05:48:14 +0800 Subject: greybus: HID: Add runtime pm support Modify HID greybus driver to support runtime PM framework. The suspend and resume fucntion have been test with tutorial-hid, e-ink-display and gpbridge-test image by sysfs resume, all HID buttons work well on suspend/resume. Testing Done: Compiled and verified on EVT2, DB3.5, GPB test module with daughter board, Red module. Signed-off-by: Philip Yang <yang_philip@projectara.com> Reviewed-by: David Lin <dtwlin@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/hid.c | 51 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index ca0c94949c6b..b558c811b7a1 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -18,6 +18,7 @@ /* Greybus HID device's structure */ struct gb_hid { + struct gb_bundle *bundle; struct gb_connection *connection; struct hid_device *hid; @@ -44,26 +45,55 @@ static int gb_hid_get_desc(struct gb_hid *ghid) static int gb_hid_get_report_desc(struct gb_hid *ghid, char *rdesc) { - return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT_DESC, + int ret; + + ret = gb_pm_runtime_get_sync(ghid->bundle); + if (ret) + return ret; + + ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT_DESC, NULL, 0, rdesc, le16_to_cpu(ghid->hdesc.wReportDescLength)); + + gb_pm_runtime_put_autosuspend(ghid->bundle); + + return ret; } static int gb_hid_set_power(struct gb_hid *ghid, int type) { - return gb_operation_sync(ghid->connection, type, NULL, 0, NULL, 0); + int ret; + + ret = gb_pm_runtime_get_sync(ghid->bundle); + if (ret) + return ret; + + ret = gb_operation_sync(ghid->connection, type, NULL, 0, NULL, 0); + + gb_pm_runtime_put_autosuspend(ghid->bundle); + + return ret; } static int gb_hid_get_report(struct gb_hid *ghid, u8 report_type, u8 report_id, unsigned char *buf, int len) { struct gb_hid_get_report_request request; + int ret; + + ret = gb_pm_runtime_get_sync(ghid->bundle); + if (ret) + return ret; request.report_type = report_type; request.report_id = report_id; - return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT, + ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT, &request, sizeof(request), buf, len); + + gb_pm_runtime_put_autosuspend(ghid->bundle); + + return ret; } static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, @@ -73,11 +103,17 @@ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, struct gb_operation *operation; int ret, size = sizeof(*request) + len - 1; + ret = gb_pm_runtime_get_sync(ghid->bundle); + if (ret) + return ret; + operation = gb_operation_create(ghid->connection, GB_HID_TYPE_SET_REPORT, size, 0, GFP_KERNEL); - if (!operation) + if (!operation) { + gb_pm_runtime_put_autosuspend(ghid->bundle); return -ENOMEM; + } request = operation->request->payload; request->report_type = report_type; @@ -93,6 +129,7 @@ static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, } gb_operation_put(operation); + gb_pm_runtime_put_autosuspend(ghid->bundle); return ret; } @@ -458,6 +495,7 @@ static int gb_hid_probe(struct gb_bundle *bundle, } ghid->hid = hid; + ghid->bundle = bundle; greybus_set_drvdata(bundle, ghid); @@ -475,6 +513,8 @@ static int gb_hid_probe(struct gb_bundle *bundle, goto err_connection_disable; } + gb_pm_runtime_put_autosuspend(bundle); + return 0; err_connection_disable: @@ -493,6 +533,9 @@ static void gb_hid_disconnect(struct gb_bundle *bundle) { struct gb_hid *ghid = greybus_get_drvdata(bundle); + if (gb_pm_runtime_get_sync(bundle)) + gb_pm_runtime_get_noresume(bundle); + hid_destroy_device(ghid->hid); gb_connection_disable(ghid->connection); gb_connection_destroy(ghid->connection); -- cgit v1.2.3-59-g8ed1b From dfcba8626f55fe5d6ba6e2847178cbf629740773 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Tue, 26 Jul 2016 17:11:28 +0200 Subject: greybus: operation: fix broken response error messages The operation type included in the error message printed for malformed responses has never been correct. An uninitialised buffer was used to retrieve the type, resulting in the type always being reported as 0. Fix this by passing a properly aligned header to the response handler, and drop the now redundant id and result parameters. Fixes: cb0ef0c019ab ("operation: print message type on errors") Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index 7475ec79b36a..e26b1e118545 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -928,13 +928,16 @@ static void gb_connection_recv_request(struct gb_connection *connection, * data into the response buffer and handle the rest via workqueue. */ static void gb_connection_recv_response(struct gb_connection *connection, - u16 operation_id, u8 result, void *data, size_t size) + const struct gb_operation_msg_hdr *header, + void *data, size_t size) { - struct gb_operation_msg_hdr *header; struct gb_operation *operation; struct gb_message *message; - int errno = gb_operation_status_map(result); size_t message_size; + u16 operation_id; + int errno; + + operation_id = le16_to_cpu(header->operation_id); if (!operation_id) { dev_err_ratelimited(&connection->hd->dev, @@ -951,8 +954,8 @@ static void gb_connection_recv_response(struct gb_connection *connection, return; } + errno = gb_operation_status_map(header->result); message = operation->response; - header = message->header; message_size = sizeof(*header) + message->payload_size; if (!errno && size > message_size) { dev_err_ratelimited(&connection->hd->dev, @@ -979,7 +982,7 @@ static void gb_connection_recv_response(struct gb_connection *connection, /* The rest will be handled in work queue context */ if (gb_operation_result_set(operation, errno)) { - memcpy(header, data, size); + memcpy(message->buffer, data, size); queue_work(gb_operation_completion_wq, &operation->work); } @@ -1026,8 +1029,8 @@ void gb_connection_recv(struct gb_connection *connection, operation_id = le16_to_cpu(header.operation_id); if (header.type & GB_MESSAGE_TYPE_RESPONSE) - gb_connection_recv_response(connection, operation_id, - header.result, data, msg_size); + gb_connection_recv_response(connection, &header, data, + msg_size); else gb_connection_recv_request(connection, operation_id, header.type, data, msg_size); -- cgit v1.2.3-59-g8ed1b From 112f563e1879ab73853fed9d875f45d96d5990fd Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Tue, 26 Jul 2016 17:11:29 +0200 Subject: greybus: operation: fix broken response tracepoint The response-received tracepoint is currently broken. Instead of parsing the received message header it was tracing a bunch of zeroed fields from an uninitialised response buffer. Fix this by moving the tracepoint after were the response buffer is initialised. Fixes: 7cb496e6890e ("greybus: tracing: fix message traces") Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index e26b1e118545..b9692b750978 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -974,7 +974,6 @@ static void gb_connection_recv_response(struct gb_connection *connection, errno = -EMSGSIZE; } } - trace_gb_message_recv_response(operation->response); /* We must ignore the payload if a bad status is returned */ if (errno) @@ -983,6 +982,9 @@ static void gb_connection_recv_response(struct gb_connection *connection, /* The rest will be handled in work queue context */ if (gb_operation_result_set(operation, errno)) { memcpy(message->buffer, data, size); + + trace_gb_message_recv_response(message); + queue_work(gb_operation_completion_wq, &operation->work); } -- cgit v1.2.3-59-g8ed1b From 2321f049a93368fffc3375cfbf2f9f9561ef1e69 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Tue, 26 Jul 2016 17:11:30 +0200 Subject: greybus: operation: clean up request handler Clean up the incoming request handler somewhat by passing a properly aligned header and dropping the now redundant id and type parameters. Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/operation.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index b9692b750978..0123109a1070 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -889,12 +889,17 @@ EXPORT_SYMBOL_GPL(greybus_message_sent); * data into the request buffer and handle the rest via workqueue. */ static void gb_connection_recv_request(struct gb_connection *connection, - u16 operation_id, u8 type, - void *data, size_t size) + const struct gb_operation_msg_hdr *header, + void *data, size_t size) { struct gb_operation *operation; + u16 operation_id; + u8 type; int ret; + operation_id = le16_to_cpu(header->operation_id); + type = header->type; + operation = gb_operation_create_incoming(connection, operation_id, type, data, size); if (!operation) { @@ -1002,7 +1007,6 @@ void gb_connection_recv(struct gb_connection *connection, struct gb_operation_msg_hdr header; struct device *dev = &connection->hd->dev; size_t msg_size; - u16 operation_id; if (connection->state == GB_CONNECTION_STATE_DISABLED || gb_connection_is_offloaded(connection)) { @@ -1029,13 +1033,13 @@ void gb_connection_recv(struct gb_connection *connection, return; /* XXX Should still complete operation */ } - operation_id = le16_to_cpu(header.operation_id); - if (header.type & GB_MESSAGE_TYPE_RESPONSE) + if (header.type & GB_MESSAGE_TYPE_RESPONSE) { gb_connection_recv_response(connection, &header, data, msg_size); - else - gb_connection_recv_request(connection, operation_id, - header.type, data, msg_size); + } else { + gb_connection_recv_request(connection, &header, data, + msg_size); + } } /* -- cgit v1.2.3-59-g8ed1b From c57fbb404af28eda44d0590ede8812b889a89b8c Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Tue, 26 Jul 2016 13:41:02 -0700 Subject: greybus: firmware: Add runtime PM support This patch implements runtime PM support for firmware management bundle. This guarantees that the bundle will be active, while the AP or the Module is trying to exchange any operations over any of the CPorts. - Firmware Management CPort: Runtime PM get/put calls are placed around the ioctl calls, which are all implemented as blocking ioctls. - Component Authentication CPort: Runtime PM get/put calls are placed around the ioctl calls, which are all implemented as blocking ioctls. - SPI: Uses the interface provided by spilib.c and runtime PM get/put are called around connection usage. - firmware-download: This is the most tricky one. All operations on this CPort are initiated from the Module and not from the AP. And the AP needs to do runtime_pm_get() before any request is received over this CPort. The module doesn't send any request over this connection, unless the AP has requested the module over firmware management CPort to download a firmware package over firmware download CPort. And so the runtime PM get/put calls around the ioctls in fw-management.c are sufficient to handle the firmware management CPort as well. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/authentication.c | 10 ++++++++-- drivers/staging/greybus/fw-core.c | 31 ++++++++++++++++++++++++++++++- drivers/staging/greybus/fw-management.c | 10 ++++++++-- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/authentication.c b/drivers/staging/greybus/authentication.c index a4ac3bbdcb86..168626ba0c03 100644 --- a/drivers/staging/greybus/authentication.c +++ b/drivers/staging/greybus/authentication.c @@ -265,6 +265,7 @@ static long cap_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) { struct gb_cap *cap = file->private_data; + struct gb_bundle *bundle = cap->connection->bundle; int ret = -ENODEV; /* @@ -278,8 +279,13 @@ static long cap_ioctl_unlocked(struct file *file, unsigned int cmd, * new operations. */ mutex_lock(&cap->mutex); - if (!cap->disabled) - ret = cap_ioctl(cap, cmd, (void __user *)arg); + if (!cap->disabled) { + ret = gb_pm_runtime_get_sync(bundle); + if (!ret) { + ret = cap_ioctl(cap, cmd, (void __user *)arg); + gb_pm_runtime_put_autosuspend(bundle); + } + } mutex_unlock(&cap->mutex); return ret; diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c index 19f92fb0331b..56296db0b509 100644 --- a/drivers/staging/greybus/fw-core.c +++ b/drivers/staging/greybus/fw-core.c @@ -20,6 +20,27 @@ struct gb_fw_core { struct gb_connection *cap_connection; }; +#ifndef SPI_CORE_SUPPORT_PM +static int fw_spi_prepare_transfer_hardware(struct device *dev) +{ + return gb_pm_runtime_get_sync(to_gb_bundle(dev)); +} + +static void fw_spi_unprepare_transfer_hardware(struct device *dev) +{ + gb_pm_runtime_put_autosuspend(to_gb_bundle(dev)); +} + +static struct spilib_ops __spilib_ops = { + .prepare_transfer_hardware = fw_spi_prepare_transfer_hardware, + .unprepare_transfer_hardware = fw_spi_unprepare_transfer_hardware, +}; + +static struct spilib_ops *spilib_ops = &__spilib_ops; +#else +static struct spilib_ops *spilib_ops = NULL; +#endif + struct gb_connection *to_fw_mgmt_connection(struct device *dev) { struct gb_fw_core *fw_core = dev_get_drvdata(dev); @@ -38,7 +59,8 @@ static int gb_fw_spi_connection_init(struct gb_connection *connection) if (ret) return ret; - ret = gb_spilib_master_init(connection, &connection->bundle->dev, NULL); + ret = gb_spilib_master_init(connection, &connection->bundle->dev, + spilib_ops); if (ret) { gb_connection_disable(connection); return ret; @@ -206,6 +228,8 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, greybus_set_drvdata(bundle, fw_core); + gb_pm_runtime_put_autosuspend(bundle); + return 0; err_exit_connections: @@ -225,6 +249,11 @@ err_destroy_connections: static void gb_fw_core_disconnect(struct gb_bundle *bundle) { struct gb_fw_core *fw_core = greybus_get_drvdata(bundle); + int ret; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + gb_pm_runtime_get_noresume(bundle); gb_fw_mgmt_connection_exit(fw_core->mgmt_connection); gb_cap_connection_exit(fw_core->cap_connection); diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index ccd9d7c91416..2efe65cfd612 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -507,6 +507,7 @@ static long fw_mgmt_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) { struct fw_mgmt *fw_mgmt = file->private_data; + struct gb_bundle *bundle = fw_mgmt->connection->bundle; int ret = -ENODEV; /* @@ -522,8 +523,13 @@ static long fw_mgmt_ioctl_unlocked(struct file *file, unsigned int cmd, * new operations. */ mutex_lock(&fw_mgmt->mutex); - if (!fw_mgmt->disabled) - ret = fw_mgmt_ioctl(fw_mgmt, cmd, (void __user *)arg); + if (!fw_mgmt->disabled) { + ret = gb_pm_runtime_get_sync(bundle); + if (!ret) { + ret = fw_mgmt_ioctl(fw_mgmt, cmd, (void __user *)arg); + gb_pm_runtime_put_autosuspend(bundle); + } + } mutex_unlock(&fw_mgmt->mutex); return ret; -- cgit v1.2.3-59-g8ed1b From 47cbaf5e55a6906f404da06cab652cd19aa37537 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Tue, 26 Jul 2016 13:41:03 -0700 Subject: greybus: interface: Rename *_NO_PM as *_NO_BUNDLE_ACTIVATE Its a special quirk just for the bootrom as it doesn't have any PM operations implemented. As the greybus bootrom bundle driver doesn't try to do any PM stuff, this quirk is used only to skip bundle activate operation currently. Rename the GB_INTERFACE_QUIRK_NO_PM quirk to GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE to suit its purpose better as the GB_INTERFACE_QUIRK_NO_PM will be used for other quirk now. Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 2 +- drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/interface.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index d7fd378026ec..34493d572684 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -534,7 +534,7 @@ int gb_control_enable(struct gb_control *control) control->has_bundle_version = true; /* FIXME: use protocol version instead */ - if (!(control->intf->quirks & GB_INTERFACE_QUIRK_NO_PM)) + if (!(control->intf->quirks & GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE)) control->has_bundle_activate = true; return 0; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 74fa298f5fe7..c20077acd0d5 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -412,7 +412,7 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) bootrom_quirks = GB_INTERFACE_QUIRK_NO_CPORT_FEATURES | GB_INTERFACE_QUIRK_FORCED_DISABLE | GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH | - GB_INTERFACE_QUIRK_NO_PM; + GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE; switch (init_status) { case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED: case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index a08d10480654..89eecf0a4bad 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -23,7 +23,7 @@ enum gb_interface_type { #define GB_INTERFACE_QUIRK_NO_ARA_IDS BIT(2) #define GB_INTERFACE_QUIRK_FORCED_DISABLE BIT(3) #define GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH BIT(4) -#define GB_INTERFACE_QUIRK_NO_PM BIT(5) +#define GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE BIT(5) struct gb_interface { struct device dev; -- cgit v1.2.3-59-g8ed1b From 0c543f9bb2dfa4239a601bff70e66020c2daec59 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Tue, 26 Jul 2016 13:41:04 -0700 Subject: greybus: interface: Add quirk for no PM for S2 Loader S2 Loader doesn't support runtime PM operations currently and we will fail to suspend/resume the bundle for firmware management protocols. Once that happens, the bundle and its connections will be pretty much useless as we would have tried to disable/enable all connections during such an operation and the S2 loader doesn't expect the connections to go away during normal operation (except in the case of mode-switch). This patch defines a new quirk GB_INTERFACE_QUIRK_NO_PM and uses a new interface init status value (GB_INIT_S2_LOADER_INITIALIZED) which will be advertised by S2 Loader now in the init status. After detecting the currently running stage as S2 Loader, the kernel wouldn't attempt suspending or resuming the bundle. Reviewed-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-core.c | 13 +++++++++---- drivers/staging/greybus/greybus_protocols.h | 1 + drivers/staging/greybus/interface.c | 10 ++++++++++ drivers/staging/greybus/interface.h | 1 + 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c index 56296db0b509..a7e4a8c24d22 100644 --- a/drivers/staging/greybus/fw-core.c +++ b/drivers/staging/greybus/fw-core.c @@ -228,7 +228,9 @@ static int gb_fw_core_probe(struct gb_bundle *bundle, greybus_set_drvdata(bundle, fw_core); - gb_pm_runtime_put_autosuspend(bundle); + /* FIXME: Remove this after S2 Loader gets runtime PM support */ + if (!(bundle->intf->quirks & GB_INTERFACE_QUIRK_NO_PM)) + gb_pm_runtime_put_autosuspend(bundle); return 0; @@ -251,9 +253,12 @@ static void gb_fw_core_disconnect(struct gb_bundle *bundle) struct gb_fw_core *fw_core = greybus_get_drvdata(bundle); int ret; - ret = gb_pm_runtime_get_sync(bundle); - if (ret) - gb_pm_runtime_get_noresume(bundle); + /* FIXME: Remove this after S2 Loader gets runtime PM support */ + if (!(bundle->intf->quirks & GB_INTERFACE_QUIRK_NO_PM)) { + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + gb_pm_runtime_get_noresume(bundle); + } gb_fw_mgmt_connection_exit(fw_core->mgmt_connection); gb_cap_connection_exit(fw_core->cap_connection); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c7fcb85892fd..2de5aef54637 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1145,6 +1145,7 @@ struct gb_svc_dme_peer_set_response { #define GB_INIT_UNTRUSTED_SPI_BOOT_FINISHED 0x04 #define GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED 0x06 #define GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED 0x09 +#define GB_INIT_S2_LOADER_BOOT_STARTED 0x0D struct gb_svc_route_create_request { __u8 intf1_id; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c20077acd0d5..faa623919757 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -363,6 +363,7 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) { struct gb_host_device *hd = intf->hd; unsigned long bootrom_quirks; + unsigned long s2l_quirks; int ret; u32 value; u16 attr; @@ -413,13 +414,22 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) GB_INTERFACE_QUIRK_FORCED_DISABLE | GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH | GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE; + + s2l_quirks = GB_INTERFACE_QUIRK_NO_PM; + switch (init_status) { case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED: case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: intf->quirks |= bootrom_quirks; break; + case GB_INIT_S2_LOADER_BOOT_STARTED: + /* S2 Loader doesn't support runtime PM */ + intf->quirks &= ~bootrom_quirks; + intf->quirks |= s2l_quirks; + break; default: intf->quirks &= ~bootrom_quirks; + intf->quirks &= ~s2l_quirks; } /* Clear the init status. */ diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 89eecf0a4bad..174bc749395e 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -24,6 +24,7 @@ enum gb_interface_type { #define GB_INTERFACE_QUIRK_FORCED_DISABLE BIT(3) #define GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH BIT(4) #define GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE BIT(5) +#define GB_INTERFACE_QUIRK_NO_PM BIT(6) struct gb_interface { struct device dev; -- cgit v1.2.3-59-g8ed1b From 911415ae6ee29cb76e018e8df8715c743a3a3d63 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 25 Jul 2016 14:38:07 -0700 Subject: greybus: firmware: Add new status types for backend updated request The specification got updated with two more status values, add their support in greybus. As retry isn't really an error, skip printing error messages for it as well. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-management.c | 3 ++- drivers/staging/greybus/greybus_protocols.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index 2efe65cfd612..642a393bd15f 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -350,7 +350,8 @@ static int fw_mgmt_backend_fw_updated_operation(struct gb_operation *op) fw_mgmt->backend_fw_request_id = 0; fw_mgmt->backend_fw_status = request->status; - if (fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_SUCCESS) + if ((fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_SUCCESS) && + (fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_RETRY)) dev_err(fw_mgmt->parent, "failed to load backend firmware: %02x\n", fw_mgmt->backend_fw_status); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 2de5aef54637..e51c2b14875f 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -367,6 +367,8 @@ struct gb_fw_download_release_firmware_request { #define GB_FW_BACKEND_FW_STATUS_FAIL_FETCH 0x03 #define GB_FW_BACKEND_FW_STATUS_FAIL_WRITE 0x04 #define GB_FW_BACKEND_FW_STATUS_INT 0x05 +#define GB_FW_BACKEND_FW_STATUS_RETRY 0x06 +#define GB_FW_BACKEND_FW_STATUS_NOT_SUPPORTED 0x07 /* firmware management interface firmware version request has no payload */ struct gb_fw_mgmt_interface_fw_version_response { -- cgit v1.2.3-59-g8ed1b From 5e10f0047a1b79e6c8ac1ee0e9a0165e7d0bc7d6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 25 Jul 2016 14:38:08 -0700 Subject: greybus: firmware: Add 'status' byte to backend fw version response The backend processor may not be ready to return the version of firmware it is running by the time AP requests for it. The greybus specification is updated to return 1-byte 'status' to return the error type, RETRY is one of them. This patch implements that in greybus now. Note that the version structure was common across interface and backend version requests earlier, but that is changing as well. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-management.c | 46 ++++++++++++++++++++++------- drivers/staging/greybus/greybus_firmware.h | 21 +++++++++++-- drivers/staging/greybus/greybus_protocols.h | 7 +++++ 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index 642a393bd15f..7cbe71d581ac 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -103,7 +103,7 @@ unlock: } static int fw_mgmt_interface_fw_version_operation(struct fw_mgmt *fw_mgmt, - struct fw_mgmt_ioc_get_fw *fw_info) + struct fw_mgmt_ioc_get_intf_version *fw_info) { struct gb_connection *connection = fw_mgmt->connection; struct gb_fw_mgmt_interface_fw_version_response response; @@ -241,7 +241,7 @@ static int fw_mgmt_interface_fw_loaded_operation(struct gb_operation *op) } static int fw_mgmt_backend_fw_version_operation(struct fw_mgmt *fw_mgmt, - struct fw_mgmt_ioc_get_fw *fw_info) + struct fw_mgmt_ioc_get_backend_version *fw_info) { struct gb_connection *connection = fw_mgmt->connection; struct gb_fw_mgmt_backend_fw_version_request request; @@ -269,8 +269,29 @@ static int fw_mgmt_backend_fw_version_operation(struct fw_mgmt *fw_mgmt, return ret; } - fw_info->major = le16_to_cpu(response.major); - fw_info->minor = le16_to_cpu(response.minor); + fw_info->status = response.status; + + /* Reset version as that should be non-zero only for success case */ + fw_info->major = 0; + fw_info->minor = 0; + + switch (fw_info->status) { + case GB_FW_BACKEND_VERSION_STATUS_SUCCESS: + fw_info->major = le16_to_cpu(response.major); + fw_info->minor = le16_to_cpu(response.minor); + break; + case GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE: + case GB_FW_BACKEND_VERSION_STATUS_RETRY: + break; + case GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED: + dev_err(fw_mgmt->parent, + "Firmware with tag %s is not supported by Interface\n", + fw_info->firmware_tag); + break; + default: + dev_err(fw_mgmt->parent, "Invalid status received: %u\n", + fw_info->status); + } return 0; } @@ -387,7 +408,8 @@ static int fw_mgmt_release(struct inode *inode, struct file *file) static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, void __user *buf) { - struct fw_mgmt_ioc_get_fw fw_info; + struct fw_mgmt_ioc_get_intf_version intf_fw_info; + struct fw_mgmt_ioc_get_backend_version backend_fw_info; struct fw_mgmt_ioc_intf_load_and_validate intf_load; struct fw_mgmt_ioc_backend_fw_update backend_update; unsigned int timeout; @@ -399,23 +421,27 @@ static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, switch (cmd) { case FW_MGMT_IOC_GET_INTF_FW: - ret = fw_mgmt_interface_fw_version_operation(fw_mgmt, &fw_info); + ret = fw_mgmt_interface_fw_version_operation(fw_mgmt, + &intf_fw_info); if (ret) return ret; - if (copy_to_user(buf, &fw_info, sizeof(fw_info))) + if (copy_to_user(buf, &intf_fw_info, sizeof(intf_fw_info))) return -EFAULT; return 0; case FW_MGMT_IOC_GET_BACKEND_FW: - if (copy_from_user(&fw_info, buf, sizeof(fw_info))) + if (copy_from_user(&backend_fw_info, buf, + sizeof(backend_fw_info))) return -EFAULT; - ret = fw_mgmt_backend_fw_version_operation(fw_mgmt, &fw_info); + ret = fw_mgmt_backend_fw_version_operation(fw_mgmt, + &backend_fw_info); if (ret) return ret; - if (copy_to_user(buf, &fw_info, sizeof(fw_info))) + if (copy_to_user(buf, &backend_fw_info, + sizeof(backend_fw_info))) return -EFAULT; return 0; diff --git a/drivers/staging/greybus/greybus_firmware.h b/drivers/staging/greybus/greybus_firmware.h index 1b6882186459..82796512075b 100644 --- a/drivers/staging/greybus/greybus_firmware.h +++ b/drivers/staging/greybus/greybus_firmware.h @@ -72,14 +72,29 @@ #define GB_FW_U_BACKEND_FW_STATUS_FAIL_FETCH 0x03 #define GB_FW_U_BACKEND_FW_STATUS_FAIL_WRITE 0x04 #define GB_FW_U_BACKEND_FW_STATUS_INT 0x05 +#define GB_FW_U_BACKEND_FW_STATUS_RETRY 0x06 +#define GB_FW_U_BACKEND_FW_STATUS_NOT_SUPPORTED 0x07 + +#define GB_FW_U_BACKEND_VERSION_STATUS_SUCCESS 0x01 +#define GB_FW_U_BACKEND_VERSION_STATUS_NOT_AVAILABLE 0x02 +#define GB_FW_U_BACKEND_VERSION_STATUS_NOT_SUPPORTED 0x03 +#define GB_FW_U_BACKEND_VERSION_STATUS_RETRY 0x04 +#define GB_FW_U_BACKEND_VERSION_STATUS_FAIL_INT 0x05 /* IOCTL support */ -struct fw_mgmt_ioc_get_fw { +struct fw_mgmt_ioc_get_intf_version { __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; __u16 major; __u16 minor; } __attribute__ ((__packed__)); +struct fw_mgmt_ioc_get_backend_version { + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u16 major; + __u16 minor; + __u8 status; +} __attribute__ ((__packed__)); + struct fw_mgmt_ioc_intf_load_and_validate { __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; __u8 load_method; @@ -94,8 +109,8 @@ struct fw_mgmt_ioc_backend_fw_update { } __attribute__ ((__packed__)); #define FW_MGMT_IOCTL_BASE 'F' -#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_fw) -#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_fw) +#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_intf_version) +#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_backend_version) #define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate) #define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update) #define FW_MGMT_IOC_SET_TIMEOUT_MS _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e51c2b14875f..edbc5dfc448f 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -370,6 +370,12 @@ struct gb_fw_download_release_firmware_request { #define GB_FW_BACKEND_FW_STATUS_RETRY 0x06 #define GB_FW_BACKEND_FW_STATUS_NOT_SUPPORTED 0x07 +#define GB_FW_BACKEND_VERSION_STATUS_SUCCESS 0x01 +#define GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE 0x02 +#define GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED 0x03 +#define GB_FW_BACKEND_VERSION_STATUS_RETRY 0x04 +#define GB_FW_BACKEND_VERSION_STATUS_FAIL_INT 0x05 + /* firmware management interface firmware version request has no payload */ struct gb_fw_mgmt_interface_fw_version_response { __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; @@ -402,6 +408,7 @@ struct gb_fw_mgmt_backend_fw_version_request { struct gb_fw_mgmt_backend_fw_version_response { __le16 major; __le16 minor; + __u8 status; } __packed; /* firmware management backend firmware update request */ -- cgit v1.2.3-59-g8ed1b From 6136cce89ca5d344d5183d36e887f0ff46896f1c Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Mon, 25 Jul 2016 14:38:09 -0700 Subject: greybus: firmware: Update Documentation and sample application Update documentation and sample application to capture the 'status' byte in backend version operation and new error types in backend firmware update operation. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../Documentation/firmware/firmware-management | 30 +++++++++++++---- .../greybus/Documentation/firmware/firmware.c | 39 ++++++++++++++++------ 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/Documentation/firmware/firmware-management b/drivers/staging/greybus/Documentation/firmware/firmware-management index 6b7b6d814507..77d272c0f245 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware-management +++ b/drivers/staging/greybus/Documentation/firmware/firmware-management @@ -93,12 +93,28 @@ Following are the IOCTLs and their data structures available to the user: #define GB_FW_BACKEND_FW_STATUS_FAIL_FETCH 0x03 #define GB_FW_BACKEND_FW_STATUS_FAIL_WRITE 0x04 #define GB_FW_BACKEND_FW_STATUS_INT 0x05 +#define GB_FW_BACKEND_FW_STATUS_RETRY 0x06 +#define GB_FW_BACKEND_FW_STATUS_NOT_SUPPORTED 0x07 -struct fw_mgmt_ioc_get_fw { - __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; - __u16 major; - __u16 minor; -} __packed; +#define GB_FW_BACKEND_VERSION_STATUS_SUCCESS 0x01 +#define GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE 0x02 +#define GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED 0x03 +#define GB_FW_BACKEND_VERSION_STATUS_RETRY 0x04 +#define GB_FW_BACKEND_VERSION_STATUS_FAIL_INT 0x05 + + +struct fw_mgmt_ioc_get_intf_version { + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u16 major; + __u16 minor; +} __attribute__ ((__packed__)); + +struct fw_mgmt_ioc_get_backend_version { + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u16 major; + __u16 minor; + __u8 status; +} __attribute__ ((__packed__)); struct fw_mgmt_ioc_intf_load_and_validate { __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; @@ -114,8 +130,8 @@ struct fw_mgmt_ioc_backend_fw_update { } __packed; #define FW_MGMT_IOCTL_BASE 'S' -#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_fw) -#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_fw) +#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_intf_version) +#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_backend_version) #define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate) #define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update) #define FW_MGMT_IOC_SET_TIMEOUT_MS _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int) diff --git a/drivers/staging/greybus/Documentation/firmware/firmware.c b/drivers/staging/greybus/Documentation/firmware/firmware.c index c557bf1ec493..5170886f8afb 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware.c +++ b/drivers/staging/greybus/Documentation/firmware/firmware.c @@ -71,7 +71,8 @@ static const char *fwdev = FW_DEV_DEFAULT; static int fw_update_type = FW_UPDATE_TYPE_DEFAULT; static int fw_timeout = FW_TIMEOUT_DEFAULT; -static struct fw_mgmt_ioc_get_fw fw_info; +static struct fw_mgmt_ioc_get_intf_version intf_fw_info; +static struct fw_mgmt_ioc_get_backend_version backend_fw_info; static struct fw_mgmt_ioc_intf_load_and_validate intf_load; static struct fw_mgmt_ioc_backend_fw_update backend_update; @@ -87,7 +88,7 @@ static int update_intf_firmware(int fd) /* Get Interface Firmware Version */ printf("Get Interface Firmware Version\n"); - ret = ioctl(fd, FW_MGMT_IOC_GET_INTF_FW, &fw_info); + ret = ioctl(fd, FW_MGMT_IOC_GET_INTF_FW, &intf_fw_info); if (ret < 0) { printf("Failed to get interface firmware version: %s (%d)\n", fwdev, ret); @@ -95,7 +96,8 @@ static int update_intf_firmware(int fd) } printf("Interface Firmware tag (%s), major (%d), minor (%d)\n", - fw_info.firmware_tag, fw_info.major, fw_info.minor); + intf_fw_info.firmware_tag, intf_fw_info.major, + intf_fw_info.minor); /* Try Interface Firmware load over Unipro */ printf("Loading Interface Firmware\n"); @@ -143,34 +145,51 @@ static int update_backend_firmware(int fd) /* Get Backend Firmware Version */ printf("Getting Backend Firmware Version\n"); - fw_info.major = 0; - fw_info.minor = 0; - strncpy((char *)&fw_info.firmware_tag, firmware_tag, + strncpy((char *)&backend_fw_info.firmware_tag, firmware_tag, GB_FIRMWARE_U_TAG_MAX_LEN); - ret = ioctl(fd, FW_MGMT_IOC_GET_BACKEND_FW, &fw_info); +retry_fw_version: + ret = ioctl(fd, FW_MGMT_IOC_GET_BACKEND_FW, &backend_fw_info); if (ret < 0) { printf("Failed to get backend firmware version: %s (%d)\n", fwdev, ret); return -1; } - printf("Backend Firmware tag (%s), major (%d), minor (%d)\n", - fw_info.firmware_tag, fw_info.major, fw_info.minor); + printf("Backend Firmware tag (%s), major (%d), minor (%d), status (%d)\n", + backend_fw_info.firmware_tag, backend_fw_info.major, + backend_fw_info.minor, backend_fw_info.status); + + if (backend_fw_info.status == GB_FW_U_BACKEND_VERSION_STATUS_RETRY) + goto retry_fw_version; + + if ((backend_fw_info.status != GB_FW_U_BACKEND_VERSION_STATUS_SUCCESS) + && (backend_fw_info.status != GB_FW_U_BACKEND_VERSION_STATUS_NOT_AVAILABLE)) { + printf("Failed to get backend firmware version: %s (%d)\n", + fwdev, backend_fw_info.status); + return -1; + } /* Try Backend Firmware Update over Unipro */ printf("Updating Backend Firmware\n"); - backend_update.status = 0; strncpy((char *)&backend_update.firmware_tag, firmware_tag, GB_FIRMWARE_U_TAG_MAX_LEN); +retry_fw_update: + backend_update.status = 0; + ret = ioctl(fd, FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE, &backend_update); if (ret < 0) { printf("Failed to load backend firmware: %s (%d)\n", fwdev, ret); return -1; } + if (backend_update.status == GB_FW_U_BACKEND_FW_STATUS_RETRY) { + printf("Retrying firmware update: %d\n", backend_update.status); + goto retry_fw_update; + } + if (backend_update.status != GB_FW_U_BACKEND_FW_STATUS_SUCCESS) { printf("Load status says loading failed: %d\n", backend_update.status); -- cgit v1.2.3-59-g8ed1b From 7c4a0edb38ba734bd89efbda4262698a58839c26 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Tue, 26 Jul 2016 16:27:28 -0700 Subject: greybus: svc_watchdog: Add sysfs file to change the behavior of bite Currently, AP performs unipro_reset if SVC fails to response to its ping. While this error recovery is best suited for the end-user experience, errors in the UniPro network could potentially go unnoticed by the QA and fishfooders in the development phase of the project. This patch adds an option to trigger a kernel panic so logs can be collected for analysis. Testing Done: - Reproduce issue and observe kernel panic when watchdob_control is changed to 'panic' Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs-bus-greybus | 16 +++++++++++ drivers/staging/greybus/svc.c | 31 ++++++++++++++++++++++ drivers/staging/greybus/svc.h | 6 +++++ drivers/staging/greybus/svc_watchdog.c | 21 +++++++++------ 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index e202eac1f001..c49c81b133bc 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -257,3 +257,19 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: If the SVC watchdog is enabled or not. Writing 0 to this file will disable the watchdog, writing 1 will enable it. + +What: /sys/bus/greybus/devices/N-svc/watchdog_action +Date: July 2016 +KernelVersion: 4.XX +Contact: Greg Kroah-Hartman <greg@kroah.com> +Description: + This attribute indicates the action to be performed upon SVC + watchdog bite. + + The action can be one of the "reset" or "panic". Writing either + one of the "reset" or "panic" will change the behavior of SVC + watchdog bite. Default value is "reset". + + "reset" means the UniPro subsystem is to be reset. + + "panic" means SVC watchdog bite will cause kernel to panic. diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index da9bb1f182f7..1170515a2a4e 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -100,6 +100,36 @@ static ssize_t watchdog_store(struct device *dev, } static DEVICE_ATTR_RW(watchdog); +static ssize_t watchdog_action_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gb_svc *svc = to_gb_svc(dev); + + if (svc->action == GB_SVC_WATCHDOG_BITE_PANIC_KERNEL) + return sprintf(buf, "panic\n"); + else if (svc->action == GB_SVC_WATCHDOG_BITE_RESET_UNIPRO) + return sprintf(buf, "reset\n"); + + return -EINVAL; +} + +static ssize_t watchdog_action_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct gb_svc *svc = to_gb_svc(dev); + + if (sysfs_streq(buf, "panic")) + svc->action = GB_SVC_WATCHDOG_BITE_PANIC_KERNEL; + else if (sysfs_streq(buf, "reset")) + svc->action = GB_SVC_WATCHDOG_BITE_RESET_UNIPRO; + else + return -EINVAL; + + return len; +} +static DEVICE_ATTR_RW(watchdog_action); + static int gb_svc_pwrmon_rail_count_get(struct gb_svc *svc, u8 *value) { struct gb_svc_pwrmon_rail_count_get_response response; @@ -222,6 +252,7 @@ static struct attribute *svc_attrs[] = { &dev_attr_ap_intf_id.attr, &dev_attr_intf_eject.attr, &dev_attr_watchdog.attr, + &dev_attr_watchdog_action.attr, NULL, }; ATTRIBUTE_GROUPS(svc); diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index f632790a54f4..d1d7ef967385 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -20,6 +20,11 @@ enum gb_svc_state { GB_SVC_STATE_SVC_HELLO, }; +enum gb_svc_watchdog_bite { + GB_SVC_WATCHDOG_BITE_RESET_UNIPRO = 0, + GB_SVC_WATCHDOG_BITE_PANIC_KERNEL, +}; + struct gb_svc_watchdog; struct svc_debugfs_pwrmon_rail { @@ -43,6 +48,7 @@ struct gb_svc { u8 protocol_minor; struct gb_svc_watchdog *watchdog; + enum gb_svc_watchdog_bite action; struct dentry *debugfs_dentry; struct svc_debugfs_pwrmon_rail *pwrmon_rails; diff --git a/drivers/staging/greybus/svc_watchdog.c b/drivers/staging/greybus/svc_watchdog.c index 6cd3bac59852..1295cc153af9 100644 --- a/drivers/staging/greybus/svc_watchdog.c +++ b/drivers/staging/greybus/svc_watchdog.c @@ -83,16 +83,21 @@ static void do_work(struct work_struct *work) dev_err(&svc->dev, "SVC ping has returned %d, something is wrong!!!\n", retval); - dev_err(&svc->dev, "Resetting the greybus network, watch out!!!\n"); - INIT_DELAYED_WORK(&reset_work, greybus_reset); - queue_delayed_work(system_wq, &reset_work, HZ/2); + if (svc->action == GB_SVC_WATCHDOG_BITE_PANIC_KERNEL) { + panic("SVC is not responding\n"); + } else if (svc->action == GB_SVC_WATCHDOG_BITE_RESET_UNIPRO) { + dev_err(&svc->dev, "Resetting the greybus network, watch out!!!\n"); - /* - * Disable ourselves, we don't want to trip again unless - * userspace wants us to. - */ - watchdog->enabled = false; + INIT_DELAYED_WORK(&reset_work, greybus_reset); + queue_delayed_work(system_wq, &reset_work, HZ / 2); + + /* + * Disable ourselves, we don't want to trip again unless + * userspace wants us to. + */ + watchdog->enabled = false; + } } /* resubmit our work to happen again, if we are still "alive" */ -- cgit v1.2.3-59-g8ed1b From 9d3717f71c22c4f39723f18ed094c552f4b73146 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Mon, 25 Jul 2016 16:29:20 -0700 Subject: greybus: audio: add runtime pm to enumerated control and DAPM widget There's an issue that the userspace is not able to control both the enumerated control and DAPM widget when audio bundle is in the SUSPEND state. This patch fixes the issue by adding pm_runtime_get/put() calls for the both controls. Testing Done: - Use tinymix to get and put both enumerated control and DAPM widget as the followings, and observe audio bundle is able to wake up from suspend. $ tinymix "GB 3 PB source" 1 $ tinymix "GB 3 PB source" GB 3 PB source: AIF1 >AIF2 $ tinymix "GB 3 AIF1_RX MUX" 2 $ tinymix "GB 3 AIF1_RX MUX" GB 3 AIF1_RX MUX: Stereo Left >Right Reported-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_topology.c | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 17ba0a028426..3c7c786d3b03 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -555,6 +555,7 @@ static int gbcodec_enum_ctl_get(struct snd_kcontrol *kcontrol, struct gb_audio_ctl_elem_value gbvalue; struct gbaudio_module_info *module; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_bundle *bundle; module = find_gb_module(gb, kcontrol->id.name); if (!module) @@ -564,8 +565,17 @@ static int gbcodec_enum_ctl_get(struct snd_kcontrol *kcontrol, if (ctl_id < 0) return -EINVAL; + bundle = to_gb_bundle(module->dev); + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); + + gb_pm_runtime_put_autosuspend(bundle); + if (ret) { dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, __func__, kcontrol->id.name); @@ -589,6 +599,7 @@ static int gbcodec_enum_ctl_put(struct snd_kcontrol *kcontrol, struct gb_audio_ctl_elem_value gbvalue; struct gbaudio_module_info *module; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); + struct gb_bundle *bundle; module = find_gb_module(gb, kcontrol->id.name); if (!module) @@ -609,8 +620,17 @@ static int gbcodec_enum_ctl_put(struct snd_kcontrol *kcontrol, ucontrol->value.enumerated.item[1]; } + bundle = to_gb_bundle(module->dev); + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); + + gb_pm_runtime_put_autosuspend(bundle); + if (ret) { dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, __func__, kcontrol->id.name); @@ -698,6 +718,7 @@ static int gbcodec_enum_dapm_ctl_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = widget->codec; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct gb_bundle *bundle; module = find_gb_module(gb, kcontrol->id.name); if (!module) @@ -707,8 +728,17 @@ static int gbcodec_enum_dapm_ctl_get(struct snd_kcontrol *kcontrol, if (ctl_id < 0) return -EINVAL; + bundle = to_gb_bundle(module->dev); + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); + + gb_pm_runtime_put_autosuspend(bundle); + if (ret) { dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, __func__, kcontrol->id.name); @@ -736,6 +766,7 @@ static int gbcodec_enum_dapm_ctl_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = widget->codec; struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct gb_bundle *bundle; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -749,8 +780,17 @@ static int gbcodec_enum_dapm_ctl_put(struct snd_kcontrol *kcontrol, return -EINVAL; change = 0; + bundle = to_gb_bundle(module->dev); + + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); + + gb_pm_runtime_put_autosuspend(bundle); + if (ret) { dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, __func__, kcontrol->id.name); @@ -782,8 +822,15 @@ static int gbcodec_enum_dapm_ctl_put(struct snd_kcontrol *kcontrol, } if (change) { + ret = gb_pm_runtime_get_sync(bundle); + if (ret) + return ret; + ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, GB_AUDIO_INVALID_INDEX, &gbvalue); + + gb_pm_runtime_put_autosuspend(bundle); + if (ret) { dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, -- cgit v1.2.3-59-g8ed1b From 563c742adab959362aca106248b4ab24c4186217 Mon Sep 17 00:00:00 2001 From: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Date: Wed, 27 Jul 2016 11:47:01 -0700 Subject: greybus: camera: Rename debug and metadata mbus formats Change ARA prefix to GB_CAM for greyus camera specific formats. Change-Id: I1a0552516e8ea727c48085c59fc49f2409a89486 Signed-off-by: Gjorgji Rosikopulos <grosikopulos@mm-sol.com> Signed-off-by: Blagovest Borisov Kolenichev <kolenichev_blagovest@projectara.com> Acked-by: Laurent Pinchart <laurent.pinchart@linaro.org> --- drivers/staging/greybus/camera.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index e0719e2db4e6..0e7f64392bbd 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -113,12 +113,12 @@ static const struct gb_camera_fmt_info gb_fmt_info[] = { .bpp = 0, }, { - .mbus_code = V4L2_MBUS_FMT_ARA_METADATA_1X8, + .mbus_code = V4L2_MBUS_FMT_GB_CAM_METADATA_1X8, .gb_format = 0x41, .bpp = 0, }, { - .mbus_code = V4L2_MBUS_FMT_ARA_DEBUG_DATA_1X8, + .mbus_code = V4L2_MBUS_FMT_GB_CAM_DEBUG_DATA_1X8, .gb_format = 0x42, .bpp = 0, }, -- cgit v1.2.3-59-g8ed1b From 20de72f5dee480e72d82d702e709a07217c0900f Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Thu, 28 Jul 2016 13:56:44 +0530 Subject: greybus: arche-apb-ctrl: Rename ara,init-disable => arche,init-disable Inline with other properties used for arche-platform driver, rename ara,init-disable => arche,init-disable. Testing Done: Boot tested on EVT2 platform Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-apb-ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index cae56fc0a6e5..049d496a41f4 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -391,7 +391,7 @@ static int arche_apb_ctrl_probe(struct platform_device *pdev) /* Initially set APB to OFF state */ apb->state = ARCHE_PLATFORM_STATE_OFF; /* Check whether device needs to be enabled on boot */ - if (of_property_read_bool(pdev->dev.of_node, "ara,init-disable")) + if (of_property_read_bool(pdev->dev.of_node, "arche,init-disable")) apb->init_disabled = true; platform_set_drvdata(pdev, apb); -- cgit v1.2.3-59-g8ed1b From 6c298035a14d30e94861833df5c490feda2c73db Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 28 Jul 2016 11:40:52 +0200 Subject: greybus: Documentation/sysfs: replace Ara references Refer to the MDK as GMP MDK. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Documentation/sysfs-bus-greybus | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus index c49c81b133bc..2e998966cbe1 100644 --- a/drivers/staging/greybus/Documentation/sysfs-bus-greybus +++ b/drivers/staging/greybus/Documentation/sysfs-bus-greybus @@ -223,7 +223,7 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The AP interface ID, a 1-byte non-zero integer which defines the position of the AP module on the frame. - The interface positions are defined in the ARA + The interface positions are defined in the GMP Module Developer Kit. What: /sys/bus/greybus/devices/N-svc/endo_id @@ -233,7 +233,7 @@ Contact: Greg Kroah-Hartman <greg@kroah.com> Description: The Endo ID, which is a 2-byte hexadecimal value defined by the Endo layout scheme, documented in - the ARA Module Developer Kit. + the GMP Module Developer Kit. What: /sys/bus/greybus/devices/N-svc/intf_eject Date: October 2015 -- cgit v1.2.3-59-g8ed1b From 23931ffb9ac892a51cdef4eefdfc255e3f810a8b Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 28 Jul 2016 11:40:53 +0200 Subject: greybus: replace Ara references Replace all occurrences of the term "Ara" with "GMP" in core. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/bootrom.c | 2 +- drivers/staging/greybus/interface.c | 28 ++++++++++++++-------------- drivers/staging/greybus/interface.h | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/staging/greybus/bootrom.c b/drivers/staging/greybus/bootrom.c index e7cfa80b6b0b..5f90721bcc51 100644 --- a/drivers/staging/greybus/bootrom.c +++ b/drivers/staging/greybus/bootrom.c @@ -114,7 +114,7 @@ static void bootrom_es2_fixup_vid_pid(struct gb_bootrom *bootrom) struct gb_interface *intf = connection->bundle->intf; int ret; - if (!(intf->quirks & GB_INTERFACE_QUIRK_NO_ARA_IDS)) + if (!(intf->quirks & GB_INTERFACE_QUIRK_NO_GMP_IDS)) return; ret = gb_operation_sync(connection, GB_BOOTROM_TYPE_GET_VID_PID, diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index faa623919757..19f5c71e078d 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -31,11 +31,11 @@ #define DME_DDBL1_MANUFACTURERID 0x5003 #define DME_DDBL1_PRODUCTID 0x5004 -#define DME_TOSHIBA_ARA_VID 0x6000 -#define DME_TOSHIBA_ARA_PID 0x6001 -#define DME_TOSHIBA_ARA_SN0 0x6002 -#define DME_TOSHIBA_ARA_SN1 0x6003 -#define DME_TOSHIBA_ARA_INIT_STATUS 0x6101 +#define DME_TOSHIBA_GMP_VID 0x6000 +#define DME_TOSHIBA_GMP_PID 0x6001 +#define DME_TOSHIBA_GMP_SN0 0x6002 +#define DME_TOSHIBA_GMP_SN1 0x6003 +#define DME_TOSHIBA_GMP_INIT_STATUS 0x6101 /* DDBL1 Manufacturer and Product ids */ #define TOSHIBA_DMID 0x0126 @@ -60,7 +60,7 @@ static int gb_interface_read_ara_dme(struct gb_interface *intf) /* * Unless this is a Toshiba bridge, bail out until we have defined - * standard Ara attributes. + * standard GMP attributes. */ if (intf->ddbl1_manufacturer_id != TOSHIBA_DMID) { dev_err(&intf->dev, "unknown manufacturer %08x\n", @@ -68,21 +68,21 @@ static int gb_interface_read_ara_dme(struct gb_interface *intf) return -ENODEV; } - ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_ARA_VID, + ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_VID, &intf->vendor_id); if (ret) return ret; - ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_ARA_PID, + ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_PID, &intf->product_id); if (ret) return ret; - ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_ARA_SN0, &sn0); + ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_SN0, &sn0); if (ret) return ret; - ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_ARA_SN1, &sn1); + ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_SN1, &sn1); if (ret) return ret; @@ -111,7 +111,7 @@ static int gb_interface_read_dme(struct gb_interface *intf) if (intf->ddbl1_manufacturer_id == TOSHIBA_DMID && intf->ddbl1_product_id == TOSHIBA_ES2_BRIDGE_DPID) { - intf->quirks |= GB_INTERFACE_QUIRK_NO_ARA_IDS; + intf->quirks |= GB_INTERFACE_QUIRK_NO_GMP_IDS; intf->quirks |= GB_INTERFACE_QUIRK_NO_INIT_STATUS; } @@ -377,7 +377,7 @@ static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS) attr = DME_T_TST_SRC_INCREMENT; else - attr = DME_TOSHIBA_ARA_INIT_STATUS; + attr = DME_TOSHIBA_GMP_INIT_STATUS; ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr, DME_SELECTOR_INDEX_NULL, &value); @@ -793,7 +793,7 @@ struct device_type greybus_interface_type = { }; /* - * A Greybus module represents a user-replaceable component on an Ara + * A Greybus module represents a user-replaceable component on a GMP * phone. An interface is the physical connection on that module. A * module may have more than one interface. * @@ -1282,7 +1282,7 @@ int gb_interface_add(struct gb_interface *intf) switch (intf->type) { case GB_INTERFACE_TYPE_GREYBUS: - dev_info(&intf->dev, "Ara VID=0x%08x, PID=0x%08x\n", + dev_info(&intf->dev, "GMP VID=0x%08x, PID=0x%08x\n", intf->vendor_id, intf->product_id); /* fall-through */ case GB_INTERFACE_TYPE_UNIPRO: diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 174bc749395e..03299d2a8be5 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -20,7 +20,7 @@ enum gb_interface_type { #define GB_INTERFACE_QUIRK_NO_CPORT_FEATURES BIT(0) #define GB_INTERFACE_QUIRK_NO_INIT_STATUS BIT(1) -#define GB_INTERFACE_QUIRK_NO_ARA_IDS BIT(2) +#define GB_INTERFACE_QUIRK_NO_GMP_IDS BIT(2) #define GB_INTERFACE_QUIRK_FORCED_DISABLE BIT(3) #define GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH BIT(4) #define GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE BIT(5) -- cgit v1.2.3-59-g8ed1b From fcba5d04c87c5c0174f159d26b295a46bfb6da58 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 27 Jul 2016 16:37:20 +0200 Subject: greybus: es2: fix arpc response-allocation error handling The wrong pointer was checked for allocation failures when allocating the ARPC response buffer, something which would lead to allocation failures going undetected. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index afc2d6c8ec5c..130549f9d74d 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1100,7 +1100,7 @@ static struct arpc *arpc_alloc(void *payload, u16 size, u8 type) goto err_free_rpc; rpc->resp = kzalloc(sizeof(*rpc->resp), GFP_KERNEL); - if (!rpc->req) + if (!rpc->resp) goto err_free_req; rpc->req->type = type; -- cgit v1.2.3-59-g8ed1b From 178457e171bf1ae7a90a6b0aa6a404fec129e8c4 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 27 Jul 2016 16:37:21 +0200 Subject: greybus: es2: fix arpc error message Add missing le16_to_cpu() to an ARPC error message in the receive path, and also use %u to print the unsigned id. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 130549f9d74d..a22cb67a9076 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1256,8 +1256,8 @@ static void arpc_in_callback(struct urb *urb) spin_lock_irqsave(&es2->arpc_lock, flags); rpc = arpc_find(es2, resp->id); if (!rpc) { - dev_err(dev, "invalid arpc response id received: %d\n", - resp->id); + dev_err(dev, "invalid arpc response id received: %u\n", + le16_to_cpu(resp->id)); spin_unlock_irqrestore(&es2->arpc_lock, flags); goto exit; } -- cgit v1.2.3-59-g8ed1b From c0219cbf72418d5355e319a0d787dc2671df9d4f Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 27 Jul 2016 16:37:22 +0200 Subject: greybus: es2: fix arpc active-list corruption Fix ARPC active-list corruption due to incorrect list-interface usage. The corruption manifested itself as ARPC timeouts due to expected responses not being recognised whenever more than one ARPC was active. This could be seen for example when two interfaces were being runtime suspended in parallel: [ 165.739896] usb 1-1.1: invalid arpc response id received: 13 [ 165.794743] greybus 1-5.5: gb_interface_refclk_set - 0 [ 166.241202] usb 1-1.1: failed to execute ARPC: -110 Fortunately the impact of this bug has so far been limited to such timeouts and error messages due to ARPC currently only being used for CPort reset in the connection tear-down path. Reported-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index a22cb67a9076..2020187b5d16 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1142,7 +1142,7 @@ static void arpc_add(struct es2_ap_dev *es2, struct arpc *rpc) { rpc->active = true; rpc->req->id = cpu_to_le16(es2->arpc_id_cycle++); - list_add_tail(&es2->arpcs, &rpc->list); + list_add_tail(&rpc->list, &es2->arpcs); } static void arpc_del(struct es2_ap_dev *es2, struct arpc *rpc) -- cgit v1.2.3-59-g8ed1b From ea2ff95ab4e178559e63b1ab5a174dd4dd6b4019 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Tue, 2 Aug 2016 15:34:46 -0700 Subject: greybus: interface: delete control device upon enable failure There is an issue that when an interface failed to be enabled due to timesync failure, a previously added control device is not deleted as part of the error clean-up. This causes a leak in the sysfs file when the interface is disabled. This would eventually cause this particular interface to be unable to register future control device even after unipro_reset. See failure logs below: [ 906.495261] greybus 1-3.3: failed to add to timesync: -19 [ 906.516497] greybus 1-3.3: failed to re-enable interface: -19 [ 907.016016] greybus 1-3.3: Interface removed ... [ 1623.677343] ------------[ cut here ]------------ [ 1623.681116] WARNING: at kernel/arche/fs/sysfs/dir.c:530 sysfs_add_one+0x98/0xb4() [ 1623.681128] sysfs: cannot create duplicate filename '/bus/greybus/devices/1-3.3.ctrl' [ 1623.681252] Call trace: [ 1623.681265] [<ffffffc000207b40>] dump_backtrace+0x0/0x268 [ 1623.681272] [<ffffffc000207db8>] show_stack+0x10/0x1c [ 1623.681284] [<ffffffc000ccb890>] dump_stack+0x1c/0x28 [ 1623.681295] [<ffffffc00021f9dc>] warn_slowpath_common+0x74/0x9c [ 1623.681301] [<ffffffc00021fa60>] warn_slowpath_fmt+0x5c/0x80 [ 1623.681307] [<ffffffc000366624>] sysfs_add_one+0x94/0xb4 [ 1623.681315] [<ffffffc0003670b4>] sysfs_do_create_link_sd+0x100/0x1c8 [ 1623.681320] [<ffffffc0003671a8>] sysfs_create_link+0x2c/0x38 [ 1623.681332] [<ffffffc0005d5890>] bus_add_device+0xd8/0x190 [ 1623.681338] [<ffffffc0005d39ec>] device_add+0x2b4/0x604 [ 1623.681349] [<ffffffbffc006dfc>] gb_control_add+0x10/0x40 [greybus] [ 1623.681362] [<ffffffbffc003dac>] gb_interface_enable+0x20c/0x3b8 [greybus] [ 1623.681373] [<ffffffbffc002a30>] gb_module_add+0x124/0x174 [greybus] [ 1623.681385] [<ffffffbffc0082cc>] gb_svc_intf_set_power_mode+0xdd4/0xfe8 [greybus] [ 1623.681394] [<ffffffc00023888c>] process_one_work+0x268/0x3c8 [ 1623.681400] [<ffffffc000239a64>] worker_thread+0x204/0x358 [ 1623.681410] [<ffffffc00023f43c>] kthread+0xb8/0xc4 [ 1623.681414] ---[ end trace 44489577dd9220db ]--- [ 1623.681818] greybus 1-3.3.ctrl: failed to register control device: -17 Testing Done: - Continuous unipro_reset stress test Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Jeffrey Carlyle <jcarlyle@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 19f5c71e078d..76569f8b086e 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -1160,7 +1160,7 @@ int gb_interface_enable(struct gb_interface *intf) ret = gb_timesync_interface_add(intf); if (ret) { dev_err(&intf->dev, "failed to add to timesync: %d\n", ret); - goto err_destroy_bundles; + goto err_del_control; } pm_runtime_use_autosuspend(&intf->dev); @@ -1186,6 +1186,8 @@ int gb_interface_enable(struct gb_interface *intf) return 0; +err_del_control: + gb_control_del(intf->control); err_destroy_bundles: list_for_each_entry_safe(bundle, tmp, &intf->bundles, links) gb_bundle_destroy(bundle); -- cgit v1.2.3-59-g8ed1b From b6fc2876a060fdd89f6dd55a4923580983092b0f Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Mon, 1 Aug 2016 20:51:38 -0700 Subject: greybus: svc_watchdog: use schedule_delayed_work helper Instead of using the queue_delayed_work call for delayed work on system_wq, use the schedule_delayed_work helper to be more consistent on the style. Testing Done: - Check SVC watchdog is pining after the change. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc_watchdog.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/svc_watchdog.c b/drivers/staging/greybus/svc_watchdog.c index 1295cc153af9..3729460fb954 100644 --- a/drivers/staging/greybus/svc_watchdog.c +++ b/drivers/staging/greybus/svc_watchdog.c @@ -90,7 +90,7 @@ static void do_work(struct work_struct *work) dev_err(&svc->dev, "Resetting the greybus network, watch out!!!\n"); INIT_DELAYED_WORK(&reset_work, greybus_reset); - queue_delayed_work(system_wq, &reset_work, HZ / 2); + schedule_delayed_work(&reset_work, HZ / 2); /* * Disable ourselves, we don't want to trip again unless @@ -102,8 +102,7 @@ static void do_work(struct work_struct *work) /* resubmit our work to happen again, if we are still "alive" */ if (watchdog->enabled) - queue_delayed_work(system_wq, &watchdog->work, - SVC_WATCHDOG_PERIOD); + schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD); } int gb_svc_watchdog_create(struct gb_svc *svc) @@ -178,8 +177,7 @@ int gb_svc_watchdog_enable(struct gb_svc *svc) return 0; watchdog->enabled = true; - queue_delayed_work(system_wq, &watchdog->work, - SVC_WATCHDOG_PERIOD); + schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD); return 0; } -- cgit v1.2.3-59-g8ed1b From 7aa278b771505f8fb85406a891af0b7743710620 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 2 Aug 2016 13:18:30 +0100 Subject: greybus: timesync: Implement a retry mechanism It's possible the AP could miss an incoming SVC timesync pulse i.e. the AP could have interrupts switched off for long enough that one SVC GPIO strobe ends up over-lapping another one. TimeSync should be able to deal with this type of transitory failure by retrying a failed synchronous TimeSync resync up to 'n' number of times. For this patch 'n' has been set to five, which is a hand-wavy choice that 'feels' right. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index ec0a51d6378d..744ae0c9d586 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -34,6 +34,9 @@ #define GB_TIMESYNC_KTIME_UPDATE msecs_to_jiffies(1000) #define GB_TIMESYNC_MAX_KTIME_CONVERSION 15 +/* Maximum number of times we'll retry a failed synchronous sync */ +#define GB_TIMESYNC_MAX_RETRIES 5 + /* Reported nanoseconds/femtoseconds per clock */ static u64 gb_timesync_ns_per_clock; static u64 gb_timesync_fs_per_clock; @@ -870,19 +873,26 @@ int gb_timesync_schedule_synchronous(struct gb_interface *interface) { int ret; struct gb_timesync_svc *timesync_svc; + int retries; if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) return 0; mutex_lock(&gb_timesync_svc_list_mutex); - timesync_svc = gb_timesync_find_timesync_svc(interface->hd); - if (!timesync_svc) { - ret = -ENODEV; - goto done; - } + for (retries = 0; retries < GB_TIMESYNC_MAX_RETRIES; retries++) { + timesync_svc = gb_timesync_find_timesync_svc(interface->hd); + if (!timesync_svc) { + ret = -ENODEV; + goto done; + } - ret = __gb_timesync_schedule_synchronous(timesync_svc, + ret = __gb_timesync_schedule_synchronous(timesync_svc, GB_TIMESYNC_STATE_INIT); + if (!ret) + break; + } + if (ret && retries == GB_TIMESYNC_MAX_RETRIES) + ret = -ETIMEDOUT; done: mutex_unlock(&gb_timesync_svc_list_mutex); return ret; -- cgit v1.2.3-59-g8ed1b From 5cb74976b9025b1a5e6e3bfd15fde0afbaf98ae4 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 2 Aug 2016 13:18:28 +0100 Subject: greybus: timesync: Ensure parallel synchronous calls succeed The guard for initiating a new synchronization operation should allow for that resync to happen in every single state except for INVALID. This patch fixes by ensuring the guard does just that. With local testing it was possible to break a sync to a Module. This hasn't been observed in a buglog but should be fixed anyway. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Acked-by: David Lin <dtwlin@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index 744ae0c9d586..561bbea9ae17 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -806,8 +806,7 @@ static int gb_timesync_schedule(struct gb_timesync_svc *timesync_svc, int state) return -EINVAL; mutex_lock(×ync_svc->mutex); - if (timesync_svc->state == GB_TIMESYNC_STATE_INACTIVE || - timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE) { + if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) { gb_timesync_set_state_atomic(timesync_svc, state); } else { ret = -ENODEV; -- cgit v1.2.3-59-g8ed1b From ca9551f6815efd4e7bbdee0cd2c71c368ebdf92c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 2 Aug 2016 13:18:29 +0100 Subject: greybus: timesync: Printout strobe count on sync failure If we failed to synchronize the FrameTime it would be useful to know how many of the expected strobes arrived, for example a value of 0/5 would indicate the SVC was completely dead but a value of 4/5 would indicate one GPIO pulse got lost i.e. the AP was too slow reacting to an interrupt and completely missed one of the strobe events. In either case the actual number of strobes is a useful thing to print out. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Alex Elder <elder@linaro.org> Reviewed-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/timesync.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/timesync.c b/drivers/staging/greybus/timesync.c index 561bbea9ae17..2e68af7dea6d 100644 --- a/drivers/staging/greybus/timesync.c +++ b/drivers/staging/greybus/timesync.c @@ -772,7 +772,8 @@ static void gb_timesync_worker(struct work_struct *work) case GB_TIMESYNC_STATE_WAIT_SVC: dev_err(×ync_svc->svc->dev, - "timeout SVC strobe completion\n"); + "timeout SVC strobe completion %d/%d\n", + timesync_svc->strobe, GB_TIMESYNC_MAX_STROBES); gb_timesync_teardown(timesync_svc); break; -- cgit v1.2.3-59-g8ed1b From ccc57e20d1cc54b6536a4e63f6da91f42b65000f Mon Sep 17 00:00:00 2001 From: Mark Greer <mgreer@animalcreek.com> Date: Tue, 2 Aug 2016 20:36:07 -0700 Subject: greybus: audio: apbridgea: Remove GET_TX/RX_DELAY message types The 'AUDIO_APBRIDGEA_TYPE_GET_TX_DELAY' and 'AUDIO_APBRIDGEA_TYPE_GET_RX_DELAY' message types have been removed from the AP <-> APBrigdeA Audio Protocol so remove them from the code. Do not coalesce the message type numbers to prevent compatibility issues between the AP and APBridgeA. Testing Done: Played music using a speaker module Signed-off-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_apbridgea.c | 16 ---------------- drivers/staging/greybus/audio_apbridgea.h | 22 ++-------------------- drivers/staging/greybus/audio_codec.h | 4 ---- 3 files changed, 2 insertions(+), 40 deletions(-) diff --git a/drivers/staging/greybus/audio_apbridgea.c b/drivers/staging/greybus/audio_apbridgea.c index 45d3522789a8..1b4252d5d255 100644 --- a/drivers/staging/greybus/audio_apbridgea.c +++ b/drivers/staging/greybus/audio_apbridgea.c @@ -84,14 +84,6 @@ int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, } EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_tx_data_size); -int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, - __u16 i2s_port, __u32 *delay) -{ - /* TODO: implement */ - return -EOPNOTSUPP; -} -EXPORT_SYMBOL_GPL(gb_audio_apbridgea_get_tx_delay); - int gb_audio_apbridgea_prepare_tx(struct gb_connection *connection, __u16 i2s_port) { @@ -158,14 +150,6 @@ int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, } EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_rx_data_size); -int gb_audio_apbridgea_get_rx_delay(struct gb_connection *connection, - __u16 i2s_port, __u32 *delay) -{ - /* TODO: implement */ - return -EOPNOTSUPP; -} -EXPORT_SYMBOL_GPL(gb_audio_apbridgea_get_rx_delay); - int gb_audio_apbridgea_prepare_rx(struct gb_connection *connection, __u16 i2s_port) { diff --git a/drivers/staging/greybus/audio_apbridgea.h b/drivers/staging/greybus/audio_apbridgea.h index a48f815bc56b..b94cb05c89e4 100644 --- a/drivers/staging/greybus/audio_apbridgea.h +++ b/drivers/staging/greybus/audio_apbridgea.h @@ -47,13 +47,13 @@ #define AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT 0x02 #define AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT 0x03 #define AUDIO_APBRIDGEA_TYPE_SET_TX_DATA_SIZE 0x04 -#define AUDIO_APBRIDGEA_TYPE_GET_TX_DELAY 0x05 + /* 0x05 unused */ #define AUDIO_APBRIDGEA_TYPE_PREPARE_TX 0x06 #define AUDIO_APBRIDGEA_TYPE_START_TX 0x07 #define AUDIO_APBRIDGEA_TYPE_STOP_TX 0x08 #define AUDIO_APBRIDGEA_TYPE_SHUTDOWN_TX 0x09 #define AUDIO_APBRIDGEA_TYPE_SET_RX_DATA_SIZE 0x0a -#define AUDIO_APBRIDGEA_TYPE_GET_RX_DELAY 0x0b + /* 0x0b unused */ #define AUDIO_APBRIDGEA_TYPE_PREPARE_RX 0x0c #define AUDIO_APBRIDGEA_TYPE_START_RX 0x0d #define AUDIO_APBRIDGEA_TYPE_STOP_RX 0x0e @@ -115,15 +115,6 @@ struct audio_apbridgea_set_tx_data_size_request { __le16 size; } __packed; -struct audio_apbridgea_get_tx_delay_request { - struct audio_apbridgea_hdr hdr; -} __packed; - -struct audio_apbridgea_get_tx_delay_response { - struct audio_apbridgea_hdr hdr; - __le16 delay; -} __packed; - struct audio_apbridgea_prepare_tx_request { struct audio_apbridgea_hdr hdr; } __packed; @@ -146,15 +137,6 @@ struct audio_apbridgea_set_rx_data_size_request { __le16 size; } __packed; -struct audio_apbridgea_get_rx_delay_request { - struct audio_apbridgea_hdr hdr; -} __packed; - -struct audio_apbridgea_get_rx_delay_response { - struct audio_apbridgea_hdr hdr; - __le16 delay; -} __packed; - struct audio_apbridgea_prepare_rx_request { struct audio_apbridgea_hdr hdr; } __packed; diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index f4936f18647b..40de7e736fa8 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -260,8 +260,6 @@ extern int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, __u8 direction); extern int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, __u16 i2s_port, __u16 size); -extern int gb_audio_apbridgea_get_tx_delay(struct gb_connection *connection, - __u16 i2s_port, __u32 *delay); extern int gb_audio_apbridgea_prepare_tx(struct gb_connection *connection, __u16 i2s_port); extern int gb_audio_apbridgea_start_tx(struct gb_connection *connection, @@ -272,8 +270,6 @@ extern int gb_audio_apbridgea_shutdown_tx(struct gb_connection *connection, __u16 i2s_port); extern int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, __u16 i2s_port, __u16 size); -extern int gb_audio_apbridgea_get_rx_delay(struct gb_connection *connection, - __u16 i2s_port, __u32 *delay); extern int gb_audio_apbridgea_prepare_rx(struct gb_connection *connection, __u16 i2s_port); extern int gb_audio_apbridgea_start_rx(struct gb_connection *connection, -- cgit v1.2.3-59-g8ed1b From 8f60ce76a4eb0911ddd5833badd0d27bb5dfb894 Mon Sep 17 00:00:00 2001 From: Mark Greer <mgreer@animalcreek.com> Date: Tue, 2 Aug 2016 20:30:28 -0700 Subject: greybus: audio: Remove GET_TX/RX_DELAY message types The 'GB_AUDIO_TYPE_GET_TX_DELAY' and 'GB_AUDIO_TYPE_GET_RX_DELAY' are no longer a part of the Greybus Audio Device Class Protocol so remove support for them. The message numbers are not coalesced to prevent compatibility issues between the AP and the module. Testing Done: Played music using a speaker module Signed-off-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.h | 4 --- drivers/staging/greybus/audio_gb.c | 40 ----------------------------- drivers/staging/greybus/greybus_protocols.h | 20 ++------------- 3 files changed, 2 insertions(+), 62 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 40de7e736fa8..1646b2ca0f0d 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -235,16 +235,12 @@ extern int gb_audio_gb_set_pcm(struct gb_connection *connection, uint8_t sig_bits); extern int gb_audio_gb_set_tx_data_size(struct gb_connection *connection, uint16_t data_cport, uint16_t size); -extern int gb_audio_gb_get_tx_delay(struct gb_connection *connection, - uint16_t data_cport, uint32_t *delay); extern int gb_audio_gb_activate_tx(struct gb_connection *connection, uint16_t data_cport); extern int gb_audio_gb_deactivate_tx(struct gb_connection *connection, uint16_t data_cport); extern int gb_audio_gb_set_rx_data_size(struct gb_connection *connection, uint16_t data_cport, uint16_t size); -extern int gb_audio_gb_get_rx_delay(struct gb_connection *connection, - uint16_t data_cport, uint32_t *delay); extern int gb_audio_gb_activate_rx(struct gb_connection *connection, uint16_t data_cport); extern int gb_audio_gb_deactivate_rx(struct gb_connection *connection, diff --git a/drivers/staging/greybus/audio_gb.c b/drivers/staging/greybus/audio_gb.c index 167683d74f1a..a2f1c92e7445 100644 --- a/drivers/staging/greybus/audio_gb.c +++ b/drivers/staging/greybus/audio_gb.c @@ -161,26 +161,6 @@ int gb_audio_gb_set_tx_data_size(struct gb_connection *connection, } EXPORT_SYMBOL_GPL(gb_audio_gb_set_tx_data_size); -int gb_audio_gb_get_tx_delay(struct gb_connection *connection, - uint16_t data_cport, uint32_t *delay) -{ - struct gb_audio_get_tx_delay_request req; - struct gb_audio_get_tx_delay_response resp; - int ret; - - req.data_cport = cpu_to_le16(data_cport); - - ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TX_DELAY, - &req, sizeof(req), &resp, sizeof(resp)); - if (ret) - return ret; - - *delay = le32_to_cpu(resp.delay); - - return 0; -} -EXPORT_SYMBOL_GPL(gb_audio_gb_get_tx_delay); - int gb_audio_gb_activate_tx(struct gb_connection *connection, uint16_t data_cport) { @@ -218,26 +198,6 @@ int gb_audio_gb_set_rx_data_size(struct gb_connection *connection, } EXPORT_SYMBOL_GPL(gb_audio_gb_set_rx_data_size); -int gb_audio_gb_get_rx_delay(struct gb_connection *connection, - uint16_t data_cport, uint32_t *delay) -{ - struct gb_audio_get_rx_delay_request req; - struct gb_audio_get_rx_delay_response resp; - int ret; - - req.data_cport = cpu_to_le16(data_cport); - - ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_RX_DELAY, - &req, sizeof(req), &resp, sizeof(resp)); - if (ret) - return ret; - - *delay = le32_to_cpu(resp.delay); - - return 0; -} -EXPORT_SYMBOL_GPL(gb_audio_gb_get_rx_delay); - int gb_audio_gb_activate_rx(struct gb_connection *connection, uint16_t data_cport) { diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index edbc5dfc448f..7628c1851292 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1935,11 +1935,11 @@ struct gb_lights_get_flash_fault_response { #define GB_AUDIO_TYPE_GET_PCM 0x08 #define GB_AUDIO_TYPE_SET_PCM 0x09 #define GB_AUDIO_TYPE_SET_TX_DATA_SIZE 0x0a -#define GB_AUDIO_TYPE_GET_TX_DELAY 0x0b + /* 0x0b unused */ #define GB_AUDIO_TYPE_ACTIVATE_TX 0x0c #define GB_AUDIO_TYPE_DEACTIVATE_TX 0x0d #define GB_AUDIO_TYPE_SET_RX_DATA_SIZE 0x0e -#define GB_AUDIO_TYPE_GET_RX_DELAY 0x0f + /* 0x0f unused */ #define GB_AUDIO_TYPE_ACTIVATE_RX 0x10 #define GB_AUDIO_TYPE_DEACTIVATE_RX 0x11 #define GB_AUDIO_TYPE_JACK_EVENT 0x12 @@ -2227,14 +2227,6 @@ struct gb_audio_set_tx_data_size_request { __le16 size; } __packed; -struct gb_audio_get_tx_delay_request { - __le16 data_cport; -} __packed; - -struct gb_audio_get_tx_delay_response { - __le32 delay; -} __packed; - struct gb_audio_activate_tx_request { __le16 data_cport; } __packed; @@ -2248,14 +2240,6 @@ struct gb_audio_set_rx_data_size_request { __le16 size; } __packed; -struct gb_audio_get_rx_delay_request { - __le16 data_cport; -} __packed; - -struct gb_audio_get_rx_delay_response { - __le32 delay; -} __packed; - struct gb_audio_activate_rx_request { __le16 data_cport; } __packed; -- cgit v1.2.3-59-g8ed1b From 6e8fc8c4c939730e4550a3f70c640f6f0a281079 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 3 Aug 2016 14:09:28 +0200 Subject: greybus: remove unused protocol-version messages Remove the unused protocol-version messages. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 7628c1851292..d8f8194bd9dc 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -99,15 +99,6 @@ struct gb_operation_msg_hdr { #define GB_REQUEST_TYPE_PING 0x00 #define GB_REQUEST_TYPE_INVALID 0x7f -struct gb_protocol_version_request { - __u8 major; - __u8 minor; -} __packed; - -struct gb_protocol_version_response { - __u8 major; - __u8 minor; -} __packed; /* Control Protocol */ -- cgit v1.2.3-59-g8ed1b From 081ee14ab4452bbf7bab9b4df61519bb48a44375 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 3 Aug 2016 14:09:29 +0200 Subject: greybus: control: remove some braces Remove some no-longer-needed braces around an error path. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 34493d572684..658f8a641ee2 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -174,9 +174,8 @@ int gb_control_mode_switch_operation(struct gb_control *control) return -ENOMEM; ret = gb_operation_request_send_sync(operation); - if (ret) { + if (ret) dev_err(&control->dev, "failed to send mode switch: %d\n", ret); - } gb_operation_put(operation); -- cgit v1.2.3-59-g8ed1b From 06d8b7d9cf37c323aae23bfe97eabdebf0e94ce7 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 3 Aug 2016 14:09:30 +0200 Subject: greybus: connection: fix offloaded-connection ping cport id The host-device cport_ping callback expects the AP side cport id, but was incorrectly passed the interface cport id. Note that no host-device driver currently implements this callback, and that it is soon even to be removed. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 634c8b1e92f3..ce8ba9ca8e5e 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -527,7 +527,7 @@ static int gb_connection_ping(struct gb_connection *connection) if (!hd->driver->cport_ping) return 0; - ret = hd->driver->cport_ping(hd, connection->intf_cport_id); + ret = hd->driver->cport_ping(hd, connection->hd_cport_id); } else { ret = gb_connection_ping_operation(connection); } -- cgit v1.2.3-59-g8ed1b From c468999e6acd3e8330db7d18dca320f8b21bf074 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 3 Aug 2016 14:09:31 +0200 Subject: greybus: es2: clean up ARPC symbol names Add a _req suffix to request message structures, and a _TYPE_ infix to request type defines. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 4 ++-- drivers/staging/greybus/greybus_protocols.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 2020187b5d16..0850b591500f 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -609,7 +609,7 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) { struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev; - struct arpc_cport_reset req; + struct arpc_cport_reset_req req; int retval; int result = 0; @@ -621,7 +621,7 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) } req.cport_id = cpu_to_le16(cport_id); - retval = arpc_sync(es2, ARPC_CPORT_RESET, &req, sizeof(req), + retval = arpc_sync(es2, ARPC_TYPE_CPORT_RESET, &req, sizeof(req), &result, ES2_TIMEOUT); if (retval == -EREMOTEIO) { dev_err(&udev->dev, "failed to reset cport %u: %d\n", cport_id, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index d8f8194bd9dc..8484f05fc427 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -292,9 +292,9 @@ struct arpc_response_message { __u8 result; /* Result of RPC */ } __packed; -#define ARPC_CPORT_RESET 0x00 +#define ARPC_TYPE_CPORT_RESET 0x00 -struct arpc_cport_reset { +struct arpc_cport_reset_req { __le16 cport_id; } __packed; -- cgit v1.2.3-59-g8ed1b From 4ae2d962637e4a002d1f0884dceec800c79b3ea4 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 3 Aug 2016 14:09:32 +0200 Subject: greybus: es2: always set result value Make sure to always set the result value for ARPC instead of forcing every caller to do it in order to avoid compiler warnings. The ARPC result should still be ignored unless arpc_sync returns -EREMOTEIO. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 0850b591500f..c31124d7defc 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -611,7 +611,7 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) struct usb_device *udev = es2->usb_dev; struct arpc_cport_reset_req req; int retval; - int result = 0; + int result; switch (cport_id) { case GB_SVC_CPORT_ID: @@ -1184,6 +1184,8 @@ static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, unsigned long flags; int retval; + *result = 0; + rpc = arpc_alloc(payload, size, type); if (!rpc) return -ENOMEM; @@ -1205,11 +1207,12 @@ static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, goto out_arpc_del; } - *result = rpc->resp->result; - if (*result) + if (rpc->resp->result) { retval = -EREMOTEIO; - else + *result = rpc->resp->result; + } else { retval = 0; + } out_arpc_del: spin_lock_irqsave(&es2->arpc_lock, flags); -- cgit v1.2.3-59-g8ed1b From 121bae2bec47a9944049f2583b1d31f479110f0f Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 3 Aug 2016 14:09:33 +0200 Subject: greybus: es2: allow ARPC result to be ignored Allow user of ARPC to pass a NULL pointer for the ARPC result. For simple requests there may only be one error code for remote errors, or the caller may simply not care to differentiate them. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index c31124d7defc..8d0e52d46a2b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1184,7 +1184,8 @@ static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, unsigned long flags; int retval; - *result = 0; + if (result) + *result = 0; rpc = arpc_alloc(payload, size, type); if (!rpc) @@ -1209,7 +1210,8 @@ static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, if (rpc->resp->result) { retval = -EREMOTEIO; - *result = rpc->resp->result; + if (result) + *result = rpc->resp->result; } else { retval = 0; } -- cgit v1.2.3-59-g8ed1b From 54f34e1e4d065ea38f531341018b41c590d71861 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 3 Aug 2016 14:09:34 +0200 Subject: greybus: es2: add define for ARPC CPort requests Add dedicated define for ARPC CPort requests instead of using the default timeout for USB vendor requests. We still allow responses to take 500 ms to arrive, but note that this adds on top of the 500ms already allowed for a requests to be acknowledged. This should probably be tightened up at some point. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 8d0e52d46a2b..66fd92d84e2a 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -19,6 +19,10 @@ #include "kernel_ver.h" #include "connection.h" + +/* Default timeout for ARPC CPort requests */ +#define ES2_ARPC_CPORT_TIMEOUT 500 + /* Fixed CPort numbers */ #define ES2_CPORT_CDSI0 16 #define ES2_CPORT_CDSI1 17 @@ -622,7 +626,7 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id) req.cport_id = cpu_to_le16(cport_id); retval = arpc_sync(es2, ARPC_TYPE_CPORT_RESET, &req, sizeof(req), - &result, ES2_TIMEOUT); + &result, ES2_ARPC_CPORT_TIMEOUT); if (retval == -EREMOTEIO) { dev_err(&udev->dev, "failed to reset cport %u: %d\n", cport_id, result); -- cgit v1.2.3-59-g8ed1b From 60793c9be9a140e2102a0f64a38d34e9b28a1357 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 3 Aug 2016 14:09:35 +0200 Subject: greybus: es2: rename USB vendor-request timeout define Give the USB vendor-request timeout define a more descriptive name. Also drop the since-long obsolete comments about allowing "the SVC to do something" and "SVC messages go down our control pipe". Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 66fd92d84e2a..3b8d81ef9726 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -20,6 +20,9 @@ #include "connection.h" +/* Default timeout for USB vendor requests. */ +#define ES2_USB_CTRL_TIMEOUT 500 + /* Default timeout for ARPC CPort requests */ #define ES2_ARPC_CPORT_TIMEOUT 500 @@ -204,8 +207,6 @@ static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id) return es2->cport_to_ep[cport_id]; } -#define ES2_TIMEOUT 500 /* 500 ms for the SVC to do something */ - /* Disable for now until we work all of this out to keep a warning-free build */ #if 0 /* Test if the endpoints pair is already mapped to a cport */ @@ -250,7 +251,7 @@ static int map_cport_to_ep(struct es2_ap_dev *es2, 0x00, 0x00, (char *)cport_to_ep, sizeof(*cport_to_ep), - ES2_TIMEOUT); + ES2_USB_CTRL_TIMEOUT); if (retval == sizeof(*cport_to_ep)) retval = 0; kfree(cport_to_ep); @@ -280,7 +281,7 @@ static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd) cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0, 0, data, size, ES2_TIMEOUT); + 0, 0, data, size, ES2_USB_CTRL_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "%s: return error %d\n", __func__, retval); else @@ -716,7 +717,7 @@ static int cport_enable(struct gb_host_device *hd, u16 cport_id, GB_APB_REQUEST_CPORT_FLAGS, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, - req, sizeof(*req), ES2_TIMEOUT); + req, sizeof(*req), ES2_USB_CTRL_TIMEOUT); if (ret != sizeof(*req)) { dev_err(&udev->dev, "failed to set cport flags for port %d\n", cport_id); @@ -754,7 +755,7 @@ static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) GB_APB_REQUEST_LATENCY_TAG_EN, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES2_TIMEOUT); + 0, ES2_USB_CTRL_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n", @@ -772,7 +773,7 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) GB_APB_REQUEST_LATENCY_TAG_DIS, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES2_TIMEOUT); + 0, ES2_USB_CTRL_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n", @@ -790,7 +791,7 @@ static int cport_features_enable(struct gb_host_device *hd, u16 cport_id) GB_APB_REQUEST_CPORT_FEAT_EN, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES2_TIMEOUT); + 0, ES2_USB_CTRL_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "Cannot enable CPort features for cport %u: %d\n", cport_id, retval); @@ -807,7 +808,7 @@ static int cport_features_disable(struct gb_host_device *hd, u16 cport_id) GB_APB_REQUEST_CPORT_FEAT_DIS, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES2_TIMEOUT); + 0, ES2_USB_CTRL_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "Cannot disable CPort features for cport %u: %d\n", @@ -835,7 +836,7 @@ static int timesync_enable(struct gb_host_device *hd, u8 count, REQUEST_TIMESYNC_ENABLE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, request, - sizeof(*request), ES2_TIMEOUT); + sizeof(*request), ES2_USB_CTRL_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "Cannot enable timesync %d\n", retval); @@ -853,7 +854,7 @@ static int timesync_disable(struct gb_host_device *hd) REQUEST_TIMESYNC_DISABLE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, NULL, - 0, ES2_TIMEOUT); + 0, ES2_USB_CTRL_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "Cannot disable timesync %d\n", retval); @@ -878,7 +879,7 @@ static int timesync_authoritative(struct gb_host_device *hd, u64 *frame_time) REQUEST_TIMESYNC_AUTHORITATIVE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, request, - sizeof(*request), ES2_TIMEOUT); + sizeof(*request), ES2_USB_CTRL_TIMEOUT); if (retval < 0) dev_err(&udev->dev, "Cannot timesync authoritative out %d\n", retval); @@ -901,7 +902,8 @@ static int timesync_get_last_event(struct gb_host_device *hd, u64 *frame_time) REQUEST_TIMESYNC_GET_LAST_EVENT, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, response_frame_time, - sizeof(*response_frame_time), ES2_TIMEOUT); + sizeof(*response_frame_time), + ES2_USB_CTRL_TIMEOUT); if (retval != sizeof(*response_frame_time)) { dev_err(&udev->dev, "Cannot get last TimeSync event: %d\n", @@ -1168,7 +1170,7 @@ static int arpc_send(struct es2_ap_dev *es2, struct arpc *rpc, int timeout) USB_RECIP_INTERFACE, 0, 0, rpc->req, le16_to_cpu(rpc->req->size), - ES2_TIMEOUT); + ES2_USB_CTRL_TIMEOUT); if (retval != le16_to_cpu(rpc->req->size)) { dev_err(&udev->dev, "failed to send ARPC request %d: %d\n", @@ -1288,7 +1290,6 @@ static void apb_log_get(struct es2_ap_dev *es2, char *buf) { int retval; - /* SVC messages go down our control pipe */ do { retval = usb_control_msg(es2->usb_dev, usb_rcvctrlpipe(es2->usb_dev, 0), @@ -1297,7 +1298,7 @@ static void apb_log_get(struct es2_ap_dev *es2, char *buf) 0x00, 0x00, buf, APB1_LOG_MSG_SIZE, - ES2_TIMEOUT); + ES2_USB_CTRL_TIMEOUT); if (retval > 0) kfifo_in(&es2->apb_log_fifo, buf, retval); } while (retval > 0); @@ -1424,7 +1425,7 @@ static int apb_get_cport_count(struct usb_device *udev) GB_APB_REQUEST_CPORT_COUNT, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, cport_count, - sizeof(*cport_count), ES2_TIMEOUT); + sizeof(*cport_count), ES2_USB_CTRL_TIMEOUT); if (retval != sizeof(*cport_count)) { dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", retval); -- cgit v1.2.3-59-g8ed1b From 8ef0b5383110977d81746cd0b09c3877d51c3a67 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <hiremath_vaibhav@projectara.com> Date: Wed, 3 Aug 2016 17:15:38 +0530 Subject: greybus: arche-platform: Reset SVC & APB only if turned off by SUSPEND_PREPARE There is possible race condition in arche platform driver for maintaining of ->state field, when shared with timesync driver. Assume device booted fine, all connected modules have been enumerated correctly. As part of suspend-resume operation we have pm_notifier callback, where we turn off SVC & APB and coldboot on resume. In the process of resume, all modules gets enumerated again, and timesync driver does come into picture everytime. So when timesync driver requests arche-platform to change the state to TIMESYNC and in the middle of sync operation, if suspend gets triggered, then execution lands into arche-platform->pm_notifier callback, leading to race condition in the driver, where it checks for (state != ACTIVE) in PM_SUSPEND_PREPARE and returns, but in PM_POST_SUSPEND it just simply coldboots SVC & APB, which would inbalance all resources (including IRQ). So we need a add check in PM_POST_SUSPEND, to make sure that, we only coldboot devices if they are in to off state, i.e. if (state != OFF) then return. Testing Done: Done regressive suspend/resume testing on EVT2 platform. Note that, I some time hit issue. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-platform.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index cbe8bdcabceb..adec1fdcb5bb 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -548,6 +548,9 @@ static int arche_platform_pm_notifier(struct notifier_block *notifier, arche_platform_poweroff_seq(arche_pdata); break; case PM_POST_SUSPEND: + if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF) + break; + arche_platform_wd_irq_en(arche_pdata); arche_platform_coldboot_seq(arche_pdata); break; -- cgit v1.2.3-59-g8ed1b From 921dbe52b40b2573d9a0e8337c768930bef25fb4 Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Date: Thu, 28 Jul 2016 13:47:37 +0530 Subject: greybus: arche-platform: Add support for SPI bus sharing for Mihi In case of Mihi, SPI bus is shared between APB1 and APB2 SPI ROMs, so their FW flashing must be sequential and arche-platform driver should make sure that they are mutual exclusive in nature. So this patch adds certain restrictions to the user of the arche-platform driver, - User can no longer flash APB1 and APB2 SPI ROM in parallel - SPI bus becomes an resource, so user must claim it by moving respective APB device into FW_FLASHING mode and release it by exiting FW_FLASHING mode. User can exit FW_FLASHING mode by switching to any other modes (ACTIVE, OFF, STANDBY). - If APB1 is in FW_FLASHING mode, APB2 can no longer enter into FW_FLASHING mode. User will get -EBUSY. Having said that, while APB1 is into FW_FLASHING mode, APB2 can independently boot from its own SPI ROM. Testing Done: Tested by simulating usecase on EVT2. - Made sure that APB1 and APB2 FW_FLASHING mode is mutual exclusive in nature. Confirmed that an attempt on second device return -EBUSY. - Added simulating code, where printed state of dummy gpio for spi-en and verified that it shows right pin status for both APBs Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arche-apb-ctrl.c | 43 ++++++++++++++++++++++++++++++++ drivers/staging/greybus/arche-platform.c | 25 ++++++------------- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 049d496a41f4..59d9d422cf04 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -42,6 +42,10 @@ struct arche_apb_ctrl_drvdata { struct pinctrl *pinctrl; struct pinctrl_state *pin_default; + + /* V2: SPI Bus control */ + int spi_en_gpio; + bool spi_en_polarity_high; }; /* @@ -73,6 +77,10 @@ static int coldboot_seq(struct platform_device *pdev) /* Hold APB in reset state */ assert_reset(apb->resetn_gpio); + if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && + gpio_is_valid(apb->spi_en_gpio)) + devm_gpio_free(dev, apb->spi_en_gpio); + /* Enable power to APB */ if (!IS_ERR(apb->vcore)) { ret = regulator_enable(apb->vcore); @@ -128,6 +136,23 @@ static int fw_flashing_seq(struct platform_device *pdev) return ret; } + if (gpio_is_valid(apb->spi_en_gpio)) { + unsigned long flags; + + if (apb->spi_en_polarity_high) + flags = GPIOF_OUT_INIT_HIGH; + else + flags = GPIOF_OUT_INIT_LOW; + + ret = devm_gpio_request_one(dev, apb->spi_en_gpio, + flags, "apb_spi_en"); + if (ret) { + dev_err(dev, "Failed requesting SPI bus en gpio %d\n", + apb->spi_en_gpio); + return ret; + } + } + /* for flashing device should be in reset state */ assert_reset(apb->resetn_gpio); apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING; @@ -137,6 +162,7 @@ static int fw_flashing_seq(struct platform_device *pdev) static int standby_boot_seq(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); if (apb->init_disabled) @@ -147,6 +173,10 @@ static int standby_boot_seq(struct platform_device *pdev) apb->state == ARCHE_PLATFORM_STATE_OFF) return 0; + if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && + gpio_is_valid(apb->spi_en_gpio)) + devm_gpio_free(dev, apb->spi_en_gpio); + /* * As per WDM spec, do nothing * @@ -162,11 +192,16 @@ static int standby_boot_seq(struct platform_device *pdev) static void poweroff_seq(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF) return; + if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && + gpio_is_valid(apb->spi_en_gpio)) + devm_gpio_free(dev, apb->spi_en_gpio); + /* disable the clock */ if (gpio_is_valid(apb->clk_en_gpio)) gpio_set_value(apb->clk_en_gpio, 0); @@ -369,6 +404,14 @@ static int apb_ctrl_get_devtree_data(struct platform_device *pdev, return PTR_ERR(apb->pin_default); } + /* Only applicable for platform >= V2 */ + apb->spi_en_gpio = of_get_named_gpio(np, "spi-en-gpio", 0); + if (apb->spi_en_gpio >= 0) { + if (of_property_read_bool(pdev->dev.of_node, + "spi-en-active-high")) + apb->spi_en_polarity_high = true; + } + return 0; } diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index adec1fdcb5bb..9d9048e6aed3 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -215,18 +215,6 @@ static int apb_cold_boot(struct device *dev, void *data) return 0; } -static int apb_fw_flashing_state(struct device *dev, void *data) -{ - int ret; - - ret = apb_ctrl_fw_flashing(dev); - if (ret) - dev_warn(dev, "failed to switch to fw flashing state\n"); - - /*Child nodes are independent, so do not exit coldboot operation */ - return 0; -} - static int apb_poweroff(struct device *dev, void *data) { apb_ctrl_poweroff(dev); @@ -485,17 +473,18 @@ retry: if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) goto exit; - /* First we want to make sure we power off everything - * and then enter FW flashing state */ - device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); - + /* + * Here we only control SVC. + * + * In case of FW_FLASHING mode we do not want to control + * APBs, as in case of V2, SPI bus is shared between both + * the APBs. So let user chose which APB he wants to flash. + */ arche_platform_poweroff_seq(arche_pdata); ret = arche_platform_fw_flashing_seq(arche_pdata); if (ret) goto exit; - - device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); } else { dev_err(arche_pdata->dev, "unknown state\n"); ret = -EINVAL; -- cgit v1.2.3-59-g8ed1b From 90579d4b577154606a54cab9771e15a41867b79b Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:28 +0530 Subject: greybus: audio: Remove un-necessary goto statement For most of the helper functions, goto statement is widely used. It was originally used with an intent of single exit point for the function with some cleanup required. This is no more the case. So, simplify code by avoiding un-necessary gotos. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index d7679e6bc364..9b98e9c282da 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -64,7 +64,8 @@ static int gbaudio_module_disable(struct gbaudio_codec_info *codec, if (module_state == GBAUDIO_CODEC_SHUTDOWN) { dev_dbg(codec->dev, "%s: module already configured\n", module->name); - goto func_exit; + mutex_unlock(&codec->lock); + return 0; } /* find the dai */ @@ -72,8 +73,8 @@ static int gbaudio_module_disable(struct gbaudio_codec_info *codec, if (!data) { dev_err(codec->dev, "%s:%s DATA connection missing\n", dai_name, module->name); - ret = -ENODEV; - goto func_exit; + mutex_unlock(&codec->lock); + return -ENODEV; } if (codec_state > GBAUDIO_CODEC_HWPARAMS) { data_cport = data->connection->intf_cport_id; @@ -162,7 +163,8 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, if (module_state == codec_state) { dev_dbg(codec->dev, "%s: module already configured\n", module->name); - goto func_exit; + mutex_unlock(&codec->lock); + return 0; } /* find the dai */ @@ -170,8 +172,8 @@ static int gbaudio_module_enable(struct gbaudio_codec_info *codec, if (!data) { dev_err(codec->dev, "%s:%s DATA connection missing\n", dai_name, module->name); - ret = -ENODEV; - goto func_exit; + mutex_unlock(&codec->lock); + return -ENODEV; } /* register cport */ @@ -754,8 +756,8 @@ static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) if (!data) { dev_err(dai->dev, "%s:%s DATA connection missing\n", dai->name, module->name); - ret = -ENODEV; - goto func_exit; + mutex_unlock(&codec->lock); + return -ENODEV; } if (!mute && !stream) {/* start playback */ @@ -792,9 +794,8 @@ static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) module->name, mute ? "Mute" : "Unmute", stream ? "Capture" : "Playback", ret); -func_exit: mutex_unlock(&codec->lock); - return 0; + return ret; } static struct snd_soc_dai_ops gbcodec_dai_ops = { -- cgit v1.2.3-59-g8ed1b From ce9413062f8e2366916beebbfa8e73c6ff1a2c8c Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:29 +0530 Subject: greybus: audio: Enable audio path based on control switch state only As per current implementation, audio data is played from each individual SPK module connected to endo frame. This is not a valid requirement in case of capture/headset path. So, provide a mechanism to enable individual module path based on it's control switch state. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 334 +++++----------------------------- 1 file changed, 50 insertions(+), 284 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 9b98e9c282da..0bcd3d455f0c 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -323,11 +323,6 @@ EXPORT_SYMBOL(gbaudio_module_update); static int gbcodec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret = 0; - __u16 i2s_port, cportid; - int state; - struct gbaudio_data_connection *data; - struct gbaudio_module_info *module; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); mutex_lock(&codec->lock); @@ -338,164 +333,26 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, return -ENODEV; } - state = codec->stream[substream->stream].state; - list_for_each_entry(module, &codec->module_list, list) { - /* find the dai */ - data = find_data(module, dai->name); - if (!data) { - dev_err(dai->dev, "%s:%s DATA connection missing\n", - dai->name, module->name); - continue; - } - - /* register cport */ - i2s_port = 0; /* fixed for now */ - cportid = data->connection->hd_cport_id; - switch (substream->stream) { - case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_apbridgea_register_cport( - data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_RX); - break; - case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_apbridgea_register_cport( - data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_TX); - break; - default: - dev_err(dai->dev, "Inavlid stream\n"); - mutex_unlock(&codec->lock); - return -EINVAL; - } - dev_dbg(dai->dev, "Register %s:%d DAI, ret:%d\n", dai->name, - cportid, ret); - state = GBAUDIO_CODEC_STARTUP; - module->ctrlstate[substream->stream] = state; - dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); - } - codec->stream[substream->stream].state = state; + codec->stream[substream->stream].state = GBAUDIO_CODEC_STARTUP; codec->stream[substream->stream].dai_name = dai->name; mutex_unlock(&codec->lock); /* to prevent suspend in case of active audio */ pm_stay_awake(dai->dev); - return ret; -} - -static int gbmodule_shutdown_tx(struct gbaudio_module_info *module, - struct gbaudio_data_connection *data, - int codec_state, struct device *dev) -{ - int ret, module_state; - __u16 i2s_port, cportid; - - module_state = module->ctrlstate[0]; - if (module_state == GBAUDIO_CODEC_SHUTDOWN) { - dev_dbg(dev, "%s: module already configured\n", - module->name); - return 0; - } - - /* deactivate */ - cportid = data->connection->intf_cport_id; - if (module_state >= GBAUDIO_CODEC_PREPARE) { - ret = gb_audio_gb_deactivate_tx(module->mgmt_connection, - cportid); - if (ret) - return ret; - } - - /* unregister cport */ - i2s_port = 0; /* fixed for now */ - cportid = data->connection->hd_cport_id; - ret = gb_audio_apbridgea_unregister_cport(data->connection, i2s_port, - cportid, - AUDIO_APBRIDGEA_DIRECTION_TX); - - return ret; -} - -static int gbmodule_shutdown_rx(struct gbaudio_module_info *module, - struct gbaudio_data_connection *data, - int codec_state, struct device *dev) -{ - int ret, module_state; - __u16 i2s_port, cportid; - - module_state = module->ctrlstate[1]; - if (module_state == GBAUDIO_CODEC_SHUTDOWN) { - dev_dbg(dev, "%s: module already configured\n", - module->name); - return 0; - } - - /* deactivate */ - cportid = data->connection->intf_cport_id; - if (module_state >= GBAUDIO_CODEC_PREPARE) { - ret = gb_audio_gb_deactivate_rx(module->mgmt_connection, - cportid); - if (ret) - return ret; - } - - /* unregister cport */ - i2s_port = 0; /* fixed for now */ - cportid = data->connection->hd_cport_id; - ret = gb_audio_apbridgea_unregister_cport(data->connection, i2s_port, - cportid, - AUDIO_APBRIDGEA_DIRECTION_RX); - - return ret; + return 0; } static void gbcodec_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret, state; - struct gbaudio_module_info *module; - struct gbaudio_data_connection *data; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); mutex_lock(&codec->lock); - if (list_empty(&codec->module_list)) { - dev_err(codec->dev, "No codec module available\n"); - codec->stream[substream->stream].state = GBAUDIO_CODEC_SHUTDOWN; - codec->stream[substream->stream].dai_name = NULL; - mutex_unlock(&codec->lock); - pm_relax(dai->dev); - return; - } + if (list_empty(&codec->module_list)) + dev_info(codec->dev, "No codec module available during shutdown\n"); - state = codec->stream[substream->stream].state; - list_for_each_entry(module, &codec->module_list, list) { - /* find the dai */ - data = find_data(module, dai->name); - if (!data) { - dev_err(dai->dev, "%s:%s DATA connection missing\n", - dai->name, module->name); - continue; - } - - switch (substream->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - ret = gbmodule_shutdown_tx(module, data, state, - dai->dev); - break; - case SNDRV_PCM_STREAM_CAPTURE: - ret = gbmodule_shutdown_rx(module, data, state, - dai->dev); - break; - } - dev_dbg(dai->dev, "Unregister %s DAI, ret:%d\n", dai->name, - ret); - state = GBAUDIO_CODEC_SHUTDOWN; - module->ctrlstate[substream->stream] = state; - dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); - } - codec->stream[substream->stream].state = state; + codec->stream[substream->stream].state = GBAUDIO_CODEC_SHUTDOWN; codec->stream[substream->stream].dai_name = NULL; mutex_unlock(&codec->lock); pm_relax(dai->dev); @@ -509,10 +366,8 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, int ret; uint8_t sig_bits, channels; uint32_t format, rate; - uint16_t data_cport; struct gbaudio_module_info *module; struct gbaudio_data_connection *data; - int state; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); mutex_lock(&codec->lock); @@ -551,122 +406,37 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, } format = GB_AUDIO_PCM_FMT_S16_LE; - state = codec->stream[substream->stream].state; + /* find the data connection */ list_for_each_entry(module, &codec->module_list, list) { - /* find the data connection */ data = find_data(module, dai->name); - if (!data) { - dev_err(dai->dev, "%s:%s DATA connection missing\n", - dai->name, module->name); - continue; - } - - data_cport = data->connection->intf_cport_id; - /* XXX check impact of sig_bit - * it should not change ideally - */ - dev_dbg(dai->dev, - "cport:%d, rate:%d, channel %d, format %d, sig_bits:%d\n", - data_cport, rate, channels, format, sig_bits); - ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, - format, rate, channels, sig_bits); - if (ret) { - dev_err_ratelimited(dai->dev, "%d: Error during set_pcm\n", ret); - goto func_exit; - } - if (state < GBAUDIO_CODEC_HWPARAMS) { - ret = gb_audio_apbridgea_set_config(data->connection, 0, - AUDIO_APBRIDGEA_PCM_FMT_16, - AUDIO_APBRIDGEA_PCM_RATE_48000, - 6144000); - if (ret) { - dev_err_ratelimited(dai->dev, - "%d: Error during set_config\n", ret); - goto func_exit; - } - } - state = GBAUDIO_CODEC_HWPARAMS; - module->ctrlstate[substream->stream] = state; - dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); + if (data) + break; } - codec->stream[substream->stream].state = state; - codec->stream[substream->stream].format = format; - codec->stream[substream->stream].rate = rate; - codec->stream[substream->stream].channels = channels; - codec->stream[substream->stream].sig_bits = sig_bits; - -func_exit: - mutex_unlock(&codec->lock); - return ret; -} - -static int gbmodule_prepare_tx(struct gbaudio_module_info *module, - struct gbaudio_data_connection *data, - int codec_state, struct device *dev) -{ - int ret; - uint16_t data_cport; - data_cport = data->connection->intf_cport_id; - ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection, data_cport, - 192); - if (ret) { - dev_err_ratelimited(dev, "%d:Error during set_tx_data_size, cport:%d\n", - ret, data_cport); - return ret; - } - if (codec_state < GBAUDIO_CODEC_PREPARE) { - ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0, - 192); - if (ret) { - dev_err_ratelimited(dev, - "%d:Error during apbridgea set_tx_data_size, cport\n", - ret); - return ret; - } + if (!data) { + dev_err(dai->dev, "DATA connection missing\n"); + mutex_unlock(&codec->lock); + return -EINVAL; } - ret = gb_audio_gb_activate_tx(module->mgmt_connection, - data_cport); - if (ret) - dev_err_ratelimited(dev, "%s:Error during activate stream,%d\n", - module->name, ret); - - return ret; -} - -static int gbmodule_prepare_rx(struct gbaudio_module_info *module, - struct gbaudio_data_connection *data, - int codec_state, struct device *dev) -{ - int ret; - uint16_t data_cport; - data_cport = data->connection->intf_cport_id; - - ret = gb_audio_gb_set_rx_data_size(module->mgmt_connection, data_cport, - 192); + ret = gb_audio_apbridgea_set_config(data->connection, 0, + AUDIO_APBRIDGEA_PCM_FMT_16, + AUDIO_APBRIDGEA_PCM_RATE_48000, + 6144000); if (ret) { - dev_err_ratelimited(dev, "%d:Error during set_rx_data_size, cport:%d\n", - ret, data_cport); + dev_err_ratelimited(dai->dev, "%d: Error during set_config\n", + ret); + mutex_unlock(&codec->lock); return ret; } - if (codec_state < GBAUDIO_CODEC_PREPARE) { - ret = gb_audio_apbridgea_set_rx_data_size(data->connection, 0, - 192); - if (ret) { - dev_err_ratelimited(dev, - "%d:Error during apbridgea_set_rx_data_size\n", - ret); - return ret; - } - } - ret = gb_audio_gb_activate_rx(module->mgmt_connection, - data_cport); - if (ret) - dev_err_ratelimited(dev, "%s:Error during activate stream,%d\n", - module->name, ret); + codec->stream[substream->stream].state = GBAUDIO_CODEC_HWPARAMS; + codec->stream[substream->stream].format = format; + codec->stream[substream->stream].rate = rate; + codec->stream[substream->stream].channels = channels; + codec->stream[substream->stream].sig_bits = sig_bits; - return ret; + mutex_unlock(&codec->lock); + return 0; } static int gbcodec_prepare(struct snd_pcm_substream *substream, @@ -674,7 +444,6 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, { int ret; struct gbaudio_module_info *module; - int state; struct gbaudio_data_connection *data; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); @@ -686,41 +455,38 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, return -ENODEV; } - state = codec->stream[substream->stream].state; list_for_each_entry(module, &codec->module_list, list) { /* find the dai */ data = find_data(module, dai->name); - if (!data) { - dev_err(dai->dev, "%s:%s DATA connection missing\n", - dai->name, module->name); - continue; - } - - switch (substream->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - ret = gbmodule_prepare_tx(module, data, state, - dai->dev); - break; - case SNDRV_PCM_STREAM_CAPTURE: - ret = gbmodule_prepare_rx(module, data, state, - dai->dev); + if (data) break; - } - if (ret == -ENODEV) - continue; - if (ret) { - goto func_exit; - } + } + if (!data) { + dev_err(dai->dev, "DATA connection missing\n"); + mutex_unlock(&codec->lock); + return -ENODEV; + } - state = GBAUDIO_CODEC_PREPARE; - module->ctrlstate[substream->stream] = state; - dev_dbg(dai->dev, "%s: state:%d\n", module->name, state); + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0, + 192); + break; + case SNDRV_PCM_STREAM_CAPTURE: + ret = gb_audio_apbridgea_set_rx_data_size(data->connection, 0, + 192); + break; + } + if (ret) { + mutex_unlock(&codec->lock); + dev_err_ratelimited(dai->dev, "set_data_size failed:%d\n", + ret); + return ret; } - codec->stream[substream->stream].state = state; -func_exit: + codec->stream[substream->stream].state = GBAUDIO_CODEC_PREPARE; mutex_unlock(&codec->lock); - return ret; + return 0; } static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) -- cgit v1.2.3-59-g8ed1b From aaef32a6cc552d50b86fcf9c5b2105f08c048cdc Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:30 +0530 Subject: greybus: audio: Split helper APIs based on stream direction Now, module is enabled/disabled based on widget event only and not during startup/shutdown callbacks. Thus, we needn't compare codec_state in enable/disable module helper APIs. Also, these can be further simplified based on stream direction. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 405 +++++++++++++++++++--------------- 1 file changed, 228 insertions(+), 177 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 0bcd3d455f0c..70b9a797afc8 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -40,239 +40,296 @@ static int find_stream(const char *name) return stream; } -static int gbaudio_module_disable(struct gbaudio_codec_info *codec, - struct gbaudio_module_info *module, - int dir) +static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, + struct gbaudio_module_info *module) { - int ret = 0; - uint16_t data_cport, cportid, i2s_port; - int codec_state, module_state; + int module_state, ret = 0; + uint16_t data_cport, i2s_port, cportid; + uint8_t sig_bits, channels; + uint32_t format, rate; struct gbaudio_data_connection *data; const char *dai_name; - mutex_lock(&codec->lock); + dai_name = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].dai_name; + format = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].format; + channels = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].channels; + rate = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].rate; + sig_bits = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].sig_bits; - codec_state = codec->stream[dir].state; - if (codec_state == GBAUDIO_CODEC_SHUTDOWN) { - mutex_unlock(&codec->lock); - return 0; + module_state = module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK]; + + /* find the dai */ + data = find_data(module, dai_name); + if (!data) { + dev_err(module->dev, "%s:DATA connection missing\n", dai_name); + return -ENODEV; + } + + /* register cport */ + if (module_state < GBAUDIO_CODEC_STARTUP) { + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_register_cport(data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); + if (ret) { + dev_err_ratelimited(module->dev, + "reg_cport failed:%d\n", ret); + return ret; + } + module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + GBAUDIO_CODEC_STARTUP; + dev_dbg(module->dev, "Dynamic Register %s:%d DAI\n", dai_name, + cportid); + } + + /* hw_params */ + if (module_state < GBAUDIO_CODEC_HWPARAMS) { + data_cport = data->connection->intf_cport_id; + ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, + format, rate, channels, sig_bits); + if (ret) { + dev_err_ratelimited(module->dev, "set_pcm failed:%d\n", + ret); + return ret; + } + module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + GBAUDIO_CODEC_HWPARAMS; + dev_dbg(module->dev, "Dynamic hw_params %s:%d DAI\n", dai_name, + data_cport); + } + + /* prepare */ + if (module_state < GBAUDIO_CODEC_PREPARE) { + data_cport = data->connection->intf_cport_id; + ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection, + data_cport, 192); + if (ret) { + dev_err_ratelimited(module->dev, + "set_tx_data_size failed:%d\n", + ret); + return ret; + } + ret = gb_audio_gb_activate_tx(module->mgmt_connection, + data_cport); + if (ret) { + dev_err_ratelimited(module->dev, + "activate_tx failed:%d\n", ret); + return ret; + } + module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + GBAUDIO_CODEC_PREPARE; + dev_dbg(module->dev, "Dynamic prepare %s:%d DAI\n", dai_name, + data_cport); } - dai_name = codec->stream[dir].dai_name; + return 0; +} + +static int gbaudio_module_disable_tx(struct gbaudio_codec_info *codec, + struct gbaudio_module_info *module) +{ + int ret; + uint16_t data_cport, cportid, i2s_port; + int module_state; + struct gbaudio_data_connection *data; + const char *dai_name; + + dai_name = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].dai_name; + module_state = module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK]; - module_state = module->ctrlstate[dir]; if (module_state == GBAUDIO_CODEC_SHUTDOWN) { - dev_dbg(codec->dev, "%s: module already configured\n", - module->name); - mutex_unlock(&codec->lock); + dev_dbg(module->dev, "module already configured\n"); return 0; } /* find the dai */ data = find_data(module, dai_name); if (!data) { - dev_err(codec->dev, "%s:%s DATA connection missing\n", - dai_name, module->name); - mutex_unlock(&codec->lock); + dev_err(module->dev, "%s:DATA connection missing\n", dai_name); return -ENODEV; } - if (codec_state > GBAUDIO_CODEC_HWPARAMS) { + + if (module_state > GBAUDIO_CODEC_HWPARAMS) { data_cport = data->connection->intf_cport_id; - switch(dir) { - case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_gb_deactivate_rx( - module->mgmt_connection, - data_cport); - break; - case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_gb_deactivate_tx( - module->mgmt_connection, + ret = gb_audio_gb_deactivate_tx(module->mgmt_connection, data_cport); - break; - default: - ret = -EINVAL; - } if (ret) { - dev_err_ratelimited(codec->dev, "deactivate for %s failed:%d\n", - module->name, ret); - goto func_exit; + dev_err_ratelimited(module->dev, + "deactivate_tx failed:%d\n", ret); + return ret; } - dev_dbg(codec->dev, "Dynamic deactivate %s:%d DAI\n", dai_name, + dev_dbg(module->dev, "Dynamic deactivate %s:%d DAI\n", dai_name, data_cport); + module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + GBAUDIO_CODEC_HWPARAMS; } - if (codec_state > GBAUDIO_CODEC_SHUTDOWN) { + + if (module_state > GBAUDIO_CODEC_SHUTDOWN) { + i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; - switch(dir) { - case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_apbridgea_unregister_cport( - data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_RX); - break; - case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_apbridgea_unregister_cport( - data->connection, + ret = gb_audio_apbridgea_unregister_cport(data->connection, i2s_port, cportid, AUDIO_APBRIDGEA_DIRECTION_TX); - break; - default: - ret = -EINVAL; - } if (ret) { - dev_err_ratelimited(codec->dev, "unregister_cport for %s failed:%d\n", - module->name, ret); - goto func_exit; + dev_err_ratelimited(module->dev, + "unregister_cport failed:%d\n", + ret); + return ret; } - dev_dbg(codec->dev, "Dynamic Unregister %s:%d DAI\n", dai_name, + dev_dbg(module->dev, "Dynamic Unregister %s:%d DAI\n", dai_name, cportid); + module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + GBAUDIO_CODEC_SHUTDOWN; } - module->ctrlstate[dir] = GBAUDIO_CODEC_SHUTDOWN; -func_exit: - mutex_unlock(&codec->lock); - return ret; + return 0; } -static int gbaudio_module_enable(struct gbaudio_codec_info *codec, - struct gbaudio_module_info *module, int dir) +static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, + struct gbaudio_module_info *module) { - int ret = 0; - __u16 i2s_port, cportid; - int codec_state, module_state; - uint16_t data_cport; + int module_state, ret = 0; + uint16_t data_cport, i2s_port, cportid; uint8_t sig_bits, channels; uint32_t format, rate; struct gbaudio_data_connection *data; const char *dai_name; - mutex_lock(&codec->lock); - - codec_state = codec->stream[dir].state; - if (codec_state == GBAUDIO_CODEC_SHUTDOWN) { - mutex_unlock(&codec->lock); - return 0; - } - - dai_name = codec->stream[dir].dai_name; - format = codec->stream[dir].format; - channels = codec->stream[dir].channels; - rate = codec->stream[dir].rate; - sig_bits = codec->stream[dir].sig_bits; - - module_state = module->ctrlstate[dir]; - if (module_state == codec_state) { - dev_dbg(codec->dev, "%s: module already configured\n", - module->name); - mutex_unlock(&codec->lock); - return 0; - } + dai_name = codec->stream[SNDRV_PCM_STREAM_CAPTURE].dai_name; + format = codec->stream[SNDRV_PCM_STREAM_CAPTURE].format; + channels = codec->stream[SNDRV_PCM_STREAM_CAPTURE].channels; + rate = codec->stream[SNDRV_PCM_STREAM_CAPTURE].rate; + sig_bits = codec->stream[SNDRV_PCM_STREAM_CAPTURE].sig_bits; + module_state = module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE]; /* find the dai */ data = find_data(module, dai_name); if (!data) { - dev_err(codec->dev, "%s:%s DATA connection missing\n", - dai_name, module->name); - mutex_unlock(&codec->lock); + dev_err(module->dev, "%s:DATA connection missing\n", dai_name); return -ENODEV; } /* register cport */ - if (module_state < codec_state) { + if (module_state < GBAUDIO_CODEC_STARTUP) { i2s_port = 0; /* fixed for now */ cportid = data->connection->hd_cport_id; - switch(dir) { - case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_apbridgea_register_cport( - data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_RX); - break; - case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_apbridgea_register_cport( - data->connection, + ret = gb_audio_apbridgea_register_cport(data->connection, i2s_port, cportid, AUDIO_APBRIDGEA_DIRECTION_TX); - break; - default: - ret = -EINVAL; - } if (ret) { - dev_err_ratelimited(codec->dev, "reg_cport for %s\n", module->name); - goto func_exit; + dev_err_ratelimited(module->dev, + "reg_cport failed:%d\n", ret); + return ret; } - module_state = GBAUDIO_CODEC_STARTUP; - dev_dbg(codec->dev, "Dynamic Register %s:%d DAI\n", dai_name, + module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + GBAUDIO_CODEC_STARTUP; + dev_dbg(module->dev, "Dynamic Register %s:%d DAI\n", dai_name, cportid); } /* hw_params */ - if (module_state < codec_state) { + if (module_state < GBAUDIO_CODEC_HWPARAMS) { data_cport = data->connection->intf_cport_id; ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, format, rate, channels, sig_bits); if (ret) { - dev_err_ratelimited(codec->dev, "set_pcm for %s\n", module->name); - goto func_exit; + dev_err_ratelimited(module->dev, "set_pcm failed:%d\n", + ret); + return ret; } - module_state = GBAUDIO_CODEC_HWPARAMS; - dev_dbg(codec->dev, "Dynamic hw_params %s:%d DAI\n", dai_name, + module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + GBAUDIO_CODEC_HWPARAMS; + dev_dbg(module->dev, "Dynamic hw_params %s:%d DAI\n", dai_name, data_cport); } /* prepare */ - if (module_state < codec_state) { + if (module_state < GBAUDIO_CODEC_PREPARE) { data_cport = data->connection->intf_cport_id; - switch(dir) { - case SNDRV_PCM_STREAM_CAPTURE: - ret = gb_audio_gb_set_rx_data_size( - module->mgmt_connection, - data_cport, 192); - if (ret) { - dev_err_ratelimited(codec->dev, - "set_rx_data_size for %s\n", - module->name); - goto func_exit; - } - ret = gb_audio_gb_activate_rx(module->mgmt_connection, - data_cport); - if (ret) { - dev_err_ratelimited(codec->dev, "activate_rx for %s\n", - module->name); - goto func_exit; - } - break; - case SNDRV_PCM_STREAM_PLAYBACK: - ret = gb_audio_gb_set_tx_data_size( - module->mgmt_connection, - data_cport, 192); - if (ret) { - dev_err_ratelimited(codec->dev, - "set_tx_data_size for %s\n", - module->name); - goto func_exit; - } - ret = gb_audio_gb_activate_tx(module->mgmt_connection, - data_cport); - if (ret) { - dev_err_ratelimited(codec->dev, "activate_tx for %s\n", - module->name); - goto func_exit; - } - break; - default: - dev_err(codec->dev, "Inavlid stream direction\n"); - ret = -EINVAL; - goto func_exit; + ret = gb_audio_gb_set_rx_data_size(module->mgmt_connection, + data_cport, 192); + if (ret) { + dev_err_ratelimited(module->dev, + "set_rx_data_size failed:%d\n", + ret); + return ret; } - module_state = GBAUDIO_CODEC_PREPARE; - dev_dbg(codec->dev, "Dynamic prepare %s:%d DAI\n", dai_name, + ret = gb_audio_gb_activate_rx(module->mgmt_connection, + data_cport); + if (ret) { + dev_err_ratelimited(module->dev, + "activate_rx failed:%d\n", ret); + return ret; + } + module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + GBAUDIO_CODEC_PREPARE; + dev_dbg(module->dev, "Dynamic prepare %s:%d DAI\n", dai_name, data_cport); } -func_exit: - module->ctrlstate[dir] = module_state; - mutex_unlock(&codec->lock); - return ret; + return 0; +} + +static int gbaudio_module_disable_rx(struct gbaudio_codec_info *codec, + struct gbaudio_module_info *module) +{ + int ret; + uint16_t data_cport, cportid, i2s_port; + int module_state; + struct gbaudio_data_connection *data; + const char *dai_name; + + dai_name = codec->stream[SNDRV_PCM_STREAM_CAPTURE].dai_name; + module_state = module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE]; + + if (module_state == GBAUDIO_CODEC_SHUTDOWN) { + dev_dbg(module->dev, "%s: module already configured\n", + module->name); + return 0; + } + + /* find the dai */ + data = find_data(module, dai_name); + if (!data) { + dev_err(module->dev, "%s:DATA connection missing\n", dai_name); + return -ENODEV; + } + + if (module_state > GBAUDIO_CODEC_HWPARAMS) { + data_cport = data->connection->intf_cport_id; + ret = gb_audio_gb_deactivate_rx(module->mgmt_connection, + data_cport); + if (ret) { + dev_err_ratelimited(module->dev, + "deactivate_rx failed:%d\n", ret); + return ret; + } + dev_dbg(module->dev, "Dynamic deactivate %s:%d DAI\n", dai_name, + data_cport); + module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + GBAUDIO_CODEC_HWPARAMS; + } + + if (module_state > GBAUDIO_CODEC_SHUTDOWN) { + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); + if (ret) { + dev_err_ratelimited(module->dev, + "unregister_cport failed:%d\n", + ret); + return ret; + } + dev_dbg(module->dev, "Dynamic Unregister %s:%d DAI\n", dai_name, + cportid); + module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + GBAUDIO_CODEC_SHUTDOWN; + } + + return 0; } int gbaudio_module_update(struct gbaudio_codec_info *codec, @@ -280,9 +337,8 @@ int gbaudio_module_update(struct gbaudio_codec_info *codec, struct gbaudio_module_info *module, int enable) { int stream, ret = 0; - int pb_state, cap_state; - dev_dbg(module->dev, "Module update %s sequence\n", + dev_dbg(module->dev, "%s:Module update %s sequence\n", w_name, enable ? "Enable":"Disable"); stream = find_stream(w_name); @@ -291,27 +347,22 @@ int gbaudio_module_update(struct gbaudio_codec_info *codec, return 0; } - /* check if playback active */ - pb_state = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].state; - if ((stream & GB_PLAYBACK) && (pb_state > GBAUDIO_CODEC_SHUTDOWN)) { + mutex_lock(&codec->lock); + if (stream & GB_PLAYBACK) { if (enable) - ret = gbaudio_module_enable(codec, module, - SNDRV_PCM_STREAM_PLAYBACK); + ret = gbaudio_module_enable_tx(codec, module); else - ret = gbaudio_module_disable(codec, module, - SNDRV_PCM_STREAM_PLAYBACK); + ret = gbaudio_module_disable_tx(codec, module); } /* check if capture active */ - cap_state = codec->stream[SNDRV_PCM_STREAM_CAPTURE].state; - if ((stream & GB_CAPTURE) && (cap_state > GBAUDIO_CODEC_SHUTDOWN)) { + if (stream & GB_CAPTURE) { if (enable) - ret = gbaudio_module_enable(codec, module, - SNDRV_PCM_STREAM_CAPTURE); + ret = gbaudio_module_enable_rx(codec, module); else - ret = gbaudio_module_disable(codec, module, - SNDRV_PCM_STREAM_CAPTURE); + ret = gbaudio_module_disable_rx(codec, module); } + mutex_unlock(&codec->lock); return ret; } -- cgit v1.2.3-59-g8ed1b From 1568159868f6b6c4ab17aa28ef4d5d4fc02317f1 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:31 +0530 Subject: greybus: audio: Update dai_driver table with appropriate fields Currently, the stream name for the DAI driver is generically set to "GB Audio Playback" and "GB Audio Capture". This is OK since we use a single interface on APB1 but that could change in the future. Update the DAI driver table entries to properly reflect the interface used. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 70b9a797afc8..a6f843784f2f 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -950,10 +950,10 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec, static struct snd_soc_dai_driver gbaudio_dai[] = { { - .name = "greybus-apb1", + .name = "apb-i2s0", .id = 0, .playback = { - .stream_name = "GB Audio Playback", + .stream_name = "I2S 0 Playback", .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FORMAT_S16_LE, .rate_max = 48000, @@ -962,7 +962,7 @@ static struct snd_soc_dai_driver gbaudio_dai[] = { .channels_max = 2, }, .capture = { - .stream_name = "GB Audio Capture", + .stream_name = "I2S 0 Capture", .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FORMAT_S16_LE, .rate_max = 48000, -- cgit v1.2.3-59-g8ed1b From 4ffca62a051c3e1722bcaf6a367b419e6e5e40e0 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:32 +0530 Subject: greybus: audio: Update parameters for gbaudio_module_update API Earlier, module path was enabled based on module's control switch e.g. 'SPK Amp switch'. Thus widget's name was sufficient to parse and identify the direction. Now individual modules' path will be enabled based on AIF widget status. So, it is required to get complete widget details, say w->type is used to identify direction (playback/capture) and w->sname is used to identify module's DATA connection used for communication via greybus. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 5 +++-- drivers/staging/greybus/audio_codec.h | 6 +++--- drivers/staging/greybus/audio_topology.c | 6 ++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index a6f843784f2f..e2a0c16397cd 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -333,10 +333,11 @@ static int gbaudio_module_disable_rx(struct gbaudio_codec_info *codec, } int gbaudio_module_update(struct gbaudio_codec_info *codec, - const char *w_name, - struct gbaudio_module_info *module, int enable) + struct snd_soc_dapm_widget *w, + struct gbaudio_module_info *module, int enable) { int stream, ret = 0; + const char *w_name = w->name; dev_dbg(module->dev, "%s:Module update %s sequence\n", w_name, enable ? "Enable":"Disable"); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 1646b2ca0f0d..c5a8808108db 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -206,9 +206,9 @@ int gbaudio_tplg_parse_data(struct gbaudio_module_info *module, void gbaudio_tplg_release(struct gbaudio_module_info *module); int gbaudio_module_update(struct gbaudio_codec_info *codec, - const char *w_name, - struct gbaudio_module_info *module, - int enable); + struct snd_soc_dapm_widget *w, + struct gbaudio_module_info *module, + int enable); int gbaudio_register_module(struct gbaudio_module_info *module); void gbaudio_unregister_module(struct gbaudio_module_info *module); diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 3c7c786d3b03..937529653cd3 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -960,14 +960,12 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_PRE_PMU: ret = gb_audio_gb_enable_widget(module->mgmt_connection, wid); if (!ret) - ret = gbaudio_module_update(gbcodec, w->name, module, - 1); + ret = gbaudio_module_update(gbcodec, w, module, 1); break; case SND_SOC_DAPM_POST_PMD: ret = gb_audio_gb_disable_widget(module->mgmt_connection, wid); if (!ret) - ret = gbaudio_module_update(gbcodec, w->name, module, - 0); + ret = gbaudio_module_update(gbcodec, w, module, 0); break; } if (ret) -- cgit v1.2.3-59-g8ed1b From 487dcbd6ba46548f8f24dbd75423785ec8153712 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:33 +0530 Subject: greybus: audio: Use AIF widget to enable path between module & APB Currently, SPK Amp switch is used to identify when to enable data path between module and APB. With headset, other switch controls added, it is not possible to use this switch to control data path. Instead path should be established based on AIF widget. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index e2a0c16397cd..3067c1a7ab39 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -30,16 +30,6 @@ find_data(struct gbaudio_module_info *module, const char *name) return NULL; } -static int find_stream(const char *name) -{ - int stream = 0; - - if (strnstr(name, "SPK Amp", NAME_SIZE)) - stream |= GB_PLAYBACK; - - return stream; -} - static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, struct gbaudio_module_info *module) { @@ -336,33 +326,30 @@ int gbaudio_module_update(struct gbaudio_codec_info *codec, struct snd_soc_dapm_widget *w, struct gbaudio_module_info *module, int enable) { - int stream, ret = 0; + int ret = 0; const char *w_name = w->name; dev_dbg(module->dev, "%s:Module update %s sequence\n", w_name, enable ? "Enable":"Disable"); - stream = find_stream(w_name); - if (!stream) { + if ((w->id != snd_soc_dapm_aif_in) && (w->id != snd_soc_dapm_aif_out)){ dev_dbg(codec->dev, "No action required for %s\n", w_name); return 0; } mutex_lock(&codec->lock); - if (stream & GB_PLAYBACK) { + if (w->id == snd_soc_dapm_aif_in) { if (enable) ret = gbaudio_module_enable_tx(codec, module); else ret = gbaudio_module_disable_tx(codec, module); - } - - /* check if capture active */ - if (stream & GB_CAPTURE) { + } else if (w->id == snd_soc_dapm_aif_out) { if (enable) ret = gbaudio_module_enable_rx(codec, module); else ret = gbaudio_module_disable_rx(codec, module); } + mutex_unlock(&codec->lock); return ret; -- cgit v1.2.3-59-g8ed1b From b7e7dc0083d662c061dcd547c7dde4db24331ebc Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:34 +0530 Subject: greybus: audio: Add id to identify data connection Added id field to data connection struct. This is used to identify which data connection to use while enabling interface between module & APB. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.h | 1 + drivers/staging/greybus/audio_module.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index c5a8808108db..8cd5bd7a6c31 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -129,6 +129,7 @@ struct gbaudio_control { }; struct gbaudio_data_connection { + int id; __le16 data_cport; int cport_configured; char name[NAME_SIZE]; diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 7cf523e7f995..9632bc119f87 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -199,6 +199,7 @@ static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule, greybus_set_drvdata(bundle, gbmodule); /* dai->name should be same as codec->dai_name */ strlcpy(dai->name, "greybus-apb1", NAME_SIZE); + dai->id = 0; dai->data_cport = connection->intf_cport_id; dai->connection = connection; list_add(&dai->list, &gbmodule->data_list); -- cgit v1.2.3-59-g8ed1b From 60e7327d54b270eeadc120b6202af50856548376 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:35 +0530 Subject: greybus: audio: Find data connection based on id Currently we are using dai->name to identify data connection from list for a module. Now since we are enabling data path based on widget, dai->name might be invalid by the time driver receives disable request for a widget. So, use id fetched from AIF widget->sname to identify data connection for a module. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 80 +++++++++++++++++------------------ 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 3067c1a7ab39..b64aca523d53 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -19,19 +19,19 @@ static struct gbaudio_codec_info *gbcodec; static struct gbaudio_data_connection * -find_data(struct gbaudio_module_info *module, const char *name) +find_data(struct gbaudio_module_info *module, int id) { struct gbaudio_data_connection *data; list_for_each_entry(data, &module->data_list, list) { - if (name && !strncmp(data->name, name, NAME_SIZE)) + if (id == data->id) return data; } return NULL; } static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, - struct gbaudio_module_info *module) + struct gbaudio_module_info *module, int id) { int module_state, ret = 0; uint16_t data_cport, i2s_port, cportid; @@ -49,9 +49,9 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, module_state = module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK]; /* find the dai */ - data = find_data(module, dai_name); + data = find_data(module, id); if (!data) { - dev_err(module->dev, "%s:DATA connection missing\n", dai_name); + dev_err(module->dev, "%d:DATA connection missing\n", id); return -ENODEV; } @@ -116,16 +116,13 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, return 0; } -static int gbaudio_module_disable_tx(struct gbaudio_codec_info *codec, - struct gbaudio_module_info *module) +static int gbaudio_module_disable_tx(struct gbaudio_module_info *module, int id) { int ret; uint16_t data_cport, cportid, i2s_port; int module_state; struct gbaudio_data_connection *data; - const char *dai_name; - dai_name = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].dai_name; module_state = module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK]; if (module_state == GBAUDIO_CODEC_SHUTDOWN) { @@ -134,9 +131,9 @@ static int gbaudio_module_disable_tx(struct gbaudio_codec_info *codec, } /* find the dai */ - data = find_data(module, dai_name); + data = find_data(module, id); if (!data) { - dev_err(module->dev, "%s:DATA connection missing\n", dai_name); + dev_err(module->dev, "%d:DATA connection missing\n", id); return -ENODEV; } @@ -149,8 +146,7 @@ static int gbaudio_module_disable_tx(struct gbaudio_codec_info *codec, "deactivate_tx failed:%d\n", ret); return ret; } - dev_dbg(module->dev, "Dynamic deactivate %s:%d DAI\n", dai_name, - data_cport); + dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport); module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_HWPARAMS; } @@ -167,8 +163,7 @@ static int gbaudio_module_disable_tx(struct gbaudio_codec_info *codec, ret); return ret; } - dev_dbg(module->dev, "Dynamic Unregister %s:%d DAI\n", dai_name, - cportid); + dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid); module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_SHUTDOWN; } @@ -177,7 +172,7 @@ static int gbaudio_module_disable_tx(struct gbaudio_codec_info *codec, } static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, - struct gbaudio_module_info *module) + struct gbaudio_module_info *module, int id) { int module_state, ret = 0; uint16_t data_cport, i2s_port, cportid; @@ -194,9 +189,9 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, module_state = module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE]; /* find the dai */ - data = find_data(module, dai_name); + data = find_data(module, id); if (!data) { - dev_err(module->dev, "%s:DATA connection missing\n", dai_name); + dev_err(module->dev, "%d:DATA connection missing\n", id); return -ENODEV; } @@ -261,16 +256,13 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, return 0; } -static int gbaudio_module_disable_rx(struct gbaudio_codec_info *codec, - struct gbaudio_module_info *module) +static int gbaudio_module_disable_rx(struct gbaudio_module_info *module, int id) { int ret; uint16_t data_cport, cportid, i2s_port; int module_state; struct gbaudio_data_connection *data; - const char *dai_name; - dai_name = codec->stream[SNDRV_PCM_STREAM_CAPTURE].dai_name; module_state = module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE]; if (module_state == GBAUDIO_CODEC_SHUTDOWN) { @@ -280,9 +272,9 @@ static int gbaudio_module_disable_rx(struct gbaudio_codec_info *codec, } /* find the dai */ - data = find_data(module, dai_name); + data = find_data(module, id); if (!data) { - dev_err(module->dev, "%s:DATA connection missing\n", dai_name); + dev_err(module->dev, "%d:DATA connection missing\n", id); return -ENODEV; } @@ -295,8 +287,7 @@ static int gbaudio_module_disable_rx(struct gbaudio_codec_info *codec, "deactivate_rx failed:%d\n", ret); return ret; } - dev_dbg(module->dev, "Dynamic deactivate %s:%d DAI\n", dai_name, - data_cport); + dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport); module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_HWPARAMS; } @@ -313,8 +304,7 @@ static int gbaudio_module_disable_rx(struct gbaudio_codec_info *codec, ret); return ret; } - dev_dbg(module->dev, "Dynamic Unregister %s:%d DAI\n", dai_name, - cportid); + dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid); module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_SHUTDOWN; } @@ -326,28 +316,36 @@ int gbaudio_module_update(struct gbaudio_codec_info *codec, struct snd_soc_dapm_widget *w, struct gbaudio_module_info *module, int enable) { - int ret = 0; - const char *w_name = w->name; + int dai_id, ret; + char intf_name[NAME_SIZE], dir[NAME_SIZE]; - dev_dbg(module->dev, "%s:Module update %s sequence\n", w_name, + dev_dbg(module->dev, "%s:Module update %s sequence\n", w->name, enable ? "Enable":"Disable"); if ((w->id != snd_soc_dapm_aif_in) && (w->id != snd_soc_dapm_aif_out)){ - dev_dbg(codec->dev, "No action required for %s\n", w_name); + dev_dbg(codec->dev, "No action required for %s\n", w->name); return 0; } + /* parse dai_id from AIF widget's stream_name */ + ret = sscanf(w->sname, "%s %d %s", intf_name, &dai_id, dir); + if (ret < 3) { + dev_err(codec->dev, "Error while parsing dai_id for %s\n", + w->name); + return -EINVAL; + } + mutex_lock(&codec->lock); if (w->id == snd_soc_dapm_aif_in) { if (enable) - ret = gbaudio_module_enable_tx(codec, module); + ret = gbaudio_module_enable_tx(codec, module, dai_id); else - ret = gbaudio_module_disable_tx(codec, module); + ret = gbaudio_module_disable_tx(module, dai_id); } else if (w->id == snd_soc_dapm_aif_out) { if (enable) - ret = gbaudio_module_enable_rx(codec, module); + ret = gbaudio_module_enable_rx(codec, module, dai_id); else - ret = gbaudio_module_disable_rx(codec, module); + ret = gbaudio_module_disable_rx(module, dai_id); } mutex_unlock(&codec->lock); @@ -447,7 +445,7 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, /* find the data connection */ list_for_each_entry(module, &codec->module_list, list) { - data = find_data(module, dai->name); + data = find_data(module, dai->id); if (data) break; } @@ -496,7 +494,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, list_for_each_entry(module, &codec->module_list, list) { /* find the dai */ - data = find_data(module, dai->name); + data = find_data(module, dai->id); if (data) break; } @@ -554,7 +552,7 @@ static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) list_for_each_entry(module, &codec->module_list, list) { /* find the dai */ - data = find_data(module, dai->name); + data = find_data(module, dai->id); if (data) break; } @@ -764,7 +762,7 @@ static void gbaudio_codec_cleanup(struct gbaudio_module_info *module) dev_dbg(gbcodec->dev, "%s: removed, cleanup APBridge\n", module->name); if (pb_state == GBAUDIO_CODEC_START) { /* cleanup PB path, only APBridge specific */ - data = find_data(module, gbcodec->stream[0].dai_name); + data = find_data(module, 1); if (!data) { dev_err(gbcodec->dev, "%s: Missing data pointer\n", __func__); @@ -790,7 +788,7 @@ static void gbaudio_codec_cleanup(struct gbaudio_module_info *module) if (cap_state == GBAUDIO_CODEC_START) { /* cleanup CAP path, only APBridge specific */ - data = find_data(module, gbcodec->stream[1].dai_name); + data = find_data(module, 1); if (!data) { dev_err(gbcodec->dev, "%s: Missing data pointer\n", __func__); -- cgit v1.2.3-59-g8ed1b From 956adf749d697decd9fb5f46aa0fbbc889f4dfe1 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:36 +0530 Subject: greybus: Remove unused field from data_connection Audio codec driver internally maintains a struct containing info about module's data connection. Remove unused field from this struct. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.h | 2 -- drivers/staging/greybus/audio_module.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 8cd5bd7a6c31..7a2dbc517663 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -131,8 +131,6 @@ struct gbaudio_control { struct gbaudio_data_connection { int id; __le16 data_cport; - int cport_configured; - char name[NAME_SIZE]; struct gb_connection *connection; struct list_head list; }; diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 9632bc119f87..c2172719e687 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -197,8 +197,6 @@ static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule, } greybus_set_drvdata(bundle, gbmodule); - /* dai->name should be same as codec->dai_name */ - strlcpy(dai->name, "greybus-apb1", NAME_SIZE); dai->id = 0; dai->data_cport = connection->intf_cport_id; dai->connection = connection; -- cgit v1.2.3-59-g8ed1b From c388ae769699262bd48da1d529bbed731c2de559 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:37 +0530 Subject: greybus: audio: Update pm runtime support in dai_ops callback Ensure pm runtime get_sync/put protection in codec_dai ops callback functions before accessing apbridge. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index b64aca523d53..9f050bd95c7c 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -405,6 +405,7 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, uint32_t format, rate; struct gbaudio_module_info *module; struct gbaudio_data_connection *data; + struct gb_bundle *bundle; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); mutex_lock(&codec->lock); @@ -456,6 +457,13 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + bundle = to_gb_bundle(module->dev); + ret = gb_pm_runtime_get_sync(bundle); + if (ret) { + mutex_unlock(&codec->lock); + return ret; + } + ret = gb_audio_apbridgea_set_config(data->connection, 0, AUDIO_APBRIDGEA_PCM_FMT_16, AUDIO_APBRIDGEA_PCM_RATE_48000, @@ -466,6 +474,9 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&codec->lock); return ret; } + + gb_pm_runtime_put_noidle(bundle); + codec->stream[substream->stream].state = GBAUDIO_CODEC_HWPARAMS; codec->stream[substream->stream].format = format; codec->stream[substream->stream].rate = rate; @@ -482,6 +493,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, int ret; struct gbaudio_module_info *module; struct gbaudio_data_connection *data; + struct gb_bundle *bundle; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); mutex_lock(&codec->lock); @@ -504,6 +516,13 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, return -ENODEV; } + bundle = to_gb_bundle(module->dev); + ret = gb_pm_runtime_get_sync(bundle); + if (ret) { + mutex_unlock(&codec->lock); + return ret; + } + switch (substream->stream) { case SNDRV_PCM_STREAM_PLAYBACK: ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0, @@ -521,6 +540,8 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, return ret; } + gb_pm_runtime_put_noidle(bundle); + codec->stream[substream->stream].state = GBAUDIO_CODEC_PREPARE; mutex_unlock(&codec->lock); return 0; @@ -531,6 +552,7 @@ static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) int ret; struct gbaudio_data_connection *data; struct gbaudio_module_info *module; + struct gb_bundle *bundle; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); @@ -563,6 +585,13 @@ static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) return -ENODEV; } + bundle = to_gb_bundle(module->dev); + ret = gb_pm_runtime_get_sync(bundle); + if (ret) { + mutex_unlock(&codec->lock); + return ret; + } + if (!mute && !stream) {/* start playback */ ret = gb_audio_apbridgea_prepare_tx(data->connection, 0); @@ -597,6 +626,7 @@ static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) module->name, mute ? "Mute" : "Unmute", stream ? "Capture" : "Playback", ret); + gb_pm_runtime_put_noidle(bundle); mutex_unlock(&codec->lock); return ret; } -- cgit v1.2.3-59-g8ed1b From 19866603be2ad58735f82511f3d5f680e61479ea Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:38 +0530 Subject: greybus: audio: Maintain runtime stream params for each DAI Runtime streams are required while configuring GB module plugged-in during active stream. Currently, it is maintained for single stream. However, this should be maintained for a stream corresponding to each DAI. Fix this! Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 197 ++++++++++++++++++++++------------ drivers/staging/greybus/audio_codec.h | 13 ++- 2 files changed, 141 insertions(+), 69 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 9f050bd95c7c..e1ad685b00dc 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -30,6 +30,18 @@ find_data(struct gbaudio_module_info *module, int id) return NULL; } +static struct gbaudio_stream_params * +find_dai_stream_params(struct gbaudio_codec_info *codec, int id, int stream) +{ + struct gbaudio_codec_dai *dai; + + list_for_each_entry(dai, &codec->dai_list, list) { + if (dai->id == id) + return &dai->params[stream]; + } + return NULL; +} + static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, struct gbaudio_module_info *module, int id) { @@ -38,13 +50,7 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, uint8_t sig_bits, channels; uint32_t format, rate; struct gbaudio_data_connection *data; - const char *dai_name; - - dai_name = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].dai_name; - format = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].format; - channels = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].channels; - rate = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].rate; - sig_bits = codec->stream[SNDRV_PCM_STREAM_PLAYBACK].sig_bits; + struct gbaudio_stream_params *params; module_state = module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK]; @@ -55,6 +61,12 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, return -ENODEV; } + params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_PLAYBACK); + if (!params) { + dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); + return -EINVAL; + } + /* register cport */ if (module_state < GBAUDIO_CODEC_STARTUP) { i2s_port = 0; /* fixed for now */ @@ -69,12 +81,15 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, } module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_STARTUP; - dev_dbg(module->dev, "Dynamic Register %s:%d DAI\n", dai_name, - cportid); + dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid); } /* hw_params */ if (module_state < GBAUDIO_CODEC_HWPARAMS) { + format = params->format; + channels = params->channels; + rate = params->rate; + sig_bits = params->sig_bits; data_cport = data->connection->intf_cport_id; ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, format, rate, channels, sig_bits); @@ -85,8 +100,7 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, } module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_HWPARAMS; - dev_dbg(module->dev, "Dynamic hw_params %s:%d DAI\n", dai_name, - data_cport); + dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport); } /* prepare */ @@ -109,8 +123,7 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, } module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_PREPARE; - dev_dbg(module->dev, "Dynamic prepare %s:%d DAI\n", dai_name, - data_cport); + dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport); } return 0; @@ -179,13 +192,8 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, uint8_t sig_bits, channels; uint32_t format, rate; struct gbaudio_data_connection *data; - const char *dai_name; + struct gbaudio_stream_params *params; - dai_name = codec->stream[SNDRV_PCM_STREAM_CAPTURE].dai_name; - format = codec->stream[SNDRV_PCM_STREAM_CAPTURE].format; - channels = codec->stream[SNDRV_PCM_STREAM_CAPTURE].channels; - rate = codec->stream[SNDRV_PCM_STREAM_CAPTURE].rate; - sig_bits = codec->stream[SNDRV_PCM_STREAM_CAPTURE].sig_bits; module_state = module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE]; /* find the dai */ @@ -195,6 +203,12 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, return -ENODEV; } + params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_CAPTURE); + if (!params) { + dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); + return -EINVAL; + } + /* register cport */ if (module_state < GBAUDIO_CODEC_STARTUP) { i2s_port = 0; /* fixed for now */ @@ -209,12 +223,15 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, } module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_STARTUP; - dev_dbg(module->dev, "Dynamic Register %s:%d DAI\n", dai_name, - cportid); + dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid); } /* hw_params */ if (module_state < GBAUDIO_CODEC_HWPARAMS) { + format = params->format; + channels = params->channels; + rate = params->rate; + sig_bits = params->sig_bits; data_cport = data->connection->intf_cport_id; ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, format, rate, channels, sig_bits); @@ -225,8 +242,7 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, } module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_HWPARAMS; - dev_dbg(module->dev, "Dynamic hw_params %s:%d DAI\n", dai_name, - data_cport); + dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport); } /* prepare */ @@ -249,8 +265,7 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, } module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_PREPARE; - dev_dbg(module->dev, "Dynamic prepare %s:%d DAI\n", dai_name, - data_cport); + dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport); } return 0; @@ -361,6 +376,7 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); + struct gbaudio_stream_params *params; mutex_lock(&codec->lock); @@ -370,8 +386,13 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, return -ENODEV; } - codec->stream[substream->stream].state = GBAUDIO_CODEC_STARTUP; - codec->stream[substream->stream].dai_name = dai->name; + params = find_dai_stream_params(codec, dai->id, substream->stream); + if (!params) { + dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); + mutex_unlock(&codec->lock); + return -EINVAL; + } + params->state = GBAUDIO_CODEC_STARTUP; mutex_unlock(&codec->lock); /* to prevent suspend in case of active audio */ pm_stay_awake(dai->dev); @@ -383,14 +404,20 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); + struct gbaudio_stream_params *params; mutex_lock(&codec->lock); if (list_empty(&codec->module_list)) dev_info(codec->dev, "No codec module available during shutdown\n"); - codec->stream[substream->stream].state = GBAUDIO_CODEC_SHUTDOWN; - codec->stream[substream->stream].dai_name = NULL; + params = find_dai_stream_params(codec, dai->id, substream->stream); + if (!params) { + dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); + mutex_unlock(&codec->lock); + return; + } + params->state = GBAUDIO_CODEC_SHUTDOWN; mutex_unlock(&codec->lock); pm_relax(dai->dev); return; @@ -407,6 +434,7 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, struct gbaudio_data_connection *data; struct gb_bundle *bundle; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); + struct gbaudio_stream_params *params; mutex_lock(&codec->lock); @@ -457,6 +485,13 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + params = find_dai_stream_params(codec, dai->id, substream->stream); + if (!params) { + dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); + mutex_unlock(&codec->lock); + return -EINVAL; + } + bundle = to_gb_bundle(module->dev); ret = gb_pm_runtime_get_sync(bundle); if (ret) { @@ -477,11 +512,11 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, gb_pm_runtime_put_noidle(bundle); - codec->stream[substream->stream].state = GBAUDIO_CODEC_HWPARAMS; - codec->stream[substream->stream].format = format; - codec->stream[substream->stream].rate = rate; - codec->stream[substream->stream].channels = channels; - codec->stream[substream->stream].sig_bits = sig_bits; + params->state = GBAUDIO_CODEC_HWPARAMS; + params->format = format; + params->rate = rate; + params->channels = channels; + params->sig_bits = sig_bits; mutex_unlock(&codec->lock); return 0; @@ -495,6 +530,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, struct gbaudio_data_connection *data; struct gb_bundle *bundle; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); + struct gbaudio_stream_params *params; mutex_lock(&codec->lock); @@ -516,6 +552,13 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, return -ENODEV; } + params = find_dai_stream_params(codec, dai->id, substream->stream); + if (!params) { + dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); + mutex_unlock(&codec->lock); + return -EINVAL; + } + bundle = to_gb_bundle(module->dev); ret = gb_pm_runtime_get_sync(bundle); if (ret) { @@ -542,7 +585,7 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, gb_pm_runtime_put_noidle(bundle); - codec->stream[substream->stream].state = GBAUDIO_CODEC_PREPARE; + params->state = GBAUDIO_CODEC_PREPARE; mutex_unlock(&codec->lock); return 0; } @@ -554,16 +597,25 @@ static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) struct gbaudio_module_info *module; struct gb_bundle *bundle; struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); + struct gbaudio_stream_params *params; dev_dbg(dai->dev, "Mute:%d, Direction:%s\n", mute, stream ? "CAPTURE":"PLAYBACK"); mutex_lock(&codec->lock); + + params = find_dai_stream_params(codec, dai->id, stream); + if (!params) { + dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); + mutex_unlock(&codec->lock); + return -EINVAL; + } + if (list_empty(&codec->module_list)) { dev_err(codec->dev, "No codec module available\n"); if (mute) { - codec->stream[stream].state = GBAUDIO_CODEC_STOP; + params->state = GBAUDIO_CODEC_STOP; ret = 0; } else { ret = -ENODEV; @@ -598,26 +650,26 @@ static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) if (!ret) ret = gb_audio_apbridgea_start_tx(data->connection, 0, 0); - codec->stream[stream].state = GBAUDIO_CODEC_START; + params->state = GBAUDIO_CODEC_START; } else if (!mute && stream) {/* start capture */ ret = gb_audio_apbridgea_prepare_rx(data->connection, 0); if (!ret) ret = gb_audio_apbridgea_start_rx(data->connection, 0); - codec->stream[stream].state = GBAUDIO_CODEC_START; + params->state = GBAUDIO_CODEC_START; } else if (mute && !stream) {/* stop playback */ ret = gb_audio_apbridgea_stop_tx(data->connection, 0); if (!ret) ret = gb_audio_apbridgea_shutdown_tx(data->connection, 0); - codec->stream[stream].state = GBAUDIO_CODEC_STOP; + params->state = GBAUDIO_CODEC_STOP; } else if (mute && stream) {/* stop capture */ ret = gb_audio_apbridgea_stop_rx(data->connection, 0); if (!ret) ret = gb_audio_apbridgea_shutdown_rx(data->connection, 0); - codec->stream[stream].state = GBAUDIO_CODEC_STOP; + params->state = GBAUDIO_CODEC_STOP; } else ret = -EINVAL; if (ret) @@ -639,6 +691,32 @@ static struct snd_soc_dai_ops gbcodec_dai_ops = { .mute_stream = gbcodec_mute_stream, }; +static struct snd_soc_dai_driver gbaudio_dai[] = { + { + .name = "apb-i2s0", + .id = 0, + .playback = { + .stream_name = "I2S 0 Playback", + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FORMAT_S16_LE, + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .capture = { + .stream_name = "I2S 0 Capture", + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FORMAT_S16_LE, + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &gbcodec_dai_ops, + }, +}; + static int gbaudio_init_jack(struct gbaudio_module_info *module, struct snd_soc_codec *codec) { @@ -898,7 +976,9 @@ EXPORT_SYMBOL(gbaudio_unregister_module); */ static int gbcodec_probe(struct snd_soc_codec *codec) { + int i; struct gbaudio_codec_info *info; + struct gbaudio_codec_dai *dai; info = devm_kzalloc(codec->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -907,6 +987,17 @@ static int gbcodec_probe(struct snd_soc_codec *codec) info->dev = codec->dev; INIT_LIST_HEAD(&info->module_list); mutex_init(&info->lock); + INIT_LIST_HEAD(&info->dai_list); + + /* init dai_list used to maintain runtime stream info */ + for (i = 0; i < ARRAY_SIZE(gbaudio_dai); i++) { + dai = devm_kzalloc(codec->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + dai->id = gbaudio_dai[i].id; + list_add(&dai->list, &info->dai_list); + } + info->codec = codec; snd_soc_codec_set_drvdata(codec, info); gbcodec = info; @@ -964,32 +1055,6 @@ static unsigned int gbcodec_read(struct snd_soc_codec *codec, return val; } -static struct snd_soc_dai_driver gbaudio_dai[] = { - { - .name = "apb-i2s0", - .id = 0, - .playback = { - .stream_name = "I2S 0 Playback", - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FORMAT_S16_LE, - .rate_max = 48000, - .rate_min = 48000, - .channels_min = 1, - .channels_max = 2, - }, - .capture = { - .stream_name = "I2S 0 Capture", - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FORMAT_S16_LE, - .rate_max = 48000, - .rate_min = 48000, - .channels_min = 1, - .channels_max = 2, - }, - .ops = &gbcodec_dai_ops, - }, -}; - static struct snd_soc_codec_driver soc_codec_dev_gbaudio = { .probe = gbcodec_probe, .remove = gbcodec_remove, diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 7a2dbc517663..40c39b30d951 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -98,18 +98,25 @@ enum gbaudio_codec_state { GBAUDIO_CODEC_STOP, }; -struct gbaudio_stream { - const char *dai_name; +struct gbaudio_stream_params { int state; uint8_t sig_bits, channels; uint32_t format, rate; }; +struct gbaudio_codec_dai { + int id; + /* runtime params for playback/capture streams */ + struct gbaudio_stream_params params[2]; + struct list_head list; +}; + struct gbaudio_codec_info { struct device *dev; struct snd_soc_codec *codec; struct list_head module_list; - struct gbaudio_stream stream[2]; /* PB/CAP */ + /* to maintain runtime stream params for each DAI */ + struct list_head dai_list; struct mutex lock; u8 reg[GBCODEC_REG_COUNT]; }; -- cgit v1.2.3-59-g8ed1b From 591c45227a77505f111dd42347b2ad09b60ed131 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 4 Aug 2016 15:14:39 +0530 Subject: greybus: audio: Maintain module stream state for each data connection For SPK module, each data connection corresponds to codec DAI. Now stream state is maintained for each DAI. So, need to maintain stream state for each DAI/data connection for individual module as well. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 150 +++++++++++++++------------------- drivers/staging/greybus/audio_codec.h | 5 +- 2 files changed, 69 insertions(+), 86 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index e1ad685b00dc..cf86f51a83a2 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -52,14 +52,13 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, struct gbaudio_data_connection *data; struct gbaudio_stream_params *params; - module_state = module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK]; - /* find the dai */ data = find_data(module, id); if (!data) { dev_err(module->dev, "%d:DATA connection missing\n", id); return -ENODEV; } + module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK]; params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_PLAYBACK); if (!params) { @@ -79,7 +78,7 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, "reg_cport failed:%d\n", ret); return ret; } - module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_STARTUP; dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid); } @@ -98,7 +97,7 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, ret); return ret; } - module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_HWPARAMS; dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport); } @@ -121,7 +120,7 @@ static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, "activate_tx failed:%d\n", ret); return ret; } - module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_PREPARE; dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport); } @@ -136,19 +135,13 @@ static int gbaudio_module_disable_tx(struct gbaudio_module_info *module, int id) int module_state; struct gbaudio_data_connection *data; - module_state = module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK]; - - if (module_state == GBAUDIO_CODEC_SHUTDOWN) { - dev_dbg(module->dev, "module already configured\n"); - return 0; - } - /* find the dai */ data = find_data(module, id); if (!data) { dev_err(module->dev, "%d:DATA connection missing\n", id); return -ENODEV; } + module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK]; if (module_state > GBAUDIO_CODEC_HWPARAMS) { data_cport = data->connection->intf_cport_id; @@ -160,7 +153,7 @@ static int gbaudio_module_disable_tx(struct gbaudio_module_info *module, int id) return ret; } dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport); - module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_HWPARAMS; } @@ -177,7 +170,7 @@ static int gbaudio_module_disable_tx(struct gbaudio_module_info *module, int id) return ret; } dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid); - module->ctrlstate[SNDRV_PCM_STREAM_PLAYBACK] = + data->state[SNDRV_PCM_STREAM_PLAYBACK] = GBAUDIO_CODEC_SHUTDOWN; } @@ -194,14 +187,13 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, struct gbaudio_data_connection *data; struct gbaudio_stream_params *params; - module_state = module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE]; - /* find the dai */ data = find_data(module, id); if (!data) { dev_err(module->dev, "%d:DATA connection missing\n", id); return -ENODEV; } + module_state = data->state[SNDRV_PCM_STREAM_CAPTURE]; params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_CAPTURE); if (!params) { @@ -221,7 +213,7 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, "reg_cport failed:%d\n", ret); return ret; } - module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_STARTUP; dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid); } @@ -240,7 +232,7 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, ret); return ret; } - module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_HWPARAMS; dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport); } @@ -263,7 +255,7 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, "activate_rx failed:%d\n", ret); return ret; } - module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_PREPARE; dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport); } @@ -278,20 +270,13 @@ static int gbaudio_module_disable_rx(struct gbaudio_module_info *module, int id) int module_state; struct gbaudio_data_connection *data; - module_state = module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE]; - - if (module_state == GBAUDIO_CODEC_SHUTDOWN) { - dev_dbg(module->dev, "%s: module already configured\n", - module->name); - return 0; - } - /* find the dai */ data = find_data(module, id); if (!data) { dev_err(module->dev, "%d:DATA connection missing\n", id); return -ENODEV; } + module_state = data->state[SNDRV_PCM_STREAM_CAPTURE]; if (module_state > GBAUDIO_CODEC_HWPARAMS) { data_cport = data->connection->intf_cport_id; @@ -303,7 +288,7 @@ static int gbaudio_module_disable_rx(struct gbaudio_module_info *module, int id) return ret; } dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport); - module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_HWPARAMS; } @@ -320,7 +305,7 @@ static int gbaudio_module_disable_rx(struct gbaudio_module_info *module, int id) return ret; } dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid); - module->ctrlstate[SNDRV_PCM_STREAM_CAPTURE] = + data->state[SNDRV_PCM_STREAM_CAPTURE] = GBAUDIO_CODEC_SHUTDOWN; } @@ -855,68 +840,67 @@ int gbaudio_register_module(struct gbaudio_module_info *module) } EXPORT_SYMBOL(gbaudio_register_module); -static void gbaudio_codec_cleanup(struct gbaudio_module_info *module) +static void gbaudio_codec_clean_data_tx(struct gbaudio_data_connection *data) { - struct gbaudio_data_connection *data; - int pb_state = gbcodec->stream[0].state; - int cap_state = gbcodec->stream[1].state; + uint16_t i2s_port, cportid; int ret; + + if (list_is_singular(&gbcodec->module_list)) { + ret = gb_audio_apbridgea_stop_tx(data->connection, 0); + if (ret) + return; + ret = gb_audio_apbridgea_shutdown_tx(data->connection, + 0); + if (ret) + return; + } + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_TX); + data->state[0] = GBAUDIO_CODEC_SHUTDOWN; +} + +static void gbaudio_codec_clean_data_rx(struct gbaudio_data_connection *data) +{ uint16_t i2s_port, cportid; + int ret; - /* locks already acquired */ - if (!pb_state && !cap_state) - return; + if (list_is_singular(&gbcodec->module_list)) { + ret = gb_audio_apbridgea_stop_rx(data->connection, 0); + if (ret) + return; + ret = gb_audio_apbridgea_shutdown_rx(data->connection, + 0); + if (ret) + return; + } + i2s_port = 0; /* fixed for now */ + cportid = data->connection->hd_cport_id; + ret = gb_audio_apbridgea_unregister_cport(data->connection, + i2s_port, cportid, + AUDIO_APBRIDGEA_DIRECTION_RX); + data->state[1] = GBAUDIO_CODEC_SHUTDOWN; +} + + +static void gbaudio_codec_cleanup(struct gbaudio_module_info *module) +{ + struct gbaudio_data_connection *data; + int pb_state, cap_state; dev_dbg(gbcodec->dev, "%s: removed, cleanup APBridge\n", module->name); - if (pb_state == GBAUDIO_CODEC_START) { - /* cleanup PB path, only APBridge specific */ - data = find_data(module, 1); - if (!data) { - dev_err(gbcodec->dev, "%s: Missing data pointer\n", - __func__); - return; - } + list_for_each_entry(data, &module->data_list, list) { + pb_state = data->state[0]; + cap_state = data->state[1]; - if (list_is_singular(&gbcodec->module_list)) { - ret = gb_audio_apbridgea_stop_tx(data->connection, 0); - if (ret) - return; - ret = gb_audio_apbridgea_shutdown_tx(data->connection, - 0); - if (ret) - return; - } - i2s_port = 0; /* fixed for now */ - cportid = data->connection->hd_cport_id; - ret = gb_audio_apbridgea_unregister_cport(data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_TX); - module->ctrlstate[0] = GBAUDIO_CODEC_SHUTDOWN; - } + if (pb_state > GBAUDIO_CODEC_SHUTDOWN) + gbaudio_codec_clean_data_tx(data); + + if (cap_state > GBAUDIO_CODEC_SHUTDOWN) + gbaudio_codec_clean_data_rx(data); - if (cap_state == GBAUDIO_CODEC_START) { - /* cleanup CAP path, only APBridge specific */ - data = find_data(module, 1); - if (!data) { - dev_err(gbcodec->dev, "%s: Missing data pointer\n", - __func__); - return; - } - if (list_is_singular(&gbcodec->module_list)) { - ret = gb_audio_apbridgea_stop_rx(data->connection, 0); - if (ret) - return; - ret = gb_audio_apbridgea_shutdown_rx(data->connection, - 0); - if (ret) - return; - } - i2s_port = 0; /* fixed for now */ - cportid = data->connection->hd_cport_id; - ret = gb_audio_apbridgea_unregister_cport(data->connection, - i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_RX); - module->ctrlstate[1] = GBAUDIO_CODEC_SHUTDOWN; } } diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 40c39b30d951..0de2ad99003b 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -140,6 +140,8 @@ struct gbaudio_data_connection { __le16 data_cport; struct gb_connection *connection; struct list_head list; + /* maintain runtime state for playback/capture stream */ + int state[2]; }; /* stream direction */ @@ -178,9 +180,6 @@ struct gbaudio_module_info { struct snd_soc_jack headset_jack; struct snd_soc_jack button_jack; - /* used by codec_ops */ - int ctrlstate[2]; /* PB/CAP */ - /* connection info */ struct gb_connection *mgmt_connection; size_t num_data_connections; -- cgit v1.2.3-59-g8ed1b From 8f3972f78f3ac52833fb781cbb689af39fc8b0f1 Mon Sep 17 00:00:00 2001 From: Jackson Chang <chang_jackson@projectara.com> Date: Fri, 5 Aug 2016 10:22:02 +0800 Subject: greybus: SDIO: Add runtime pm support Modify SDIO greybus driver to support runtime PM framework. To enable SDIO runtime PM, it needs to remove MMC_CAP_NEEDS_POLL and add MMC_CAP2_CORE_RUNTIME_PM in set_host_caps(). The suspend function and resume function have been tested with micron-sdio image by sysfs. SDIO functions work well on suspend/resume. Testing Done: Compiled and verified on EVT2.0 + Micron ARA SD module with USB connector Signed-off-by: Jackson Chang <chang_jackson@projectara.com> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 4 ++++ drivers/staging/greybus/sdio.c | 31 +++++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 5d13e36008d3..62d640013035 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -140,6 +140,10 @@ static inline void sysfs_remove_groups(struct kobject *kobj, #define MMC_DDR52_DEFINED #endif +#ifndef MMC_CAP2_CORE_RUNTIME_PM +#define MMC_CAP2_CORE_RUNTIME_PM 0 +#endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) #define MMC_POWER_UNDEFINED_SUPPORTED #endif diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index ad448c6388bb..3d3599a8b077 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -84,8 +84,8 @@ static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) #endif ((r & GB_SDIO_CAP_HS200_1_8V) ? MMC_CAP2_HS200_1_8V_SDR : 0); - host->mmc->caps = caps | MMC_CAP_NEEDS_POLL; - host->mmc->caps2 = caps2; + host->mmc->caps = caps; + host->mmc->caps2 = caps2 | MMC_CAP2_CORE_RUNTIME_PM; if (caps & MMC_CAP_NONREMOVABLE) host->card_present = true; @@ -239,8 +239,18 @@ static int gb_sdio_request_handler(struct gb_operation *op) static int gb_sdio_set_ios(struct gb_sdio_host *host, struct gb_sdio_set_ios_request *request) { - return gb_operation_sync(host->connection, GB_SDIO_TYPE_SET_IOS, - request, sizeof(*request), NULL, 0); + int ret; + + ret = gbphy_runtime_get_sync(host->gbphy_dev); + if (ret) + return ret; + + ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_SET_IOS, request, + sizeof(*request), NULL, 0); + + gbphy_runtime_put_autosuspend(host->gbphy_dev); + + return ret; } static int _gb_sdio_send(struct gb_sdio_host *host, struct mmc_data *data, @@ -489,10 +499,15 @@ static void gb_sdio_mrq_work(struct work_struct *work) host = container_of(work, struct gb_sdio_host, mrqwork); + ret = gbphy_runtime_get_sync(host->gbphy_dev); + if (ret) + return; + mutex_lock(&host->lock); mrq = host->mrq; if (!mrq) { mutex_unlock(&host->lock); + gbphy_runtime_put_autosuspend(host->gbphy_dev); dev_err(mmc_dev(host->mmc), "mmc request is NULL"); return; } @@ -528,6 +543,7 @@ done: host->mrq = NULL; mutex_unlock(&host->lock); mmc_request_done(host->mmc, mrq); + gbphy_runtime_put_autosuspend(host->gbphy_dev); } static void gb_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) @@ -813,6 +829,8 @@ static int gb_sdio_probe(struct gbphy_device *gbphy_dev, ret = _gb_sdio_process_events(host, host->queued_events); host->queued_events = 0; + gbphy_runtime_put_autosuspend(gbphy_dev); + return ret; exit_wq_destroy: @@ -832,6 +850,11 @@ static void gb_sdio_remove(struct gbphy_device *gbphy_dev) struct gb_sdio_host *host = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = host->connection; struct mmc_host *mmc; + int ret; + + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + gbphy_runtime_get_noresume(gbphy_dev); mutex_lock(&host->lock); host->removed = true; -- cgit v1.2.3-59-g8ed1b From 9141ad8773f4f6ebb8cd9fa376d9497fa7e73374 Mon Sep 17 00:00:00 2001 From: Kris Huang <huang_kris@projectara.com> Date: Fri, 5 Aug 2016 12:59:08 +0800 Subject: greybus: lights: Add runtime pm support Modify Lights greybus driver to support runtime PM framework. The suspend and resume function have been tested with gpbridge-test image by sysfs. Lights functions work well on suspend/resume. Testing Done: Compiled and verified on EVT2 and gpbridge-test module with device class daughter board. Signed-off-by: Kris Huang <huang_kris@projectara.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/light.c | 116 ++++++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 22 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 3d42a5dafee9..fb41b0b1a98c 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -117,17 +117,27 @@ static int __gb_lights_flash_intensity_set(struct gb_channel *channel, u32 intensity) { struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_bundle *bundle = connection->bundle; struct gb_lights_set_flash_intensity_request req; + int ret; if (channel->releasing) return -ESHUTDOWN; + ret = gb_pm_runtime_get_sync(bundle); + if (ret < 0) + return ret; + req.light_id = channel->light->id; req.channel_id = channel->id; req.intensity_uA = cpu_to_le32(intensity); - return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_INTENSITY, - &req, sizeof(req), NULL, 0); + ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_INTENSITY, + &req, sizeof(req), NULL, 0); + + gb_pm_runtime_put_autosuspend(bundle); + + return ret; } static int __gb_lights_flash_brightness_set(struct gb_channel *channel) @@ -321,32 +331,52 @@ static int channel_attr_groups_set(struct gb_channel *channel, static int gb_lights_fade_set(struct gb_channel *channel) { struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_bundle *bundle = connection->bundle; struct gb_lights_set_fade_request req; + int ret; if (channel->releasing) return -ESHUTDOWN; + ret = gb_pm_runtime_get_sync(bundle); + if (ret < 0) + return ret; + req.light_id = channel->light->id; req.channel_id = channel->id; req.fade_in = channel->fade_in; req.fade_out = channel->fade_out; - return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FADE, - &req, sizeof(req), NULL, 0); + ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FADE, + &req, sizeof(req), NULL, 0); + + gb_pm_runtime_put_autosuspend(bundle); + + return ret; } static int gb_lights_color_set(struct gb_channel *channel, u32 color) { struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_bundle *bundle = connection->bundle; struct gb_lights_set_color_request req; + int ret; if (channel->releasing) return -ESHUTDOWN; + ret = gb_pm_runtime_get_sync(bundle); + if (ret < 0) + return ret; + req.light_id = channel->light->id; req.channel_id = channel->id; req.color = cpu_to_le32(color); - return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_COLOR, - &req, sizeof(req), NULL, 0); + ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_COLOR, + &req, sizeof(req), NULL, 0); + + gb_pm_runtime_put_autosuspend(bundle); + + return ret; } #else /* LED_HAVE_GROUPS */ static int channel_attr_groups_set(struct gb_channel *channel, @@ -360,13 +390,23 @@ static int __gb_lights_led_brightness_set(struct gb_channel *channel) { struct gb_lights_set_brightness_request req; struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_bundle *bundle = connection->bundle; + int ret; + + ret = gb_pm_runtime_get_sync(bundle); + if (ret < 0) + return ret; req.light_id = channel->light->id; req.channel_id = channel->id; req.brightness = (u8)channel->led->brightness; - return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BRIGHTNESS, - &req, sizeof(req), NULL, 0); + ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BRIGHTNESS, + &req, sizeof(req), NULL, 0); + + gb_pm_runtime_put_autosuspend(bundle); + + return ret; } static int __gb_lights_brightness_set(struct gb_channel *channel) @@ -441,18 +481,28 @@ static int gb_blink_set(struct led_classdev *cdev, unsigned long *delay_on, { struct gb_channel *channel = get_channel_from_cdev(cdev); struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_bundle *bundle = connection->bundle; struct gb_lights_blink_request req; + int ret; if (channel->releasing) return -ESHUTDOWN; + ret = gb_pm_runtime_get_sync(bundle); + if (ret < 0) + return ret; + req.light_id = channel->light->id; req.channel_id = channel->id; req.time_on_ms = cpu_to_le16(*delay_on); req.time_off_ms = cpu_to_le16(*delay_off); - return gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BLINK, &req, - sizeof(req), NULL, 0); + ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BLINK, &req, + sizeof(req), NULL, 0); + + gb_pm_runtime_put_autosuspend(bundle); + + return ret; } static void gb_lights_led_operations_set(struct gb_channel *channel, @@ -592,23 +642,29 @@ static int gb_lights_flash_strobe_set(struct led_classdev_flash *fcdev, struct gb_channel *channel = container_of(fcdev, struct gb_channel, fled); struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_bundle *bundle = connection->bundle; struct gb_lights_set_flash_strobe_request req; int ret; if (channel->releasing) return -ESHUTDOWN; + ret = gb_pm_runtime_get_sync(bundle); + if (ret < 0) + return ret; + req.light_id = channel->light->id; req.channel_id = channel->id; req.state = state ? 1 : 0; ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_STROBE, &req, sizeof(req), NULL, 0); - if (ret < 0) - return ret; - channel->strobe_state = state; + if (!ret) + channel->strobe_state = state; - return 0; + gb_pm_runtime_put_autosuspend(bundle); + + return ret; } static int gb_lights_flash_strobe_get(struct led_classdev_flash *fcdev, @@ -627,23 +683,29 @@ static int gb_lights_flash_timeout_set(struct led_classdev_flash *fcdev, struct gb_channel *channel = container_of(fcdev, struct gb_channel, fled); struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_bundle *bundle = connection->bundle; struct gb_lights_set_flash_timeout_request req; int ret; if (channel->releasing) return -ESHUTDOWN; + ret = gb_pm_runtime_get_sync(bundle); + if (ret < 0) + return ret; + req.light_id = channel->light->id; req.channel_id = channel->id; req.timeout_us = cpu_to_le32(timeout); ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_TIMEOUT, &req, sizeof(req), NULL, 0); - if (ret < 0) - return ret; - fcdev->timeout.val = timeout; + if (!ret) + fcdev->timeout.val = timeout; - return 0; + gb_pm_runtime_put_autosuspend(bundle); + + return ret; } static int gb_lights_flash_fault_get(struct led_classdev_flash *fcdev, @@ -652,6 +714,7 @@ static int gb_lights_flash_fault_get(struct led_classdev_flash *fcdev, struct gb_channel *channel = container_of(fcdev, struct gb_channel, fled); struct gb_connection *connection = get_conn_from_channel(channel); + struct gb_bundle *bundle = connection->bundle; struct gb_lights_get_flash_fault_request req; struct gb_lights_get_flash_fault_response resp; int ret; @@ -659,17 +722,21 @@ static int gb_lights_flash_fault_get(struct led_classdev_flash *fcdev, if (channel->releasing) return -ESHUTDOWN; + ret = gb_pm_runtime_get_sync(bundle); + if (ret < 0) + return ret; + req.light_id = channel->light->id; req.channel_id = channel->id; ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_GET_FLASH_FAULT, &req, sizeof(req), &resp, sizeof(resp)); - if (ret < 0) - return ret; + if (!ret) + *fault = le32_to_cpu(resp.fault); - *fault = le32_to_cpu(resp.fault); + gb_pm_runtime_put_autosuspend(bundle); - return 0; + return ret; } static const struct led_flash_ops gb_lights_flash_ops = { @@ -1258,6 +1325,8 @@ static int gb_lights_probe(struct gb_bundle *bundle, if (ret < 0) goto error_connection_disable; + gb_pm_runtime_put_autosuspend(bundle); + return 0; error_connection_disable: @@ -1273,6 +1342,9 @@ static void gb_lights_disconnect(struct gb_bundle *bundle) { struct gb_lights *glights = greybus_get_drvdata(bundle); + if (gb_pm_runtime_get_sync(bundle)) + gb_pm_runtime_get_noresume(bundle); + gb_connection_disable(glights->connection); gb_connection_destroy(glights->connection); -- cgit v1.2.3-59-g8ed1b From 33cc283928249b2230a6519a2303fe30161cf788 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Fri, 5 Aug 2016 18:16:30 +0530 Subject: greybus: audio: Report jack removal along with module removal For GB module with jack slot supported, headset/headphone can still be inserted at the time of module removal. In this case, above layer is unaware about jack removal event which happened due to module removal. This may lead to inconsistent state in above HAL layer. Fix this by reporting jack removal event explicitly. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index cf86f51a83a2..3eb3d2cf014a 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -909,6 +909,7 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) struct snd_soc_codec *codec = gbcodec->codec; struct snd_card *card = codec->card->snd_card; struct snd_soc_jack *jack, *next_j; + int mask; dev_dbg(codec->dev, "Unregister %s module\n", module->name); @@ -922,8 +923,16 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) #ifdef CONFIG_SND_JACK /* free jack devices for this module from codec->jack_list */ list_for_each_entry_safe(jack, next_j, &codec->jack_list, list) { - if ((jack == &module->headset_jack) - || (jack == &module->button_jack)) { + if (jack == &module->headset_jack) + mask = GBCODEC_JACK_MASK; + else if (jack == &module->button_jack) + mask = GBCODEC_JACK_BUTTON_MASK; + else + mask = 0; + if (mask) { + dev_dbg(module->dev, "Report %s removal\n", + jack->jack->id); + snd_soc_jack_report(jack, 0, mask); snd_device_free(codec->card->snd_card, jack->jack); list_del(&jack->list); } -- cgit v1.2.3-59-g8ed1b From 4523eae6f8446e3943415683bc9a136ce11fb32b Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Fri, 5 Aug 2016 15:08:40 -0700 Subject: greybus: interface: fix timesync registration sequencing When enabling an interface, control device should be registered after having successfully added the timesync. Similarly for the interface disable path, control device should be removed first before removing timesync. Testing Done: - Enable and disable the interface Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Suggested-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 76569f8b086e..c3ed3d7dee99 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -1152,17 +1152,17 @@ int gb_interface_enable(struct gb_interface *intf) if (ret) goto err_destroy_bundles; - /* Register the control device and any bundles */ - ret = gb_control_add(intf->control); - if (ret) - goto err_destroy_bundles; - ret = gb_timesync_interface_add(intf); if (ret) { dev_err(&intf->dev, "failed to add to timesync: %d\n", ret); - goto err_del_control; + goto err_destroy_bundles; } + /* Register the control device and any bundles */ + ret = gb_control_add(intf->control); + if (ret) + goto err_remove_timesync; + pm_runtime_use_autosuspend(&intf->dev); pm_runtime_get_noresume(&intf->dev); pm_runtime_set_active(&intf->dev); @@ -1186,8 +1186,8 @@ int gb_interface_enable(struct gb_interface *intf) return 0; -err_del_control: - gb_control_del(intf->control); +err_remove_timesync: + gb_timesync_interface_remove(intf); err_destroy_bundles: list_for_each_entry_safe(bundle, tmp, &intf->bundles, links) gb_bundle_destroy(bundle); @@ -1229,8 +1229,8 @@ void gb_interface_disable(struct gb_interface *intf) if (!intf->mode_switch && !intf->disconnected) gb_control_interface_deactivate_prepare(intf->control); - gb_timesync_interface_remove(intf); gb_control_del(intf->control); + gb_timesync_interface_remove(intf); gb_control_disable(intf->control); gb_control_put(intf->control); intf->control = NULL; -- cgit v1.2.3-59-g8ed1b From 64f28ada9349ab071096484281548b6be1791cd7 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Tue, 9 Aug 2016 23:35:34 +0100 Subject: greybus: greybus_protocol: fix order of sdio get caps response Order of the field of the sdio get caps operation response were wrong, that influence later the frequencies used by core, during normal operation. Tested: verified that the values inserted by the fw are the correct ones for the field. Suggested-by: Jackson Chang <jacksonc@bsquare.com> Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 8484f05fc427..3082cbf3b1b2 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1540,10 +1540,10 @@ struct gb_sdio_get_caps_response { /* see possible values below at vdd */ __le32 ocr; - __le16 max_blk_count; - __le16 max_blk_size; __le32 f_min; __le32 f_max; + __le16 max_blk_count; + __le16 max_blk_size; } __packed; /* set ios request: response has no payload */ -- cgit v1.2.3-59-g8ed1b From afbf72505be3da3bb7563e3e403020a3ba62f4f7 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Tue, 9 Aug 2016 23:35:35 +0100 Subject: greybus: sdio: increase maximum segment size Adjust maximum segment size of the sg list to meet the maximum request size, this will allow to have less segments which increase the performance of data movement during transfer operations. Test Done: using mmc_test with best-case read/write performance and see that only one segment is used and that the results are better (288KiB vs 300KiB in read operation). Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 3d3599a8b077..a78d9e4a0321 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -802,11 +802,11 @@ static int gb_sdio_probe(struct gbphy_device *gbphy_dev, mmc->ops = &gb_sdio_ops; - /* for now we just make a map 1:1 between max blocks and segments */ mmc->max_segs = host->mmc->max_blk_count; - mmc->max_seg_size = host->mmc->max_blk_size; + /* for now we make a map 1:1 between max request and segment size */ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; mutex_init(&host->lock); spin_lock_init(&host->xfer); -- cgit v1.2.3-59-g8ed1b From a4fe35072f3c531c97694de2e7fe28939b72c742 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 10 Aug 2016 12:58:39 +0200 Subject: greybus: greybus_protocols: fix apba vendor-request comment Fix copy-paste error in an APBA USB vendor-request comment. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 3082cbf3b1b2..63fd48d991a1 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -243,7 +243,7 @@ struct gb_control_intf_pm_response { /* request to control the CSI transmitter */ #define GB_APB_REQUEST_CSI_TX_CONTROL 0x08 -/* request to control the CSI transmitter */ +/* request to control audio streaming */ #define GB_APB_REQUEST_AUDIO_CONTROL 0x09 /* vendor requests to enable/disable CPort features */ -- cgit v1.2.3-59-g8ed1b From 83d474f7aa467a8cc4cb33337a790fea891a98f1 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 10 Aug 2016 12:58:40 +0200 Subject: greybus: es2: fix USB vendor-request prefixes Make sure to use the common GB_APB prefix for all APBA USB vendor requests. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 10 +++++----- drivers/staging/greybus/greybus_protocols.h | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 3b8d81ef9726..996cfc63709c 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -833,7 +833,7 @@ static int timesync_enable(struct gb_host_device *hd, u8 count, request->strobe_delay = cpu_to_le32(strobe_delay); request->refclk = cpu_to_le32(refclk); retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - REQUEST_TIMESYNC_ENABLE, + GB_APB_REQUEST_TIMESYNC_ENABLE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, request, sizeof(*request), ES2_USB_CTRL_TIMEOUT); @@ -851,7 +851,7 @@ static int timesync_disable(struct gb_host_device *hd) struct usb_device *udev = es2->usb_dev; retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - REQUEST_TIMESYNC_DISABLE, + GB_APB_REQUEST_TIMESYNC_DISABLE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, NULL, 0, ES2_USB_CTRL_TIMEOUT); @@ -876,7 +876,7 @@ static int timesync_authoritative(struct gb_host_device *hd, u64 *frame_time) request->frame_time[i] = cpu_to_le64(frame_time[i]); retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - REQUEST_TIMESYNC_AUTHORITATIVE, + GB_APB_REQUEST_TIMESYNC_AUTHORITATIVE, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, request, sizeof(*request), ES2_USB_CTRL_TIMEOUT); @@ -899,7 +899,7 @@ static int timesync_get_last_event(struct gb_host_device *hd, u64 *frame_time) return -ENOMEM; retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - REQUEST_TIMESYNC_GET_LAST_EVENT, + GB_APB_REQUEST_TIMESYNC_GET_LAST_EVENT, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, response_frame_time, sizeof(*response_frame_time), @@ -1165,7 +1165,7 @@ static int arpc_send(struct es2_ap_dev *es2, struct arpc *rpc, int timeout) int retval; retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - APBA_REQUEST_ARPC_RUN, + GB_APB_REQUEST_ARPC_RUN, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0, 0, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 63fd48d991a1..4bf494c8d856 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -250,17 +250,17 @@ struct gb_control_intf_pm_response { #define GB_APB_REQUEST_CPORT_FEAT_EN 0x0b #define GB_APB_REQUEST_CPORT_FEAT_DIS 0x0c -/* TimeSync commands */ -#define REQUEST_TIMESYNC_ENABLE 0x0d -#define REQUEST_TIMESYNC_DISABLE 0x0e -#define REQUEST_TIMESYNC_AUTHORITATIVE 0x0f -#define REQUEST_TIMESYNC_GET_LAST_EVENT 0x10 +/* TimeSync requests */ +#define GB_APB_REQUEST_TIMESYNC_ENABLE 0x0d +#define GB_APB_REQUEST_TIMESYNC_DISABLE 0x0e +#define GB_APB_REQUEST_TIMESYNC_AUTHORITATIVE 0x0f +#define GB_APB_REQUEST_TIMESYNC_GET_LAST_EVENT 0x10 /* requests to set Greybus CPort flags */ #define GB_APB_REQUEST_CPORT_FLAGS 0x11 -/* ARPC command */ -#define APBA_REQUEST_ARPC_RUN 0x12 +/* ARPC request */ +#define GB_APB_REQUEST_ARPC_RUN 0x12 struct gb_apb_request_cport_flags { __le32 flags; -- cgit v1.2.3-59-g8ed1b From 7d0e76d6cbbcc7b71f65d39ee2b4231beab42faa Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 10 Aug 2016 12:58:41 +0200 Subject: greybus: arpc: move arpc definitions to their own header Move the ARPC definitions to their own header. ARPC is not part of greybus, but is rather an implementation-specific means of communicating with a certain class of host-device hardware. Note that the same is true for the APBA USB vendor requests, but we keep them in the greybus header for the time being. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arpc.h | 85 +++++++++++++++++++++++++++++ drivers/staging/greybus/es2.c | 1 + drivers/staging/greybus/greybus_protocols.h | 29 ---------- 3 files changed, 86 insertions(+), 29 deletions(-) create mode 100644 drivers/staging/greybus/arpc.h diff --git a/drivers/staging/greybus/arpc.h b/drivers/staging/greybus/arpc.h new file mode 100644 index 000000000000..7ce7a84cc33c --- /dev/null +++ b/drivers/staging/greybus/arpc.h @@ -0,0 +1,85 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014-2016 Google Inc. All rights reserved. + * Copyright(c) 2014-2016 Linaro Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program 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 version 2 for more details. + * + * BSD LICENSE + * + * Copyright(c) 2014-2016 Google Inc. All rights reserved. + * Copyright(c) 2014-2016 Linaro Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. or Linaro Ltd. 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 COPYRIGHT HOLDERS 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 GOOGLE INC. OR + * LINARO LTD. 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 __ARPC_H +#define __ARPC_H + +/* APBridgeA RPC (ARPC) */ + +enum arpc_result { + ARPC_SUCCESS = 0x00, + ARPC_NO_MEMORY = 0x01, + ARPC_INVALID = 0x02, + ARPC_TIMEOUT = 0x03, + ARPC_UNKNOWN_ERROR = 0xff, +}; + +struct arpc_request_message { + __le16 id; /* RPC unique id */ + __le16 size; /* Size in bytes of header + payload */ + __u8 type; /* RPC type */ + __u8 data[0]; /* ARPC data */ +} __packed; + +struct arpc_response_message { + __le16 id; /* RPC unique id */ + __u8 result; /* Result of RPC */ +} __packed; + + +/* ARPC requests */ +#define ARPC_TYPE_CPORT_RESET 0x00 + +struct arpc_cport_reset_req { + __le16 cport_id; +} __packed; + +#endif /* __ARPC_H */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 996cfc63709c..955b37d0c4c6 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -14,6 +14,7 @@ #include <linux/list.h> #include <asm/unaligned.h> +#include "arpc.h" #include "greybus.h" #include "greybus_trace.h" #include "kernel_ver.h" diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 4bf494c8d856..d43b36747113 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -268,35 +268,6 @@ struct gb_apb_request_cport_flags { #define GB_APB_CPORT_FLAG_HIGH_PRIO 0x02 } __packed; -/* APBridgeA RPC (ARPC) */ - -enum arpc_result { - ARPC_SUCCESS = 0x00, - ARPC_NO_MEMORY = 0x01, - ARPC_INVALID = 0x02, - ARPC_TIMEOUT = 0x03, - ARPC_UNKNOWN_ERROR = 0xff, -}; - -/* ARPC request */ -struct arpc_request_message { - __le16 id; /* RPC unique id */ - __le16 size; /* Size in bytes of header + payload */ - __u8 type; /* RPC type */ - __u8 data[0]; /* ARPC data */ -} __packed; - -/* ARPC response */ -struct arpc_response_message { - __le16 id; /* RPC unique id */ - __u8 result; /* Result of RPC */ -} __packed; - -#define ARPC_TYPE_CPORT_RESET 0x00 - -struct arpc_cport_reset_req { - __le16 cport_id; -} __packed; /* Firmware Download Protocol */ -- cgit v1.2.3-59-g8ed1b From 127bada1a1951d9624c08eeab99234ced0df900e Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Wed, 10 Aug 2016 09:08:19 +0200 Subject: greybus: camera: Remove reference to ara subdevice Remove last occurrence of "ara" term in camera driver. Replace reference to "ara subdevice" with "gmp subdevice" Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 0e7f64392bbd..94b67123c05b 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -848,7 +848,7 @@ static int gb_camera_request_handler(struct gb_operation *op) } /* ----------------------------------------------------------------------------- - * Interface with HOST ara camera. + * Interface with HOST gmp camera. */ static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code) { -- cgit v1.2.3-59-g8ed1b From 57fa2de1e5671c3632c5666ce08e03f1e3dc0242 Mon Sep 17 00:00:00 2001 From: Georgi Dobrev <gdobrev@mm-sol.com> Date: Tue, 9 Aug 2016 14:37:32 -0700 Subject: greybus: greybus-driver: Add intf_oops operation Add intf_oops operation to SVC Protocol. This operation will notify the AP about a fatal error in a module. The request has two arguments: -u8 intf - the interface in question -u8 reason - reason of the error The response has no payload. Upon receiving the Request, the driver disables the Interface. Signed-off-by: Georgi Dobrev <gdobrev@mm-sol.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Ashwin Chaugule <ashwinch@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 7 ++++ drivers/staging/greybus/svc.c | 50 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index d43b36747113..19ca1dbd9969 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1018,6 +1018,7 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_ACTIVATE 0x27 #define GB_SVC_TYPE_INTF_RESUME 0x28 #define GB_SVC_TYPE_INTF_MAILBOX_EVENT 0x29 +#define GB_SVC_TYPE_INTF_OOPS 0x2a /* Greybus SVC protocol status values */ #define GB_SVC_OP_SUCCESS 0x00 @@ -1357,6 +1358,12 @@ struct gb_svc_intf_mailbox_event_request { } __packed; /* intf_mailbox_event response has no payload */ +struct gb_svc_intf_oops_request { + __u8 intf_id; + __u8 reason; +} __packed; +/* intf_oops response has no payload */ + /* RAW */ diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 1170515a2a4e..c5aedd55e90d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -1112,6 +1112,37 @@ static void gb_svc_process_module_removed(struct gb_operation *operation) gb_module_put(module); } +static void gb_svc_process_intf_oops(struct gb_operation *operation) +{ + struct gb_svc_intf_oops_request *request; + struct gb_connection *connection = operation->connection; + struct gb_svc *svc = gb_connection_get_data(connection); + struct gb_interface *intf; + u8 intf_id; + u8 reason; + + /* The request message size has already been verified. */ + request = operation->request->payload; + intf_id = request->intf_id; + reason = request->reason; + + intf = gb_svc_interface_lookup(svc, intf_id); + if (!intf) { + dev_warn(&svc->dev, "unexpected interface-oops event %u\n", + intf_id); + return; + } + + dev_info(&svc->dev, "Deactivating interface %u, interface oops reason = %u\n", + intf_id, reason); + + mutex_lock(&intf->mutex); + intf->disconnected = true; + gb_interface_disable(intf); + gb_interface_deactivate(intf); + mutex_unlock(&intf->mutex); +} + static void gb_svc_process_intf_mailbox_event(struct gb_operation *operation) { struct gb_svc_intf_mailbox_event_request *request; @@ -1165,6 +1196,9 @@ static void gb_svc_process_deferred_request(struct work_struct *work) case GB_SVC_TYPE_INTF_MAILBOX_EVENT: gb_svc_process_intf_mailbox_event(operation); break; + case GB_SVC_TYPE_INTF_OOPS: + gb_svc_process_intf_oops(operation); + break; default: dev_err(&svc->dev, "bad deferred request type: 0x%02x\n", type); } @@ -1251,6 +1285,20 @@ static int gb_svc_module_removed_recv(struct gb_operation *op) return gb_svc_queue_deferred_request(op); } +static int gb_svc_intf_oops_recv(struct gb_operation *op) +{ + struct gb_svc *svc = gb_connection_get_data(op->connection); + struct gb_svc_intf_oops_request *request; + + if (op->request->payload_size < sizeof(*request)) { + dev_warn(&svc->dev, "short intf-oops request received (%zu < %zu)\n", + op->request->payload_size, sizeof(*request)); + return -EINVAL; + } + + return gb_svc_queue_deferred_request(op); +} + static int gb_svc_intf_mailbox_event_recv(struct gb_operation *op) { struct gb_svc *svc = gb_connection_get_data(op->connection); @@ -1326,6 +1374,8 @@ static int gb_svc_request_handler(struct gb_operation *op) return gb_svc_module_removed_recv(op); case GB_SVC_TYPE_INTF_MAILBOX_EVENT: return gb_svc_intf_mailbox_event_recv(op); + case GB_SVC_TYPE_INTF_OOPS: + return gb_svc_intf_oops_recv(op); default: dev_warn(&svc->dev, "unsupported request 0x%02x\n", type); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 811b1f58610d219610c223a1d63031171a079a7c Mon Sep 17 00:00:00 2001 From: Kris Huang <huang_kris@projectara.com> Date: Tue, 9 Aug 2016 11:28:37 +0800 Subject: greybus: lights: enable multi color LED support A backport (commit 79c4de08c0e5a26b04a4ac9e6543dad6379f0b40) was applied in kernel which adding attribute-group support in led-class. Remove the LED_HAVE_GROUPS flag check entirely that allow led drivers to create custom attributes like color/fade_in/fade_out. Testing Done: Compiled and verified on EVT2 and gpbridge-test module with device class daughter board. Signed-off-by: Kris Huang <huang_kris@projectara.com> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 7 ------- drivers/staging/greybus/light.c | 8 -------- 2 files changed, 15 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 62d640013035..97e7ac9498fd 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -252,13 +252,6 @@ static inline size_t sg_pcopy_from_buffer(struct scatterlist *sgl, list_entry((ptr)->prev, type, member) #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) -/* - * Before this version the led classdev did not support groups - */ -#define LED_HAVE_GROUPS -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) /* * At this time the internal API for the set brightness was changed to the async diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index fb41b0b1a98c..a57d59339bd6 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -172,7 +172,6 @@ static int __gb_lights_flash_brightness_set(struct gb_channel *channel) } #endif /* !LED_HAVE_FLASH */ -#ifdef LED_HAVE_GROUPS static int gb_lights_color_set(struct gb_channel *channel, u32 color); static int gb_lights_fade_set(struct gb_channel *channel); @@ -378,13 +377,6 @@ static int gb_lights_color_set(struct gb_channel *channel, u32 color) return ret; } -#else /* LED_HAVE_GROUPS */ -static int channel_attr_groups_set(struct gb_channel *channel, - struct led_classdev *cdev) -{ - return 0; -} -#endif /* !LED_HAVE_GROUPS */ static int __gb_lights_led_brightness_set(struct gb_channel *channel) { -- cgit v1.2.3-59-g8ed1b From b3136a77a49026c2f393e8ad6c99e3b5514d1e5a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Wed, 10 Aug 2016 17:02:05 +0200 Subject: greybus: fix up copyright dates on arpc.h I got them wrong, Johan was right, my fault. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arpc.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/arpc.h b/drivers/staging/greybus/arpc.h index 7ce7a84cc33c..1bfb7db8efe3 100644 --- a/drivers/staging/greybus/arpc.h +++ b/drivers/staging/greybus/arpc.h @@ -4,8 +4,8 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2014-2016 Google Inc. All rights reserved. - * Copyright(c) 2014-2016 Linaro Ltd. All rights reserved. + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -18,8 +18,8 @@ * * BSD LICENSE * - * Copyright(c) 2014-2016 Google Inc. All rights reserved. - * Copyright(c) 2014-2016 Linaro Ltd. All rights reserved. + * Copyright(c) 2016 Google Inc. All rights reserved. + * Copyright(c) 2016 Linaro Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions -- cgit v1.2.3-59-g8ed1b From a9dc1cf5b7b5ce8183249c2229ae0ab849525e37 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 10 Aug 2016 12:58:42 +0200 Subject: greybus: hd/es2: add cport_connected callback and ARPC Add a host-device cport_connected callback, which will be called after a connection has been created and that can be used by the host-device driver to make sure its internal state is updated to match the CPort attributes set by the SVC. This callback will eventually replace the cport_features_enable callback. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arpc.h | 6 ++++++ drivers/staging/greybus/es2.c | 20 ++++++++++++++++++++ drivers/staging/greybus/hd.h | 1 + 3 files changed, 27 insertions(+) diff --git a/drivers/staging/greybus/arpc.h b/drivers/staging/greybus/arpc.h index 1bfb7db8efe3..8a9027101b37 100644 --- a/drivers/staging/greybus/arpc.h +++ b/drivers/staging/greybus/arpc.h @@ -77,9 +77,15 @@ struct arpc_response_message { /* ARPC requests */ #define ARPC_TYPE_CPORT_RESET 0x00 +#define ARPC_TYPE_CPORT_CONNECTED 0x01 struct arpc_cport_reset_req { __le16 cport_id; } __packed; +struct arpc_cport_connected_req { + __le16 cport_id; +} __packed; + + #endif /* __ARPC_H */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 955b37d0c4c6..9af9b73818f4 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -746,6 +746,25 @@ static int cport_disable(struct gb_host_device *hd, u16 cport_id) return 0; } +static int es2_cport_connected(struct gb_host_device *hd, u16 cport_id) +{ + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct device *dev = &es2->usb_dev->dev; + struct arpc_cport_connected_req req; + int ret; + + req.cport_id = cpu_to_le16(cport_id); + ret = arpc_sync(es2, ARPC_TYPE_CPORT_CONNECTED, &req, sizeof(req), + NULL, ES2_ARPC_CPORT_TIMEOUT); + if (ret) { + dev_err(dev, "failed to set connected state for cport %u: %d\n", + cport_id, ret); + return ret; + } + + return 0; +} + static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) { int retval; @@ -930,6 +949,7 @@ static struct gb_hd_driver es2_driver = { .cport_release = es2_cport_release, .cport_enable = cport_enable, .cport_disable = cport_disable, + .cport_connected = es2_cport_connected, .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, .output = output, diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 6ea5e28d9eb9..be818797d6c6 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -22,6 +22,7 @@ struct gb_hd_driver { int (*cport_enable)(struct gb_host_device *hd, u16 cport_id, unsigned long flags); int (*cport_disable)(struct gb_host_device *hd, u16 cport_id); + int (*cport_connected)(struct gb_host_device *hd, u16 cport_id); int (*cport_flush)(struct gb_host_device *hd, u16 cport_id); int (*cport_ping)(struct gb_host_device *hd, u16 cport_id); int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id, -- cgit v1.2.3-59-g8ed1b From 0e7cd0d259890f21d13171d5798eed24a03903dc Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 10 Aug 2016 12:58:43 +0200 Subject: greybus: hd/es2: add cport_quiesce callback and ARPC Add a host-device cport_quiesce callback, which will be called as part of the new connection tear-down sequence to disable flow control after first making sure that enough peer buffer space is available for the next messages about to be sent. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arpc.h | 7 +++++++ drivers/staging/greybus/es2.c | 30 ++++++++++++++++++++++++++++++ drivers/staging/greybus/hd.h | 2 ++ 3 files changed, 39 insertions(+) diff --git a/drivers/staging/greybus/arpc.h b/drivers/staging/greybus/arpc.h index 8a9027101b37..9fb1608a35a0 100644 --- a/drivers/staging/greybus/arpc.h +++ b/drivers/staging/greybus/arpc.h @@ -78,6 +78,7 @@ struct arpc_response_message { /* ARPC requests */ #define ARPC_TYPE_CPORT_RESET 0x00 #define ARPC_TYPE_CPORT_CONNECTED 0x01 +#define ARPC_TYPE_CPORT_QUIESCE 0x02 struct arpc_cport_reset_req { __le16 cport_id; @@ -87,5 +88,11 @@ struct arpc_cport_connected_req { __le16 cport_id; } __packed; +struct arpc_cport_quiesce_req { + __le16 cport_id; + __le16 peer_space; + __le16 timeout; +} __packed; + #endif /* __ARPC_H */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 9af9b73818f4..42f248362b35 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -765,6 +765,35 @@ static int es2_cport_connected(struct gb_host_device *hd, u16 cport_id) return 0; } +static int es2_cport_quiesce(struct gb_host_device *hd, u16 cport_id, + size_t peer_space, unsigned int timeout) +{ + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct device *dev = &es2->usb_dev->dev; + struct arpc_cport_quiesce_req req; + int result; + int ret; + + if (peer_space > U16_MAX) + return -EINVAL; + + if (timeout > U16_MAX) + return -EINVAL; + + req.cport_id = cpu_to_le16(cport_id); + req.peer_space = cpu_to_le16(peer_space); + req.timeout = cpu_to_le16(timeout); + ret = arpc_sync(es2, ARPC_TYPE_CPORT_QUIESCE, &req, sizeof(req), + &result, ES2_ARPC_CPORT_TIMEOUT + timeout); + if (ret) { + dev_err(dev, "failed to quiesce cport %u: %d (%d)\n", + cport_id, ret, result); + return ret; + } + + return 0; +} + static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) { int retval; @@ -950,6 +979,7 @@ static struct gb_hd_driver es2_driver = { .cport_enable = cport_enable, .cport_disable = cport_disable, .cport_connected = es2_cport_connected, + .cport_quiesce = es2_cport_quiesce, .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, .output = output, diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index be818797d6c6..feaba0171786 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -25,6 +25,8 @@ struct gb_hd_driver { int (*cport_connected)(struct gb_host_device *hd, u16 cport_id); int (*cport_flush)(struct gb_host_device *hd, u16 cport_id); int (*cport_ping)(struct gb_host_device *hd, u16 cport_id); + int (*cport_quiesce)(struct gb_host_device *hd, u16 cport_id, + size_t peer_space, unsigned int timeout); int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); -- cgit v1.2.3-59-g8ed1b From 6471c0039cd2fa92bb7df1262c57cb5190eb4311 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 10 Aug 2016 12:58:44 +0200 Subject: greybus: hd/es2: add cport_clear callback and ARPC Add a host-device cport_clear callback, which will be called as part of the new connection tear-down sequence to reset the CPort state. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arpc.h | 5 +++++ drivers/staging/greybus/es2.c | 19 +++++++++++++++++++ drivers/staging/greybus/hd.h | 2 ++ 3 files changed, 26 insertions(+) diff --git a/drivers/staging/greybus/arpc.h b/drivers/staging/greybus/arpc.h index 9fb1608a35a0..6d277695e01d 100644 --- a/drivers/staging/greybus/arpc.h +++ b/drivers/staging/greybus/arpc.h @@ -79,6 +79,7 @@ struct arpc_response_message { #define ARPC_TYPE_CPORT_RESET 0x00 #define ARPC_TYPE_CPORT_CONNECTED 0x01 #define ARPC_TYPE_CPORT_QUIESCE 0x02 +#define ARPC_TYPE_CPORT_CLEAR 0x03 struct arpc_cport_reset_req { __le16 cport_id; @@ -94,5 +95,9 @@ struct arpc_cport_quiesce_req { __le16 timeout; } __packed; +struct arpc_cport_clear_req { + __le16 cport_id; +} __packed; + #endif /* __ARPC_H */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 42f248362b35..7f46f2a91833 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -794,6 +794,24 @@ static int es2_cport_quiesce(struct gb_host_device *hd, u16 cport_id, return 0; } +static int es2_cport_clear(struct gb_host_device *hd, u16 cport_id) +{ + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct device *dev = &es2->usb_dev->dev; + struct arpc_cport_clear_req req; + int ret; + + req.cport_id = cpu_to_le16(cport_id); + ret = arpc_sync(es2, ARPC_TYPE_CPORT_CLEAR, &req, sizeof(req), + NULL, ES2_ARPC_CPORT_TIMEOUT); + if (ret) { + dev_err(dev, "failed to clear cport %u: %d\n", cport_id, ret); + return ret; + } + + return 0; +} + static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) { int retval; @@ -980,6 +998,7 @@ static struct gb_hd_driver es2_driver = { .cport_disable = cport_disable, .cport_connected = es2_cport_connected, .cport_quiesce = es2_cport_quiesce, + .cport_clear = es2_cport_clear, .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, .output = output, diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index feaba0171786..67f4f3c7a8e6 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -27,6 +27,8 @@ struct gb_hd_driver { int (*cport_ping)(struct gb_host_device *hd, u16 cport_id); int (*cport_quiesce)(struct gb_host_device *hd, u16 cport_id, size_t peer_space, unsigned int timeout); + int (*cport_clear)(struct gb_host_device *hd, u16 cport_id); + int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); -- cgit v1.2.3-59-g8ed1b From 8f71a975d4150abf8ab1fbea65b51e73ff9936d6 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 10 Aug 2016 12:58:45 +0200 Subject: greybus: hd: add cport_shutdown callback Add a host-device cport_shutdown callback which will be called as part of the new connection tear-down sequence for offloaded connection in order to do a cport_shutdown operation on behalf of the AP. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/hd.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 67f4f3c7a8e6..1274224388ed 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -25,6 +25,8 @@ struct gb_hd_driver { int (*cport_connected)(struct gb_host_device *hd, u16 cport_id); int (*cport_flush)(struct gb_host_device *hd, u16 cport_id); int (*cport_ping)(struct gb_host_device *hd, u16 cport_id); + int (*cport_shutdown)(struct gb_host_device *hd, u16 cport_id, + u8 phase, unsigned int timeout); int (*cport_quiesce)(struct gb_host_device *hd, u16 cport_id, size_t peer_space, unsigned int timeout); int (*cport_clear)(struct gb_host_device *hd, u16 cport_id); -- cgit v1.2.3-59-g8ed1b From 9f77b80fdfeb72bdecf7855bb526ef14b6ed2ba8 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 10 Aug 2016 12:58:46 +0200 Subject: greybus: control: make disconnecting a core operation Make the control-protocol disconnecting operation a "core" operation. This is needed to be able to use the operation in the new connection tear-down sequence of control connections, which moves disconnecting after the flush barrier. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Sandeep Patil <sspatil@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/control.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/control.c b/drivers/staging/greybus/control.c index 658f8a641ee2..4716190e740a 100644 --- a/drivers/staging/greybus/control.c +++ b/drivers/staging/greybus/control.c @@ -152,13 +152,29 @@ int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id) int gb_control_disconnecting_operation(struct gb_control *control, u16 cport_id) { - struct gb_control_disconnecting_request request; + struct gb_control_disconnecting_request *request; + struct gb_operation *operation; + int ret; - request.cport_id = cpu_to_le16(cport_id); + operation = gb_operation_create_core(control->connection, + GB_CONTROL_TYPE_DISCONNECTING, + sizeof(*request), 0, 0, + GFP_KERNEL); + if (!operation) + return -ENOMEM; - return gb_operation_sync(control->connection, - GB_CONTROL_TYPE_DISCONNECTING, &request, - sizeof(request), NULL, 0); + request = operation->request->payload; + request->cport_id = cpu_to_le16(cport_id); + + ret = gb_operation_request_send_sync(operation); + if (ret) { + dev_err(&control->dev, "failed to send disconnecting: %d\n", + ret); + } + + gb_operation_put(operation); + + return ret; } int gb_control_mode_switch_operation(struct gb_control *control) -- cgit v1.2.3-59-g8ed1b From 295b5269d63841183257d9da9035dd3e3fde4200 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 10 Aug 2016 10:48:31 -0700 Subject: greybus: firmware: use 'tag' instead of 'firmware_tag' We already have another direct pointer for this, use that instead. Testing Done: Compiled. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-download.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 41a45ecb62b6..16de71cd9e33 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -245,7 +245,7 @@ static int fw_download_find_firmware(struct gb_operation *op) return -EINVAL; } - fw_req = find_firmware(fw_download, request->firmware_tag); + fw_req = find_firmware(fw_download, tag); if (IS_ERR(fw_req)) return PTR_ERR(fw_req); -- cgit v1.2.3-59-g8ed1b From d581bc88cb0b89ba3bf135855843916fc78e6693 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 10 Aug 2016 10:48:32 -0700 Subject: greybus: firmware: Fix typo in documentation s/shall be used the user/shall be used by the user/ Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../Documentation/firmware/firmware-management | 51 +++++++++++----------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/staging/greybus/Documentation/firmware/firmware-management b/drivers/staging/greybus/Documentation/firmware/firmware-management index 77d272c0f245..ced41d244005 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware-management +++ b/drivers/staging/greybus/Documentation/firmware/firmware-management @@ -139,44 +139,45 @@ struct fw_mgmt_ioc_backend_fw_update { 1. FW_MGMT_IOC_GET_INTF_FW: - This ioctl shall be used the user to get the version and firmware-tag of the - currently running Interface Firmware. All the fields of the 'struct + This ioctl shall be used by the user to get the version and firmware-tag of + the currently running Interface Firmware. All the fields of the 'struct fw_mgmt_ioc_get_fw' are filled by the kernel. 2. FW_MGMT_IOC_GET_BACKEND_FW: - This ioctl shall be used the user to get the version of a currently running - Backend Interface Firmware identified by a firmware-tag. The user is required - to fill the 'firmware_tag' field of the 'struct fw_mgmt_ioc_get_fw' in this - case. The 'major' and 'minor' fields are set by the kernel in response. + This ioctl shall be used by the user to get the version of a currently + running Backend Interface Firmware identified by a firmware-tag. The user is + required to fill the 'firmware_tag' field of the 'struct fw_mgmt_ioc_get_fw' + in this case. The 'major' and 'minor' fields are set by the kernel in + response. 3. FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE: - This ioctl shall be used the user to load an Interface Firmware package on an - Interface. The user needs to fill the 'firmware_tag' and 'load_method' fields - of the 'struct fw_mgmt_ioc_intf_load_and_validate'. The 'status', 'major' and - 'minor' fields are set by the kernel in response. + This ioctl shall be used by the user to load an Interface Firmware package on + an Interface. The user needs to fill the 'firmware_tag' and 'load_method' + fields of the 'struct fw_mgmt_ioc_intf_load_and_validate'. The 'status', + 'major' and 'minor' fields are set by the kernel in response. 4. FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE: - This ioctl shall be used the user to request an Interface to update a Backend - Interface Firmware. The user is required to fill the 'firmware_tag' field of - the 'struct fw_mgmt_ioc_get_fw' in this case. The 'status' field is set by - the kernel in response. + This ioctl shall be used by the user to request an Interface to update a + Backend Interface Firmware. The user is required to fill the 'firmware_tag' + field of the 'struct fw_mgmt_ioc_get_fw' in this case. The 'status' field is + set by the kernel in response. 5. FW_MGMT_IOC_SET_TIMEOUT_MS: - This ioctl shall be used the user to increase the timeout interval within + This ioctl shall be used by the user to increase the timeout interval within which the firmware must get loaded by the Module. The default timeout is 1 second. The user needs to pass the timeout in milliseconds. 6. FW_MGMT_IOC_MODE_SWITCH: - This ioctl shall be used the user to mode-switch the module to the previously - loaded interface firmware. If the interface firmware isn't loaded previously, - or if another unsuccessful FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE operation is - started after loading interface firmware, then the firmware core wouldn't - allow mode-switch. + This ioctl shall be used by the user to mode-switch the module to the + previously loaded interface firmware. If the interface firmware isn't loaded + previously, or if another unsuccessful FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE + operation is started after loading interface firmware, then the firmware core + wouldn't allow mode-switch. Sysfs Interfaces - Authentication @@ -279,13 +280,13 @@ struct cap_ioc_authenticate { 1. CAP_IOC_GET_ENDPOINT_UID: - This ioctl shall be used the user to get the endpoint UID associated with the - Interface. All the fields of the 'struct cap_ioc_get_endpoint_uid' are + This ioctl shall be used by the user to get the endpoint UID associated with + the Interface. All the fields of the 'struct cap_ioc_get_endpoint_uid' are filled by the kernel. 2. CAP_IOC_GET_IMS_CERTIFICATE: - This ioctl shall be used the user to retrieve one of the available + This ioctl shall be used by the user to retrieve one of the available cryptographic certificates held by the Interface for use in Component Authentication. The user is required to fill the 'certificate_class' and 'certificate_id' field of the 'struct cap_ioc_get_ims_certificate' in this @@ -295,8 +296,8 @@ struct cap_ioc_authenticate { 3. CAP_IOC_AUTHENTICATE: - This ioctl shall be used the user to authenticate the Module attached to an - Interface. The user needs to fill the 'auth_type', 'uid', and 'challenge' + This ioctl shall be used by the user to authenticate the Module attached to + an Interface. The user needs to fill the 'auth_type', 'uid', and 'challenge' fields of the 'struct cap_ioc_authenticate'. The other fields will be set by the kernel in response. The first 'signature_size' bytes of the 'signature' shall be read by the user and others must be discarded. -- cgit v1.2.3-59-g8ed1b From 769cb83704db127119d063b55091f782007368e7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 11 Aug 2016 13:27:22 -0700 Subject: greybus: firmware: Remove extra parenthesis Remove the unnecessary parenthesis. Reported-by: Alex Elder <alex.elder@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-download.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 16de71cd9e33..96c11aac4096 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -236,7 +236,7 @@ static int fw_download_find_firmware(struct gb_operation *op) } request = op->request->payload; - tag = (const char *)(request->firmware_tag); + tag = (const char *)request->firmware_tag; /* firmware_tag should be null-terminated */ if (strnlen(tag, GB_FIRMWARE_TAG_MAX_LEN) == GB_FIRMWARE_TAG_MAX_LEN) { -- cgit v1.2.3-59-g8ed1b From b2abeaa10d5711e7730bb07120dd60ae27d7b930 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 11 Aug 2016 13:27:23 -0700 Subject: greybus: firmware: s/_LEN/_SIZE Alex Elder pointed out that the macros also count the trailing NULL ('\0') character and so it should be using SIZE instead of LEN. This patch makes that change. Reported-by: Alex Elder <alex.elder@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/firmware/firmware-management | 8 ++++---- .../staging/greybus/Documentation/firmware/firmware.c | 6 +++--- drivers/staging/greybus/firmware.h | 2 +- drivers/staging/greybus/fw-download.c | 4 ++-- drivers/staging/greybus/fw-management.c | 18 +++++++++--------- drivers/staging/greybus/greybus_firmware.h | 10 +++++----- drivers/staging/greybus/greybus_protocols.h | 12 ++++++------ 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/staging/greybus/Documentation/firmware/firmware-management b/drivers/staging/greybus/Documentation/firmware/firmware-management index ced41d244005..7918257e5b3b 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware-management +++ b/drivers/staging/greybus/Documentation/firmware/firmware-management @@ -104,20 +104,20 @@ Following are the IOCTLs and their data structures available to the user: struct fw_mgmt_ioc_get_intf_version { - __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; __u16 major; __u16 minor; } __attribute__ ((__packed__)); struct fw_mgmt_ioc_get_backend_version { - __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; __u16 major; __u16 minor; __u8 status; } __attribute__ ((__packed__)); struct fw_mgmt_ioc_intf_load_and_validate { - __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; __u8 load_method; __u8 status; __u16 major; @@ -125,7 +125,7 @@ struct fw_mgmt_ioc_intf_load_and_validate { } __packed; struct fw_mgmt_ioc_backend_fw_update { - __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; __u8 status; } __packed; diff --git a/drivers/staging/greybus/Documentation/firmware/firmware.c b/drivers/staging/greybus/Documentation/firmware/firmware.c index 5170886f8afb..ff9382401030 100644 --- a/drivers/staging/greybus/Documentation/firmware/firmware.c +++ b/drivers/staging/greybus/Documentation/firmware/firmware.c @@ -108,7 +108,7 @@ static int update_intf_firmware(int fd) intf_load.minor = 0; strncpy((char *)&intf_load.firmware_tag, firmware_tag, - GB_FIRMWARE_U_TAG_MAX_LEN); + GB_FIRMWARE_U_TAG_MAX_SIZE); ret = ioctl(fd, FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE, &intf_load); if (ret < 0) { @@ -146,7 +146,7 @@ static int update_backend_firmware(int fd) printf("Getting Backend Firmware Version\n"); strncpy((char *)&backend_fw_info.firmware_tag, firmware_tag, - GB_FIRMWARE_U_TAG_MAX_LEN); + GB_FIRMWARE_U_TAG_MAX_SIZE); retry_fw_version: ret = ioctl(fd, FW_MGMT_IOC_GET_BACKEND_FW, &backend_fw_info); @@ -174,7 +174,7 @@ retry_fw_version: printf("Updating Backend Firmware\n"); strncpy((char *)&backend_update.firmware_tag, firmware_tag, - GB_FIRMWARE_U_TAG_MAX_LEN); + GB_FIRMWARE_U_TAG_MAX_SIZE); retry_fw_update: backend_update.status = 0; diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h index 841a0d532288..ec6bfb7f95c0 100644 --- a/drivers/staging/greybus/firmware.h +++ b/drivers/staging/greybus/firmware.h @@ -15,7 +15,7 @@ #define FW_NAME_PREFIX "gmp_" /* Length of the string in format: "FW_NAME_PREFIX""%08x_%08x_%08x_%08x_%s.tftf" */ -#define FW_NAME_LEN 56 +#define FW_NAME_SIZE 56 /* Firmware Management Protocol specific functions */ int fw_mgmt_init(void); diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 96c11aac4096..1c63256b96a6 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -23,7 +23,7 @@ struct fw_request { u8 firmware_id; bool disabled; bool timedout; - char name[FW_NAME_LEN]; + char name[FW_NAME_SIZE]; const struct firmware *fw; struct list_head node; @@ -239,7 +239,7 @@ static int fw_download_find_firmware(struct gb_operation *op) tag = (const char *)request->firmware_tag; /* firmware_tag should be null-terminated */ - if (strnlen(tag, GB_FIRMWARE_TAG_MAX_LEN) == GB_FIRMWARE_TAG_MAX_LEN) { + if (strnlen(tag, GB_FIRMWARE_TAG_MAX_SIZE) == GB_FIRMWARE_TAG_MAX_SIZE) { dev_err(fw_download->parent, "firmware-tag is not null-terminated\n"); return -EINVAL; diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index 7cbe71d581ac..3cd6cf0a656b 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -122,16 +122,16 @@ static int fw_mgmt_interface_fw_version_operation(struct fw_mgmt *fw_mgmt, fw_info->minor = le16_to_cpu(response.minor); strncpy(fw_info->firmware_tag, response.firmware_tag, - GB_FIRMWARE_TAG_MAX_LEN); + GB_FIRMWARE_TAG_MAX_SIZE); /* * The firmware-tag should be NULL terminated, otherwise throw error but * don't fail. */ - if (fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] != '\0') { + if (fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') { dev_err(fw_mgmt->parent, "fw-version: firmware-tag is not NULL terminated\n"); - fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] = '\0'; + fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] = '\0'; } return 0; @@ -151,13 +151,13 @@ static int fw_mgmt_load_and_validate_operation(struct fw_mgmt *fw_mgmt, } request.load_method = load_method; - strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_LEN); + strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_SIZE); /* * The firmware-tag should be NULL terminated, otherwise throw error and * fail. */ - if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] != '\0') { + if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') { dev_err(fw_mgmt->parent, "load-and-validate: firmware-tag is not NULL terminated\n"); return -EINVAL; } @@ -249,13 +249,13 @@ static int fw_mgmt_backend_fw_version_operation(struct fw_mgmt *fw_mgmt, int ret; strncpy(request.firmware_tag, fw_info->firmware_tag, - GB_FIRMWARE_TAG_MAX_LEN); + GB_FIRMWARE_TAG_MAX_SIZE); /* * The firmware-tag should be NULL terminated, otherwise throw error and * fail. */ - if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] != '\0') { + if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') { dev_err(fw_mgmt->parent, "backend-version: firmware-tag is not NULL terminated\n"); return -EINVAL; } @@ -302,13 +302,13 @@ static int fw_mgmt_backend_fw_update_operation(struct fw_mgmt *fw_mgmt, struct gb_fw_mgmt_backend_fw_update_request request; int ret; - strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_LEN); + strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_SIZE); /* * The firmware-tag should be NULL terminated, otherwise throw error and * fail. */ - if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_LEN - 1] != '\0') { + if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') { dev_err(fw_mgmt->parent, "backend-update: firmware-tag is not NULL terminated\n"); return -EINVAL; } diff --git a/drivers/staging/greybus/greybus_firmware.h b/drivers/staging/greybus/greybus_firmware.h index 82796512075b..277a2acce6fd 100644 --- a/drivers/staging/greybus/greybus_firmware.h +++ b/drivers/staging/greybus/greybus_firmware.h @@ -57,7 +57,7 @@ #include <linux/ioctl.h> #include <linux/types.h> -#define GB_FIRMWARE_U_TAG_MAX_LEN 10 +#define GB_FIRMWARE_U_TAG_MAX_SIZE 10 #define GB_FW_U_LOAD_METHOD_UNIPRO 0x01 #define GB_FW_U_LOAD_METHOD_INTERNAL 0x02 @@ -83,20 +83,20 @@ /* IOCTL support */ struct fw_mgmt_ioc_get_intf_version { - __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; __u16 major; __u16 minor; } __attribute__ ((__packed__)); struct fw_mgmt_ioc_get_backend_version { - __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; __u16 major; __u16 minor; __u8 status; } __attribute__ ((__packed__)); struct fw_mgmt_ioc_intf_load_and_validate { - __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; __u8 load_method; __u8 status; __u16 major; @@ -104,7 +104,7 @@ struct fw_mgmt_ioc_intf_load_and_validate { } __attribute__ ((__packed__)); struct fw_mgmt_ioc_backend_fw_update { - __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; __u8 status; } __attribute__ ((__packed__)); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index 19ca1dbd9969..be977592a056 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -276,11 +276,11 @@ struct gb_apb_request_cport_flags { #define GB_FW_DOWNLOAD_TYPE_FETCH_FIRMWARE 0x02 #define GB_FW_DOWNLOAD_TYPE_RELEASE_FIRMWARE 0x03 -#define GB_FIRMWARE_TAG_MAX_LEN 10 +#define GB_FIRMWARE_TAG_MAX_SIZE 10 /* firmware download find firmware request/response */ struct gb_fw_download_find_firmware_request { - __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; } __packed; struct gb_fw_download_find_firmware_response { @@ -340,7 +340,7 @@ struct gb_fw_download_release_firmware_request { /* firmware management interface firmware version request has no payload */ struct gb_fw_mgmt_interface_fw_version_response { - __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; __le16 major; __le16 minor; } __packed; @@ -349,7 +349,7 @@ struct gb_fw_mgmt_interface_fw_version_response { struct gb_fw_mgmt_load_and_validate_fw_request { __u8 request_id; __u8 load_method; - __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; } __packed; /* firmware management load and validate firmware response has no payload*/ @@ -364,7 +364,7 @@ struct gb_fw_mgmt_loaded_fw_request { /* firmware management backend firmware version request/response */ struct gb_fw_mgmt_backend_fw_version_request { - __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; } __packed; struct gb_fw_mgmt_backend_fw_version_response { @@ -376,7 +376,7 @@ struct gb_fw_mgmt_backend_fw_version_response { /* firmware management backend firmware update request */ struct gb_fw_mgmt_backend_fw_update_request { __u8 request_id; - __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_LEN]; + __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; } __packed; /* firmware management backend firmware update response has no payload */ -- cgit v1.2.3-59-g8ed1b From 0675363ac7ffe27a25bf237ce2bf1e084637a3a0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 11 Aug 2016 13:27:24 -0700 Subject: greybus: firmware: s/should/must Replace 'should' with 'must' to clear the expectation a bit more. Reported-by: Alex Elder <alex.elder@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-download.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/fw-download.c b/drivers/staging/greybus/fw-download.c index 1c63256b96a6..2d7246887547 100644 --- a/drivers/staging/greybus/fw-download.c +++ b/drivers/staging/greybus/fw-download.c @@ -238,7 +238,7 @@ static int fw_download_find_firmware(struct gb_operation *op) request = op->request->payload; tag = (const char *)request->firmware_tag; - /* firmware_tag should be null-terminated */ + /* firmware_tag must be null-terminated */ if (strnlen(tag, GB_FIRMWARE_TAG_MAX_SIZE) == GB_FIRMWARE_TAG_MAX_SIZE) { dev_err(fw_download->parent, "firmware-tag is not null-terminated\n"); -- cgit v1.2.3-59-g8ed1b From ac96a609dca8b1909f93d13277dd5368e2de5a2a Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Thu, 11 Aug 2016 13:27:25 -0700 Subject: greybus: firmware: add comment to show size calculations The firmware file name's maximum length is set to 56 right now, but it isn't very much readable how we got to that value (unless someone reads that line). Update the comment to show calculations. Reported-by: Alex Elder <alex.elder@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/firmware.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h index ec6bfb7f95c0..f4f0db1cefe8 100644 --- a/drivers/staging/greybus/firmware.h +++ b/drivers/staging/greybus/firmware.h @@ -14,7 +14,10 @@ #define FW_NAME_PREFIX "gmp_" -/* Length of the string in format: "FW_NAME_PREFIX""%08x_%08x_%08x_%08x_%s.tftf" */ +/* + * Length of the string in format: "FW_NAME_PREFIX""%08x_%08x_%08x_%08x_%s.tftf" + * (3 + 1 + 4 * (8 + 1) + 10 + 1 + 4 + 1) + */ #define FW_NAME_SIZE 56 /* Firmware Management Protocol specific functions */ -- cgit v1.2.3-59-g8ed1b From c9161d72b33ff5a3e43dd60c87adf113363866a1 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi <jacopo.mondi@linaro.org> Date: Fri, 12 Aug 2016 10:05:41 +0200 Subject: greybus: camera: Remove support for legacy modules Remove support for module implementing legacy version of camera bandwidth requirements specifications, now that all available camera modules have been updated to use the new version Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/camera.c | 121 +++------------------------- drivers/staging/greybus/greybus_protocols.h | 12 --- 2 files changed, 9 insertions(+), 124 deletions(-) diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index 94b67123c05b..e1f3046105c1 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -264,125 +264,35 @@ static int gb_camera_get_max_pkt_size(struct gb_camera *gcam, return max_pkt_size; } -/* - * Temporary support for camera modules implementing legacy version - * of camera specifications - */ -static int gb_camera_configure_stream_translate_deprecated( - struct gb_camera *gcam, - void *module_resp, - struct gb_camera_configure_streams_response *resp) -{ - unsigned int i; - struct gb_camera_configure_streams_response_deprecated *dresp = - (struct gb_camera_configure_streams_response_deprecated *) - module_resp; - - if (dresp->padding != 0) { - gcam_err(gcam, "legacy response padding != 0\n"); - return -EIO; - } - - resp->num_streams = dresp->num_streams; - resp->flags = dresp->flags; - resp->data_rate = dresp->bus_freq; - - for (i = 0; i < dresp->num_streams; i++) { - const struct gb_camera_fmt_info *fmt_info; - struct gb_camera_stream_config_response *cfg; - - if (dresp->config[i].padding || - dresp->config[i].max_pkt_size) { - gcam_err(gcam, "legacy stream #%u padding != 0\n", i); - return -EIO; - } - - resp->config[i] = dresp->config[i]; - cfg = &resp->config[i]; - - /* - * As implementations of legacy version of camera protocol do - * not provide the max_pkt_size attribute, re-calculate it on - * AP side. - */ - fmt_info = gb_camera_get_format_info(cfg->format); - if (!fmt_info) { - gcam_err(gcam, "unsupported greybus image format %d\n", - cfg->format); - return -EIO; - } - - if (fmt_info->bpp == 0) { - cfg->max_pkt_size = cpu_to_le16(4096); - } else if (fmt_info->bpp > 0) { - unsigned int width = le16_to_cpu(cfg->width); - - cfg->max_pkt_size = cpu_to_le32(width * fmt_info->bpp / 8); - } - } - - return 0; -} - /* * Validate the stream configuration response verifying padding is correctly * set and the returned number of streams is supported - * - * FIXME: The function also checks which protocol version the camera module - * implements and if it supports or not the new bandwidth requirements - * definition parameters. - * In case the camera implements the legacy version of protocol - * specifications, it gets translated to the new one. */ -static int gb_camera_configure_streams_validate_response( +static const int gb_camera_configure_streams_validate_response( struct gb_camera *gcam, - void *module_resp, - struct gb_camera_configure_streams_response *ap_resp, - unsigned int resp_size, + struct gb_camera_configure_streams_response *resp, unsigned int nstreams) { - struct gb_camera_configure_streams_response *resp; unsigned int i; - unsigned int module_resp_size = - resp_size - - sizeof(struct gb_camera_stream_config_response) * - nstreams; - - /* TODO: remove support for legacy camera modules */ - if (module_resp_size == GB_CAMERA_CONFIGURE_STREAMS_DEPRECATED_SIZE) - return gb_camera_configure_stream_translate_deprecated(gcam, - module_resp, ap_resp); - - if (module_resp_size != GB_CAMERA_CONFIGURE_STREAMS_SIZE) { - gcam_err(gcam, "unrecognized protocol version %u\n", - module_resp_size); - return -EIO; - } - - resp = (struct gb_camera_configure_streams_response *) module_resp; - *ap_resp = *resp; /* Validate the returned response structure */ - if (ap_resp->padding[0] || ap_resp->padding[1]) { + if (resp->padding[0] || resp->padding[1]) { gcam_err(gcam, "response padding != 0\n"); return -EIO; } - if (ap_resp->num_streams > nstreams) { + if (resp->num_streams > nstreams) { gcam_err(gcam, "got #streams %u > request %u\n", resp->num_streams, nstreams); return -EIO; } - for (i = 0; i < ap_resp->num_streams; i++) { + for (i = 0; i < resp->num_streams; i++) { struct gb_camera_stream_config_response *cfg = &resp->config[i]; - if (cfg->padding) { gcam_err(gcam, "stream #%u padding != 0\n", i); return -EIO; } - - ap_resp->config[i] = *cfg; } return 0; @@ -616,12 +526,10 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, { struct gb_camera_configure_streams_request *req; struct gb_camera_configure_streams_response *resp; - void *module_resp; unsigned int nstreams = *num_streams; unsigned int i; size_t req_size; size_t resp_size; - size_t module_resp_size; int ret; if (nstreams > GB_CAMERA_MAX_STREAMS) @@ -630,21 +538,11 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); - /* - * FIXME: Reserve enough space for the deprecated version of the - * configure_stream response, as it is bigger than the - * newly defined one - */ - module_resp_size = GB_CAMERA_CONFIGURE_STREAMS_DEPRECATED_SIZE + - nstreams * sizeof(resp->config[0]); - req = kmalloc(req_size, GFP_KERNEL); resp = kmalloc(resp_size, GFP_KERNEL); - module_resp = kmalloc(module_resp_size, GFP_KERNEL); - if (!req || !resp || !module_resp) { + if (!req || !resp) { kfree(req); kfree(resp); - kfree(module_resp); return -ENOMEM; } @@ -676,12 +574,12 @@ static int gb_camera_configure_streams(struct gb_camera *gcam, GB_CAMERA_TYPE_CONFIGURE_STREAMS, GB_OPERATION_FLAG_SHORT_RESPONSE, req, req_size, - module_resp, &module_resp_size); + resp, &resp_size); if (ret < 0) goto done; - ret = gb_camera_configure_streams_validate_response(gcam, - module_resp, resp, module_resp_size, nstreams); + ret = gb_camera_configure_streams_validate_response(gcam, resp, + nstreams); if (ret < 0) goto done; @@ -750,7 +648,6 @@ done_skip_pm_put: mutex_unlock(&gcam->mutex); kfree(req); kfree(resp); - kfree(module_resp); return ret; } diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index be977592a056..dd84e7569384 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -1678,17 +1678,6 @@ struct gb_camera_stream_config_response { __le32 max_size; } __packed; -struct gb_camera_configure_streams_response_deprecated { - __u8 num_streams; - __u8 flags; - __u8 num_lanes; - __u8 padding; - __le32 bus_freq; - __le32 lines_per_second; - struct gb_camera_stream_config_response config[0]; -} __packed; -#define GB_CAMERA_CONFIGURE_STREAMS_DEPRECATED_SIZE 12 - struct gb_camera_configure_streams_response { __u8 num_streams; #define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED 0x01 @@ -1697,7 +1686,6 @@ struct gb_camera_configure_streams_response { __le32 data_rate; struct gb_camera_stream_config_response config[0]; }; -#define GB_CAMERA_CONFIGURE_STREAMS_SIZE 8 /* Greybus Camera Capture request payload - response has no payload */ struct gb_camera_capture_request { -- cgit v1.2.3-59-g8ed1b From 5f62eab04ab7d78afd8acf00c48a7a1539794e1f Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 17 Aug 2016 15:43:32 +0200 Subject: greybus: es2: fix memory leak in probe error path In case a bulk-in transfer-buffer allocation failed during probe, we'd currently leak the corresponding URB. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 7f46f2a91833..8803bc9d410d 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1623,6 +1623,8 @@ static int ap_probe(struct usb_interface *interface, retval = -ENOMEM; goto error; } + cport_in->urb[i] = urb; + buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL); if (!buffer) { retval = -ENOMEM; @@ -1634,7 +1636,7 @@ static int ap_probe(struct usb_interface *interface, cport_in->endpoint), buffer, ES2_GBUF_MSG_SIZE_MAX, cport_in_callback, hd); - cport_in->urb[i] = urb; + cport_in->buffer[i] = buffer; } } @@ -1649,6 +1651,8 @@ static int ap_probe(struct usb_interface *interface, retval = -ENOMEM; goto error; } + es2->arpc_urb[i] = urb; + buffer = kmalloc(ARPC_IN_SIZE_MAX, GFP_KERNEL); if (!buffer) { retval = -ENOMEM; @@ -1661,7 +1665,6 @@ static int ap_probe(struct usb_interface *interface, buffer, ARPC_IN_SIZE_MAX, arpc_in_callback, es2); - es2->arpc_urb[i] = urb; es2->arpc_buffer[i] = buffer; } -- cgit v1.2.3-59-g8ed1b From 0900845ab741dd7b0b8b3f03ded0ffc6a348dd90 Mon Sep 17 00:00:00 2001 From: Ann Chen <chen_ann@projectara.com> Date: Wed, 17 Aug 2016 16:38:56 +0800 Subject: greybus: Add workqueue to handle vibrator timeout In the beginning, module side can control the vibrator timeout value, it can disable vibrator until timeout. But after Runtime PM control added in, AP side didn't know when module can be suspended, the vibrator task will be interrupted by suspending event. Because of this problem, the module can not be in charge of counting down the timeout value, it is now up to the AP to manage this. So add workqueue to handle the vibrator timeout. Signed-off-by: Ann Chen <chen_ann@projectara.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/vibrator.c | 47 ++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index db5583962101..7296a4dd0a0c 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -21,36 +21,27 @@ struct gb_vibrator_device { struct gb_connection *connection; struct device *dev; int minor; /* vibrator minor number */ + struct delayed_work delayed_work; }; /* Greybus Vibrator operation types */ #define GB_VIBRATOR_TYPE_ON 0x02 #define GB_VIBRATOR_TYPE_OFF 0x03 -struct gb_vibrator_on_request { - __le16 timeout_ms; -}; - -static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) +static int turn_off(struct gb_vibrator_device *vib) { - struct gb_vibrator_on_request request; struct gb_bundle *bundle = vib->connection->bundle; int ret; - ret = gb_pm_runtime_get_sync(bundle); - if (ret) - return ret; - - request.timeout_ms = cpu_to_le16(timeout_ms); - ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, - &request, sizeof(request), NULL, 0); + ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, + NULL, 0, NULL, 0); gb_pm_runtime_put_autosuspend(bundle); return ret; } -static int turn_off(struct gb_vibrator_device *vib) +static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) { struct gb_bundle *bundle = vib->connection->bundle; int ret; @@ -59,12 +50,29 @@ static int turn_off(struct gb_vibrator_device *vib) if (ret) return ret; - ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, + /* Vibrator was switched ON earlier */ + if (cancel_delayed_work_sync(&vib->delayed_work)) + turn_off(vib); + + ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, NULL, 0, NULL, 0); + if (ret) { + gb_pm_runtime_put_autosuspend(bundle); + return ret; + } - gb_pm_runtime_put_autosuspend(bundle); + schedule_delayed_work(&vib->delayed_work, msecs_to_jiffies(timeout_ms)); - return ret; + return 0; +} + +static void gb_vibrator_worker(struct work_struct *work) +{ + struct delayed_work *delayed_work = to_delayed_work(work); + struct gb_vibrator_device *vib = + container_of(delayed_work, struct gb_vibrator_device, delayed_work); + + turn_off(vib); } static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, @@ -174,6 +182,8 @@ static int gb_vibrator_probe(struct gb_bundle *bundle, } #endif + INIT_DELAYED_WORK(&vib->delayed_work, gb_vibrator_worker); + gb_pm_runtime_put_autosuspend(bundle); return 0; @@ -199,6 +209,9 @@ static void gb_vibrator_disconnect(struct gb_bundle *bundle) if (ret) gb_pm_runtime_get_noresume(bundle); + if (cancel_delayed_work_sync(&vib->delayed_work)) + turn_off(vib); + #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); #endif -- cgit v1.2.3-59-g8ed1b From 15c726ea5218b4e40d2331bd8b25b85848f73c42 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Tue, 16 Aug 2016 12:36:50 +0530 Subject: greybus: audio: Add check for invalid index while mapping control While mapping control id to define DAPM routes, invalid control index may cause kernel oops. Add extra check to validate index while mapping names to control_id. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.h | 1 + drivers/staging/greybus/audio_topology.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 0de2ad99003b..0153809e72ab 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -132,6 +132,7 @@ struct gbaudio_control { char *name; char *wname; const char * const *texts; + int items; struct list_head list; }; diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 937529653cd3..5c5b813b75d3 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -64,6 +64,8 @@ static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, if (control->id == control_id) { if (index == GBAUDIO_INVALID_ID) return control->name; + if (index >= control->items) + return NULL; return control->texts[index]; } } @@ -71,6 +73,8 @@ static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, if (control->id == control_id) { if (index == GBAUDIO_INVALID_ID) return control->name; + if (index >= control->items) + return NULL; return control->texts[index]; } } @@ -1038,6 +1042,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, csize += gbenum->names_length; control->texts = (const char * const *) gb_generate_enum_strings(module, gbenum); + control->items = gbenum->items; } else csize = sizeof(struct gb_audio_control); *w_size += csize; @@ -1184,6 +1189,7 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module, csize += gbenum->names_length; control->texts = (const char * const *) gb_generate_enum_strings(module, gbenum); + control->items = gbenum->items; } else csize = sizeof(struct gb_audio_control); -- cgit v1.2.3-59-g8ed1b From 6198f892fafbb3e5875c8a410eb2229a9cbac6ca Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Tue, 16 Aug 2016 12:36:51 +0530 Subject: greybus: Use valid control pointer while freeing memory While releasing memory during error path exit, invalid memory pointer was used for dapm_routes. Use a valid one. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 5c5b813b75d3..e54078ad0583 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -1317,7 +1317,7 @@ static int gbaudio_tplg_process_routes(struct gbaudio_module_info *module, return 0; error: - devm_kfree(module->dev, dapm_routes); + devm_kfree(module->dev, module->dapm_routes); return ret; } -- cgit v1.2.3-59-g8ed1b From c4582f9d7969019dc67234e96340e21dd3712c46 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Tue, 16 Aug 2016 22:31:55 +0100 Subject: greybus: power_supply: add callback to handle power supply changes When checking for property changes we may need to act upon that change besides reporting it using power_supply_changed. So, add a function that will be call if the specific property changed. As at it, adjust some indentation of the psy_props_changes array. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/power_supply.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 058fd3c60bd4..aeb6a07e43a6 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -70,17 +70,22 @@ static unsigned int update_interval_max = 30 * HZ; struct gb_power_supply_changes { enum power_supply_property prop; u32 tolerance_change; + void (*prop_changed)(struct gb_power_supply *gbpsy, + struct gb_power_supply_prop *prop); }; static const struct gb_power_supply_changes psy_props_changes[] = { - { .prop = GB_POWER_SUPPLY_PROP_STATUS, - .tolerance_change = 0, + { .prop = GB_POWER_SUPPLY_PROP_STATUS, + .tolerance_change = 0, + .prop_changed = NULL, }, - { .prop = GB_POWER_SUPPLY_PROP_TEMP, - .tolerance_change = 500, + { .prop = GB_POWER_SUPPLY_PROP_TEMP, + .tolerance_change = 500, + .prop_changed = NULL, }, - { .prop = GB_POWER_SUPPLY_PROP_ONLINE, - .tolerance_change = 0, + { .prop = GB_POWER_SUPPLY_PROP_ONLINE, + .tolerance_change = 0, + .prop_changed = NULL, }, }; @@ -349,18 +354,25 @@ static void check_changed(struct gb_power_supply *gbpsy, const struct gb_power_supply_changes *psyc; int val = prop->val; int prev_val = prop->previous_val; + bool changed = false; int i; for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) { psyc = &psy_props_changes[i]; if (prop->prop == psyc->prop) { if (!psyc->tolerance_change) - gbpsy->changed = true; + changed = true; else if (val < prev_val && prev_val - val > psyc->tolerance_change) - gbpsy->changed = true; + changed = true; else if (val > prev_val && val - prev_val > psyc->tolerance_change) + changed = true; + + if (changed && psyc->prop_changed) + psyc->prop_changed(gbpsy, prop); + + if (changed) gbpsy->changed = true; break; } -- cgit v1.2.3-59-g8ed1b From 5f66d62e8372d8a9c97bd75d772a5a5788925218 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Tue, 16 Aug 2016 22:31:56 +0100 Subject: greybus: power_supply: fix update interval check at request handler We use the update interval to control the remove path and we set it to zero when we do not want to have more updates in transit. That means that the check in the request handler needs to be for interval update zero to discard the newly received request and not the other way around like it is. This will fix the issue that all incoming requests were being discard. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/power_supply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index aeb6a07e43a6..3d6f81017d98 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -982,7 +982,7 @@ static int gb_supplies_request_handler(struct gb_operation *op) * running. For that just check update_interval. */ gbpsy = &supplies->supply[psy_id]; - if (gbpsy->update_interval) { + if (!gbpsy->update_interval) { ret = -ESHUTDOWN; goto out_unlock; } -- cgit v1.2.3-59-g8ed1b From b5fbe819af2f8d8dceb421635a976462bb6885b8 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Tue, 16 Aug 2016 22:31:57 +0100 Subject: greybus: power_supply: invalidate cache at update request When we receive a update request we shall not trust the cache mechanism and for that we need a way to invalidate the cache. Add a field that will control the cache status and refactor the code to check if cache is valid in a helper function. This will fix the scenario where an update request is received within the cache expiration time after a previous update as happened and would be ignored. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/power_supply.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 3d6f81017d98..199a19a634b6 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -45,6 +45,7 @@ struct gb_power_supply { u8 properties_count; u8 properties_count_str; unsigned long last_update; + u8 cache_invalid; unsigned int update_interval; bool changed; struct gb_power_supply_prop *props; @@ -636,15 +637,28 @@ static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy, return 0; } -static int gb_power_supply_status_get(struct gb_power_supply *gbpsy) +static int is_cache_valid(struct gb_power_supply *gbpsy) { - int ret = 0; - int i; + /* check if cache is good enough or it has expired */ + if (gbpsy->cache_invalid) { + gbpsy->cache_invalid = 0; + return 0; + } - /* check if cache is good enough */ if (gbpsy->last_update && time_is_after_jiffies(gbpsy->last_update + msecs_to_jiffies(cache_time))) + return 1; + + return 0; +} + +static int gb_power_supply_status_get(struct gb_power_supply *gbpsy) +{ + int ret = 0; + int i; + + if (is_cache_valid(gbpsy)) return 0; for (i = 0; i < gbpsy->properties_count; i++) { @@ -987,8 +1001,15 @@ static int gb_supplies_request_handler(struct gb_operation *op) goto out_unlock; } - if (event & GB_POWER_SUPPLY_UPDATE) + if (event & GB_POWER_SUPPLY_UPDATE) { + /* + * we need to make sure we invalidate cache, if not no new + * values for the properties will be fetch and the all propose + * of this event is missed + */ + gbpsy->cache_invalid = 1; gb_power_supply_status_update(gbpsy); + } out_unlock: mutex_unlock(&supplies->supplies_lock); -- cgit v1.2.3-59-g8ed1b From 6d13b6ff112b9a047f82d6cc01aa9d32c9133d8d Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Thu, 18 Aug 2016 17:43:22 +0200 Subject: greybus: svc: drop all symbol exports The svc functions are only supposed to be called by core and should not be exported. Most of these functions should never have been exported in the first place, while a few no longer needs to be (e.g. since core gained support for offloaded connections). Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index c5aedd55e90d..162ba7c5af4f 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -447,7 +447,6 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, return 0; } -EXPORT_SYMBOL_GPL(gb_svc_dme_peer_get); int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 value) @@ -480,7 +479,6 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, return 0; } -EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, @@ -499,7 +497,6 @@ int gb_svc_connection_create(struct gb_svc *svc, return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_connection_create); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) @@ -520,7 +517,6 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, intf1_id, cport1_id, intf2_id, cport2_id, ret); } } -EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, u32 strobe_delay, u32 refclk) @@ -536,7 +532,6 @@ int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, GB_SVC_TYPE_TIMESYNC_ENABLE, &request, sizeof(request), NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_timesync_enable); int gb_svc_timesync_disable(struct gb_svc *svc) { @@ -546,7 +541,6 @@ int gb_svc_timesync_disable(struct gb_svc *svc) GB_SVC_TYPE_TIMESYNC_DISABLE, NULL, 0, NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_timesync_disable); int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time) { @@ -564,7 +558,6 @@ int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time) frame_time[i] = le64_to_cpu(response.frame_time[i]); return 0; } -EXPORT_SYMBOL_GPL(gb_svc_timesync_authoritative); int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time) { @@ -582,7 +575,6 @@ int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time) *frame_time = le64_to_cpu(response.frame_time); return 0; } -EXPORT_SYMBOL_GPL(gb_svc_timesync_ping); int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask) { @@ -595,7 +587,6 @@ int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask) &request, sizeof(request), NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_timesync_wake_pins_acquire); int gb_svc_timesync_wake_pins_release(struct gb_svc *svc) { @@ -605,7 +596,6 @@ int gb_svc_timesync_wake_pins_release(struct gb_svc *svc) GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE, NULL, 0, NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_timesync_wake_pins_release); /* Creates bi-directional routes between the devices */ int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, @@ -685,7 +675,6 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, return 0; } -EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); int gb_svc_intf_set_power_mode_hibernate(struct gb_svc *svc, u8 intf_id) { @@ -728,7 +717,6 @@ int gb_svc_ping(struct gb_svc *svc) NULL, 0, NULL, 0, GB_OPERATION_TIMEOUT_DEFAULT * 2); } -EXPORT_SYMBOL_GPL(gb_svc_ping); static int gb_svc_version_request(struct gb_operation *op) { -- cgit v1.2.3-59-g8ed1b From 46bb647b1ca0f136b991151e3dd365b751572f40 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 18 Aug 2016 21:30:19 +0200 Subject: greybus: Revert "svc: drop all symbol exports" This reverts commit a0e559d03bbb3f9774a091e31c0f77d98db1f60d. It breaks the camera driver :( Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 162ba7c5af4f..c5aedd55e90d 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -447,6 +447,7 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, return 0; } +EXPORT_SYMBOL_GPL(gb_svc_dme_peer_get); int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 value) @@ -479,6 +480,7 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, return 0; } +EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, @@ -497,6 +499,7 @@ int gb_svc_connection_create(struct gb_svc *svc, return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); } +EXPORT_SYMBOL_GPL(gb_svc_connection_create); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) @@ -517,6 +520,7 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, intf1_id, cport1_id, intf2_id, cport2_id, ret); } } +EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, u32 strobe_delay, u32 refclk) @@ -532,6 +536,7 @@ int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, GB_SVC_TYPE_TIMESYNC_ENABLE, &request, sizeof(request), NULL, 0); } +EXPORT_SYMBOL_GPL(gb_svc_timesync_enable); int gb_svc_timesync_disable(struct gb_svc *svc) { @@ -541,6 +546,7 @@ int gb_svc_timesync_disable(struct gb_svc *svc) GB_SVC_TYPE_TIMESYNC_DISABLE, NULL, 0, NULL, 0); } +EXPORT_SYMBOL_GPL(gb_svc_timesync_disable); int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time) { @@ -558,6 +564,7 @@ int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time) frame_time[i] = le64_to_cpu(response.frame_time[i]); return 0; } +EXPORT_SYMBOL_GPL(gb_svc_timesync_authoritative); int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time) { @@ -575,6 +582,7 @@ int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time) *frame_time = le64_to_cpu(response.frame_time); return 0; } +EXPORT_SYMBOL_GPL(gb_svc_timesync_ping); int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask) { @@ -587,6 +595,7 @@ int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask) &request, sizeof(request), NULL, 0); } +EXPORT_SYMBOL_GPL(gb_svc_timesync_wake_pins_acquire); int gb_svc_timesync_wake_pins_release(struct gb_svc *svc) { @@ -596,6 +605,7 @@ int gb_svc_timesync_wake_pins_release(struct gb_svc *svc) GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE, NULL, 0, NULL, 0); } +EXPORT_SYMBOL_GPL(gb_svc_timesync_wake_pins_release); /* Creates bi-directional routes between the devices */ int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, @@ -675,6 +685,7 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, return 0; } +EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); int gb_svc_intf_set_power_mode_hibernate(struct gb_svc *svc, u8 intf_id) { @@ -717,6 +728,7 @@ int gb_svc_ping(struct gb_svc *svc) NULL, 0, NULL, 0, GB_OPERATION_TIMEOUT_DEFAULT * 2); } +EXPORT_SYMBOL_GPL(gb_svc_ping); static int gb_svc_version_request(struct gb_operation *op) { -- cgit v1.2.3-59-g8ed1b From be67e7e54bb1c22e396285bd7b4721786cba1209 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 19 Aug 2016 09:42:21 +0200 Subject: greybus: svc: drop unnecessary symbol exports The svc functions are only supposed to be called by core and should not be exported. Most of these functions should never have been exported in the first place, while a few no longer needs to be (e.g. since core gained support for offloaded connections). The only remaining exception is gb_svc_intf_set_power_mode() which is needed by the camera driver until proper link management is in place. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index c5aedd55e90d..6182e1304e77 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -447,7 +447,6 @@ int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, return 0; } -EXPORT_SYMBOL_GPL(gb_svc_dme_peer_get); int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, u32 value) @@ -480,7 +479,6 @@ int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, return 0; } -EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set); int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, @@ -499,7 +497,6 @@ int gb_svc_connection_create(struct gb_svc *svc, return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_connection_create); void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, u8 intf2_id, u16 cport2_id) @@ -520,7 +517,6 @@ void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, intf1_id, cport1_id, intf2_id, cport2_id, ret); } } -EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, u32 strobe_delay, u32 refclk) @@ -536,7 +532,6 @@ int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, GB_SVC_TYPE_TIMESYNC_ENABLE, &request, sizeof(request), NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_timesync_enable); int gb_svc_timesync_disable(struct gb_svc *svc) { @@ -546,7 +541,6 @@ int gb_svc_timesync_disable(struct gb_svc *svc) GB_SVC_TYPE_TIMESYNC_DISABLE, NULL, 0, NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_timesync_disable); int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time) { @@ -564,7 +558,6 @@ int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time) frame_time[i] = le64_to_cpu(response.frame_time[i]); return 0; } -EXPORT_SYMBOL_GPL(gb_svc_timesync_authoritative); int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time) { @@ -582,7 +575,6 @@ int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time) *frame_time = le64_to_cpu(response.frame_time); return 0; } -EXPORT_SYMBOL_GPL(gb_svc_timesync_ping); int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask) { @@ -595,7 +587,6 @@ int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask) &request, sizeof(request), NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_timesync_wake_pins_acquire); int gb_svc_timesync_wake_pins_release(struct gb_svc *svc) { @@ -605,7 +596,6 @@ int gb_svc_timesync_wake_pins_release(struct gb_svc *svc) GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE, NULL, 0, NULL, 0); } -EXPORT_SYMBOL_GPL(gb_svc_timesync_wake_pins_release); /* Creates bi-directional routes between the devices */ int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, @@ -728,7 +718,6 @@ int gb_svc_ping(struct gb_svc *svc) NULL, 0, NULL, 0, GB_OPERATION_TIMEOUT_DEFAULT * 2); } -EXPORT_SYMBOL_GPL(gb_svc_ping); static int gb_svc_version_request(struct gb_operation *op) { -- cgit v1.2.3-59-g8ed1b From f0ec8cd5eb49e0360ce6ba865d5f2267b28d1f84 Mon Sep 17 00:00:00 2001 From: Mark Greer <mgreer@animalcreek.com> Date: Sat, 20 Aug 2016 16:25:06 -0700 Subject: greybus: audio: Fix incorrect direction value when enabling RX The direction value passed to gb_audio_apbridgea_register_cport() in the gbaudio_module_enable_rx() routine is TX and not RX like it should be so fix it. Testing Done: Recorded microphone data from a headset. Signed-off-by: Mark Greer <mgreer@animalcreek.com> Fixes: c80e7c6fafa5 ("audio: Split helper APIs based on stream direction") Tested-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewd-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 3eb3d2cf014a..6ebde18a5a53 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -207,7 +207,7 @@ static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, cportid = data->connection->hd_cport_id; ret = gb_audio_apbridgea_register_cport(data->connection, i2s_port, cportid, - AUDIO_APBRIDGEA_DIRECTION_TX); + AUDIO_APBRIDGEA_DIRECTION_RX); if (ret) { dev_err_ratelimited(module->dev, "reg_cport failed:%d\n", ret); -- cgit v1.2.3-59-g8ed1b From 0e352343c2e371ad618d8850f542e9ca230e3aa6 Mon Sep 17 00:00:00 2001 From: David Lin <dtwlin@google.com> Date: Mon, 22 Aug 2016 15:51:11 -0700 Subject: greybus: light: fix incorrect led attribute files allocation Fix incorrect attribute size when the channel supports fader, as well as fixing the issue that the attribute list is not null terminated. Testing Done: - Verified by setting brightness and color on red/green/blue leds of the device class test board. Signed-off-by: David Lin <dtwlin@google.com> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/light.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index a57d59339bd6..85bf5559f6fb 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -293,13 +293,14 @@ static int channel_attr_groups_set(struct gb_channel *channel, if (channel->flags & GB_LIGHT_CHANNEL_MULTICOLOR) size++; if (channel->flags & GB_LIGHT_CHANNEL_FADER) - size++; + size += 2; if (!size) return 0; /* Set attributes based in the channel flags */ - channel->attrs = kcalloc(size, sizeof(**channel->attrs), GFP_KERNEL); + channel->attrs = kcalloc(size + 1, sizeof(**channel->attrs), + GFP_KERNEL); if (!channel->attrs) return -ENOMEM; channel->attr_group = kcalloc(1, sizeof(*channel->attr_group), -- cgit v1.2.3-59-g8ed1b From cc43368a3cde151739ad20cbf71139530bd53f1a Mon Sep 17 00:00:00 2001 From: Kris Huang <huang_kris@projectara.com> Date: Thu, 25 Aug 2016 16:57:14 +0800 Subject: greybus: lights: Control runtime pm suspend/resume on AP side According to runtime pm architecture, the kernel side driver should be as smart as needed to know when the module is idle or active, so that it can issue the suspend/resume operations to the firmware side at the right time. To add logics prevents AP from issuing the suspend request to the firmware when a channel turning to active state, and put it to suspend if the state is going to inactive with still holding a reference. Testing Done: Compiled and verified on EVT2 and gpbridge-test module with device class daughter board. Signed-off-by: Kris Huang <huang_kris@projectara.com> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/light.c | 59 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 85bf5559f6fb..71db077765f7 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -45,6 +45,8 @@ struct gb_channel { bool is_registered; bool releasing; bool strobe_state; + bool active; + struct mutex lock; }; struct gb_light { @@ -384,11 +386,15 @@ static int __gb_lights_led_brightness_set(struct gb_channel *channel) struct gb_lights_set_brightness_request req; struct gb_connection *connection = get_conn_from_channel(channel); struct gb_bundle *bundle = connection->bundle; + bool old_active; int ret; + mutex_lock(&channel->lock); ret = gb_pm_runtime_get_sync(bundle); if (ret < 0) - return ret; + goto out_unlock; + + old_active = channel->active; req.light_id = channel->light->id; req.channel_id = channel->id; @@ -396,8 +402,29 @@ static int __gb_lights_led_brightness_set(struct gb_channel *channel) ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BRIGHTNESS, &req, sizeof(req), NULL, 0); + if (ret < 0) + goto out_pm_put; + + if (channel->led->brightness) + channel->active = true; + else + channel->active = false; + /* we need to keep module alive when turning to active state */ + if (!old_active && channel->active) + goto out_unlock; + + /* + * on the other hand if going to inactive we still hold a reference and + * need to put it, so we could go to suspend. + */ + if (old_active && !channel->active) + gb_pm_runtime_put_autosuspend(bundle); + +out_pm_put: gb_pm_runtime_put_autosuspend(bundle); +out_unlock: + mutex_unlock(&channel->lock); return ret; } @@ -476,14 +503,18 @@ static int gb_blink_set(struct led_classdev *cdev, unsigned long *delay_on, struct gb_connection *connection = get_conn_from_channel(channel); struct gb_bundle *bundle = connection->bundle; struct gb_lights_blink_request req; + bool old_active; int ret; if (channel->releasing) return -ESHUTDOWN; + mutex_lock(&channel->lock); ret = gb_pm_runtime_get_sync(bundle); if (ret < 0) - return ret; + goto out_unlock; + + old_active = channel->active; req.light_id = channel->light->id; req.channel_id = channel->id; @@ -492,8 +523,29 @@ static int gb_blink_set(struct led_classdev *cdev, unsigned long *delay_on, ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BLINK, &req, sizeof(req), NULL, 0); + if (ret < 0) + goto out_pm_put; + + if (delay_on) + channel->active = true; + else + channel->active = false; + + /* we need to keep module alive when turning to active state */ + if (!old_active && channel->active) + goto out_unlock; + /* + * on the other hand if going to inactive we still hold a reference and + * need to put it, so we could go to suspend. + */ + if (old_active && !channel->active) + gb_pm_runtime_put_autosuspend(bundle); + +out_pm_put: gb_pm_runtime_put_autosuspend(bundle); +out_unlock: + mutex_unlock(&channel->lock); return ret; } @@ -1061,6 +1113,8 @@ static int gb_lights_light_register(struct gb_light *light) ret = gb_lights_channel_register(&light->channels[i]); if (ret < 0) return ret; + + mutex_init(&light->channels[i].lock); } light->ready = true; @@ -1086,6 +1140,7 @@ static void gb_lights_channel_free(struct gb_channel *channel) kfree(channel->attr_groups); kfree(channel->color_name); kfree(channel->mode_name); + mutex_destroy(&channel->lock); } static void gb_lights_channel_release(struct gb_channel *channel) -- cgit v1.2.3-59-g8ed1b From 6ac9166d4e91a6dea03b64b64f128e0f927aa7ec Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Fri, 26 Aug 2016 11:52:05 +0100 Subject: greybus: power_supply: fix name setting location We were checking for existing power supply names in the wrong place, i.e, we were checking before any of the module power supply were registered, because of that of course no name collision was detected. Move the check to the register loop and with that we guarantee that this mechanism works for greybus power supply naming. Tested: using gbsim and using power supply with the same name and check that: 1. no problems creating sysfs entries; 2. naming is done following the desired rules. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/power_supply.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 199a19a634b6..578d38b25d71 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -870,19 +870,19 @@ static int gb_power_supply_config(struct gb_power_supplies *supplies, int id) if (ret < 0) return ret; - ret = gb_power_supply_prop_descriptors_get(gbpsy); - if (ret < 0) - return ret; - - /* guarantee that we have an unique name, before register */ - return __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name, - sizeof(gbpsy->name)); + return gb_power_supply_prop_descriptors_get(gbpsy); } static int gb_power_supply_enable(struct gb_power_supply *gbpsy) { int ret; + /* guarantee that we have an unique name, before register */ + ret = __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name, + sizeof(gbpsy->name)); + if (ret < 0) + return ret; + ret = gb_power_supply_register(gbpsy); if (ret < 0) return ret; -- cgit v1.2.3-59-g8ed1b From d2dee94b66162d0839dcccee663c90400492fd1b Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 26 Aug 2016 12:55:47 +0200 Subject: greybus: es2: implement flush callback Implement the flush callback which is called as part of connection tear down to flush host-device queues and stop any ongoing transfers. Note that this should be considered an optimisation of sort since if the CPort is stuck waiting for credit, the CPort is likely still stuck when we try to send the cport_shutdown request over it after the callback returns. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arpc.h | 4 ++++ drivers/staging/greybus/es2.c | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/staging/greybus/arpc.h b/drivers/staging/greybus/arpc.h index 6d277695e01d..d44434f7ed28 100644 --- a/drivers/staging/greybus/arpc.h +++ b/drivers/staging/greybus/arpc.h @@ -80,6 +80,7 @@ struct arpc_response_message { #define ARPC_TYPE_CPORT_CONNECTED 0x01 #define ARPC_TYPE_CPORT_QUIESCE 0x02 #define ARPC_TYPE_CPORT_CLEAR 0x03 +#define ARPC_TYPE_CPORT_FLUSH 0x04 struct arpc_cport_reset_req { __le16 cport_id; @@ -99,5 +100,8 @@ struct arpc_cport_clear_req { __le16 cport_id; } __packed; +struct arpc_cport_flush_req { + __le16 cport_id; +} __packed; #endif /* __ARPC_H */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 8803bc9d410d..1817c3535fa9 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -765,6 +765,24 @@ static int es2_cport_connected(struct gb_host_device *hd, u16 cport_id) return 0; } +static int es2_cport_flush(struct gb_host_device *hd, u16 cport_id) +{ + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct device *dev = &es2->usb_dev->dev; + struct arpc_cport_flush_req req; + int ret; + + req.cport_id = cpu_to_le16(cport_id); + ret = arpc_sync(es2, ARPC_TYPE_CPORT_FLUSH, &req, sizeof(req), + NULL, ES2_ARPC_CPORT_TIMEOUT); + if (ret) { + dev_err(dev, "failed to flush cport %u: %d\n", cport_id, ret); + return ret; + } + + return 0; +} + static int es2_cport_quiesce(struct gb_host_device *hd, u16 cport_id, size_t peer_space, unsigned int timeout) { @@ -997,6 +1015,7 @@ static struct gb_hd_driver es2_driver = { .cport_enable = cport_enable, .cport_disable = cport_disable, .cport_connected = es2_cport_connected, + .cport_flush = es2_cport_flush, .cport_quiesce = es2_cport_quiesce, .cport_clear = es2_cport_clear, .latency_tag_enable = latency_tag_enable, -- cgit v1.2.3-59-g8ed1b From 77e52b3b0b6489968fa7d230052afca47c556ad6 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 26 Aug 2016 12:55:48 +0200 Subject: greybus: es2: implement shutdown callback Implement the shutdown callback which is used to execute shutdown operations on offloaded connections. This adds a new shutdown ARPC. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arpc.h | 7 +++++++ drivers/staging/greybus/es2.c | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/drivers/staging/greybus/arpc.h b/drivers/staging/greybus/arpc.h index d44434f7ed28..d0230ab6b81f 100644 --- a/drivers/staging/greybus/arpc.h +++ b/drivers/staging/greybus/arpc.h @@ -81,6 +81,7 @@ struct arpc_response_message { #define ARPC_TYPE_CPORT_QUIESCE 0x02 #define ARPC_TYPE_CPORT_CLEAR 0x03 #define ARPC_TYPE_CPORT_FLUSH 0x04 +#define ARPC_TYPE_CPORT_SHUTDOWN 0x05 struct arpc_cport_reset_req { __le16 cport_id; @@ -104,4 +105,10 @@ struct arpc_cport_flush_req { __le16 cport_id; } __packed; +struct arpc_cport_shutdown_req { + __le16 cport_id; + __le16 timeout; + __u8 phase; +} __packed; + #endif /* __ARPC_H */ diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 1817c3535fa9..123dc9d882fb 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -783,6 +783,32 @@ static int es2_cport_flush(struct gb_host_device *hd, u16 cport_id) return 0; } +static int es2_cport_shutdown(struct gb_host_device *hd, u16 cport_id, + u8 phase, unsigned int timeout) +{ + struct es2_ap_dev *es2 = hd_to_es2(hd); + struct device *dev = &es2->usb_dev->dev; + struct arpc_cport_shutdown_req req; + int result; + int ret; + + if (timeout > U16_MAX) + return -EINVAL; + + req.cport_id = cpu_to_le16(cport_id); + req.timeout = cpu_to_le16(timeout); + req.phase = phase; + ret = arpc_sync(es2, ARPC_TYPE_CPORT_SHUTDOWN, &req, sizeof(req), + &result, ES2_ARPC_CPORT_TIMEOUT + timeout); + if (ret) { + dev_err(dev, "failed to send shutdown over cport %u: %d (%d)\n", + cport_id, ret, result); + return ret; + } + + return 0; +} + static int es2_cport_quiesce(struct gb_host_device *hd, u16 cport_id, size_t peer_space, unsigned int timeout) { @@ -1016,6 +1042,7 @@ static struct gb_hd_driver es2_driver = { .cport_disable = cport_disable, .cport_connected = es2_cport_connected, .cport_flush = es2_cport_flush, + .cport_shutdown = es2_cport_shutdown, .cport_quiesce = es2_cport_quiesce, .cport_clear = es2_cport_clear, .latency_tag_enable = latency_tag_enable, -- cgit v1.2.3-59-g8ed1b From aac0839ea20306401f6e18cf8ea97d98fd42bcb5 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 26 Aug 2016 12:55:49 +0200 Subject: greybus: connection: implement new connection handling Implement the new connection setup and tear-down sequences for control connections and bundle connections (offloaded and non-offloaded). Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 127 ++++++++++++++++++++-------- drivers/staging/greybus/greybus_protocols.h | 6 +- 2 files changed, 97 insertions(+), 36 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index ce8ba9ca8e5e..557075147f2d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -13,6 +13,9 @@ #include "greybus_trace.h" +#define GB_CONNECTION_CPORT_QUIESCE_TIMEOUT 1000 + + static void gb_connection_kref_release(struct kref *kref); @@ -312,6 +315,24 @@ static void gb_connection_hd_cport_disable(struct gb_connection *connection) } } +static int gb_connection_hd_cport_connected(struct gb_connection *connection) +{ + struct gb_host_device *hd = connection->hd; + int ret; + + if (!hd->driver->cport_connected) + return 0; + + ret = hd->driver->cport_connected(hd, connection->hd_cport_id); + if (ret) { + dev_err(&hd->dev, "%s: failed to set connected state: %d\n", + connection->name, ret); + return ret; + } + + return 0; +} + static int gb_connection_hd_cport_flush(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; @@ -330,34 +351,43 @@ static int gb_connection_hd_cport_flush(struct gb_connection *connection) return 0; } -static int -gb_connection_hd_cport_features_enable(struct gb_connection *connection) +static int gb_connection_hd_cport_quiesce(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; + size_t peer_space; int ret; - if (!hd->driver->cport_features_enable) - return 0; + peer_space = sizeof(struct gb_operation_msg_hdr) + + sizeof(struct gb_cport_shutdown_request); - ret = hd->driver->cport_features_enable(hd, connection->hd_cport_id); + if (connection->mode_switch) + peer_space += sizeof(struct gb_operation_msg_hdr); + + ret = hd->driver->cport_quiesce(hd, connection->hd_cport_id, + peer_space, + GB_CONNECTION_CPORT_QUIESCE_TIMEOUT); if (ret) { - dev_err(&hd->dev, "%s: failed to enable CPort features: %d\n", - connection->name, ret); + dev_err(&hd->dev, "%s: failed to quiesce host cport: %d\n", + connection->name, ret); return ret; } return 0; } -static void -gb_connection_hd_cport_features_disable(struct gb_connection *connection) +static int gb_connection_hd_cport_clear(struct gb_connection *connection) { struct gb_host_device *hd = connection->hd; + int ret; - if (!hd->driver->cport_features_disable) - return; + ret = hd->driver->cport_clear(hd, connection->hd_cport_id); + if (ret) { + dev_err(&hd->dev, "%s: failed to clear host cport: %d\n", + connection->name, ret); + return ret; + } - hd->driver->cport_features_disable(hd, connection->hd_cport_id); + return 0; } /* @@ -496,18 +526,23 @@ gb_connection_control_disconnected(struct gb_connection *connection) } } -static int gb_connection_ping_operation(struct gb_connection *connection) +static int gb_connection_shutdown_operation(struct gb_connection *connection, + u8 phase) { + struct gb_cport_shutdown_request *req; struct gb_operation *operation; int ret; operation = gb_operation_create_core(connection, - GB_REQUEST_TYPE_PING, - 0, 0, 0, + GB_REQUEST_TYPE_CPORT_SHUTDOWN, + sizeof(*req), 0, 0, GFP_KERNEL); if (!operation) return -ENOMEM; + req = operation->request->payload; + req->phase = phase; + ret = gb_operation_request_send_sync(operation); gb_operation_put(operation); @@ -515,32 +550,47 @@ static int gb_connection_ping_operation(struct gb_connection *connection) return ret; } -static int gb_connection_ping(struct gb_connection *connection) +static int gb_connection_cport_shutdown(struct gb_connection *connection, + u8 phase) { struct gb_host_device *hd = connection->hd; + const struct gb_hd_driver *drv = hd->driver; int ret; if (gb_connection_is_static(connection)) return 0; if (gb_connection_is_offloaded(connection)) { - if (!hd->driver->cport_ping) + if (!drv->cport_shutdown) return 0; - ret = hd->driver->cport_ping(hd, connection->hd_cport_id); + ret = drv->cport_shutdown(hd, connection->hd_cport_id, phase, + GB_OPERATION_TIMEOUT_DEFAULT); } else { - ret = gb_connection_ping_operation(connection); + ret = gb_connection_shutdown_operation(connection, phase); } if (ret) { - dev_err(&hd->dev, "%s: failed to send ping: %d\n", - connection->name, ret); + dev_err(&hd->dev, "%s: failed to send cport shutdown (phase %d): %d\n", + connection->name, phase, ret); return ret; } return 0; } +static int +gb_connection_cport_shutdown_phase_1(struct gb_connection *connection) +{ + return gb_connection_cport_shutdown(connection, 1); +} + +static int +gb_connection_cport_shutdown_phase_2(struct gb_connection *connection) +{ + return gb_connection_cport_shutdown(connection, 2); +} + /* * Cancel all active operations on a connection. * @@ -639,9 +689,9 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) ret = gb_connection_svc_connection_create(connection); if (ret) - goto err_hd_cport_disable; + goto err_hd_cport_clear; - ret = gb_connection_hd_cport_features_enable(connection); + ret = gb_connection_hd_cport_connected(connection); if (ret) goto err_svc_connection_destroy; @@ -659,8 +709,6 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx) return 0; err_control_disconnecting: - gb_connection_control_disconnecting(connection); - spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISCONNECTING; gb_connection_cancel_operations(connection, -ESHUTDOWN); @@ -669,13 +717,17 @@ err_control_disconnecting: /* Transmit queue should already be empty. */ gb_connection_hd_cport_flush(connection); - gb_connection_ping(connection); - gb_connection_hd_cport_features_disable(connection); + gb_connection_control_disconnecting(connection); + gb_connection_cport_shutdown_phase_1(connection); + gb_connection_hd_cport_quiesce(connection); + gb_connection_cport_shutdown_phase_2(connection); gb_connection_control_disconnected(connection); connection->state = GB_CONNECTION_STATE_DISABLED; err_svc_connection_destroy: gb_connection_svc_connection_destroy(connection); -err_hd_cport_disable: +err_hd_cport_clear: + gb_connection_hd_cport_clear(connection); + gb_connection_hd_cport_disable(connection); return ret; @@ -754,7 +806,10 @@ void gb_connection_mode_switch_prepare(struct gb_connection *connection) void gb_connection_mode_switch_complete(struct gb_connection *connection) { gb_connection_svc_connection_destroy(connection); + gb_connection_hd_cport_clear(connection); + gb_connection_hd_cport_disable(connection); + connection->mode_switch = false; } @@ -767,8 +822,6 @@ void gb_connection_disable(struct gb_connection *connection) trace_gb_connection_disable(connection); - gb_connection_control_disconnecting(connection); - spin_lock_irq(&connection->lock); connection->state = GB_CONNECTION_STATE_DISCONNECTING; gb_connection_cancel_operations(connection, -ESHUTDOWN); @@ -776,9 +829,10 @@ void gb_connection_disable(struct gb_connection *connection) gb_connection_hd_cport_flush(connection); - gb_connection_ping(connection); - gb_connection_hd_cport_features_disable(connection); - + gb_connection_control_disconnecting(connection); + gb_connection_cport_shutdown_phase_1(connection); + gb_connection_hd_cport_quiesce(connection); + gb_connection_cport_shutdown_phase_2(connection); gb_connection_control_disconnected(connection); connection->state = GB_CONNECTION_STATE_DISABLED; @@ -786,6 +840,8 @@ void gb_connection_disable(struct gb_connection *connection) /* control-connection tear down is deferred when mode switching */ if (!connection->mode_switch) { gb_connection_svc_connection_destroy(connection); + gb_connection_hd_cport_clear(connection); + gb_connection_hd_cport_disable(connection); } @@ -810,10 +866,11 @@ void gb_connection_disable_forced(struct gb_connection *connection) spin_unlock_irq(&connection->lock); gb_connection_hd_cport_flush(connection); - gb_connection_hd_cport_features_disable(connection); + gb_connection_svc_connection_destroy(connection); - gb_connection_hd_cport_disable(connection); + gb_connection_hd_cport_clear(connection); + gb_connection_hd_cport_disable(connection); out_unlock: mutex_unlock(&connection->mutex); } diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index dd84e7569384..b8ce99c51926 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -96,9 +96,13 @@ struct gb_operation_msg_hdr { /* Generic request types */ -#define GB_REQUEST_TYPE_PING 0x00 +#define GB_REQUEST_TYPE_CPORT_SHUTDOWN 0x00 #define GB_REQUEST_TYPE_INVALID 0x7f +struct gb_cport_shutdown_request { + __u8 phase; +} __packed; + /* Control Protocol */ -- cgit v1.2.3-59-g8ed1b From 2045c9f265a1ed882a8d773da916460697c51ffc Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 26 Aug 2016 12:55:50 +0200 Subject: greybus: hd/es2: remove obsolete callbacks Remove the now obsolete ping and cport_features_enable/disable callbacks. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 37 ----------------------------- drivers/staging/greybus/greybus_protocols.h | 4 ---- drivers/staging/greybus/hd.h | 3 --- 3 files changed, 44 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 123dc9d882fb..b225fc3e3e07 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -892,41 +892,6 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) return retval; } -static int cport_features_enable(struct gb_host_device *hd, u16 cport_id) -{ - int retval; - struct es2_ap_dev *es2 = hd_to_es2(hd); - struct usb_device *udev = es2->usb_dev; - - retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - GB_APB_REQUEST_CPORT_FEAT_EN, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES2_USB_CTRL_TIMEOUT); - if (retval < 0) - dev_err(&udev->dev, "Cannot enable CPort features for cport %u: %d\n", - cport_id, retval); - return retval; -} - -static int cport_features_disable(struct gb_host_device *hd, u16 cport_id) -{ - int retval; - struct es2_ap_dev *es2 = hd_to_es2(hd); - struct usb_device *udev = es2->usb_dev; - - retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - GB_APB_REQUEST_CPORT_FEAT_DIS, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, cport_id, 0, NULL, - 0, ES2_USB_CTRL_TIMEOUT); - if (retval < 0) - dev_err(&udev->dev, - "Cannot disable CPort features for cport %u: %d\n", - cport_id, retval); - return retval; -} - static int timesync_enable(struct gb_host_device *hd, u8 count, u64 frame_time, u32 strobe_delay, u32 refclk) { @@ -1048,8 +1013,6 @@ static struct gb_hd_driver es2_driver = { .latency_tag_enable = latency_tag_enable, .latency_tag_disable = latency_tag_disable, .output = output, - .cport_features_enable = cport_features_enable, - .cport_features_disable = cport_features_disable, .timesync_enable = timesync_enable, .timesync_disable = timesync_disable, .timesync_authoritative = timesync_authoritative, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b8ce99c51926..c4ef00548d51 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -250,10 +250,6 @@ struct gb_control_intf_pm_response { /* request to control audio streaming */ #define GB_APB_REQUEST_AUDIO_CONTROL 0x09 -/* vendor requests to enable/disable CPort features */ -#define GB_APB_REQUEST_CPORT_FEAT_EN 0x0b -#define GB_APB_REQUEST_CPORT_FEAT_DIS 0x0c - /* TimeSync requests */ #define GB_APB_REQUEST_TIMESYNC_ENABLE 0x0d #define GB_APB_REQUEST_TIMESYNC_DISABLE 0x0e diff --git a/drivers/staging/greybus/hd.h b/drivers/staging/greybus/hd.h index 1274224388ed..c4250cfe595f 100644 --- a/drivers/staging/greybus/hd.h +++ b/drivers/staging/greybus/hd.h @@ -24,7 +24,6 @@ struct gb_hd_driver { int (*cport_disable)(struct gb_host_device *hd, u16 cport_id); int (*cport_connected)(struct gb_host_device *hd, u16 cport_id); int (*cport_flush)(struct gb_host_device *hd, u16 cport_id); - int (*cport_ping)(struct gb_host_device *hd, u16 cport_id); int (*cport_shutdown)(struct gb_host_device *hd, u16 cport_id, u8 phase, unsigned int timeout); int (*cport_quiesce)(struct gb_host_device *hd, u16 cport_id, @@ -38,8 +37,6 @@ struct gb_hd_driver { int (*latency_tag_disable)(struct gb_host_device *hd, u16 cport_id); int (*output)(struct gb_host_device *hd, void *req, u16 size, u8 cmd, bool async); - int (*cport_features_enable)(struct gb_host_device *hd, u16 cport_id); - int (*cport_features_disable)(struct gb_host_device *hd, u16 cport_id); int (*timesync_enable)(struct gb_host_device *hd, u8 count, u64 frame_time, u32 strobe_delay, u32 refclk); int (*timesync_disable)(struct gb_host_device *hd); -- cgit v1.2.3-59-g8ed1b From 1b1732c4c473ba59901cf5cc5c6ef2d3578cce14 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 26 Aug 2016 12:55:51 +0200 Subject: greybus: es2: remove obsolete cport-reset ARPC Remove the now obsolete and redundant cport-reset ARPC, along with the consequently unused cport_disable callback. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/arpc.h | 5 ----- drivers/staging/greybus/es2.c | 38 -------------------------------------- 2 files changed, 43 deletions(-) diff --git a/drivers/staging/greybus/arpc.h b/drivers/staging/greybus/arpc.h index d0230ab6b81f..7fbddfc40d83 100644 --- a/drivers/staging/greybus/arpc.h +++ b/drivers/staging/greybus/arpc.h @@ -76,17 +76,12 @@ struct arpc_response_message { /* ARPC requests */ -#define ARPC_TYPE_CPORT_RESET 0x00 #define ARPC_TYPE_CPORT_CONNECTED 0x01 #define ARPC_TYPE_CPORT_QUIESCE 0x02 #define ARPC_TYPE_CPORT_CLEAR 0x03 #define ARPC_TYPE_CPORT_FLUSH 0x04 #define ARPC_TYPE_CPORT_SHUTDOWN 0x05 -struct arpc_cport_reset_req { - __le16 cport_id; -} __packed; - struct arpc_cport_connected_req { __le16 cport_id; } __packed; diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index b225fc3e3e07..df104796a285 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -611,32 +611,6 @@ static void message_cancel(struct gb_message *message) usb_free_urb(urb); } -static int cport_reset(struct gb_host_device *hd, u16 cport_id) -{ - struct es2_ap_dev *es2 = hd_to_es2(hd); - struct usb_device *udev = es2->usb_dev; - struct arpc_cport_reset_req req; - int retval; - int result; - - switch (cport_id) { - case GB_SVC_CPORT_ID: - case ES2_CPORT_CDSI0: - case ES2_CPORT_CDSI1: - return 0; - } - - req.cport_id = cpu_to_le16(cport_id); - retval = arpc_sync(es2, ARPC_TYPE_CPORT_RESET, &req, sizeof(req), - &result, ES2_ARPC_CPORT_TIMEOUT); - if (retval == -EREMOTEIO) { - dev_err(&udev->dev, "failed to reset cport %u: %d\n", cport_id, - result); - } - - return retval; -} - static int es2_cport_allocate(struct gb_host_device *hd, int cport_id, unsigned long flags) { @@ -735,17 +709,6 @@ out: return ret; } -static int cport_disable(struct gb_host_device *hd, u16 cport_id) -{ - int retval; - - retval = cport_reset(hd, cport_id); - if (retval) - return retval; - - return 0; -} - static int es2_cport_connected(struct gb_host_device *hd, u16 cport_id) { struct es2_ap_dev *es2 = hd_to_es2(hd); @@ -1004,7 +967,6 @@ static struct gb_hd_driver es2_driver = { .cport_allocate = es2_cport_allocate, .cport_release = es2_cport_release, .cport_enable = cport_enable, - .cport_disable = cport_disable, .cport_connected = es2_cport_connected, .cport_flush = es2_cport_flush, .cport_shutdown = es2_cport_shutdown, -- cgit v1.2.3-59-g8ed1b From 1f3e09e759061f803b03039070605b5a524da547 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Fri, 26 Aug 2016 12:59:45 +0200 Subject: greybus: svc: fix timeout indentation Make sure the timeout values are aligned. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/svc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 6182e1304e77..550055ec27a5 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -14,7 +14,7 @@ #define SVC_INTF_EJECT_TIMEOUT 9000 #define SVC_INTF_ACTIVATE_TIMEOUT 6000 -#define SVC_INTF_RESUME_TIMEOUT 3000 +#define SVC_INTF_RESUME_TIMEOUT 3000 struct gb_svc_deferred_request { struct work_struct work; -- cgit v1.2.3-59-g8ed1b From 79c222bcb72789456076a26a9bad2acc62cb2cdc Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 1 Sep 2016 11:38:38 +0530 Subject: greybus: audio: Remove unnecessary num_jack field from module_info snd_jack will be registered based on real capabilities shared by module's FW instead of parsing widgets and register it with fixed capabilities. Remove module_info->num_jack, since it is no more required. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 9 --------- drivers/staging/greybus/audio_codec.h | 1 - drivers/staging/greybus/audio_topology.c | 1 - 3 files changed, 11 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 6ebde18a5a53..810ac6269cff 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -707,15 +707,6 @@ static int gbaudio_init_jack(struct gbaudio_module_info *module, { int ret; - if (!module->num_jacks) - return 0; - - /* register jack(s) in case any */ - if (module->num_jacks > 1) { - dev_err(module->dev, "Currently supports max=1 jack\n"); - return -EINVAL; - } - snprintf(module->jack_name, NAME_SIZE, "GB %d Headset Jack", module->dev_id); ret = snd_soc_jack_new(codec, module->jack_name, GBCODEC_JACK_MASK, diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 0153809e72ab..5a397b0a1886 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -175,7 +175,6 @@ struct gbaudio_module_info { /* jack related */ char jack_name[NAME_SIZE]; char button_name[NAME_SIZE]; - int num_jacks; int jack_type; int button_status; struct snd_soc_jack headset_jack; diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index e54078ad0583..e2fc186756a7 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -1065,7 +1065,6 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, case snd_soc_dapm_hp: *dw = (struct snd_soc_dapm_widget) SND_SOC_DAPM_HP(w->name, gbcodec_event_hp); - module->num_jacks++; module->op_devices |= (GBAUDIO_DEVICE_OUT_WIRED_HEADSET | GBAUDIO_DEVICE_OUT_WIRED_HEADPHONE); module->ip_devices |= GBAUDIO_DEVICE_IN_WIRED_HEADSET; -- cgit v1.2.3-59-g8ed1b From a695c302b49c8bf10b5336585a533d363d6436db Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 1 Sep 2016 11:38:39 +0530 Subject: greybus: audio: Added jack_type support in topology struct This patch adds extra field jack_type to gb_audio_topology struct. Also, it defines bit fields to be used by module while defining it's jack and jack-button capabilities. Currently, module can populate a single jack and associated buttons. In case multiple jacks are supported data routing (say duplicating, etc.) should be handled within module's FW. It can populate additional mixer controls to do so. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index c4ef00548d51..b407a262391a 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -2030,6 +2030,29 @@ struct gb_lights_get_flash_fault_response { #define GB_AUDIO_INVALID_INDEX 0xff +/* enum snd_jack_types */ +#define GB_AUDIO_JACK_HEADPHONE 0x0000001 +#define GB_AUDIO_JACK_MICROPHONE 0x0000002 +#define GB_AUDIO_JACK_HEADSET (GB_AUDIO_JACK_HEADPHONE | \ + GB_AUDIO_JACK_MICROPHONE) +#define GB_AUDIO_JACK_LINEOUT 0x0000004 +#define GB_AUDIO_JACK_MECHANICAL 0x0000008 +#define GB_AUDIO_JACK_VIDEOOUT 0x0000010 +#define GB_AUDIO_JACK_AVOUT (GB_AUDIO_JACK_LINEOUT | \ + GB_AUDIO_JACK_VIDEOOUT) +#define GB_AUDIO_JACK_LINEIN 0x0000020 +#define GB_AUDIO_JACK_OC_HPHL 0x0000040 +#define GB_AUDIO_JACK_OC_HPHR 0x0000080 +#define GB_AUDIO_JACK_MICROPHONE2 0x0000200 +#define GB_AUDIO_JACK_ANC_HEADPHONE (GB_AUDIO_JACK_HEADPHONE | \ + GB_AUDIO_JACK_MICROPHONE | \ + GB_AUDIO_JACK_MICROPHONE2) +/* Kept separate from switches to facilitate implementation */ +#define GB_AUDIO_JACK_BTN_0 0x4000000 +#define GB_AUDIO_JACK_BTN_1 0x2000000 +#define GB_AUDIO_JACK_BTN_2 0x1000000 +#define GB_AUDIO_JACK_BTN_3 0x0800000 + struct gb_audio_pcm { __u8 stream_name[GB_AUDIO_PCM_NAME_MAX]; __le32 formats; /* GB_AUDIO_PCM_FMT_* */ @@ -2120,6 +2143,7 @@ struct gb_audio_topology { __le32 size_controls; __le32 size_widgets; __le32 size_routes; + __le32 jack_type; /* * struct gb_audio_dai dai[num_dais]; * struct gb_audio_control controls[num_controls]; -- cgit v1.2.3-59-g8ed1b From 847175e8e660045f9366e7efd091969e8f32cc0c Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 1 Sep 2016 11:38:40 +0530 Subject: greybus: audio: Fetch jack_mask, button_mask from module's topology data Added extra fields namely jack_mask & button_mask for each module_info. These fields are required while registering jack & reporting jack events. Earlier, these were hard coded values assuming fixed capabilities say HEADSET, LINEOUT, etc. supported by GB-codec driver. Now these are computed dynamically based on module's jack capability shared via topology data. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_codec.c | 68 +++++++++++++++++++++----------- drivers/staging/greybus/audio_codec.h | 8 ++-- drivers/staging/greybus/audio_module.c | 15 ++++--- drivers/staging/greybus/audio_topology.c | 7 ++++ 4 files changed, 63 insertions(+), 35 deletions(-) diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 810ac6269cff..2f70295e0094 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -707,50 +707,72 @@ static int gbaudio_init_jack(struct gbaudio_module_info *module, { int ret; + if (!module->jack_mask) + return 0; + snprintf(module->jack_name, NAME_SIZE, "GB %d Headset Jack", module->dev_id); - ret = snd_soc_jack_new(codec, module->jack_name, GBCODEC_JACK_MASK, + ret = snd_soc_jack_new(codec, module->jack_name, module->jack_mask, &module->headset_jack); if (ret) { dev_err(module->dev, "Failed to create new jack\n"); return ret; } + if (!module->button_mask) + return 0; + snprintf(module->button_name, NAME_SIZE, "GB %d Button Jack", module->dev_id); - ret = snd_soc_jack_new(codec, module->button_name, - GBCODEC_JACK_BUTTON_MASK, &module->button_jack); + ret = snd_soc_jack_new(codec, module->button_name, module->button_mask, + &module->button_jack); if (ret) { dev_err(module->dev, "Failed to create button jack\n"); return ret; } - ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_0, - KEY_MEDIA); - if (ret) { - dev_err(module->dev, "Failed to set BTN_0\n"); - return ret; + /* + * Currently, max 4 buttons are supported with following key mapping + * BTN_0 = KEY_MEDIA + * BTN_1 = KEY_VOICECOMMAND + * BTN_2 = KEY_VOLUMEUP + * BTN_3 = KEY_VOLUMEDOWN + */ + + if (module->button_mask & SND_JACK_BTN_0) { + ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_0, + KEY_MEDIA); + if (ret) { + dev_err(module->dev, "Failed to set BTN_0\n"); + return ret; + } } - ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_1, - KEY_VOICECOMMAND); - if (ret) { - dev_err(module->dev, "Failed to set BTN_1\n"); - return ret; + if (module->button_mask & SND_JACK_BTN_1) { + ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_1, + KEY_VOICECOMMAND); + if (ret) { + dev_err(module->dev, "Failed to set BTN_1\n"); + return ret; + } } - ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_2, - KEY_VOLUMEUP); - if (ret) { - dev_err(module->dev, "Failed to set BTN_2\n"); - return ret; + if (module->button_mask & SND_JACK_BTN_2) { + ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_2, + KEY_VOLUMEUP); + if (ret) { + dev_err(module->dev, "Failed to set BTN_2\n"); + return ret; + } } - ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_3, - KEY_VOLUMEDOWN); - if (ret) { - dev_err(module->dev, "Failed to set BTN_0\n"); - return ret; + if (module->button_mask & SND_JACK_BTN_3) { + ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_3, + KEY_VOLUMEDOWN); + if (ret) { + dev_err(module->dev, "Failed to set BTN_0\n"); + return ret; + } } /* FIXME diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 5a397b0a1886..0a864592560f 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -73,10 +73,8 @@ enum { #define GBCODEC_APB1_MUX_REG_DEFAULT 0x00 #define GBCODEC_APB2_MUX_REG_DEFAULT 0x00 -#define GBCODEC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \ - SND_JACK_LINEIN | SND_JACK_UNSUPPORTED) -#define GBCODEC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ - SND_JACK_BTN_2 | SND_JACK_BTN_3) +#define GBCODEC_JACK_MASK 0x0000FFFF +#define GBCODEC_JACK_BUTTON_MASK 0xFFFF0000 static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { GBCODEC_CTL_REG_DEFAULT, @@ -176,6 +174,8 @@ struct gbaudio_module_info { char jack_name[NAME_SIZE]; char button_name[NAME_SIZE]; int jack_type; + int jack_mask; + int button_mask; int button_status; struct snd_soc_jack headset_jack; struct snd_soc_jack button_jack; diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index c2172719e687..f764f007b543 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -33,22 +33,22 @@ static int gbaudio_request_jack(struct gbaudio_module_info *module, module->button_status = 0; if (button_status) snd_soc_jack_report(&module->button_jack, 0, - GBCODEC_JACK_BUTTON_MASK); + module->button_mask); snd_soc_jack_report(&module->headset_jack, 0, - GBCODEC_JACK_MASK); + module->jack_mask); return 0; } /* currently supports Headphone, Headset & Lineout only */ - report &= ~GBCODEC_JACK_MASK; - report |= req->jack_attribute & GBCODEC_JACK_MASK; + report &= ~module->jack_mask; + report |= req->jack_attribute & module->jack_mask; if (module->jack_type) dev_warn_ratelimited(module->dev, "Modifying jack from %d to %d\n", module->jack_type, report); module->jack_type = report; - snd_soc_jack_report(&module->headset_jack, report, GBCODEC_JACK_MASK); + snd_soc_jack_report(&module->headset_jack, report, module->jack_mask); return 0; } @@ -69,7 +69,7 @@ static int gbaudio_request_button(struct gbaudio_module_info *module, return -EINVAL; } - report = module->button_status & GBCODEC_JACK_BUTTON_MASK; + report = module->button_status & module->button_mask; switch (req->button_id) { case 1: @@ -100,8 +100,7 @@ static int gbaudio_request_button(struct gbaudio_module_info *module, module->button_status = report; - snd_soc_jack_report(&module->button_jack, report, - GBCODEC_JACK_BUTTON_MASK); + snd_soc_jack_report(&module->button_jack, report, module->button_mask); return 0; } diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index e2fc186756a7..5eef5367896c 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -1393,6 +1393,13 @@ int gbaudio_tplg_parse_data(struct gbaudio_module_info *module, } dev_dbg(module->dev, "Route parsing finished\n"); + /* parse jack capabilities */ + if (tplg_data->jack_type) { + module->jack_mask = tplg_data->jack_type & GBCODEC_JACK_MASK; + module->button_mask = tplg_data->jack_type & + GBCODEC_JACK_BUTTON_MASK; + } + return ret; } -- cgit v1.2.3-59-g8ed1b From cec89df44692fa8cff2a52542b11878ee49b0d69 Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 1 Sep 2016 11:38:41 +0530 Subject: greybus: audio: Report jack events conditionally Now jack & jack-buttons are registered to snd_jack framework based on the capability shared by module's topology data. Thus, jack events should be reported to above snd framework only in case corresponding jack type is registered. This patch adds additional checks to avoid reporting fake jack events. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 36 +++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index f764f007b543..d3284ab1dacf 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -21,7 +21,16 @@ static int gbaudio_request_jack(struct gbaudio_module_info *module, struct gb_audio_jack_event_request *req) { - int report, button_status; + int report; + struct snd_jack *jack = module->headset_jack.jack; + struct snd_jack *btn_jack = module->button_jack.jack; + + if (!jack) { + dev_err_ratelimited(module->dev, + "Invalid jack event received:type: %u, event: %u\n", + req->jack_attribute, req->event); + return -EINVAL; + } dev_warn_ratelimited(module->dev, "Jack Event received: type: %u, event: %u\n", @@ -29,19 +38,24 @@ static int gbaudio_request_jack(struct gbaudio_module_info *module, if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) { module->jack_type = 0; - button_status = module->button_status; - module->button_status = 0; - if (button_status) + if (btn_jack && module->button_status) { snd_soc_jack_report(&module->button_jack, 0, module->button_mask); + module->button_status = 0; + } snd_soc_jack_report(&module->headset_jack, 0, module->jack_mask); return 0; } - /* currently supports Headphone, Headset & Lineout only */ - report &= ~module->jack_mask; - report |= req->jack_attribute & module->jack_mask; + report = req->jack_attribute & module->jack_mask; + if (!report) { + dev_err_ratelimited(module->dev, + "Invalid jack event received:type: %u, event: %u\n", + req->jack_attribute, req->event); + return -EINVAL; + } + if (module->jack_type) dev_warn_ratelimited(module->dev, "Modifying jack from %d to %d\n", @@ -57,6 +71,14 @@ static int gbaudio_request_button(struct gbaudio_module_info *module, struct gb_audio_button_event_request *req) { int soc_button_id, report; + struct snd_jack *btn_jack = module->button_jack.jack; + + if (!btn_jack) { + dev_err_ratelimited(module->dev, + "Invalid button event received:type: %u, event: %u\n", + req->button_id, req->event); + return -EINVAL; + } dev_warn_ratelimited(module->dev, "Button Event received: id: %u, event: %u\n", -- cgit v1.2.3-59-g8ed1b From 6a57ddc97acb2a1d37ce94a237dc0fab2e5a3f5b Mon Sep 17 00:00:00 2001 From: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Date: Thu, 1 Sep 2016 11:38:42 +0530 Subject: greybus: audio: Avoid reporting spurious button events Now jack-button are registered to snd framework based on capabilities populated by codec module's topology data. Thus, valid ids for button events can also vary for different modules. This patch modifies existing button reporting mechanism to avoid reporting spurious button events for invalid button ids. Signed-off-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Reviewed-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index d3284ab1dacf..411735df2bb4 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -92,24 +92,27 @@ static int gbaudio_request_button(struct gbaudio_module_info *module, } report = module->button_status & module->button_mask; + soc_button_id = 0; switch (req->button_id) { case 1: - soc_button_id = SND_JACK_BTN_0; + soc_button_id = SND_JACK_BTN_0 & module->button_mask; break; case 2: - soc_button_id = SND_JACK_BTN_1; + soc_button_id = SND_JACK_BTN_1 & module->button_mask; break; case 3: - soc_button_id = SND_JACK_BTN_2; + soc_button_id = SND_JACK_BTN_2 & module->button_mask; break; case 4: - soc_button_id = SND_JACK_BTN_3; + soc_button_id = SND_JACK_BTN_3 & module->button_mask; break; - default: + } + + if (!soc_button_id) { dev_err_ratelimited(module->dev, "Invalid button request received\n"); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 4e013b64c1ee3a60438caa0df6bf79664e0272bc Mon Sep 17 00:00:00 2001 From: Philip Yang <yang_philip@projectara.com> Date: Wed, 31 Aug 2016 11:11:18 +0800 Subject: greybus: power_supply: Add runtime pm support Modify Power_supply greybus driver to support runtime PM framework. During charging state, the driver will block remote device of suspending, and then enables runtime suspend when remote device is in none chargin state. Testing Done: Compiled and verified on EVT2, EVT2 1x2 GPB test module and Device class daughter board. Signed-off-by: Philip Yang <yang_philip@projectara.com> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/power_supply.c | 62 ++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 578d38b25d71..68dd3d2f7585 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -50,6 +50,8 @@ struct gb_power_supply { bool changed; struct gb_power_supply_prop *props; enum power_supply_property *props_raw; + bool pm_acquired; + struct mutex supply_lock; }; struct gb_power_supplies { @@ -75,10 +77,13 @@ struct gb_power_supply_changes { struct gb_power_supply_prop *prop); }; +static void gb_power_supply_state_change(struct gb_power_supply *gbpsy, + struct gb_power_supply_prop *prop); + static const struct gb_power_supply_changes psy_props_changes[] = { { .prop = GB_POWER_SUPPLY_PROP_STATUS, .tolerance_change = 0, - .prop_changed = NULL, + .prop_changed = gb_power_supply_state_change, }, { .prop = GB_POWER_SUPPLY_PROP_TEMP, .tolerance_change = 500, @@ -349,6 +354,40 @@ static void __gb_power_supply_changed(struct gb_power_supply *gbpsy) } #endif +static void gb_power_supply_state_change(struct gb_power_supply *gbpsy, + struct gb_power_supply_prop *prop) +{ + struct gb_connection *connection = get_conn_from_psy(gbpsy); + int ret; + + /* + * Check gbpsy->pm_acquired to make sure only one pair of 'get_sync' + * and 'put_autosuspend' runtime pm call for state property change. + */ + mutex_lock(&gbpsy->supply_lock); + + if ((prop->val == GB_POWER_SUPPLY_STATUS_CHARGING) && + !gbpsy->pm_acquired) { + ret = gb_pm_runtime_get_sync(connection->bundle); + if (ret) + dev_err(&connection->bundle->dev, + "Fail to set wake lock for charging state\n"); + else + gbpsy->pm_acquired = true; + } else { + if (gbpsy->pm_acquired) { + ret = gb_pm_runtime_put_autosuspend(connection->bundle); + if (ret) + dev_err(&connection->bundle->dev, + "Fail to set wake unlock for none charging\n"); + else + gbpsy->pm_acquired = false; + } + } + + mutex_unlock(&gbpsy->supply_lock); +} + static void check_changed(struct gb_power_supply *gbpsy, struct gb_power_supply_prop *prop) { @@ -655,12 +694,17 @@ static int is_cache_valid(struct gb_power_supply *gbpsy) static int gb_power_supply_status_get(struct gb_power_supply *gbpsy) { + struct gb_connection *connection = get_conn_from_psy(gbpsy); int ret = 0; int i; if (is_cache_valid(gbpsy)) return 0; + ret = gb_pm_runtime_get_sync(connection->bundle); + if (ret) + return ret; + for (i = 0; i < gbpsy->properties_count; i++) { ret = __gb_power_supply_property_update(gbpsy, gbpsy->props[i].prop); @@ -671,6 +715,7 @@ static int gb_power_supply_status_get(struct gb_power_supply *gbpsy) if (ret == 0) gbpsy->last_update = jiffies; + gb_pm_runtime_put_autosuspend(connection->bundle); return ret; } @@ -725,9 +770,16 @@ static int gb_power_supply_property_set(struct gb_power_supply *gbpsy, struct gb_power_supply_set_property_request req; int ret; + ret = gb_pm_runtime_get_sync(connection->bundle); + if (ret) + return ret; + prop = get_psy_prop(gbpsy, psp); - if (!prop) - return -EINVAL; + if (!prop) { + ret = -EINVAL; + goto out; + } + req.psy_id = gbpsy->id; req.property = prop->gb_prop; req.prop_val = cpu_to_le32((s32)val); @@ -741,6 +793,7 @@ static int gb_power_supply_property_set(struct gb_power_supply *gbpsy, prop->val = val; out: + gb_pm_runtime_put_autosuspend(connection->bundle); return ret; } @@ -883,6 +936,8 @@ static int gb_power_supply_enable(struct gb_power_supply *gbpsy) if (ret < 0) return ret; + mutex_init(&gbpsy->supply_lock); + ret = gb_power_supply_register(gbpsy); if (ret < 0) return ret; @@ -1067,6 +1122,7 @@ static int gb_power_supply_probe(struct gb_bundle *bundle, if (ret < 0) goto error_connection_disable; + gb_pm_runtime_put_autosuspend(bundle); return 0; error_connection_disable: -- cgit v1.2.3-59-g8ed1b From 6fa4d3d5bbbd32dd7d70868c48abd7818134efa7 Mon Sep 17 00:00:00 2001 From: Johan Hovold <johan@hovoldconsulting.com> Date: Wed, 31 Aug 2016 10:58:53 +0200 Subject: greybus: greybus_protocols: remove svc key-event type Remove the deprecated svc key-event type, which has already been removed from the specification. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/greybus_protocols.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b407a262391a..639578309c2a 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -998,7 +998,6 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_TIMESYNC_AUTHORITATIVE 0x0f #define GB_SVC_TYPE_INTF_SET_PWRM 0x10 #define GB_SVC_TYPE_INTF_EJECT 0x11 -#define GB_SVC_TYPE_KEY_EVENT 0x12 #define GB_SVC_TYPE_PING 0x13 #define GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET 0x14 #define GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET 0x15 -- cgit v1.2.3-59-g8ed1b From 4de02894a28bfc0e3315fc364b3310aacd8cd48a Mon Sep 17 00:00:00 2001 From: Sandeep Patil <sspatil@google.com> Date: Mon, 15 Aug 2016 14:20:54 -0700 Subject: greybus: es2: use a single bulk ep pair for all greybus data This matches a corresponding firmware change to declare a single BULK EP pair for all greybus traffic and 1 BULK IN ep for ARPC. Thus, avoiding URB submittions for all the other unused BULK IN endpoints on the HOST side that took considerable amount of bus time. Testing Done: Tested with modified AP<->APB1 loopback test and also with GPBridge modules to ensure there are no regressions Signed-off-by: Sandeep Patil <sspatil@google.com> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index df104796a285..97a33439e4f2 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -47,7 +47,7 @@ MODULE_DEVICE_TABLE(usb, id_table); #define APB1_LOG_SIZE SZ_16K /* Number of bulk in and bulk out couple */ -#define NUM_BULKS 7 +#define NUM_BULKS 1 /* Expected number of bulk out endpoints */ #define NUM_BULKS_OUT NUM_BULKS -- cgit v1.2.3-59-g8ed1b From b6c1bd3a8e466759fbfd2a649a9058d51ff136c0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Wed, 17 Aug 2016 11:07:28 +0200 Subject: greybus: es2: remove #if 0 code These functions were never used, and we are about to remove the structures it was trying to reference, so let's remove it to get it out of the way. Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 59 ------------------------------------------- 1 file changed, 59 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 97a33439e4f2..cef651024074 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -208,65 +208,6 @@ static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id) return es2->cport_to_ep[cport_id]; } -/* Disable for now until we work all of this out to keep a warning-free build */ -#if 0 -/* Test if the endpoints pair is already mapped to a cport */ -static int ep_pair_in_use(struct es2_ap_dev *es2, int ep_pair) -{ - int i; - - for (i = 0; i < es2->hd->num_cports; i++) { - if (es2->cport_to_ep[i] == ep_pair) - return 1; - } - return 0; -} - -/* Configure the endpoint mapping and send the request to APBridge */ -static int map_cport_to_ep(struct es2_ap_dev *es2, - u16 cport_id, int ep_pair) -{ - int retval; - struct cport_to_ep *cport_to_ep; - - if (ep_pair < 0 || ep_pair >= NUM_BULKS) - return -EINVAL; - if (cport_id >= es2->hd->num_cports) - return -EINVAL; - if (ep_pair && ep_pair_in_use(es2, ep_pair)) - return -EINVAL; - - cport_to_ep = kmalloc(sizeof(*cport_to_ep), GFP_KERNEL); - if (!cport_to_ep) - return -ENOMEM; - - es2->cport_to_ep[cport_id] = ep_pair; - cport_to_ep->cport_id = cpu_to_le16(cport_id); - cport_to_ep->endpoint_in = es2->cport_in[ep_pair].endpoint; - cport_to_ep->endpoint_out = es2->cport_out[ep_pair].endpoint; - - retval = usb_control_msg(es2->usb_dev, - usb_sndctrlpipe(es2->usb_dev, 0), - GB_APB_REQUEST_EP_MAPPING, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - 0x00, 0x00, - (char *)cport_to_ep, - sizeof(*cport_to_ep), - ES2_USB_CTRL_TIMEOUT); - if (retval == sizeof(*cport_to_ep)) - retval = 0; - kfree(cport_to_ep); - - return retval; -} - -/* Unmap a cport: use the muxed endpoints pair */ -static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id) -{ - return map_cport_to_ep(es2, cport_id, 0); -} -#endif - static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd) { struct usb_device *udev = es2->usb_dev; -- cgit v1.2.3-59-g8ed1b From 84d5077cae3e00611b7da6491f363e6ed95e073a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Wed, 17 Aug 2016 11:10:37 +0200 Subject: greybus: es2: remove struct cport_to_ep We were not really using this structure at all, it was only returning '0' when asked what cport matched to what pair, so remove it all. Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index cef651024074..8560102425cd 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -130,8 +130,6 @@ struct es2_ap_dev { bool cdsi1_in_use; - int *cport_to_ep; - struct task_struct *apb_log_task; struct dentry *apb_log_dentry; struct dentry *apb_log_enable_dentry; @@ -146,18 +144,6 @@ struct es2_ap_dev { struct list_head arpcs; }; -/** - * cport_to_ep - information about cport to endpoints mapping - * @cport_id: the id of cport to map to endpoints - * @endpoint_in: the endpoint number to use for in transfer - * @endpoint_out: he endpoint number to use for out transfer - */ -struct cport_to_ep { - __le16 cport_id; - __u8 endpoint_in; - __u8 endpoint_out; -}; - /** * timesync_enable_request - Enable timesync in an APBridge * @count: number of TimeSync Pulses to expect @@ -200,14 +186,6 @@ static void usb_log_disable(struct es2_ap_dev *es2); static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, size_t size, int *result, unsigned int timeout); -/* Get the endpoints pair mapped to the cport */ -static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id) -{ - if (cport_id >= es2->hd->num_cports) - return 0; - return es2->cport_to_ep[cport_id]; -} - static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd) { struct usb_device *udev = es2->usb_dev; @@ -460,7 +438,6 @@ static int message_send(struct gb_host_device *hd, u16 cport_id, size_t buffer_size; int retval; struct urb *urb; - int ep_pair; unsigned long flags; /* @@ -487,10 +464,9 @@ static int message_send(struct gb_host_device *hd, u16 cport_id, buffer_size = sizeof(*message->header) + message->payload_size; - ep_pair = cport_to_ep_pair(es2, cport_id); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, - es2->cport_out[ep_pair].endpoint), + es2->cport_out[0].endpoint), message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; @@ -993,8 +969,6 @@ static void es2_destroy(struct es2_ap_dev *es2) } } - kfree(es2->cport_to_ep); - /* release reserved CDSI0 and CDSI1 cports */ gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI1); gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI0); @@ -1487,13 +1461,6 @@ static int ap_probe(struct usb_interface *interface, if (retval) goto error; - es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep), - GFP_KERNEL); - if (!es2->cport_to_ep) { - retval = -ENOMEM; - goto error; - } - /* find all bulk endpoints */ iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { -- cgit v1.2.3-59-g8ed1b From 403074b50b66f1a6cd038bd9f60119d69916a928 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Wed, 17 Aug 2016 16:27:49 +0200 Subject: greybus: es2: remove bulk_out array We only care about one bulk out endpoint, the first one, so remove the pretense of keeping an array of these things. Just grab the first one in the list and run away! Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 8560102425cd..c6c078642e92 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -83,13 +83,6 @@ struct es2_cport_in { u8 *buffer[NUM_CPORT_IN_URB]; }; -/* - * @endpoint: bulk out endpoint for CPort data - */ -struct es2_cport_out { - __u8 endpoint; -}; - /** * es2_ap_dev - ES2 USB Bridge to AP structure * @usb_dev: pointer to the USB device we are. @@ -97,7 +90,7 @@ struct es2_cport_out { * @hd: pointer to our gb_host_device structure * @cport_in: endpoint, urbs and buffer for cport in messages - * @cport_out: endpoint for for cport out messages + * @cport_out_endpoint: endpoint for for cport out messages * @cport_out_urb: array of urbs for the CPort out messages * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or * not. @@ -122,7 +115,7 @@ struct es2_ap_dev { struct gb_host_device *hd; struct es2_cport_in cport_in[NUM_BULKS]; - struct es2_cport_out cport_out[NUM_BULKS]; + __u8 cport_out_endpoint; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; @@ -466,7 +459,7 @@ static int message_send(struct gb_host_device *hd, u16 cport_id, usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, - es2->cport_out[0].endpoint), + es2->cport_out_endpoint), message->buffer, buffer_size, cport_out_callback, message); urb->transfer_flags |= URB_ZERO_PACKET; @@ -1420,10 +1413,10 @@ static int ap_probe(struct usb_interface *interface, struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; int bulk_in = 0; - int bulk_out = 0; int retval; int i; int num_cports; + bool bulk_out_found = false; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -1474,16 +1467,17 @@ static int ap_probe(struct usb_interface *interface, es2->arpc_endpoint_in = endpoint->bEndpointAddress; bulk_in++; - } else if (usb_endpoint_is_bulk_out(endpoint)) { - es2->cport_out[bulk_out++].endpoint = - endpoint->bEndpointAddress; + } else if (usb_endpoint_is_bulk_out(endpoint) && + (!bulk_out_found)) { + es2->cport_out_endpoint = endpoint->bEndpointAddress; + bulk_out_found = true; } else { dev_err(&udev->dev, "Unknown endpoint type found, address 0x%02x\n", endpoint->bEndpointAddress); } } - if (bulk_in != NUM_BULKS_IN || bulk_out != NUM_BULKS_OUT) { + if (bulk_in != NUM_BULKS_IN || !bulk_out_found) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); retval = -ENODEV; goto error; -- cgit v1.2.3-59-g8ed1b From 7330c48ec09367e8da65e3ebf642b6c37bf244e3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Wed, 17 Aug 2016 16:37:39 +0200 Subject: greybus: es2: remove bulk_in array We only care about one bulk IN endpoint for cports, and one for ARPC, so drop the array of bulk IN endpoints to simplify things. Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 93 ++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 55 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index c6c078642e92..69123b7ee94b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -114,7 +114,7 @@ struct es2_ap_dev { struct usb_interface *usb_intf; struct gb_host_device *hd; - struct es2_cport_in cport_in[NUM_BULKS]; + struct es2_cport_in cport_in; __u8 cport_out_endpoint; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; @@ -920,7 +920,6 @@ static int check_urb_status(struct urb *urb) static void es2_destroy(struct es2_ap_dev *es2) { struct usb_device *udev; - int bulk_in; int i; debugfs_remove(es2->apb_log_enable_dentry); @@ -948,18 +947,10 @@ static void es2_destroy(struct es2_ap_dev *es2) es2->arpc_buffer[i] = NULL; } - for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { - struct es2_cport_in *cport_in = &es2->cport_in[bulk_in]; - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb = cport_in->urb[i]; - - if (!urb) - break; - usb_free_urb(urb); - kfree(cport_in->buffer[i]); - cport_in->buffer[i] = NULL; - } + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + usb_free_urb(es2->cport_in.urb[i]); + kfree(es2->cport_in.buffer[i]); + es2->cport_in.buffer[i] = NULL; } /* release reserved CDSI0 and CDSI1 cports */ @@ -1412,11 +1403,12 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - int bulk_in = 0; int retval; int i; int num_cports; bool bulk_out_found = false; + bool bulk_in_found = false; + bool arpc_in_found = false; udev = usb_get_dev(interface_to_usbdev(interface)); @@ -1460,13 +1452,15 @@ static int ap_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[i].desc; if (usb_endpoint_is_bulk_in(endpoint)) { - if (bulk_in < NUM_BULKS) - es2->cport_in[bulk_in].endpoint = + if (!bulk_in_found) { + es2->cport_in.endpoint = endpoint->bEndpointAddress; - else + bulk_in_found = true; + } else if (!arpc_in_found) { es2->arpc_endpoint_in = endpoint->bEndpointAddress; - bulk_in++; + arpc_in_found = true; + } } else if (usb_endpoint_is_bulk_out(endpoint) && (!bulk_out_found)) { es2->cport_out_endpoint = endpoint->bEndpointAddress; @@ -1477,41 +1471,36 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if (bulk_in != NUM_BULKS_IN || !bulk_out_found) { + if (!bulk_in_found || !arpc_in_found || !bulk_out_found) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); retval = -ENODEV; goto error; } /* Allocate buffers for our cport in messages */ - for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { - struct es2_cport_in *cport_in = &es2->cport_in[bulk_in]; - - for (i = 0; i < NUM_CPORT_IN_URB; ++i) { - struct urb *urb; - u8 *buffer; + for (i = 0; i < NUM_CPORT_IN_URB; ++i) { + struct urb *urb; + u8 *buffer; - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - retval = -ENOMEM; - goto error; - } - cport_in->urb[i] = urb; + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + retval = -ENOMEM; + goto error; + } + es2->cport_in.urb[i] = urb; - buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL); - if (!buffer) { - retval = -ENOMEM; - goto error; - } + buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL); + if (!buffer) { + retval = -ENOMEM; + goto error; + } - usb_fill_bulk_urb(urb, udev, - usb_rcvbulkpipe(udev, - cport_in->endpoint), - buffer, ES2_GBUF_MSG_SIZE_MAX, - cport_in_callback, hd); + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, es2->cport_in.endpoint), + buffer, ES2_GBUF_MSG_SIZE_MAX, + cport_in_callback, hd); - cport_in->buffer[i] = buffer; - } + es2->cport_in.buffer[i] = buffer; } /* Allocate buffers for ARPC in messages */ @@ -1571,17 +1560,13 @@ static int ap_probe(struct usb_interface *interface, if (retval) goto err_disable_arpc_in; - for (i = 0; i < NUM_BULKS; ++i) { - retval = es2_cport_in_enable(es2, &es2->cport_in[i]); - if (retval) - goto err_disable_cport_in; - } + retval = es2_cport_in_enable(es2, &es2->cport_in); + if (retval) + goto err_hd_del; return 0; -err_disable_cport_in: - for (--i; i >= 0; --i) - es2_cport_in_disable(es2, &es2->cport_in[i]); +err_hd_del: gb_hd_del(hd); err_disable_arpc_in: es2_arpc_in_disable(es2); @@ -1594,12 +1579,10 @@ error: static void ap_disconnect(struct usb_interface *interface) { struct es2_ap_dev *es2 = usb_get_intfdata(interface); - int i; gb_hd_del(es2->hd); - for (i = 0; i < NUM_BULKS; ++i) - es2_cport_in_disable(es2, &es2->cport_in[i]); + es2_cport_in_disable(es2, &es2->cport_in); es2_arpc_in_disable(es2); es2_destroy(es2); -- cgit v1.2.3-59-g8ed1b From fc994f0f8363eb4c50361800c7d2217eb0306014 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Wed, 17 Aug 2016 13:21:51 +0200 Subject: greybus: es2: remove unneeded BULK_* #defines We don't need the defines for the number of bulk in or out endpoints anymore, as the driver just grabs the first ones it finds and runs with it. Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 69123b7ee94b..4f10accbd50b 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -46,15 +46,6 @@ MODULE_DEVICE_TABLE(usb, id_table); #define APB1_LOG_SIZE SZ_16K -/* Number of bulk in and bulk out couple */ -#define NUM_BULKS 1 - -/* Expected number of bulk out endpoints */ -#define NUM_BULKS_OUT NUM_BULKS - -/* Expected number of bulk in endpoints (including ARPC endpoint) */ -#define NUM_BULKS_IN (NUM_BULKS + 1) - /* * Number of CPort IN urbs in flight at any point in time. * Adjust if we are having stalls in the USB buffer due to not enough urbs in @@ -65,7 +56,7 @@ MODULE_DEVICE_TABLE(usb, id_table); /* Number of CPort OUT urbs in flight at any point in time. * Adjust if we get messages saying we are out of urbs in the system log. */ -#define NUM_CPORT_OUT_URB (8 * NUM_BULKS) +#define NUM_CPORT_OUT_URB 8 /* * Number of ARPC in urbs in flight at any point in time. -- cgit v1.2.3-59-g8ed1b From 1521eb6b6995dd8db57c96d36bc3c64b2a7da3b1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Wed, 17 Aug 2016 16:44:11 +0200 Subject: greybus: es2: No need to check before freeing an urb usb_kill_urb() and usb_free_urb() can be called with NULL pointers, so no need to check before calling them. Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 4f10accbd50b..6cd3a6411ad3 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -911,6 +911,7 @@ static int check_urb_status(struct urb *urb) static void es2_destroy(struct es2_ap_dev *es2) { struct usb_device *udev; + struct urb *urb; int i; debugfs_remove(es2->apb_log_enable_dentry); @@ -918,10 +919,7 @@ static void es2_destroy(struct es2_ap_dev *es2) /* Tear down everything! */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { - struct urb *urb = es2->cport_out_urb[i]; - - if (!urb) - break; + urb = es2->cport_out_urb[i]; usb_kill_urb(urb); usb_free_urb(urb); es2->cport_out_urb[i] = NULL; @@ -929,11 +927,7 @@ static void es2_destroy(struct es2_ap_dev *es2) } for (i = 0; i < NUM_ARPC_IN_URB; ++i) { - struct urb *urb = es2->arpc_urb[i]; - - if (!urb) - break; - usb_free_urb(urb); + usb_free_urb(es2->arpc_urb[i]); kfree(es2->arpc_buffer[i]); es2->arpc_buffer[i] = NULL; } -- cgit v1.2.3-59-g8ed1b From 272291008fc55e7692f3219b0f85ee1e6ffdea71 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Wed, 17 Aug 2016 17:11:04 +0200 Subject: greybus: es2: fix up usb probe error messages Properly report which endpoints are being ignored and which ones are "unknown" to the driver. Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es2.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 6cd3a6411ad3..6cb13fcf62d4 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -1388,6 +1388,7 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; + __u8 ep_addr; int retval; int i; int num_cports; @@ -1435,26 +1436,36 @@ static int ap_probe(struct usb_interface *interface, iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; + ep_addr = endpoint->bEndpointAddress; if (usb_endpoint_is_bulk_in(endpoint)) { if (!bulk_in_found) { - es2->cport_in.endpoint = - endpoint->bEndpointAddress; + es2->cport_in.endpoint = ep_addr; bulk_in_found = true; } else if (!arpc_in_found) { - es2->arpc_endpoint_in = - endpoint->bEndpointAddress; + es2->arpc_endpoint_in = ep_addr; arpc_in_found = true; + } else { + dev_warn(&udev->dev, + "Unused bulk IN endpoint found: 0x%02x\n", + ep_addr); } - } else if (usb_endpoint_is_bulk_out(endpoint) && - (!bulk_out_found)) { - es2->cport_out_endpoint = endpoint->bEndpointAddress; - bulk_out_found = true; - } else { - dev_err(&udev->dev, - "Unknown endpoint type found, address 0x%02x\n", - endpoint->bEndpointAddress); + continue; } + if (usb_endpoint_is_bulk_out(endpoint)) { + if (!bulk_out_found) { + es2->cport_out_endpoint = ep_addr; + bulk_out_found = true; + } else { + dev_warn(&udev->dev, + "Unused bulk OUT endpoint found: 0x%02x\n", + ep_addr); + } + continue; + } + dev_warn(&udev->dev, + "Unknown endpoint type found, address 0x%02x\n", + ep_addr); } if (!bulk_in_found || !arpc_in_found || !bulk_out_found) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); -- cgit v1.2.3-59-g8ed1b From 88f6ba61f25bfe6eb92cb8f511b0879cdfbb64d3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 3 Aug 2016 15:52:10 -0700 Subject: greybus: gpio: create irqdomain before registering gpio controller If a gpio line is getting used for an irq, gpio core will create /proc/irq/X/gpiolib/ directory for it. This directory gets removed while the gpio controller is unregistered. In case of greybus, gb_gpio_irqchip_add() creates an irqdomain and creates irq mappings for the gpio lines. Currently they are added after registering the gpio controller and removed before the gpio controller is removed. On the removal path, while the core tries to remove the irq directory (/proc/irq/X), it finds that the irq is still getting used and a "gpiolib" directory is present within it and so it gives this warning: Steps to reproduce: $ cd /sys/class/gpio $ echo X > export $ echo both > gpioX/edge $ echo <interface-number> > /sys/bus/greybus/devices/1-svc/intf_eject [ 139.171436] ------------[ cut here ]------------ [ 139.171468] WARNING: at /home/vireshk/all/work/repos/ara/arche/kernel/arche/fs/proc/generic.c:552 remove_proc_entry+0x154/0x188() [ 139.171476] remove_proc_entry: removing non-empty directory 'irq/683', leaking at least 'gpiolib' [ 139.171589] Modules linked in: gb_vibrator(O) gb_usb(O) gb_uart(O) gb_spi(O) gb_sdio(O) gb_raw(O) gb_pwm(O) gb_power_supply(O) gb_loopback(O) gb_log(O) gb_light(O) gb_i2c(O) gb_hid(O) gb_gpio(O) gb_gbphy(O) gb_firmware(O) gb_spilib(O) gb_es2(O) gb_camera(O) gb_bootrom(O) gb_audio_module(O) gb_audio_manager(O) gb_audio_codec(O) gb_audio_gb(O) gb_audio_apbridgea(O) gb_arche(O) greybus(O) [ 139.171605] CPU: 1 PID: 280 Comm: kworker/u16:4 Tainted: G W O 3.10.83-g9771b10cbeed #107 [ 139.171652] Workqueue: greybus1:svc gb_svc_intf_set_power_mode [greybus] [ 139.171657] Call trace: [ 139.171677] [<ffffffc000207b40>] dump_backtrace+0x0/0x268 [ 139.171689] [<ffffffc000207db8>] show_stack+0x10/0x1c [ 139.171707] [<ffffffc000ccad78>] dump_stack+0x1c/0x28 [ 139.171723] [<ffffffc00021f9dc>] warn_slowpath_common+0x74/0x9c [ 139.171735] [<ffffffc00021fa60>] warn_slowpath_fmt+0x5c/0x80 [ 139.171747] [<ffffffc00035fa38>] remove_proc_entry+0x150/0x188 [ 139.171763] [<ffffffc00027464c>] unregister_irq_proc+0xb4/0xdc [ 139.171779] [<ffffffc00026e3f4>] free_desc+0x2c/0x70 [ 139.171791] [<ffffffc00026e48c>] irq_free_descs+0x54/0x9c [ 139.171802] [<ffffffc000273448>] irq_dispose_mapping+0x54/0x64 [ 139.171814] [<ffffffbffc0621e8>] 0xffffffbffc0621e8 [ 139.171825] [<ffffffbffc05e01c>] 0xffffffbffc05e01c [ 139.171843] [<ffffffc0005d4e30>] __device_release_driver+0x90/0xe4 [ 139.171854] [<ffffffc0005d4ea4>] device_release_driver+0x20/0x38 [ 139.171867] [<ffffffc0005d4634>] bus_remove_device+0x12c/0x148 [ 139.171878] [<ffffffc0005d1cb0>] device_del+0x108/0x16c [ 139.171888] [<ffffffc0005d1d64>] device_unregister+0x50/0x68 [ 139.171901] [<ffffffbffc05e2bc>] gb_gbphy_deregister_driver+0xf0/0x4ec [gb_gbphy] [ 139.171924] [<ffffffbffc0014c4>] greybus_disabled+0x14c4/0x1760 [greybus] [ 139.171936] [<ffffffc0005d4e30>] __device_release_driver+0x90/0xe4 [ 139.171948] [<ffffffc0005d4ea4>] device_release_driver+0x20/0x38 [ 139.171959] [<ffffffc0005d4634>] bus_remove_device+0x12c/0x148 [ 139.171969] [<ffffffc0005d1cb0>] device_del+0x108/0x16c [ 139.171992] [<ffffffbffc004cec>] gb_bundle_destroy+0x7c/0x1b0 [greybus] [ 139.172017] [<ffffffbffc004074>] gb_interface_disable+0xb4/0x178 [greybus] [ 139.172040] [<ffffffbffc002ae4>] gb_module_del+0x5c/0xf8 [greybus] [ 139.172063] [<ffffffbffc008418>] gb_svc_intf_set_power_mode+0xea0/0xfe8 [greybus] [ 139.172078] [<ffffffc00023888c>] process_one_work+0x268/0x3c8 [ 139.172089] [<ffffffc000239a64>] worker_thread+0x204/0x358 [ 139.172108] [<ffffffc00023f43c>] kthread+0xb8/0xc4 [ 139.172114] ---[ end trace 6fa3314e8c6157ca ]--- Also note that registering the gpio controller before creating irqdomain is incorrect as well and may lead to kernel panic, as a gpio may get requested as an interrupt source right after the controller is registered, and the greybus gpio driver wouldn't be fully ready by then. This patch changes the sequence in both probe() and remove() to fix it. Fixes: 426e88a47d39 ("greybus: gpio: add interrupt handling support") Reported-by: David Hsu <davidhsu@google.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpio.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 8fa9998b4c3d..294e2f52f1d4 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -708,26 +708,26 @@ static int gb_gpio_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_line_free; - ret = gpiochip_add(gpio); + ret = gb_gpio_irqchip_add(gpio, irqc, 0, + handle_level_irq, IRQ_TYPE_NONE); if (ret) { dev_err(&connection->bundle->dev, - "failed to add gpio chip: %d\n", ret); + "failed to add irq chip: %d\n", ret); goto exit_line_free; } - ret = gb_gpio_irqchip_add(gpio, irqc, 0, - handle_level_irq, IRQ_TYPE_NONE); + ret = gpiochip_add(gpio); if (ret) { dev_err(&connection->bundle->dev, - "failed to add irq chip: %d\n", ret); - goto exit_gpiochip_remove; + "failed to add gpio chip: %d\n", ret); + goto exit_gpio_irqchip_remove; } gbphy_runtime_put_autosuspend(gbphy_dev); return 0; -exit_gpiochip_remove: - gb_gpiochip_remove(gpio); +exit_gpio_irqchip_remove: + gb_gpio_irqchip_remove(ggc); exit_line_free: kfree(ggc->lines); exit_connection_disable: @@ -750,8 +750,8 @@ static void gb_gpio_remove(struct gbphy_device *gbphy_dev) gbphy_runtime_get_noresume(gbphy_dev); gb_connection_disable_rx(connection); - gb_gpio_irqchip_remove(ggc); gb_gpiochip_remove(&ggc->chip); + gb_gpio_irqchip_remove(ggc); gb_connection_disable(connection); gb_connection_destroy(connection); kfree(ggc->lines); -- cgit v1.2.3-59-g8ed1b From 7398a66f10fc81403009120760b129f1e4ec7e52 Mon Sep 17 00:00:00 2001 From: Axel Haslam <ahaslam@baylibre.com> Date: Thu, 8 Sep 2016 18:48:43 +0200 Subject: greybus: gbphy: fix compile error with CONFIG_PM_RUNTIME disabled gb_phy runtime functions use struct gbphy_device *gbphy_dev, and not struct device. When CONFIG_PM_RUNTIME is not enabled a compile error will show. Fix this by passing struct gbphy_device * as parameter Testing Done: compile with CONFIG_PM_RUNTIME disabled Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Alex Elder <elder@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gbphy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/gbphy.h b/drivers/staging/greybus/gbphy.h index 57c6f65aeb7f..e251186d5cbb 100644 --- a/drivers/staging/greybus/gbphy.h +++ b/drivers/staging/greybus/gbphy.h @@ -100,8 +100,8 @@ static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev) pm_runtime_put_noidle(&gbphy_dev->dev); } #else -static inline int gbphy_runtime_get_sync(struct device *dev) { return 0; } -static inline void gbphy_runtime_put_autosuspend(struct device *dev) {} +static inline int gbphy_runtime_get_sync(struct gbphy_device *gbphy_dev) { return 0; } +static inline void gbphy_runtime_put_autosuspend(struct gbphy_device *gbphy_dev) {} static inline void gbphy_runtime_get_noresume(struct gbphy_device *gbphy_dev) {} static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev) {} #endif -- cgit v1.2.3-59-g8ed1b From dae09011115133666e47c35673c0564b0a702db7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 8 Sep 2016 11:44:34 +0200 Subject: greybus: remove local checkpatch.pl copy When the greybus tree was external, it contained a copy of checkpatch.pl to keep everyone "in line". This is no longer needed and can now be removed. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/scripts/checkpatch.pl | 5993 ------------------------- drivers/staging/greybus/scripts/spelling.txt | 1072 ----- 2 files changed, 7065 deletions(-) delete mode 100755 drivers/staging/greybus/scripts/checkpatch.pl delete mode 100644 drivers/staging/greybus/scripts/spelling.txt diff --git a/drivers/staging/greybus/scripts/checkpatch.pl b/drivers/staging/greybus/scripts/checkpatch.pl deleted file mode 100755 index 9930b73211b3..000000000000 --- a/drivers/staging/greybus/scripts/checkpatch.pl +++ /dev/null @@ -1,5993 +0,0 @@ -#!/usr/bin/perl -w -# (c) 2001, Dave Jones. (the file handling bit) -# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit) -# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite) -# (c) 2008-2010 Andy Whitcroft <apw@canonical.com> -# Licensed under the terms of the GNU GPL License version 2 - -use strict; -use POSIX; -use File::Basename; -use Cwd 'abs_path'; -use Term::ANSIColor qw(:constants); - -my $P = $0; -my $D = dirname(abs_path($P)); - -my $V = '0.32'; - -use Getopt::Long qw(:config no_auto_abbrev); - -my $quiet = 0; -my $tree = 0; -my $chk_signoff = 1; -my $chk_patch = 1; -my $tst_only; -my $emacs = 0; -my $terse = 0; -my $showfile = 0; -my $file = 0; -my $check = 0; -my $check_orig = 0; -my $summary = 1; -my $mailback = 0; -my $summary_file = 0; -my $show_types = 0; -my $fix = 0; -my $fix_inplace = 0; -my $root; -my %debug; -my %camelcase = (); -my %use_type = (); -my @use = (); -my %ignore_type = (); -my @ignore = (); -my $help = 0; -my $configuration_file = ".checkpatch.conf"; -my $max_line_length = 80; -my $ignore_perl_version = 0; -my $minimum_perl_version = 5.10.0; -my $min_conf_desc_length = 4; -my $spelling_file = "$D/spelling.txt"; -my $codespell = 0; -my $codespellfile = "/usr/share/codespell/dictionary.txt"; -my $color = 1; - -sub help { - my ($exitcode) = @_; - - print << "EOM"; -Usage: $P [OPTION]... [FILE]... -Version: $V - -Options: - -q, --quiet quiet - --no-tree run without a kernel tree - --no-signoff do not check for 'Signed-off-by' line - --patch treat FILE as patchfile (default) - --emacs emacs compile window format - --terse one line per report - --showfile emit diffed file position, not input file position - -f, --file treat FILE as regular source file - --subjective, --strict enable more subjective tests - --types TYPE(,TYPE2...) show only these comma separated message types - --ignore TYPE(,TYPE2...) ignore various comma separated message types - --max-line-length=n set the maximum line length, if exceeded, warn - --min-conf-desc-length=n set the min description length, if shorter, warn - --show-types show the message "types" in the output - --root=PATH PATH to the kernel tree root - --no-summary suppress the per-file summary - --mailback only produce a report in case of warnings/errors - --summary-file include the filename in summary - --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of - 'values', 'possible', 'type', and 'attr' (default - is all off) - --test-only=WORD report only warnings/errors containing WORD - literally - --fix EXPERIMENTAL - may create horrible results - If correctable single-line errors exist, create - "<inputfile>.EXPERIMENTAL-checkpatch-fixes" - with potential errors corrected to the preferred - checkpatch style - --fix-inplace EXPERIMENTAL - may create horrible results - Is the same as --fix, but overwrites the input - file. It's your fault if there's no backup or git - --ignore-perl-version override checking of perl version. expect - runtime errors. - --codespell Use the codespell dictionary for spelling/typos - (default:/usr/share/codespell/dictionary.txt) - --codespellfile Use this codespell dictionary - --color Use colors when output is STDOUT (default: on) - -h, --help, --version display this help and exit - -When FILE is - read standard input. -EOM - - exit($exitcode); -} - -my $conf = which_conf($configuration_file); -if (-f $conf) { - my @conf_args; - open(my $conffile, '<', "$conf") - or warn "$P: Can't find a readable $configuration_file file $!\n"; - - while (<$conffile>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - $line =~ s/\s+/ /g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - - my @words = split(" ", $line); - foreach my $word (@words) { - last if ($word =~ m/^#/); - push (@conf_args, $word); - } - } - close($conffile); - unshift(@ARGV, @conf_args) if @conf_args; -} - -GetOptions( - 'q|quiet+' => \$quiet, - 'tree!' => \$tree, - 'signoff!' => \$chk_signoff, - 'patch!' => \$chk_patch, - 'emacs!' => \$emacs, - 'terse!' => \$terse, - 'showfile!' => \$showfile, - 'f|file!' => \$file, - 'subjective!' => \$check, - 'strict!' => \$check, - 'ignore=s' => \@ignore, - 'types=s' => \@use, - 'show-types!' => \$show_types, - 'max-line-length=i' => \$max_line_length, - 'min-conf-desc-length=i' => \$min_conf_desc_length, - 'root=s' => \$root, - 'summary!' => \$summary, - 'mailback!' => \$mailback, - 'summary-file!' => \$summary_file, - 'fix!' => \$fix, - 'fix-inplace!' => \$fix_inplace, - 'ignore-perl-version!' => \$ignore_perl_version, - 'debug=s' => \%debug, - 'test-only=s' => \$tst_only, - 'codespell!' => \$codespell, - 'codespellfile=s' => \$codespellfile, - 'color!' => \$color, - 'h|help' => \$help, - 'version' => \$help -) or help(1); - -help(0) if ($help); - -$fix = 1 if ($fix_inplace); -$check_orig = $check; - -my $exit = 0; - -if ($^V && $^V lt $minimum_perl_version) { - printf "$P: requires at least perl version %vd\n", $minimum_perl_version; - if (!$ignore_perl_version) { - exit(1); - } -} - -if ($#ARGV < 0) { - print "$P: no input files\n"; - exit(1); -} - -sub hash_save_array_words { - my ($hashRef, $arrayRef) = @_; - - my @array = split(/,/, join(',', @$arrayRef)); - foreach my $word (@array) { - $word =~ s/\s*\n?$//g; - $word =~ s/^\s*//g; - $word =~ s/\s+/ /g; - $word =~ tr/[a-z]/[A-Z]/; - - next if ($word =~ m/^\s*#/); - next if ($word =~ m/^\s*$/); - - $hashRef->{$word}++; - } -} - -sub hash_show_words { - my ($hashRef, $prefix) = @_; - - if (keys %$hashRef) { - print "\nNOTE: $prefix message types:"; - foreach my $word (sort keys %$hashRef) { - print " $word"; - } - print "\n"; - } -} - -hash_save_array_words(\%ignore_type, \@ignore); -hash_save_array_words(\%use_type, \@use); - -my $dbg_values = 0; -my $dbg_possible = 0; -my $dbg_type = 0; -my $dbg_attr = 0; -for my $key (keys %debug) { - ## no critic - eval "\${dbg_$key} = '$debug{$key}';"; - die "$@" if ($@); -} - -my $rpt_cleaners = 0; - -if ($terse) { - $emacs = 1; - $quiet++; -} - -if ($tree) { - if (defined $root) { - if (!top_of_kernel_tree($root)) { - die "$P: $root: --root does not point at a valid tree\n"; - } - } else { - if (top_of_kernel_tree('.')) { - $root = '.'; - } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && - top_of_kernel_tree($1)) { - $root = $1; - } - } - - if (!defined $root) { - print "Must be run from the top-level dir. of a kernel tree\n"; - exit(2); - } -} - -my $emitted_corrupt = 0; - -our $Ident = qr{ - [A-Za-z_][A-Za-z\d_]* - (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* - }x; -our $Storage = qr{extern|static|asmlinkage}; -our $Sparse = qr{ - __user| - __kernel| - __force| - __iomem| - __pmem| - __must_check| - __init_refok| - __kprobes| - __ref| - __rcu| - __private - }x; -our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; -our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; -our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; -our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; -our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; - -# Notes to $Attribute: -# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check -our $Attribute = qr{ - const| - __percpu| - __nocast| - __safe| - __bitwise__| - __packed__| - __packed2__| - __naked| - __maybe_unused| - __always_unused| - __noreturn| - __used| - __cold| - __pure| - __noclone| - __deprecated| - __read_mostly| - __kprobes| - $InitAttribute| - ____cacheline_aligned| - ____cacheline_aligned_in_smp| - ____cacheline_internodealigned_in_smp| - __weak - }x; -our $Modifier; -our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; -our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; -our $Lval = qr{$Ident(?:$Member)*}; - -our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; -our $Binary = qr{(?i)0b[01]+$Int_type?}; -our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; -our $Int = qr{[0-9]+$Int_type?}; -our $Octal = qr{0[0-7]+$Int_type?}; -our $String = qr{"[X\t]*"}; -our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; -our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; -our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; -our $Float = qr{$Float_hex|$Float_dec|$Float_int}; -our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; -our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; -our $Compare = qr{<=|>=|==|!=|<|(?<!-)>}; -our $Arithmetic = qr{\+|-|\*|\/|%}; -our $Operators = qr{ - <=|>=|==|!=| - =>|->|<<|>>|<|>|!|~| - &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic - }x; - -our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; - -our $BasicType; -our $NonptrType; -our $NonptrTypeMisordered; -our $NonptrTypeWithAttr; -our $Type; -our $TypeMisordered; -our $Declare; -our $DeclareMisordered; - -our $NON_ASCII_UTF8 = qr{ - [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte - | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs - | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte - | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates - | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 - | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 - | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 -}x; - -our $UTF8 = qr{ - [\x09\x0A\x0D\x20-\x7E] # ASCII - | $NON_ASCII_UTF8 -}x; - -our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; -our $typeOtherOSTypedefs = qr{(?x: - u_(?:char|short|int|long) | # bsd - u(?:nchar|short|int|long) # sysv -)}; -our $typeKernelTypedefs = qr{(?x: - (?:__)?(?:u|s|be|le)(?:8|16|32|64)| - atomic_t -)}; -our $typeTypedefs = qr{(?x: - $typeC99Typedefs\b| - $typeOtherOSTypedefs\b| - $typeKernelTypedefs\b -)}; - -our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; - -our $logFunctions = qr{(?x: - printk(?:_ratelimited|_once|)| - (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| - WARN(?:_RATELIMIT|_ONCE|)| - panic| - MODULE_[A-Z_]+| - seq_vprintf|seq_printf|seq_puts -)}; - -our $signature_tags = qr{(?xi: - Signed-off-by:| - Acked-by:| - Tested-by:| - Reviewed-by:| - Reported-by:| - Suggested-by:| - To:| - Cc: -)}; - -our @typeListMisordered = ( - qr{char\s+(?:un)?signed}, - qr{int\s+(?:(?:un)?signed\s+)?short\s}, - qr{int\s+short(?:\s+(?:un)?signed)}, - qr{short\s+int(?:\s+(?:un)?signed)}, - qr{(?:un)?signed\s+int\s+short}, - qr{short\s+(?:un)?signed}, - qr{long\s+int\s+(?:un)?signed}, - qr{int\s+long\s+(?:un)?signed}, - qr{long\s+(?:un)?signed\s+int}, - qr{int\s+(?:un)?signed\s+long}, - qr{int\s+(?:un)?signed}, - qr{int\s+long\s+long\s+(?:un)?signed}, - qr{long\s+long\s+int\s+(?:un)?signed}, - qr{long\s+long\s+(?:un)?signed\s+int}, - qr{long\s+long\s+(?:un)?signed}, - qr{long\s+(?:un)?signed}, -); - -our @typeList = ( - qr{void}, - qr{(?:(?:un)?signed\s+)?char}, - qr{(?:(?:un)?signed\s+)?short\s+int}, - qr{(?:(?:un)?signed\s+)?short}, - qr{(?:(?:un)?signed\s+)?int}, - qr{(?:(?:un)?signed\s+)?long\s+int}, - qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, - qr{(?:(?:un)?signed\s+)?long\s+long}, - qr{(?:(?:un)?signed\s+)?long}, - qr{(?:un)?signed}, - qr{float}, - qr{double}, - qr{bool}, - qr{struct\s+$Ident}, - qr{union\s+$Ident}, - qr{enum\s+$Ident}, - qr{${Ident}_t}, - qr{${Ident}_handler}, - qr{${Ident}_handler_fn}, - @typeListMisordered, -); - -our $C90_int_types = qr{(?x: - long\s+long\s+int\s+(?:un)?signed| - long\s+long\s+(?:un)?signed\s+int| - long\s+long\s+(?:un)?signed| - (?:(?:un)?signed\s+)?long\s+long\s+int| - (?:(?:un)?signed\s+)?long\s+long| - int\s+long\s+long\s+(?:un)?signed| - int\s+(?:(?:un)?signed\s+)?long\s+long| - - long\s+int\s+(?:un)?signed| - long\s+(?:un)?signed\s+int| - long\s+(?:un)?signed| - (?:(?:un)?signed\s+)?long\s+int| - (?:(?:un)?signed\s+)?long| - int\s+long\s+(?:un)?signed| - int\s+(?:(?:un)?signed\s+)?long| - - int\s+(?:un)?signed| - (?:(?:un)?signed\s+)?int -)}; - -our @typeListFile = (); -our @typeListWithAttr = ( - @typeList, - qr{struct\s+$InitAttribute\s+$Ident}, - qr{union\s+$InitAttribute\s+$Ident}, -); - -our @modifierList = ( - qr{fastcall}, -); -our @modifierListFile = (); - -our @mode_permission_funcs = ( - ["module_param", 3], - ["module_param_(?:array|named|string)", 4], - ["module_param_array_named", 5], - ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], - ["proc_create(?:_data|)", 2], - ["(?:CLASS|DEVICE|SENSOR)_ATTR", 2], -); - -#Create a search pattern for all these functions to speed up a loop below -our $mode_perms_search = ""; -foreach my $entry (@mode_permission_funcs) { - $mode_perms_search .= '|' if ($mode_perms_search ne ""); - $mode_perms_search .= $entry->[0]; -} - -our $mode_perms_world_writable = qr{ - S_IWUGO | - S_IWOTH | - S_IRWXUGO | - S_IALLUGO | - 0[0-7][0-7][2367] -}x; - -our $allowed_asm_includes = qr{(?x: - irq| - memory| - time| - reboot -)}; -# memory.h: ARM has a custom one - -# Load common spelling mistakes and build regular expression list. -my $misspellings; -my %spelling_fix; - -if (open(my $spelling, '<', $spelling_file)) { - while (<$spelling>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - - my ($suspect, $fix) = split(/\|\|/, $line); - - $spelling_fix{$suspect} = $fix; - } - close($spelling); -} else { - warn "No typos will be found - file '$spelling_file': $!\n"; -} - -if ($codespell) { - if (open(my $spelling, '<', $codespellfile)) { - while (<$spelling>) { - my $line = $_; - - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; - - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - next if ($line =~ m/, disabled/i); - - $line =~ s/,.*$//; - - my ($suspect, $fix) = split(/->/, $line); - - $spelling_fix{$suspect} = $fix; - } - close($spelling); - } else { - warn "No codespell typos will be found - file '$codespellfile': $!\n"; - } -} - -$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; - -sub build_types { - my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; - my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; - my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; - my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; - $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; - $BasicType = qr{ - (?:$typeTypedefs\b)| - (?:${all}\b) - }x; - $NonptrType = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:typeof|__typeof__)\s*\([^\)]*\)| - (?:$typeTypedefs\b)| - (?:${all}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $NonptrTypeMisordered = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:${Misordered}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $NonptrTypeWithAttr = qr{ - (?:$Modifier\s+|const\s+)* - (?: - (?:typeof|__typeof__)\s*\([^\)]*\)| - (?:$typeTypedefs\b)| - (?:${allWithAttr}\b) - ) - (?:\s+$Modifier|\s+const)* - }x; - $Type = qr{ - $NonptrType - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? - (?:\s+$Inline|\s+$Modifier)* - }x; - $TypeMisordered = qr{ - $NonptrTypeMisordered - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? - (?:\s+$Inline|\s+$Modifier)* - }x; - $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; - $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; -} -build_types(); - -our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; - -# Using $balanced_parens, $LvalOrFunc, or $FuncArg -# requires at least perl version v5.10.0 -# Any use must be runtime checked with $^V - -our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; -our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; -our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; - -our $declaration_macros = qr{(?x: - (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| - (?:$Storage\s+)?LIST_HEAD\s*\(| - (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\( -)}; - -sub deparenthesize { - my ($string) = @_; - return "" if (!defined($string)); - - while ($string =~ /^\s*\(.*\)\s*$/) { - $string =~ s@^\s*\(\s*@@; - $string =~ s@\s*\)\s*$@@; - } - - $string =~ s@\s+@ @g; - - return $string; -} - -sub seed_camelcase_file { - my ($file) = @_; - - return if (!(-f $file)); - - local $/; - - open(my $include_file, '<', "$file") - or warn "$P: Can't read '$file' $!\n"; - my $text = <$include_file>; - close($include_file); - - my @lines = split('\n', $text); - - foreach my $line (@lines) { - next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); - if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { - $camelcase{$1} = 1; - } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { - $camelcase{$1} = 1; - } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { - $camelcase{$1} = 1; - } - } -} - -my $camelcase_seeded = 0; -sub seed_camelcase_includes { - return if ($camelcase_seeded); - - my $files; - my $camelcase_cache = ""; - my @include_files = (); - - $camelcase_seeded = 1; - - if (-e ".git") { - my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; - chomp $git_last_include_commit; - $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; - } else { - my $last_mod_date = 0; - $files = `find $root/include -name "*.h"`; - @include_files = split('\n', $files); - foreach my $file (@include_files) { - my $date = POSIX::strftime("%Y%m%d%H%M", - localtime((stat $file)[9])); - $last_mod_date = $date if ($last_mod_date < $date); - } - $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; - } - - if ($camelcase_cache ne "" && -f $camelcase_cache) { - open(my $camelcase_file, '<', "$camelcase_cache") - or warn "$P: Can't read '$camelcase_cache' $!\n"; - while (<$camelcase_file>) { - chomp; - $camelcase{$_} = 1; - } - close($camelcase_file); - - return; - } - - if (-e ".git") { - $files = `git ls-files "include/*.h"`; - @include_files = split('\n', $files); - } - - foreach my $file (@include_files) { - seed_camelcase_file($file); - } - - if ($camelcase_cache ne "") { - unlink glob ".checkpatch-camelcase.*"; - open(my $camelcase_file, '>', "$camelcase_cache") - or warn "$P: Can't write '$camelcase_cache' $!\n"; - foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { - print $camelcase_file ("$_\n"); - } - close($camelcase_file); - } -} - -sub git_commit_info { - my ($commit, $id, $desc) = @_; - - return ($id, $desc) if ((which("git") eq "") || !(-e ".git")); - - my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`; - $output =~ s/^\s*//gm; - my @lines = split("\n", $output); - - return ($id, $desc) if ($#lines < 0); - - if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) { -# Maybe one day convert this block of bash into something that returns -# all matching commit ids, but it's very slow... -# -# echo "checking commits $1..." -# git rev-list --remotes | grep -i "^$1" | -# while read line ; do -# git log --format='%H %s' -1 $line | -# echo "commit $(cut -c 1-12,41-)" -# done - } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { - } else { - $id = substr($lines[0], 0, 12); - $desc = substr($lines[0], 41); - } - - return ($id, $desc); -} - -$chk_signoff = 0 if ($file); - -my @rawlines = (); -my @lines = (); -my @fixed = (); -my @fixed_inserted = (); -my @fixed_deleted = (); -my $fixlinenr = -1; - -my $vname; -for my $filename (@ARGV) { - my $FILE; - if ($file) { - open($FILE, '-|', "diff -u /dev/null $filename") || - die "$P: $filename: diff failed - $!\n"; - } elsif ($filename eq '-') { - open($FILE, '<&STDIN'); - } else { - open($FILE, '<', "$filename") || - die "$P: $filename: open failed - $!\n"; - } - if ($filename eq '-') { - $vname = 'Your patch'; - } else { - $vname = $filename; - } - while (<$FILE>) { - chomp; - push(@rawlines, $_); - } - close($FILE); - - if ($#ARGV > 0 && $quiet == 0) { - print '-' x length($vname) . "\n"; - print "$vname\n"; - print '-' x length($vname) . "\n"; - } - - if (!process($filename)) { - $exit = 1; - } - @rawlines = (); - @lines = (); - @fixed = (); - @fixed_inserted = (); - @fixed_deleted = (); - $fixlinenr = -1; - @modifierListFile = (); - @typeListFile = (); - build_types(); -} - -if (!$quiet) { - hash_show_words(\%use_type, "Used"); - hash_show_words(\%ignore_type, "Ignored"); - - if ($^V lt 5.10.0) { - print << "EOM" - -NOTE: perl $^V is not modern enough to detect all possible issues. - An upgrade to at least perl v5.10.0 is suggested. -EOM - } - if ($exit) { - print << "EOM" - -NOTE: If any of the errors are false positives, please report - them to the maintainer, see CHECKPATCH in MAINTAINERS. -EOM - } -} - -exit($exit); - -sub top_of_kernel_tree { - my ($root) = @_; - - my @tree_check = ( - "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", - "README", "Documentation", "arch", "include", "drivers", - "fs", "init", "ipc", "kernel", "lib", "scripts", - ); - - foreach my $check (@tree_check) { - if (! -e $root . '/' . $check) { - return 0; - } - } - return 1; -} - -sub parse_email { - my ($formatted_email) = @_; - - my $name = ""; - my $address = ""; - my $comment = ""; - - if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { - $name = $1; - $address = $2; - $comment = $3 if defined $3; - } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { - $address = $1; - $comment = $2 if defined $2; - } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { - $address = $1; - $comment = $2 if defined $2; - $formatted_email =~ s/$address.*$//; - $name = $formatted_email; - $name = trim($name); - $name =~ s/^\"|\"$//g; - # If there's a name left after stripping spaces and - # leading quotes, and the address doesn't have both - # leading and trailing angle brackets, the address - # is invalid. ie: - # "joe smith joe@smith.com" bad - # "joe smith <joe@smith.com" bad - if ($name ne "" && $address !~ /^<[^>]+>$/) { - $name = ""; - $address = ""; - $comment = ""; - } - } - - $name = trim($name); - $name =~ s/^\"|\"$//g; - $address = trim($address); - $address =~ s/^\<|\>$//g; - - if ($name =~ /[^\w \-]/i) { ##has "must quote" chars - $name =~ s/(?<!\\)"/\\"/g; ##escape quotes - $name = "\"$name\""; - } - - return ($name, $address, $comment); -} - -sub format_email { - my ($name, $address) = @_; - - my $formatted_email; - - $name = trim($name); - $name =~ s/^\"|\"$//g; - $address = trim($address); - - if ($name =~ /[^\w \-]/i) { ##has "must quote" chars - $name =~ s/(?<!\\)"/\\"/g; ##escape quotes - $name = "\"$name\""; - } - - if ("$name" eq "") { - $formatted_email = "$address"; - } else { - $formatted_email = "$name <$address>"; - } - - return $formatted_email; -} - -sub which { - my ($bin) = @_; - - foreach my $path (split(/:/, $ENV{PATH})) { - if (-e "$path/$bin") { - return "$path/$bin"; - } - } - - return ""; -} - -sub which_conf { - my ($conf) = @_; - - foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { - if (-e "$path/$conf") { - return "$path/$conf"; - } - } - - return ""; -} - -sub expand_tabs { - my ($str) = @_; - - my $res = ''; - my $n = 0; - for my $c (split(//, $str)) { - if ($c eq "\t") { - $res .= ' '; - $n++; - for (; ($n % 8) != 0; $n++) { - $res .= ' '; - } - next; - } - $res .= $c; - $n++; - } - - return $res; -} -sub copy_spacing { - (my $res = shift) =~ tr/\t/ /c; - return $res; -} - -sub line_stats { - my ($line) = @_; - - # Drop the diff line leader and expand tabs - $line =~ s/^.//; - $line = expand_tabs($line); - - # Pick the indent from the front of the line. - my ($white) = ($line =~ /^(\s*)/); - - return (length($line), length($white)); -} - -my $sanitise_quote = ''; - -sub sanitise_line_reset { - my ($in_comment) = @_; - - if ($in_comment) { - $sanitise_quote = '*/'; - } else { - $sanitise_quote = ''; - } -} -sub sanitise_line { - my ($line) = @_; - - my $res = ''; - my $l = ''; - - my $qlen = 0; - my $off = 0; - my $c; - - # Always copy over the diff marker. - $res = substr($line, 0, 1); - - for ($off = 1; $off < length($line); $off++) { - $c = substr($line, $off, 1); - - # Comments we are wacking completly including the begin - # and end, all to $;. - if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { - $sanitise_quote = '*/'; - - substr($res, $off, 2, "$;$;"); - $off++; - next; - } - if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { - $sanitise_quote = ''; - substr($res, $off, 2, "$;$;"); - $off++; - next; - } - if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { - $sanitise_quote = '//'; - - substr($res, $off, 2, $sanitise_quote); - $off++; - next; - } - - # A \ in a string means ignore the next character. - if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && - $c eq "\\") { - substr($res, $off, 2, 'XX'); - $off++; - next; - } - # Regular quotes. - if ($c eq "'" || $c eq '"') { - if ($sanitise_quote eq '') { - $sanitise_quote = $c; - - substr($res, $off, 1, $c); - next; - } elsif ($sanitise_quote eq $c) { - $sanitise_quote = ''; - } - } - - #print "c<$c> SQ<$sanitise_quote>\n"; - if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { - substr($res, $off, 1, $;); - } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { - substr($res, $off, 1, $;); - } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { - substr($res, $off, 1, 'X'); - } else { - substr($res, $off, 1, $c); - } - } - - if ($sanitise_quote eq '//') { - $sanitise_quote = ''; - } - - # The pathname on a #include may be surrounded by '<' and '>'. - if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { - my $clean = 'X' x length($1); - $res =~ s@\<.*\>@<$clean>@; - - # The whole of a #error is a string. - } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { - my $clean = 'X' x length($1); - $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; - } - - return $res; -} - -sub get_quoted_string { - my ($line, $rawline) = @_; - - return "" if ($line !~ m/($String)/g); - return substr($rawline, $-[0], $+[0] - $-[0]); -} - -sub ctx_statement_block { - my ($linenr, $remain, $off) = @_; - my $line = $linenr - 1; - my $blk = ''; - my $soff = $off; - my $coff = $off - 1; - my $coff_set = 0; - - my $loff = 0; - - my $type = ''; - my $level = 0; - my @stack = (); - my $p; - my $c; - my $len = 0; - - my $remainder; - while (1) { - @stack = (['', 0]) if ($#stack == -1); - - #warn "CSB: blk<$blk> remain<$remain>\n"; - # If we are about to drop off the end, pull in more - # context. - if ($off >= $len) { - for (; $remain > 0; $line++) { - last if (!defined $lines[$line]); - next if ($lines[$line] =~ /^-/); - $remain--; - $loff = $len; - $blk .= $lines[$line] . "\n"; - $len = length($blk); - $line++; - last; - } - # Bail if there is no further context. - #warn "CSB: blk<$blk> off<$off> len<$len>\n"; - if ($off >= $len) { - last; - } - if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { - $level++; - $type = '#'; - } - } - $p = $c; - $c = substr($blk, $off, 1); - $remainder = substr($blk, $off); - - #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; - - # Handle nested #if/#else. - if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { - push(@stack, [ $type, $level ]); - } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { - ($type, $level) = @{$stack[$#stack - 1]}; - } elsif ($remainder =~ /^#\s*endif\b/) { - ($type, $level) = @{pop(@stack)}; - } - - # Statement ends at the ';' or a close '}' at the - # outermost level. - if ($level == 0 && $c eq ';') { - last; - } - - # An else is really a conditional as long as its not else if - if ($level == 0 && $coff_set == 0 && - (!defined($p) || $p =~ /(?:\s|\}|\+)/) && - $remainder =~ /^(else)(?:\s|{)/ && - $remainder !~ /^else\s+if\b/) { - $coff = $off + length($1) - 1; - $coff_set = 1; - #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; - #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; - } - - if (($type eq '' || $type eq '(') && $c eq '(') { - $level++; - $type = '('; - } - if ($type eq '(' && $c eq ')') { - $level--; - $type = ($level != 0)? '(' : ''; - - if ($level == 0 && $coff < $soff) { - $coff = $off; - $coff_set = 1; - #warn "CSB: mark coff<$coff>\n"; - } - } - if (($type eq '' || $type eq '{') && $c eq '{') { - $level++; - $type = '{'; - } - if ($type eq '{' && $c eq '}') { - $level--; - $type = ($level != 0)? '{' : ''; - - if ($level == 0) { - if (substr($blk, $off + 1, 1) eq ';') { - $off++; - } - last; - } - } - # Preprocessor commands end at the newline unless escaped. - if ($type eq '#' && $c eq "\n" && $p ne "\\") { - $level--; - $type = ''; - $off++; - last; - } - $off++; - } - # We are truly at the end, so shuffle to the next line. - if ($off == $len) { - $loff = $len + 1; - $line++; - $remain--; - } - - my $statement = substr($blk, $soff, $off - $soff + 1); - my $condition = substr($blk, $soff, $coff - $soff + 1); - - #warn "STATEMENT<$statement>\n"; - #warn "CONDITION<$condition>\n"; - - #print "coff<$coff> soff<$off> loff<$loff>\n"; - - return ($statement, $condition, - $line, $remain + 1, $off - $loff + 1, $level); -} - -sub statement_lines { - my ($stmt) = @_; - - # Strip the diff line prefixes and rip blank lines at start and end. - $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*//; - $stmt =~ s/\s*$//; - - my @stmt_lines = ($stmt =~ /\n/g); - - return $#stmt_lines + 2; -} - -sub statement_rawlines { - my ($stmt) = @_; - - my @stmt_lines = ($stmt =~ /\n/g); - - return $#stmt_lines + 2; -} - -sub statement_block_size { - my ($stmt) = @_; - - $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*{//; - $stmt =~ s/}\s*$//; - $stmt =~ s/^\s*//; - $stmt =~ s/\s*$//; - - my @stmt_lines = ($stmt =~ /\n/g); - my @stmt_statements = ($stmt =~ /;/g); - - my $stmt_lines = $#stmt_lines + 2; - my $stmt_statements = $#stmt_statements + 1; - - if ($stmt_lines > $stmt_statements) { - return $stmt_lines; - } else { - return $stmt_statements; - } -} - -sub ctx_statement_full { - my ($linenr, $remain, $off) = @_; - my ($statement, $condition, $level); - - my (@chunks); - - # Grab the first conditional/block pair. - ($statement, $condition, $linenr, $remain, $off, $level) = - ctx_statement_block($linenr, $remain, $off); - #print "F: c<$condition> s<$statement> remain<$remain>\n"; - push(@chunks, [ $condition, $statement ]); - if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { - return ($level, $linenr, @chunks); - } - - # Pull in the following conditional/block pairs and see if they - # could continue the statement. - for (;;) { - ($statement, $condition, $linenr, $remain, $off, $level) = - ctx_statement_block($linenr, $remain, $off); - #print "C: c<$condition> s<$statement> remain<$remain>\n"; - last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); - #print "C: push\n"; - push(@chunks, [ $condition, $statement ]); - } - - return ($level, $linenr, @chunks); -} - -sub ctx_block_get { - my ($linenr, $remain, $outer, $open, $close, $off) = @_; - my $line; - my $start = $linenr - 1; - my $blk = ''; - my @o; - my @c; - my @res = (); - - my $level = 0; - my @stack = ($level); - for ($line = $start; $remain > 0; $line++) { - next if ($rawlines[$line] =~ /^-/); - $remain--; - - $blk .= $rawlines[$line]; - - # Handle nested #if/#else. - if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { - push(@stack, $level); - } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { - $level = $stack[$#stack - 1]; - } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { - $level = pop(@stack); - } - - foreach my $c (split(//, $lines[$line])) { - ##print "C<$c>L<$level><$open$close>O<$off>\n"; - if ($off > 0) { - $off--; - next; - } - - if ($c eq $close && $level > 0) { - $level--; - last if ($level == 0); - } elsif ($c eq $open) { - $level++; - } - } - - if (!$outer || $level <= 1) { - push(@res, $rawlines[$line]); - } - - last if ($level == 0); - } - - return ($level, @res); -} -sub ctx_block_outer { - my ($linenr, $remain) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); - return @r; -} -sub ctx_block { - my ($linenr, $remain) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); - return @r; -} -sub ctx_statement { - my ($linenr, $remain, $off) = @_; - - my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); - return @r; -} -sub ctx_block_level { - my ($linenr, $remain) = @_; - - return ctx_block_get($linenr, $remain, 0, '{', '}', 0); -} -sub ctx_statement_level { - my ($linenr, $remain, $off) = @_; - - return ctx_block_get($linenr, $remain, 0, '(', ')', $off); -} - -sub ctx_locate_comment { - my ($first_line, $end_line) = @_; - - # Catch a comment on the end of the line itself. - my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); - return $current_comment if (defined $current_comment); - - # Look through the context and try and figure out if there is a - # comment. - my $in_comment = 0; - $current_comment = ''; - for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { - my $line = $rawlines[$linenr - 1]; - #warn " $line\n"; - if ($linenr == $first_line and $line =~ m@^.\s*\*@) { - $in_comment = 1; - } - if ($line =~ m@/\*@) { - $in_comment = 1; - } - if (!$in_comment && $current_comment ne '') { - $current_comment = ''; - } - $current_comment .= $line . "\n" if ($in_comment); - if ($line =~ m@\*/@) { - $in_comment = 0; - } - } - - chomp($current_comment); - return($current_comment); -} -sub ctx_has_comment { - my ($first_line, $end_line) = @_; - my $cmt = ctx_locate_comment($first_line, $end_line); - - ##print "LINE: $rawlines[$end_line - 1 ]\n"; - ##print "CMMT: $cmt\n"; - - return ($cmt ne ''); -} - -sub raw_line { - my ($linenr, $cnt) = @_; - - my $offset = $linenr - 1; - $cnt++; - - my $line; - while ($cnt) { - $line = $rawlines[$offset++]; - next if (defined($line) && $line =~ /^-/); - $cnt--; - } - - return $line; -} - -sub cat_vet { - my ($vet) = @_; - my ($res, $coded); - - $res = ''; - while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { - $res .= $1; - if ($2 ne '') { - $coded = sprintf("^%c", unpack('C', $2) + 64); - $res .= $coded; - } - } - $res =~ s/$/\$/; - - return $res; -} - -my $av_preprocessor = 0; -my $av_pending; -my @av_paren_type; -my $av_pend_colon; - -sub annotate_reset { - $av_preprocessor = 0; - $av_pending = '_'; - @av_paren_type = ('E'); - $av_pend_colon = 'O'; -} - -sub annotate_values { - my ($stream, $type) = @_; - - my $res; - my $var = '_' x length($stream); - my $cur = $stream; - - print "$stream\n" if ($dbg_values > 1); - - while (length($cur)) { - @av_paren_type = ('E') if ($#av_paren_type < 0); - print " <" . join('', @av_paren_type) . - "> <$type> <$av_pending>" if ($dbg_values > 1); - if ($cur =~ /^(\s+)/o) { - print "WS($1)\n" if ($dbg_values > 1); - if ($1 =~ /\n/ && $av_preprocessor) { - $type = pop(@av_paren_type); - $av_preprocessor = 0; - } - - } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { - print "CAST($1)\n" if ($dbg_values > 1); - push(@av_paren_type, $type); - $type = 'c'; - - } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { - print "DECLARE($1)\n" if ($dbg_values > 1); - $type = 'T'; - - } elsif ($cur =~ /^($Modifier)\s*/) { - print "MODIFIER($1)\n" if ($dbg_values > 1); - $type = 'T'; - - } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { - print "DEFINE($1,$2)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - push(@av_paren_type, $type); - if ($2 ne '') { - $av_pending = 'N'; - } - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { - print "UNDEF($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - push(@av_paren_type, $type); - - } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { - print "PRE_START($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - - push(@av_paren_type, $type); - push(@av_paren_type, $type); - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { - print "PRE_RESTART($1)\n" if ($dbg_values > 1); - $av_preprocessor = 1; - - push(@av_paren_type, $av_paren_type[$#av_paren_type]); - - $type = 'E'; - - } elsif ($cur =~ /^(\#\s*(?:endif))/o) { - print "PRE_END($1)\n" if ($dbg_values > 1); - - $av_preprocessor = 1; - - # Assume all arms of the conditional end as this - # one does, and continue as if the #endif was not here. - pop(@av_paren_type); - push(@av_paren_type, $type); - $type = 'E'; - - } elsif ($cur =~ /^(\\\n)/o) { - print "PRECONT($1)\n" if ($dbg_values > 1); - - } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { - print "ATTR($1)\n" if ($dbg_values > 1); - $av_pending = $type; - $type = 'N'; - - } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { - print "SIZEOF($1)\n" if ($dbg_values > 1); - if (defined $2) { - $av_pending = 'V'; - } - $type = 'N'; - - } elsif ($cur =~ /^(if|while|for)\b/o) { - print "COND($1)\n" if ($dbg_values > 1); - $av_pending = 'E'; - $type = 'N'; - - } elsif ($cur =~/^(case)/o) { - print "CASE($1)\n" if ($dbg_values > 1); - $av_pend_colon = 'C'; - $type = 'N'; - - } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { - print "KEYWORD($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(\()/o) { - print "PAREN('$1')\n" if ($dbg_values > 1); - push(@av_paren_type, $av_pending); - $av_pending = '_'; - $type = 'N'; - - } elsif ($cur =~ /^(\))/o) { - my $new_type = pop(@av_paren_type); - if ($new_type ne '_') { - $type = $new_type; - print "PAREN('$1') -> $type\n" - if ($dbg_values > 1); - } else { - print "PAREN('$1')\n" if ($dbg_values > 1); - } - - } elsif ($cur =~ /^($Ident)\s*\(/o) { - print "FUNC($1)\n" if ($dbg_values > 1); - $type = 'V'; - $av_pending = 'V'; - - } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { - if (defined $2 && $type eq 'C' || $type eq 'T') { - $av_pend_colon = 'B'; - } elsif ($type eq 'E') { - $av_pend_colon = 'L'; - } - print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); - $type = 'V'; - - } elsif ($cur =~ /^($Ident|$Constant)/o) { - print "IDENT($1)\n" if ($dbg_values > 1); - $type = 'V'; - - } elsif ($cur =~ /^($Assignment)/o) { - print "ASSIGN($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~/^(;|{|})/) { - print "END($1)\n" if ($dbg_values > 1); - $type = 'E'; - $av_pend_colon = 'O'; - - } elsif ($cur =~/^(,)/) { - print "COMMA($1)\n" if ($dbg_values > 1); - $type = 'C'; - - } elsif ($cur =~ /^(\?)/o) { - print "QUESTION($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(:)/o) { - print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); - - substr($var, length($res), 1, $av_pend_colon); - if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { - $type = 'E'; - } else { - $type = 'N'; - } - $av_pend_colon = 'O'; - - } elsif ($cur =~ /^(\[)/o) { - print "CLOSE($1)\n" if ($dbg_values > 1); - $type = 'N'; - - } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { - my $variant; - - print "OPV($1)\n" if ($dbg_values > 1); - if ($type eq 'V') { - $variant = 'B'; - } else { - $variant = 'U'; - } - - substr($var, length($res), 1, $variant); - $type = 'N'; - - } elsif ($cur =~ /^($Operators)/o) { - print "OP($1)\n" if ($dbg_values > 1); - if ($1 ne '++' && $1 ne '--') { - $type = 'N'; - } - - } elsif ($cur =~ /(^.)/o) { - print "C($1)\n" if ($dbg_values > 1); - } - if (defined $1) { - $cur = substr($cur, length($1)); - $res .= $type x length($1); - } - } - - return ($res, $var); -} - -sub possible { - my ($possible, $line) = @_; - my $notPermitted = qr{(?: - ^(?: - $Modifier| - $Storage| - $Type| - DEFINE_\S+ - )$| - ^(?: - goto| - return| - case| - else| - asm|__asm__| - do| - \#| - \#\#| - )(?:\s|$)| - ^(?:typedef|struct|enum)\b - )}x; - warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); - if ($possible !~ $notPermitted) { - # Check for modifiers. - $possible =~ s/\s*$Storage\s*//g; - $possible =~ s/\s*$Sparse\s*//g; - if ($possible =~ /^\s*$/) { - - } elsif ($possible =~ /\s/) { - $possible =~ s/\s*$Type\s*//g; - for my $modifier (split(' ', $possible)) { - if ($modifier !~ $notPermitted) { - warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); - push(@modifierListFile, $modifier); - } - } - - } else { - warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); - push(@typeListFile, $possible); - } - build_types(); - } else { - warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); - } -} - -my $prefix = ''; - -sub show_type { - my ($type) = @_; - - return defined $use_type{$type} if (scalar keys %use_type > 0); - - return !defined $ignore_type{$type}; -} - -sub report { - my ($level, $type, $msg) = @_; - - if (!show_type($type) || - (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { - return 0; - } - my $output = ''; - if (-t STDOUT && $color) { - if ($level eq 'ERROR') { - $output .= RED; - } elsif ($level eq 'WARNING') { - $output .= YELLOW; - } else { - $output .= GREEN; - } - } - $output .= $prefix . $level . ':'; - if ($show_types) { - $output .= BLUE if (-t STDOUT && $color); - $output .= "$type:"; - } - $output .= RESET if (-t STDOUT && $color); - $output .= ' ' . $msg . "\n"; - - if ($showfile) { - my @lines = split("\n", $output, -1); - splice(@lines, 1, 1); - $output = join("\n", @lines); - } - $output = (split('\n', $output))[0] . "\n" if ($terse); - - push(our @report, $output); - - return 1; -} - -sub report_dump { - our @report; -} - -sub fixup_current_range { - my ($lineRef, $offset, $length) = @_; - - if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { - my $o = $1; - my $l = $2; - my $no = $o + $offset; - my $nl = $l + $length; - $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; - } -} - -sub fix_inserted_deleted_lines { - my ($linesRef, $insertedRef, $deletedRef) = @_; - - my $range_last_linenr = 0; - my $delta_offset = 0; - - my $old_linenr = 0; - my $new_linenr = 0; - - my $next_insert = 0; - my $next_delete = 0; - - my @lines = (); - - my $inserted = @{$insertedRef}[$next_insert++]; - my $deleted = @{$deletedRef}[$next_delete++]; - - foreach my $old_line (@{$linesRef}) { - my $save_line = 1; - my $line = $old_line; #don't modify the array - if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename - $delta_offset = 0; - } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk - $range_last_linenr = $new_linenr; - fixup_current_range(\$line, $delta_offset, 0); - } - - while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { - $deleted = @{$deletedRef}[$next_delete++]; - $save_line = 0; - fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); - } - - while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { - push(@lines, ${$inserted}{'LINE'}); - $inserted = @{$insertedRef}[$next_insert++]; - $new_linenr++; - fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); - } - - if ($save_line) { - push(@lines, $line); - $new_linenr++; - } - - $old_linenr++; - } - - return @lines; -} - -sub fix_insert_line { - my ($linenr, $line) = @_; - - my $inserted = { - LINENR => $linenr, - LINE => $line, - }; - push(@fixed_inserted, $inserted); -} - -sub fix_delete_line { - my ($linenr, $line) = @_; - - my $deleted = { - LINENR => $linenr, - LINE => $line, - }; - - push(@fixed_deleted, $deleted); -} - -sub ERROR { - my ($type, $msg) = @_; - - if (report("ERROR", $type, $msg)) { - our $clean = 0; - our $cnt_error++; - return 1; - } - return 0; -} -sub WARN { - my ($type, $msg) = @_; - - if (report("WARNING", $type, $msg)) { - our $clean = 0; - our $cnt_warn++; - return 1; - } - return 0; -} -sub CHK { - my ($type, $msg) = @_; - - if ($check && report("CHECK", $type, $msg)) { - our $clean = 0; - our $cnt_chk++; - return 1; - } - return 0; -} - -sub check_absolute_file { - my ($absolute, $herecurr) = @_; - my $file = $absolute; - - ##print "absolute<$absolute>\n"; - - # See if any suffix of this path is a path within the tree. - while ($file =~ s@^[^/]*/@@) { - if (-f "$root/$file") { - ##print "file<$file>\n"; - last; - } - } - if (! -f _) { - return 0; - } - - # It is, so see if the prefix is acceptable. - my $prefix = $absolute; - substr($prefix, -length($file)) = ''; - - ##print "prefix<$prefix>\n"; - if ($prefix ne ".../") { - WARN("USE_RELATIVE_PATH", - "use relative pathname instead of absolute in changelog text\n" . $herecurr); - } -} - -sub trim { - my ($string) = @_; - - $string =~ s/^\s+|\s+$//g; - - return $string; -} - -sub ltrim { - my ($string) = @_; - - $string =~ s/^\s+//; - - return $string; -} - -sub rtrim { - my ($string) = @_; - - $string =~ s/\s+$//; - - return $string; -} - -sub string_find_replace { - my ($string, $find, $replace) = @_; - - $string =~ s/$find/$replace/g; - - return $string; -} - -sub tabify { - my ($leading) = @_; - - my $source_indent = 8; - my $max_spaces_before_tab = $source_indent - 1; - my $spaces_to_tab = " " x $source_indent; - - #convert leading spaces to tabs - 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; - #Remove spaces before a tab - 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; - - return "$leading"; -} - -sub pos_last_openparen { - my ($line) = @_; - - my $pos = 0; - - my $opens = $line =~ tr/\(/\(/; - my $closes = $line =~ tr/\)/\)/; - - my $last_openparen = 0; - - if (($opens == 0) || ($closes >= $opens)) { - return -1; - } - - my $len = length($line); - - for ($pos = 0; $pos < $len; $pos++) { - my $string = substr($line, $pos); - if ($string =~ /^($FuncArg|$balanced_parens)/) { - $pos += length($1) - 1; - } elsif (substr($line, $pos, 1) eq '(') { - $last_openparen = $pos; - } elsif (index($string, '(') == -1) { - last; - } - } - - return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; -} - -sub process { - my $filename = shift; - - my $linenr=0; - my $prevline=""; - my $prevrawline=""; - my $stashline=""; - my $stashrawline=""; - - my $length; - my $indent; - my $previndent=0; - my $stashindent=0; - - our $clean = 1; - my $signoff = 0; - my $is_patch = 0; - my $in_header_lines = $file ? 0 : 1; - my $in_commit_log = 0; #Scanning lines before patch - my $commit_log_possible_stack_dump = 0; - my $commit_log_long_line = 0; - my $commit_log_has_diff = 0; - my $reported_maintainer_file = 0; - my $non_utf8_charset = 0; - - my $last_blank_line = 0; - my $last_coalesced_string_linenr = -1; - - our @report = (); - our $cnt_lines = 0; - our $cnt_error = 0; - our $cnt_warn = 0; - our $cnt_chk = 0; - - # Trace the real file/line as we go. - my $realfile = ''; - my $realline = 0; - my $realcnt = 0; - my $here = ''; - my $in_comment = 0; - my $comment_edge = 0; - my $first_line = 0; - my $p1_prefix = ''; - - my $prev_values = 'E'; - - # suppression flags - my %suppress_ifbraces; - my %suppress_whiletrailers; - my %suppress_export; - my $suppress_statement = 0; - - my %signatures = (); - - # Pre-scan the patch sanitizing the lines. - # Pre-scan the patch looking for any __setup documentation. - # - my @setup_docs = (); - my $setup_docs = 0; - - my $camelcase_file_seeded = 0; - - sanitise_line_reset(); - my $line; - foreach my $rawline (@rawlines) { - $linenr++; - $line = $rawline; - - push(@fixed, $rawline) if ($fix); - - if ($rawline=~/^\+\+\+\s+(\S+)/) { - $setup_docs = 0; - if ($1 =~ m@Documentation/kernel-parameters.txt$@) { - $setup_docs = 1; - } - #next; - } - if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { - $realline=$1-1; - if (defined $2) { - $realcnt=$3+1; - } else { - $realcnt=1+1; - } - $in_comment = 0; - - # Guestimate if this is a continuing comment. Run - # the context looking for a comment "edge". If this - # edge is a close comment then we must be in a comment - # at context start. - my $edge; - my $cnt = $realcnt; - for (my $ln = $linenr + 1; $cnt > 0; $ln++) { - next if (defined $rawlines[$ln - 1] && - $rawlines[$ln - 1] =~ /^-/); - $cnt--; - #print "RAW<$rawlines[$ln - 1]>\n"; - last if (!defined $rawlines[$ln - 1]); - if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && - $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { - ($edge) = $1; - last; - } - } - if (defined $edge && $edge eq '*/') { - $in_comment = 1; - } - - # Guestimate if this is a continuing comment. If this - # is the start of a diff block and this line starts - # ' *' then it is very likely a comment. - if (!defined $edge && - $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) - { - $in_comment = 1; - } - - ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; - sanitise_line_reset($in_comment); - - } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { - # Standardise the strings and chars within the input to - # simplify matching -- only bother with positive lines. - $line = sanitise_line($rawline); - } - push(@lines, $line); - - if ($realcnt > 1) { - $realcnt-- if ($line =~ /^(?:\+| |$)/); - } else { - $realcnt = 0; - } - - #print "==>$rawline\n"; - #print "-->$line\n"; - - if ($setup_docs && $line =~ /^\+/) { - push(@setup_docs, $line); - } - } - - $prefix = ''; - - $realcnt = 0; - $linenr = 0; - $fixlinenr = -1; - foreach my $line (@lines) { - $linenr++; - $fixlinenr++; - my $sline = $line; #copy of $line - $sline =~ s/$;/ /g; #with comments as spaces - - my $rawline = $rawlines[$linenr - 1]; - -#extract the line range in the file after the patch is applied - if (!$in_commit_log && - $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { - $is_patch = 1; - $first_line = $linenr + 1; - $realline=$1-1; - if (defined $2) { - $realcnt=$3+1; - } else { - $realcnt=1+1; - } - annotate_reset(); - $prev_values = 'E'; - - %suppress_ifbraces = (); - %suppress_whiletrailers = (); - %suppress_export = (); - $suppress_statement = 0; - next; - -# track the line number as we move through the hunk, note that -# new versions of GNU diff omit the leading space on completely -# blank context lines so we need to count that too. - } elsif ($line =~ /^( |\+|$)/) { - $realline++; - $realcnt-- if ($realcnt != 0); - - # Measure the line length and indent. - ($length, $indent) = line_stats($rawline); - - # Track the previous line. - ($prevline, $stashline) = ($stashline, $line); - ($previndent, $stashindent) = ($stashindent, $indent); - ($prevrawline, $stashrawline) = ($stashrawline, $rawline); - - #warn "line<$line>\n"; - - } elsif ($realcnt == 1) { - $realcnt--; - } - - my $hunk_line = ($realcnt != 0); - - $here = "#$linenr: " if (!$file); - $here = "#$realline: " if ($file); - - my $found_file = 0; - # extract the filename as it passes - if ($line =~ /^diff --git.*?(\S+)$/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); - $in_commit_log = 0; - $found_file = 1; - } elsif ($line =~ /^\+\+\+\s+(\S+)/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); - $in_commit_log = 0; - - $p1_prefix = $1; - if (!$file && $tree && $p1_prefix ne '' && - -e "$root/$p1_prefix") { - WARN("PATCH_PREFIX", - "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); - } - - if ($realfile =~ m@^include/asm/@) { - ERROR("MODIFIED_INCLUDE_ASM", - "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n"); - } - $found_file = 1; - } - -#make up the handle for any error we report on this line - if ($showfile) { - $prefix = "$realfile:$realline: " - } elsif ($emacs) { - if ($file) { - $prefix = "$filename:$realline: "; - } else { - $prefix = "$filename:$linenr: "; - } - } - - if ($found_file) { - if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { - $check = 1; - } else { - $check = $check_orig; - } - next; - } - - $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); - - my $hereline = "$here\n$rawline\n"; - my $herecurr = "$here\n$rawline\n"; - my $hereprev = "$here\n$prevrawline\n$rawline\n"; - - $cnt_lines++ if ($realcnt != 0); - -# Check if the commit log has what seems like a diff which can confuse patch - if ($in_commit_log && !$commit_log_has_diff && - (($line =~ m@^\s+diff\b.*a/[\w/]+@ && - $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) || - $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || - $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { - ERROR("DIFF_IN_COMMIT_MSG", - "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); - $commit_log_has_diff = 1; - } - -# Check for incorrect file permissions - if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { - my $permhere = $here . "FILE: $realfile\n"; - if ($realfile !~ m@scripts/@ && - $realfile !~ /\.(py|pl|awk|sh)$/) { - ERROR("EXECUTE_PERMISSIONS", - "do not set execute permissions for source files\n" . $permhere); - } - } - -# Check the patch for a signoff: - if ($line =~ /^\s*signed-off-by:/i) { - $signoff++; - $in_commit_log = 0; - } - -# Check if MAINTAINERS is being updated. If so, there's probably no need to -# emit the "does MAINTAINERS need updating?" message on file add/move/delete - if ($line =~ /^\s*MAINTAINERS\s*\|/) { - $reported_maintainer_file = 1; - } - -# Check signature styles - if (!$in_header_lines && - $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { - my $space_before = $1; - my $sign_off = $2; - my $space_after = $3; - my $email = $4; - my $ucfirst_sign_off = ucfirst(lc($sign_off)); - - if ($sign_off !~ /$signature_tags/) { - WARN("BAD_SIGN_OFF", - "Non-standard signature: $sign_off\n" . $herecurr); - } - if (defined $space_before && $space_before ne "") { - if (WARN("BAD_SIGN_OFF", - "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - } - if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { - if (WARN("BAD_SIGN_OFF", - "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - - } - if (!defined $space_after || $space_after ne " ") { - if (WARN("BAD_SIGN_OFF", - "Use a single space after $ucfirst_sign_off\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] = - "$ucfirst_sign_off $email"; - } - } - - my ($email_name, $email_address, $comment) = parse_email($email); - my $suggested_email = format_email(($email_name, $email_address)); - if ($suggested_email eq "") { - ERROR("BAD_SIGN_OFF", - "Unrecognized email address: '$email'\n" . $herecurr); - } else { - my $dequoted = $suggested_email; - $dequoted =~ s/^"//; - $dequoted =~ s/" </ </; - # Don't force email to have quotes - # Allow just an angle bracketed address - if ("$dequoted$comment" ne $email && - "<$email_address>$comment" ne $email && - "$suggested_email$comment" ne $email) { - WARN("BAD_SIGN_OFF", - "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); - } - } - -# Check for duplicate signatures - my $sig_nospace = $line; - $sig_nospace =~ s/\s//g; - $sig_nospace = lc($sig_nospace); - if (defined $signatures{$sig_nospace}) { - WARN("BAD_SIGN_OFF", - "Duplicate signature\n" . $herecurr); - } else { - $signatures{$sig_nospace} = 1; - } - } - -# Check email subject for common tools that don't need to be mentioned - if ($in_header_lines && - $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { - WARN("EMAIL_SUBJECT", - "A patch subject line should describe the change not the tool that found it\n" . $herecurr); - } - -# Check for old stable address - if ($line =~ /^\s*cc:\s*.*<?\bstable\@kernel\.org\b>?.*$/i) { - ERROR("STABLE_ADDRESS", - "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr); - } - -# Check for unwanted Gerrit info - if ($in_commit_log && $line =~ /^\s*change-id:/i) { - ERROR("GERRIT_CHANGE_ID", - "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); - } - -# Check if the commit log is in a possible stack dump - if ($in_commit_log && !$commit_log_possible_stack_dump && - ($line =~ /^\s*(?:WARNING:|BUG:)/ || - $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || - # timestamp - $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) { - # stack dump address - $commit_log_possible_stack_dump = 1; - } - -# Check for line lengths > 75 in commit log, warn once - if ($in_commit_log && !$commit_log_long_line && - length($line) > 75 && - !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || - # file delta changes - $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || - # filename then : - $line =~ /^\s*(?:Fixes:|Link:)/i || - # A Fixes: or Link: line - $commit_log_possible_stack_dump)) { - WARN("COMMIT_LOG_LONG_LINE", - "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); - $commit_log_long_line = 1; - } - -# Reset possible stack dump if a blank line is found - if ($in_commit_log && $commit_log_possible_stack_dump && - $line =~ /^\s*$/) { - $commit_log_possible_stack_dump = 0; - } - -# Check for git id commit length and improperly formed commit descriptions - if ($in_commit_log && !$commit_log_possible_stack_dump && - ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || - ($line =~ /\b[0-9a-f]{12,40}\b/i && - $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && - $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { - my $init_char = "c"; - my $orig_commit = ""; - my $short = 1; - my $long = 0; - my $case = 1; - my $space = 1; - my $hasdesc = 0; - my $hasparens = 0; - my $id = '0123456789ab'; - my $orig_desc = "commit description"; - my $description = ""; - - if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { - $init_char = $1; - $orig_commit = lc($2); - } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) { - $orig_commit = lc($1); - } - - $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i); - $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i); - $space = 0 if ($line =~ /\bcommit [0-9a-f]/i); - $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); - if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) { - $orig_desc = $1; - $hasparens = 1; - } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i && - defined $rawlines[$linenr] && - $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) { - $orig_desc = $1; - $hasparens = 1; - } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i && - defined $rawlines[$linenr] && - $rawlines[$linenr] =~ /^\s*[^"]+"\)/) { - $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i; - $orig_desc = $1; - $rawlines[$linenr] =~ /^\s*([^"]+)"\)/; - $orig_desc .= " " . $1; - $hasparens = 1; - } - - ($id, $description) = git_commit_info($orig_commit, - $id, $orig_desc); - - if ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens) { - ERROR("GIT_COMMIT_ID", - "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); - } - } - -# Check for added, moved or deleted files - if (!$reported_maintainer_file && !$in_commit_log && - ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || - $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || - ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && - (defined($1) || defined($2))))) { - $reported_maintainer_file = 1; - WARN("FILE_PATH_CHANGES", - "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); - } - -# Check for wrappage within a valid hunk of the file - if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { - ERROR("CORRUPTED_PATCH", - "patch seems to be corrupt (line wrapped?)\n" . - $herecurr) if (!$emitted_corrupt++); - } - -# Check for absolute kernel paths. - if ($tree) { - while ($line =~ m{(?:^|\s)(/\S*)}g) { - my $file = $1; - - if ($file =~ m{^(.*?)(?::\d+)+:?$} && - check_absolute_file($1, $herecurr)) { - # - } else { - check_absolute_file($file, $herecurr); - } - } - } - -# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php - if (($realfile =~ /^$/ || $line =~ /^\+/) && - $rawline !~ m/^$UTF8*$/) { - my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); - - my $blank = copy_spacing($rawline); - my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; - my $hereptr = "$hereline$ptr\n"; - - CHK("INVALID_UTF8", - "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); - } - -# Check if it's the start of a commit log -# (not a header line and we haven't seen the patch filename) - if ($in_header_lines && $realfile =~ /^$/ && - !($rawline =~ /^\s+\S/ || - $rawline =~ /^(commit\b|from\b|[\w-]+:).*$/i)) { - $in_header_lines = 0; - $in_commit_log = 1; - } - -# Check if there is UTF-8 in a commit log when a mail header has explicitly -# declined it, i.e defined some charset where it is missing. - if ($in_header_lines && - $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && - $1 !~ /utf-8/i) { - $non_utf8_charset = 1; - } - - if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && - $rawline =~ /$NON_ASCII_UTF8/) { - WARN("UTF8_BEFORE_PATCH", - "8-bit UTF-8 used in possible commit log\n" . $herecurr); - } - -# Check for various typo / spelling mistakes - if (defined($misspellings) && - ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { - while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) { - my $typo = $1; - my $typo_fix = $spelling_fix{lc($typo)}; - $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); - $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); - my $msg_type = \&WARN; - $msg_type = \&CHK if ($file); - if (&{$msg_type}("TYPO_SPELLING", - "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; - } - } - } - -# ignore non-hunk lines and lines being removed - next if (!$hunk_line || $line =~ /^-/); - -#trailing whitespace - if ($line =~ /^\+.*\015/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (ERROR("DOS_LINE_ENDINGS", - "DOS line endings\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/[\s\015]+$//; - } - } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (ERROR("TRAILING_WHITESPACE", - "trailing whitespace\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+$//; - } - - $rpt_cleaners = 1; - } - -# Check for FSF mailing addresses. - if ($rawline =~ /\bwrite to the Free/i || - $rawline =~ /\b59\s+Temple\s+Pl/i || - $rawline =~ /\b51\s+Franklin\s+St/i) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - my $msg_type = \&ERROR; - $msg_type = \&CHK if ($file); - &{$msg_type}("FSF_MAILING_ADDRESS", - "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) - } - -# check for Kconfig help text having a real description -# Only applies when adding the entry originally, after that we do not have -# sufficient context to determine whether it is indeed long enough. - if ($realfile =~ /Kconfig/ && - $line =~ /^\+\s*config\s+/) { - my $length = 0; - my $cnt = $realcnt; - my $ln = $linenr + 1; - my $f; - my $is_start = 0; - my $is_end = 0; - for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) { - $f = $lines[$ln - 1]; - $cnt-- if ($lines[$ln - 1] !~ /^-/); - $is_end = $lines[$ln - 1] =~ /^\+/; - - next if ($f =~ /^-/); - last if (!$file && $f =~ /^\@\@/); - - if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { - $is_start = 1; - } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { - $length = -1; - } - - $f =~ s/^.//; - $f =~ s/#.*//; - $f =~ s/^\s+//; - next if ($f =~ /^$/); - if ($f =~ /^\s*config\s/) { - $is_end = 1; - last; - } - $length++; - } - if ($is_start && $is_end && $length < $min_conf_desc_length) { - WARN("CONFIG_DESCRIPTION", - "please write a paragraph that describes the config symbol fully\n" . $herecurr); - } - #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; - } - -# discourage the addition of CONFIG_EXPERIMENTAL in Kconfig. - if ($realfile =~ /Kconfig/ && - $line =~ /.\s*depends on\s+.*\bEXPERIMENTAL\b/) { - WARN("CONFIG_EXPERIMENTAL", - "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); - } - -# discourage the use of boolean for type definition attributes of Kconfig options - if ($realfile =~ /Kconfig/ && - $line =~ /^\+\s*\bboolean\b/) { - WARN("CONFIG_TYPE_BOOLEAN", - "Use of boolean is deprecated, please use bool instead.\n" . $herecurr); - } - - if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && - ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { - my $flag = $1; - my $replacement = { - 'EXTRA_AFLAGS' => 'asflags-y', - 'EXTRA_CFLAGS' => 'ccflags-y', - 'EXTRA_CPPFLAGS' => 'cppflags-y', - 'EXTRA_LDFLAGS' => 'ldflags-y', - }; - - WARN("DEPRECATED_VARIABLE", - "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); - } - -# check for DT compatible documentation - if (defined $root && - (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || - ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { - - my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; - - my $dt_path = $root . "/Documentation/devicetree/bindings/"; - my $vp_file = $dt_path . "vendor-prefixes.txt"; - - foreach my $compat (@compats) { - my $compat2 = $compat; - $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/; - my $compat3 = $compat; - $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/; - `grep -Erq "$compat|$compat2|$compat3" $dt_path`; - if ( $? >> 8 ) { - WARN("UNDOCUMENTED_DT_STRING", - "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); - } - - next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; - my $vendor = $1; - `grep -Eq "^$vendor\\b" $vp_file`; - if ( $? >> 8 ) { - WARN("UNDOCUMENTED_DT_STRING", - "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); - } - } - } - -# check we are in a valid source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c|s|S|pl|sh|dtsi|dts)$/); - -# line length limit (with some exclusions) -# -# There are a few types of lines that may extend beyond $max_line_length: -# logging functions like pr_info that end in a string -# lines with a single string -# #defines that are a single string -# -# There are 3 different line length message types: -# LONG_LINE_COMMENT a comment starts before but extends beyond $max_linelength -# LONG_LINE_STRING a string starts before but extends beyond $max_line_length -# LONG_LINE all other lines longer than $max_line_length -# -# if LONG_LINE is ignored, the other 2 types are also ignored -# - - if ($line =~ /^\+/ && $length > $max_line_length) { - my $msg_type = "LONG_LINE"; - - # Check the allowed long line types first - - # logging functions that end in a string that starts - # before $max_line_length - if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = ""; - - # lines with only strings (w/ possible termination) - # #defines with only strings - } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || - $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { - $msg_type = ""; - - # Otherwise set the alternate message types - - # a comment starts before $max_line_length - } elsif ($line =~ /($;[\s$;]*)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = "LONG_LINE_COMMENT" - - # a quoted string starts before $max_line_length - } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && - length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { - $msg_type = "LONG_LINE_STRING" - } - - if ($msg_type ne "" && - (show_type("LONG_LINE") || show_type($msg_type))) { - WARN($msg_type, - "line over $max_line_length characters\n" . $herecurr); - } - } - -# check for adding lines without a newline. - if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { - WARN("MISSING_EOF_NEWLINE", - "adding a line without newline at end of file\n" . $herecurr); - } - -# Blackfin: use hi/lo macros - if ($realfile =~ m@arch/blackfin/.*\.S$@) { - if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("LO_MACRO", - "use the LO() macro, not (... & 0xFFFF)\n" . $herevet); - } - if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("HI_MACRO", - "use the HI() macro, not (... >> 16)\n" . $herevet); - } - } - -# check we are in a valid source file C or perl if not then ignore this hunk - next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); - -# at the beginning of a line any tabs must come first and anything -# more than 8 must use tabs. - if ($rawline =~ /^\+\s* \t\s*\S/ || - $rawline =~ /^\+\s* \s*/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - $rpt_cleaners = 1; - if (ERROR("CODE_INDENT", - "code indent should use tabs where possible\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; - } - } - -# check for space before tabs. - if ($rawline =~ /^\+/ && $rawline =~ / \t/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (WARN("SPACE_BEFORE_TAB", - "please, no space before tabs\n" . $herevet) && - $fix) { - while ($fixed[$fixlinenr] =~ - s/(^\+.*) {8,8}\t/$1\t\t/) {} - while ($fixed[$fixlinenr] =~ - s/(^\+.*) +\t/$1\t/) {} - } - } - -# check for && or || at the start of a line - if ($rawline =~ /^\+\s*(&&|\|\|)/) { - CHK("LOGICAL_CONTINUATIONS", - "Logical continuations should be on the previous line\n" . $hereprev); - } - -# check multi-line statement indentation matches previous line - if ($^V && $^V ge 5.10.0 && - $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { - $prevline =~ /^\+(\t*)(.*)$/; - my $oldindent = $1; - my $rest = $2; - - my $pos = pos_last_openparen($rest); - if ($pos >= 0) { - $line =~ /^(\+| )([ \t]*)/; - my $newindent = $2; - - my $goodtabindent = $oldindent . - "\t" x ($pos / 8) . - " " x ($pos % 8); - my $goodspaceindent = $oldindent . " " x $pos; - - if ($newindent ne $goodtabindent && - $newindent ne $goodspaceindent) { - - if (CHK("PARENTHESIS_ALIGNMENT", - "Alignment should match open parenthesis\n" . $hereprev) && - $fix && $line =~ /^\+/) { - $fixed[$fixlinenr] =~ - s/^\+[ \t]*/\+$goodtabindent/; - } - } - } - } - -# check for space after cast like "(int) foo" or "(struct foo) bar" -# avoid checking a few false positives: -# "sizeof(<type>)" or "__alignof__(<type>)" -# function pointer declarations like "(*foo)(int) = bar;" -# structure definitions like "(struct foo) { 0 };" -# multiline macros that define functions -# known attributes or the __attribute__ keyword - if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && - (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { - if (CHK("SPACING", - "No space is necessary after a cast\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/(\(\s*$Type\s*\))[ \t]+/$1/; - } - } - -# Block comment styles -# Networking with an initial /* - if ($realfile =~ m@^(drivers/net/|net/)@ && - $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && - $rawline =~ /^\+[ \t]*\*/ && - $realline > 2) { - WARN("NETWORKING_BLOCK_COMMENT_STYLE", - "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); - } - -# Block comments use * on subsequent lines - if ($prevline =~ /$;[ \t]*$/ && #ends in comment - $prevrawline =~ /^\+.*?\/\*/ && #starting /* - $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ - $rawline =~ /^\+/ && #line is new - $rawline !~ /^\+[ \t]*\*/) { #no leading * - WARN("BLOCK_COMMENT_STYLE", - "Block comments use * on subsequent lines\n" . $hereprev); - } - -# Block comments use */ on trailing lines - if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ - $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ - $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ - $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ - WARN("BLOCK_COMMENT_STYLE", - "Block comments use a trailing */ on a separate line\n" . $herecurr); - } - -# check for missing blank lines after struct/union declarations -# with exceptions for various attributes and macros - if ($prevline =~ /^[\+ ]};?\s*$/ && - $line =~ /^\+/ && - !($line =~ /^\+\s*$/ || - $line =~ /^\+\s*EXPORT_SYMBOL/ || - $line =~ /^\+\s*MODULE_/i || - $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || - $line =~ /^\+[a-z_]*init/ || - $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || - $line =~ /^\+\s*DECLARE/ || - $line =~ /^\+\s*__setup/)) { - if (CHK("LINE_SPACING", - "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && - $fix) { - fix_insert_line($fixlinenr, "\+"); - } - } - -# check for multiple consecutive blank lines - if ($prevline =~ /^[\+ ]\s*$/ && - $line =~ /^\+\s*$/ && - $last_blank_line != ($linenr - 1)) { - if (CHK("LINE_SPACING", - "Please don't use multiple blank lines\n" . $hereprev) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - - $last_blank_line = $linenr; - } - -# check for missing blank lines after declarations - if ($sline =~ /^\+\s+\S/ && #Not at char 1 - # actual declarations - ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || - # function pointer declarations - $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || - # foo bar; where foo is some local typedef or #define - $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || - # known declaration macros - $prevline =~ /^\+\s+$declaration_macros/) && - # for "else if" which can look like "$Ident $Ident" - !($prevline =~ /^\+\s+$c90_Keywords\b/ || - # other possible extensions of declaration lines - $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || - # not starting a section or a macro "\" extended line - $prevline =~ /(?:\{\s*|\\)$/) && - # looks like a declaration - !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || - # function pointer declarations - $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || - # foo bar; where foo is some local typedef or #define - $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || - # known declaration macros - $sline =~ /^\+\s+$declaration_macros/ || - # start of struct or union or enum - $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || - # start or end of block or continuation of declaration - $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || - # bitfield continuation - $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || - # other possible extensions of declaration lines - $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && - # indentation of previous and current line are the same - (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { - if (WARN("LINE_SPACING", - "Missing a blank line after declarations\n" . $hereprev) && - $fix) { - fix_insert_line($fixlinenr, "\+"); - } - } - -# check for spaces at the beginning of a line. -# Exceptions: -# 1) within comments -# 2) indented preprocessor commands -# 3) hanging labels - if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { - my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - if (WARN("LEADING_SPACE", - "please, no spaces at the start of a line\n" . $herevet) && - $fix) { - $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; - } - } - -# check we are in a valid C source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c)$/); - -# check indentation of any line with a bare else -# (but not if it is a multiple line "if (foo) return bar; else return baz;") -# if the previous line is a break or return and is indented 1 tab more... - if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { - my $tabs = length($1) + 1; - if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || - ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && - defined $lines[$linenr] && - $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { - WARN("UNNECESSARY_ELSE", - "else is not generally useful after a break or return\n" . $hereprev); - } - } - -# check indentation of a line with a break; -# if the previous line is a goto or return and is indented the same # of tabs - if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { - my $tabs = $1; - if ($prevline =~ /^\+$tabs(?:goto|return)\b/) { - WARN("UNNECESSARY_BREAK", - "break is not useful after a goto or return\n" . $hereprev); - } - } - -# discourage the addition of CONFIG_EXPERIMENTAL in #if(def). - if ($line =~ /^\+\s*\#\s*if.*\bCONFIG_EXPERIMENTAL\b/) { - WARN("CONFIG_EXPERIMENTAL", - "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); - } - -# check for RCS/CVS revision markers - if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { - WARN("CVS_KEYWORD", - "CVS style keyword markers, these will _not_ be updated\n". $herecurr); - } - -# Blackfin: don't use __builtin_bfin_[cs]sync - if ($line =~ /__builtin_bfin_csync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("CSYNC", - "use the CSYNC() macro in asm/blackfin.h\n" . $herevet); - } - if ($line =~ /__builtin_bfin_ssync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("SSYNC", - "use the SSYNC() macro in asm/blackfin.h\n" . $herevet); - } - -# check for old HOTPLUG __dev<foo> section markings - if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { - WARN("HOTPLUG_SECTION", - "Using $1 is unnecessary\n" . $herecurr); - } - -# Check for potential 'bare' types - my ($stat, $cond, $line_nr_next, $remain_next, $off_next, - $realline_next); -#print "LINE<$line>\n"; - if ($linenr >= $suppress_statement && - $realcnt && $sline =~ /.\s*\S/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0); - $stat =~ s/\n./\n /g; - $cond =~ s/\n./\n /g; - -#print "linenr<$linenr> <$stat>\n"; - # If this statement has no statement boundaries within - # it there is no point in retrying a statement scan - # until we hit end of it. - my $frag = $stat; $frag =~ s/;+\s*$//; - if ($frag !~ /(?:{|;)/) { -#print "skip<$line_nr_next>\n"; - $suppress_statement = $line_nr_next; - } - - # Find the real next line. - $realline_next = $line_nr_next; - if (defined $realline_next && - (!defined $lines[$realline_next - 1] || - substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { - $realline_next++; - } - - my $s = $stat; - $s =~ s/{.*$//s; - - # Ignore goto labels. - if ($s =~ /$Ident:\*$/s) { - - # Ignore functions being called - } elsif ($s =~ /^.\s*$Ident\s*\(/s) { - - } elsif ($s =~ /^.\s*else\b/s) { - - # declarations always start with types - } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { - my $type = $1; - $type =~ s/\s+/ /g; - possible($type, "A:" . $s); - - # definitions in global scope can only start with types - } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { - possible($1, "B:" . $s); - } - - # any (foo ... *) is a pointer cast, and foo is a type - while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { - possible($1, "C:" . $s); - } - - # Check for any sort of function declaration. - # int foo(something bar, other baz); - # void (*store_gdt)(x86_descr_ptr *); - if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { - my ($name_len) = length($1); - - my $ctx = $s; - substr($ctx, 0, $name_len + 1, ''); - $ctx =~ s/\)[^\)]*$//; - - for my $arg (split(/\s*,\s*/, $ctx)) { - if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { - - possible($1, "D:" . $s); - } - } - } - - } - -# -# Checks which may be anchored in the context. -# - -# Check for switch () and associated case and default -# statements should be at the same indent. - if ($line=~/\bswitch\s*\(.*\)/) { - my $err = ''; - my $sep = ''; - my @ctx = ctx_block_outer($linenr, $realcnt); - shift(@ctx); - for my $ctx (@ctx) { - my ($clen, $cindent) = line_stats($ctx); - if ($ctx =~ /^\+\s*(case\s+|default:)/ && - $indent != $cindent) { - $err .= "$sep$ctx\n"; - $sep = ''; - } else { - $sep = "[...]\n"; - } - } - if ($err ne '') { - ERROR("SWITCH_CASE_INDENT_LEVEL", - "switch and case should be at the same indent\n$hereline$err"); - } - } - -# if/while/etc brace do not go on next line, unless defining a do while loop, -# or if that brace on the next line is for something else - if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { - my $pre_ctx = "$1$2"; - - my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); - - if ($line =~ /^\+\t{6,}/) { - WARN("DEEP_INDENTATION", - "Too many leading tabs - consider code refactoring\n" . $herecurr); - } - - my $ctx_cnt = $realcnt - $#ctx - 1; - my $ctx = join("\n", @ctx); - - my $ctx_ln = $linenr; - my $ctx_skip = $realcnt; - - while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && - defined $lines[$ctx_ln - 1] && - $lines[$ctx_ln - 1] =~ /^-/)) { - ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; - $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); - $ctx_ln++; - } - - #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; - #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; - - if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { - ERROR("OPEN_BRACE", - "that open brace { should be on the previous line\n" . - "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); - } - if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && - $ctx =~ /\)\s*\;\s*$/ && - defined $lines[$ctx_ln - 1]) - { - my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); - if ($nindent > $indent) { - WARN("TRAILING_SEMICOLON", - "trailing semicolon indicates no statements, indent implies otherwise\n" . - "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); - } - } - } - -# Check relative indent for conditionals and blocks. - if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0) - if (!defined $stat); - my ($s, $c) = ($stat, $cond); - - substr($s, 0, length($c), ''); - - # remove inline comments - $s =~ s/$;/ /g; - $c =~ s/$;/ /g; - - # Find out how long the conditional actually is. - my @newlines = ($c =~ /\n/gs); - my $cond_lines = 1 + $#newlines; - - # Make sure we remove the line prefixes as we have - # none on the first line, and are going to readd them - # where necessary. - $s =~ s/\n./\n/gs; - while ($s =~ /\n\s+\\\n/) { - $cond_lines += $s =~ s/\n\s+\\\n/\n/g; - } - - # We want to check the first line inside the block - # starting at the end of the conditional, so remove: - # 1) any blank line termination - # 2) any opening brace { on end of the line - # 3) any do (...) { - my $continuation = 0; - my $check = 0; - $s =~ s/^.*\bdo\b//; - $s =~ s/^\s*{//; - if ($s =~ s/^\s*\\//) { - $continuation = 1; - } - if ($s =~ s/^\s*?\n//) { - $check = 1; - $cond_lines++; - } - - # Also ignore a loop construct at the end of a - # preprocessor statement. - if (($prevline =~ /^.\s*#\s*define\s/ || - $prevline =~ /\\\s*$/) && $continuation == 0) { - $check = 0; - } - - my $cond_ptr = -1; - $continuation = 0; - while ($cond_ptr != $cond_lines) { - $cond_ptr = $cond_lines; - - # If we see an #else/#elif then the code - # is not linear. - if ($s =~ /^\s*\#\s*(?:else|elif)/) { - $check = 0; - } - - # Ignore: - # 1) blank lines, they should be at 0, - # 2) preprocessor lines, and - # 3) labels. - if ($continuation || - $s =~ /^\s*?\n/ || - $s =~ /^\s*#\s*?/ || - $s =~ /^\s*$Ident\s*:/) { - $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; - if ($s =~ s/^.*?\n//) { - $cond_lines++; - } - } - } - - my (undef, $sindent) = line_stats("+" . $s); - my $stat_real = raw_line($linenr, $cond_lines); - - # Check if either of these lines are modified, else - # this is not this patch's fault. - if (!defined($stat_real) || - $stat !~ /^\+/ && $stat_real !~ /^\+/) { - $check = 0; - } - if (defined($stat_real) && $cond_lines > 1) { - $stat_real = "[...]\n$stat_real"; - } - - #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; - - if ($check && $s ne '' && - (($sindent % 8) != 0 || - ($sindent < $indent) || - ($sindent > $indent + 8))) { - WARN("SUSPECT_CODE_INDENT", - "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); - } - } - - # Track the 'values' across context and added lines. - my $opline = $line; $opline =~ s/^./ /; - my ($curr_values, $curr_vars) = - annotate_values($opline . "\n", $prev_values); - $curr_values = $prev_values . $curr_values; - if ($dbg_values) { - my $outline = $opline; $outline =~ s/\t/ /g; - print "$linenr > .$outline\n"; - print "$linenr > $curr_values\n"; - print "$linenr > $curr_vars\n"; - } - $prev_values = substr($curr_values, -1); - -#ignore lines not being added - next if ($line =~ /^[^\+]/); - -# check for declarations of signed or unsigned without int - while ($line =~ m{($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { - my $type = $1; - my $var = $2; - $var = "" if (!defined $var); - if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { - my $sign = $1; - my $pointer = $2; - - $pointer = "" if (!defined $pointer); - - if (WARN("UNSPECIFIED_INT", - "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && - $fix) { - my $decl = trim($sign) . " int "; - my $comp_pointer = $pointer; - $comp_pointer =~ s/\s//g; - $decl .= $comp_pointer; - $decl = rtrim($decl) if ($var eq ""); - $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; - } - } - } - -# TEST: allow direct testing of the type matcher. - if ($dbg_type) { - if ($line =~ /^.\s*$Declare\s*$/) { - ERROR("TEST_TYPE", - "TEST: is type\n" . $herecurr); - } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { - ERROR("TEST_NOT_TYPE", - "TEST: is not type ($1 is)\n". $herecurr); - } - next; - } -# TEST: allow direct testing of the attribute matcher. - if ($dbg_attr) { - if ($line =~ /^.\s*$Modifier\s*$/) { - ERROR("TEST_ATTR", - "TEST: is attr\n" . $herecurr); - } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { - ERROR("TEST_NOT_ATTR", - "TEST: is not attr ($1 is)\n". $herecurr); - } - next; - } - -# check for initialisation to aggregates open brace on the next line - if ($line =~ /^.\s*{/ && - $prevline =~ /(?:^|[^=])=\s*$/) { - if (ERROR("OPEN_BRACE", - "that open brace { should be on the previous line\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/\s*=\s*$/ = {/; - fix_insert_line($fixlinenr, $fixedline); - $fixedline = $line; - $fixedline =~ s/^(.\s*){\s*/$1/; - fix_insert_line($fixlinenr, $fixedline); - } - } - -# -# Checks which are anchored on the added line. -# - -# check for malformed paths in #include statements (uses RAW line) - if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { - my $path = $1; - if ($path =~ m{//}) { - ERROR("MALFORMED_INCLUDE", - "malformed #include filename\n" . $herecurr); - } - if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { - ERROR("UAPI_INCLUDE", - "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); - } - } - -# no C99 // comments - if ($line =~ m{//}) { - if (ERROR("C99_COMMENTS", - "do not use C99 // comments\n" . $herecurr) && - $fix) { - my $line = $fixed[$fixlinenr]; - if ($line =~ /\/\/(.*)$/) { - my $comment = trim($1); - $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; - } - } - } - # Remove C99 comments. - $line =~ s@//.*@@; - $opline =~ s@//.*@@; - -# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider -# the whole statement. -#print "APW <$lines[$realline_next - 1]>\n"; - if (defined $realline_next && - exists $lines[$realline_next - 1] && - !defined $suppress_export{$realline_next} && - ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { - # Handle definitions which produce identifiers with - # a prefix: - # XXX(foo); - # EXPORT_SYMBOL(something_foo); - my $name = $1; - if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && - $name =~ /^${Ident}_$2/) { -#print "FOO C name<$name>\n"; - $suppress_export{$realline_next} = 1; - - } elsif ($stat !~ /(?: - \n.}\s*$| - ^.DEFINE_$Ident\(\Q$name\E\)| - ^.DECLARE_$Ident\(\Q$name\E\)| - ^.LIST_HEAD\(\Q$name\E\)| - ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| - \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() - )/x) { -#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; - $suppress_export{$realline_next} = 2; - } else { - $suppress_export{$realline_next} = 1; - } - } - if (!defined $suppress_export{$linenr} && - $prevline =~ /^.\s*$/ && - ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { -#print "FOO B <$lines[$linenr - 1]>\n"; - $suppress_export{$linenr} = 2; - } - if (defined $suppress_export{$linenr} && - $suppress_export{$linenr} == 2) { - WARN("EXPORT_SYMBOL", - "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); - } - -# check for global initialisers. - if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) { - if (ERROR("GLOBAL_INITIALISERS", - "do not initialise globals to $1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; - } - } -# check for static initialisers. - if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { - if (ERROR("INITIALISED_STATIC", - "do not initialise statics to $1\n" . - $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; - } - } - -# check for misordered declarations of char/short/int/long with signed/unsigned - while ($sline =~ m{(\b$TypeMisordered\b)}g) { - my $tmp = trim($1); - WARN("MISORDERED_TYPE", - "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); - } - -# check for static const char * arrays. - if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "static const char * array should probably be static const char * const\n" . - $herecurr); - } - -# check for static char foo[] = "bar" declarations. - if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "static char array declaration should probably be static const char\n" . - $herecurr); - } - -# check for const <foo> const where <foo> is not a pointer or array type - if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { - my $found = $1; - if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { - WARN("CONST_CONST", - "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); - } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { - WARN("CONST_CONST", - "'const $found const' should probably be 'const $found'\n" . $herecurr); - } - } - -# check for non-global char *foo[] = {"bar", ...} declarations. - if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { - WARN("STATIC_CONST_CHAR_ARRAY", - "char * array declaration might be better as static const\n" . - $herecurr); - } - -# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) - if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { - my $array = $1; - if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { - my $array_div = $1; - if (WARN("ARRAY_SIZE", - "Prefer ARRAY_SIZE($array)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; - } - } - } - -# check for function declarations without arguments like "int foo()" - if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { - if (ERROR("FUNCTION_WITHOUT_ARGS", - "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; - } - } - -# check for uses of DEFINE_PCI_DEVICE_TABLE - if ($line =~ /\bDEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=/) { - if (WARN("DEFINE_PCI_DEVICE_TABLE", - "Prefer struct pci_device_id over deprecated DEFINE_PCI_DEVICE_TABLE\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b(?:static\s+|)DEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=\s*/static const struct pci_device_id $1\[\] = /; - } - } - -# check for new typedefs, only function parameters and sparse annotations -# make sense. - if ($line =~ /\btypedef\s/ && - $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && - $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && - $line !~ /\b$typeTypedefs\b/ && - $line !~ /\b__bitwise(?:__|)\b/) { - WARN("NEW_TYPEDEFS", - "do not add new typedefs\n" . $herecurr); - } - -# * goes on variable not on type - # (char*[ const]) - while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { - #print "AA<$1>\n"; - my ($ident, $from, $to) = ($1, $2, $2); - - # Should start with a space. - $to =~ s/^(\S)/ $1/; - # Should not end with a space. - $to =~ s/\s+$//; - # '*'s should not have spaces between. - while ($to =~ s/\*\s+\*/\*\*/) { - } - -## print "1: from<$from> to<$to> ident<$ident>\n"; - if ($from ne $to) { - if (ERROR("POINTER_LOCATION", - "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && - $fix) { - my $sub_from = $ident; - my $sub_to = $ident; - $sub_to =~ s/\Q$from\E/$to/; - $fixed[$fixlinenr] =~ - s@\Q$sub_from\E@$sub_to@; - } - } - } - while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { - #print "BB<$1>\n"; - my ($match, $from, $to, $ident) = ($1, $2, $2, $3); - - # Should start with a space. - $to =~ s/^(\S)/ $1/; - # Should not end with a space. - $to =~ s/\s+$//; - # '*'s should not have spaces between. - while ($to =~ s/\*\s+\*/\*\*/) { - } - # Modifiers should have spaces. - $to =~ s/(\b$Modifier$)/$1 /; - -## print "2: from<$from> to<$to> ident<$ident>\n"; - if ($from ne $to && $ident !~ /^$Modifier$/) { - if (ERROR("POINTER_LOCATION", - "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && - $fix) { - - my $sub_from = $match; - my $sub_to = $match; - $sub_to =~ s/\Q$from\E/$to/; - $fixed[$fixlinenr] =~ - s@\Q$sub_from\E@$sub_to@; - } - } - } - -# avoid BUG() or BUG_ON() - if ($line =~ /\b(?:BUG|BUG_ON)\b/) { - my $msg_type = \&WARN; - $msg_type = \&CHK if ($file); - &{$msg_type}("AVOID_BUG", - "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); - } - -# avoid LINUX_VERSION_CODE - if ($line =~ /\bLINUX_VERSION_CODE\b/) { - WARN("LINUX_VERSION_CODE", - "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); - } - -# check for uses of printk_ratelimit - if ($line =~ /\bprintk_ratelimit\s*\(/) { - WARN("PRINTK_RATELIMITED", - "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); - } - -# printk should use KERN_* levels. Note that follow on printk's on the -# same line do not need a level, so we use the current block context -# to try and find and validate the current printk. In summary the current -# printk includes all preceding printk's which have no newline on the end. -# we assume the first bad printk is the one to report. - if ($line =~ /\bprintk\((?!KERN_)\s*"/) { - my $ok = 0; - for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { - #print "CHECK<$lines[$ln - 1]\n"; - # we have a preceding printk if it ends - # with "\n" ignore it, else it is to blame - if ($lines[$ln - 1] =~ m{\bprintk\(}) { - if ($rawlines[$ln - 1] !~ m{\\n"}) { - $ok = 1; - } - last; - } - } - if ($ok == 0) { - WARN("PRINTK_WITHOUT_KERN_LEVEL", - "printk() should include KERN_ facility level\n" . $herecurr); - } - } - - if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { - my $orig = $1; - my $level = lc($orig); - $level = "warn" if ($level eq "warning"); - my $level2 = $level; - $level2 = "dbg" if ($level eq "debug"); - WARN("PREFER_PR_LEVEL", - "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); - } - - if ($line =~ /\bpr_warning\s*\(/) { - if (WARN("PREFER_PR_LEVEL", - "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\bpr_warning\b/pr_warn/; - } - } - - if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { - my $orig = $1; - my $level = lc($orig); - $level = "warn" if ($level eq "warning"); - $level = "dbg" if ($level eq "debug"); - WARN("PREFER_DEV_LEVEL", - "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); - } - -# ENOSYS means "bad syscall nr" and nothing else. This will have a small -# number of false positives, but assembly files are not checked, so at -# least the arch entry code will not trigger this warning. - if ($line =~ /\bENOSYS\b/) { - WARN("ENOSYS", - "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); - } - -# function brace can't be on same line, except for #defines of do while, -# or if closed on same line - if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and - !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { - if (ERROR("OPEN_BRACE", - "open brace '{' following function declarations go on the next line\n" . $herecurr) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - my $fixed_line = $rawline; - $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/; - my $line1 = $1; - my $line2 = $2; - fix_insert_line($fixlinenr, ltrim($line1)); - fix_insert_line($fixlinenr, "\+{"); - if ($line2 !~ /^\s*$/) { - fix_insert_line($fixlinenr, "\+\t" . trim($line2)); - } - } - } - -# open braces for enum, union and struct go on the same line. - if ($line =~ /^.\s*{/ && - $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { - if (ERROR("OPEN_BRACE", - "open brace '{' following $1 go on the same line\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = rtrim($prevrawline) . " {"; - fix_insert_line($fixlinenr, $fixedline); - $fixedline = $rawline; - $fixedline =~ s/^(.\s*){\s*/$1\t/; - if ($fixedline !~ /^\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - } - } - -# missing space after union, struct or enum definition - if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { - if (WARN("SPACING", - "missing space after $1 definition\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; - } - } - -# Function pointer declarations -# check spacing between type, funcptr, and args -# canonical declaration is "type (*funcptr)(args...)" - if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { - my $declare = $1; - my $pre_pointer_space = $2; - my $post_pointer_space = $3; - my $funcname = $4; - my $post_funcname_space = $5; - my $pre_args_space = $6; - -# the $Declare variable will capture all spaces after the type -# so check it for a missing trailing missing space but pointer return types -# don't need a space so don't warn for those. - my $post_declare_space = ""; - if ($declare =~ /(\s+)$/) { - $post_declare_space = $1; - $declare = rtrim($declare); - } - if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { - WARN("SPACING", - "missing space after return type\n" . $herecurr); - $post_declare_space = " "; - } - -# unnecessary space "type (*funcptr)(args...)" -# This test is not currently implemented because these declarations are -# equivalent to -# int foo(int bar, ...) -# and this is form shouldn't/doesn't generate a checkpatch warning. -# -# elsif ($declare =~ /\s{2,}$/) { -# WARN("SPACING", -# "Multiple spaces after return type\n" . $herecurr); -# } - -# unnecessary space "type ( *funcptr)(args...)" - if (defined $pre_pointer_space && - $pre_pointer_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space after function pointer open parenthesis\n" . $herecurr); - } - -# unnecessary space "type (* funcptr)(args...)" - if (defined $post_pointer_space && - $post_pointer_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space before function pointer name\n" . $herecurr); - } - -# unnecessary space "type (*funcptr )(args...)" - if (defined $post_funcname_space && - $post_funcname_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space after function pointer name\n" . $herecurr); - } - -# unnecessary space "type (*funcptr) (args...)" - if (defined $pre_args_space && - $pre_args_space =~ /^\s/) { - WARN("SPACING", - "Unnecessary space before function pointer arguments\n" . $herecurr); - } - - if (show_type("SPACING") && $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; - } - } - -# check for spacing round square brackets; allowed: -# 1. with a type on the left -- int [] a; -# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, -# 3. inside a curly brace -- = { [0...10] = 5 } - while ($line =~ /(.*?\s)\[/g) { - my ($where, $prefix) = ($-[1], $1); - if ($prefix !~ /$Type\s+$/ && - ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /[{,]\s+$/) { - if (ERROR("BRACKET_SPACE", - "space prohibited before open square bracket '['\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(\+.*?)\s+\[/$1\[/; - } - } - } - -# check for spaces between functions and their parentheses. - while ($line =~ /($Ident)\s+\(/g) { - my $name = $1; - my $ctx_before = substr($line, 0, $-[1]); - my $ctx = "$ctx_before$name"; - - # Ignore those directives where spaces _are_ permitted. - if ($name =~ /^(?: - if|for|while|switch|return|case| - volatile|__volatile__| - __attribute__|format|__extension__| - asm|__asm__)$/x) - { - # cpp #define statements have non-optional spaces, ie - # if there is a space between the name and the open - # parenthesis it is simply not a parameter group. - } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { - - # cpp #elif statement condition may start with a ( - } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { - - # If this whole things ends with a type its most - # likely a typedef for a function. - } elsif ($ctx =~ /$Type$/) { - - } else { - if (WARN("SPACING", - "space prohibited between function name and open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\b$name\s+\(/$name\(/; - } - } - } - -# Check operator spacing. - if (!($line=~/\#\s*include/)) { - my $fixed_line = ""; - my $line_fixed = 0; - - my $ops = qr{ - <<=|>>=|<=|>=|==|!=| - \+=|-=|\*=|\/=|%=|\^=|\|=|&=| - =>|->|<<|>>|<|>|=|!|~| - &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| - \?:|\?|: - }x; - my @elements = split(/($ops|;)/, $opline); - -## print("element count: <" . $#elements . ">\n"); -## foreach my $el (@elements) { -## print("el: <$el>\n"); -## } - - my @fix_elements = (); - my $off = 0; - - foreach my $el (@elements) { - push(@fix_elements, substr($rawline, $off, length($el))); - $off += length($el); - } - - $off = 0; - - my $blank = copy_spacing($opline); - my $last_after = -1; - - for (my $n = 0; $n < $#elements; $n += 2) { - - my $good = $fix_elements[$n] . $fix_elements[$n + 1]; - -## print("n: <$n> good: <$good>\n"); - - $off += length($elements[$n]); - - # Pick up the preceding and succeeding characters. - my $ca = substr($opline, 0, $off); - my $cc = ''; - if (length($opline) >= ($off + length($elements[$n + 1]))) { - $cc = substr($opline, $off + length($elements[$n + 1])); - } - my $cb = "$ca$;$cc"; - - my $a = ''; - $a = 'V' if ($elements[$n] ne ''); - $a = 'W' if ($elements[$n] =~ /\s$/); - $a = 'C' if ($elements[$n] =~ /$;$/); - $a = 'B' if ($elements[$n] =~ /(\[|\()$/); - $a = 'O' if ($elements[$n] eq ''); - $a = 'E' if ($ca =~ /^\s*$/); - - my $op = $elements[$n + 1]; - - my $c = ''; - if (defined $elements[$n + 2]) { - $c = 'V' if ($elements[$n + 2] ne ''); - $c = 'W' if ($elements[$n + 2] =~ /^\s/); - $c = 'C' if ($elements[$n + 2] =~ /^$;/); - $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); - $c = 'O' if ($elements[$n + 2] eq ''); - $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); - } else { - $c = 'E'; - } - - my $ctx = "${a}x${c}"; - - my $at = "(ctx:$ctx)"; - - my $ptr = substr($blank, 0, $off) . "^"; - my $hereptr = "$hereline$ptr\n"; - - # Pull out the value of this operator. - my $op_type = substr($curr_values, $off + 1, 1); - - # Get the full operator variant. - my $opv = $op . substr($curr_vars, $off, 1); - - # Ignore operators passed as parameters. - if ($op_type ne 'V' && - $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { - -# # Ignore comments -# } elsif ($op =~ /^$;+$/) { - - # ; should have either the end of line or a space or \ after it - } elsif ($op eq ';') { - if ($ctx !~ /.x[WEBC]/ && - $cc !~ /^\\/ && $cc !~ /^;/) { - if (ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; - $line_fixed = 1; - } - } - - # // is a comment - } elsif ($op eq '//') { - - # : when part of a bitfield - } elsif ($opv eq ':B') { - # skip the bitfield test for now - - # No spaces for: - # -> - } elsif ($op eq '->') { - if ($ctx =~ /Wx.|.xW/) { - if (ERROR("SPACING", - "spaces prohibited around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # , must not have a space before and must have a space on the right. - } elsif ($op eq ',') { - my $rtrim_before = 0; - my $space_after = 0; - if ($ctx =~ /Wx./) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $line_fixed = 1; - $rtrim_before = 1; - } - } - if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { - if (ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr)) { - $line_fixed = 1; - $last_after = $n; - $space_after = 1; - } - } - if ($rtrim_before || $space_after) { - if ($rtrim_before) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - } else { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); - } - if ($space_after) { - $good .= " "; - } - } - - # '*' as part of a type definition -- reported already. - } elsif ($opv eq '*_') { - #warn "'*' is part of type\n"; - - # unary operators should have a space before and - # none after. May be left adjacent to another - # unary operator, or a cast - } elsif ($op eq '!' || $op eq '~' || - $opv eq '*U' || $opv eq '-U' || - $opv eq '&U' || $opv eq '&&U') { - if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { - if (ERROR("SPACING", - "space required before that '$op' $at\n" . $hereptr)) { - if ($n != $last_after + 2) { - $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - } - if ($op eq '*' && $cc =~/\s*$Modifier\b/) { - # A unary '*' may be const - - } elsif ($ctx =~ /.xW/) { - if (ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # unary ++ and unary -- are allowed no space on one side. - } elsif ($op eq '++' or $op eq '--') { - if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { - if (ERROR("SPACING", - "space required one side of that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; - $line_fixed = 1; - } - } - if ($ctx =~ /Wx[BE]/ || - ($ctx =~ /Wx./ && $cc =~ /^;/)) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - if ($ctx =~ /ExW/) { - if (ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr)) { - $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # << and >> may either have or not have spaces both sides - } elsif ($op eq '<<' or $op eq '>>' or - $op eq '&' or $op eq '^' or $op eq '|' or - $op eq '+' or $op eq '-' or - $op eq '*' or $op eq '/' or - $op eq '%') - { - if ($check) { - if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { - if (CHK("SPACING", - "spaces preferred around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - $fix_elements[$n + 2] =~ s/^\s+//; - $line_fixed = 1; - } - } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { - if (CHK("SPACING", - "space preferred before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { - if (ERROR("SPACING", - "need consistent spacing around '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - - # A colon needs no spaces before when it is - # terminating a case value or a label. - } elsif ($opv eq ':C' || $opv eq ':L') { - if ($ctx =~ /Wx./) { - if (ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); - $line_fixed = 1; - } - } - - # All the others need spaces both sides. - } elsif ($ctx !~ /[EWC]x[CWE]/) { - my $ok = 0; - - # Ignore email addresses <foo@bar> - if (($op eq '<' && - $cc =~ /^\S+\@\S+>/) || - ($op eq '>' && - $ca =~ /<\S+\@\S+$/)) - { - $ok = 1; - } - - # for asm volatile statements - # ignore a colon with another - # colon immediately before or after - if (($op eq ':') && - ($ca =~ /:$/ || $cc =~ /^:/)) { - $ok = 1; - } - - # messages are ERROR, but ?: are CHK - if ($ok == 0) { - my $msg_type = \&ERROR; - $msg_type = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); - - if (&{$msg_type}("SPACING", - "spaces required around that '$op' $at\n" . $hereptr)) { - $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; - if (defined $fix_elements[$n + 2]) { - $fix_elements[$n + 2] =~ s/^\s+//; - } - $line_fixed = 1; - } - } - } - $off += length($elements[$n + 1]); - -## print("n: <$n> GOOD: <$good>\n"); - - $fixed_line = $fixed_line . $good; - } - - if (($#elements % 2) == 0) { - $fixed_line = $fixed_line . $fix_elements[$#elements]; - } - - if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { - $fixed[$fixlinenr] = $fixed_line; - } - - - } - -# check for whitespace before a non-naked semicolon - if ($line =~ /^\+.*\S\s+;\s*$/) { - if (WARN("SPACING", - "space prohibited before semicolon\n" . $herecurr) && - $fix) { - 1 while $fixed[$fixlinenr] =~ - s/^(\+.*\S)\s+;/$1;/; - } - } - -# check for multiple assignments - if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { - CHK("MULTIPLE_ASSIGNMENTS", - "multiple assignments should be avoided\n" . $herecurr); - } - -## # check for multiple declarations, allowing for a function declaration -## # continuation. -## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && -## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { -## -## # Remove any bracketed sections to ensure we do not -## # falsly report the parameters of functions. -## my $ln = $line; -## while ($ln =~ s/\([^\(\)]*\)//g) { -## } -## if ($ln =~ /,/) { -## WARN("MULTIPLE_DECLARATION", -## "declaring multiple variables together should be avoided\n" . $herecurr); -## } -## } - -#need space before brace following if, while, etc - if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || - $line =~ /do\{/) { - if (ERROR("SPACING", - "space required before the open brace '{'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\))){/$1 {/; - } - } - -## # check for blank lines before declarations -## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && -## $prevrawline =~ /^.\s*$/) { -## WARN("SPACING", -## "No blank lines before declarations\n" . $hereprev); -## } -## - -# closing brace should have a space following it when it has anything -# on the line - if ($line =~ /}(?!(?:,|;|\)))\S/) { - if (ERROR("SPACING", - "space required after that close brace '}'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/}((?!(?:,|;|\)))\S)/} $1/; - } - } - -# check spacing on square brackets - if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { - if (ERROR("SPACING", - "space prohibited after that open square bracket '['\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\[\s+/\[/; - } - } - if ($line =~ /\s\]/) { - if (ERROR("SPACING", - "space prohibited before that close square bracket ']'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\s+\]/\]/; - } - } - -# check spacing on parentheses - if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && - $line !~ /for\s*\(\s+;/) { - if (ERROR("SPACING", - "space prohibited after that open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\(\s+/\(/; - } - } - if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && - $line !~ /for\s*\(.*;\s+\)/ && - $line !~ /:\s+\)/) { - if (ERROR("SPACING", - "space prohibited before that close parenthesis ')'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\s+\)/\)/; - } - } - -# check unnecessary parentheses around addressof/dereference single $Lvals -# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar - - while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { - my $var = $1; - if (CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around $var\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; - } - } - -# check for unnecessary parentheses around function pointer uses -# ie: (foo->bar)(); should be foo->bar(); -# but not "if (foo->bar) (" to avoid some false positives - if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { - my $var = $2; - if (CHK("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses around function pointer $var\n" . $herecurr) && - $fix) { - my $var2 = deparenthesize($var); - $var2 =~ s/\s//g; - $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; - } - } - -#goto labels aren't indented, allow a single space however - if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and - !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { - if (WARN("INDENTED_LABEL", - "labels should not be indented\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.)\s+/$1/; - } - } - -# return is not a function - if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { - my $spacing = $1; - if ($^V && $^V ge 5.10.0 && - $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { - my $value = $1; - $value = deparenthesize($value); - if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { - ERROR("RETURN_PARENTHESES", - "return is not a function, parentheses are not required\n" . $herecurr); - } - } elsif ($spacing !~ /\s+/) { - ERROR("SPACING", - "space required before the open parenthesis '('\n" . $herecurr); - } - } - -# unnecessary return in a void function -# at end-of-function, with the previous line a single leading tab, then return; -# and the line before that not a goto label target like "out:" - if ($sline =~ /^[ \+]}\s*$/ && - $prevline =~ /^\+\treturn\s*;\s*$/ && - $linenr >= 3 && - $lines[$linenr - 3] =~ /^[ +]/ && - $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { - WARN("RETURN_VOID", - "void function return statements are not generally useful\n" . $hereprev); - } - -# if statements using unnecessary parentheses - ie: if ((foo == bar)) - if ($^V && $^V ge 5.10.0 && - $line =~ /\bif\s*((?:\(\s*){2,})/) { - my $openparens = $1; - my $count = $openparens =~ tr@\(@\(@; - my $msg = ""; - if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { - my $comp = $4; #Not $1 because of $LvalOrFunc - $msg = " - maybe == should be = ?" if ($comp eq "=="); - WARN("UNNECESSARY_PARENTHESES", - "Unnecessary parentheses$msg\n" . $herecurr); - } - } - -# comparisons with a constant or upper case identifier on the left -# avoid cases like "foo + BAR < baz" -# only fix matches surrounded by parentheses to avoid incorrect -# conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" - if ($^V && $^V ge 5.10.0 && - $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { - my $lead = $1; - my $const = $2; - my $comp = $3; - my $to = $4; - my $newcomp = $comp; - if ($lead !~ /$Operators\s*$/ && - $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && - WARN("CONSTANT_COMPARISON", - "Comparisons should place the constant on the right side of the test\n" . $herecurr) && - $fix) { - if ($comp eq "<") { - $newcomp = ">"; - } elsif ($comp eq "<=") { - $newcomp = ">="; - } elsif ($comp eq ">") { - $newcomp = "<"; - } elsif ($comp eq ">=") { - $newcomp = "<="; - } - $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; - } - } - -# Return of what appears to be an errno should normally be negative - if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { - my $name = $1; - if ($name ne 'EOF' && $name ne 'ERROR') { - WARN("USE_NEGATIVE_ERRNO", - "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); - } - } - -# Need a space before open parenthesis after if, while etc - if ($line =~ /\b(if|while|for|switch)\(/) { - if (ERROR("SPACING", - "space required before the open parenthesis '('\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\b(if|while|for|switch)\(/$1 \(/; - } - } - -# Check for illegal assignment in if conditional -- and check for trailing -# statements after the conditional. - if ($line =~ /do\s*(?!{)/) { - ($stat, $cond, $line_nr_next, $remain_next, $off_next) = - ctx_statement_block($linenr, $realcnt, 0) - if (!defined $stat); - my ($stat_next) = ctx_statement_block($line_nr_next, - $remain_next, $off_next); - $stat_next =~ s/\n./\n /g; - ##print "stat<$stat> stat_next<$stat_next>\n"; - - if ($stat_next =~ /^\s*while\b/) { - # If the statement carries leading newlines, - # then count those as offsets. - my ($whitespace) = - ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); - my $offset = - statement_rawlines($whitespace) - 1; - - $suppress_whiletrailers{$line_nr_next + - $offset} = 1; - } - } - if (!defined $suppress_whiletrailers{$linenr} && - defined($stat) && defined($cond) && - $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { - my ($s, $c) = ($stat, $cond); - - if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { - ERROR("ASSIGN_IN_IF", - "do not use assignment in if condition\n" . $herecurr); - } - - # Find out what is on the end of the line after the - # conditional. - substr($s, 0, length($c), ''); - $s =~ s/\n.*//g; - $s =~ s/$;//g; # Remove any comments - if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && - $c !~ /}\s*while\s*/) - { - # Find out how long the conditional actually is. - my @newlines = ($c =~ /\n/gs); - my $cond_lines = 1 + $#newlines; - my $stat_real = ''; - - $stat_real = raw_line($linenr, $cond_lines) - . "\n" if ($cond_lines); - if (defined($stat_real) && $cond_lines > 1) { - $stat_real = "[...]\n$stat_real"; - } - - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr . $stat_real); - } - } - -# Check for bitwise tests written as boolean - if ($line =~ / - (?: - (?:\[|\(|\&\&|\|\|) - \s*0[xX][0-9]+\s* - (?:\&\&|\|\|) - | - (?:\&\&|\|\|) - \s*0[xX][0-9]+\s* - (?:\&\&|\|\||\)|\]) - )/x) - { - WARN("HEXADECIMAL_BOOLEAN_TEST", - "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); - } - -# if and else should not have general statements after it - if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { - my $s = $1; - $s =~ s/$;//g; # Remove any comments - if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr); - } - } -# if should not continue a brace - if ($line =~ /}\s*if\b/) { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line (or did you mean 'else if'?)\n" . - $herecurr); - } -# case and default should not have general statements after them - if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && - $line !~ /\G(?: - (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| - \s*return\s+ - )/xg) - { - ERROR("TRAILING_STATEMENTS", - "trailing statements should be on next line\n" . $herecurr); - } - - # Check for }<nl>else {, these must be at the same - # indent level to be relevant to each other. - if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && - $previndent == $indent) { - if (ERROR("ELSE_AFTER_BRACE", - "else should follow close brace '}'\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/}\s*$//; - if ($fixedline !~ /^\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - $fixedline = $rawline; - $fixedline =~ s/^(.\s*)else/$1} else/; - fix_insert_line($fixlinenr, $fixedline); - } - } - - if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && - $previndent == $indent) { - my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); - - # Find out what is on the end of the line after the - # conditional. - substr($s, 0, length($c), ''); - $s =~ s/\n.*//g; - - if ($s =~ /^\s*;/) { - if (ERROR("WHILE_AFTER_BRACE", - "while should follow close brace '}'\n" . $hereprev) && - $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - my $trailing = $rawline; - $trailing =~ s/^\+//; - $trailing = trim($trailing); - $fixedline =~ s/}\s*$/} $trailing/; - fix_insert_line($fixlinenr, $fixedline); - } - } - } - -#Specific variable tests - while ($line =~ m{($Constant|$Lval)}g) { - my $var = $1; - -#gcc binary extension - if ($var =~ /^$Binary$/) { - if (WARN("GCC_BINARY_CONSTANT", - "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && - $fix) { - my $hexval = sprintf("0x%x", oct($var)); - $fixed[$fixlinenr] =~ - s/\b$var\b/$hexval/; - } - } - -#CamelCase - if ($var !~ /^$Constant$/ && - $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && -#Ignore Page<foo> variants - $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && -#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) - $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && -#Ignore some three character SI units explicitly, like MiB and KHz - $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { - while ($var =~ m{($Ident)}g) { - my $word = $1; - next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); - if ($check) { - seed_camelcase_includes(); - if (!$file && !$camelcase_file_seeded) { - seed_camelcase_file($realfile); - $camelcase_file_seeded = 1; - } - } - if (!defined $camelcase{$word}) { - $camelcase{$word} = 1; - CHK("CAMELCASE", - "Avoid CamelCase: <$word>\n" . $herecurr); - } - } - } - } - -#no spaces allowed after \ in define - if ($line =~ /\#\s*define.*\\\s+$/) { - if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", - "Whitespace after \\ makes next lines useless\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+$//; - } - } - -# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes -# itself <asm/foo.h> (uses RAW line) - if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { - my $file = "$1.h"; - my $checkfile = "include/linux/$file"; - if (-f "$root/$checkfile" && - $realfile ne $checkfile && - $1 !~ /$allowed_asm_includes/) - { - my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; - if ($asminclude > 0) { - if ($realfile =~ m{^arch/}) { - CHK("ARCH_INCLUDE_LINUX", - "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } else { - WARN("INCLUDE_LINUX", - "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } - } - } - } - -# multi-statement macros should be enclosed in a do while loop, grab the -# first statement and ensure its the whole macro if its not enclosed -# in a known good container - if ($realfile !~ m@/vmlinux.lds.h$@ && - $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { - my $ln = $linenr; - my $cnt = $realcnt; - my ($off, $dstat, $dcond, $rest); - my $ctx = ''; - my $has_flow_statement = 0; - my $has_arg_concat = 0; - ($dstat, $dcond, $ln, $cnt, $off) = - ctx_statement_block($linenr, $realcnt, 0); - $ctx = $dstat; - #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; - #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; - - $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); - $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); - - $dstat =~ s/^.\s*\#\s*define\s+$Ident(?:\([^\)]*\))?\s*//; - $dstat =~ s/$;//g; - $dstat =~ s/\\\n.//g; - $dstat =~ s/^\s*//s; - $dstat =~ s/\s*$//s; - - # Flatten any parentheses and braces - while ($dstat =~ s/\([^\(\)]*\)/1/ || - $dstat =~ s/\{[^\{\}]*\}/1/ || - $dstat =~ s/.\[[^\[\]]*\]/1/) - { - } - - # Flatten any obvious string concatentation. - while ($dstat =~ s/($String)\s*$Ident/$1/ || - $dstat =~ s/$Ident\s*($String)/$1/) - { - } - - # Make asm volatile uses seem like a generic function - $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; - - my $exceptions = qr{ - $Declare| - module_param_named| - MODULE_PARM_DESC| - DECLARE_PER_CPU| - DEFINE_PER_CPU| - __typeof__\(| - union| - struct| - \.$Ident\s*=\s*| - ^\"|\"$| - ^\[ - }x; - #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; - if ($dstat ne '' && - $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), - $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); - $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz - $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants - $dstat !~ /$exceptions/ && - $dstat !~ /^\.$Ident\s*=/ && # .foo = - $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo - $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) - $dstat !~ /^for\s*$Constant$/ && # for (...) - $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() - $dstat !~ /^do\s*{/ && # do {... - $dstat !~ /^\(\{/ && # ({... - $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) - { - $ctx =~ s/\n*$//; - my $herectx = $here . "\n"; - my $cnt = statement_rawlines($ctx); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - if ($dstat =~ /;/) { - ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", - "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); - } else { - ERROR("COMPLEX_MACRO", - "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); - } - } - -# check for macros with flow control, but without ## concatenation -# ## concatenation is commonly a macro that defines a function so ignore those - if ($has_flow_statement && !$has_arg_concat) { - my $herectx = $here . "\n"; - my $cnt = statement_rawlines($ctx); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - WARN("MACRO_WITH_FLOW_CONTROL", - "Macros with flow control statements should be avoided\n" . "$herectx"); - } - -# check for line continuations outside of #defines, preprocessor #, and asm - - } else { - if ($prevline !~ /^..*\\$/ && - $line !~ /^\+\s*\#.*\\$/ && # preprocessor - $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm - $line =~ /^\+.*\\$/) { - WARN("LINE_CONTINUATIONS", - "Avoid unnecessary line continuations\n" . $herecurr); - } - } - -# do {} while (0) macro tests: -# single-statement macros do not need to be enclosed in do while (0) loop, -# macro should not end with a semicolon - if ($^V && $^V ge 5.10.0 && - $realfile !~ m@/vmlinux.lds.h$@ && - $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { - my $ln = $linenr; - my $cnt = $realcnt; - my ($off, $dstat, $dcond, $rest); - my $ctx = ''; - ($dstat, $dcond, $ln, $cnt, $off) = - ctx_statement_block($linenr, $realcnt, 0); - $ctx = $dstat; - - $dstat =~ s/\\\n.//g; - $dstat =~ s/$;/ /g; - - if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { - my $stmts = $2; - my $semis = $3; - - $ctx =~ s/\n*$//; - my $cnt = statement_rawlines($ctx); - my $herectx = $here . "\n"; - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - if (($stmts =~ tr/;/;/) == 1 && - $stmts !~ /^\s*(if|while|for|switch)\b/) { - WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", - "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); - } - if (defined $semis && $semis ne "") { - WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", - "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); - } - } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { - $ctx =~ s/\n*$//; - my $cnt = statement_rawlines($ctx); - my $herectx = $here . "\n"; - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - WARN("TRAILING_SEMICOLON", - "macros should not use a trailing semicolon\n" . "$herectx"); - } - } - -# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... -# all assignments may have only one of the following with an assignment: -# . -# ALIGN(...) -# VMLINUX_SYMBOL(...) - if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { - WARN("MISSING_VMLINUX_SYMBOL", - "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); - } - -# check for redundant bracing round if etc - if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, 1); - #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; - #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; - if ($#chunks > 0 && $level == 0) { - my @allowed = (); - my $allow = 0; - my $seen = 0; - my $herectx = $here . "\n"; - my $ln = $linenr - 1; - for my $chunk (@chunks) { - my ($cond, $block) = @{$chunk}; - - # If the condition carries leading newlines, then count those as offsets. - my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); - my $offset = statement_rawlines($whitespace) - 1; - - $allowed[$allow] = 0; - #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; - - # We have looked at and allowed this specific line. - $suppress_ifbraces{$ln + $offset} = 1; - - $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; - $ln += statement_rawlines($block) - 1; - - substr($block, 0, length($cond), ''); - - $seen++ if ($block =~ /^\s*{/); - - #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; - if (statement_lines($cond) > 1) { - #print "APW: ALLOWED: cond<$cond>\n"; - $allowed[$allow] = 1; - } - if ($block =~/\b(?:if|for|while)\b/) { - #print "APW: ALLOWED: block<$block>\n"; - $allowed[$allow] = 1; - } - if (statement_block_size($block) > 1) { - #print "APW: ALLOWED: lines block<$block>\n"; - $allowed[$allow] = 1; - } - $allow++; - } - if ($seen) { - my $sum_allowed = 0; - foreach (@allowed) { - $sum_allowed += $_; - } - if ($sum_allowed == 0) { - WARN("BRACES", - "braces {} are not necessary for any arm of this statement\n" . $herectx); - } elsif ($sum_allowed != $allow && - $seen != $allow) { - CHK("BRACES", - "braces {} should be used on all arms of this statement\n" . $herectx); - } - } - } - } - if (!defined $suppress_ifbraces{$linenr - 1} && - $line =~ /\b(if|while|for|else)\b/) { - my $allowed = 0; - - # Check the pre-context. - if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { - #print "APW: ALLOWED: pre<$1>\n"; - $allowed = 1; - } - - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, $-[0]); - - # Check the condition. - my ($cond, $block) = @{$chunks[0]}; - #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; - if (defined $cond) { - substr($block, 0, length($cond), ''); - } - if (statement_lines($cond) > 1) { - #print "APW: ALLOWED: cond<$cond>\n"; - $allowed = 1; - } - if ($block =~/\b(?:if|for|while)\b/) { - #print "APW: ALLOWED: block<$block>\n"; - $allowed = 1; - } - if (statement_block_size($block) > 1) { - #print "APW: ALLOWED: lines block<$block>\n"; - $allowed = 1; - } - # Check the post-context. - if (defined $chunks[1]) { - my ($cond, $block) = @{$chunks[1]}; - if (defined $cond) { - substr($block, 0, length($cond), ''); - } - if ($block =~ /^\s*\{/) { - #print "APW: ALLOWED: chunk-1 block<$block>\n"; - $allowed = 1; - } - } - if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { - my $herectx = $here . "\n"; - my $cnt = statement_rawlines($block); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - - WARN("BRACES", - "braces {} are not necessary for single statement blocks\n" . $herectx); - } - } - -# check for unnecessary blank lines around braces - if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { - if (CHK("BRACES", - "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && - $fix && $prevrawline =~ /^\+/) { - fix_delete_line($fixlinenr - 1, $prevrawline); - } - } - if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { - if (CHK("BRACES", - "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && - $fix) { - fix_delete_line($fixlinenr, $rawline); - } - } - -# no volatiles please - my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; - if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { - WARN("VOLATILE", - "Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); - } - -# Check for user-visible strings broken across lines, which breaks the ability -# to grep for the string. Make exceptions when the previous string ends in a -# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' -# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value - if ($line =~ /^\+\s*$String/ && - $prevline =~ /"\s*$/ && - $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { - if (WARN("SPLIT_STRING", - "quoted string split across lines\n" . $hereprev) && - $fix && - $prevrawline =~ /^\+.*"\s*$/ && - $last_coalesced_string_linenr != $linenr - 1) { - my $extracted_string = get_quoted_string($line, $rawline); - my $comma_close = ""; - if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { - $comma_close = $1; - } - - fix_delete_line($fixlinenr - 1, $prevrawline); - fix_delete_line($fixlinenr, $rawline); - my $fixedline = $prevrawline; - $fixedline =~ s/"\s*$//; - $fixedline .= substr($extracted_string, 1) . trim($comma_close); - fix_insert_line($fixlinenr - 1, $fixedline); - $fixedline = $rawline; - $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; - if ($fixedline !~ /\+\s*$/) { - fix_insert_line($fixlinenr, $fixedline); - } - $last_coalesced_string_linenr = $linenr; - } - } - -# check for missing a space in a string concatenation - if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { - WARN('MISSING_SPACE', - "break quoted strings at a space character\n" . $hereprev); - } - -# check for spaces before a quoted newline - if ($rawline =~ /^.*\".*\s\\n/) { - if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", - "unnecessary whitespace before a quoted newline\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; - } - - } - -# concatenated string without spaces between elements - if ($line =~ /$String[A-Z_]/ || $line =~ /[A-Za-z0-9_]$String/) { - CHK("CONCATENATED_STRING", - "Concatenated strings should use spaces between elements\n" . $herecurr); - } - -# uncoalesced string fragments - if ($line =~ /$String\s*"/) { - WARN("STRING_FRAGMENTS", - "Consecutive strings are generally better as a single string\n" . $herecurr); - } - -# check for %L{u,d,i} and 0x%[udi] in strings - my $string; - while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { - $string = substr($rawline, $-[1], $+[1] - $-[1]); - $string =~ s/%%/__/g; - if ($string =~ /(?<!%)%[\*\d\.\$]*L[udi]/) { - WARN("PRINTF_L", - "\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr); - last; - } - if ($string =~ /0x%[\*\d\.\$\Llzth]*[udi]/) { - ERROR("PRINTF_0xDECIMAL", - "Prefixing 0x with decimal output is defective\n" . $herecurr); - } - } - -# check for line continuations in quoted strings with odd counts of " - if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { - WARN("LINE_CONTINUATIONS", - "Avoid line continuations in quoted strings\n" . $herecurr); - } - -# warn about #if 0 - if ($line =~ /^.\s*\#\s*if\s+0\b/) { - CHK("REDUNDANT_CODE", - "if this code is redundant consider removing it\n" . - $herecurr); - } - -# check for needless "if (<foo>) fn(<foo>)" uses - if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { - my $tested = quotemeta($1); - my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; - if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { - my $func = $1; - if (WARN('NEEDLESS_IF', - "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && - $fix) { - my $do_fix = 1; - my $leading_tabs = ""; - my $new_leading_tabs = ""; - if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { - $leading_tabs = $1; - } else { - $do_fix = 0; - } - if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { - $new_leading_tabs = $1; - if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { - $do_fix = 0; - } - } else { - $do_fix = 0; - } - if ($do_fix) { - fix_delete_line($fixlinenr - 1, $prevrawline); - $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; - } - } - } - } - -# check for unnecessary "Out of Memory" messages - if ($line =~ /^\+.*\b$logFunctions\s*\(/ && - $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && - (defined $1 || defined $3) && - $linenr > 3) { - my $testval = $2; - my $testline = $lines[$linenr - 3]; - - my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); -# print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); - - if ($c =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|(?:dev_)?alloc_skb)/) { - WARN("OOM_MESSAGE", - "Possible unnecessary 'out of memory' message\n" . $hereprev); - } - } - -# check for logging functions with KERN_<LEVEL> - if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && - $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { - my $level = $1; - if (WARN("UNNECESSARY_KERN_LEVEL", - "Possible unnecessary $level\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s*$level\s*//; - } - } - -# check for mask then right shift without a parentheses - if ($^V && $^V ge 5.10.0 && - $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && - $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so - WARN("MASK_THEN_SHIFT", - "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); - } - -# check for pointer comparisons to NULL - if ($^V && $^V ge 5.10.0) { - while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { - my $val = $1; - my $equal = "!"; - $equal = "" if ($4 eq "!="); - if (CHK("COMPARISON_TO_NULL", - "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; - } - } - } - -# check for bad placement of section $InitAttribute (e.g.: __initdata) - if ($line =~ /(\b$InitAttribute\b)/) { - my $attr = $1; - if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { - my $ptr = $1; - my $var = $2; - if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && - ERROR("MISPLACED_INIT", - "$attr should be placed after $var\n" . $herecurr)) || - ($ptr !~ /\b(union|struct)\s+$attr\b/ && - WARN("MISPLACED_INIT", - "$attr should be placed after $var\n" . $herecurr))) && - $fix) { - $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; - } - } - } - -# check for $InitAttributeData (ie: __initdata) with const - if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { - my $attr = $1; - $attr =~ /($InitAttributePrefix)(.*)/; - my $attr_prefix = $1; - my $attr_type = $2; - if (ERROR("INIT_ATTRIBUTE", - "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/$InitAttributeData/${attr_prefix}initconst/; - } - } - -# check for $InitAttributeConst (ie: __initconst) without const - if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { - my $attr = $1; - if (ERROR("INIT_ATTRIBUTE", - "Use of $attr requires a separate use of const\n" . $herecurr) && - $fix) { - my $lead = $fixed[$fixlinenr] =~ - /(^\+\s*(?:static\s+))/; - $lead = rtrim($1); - $lead = "$lead " if ($lead !~ /^\+$/); - $lead = "${lead}const "; - $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; - } - } - -# check for __read_mostly with const non-pointer (should just be const) - if ($line =~ /\b__read_mostly\b/ && - $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { - if (ERROR("CONST_READ_MOSTLY", - "Invalid use of __read_mostly with const type\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; - } - } - -# don't use __constant_<foo> functions outside of include/uapi/ - if ($realfile !~ m@^include/uapi/@ && - $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { - my $constant_func = $1; - my $func = $constant_func; - $func =~ s/^__constant_//; - if (WARN("CONSTANT_CONVERSION", - "$constant_func should be $func\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; - } - } - -# prefer usleep_range over udelay - if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { - my $delay = $1; - # ignore udelay's < 10, however - if (! ($delay < 10) ) { - CHK("USLEEP_RANGE", - "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr); - } - if ($delay > 2000) { - WARN("LONG_UDELAY", - "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); - } - } - -# warn about unexpectedly long msleep's - if ($line =~ /\bmsleep\s*\((\d+)\);/) { - if ($1 < 20) { - WARN("MSLEEP", - "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr); - } - } - -# check for comparisons of jiffies - if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { - WARN("JIFFIES_COMPARISON", - "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); - } - -# check for comparisons of get_jiffies_64() - if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { - WARN("JIFFIES_COMPARISON", - "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); - } - -# warn about #ifdefs in C files -# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { -# print "#ifdef in C files should be avoided\n"; -# print "$herecurr"; -# $clean = 0; -# } - -# warn about spacing in #ifdefs - if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { - if (ERROR("SPACING", - "exactly one space required after that #$1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; - } - - } - -# check for spinlock_t definitions without a comment. - if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || - $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { - my $which = $1; - if (!ctx_has_comment($first_line, $linenr)) { - CHK("UNCOMMENTED_DEFINITION", - "$1 definition without comment\n" . $herecurr); - } - } -# check for memory barriers without a comment. - - my $barriers = qr{ - mb| - rmb| - wmb| - read_barrier_depends - }x; - my $barrier_stems = qr{ - mb__before_atomic| - mb__after_atomic| - store_release| - load_acquire| - store_mb| - (?:$barriers) - }x; - my $all_barriers = qr{ - (?:$barriers)| - smp_(?:$barrier_stems)| - virt_(?:$barrier_stems) - }x; - - if ($line =~ /\b(?:$all_barriers)\s*\(/) { - if (!ctx_has_comment($first_line, $linenr)) { - WARN("MEMORY_BARRIER", - "memory barrier without comment\n" . $herecurr); - } - } - - my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; - - if ($realfile !~ m@^include/asm-generic/@ && - $realfile !~ m@/barrier\.h$@ && - $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && - $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { - WARN("MEMORY_BARRIER", - "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); - } - -# check for waitqueue_active without a comment. - if ($line =~ /\bwaitqueue_active\s*\(/) { - if (!ctx_has_comment($first_line, $linenr)) { - WARN("WAITQUEUE_ACTIVE", - "waitqueue_active without comment\n" . $herecurr); - } - } - -# Check for expedited grace periods that interrupt non-idle non-nohz -# online CPUs. These expedited can therefore degrade real-time response -# if used carelessly, and should be avoided where not absolutely -# needed. It is always OK to use synchronize_rcu_expedited() and -# synchronize_sched_expedited() at boot time (before real-time applications -# start) and in error situations where real-time response is compromised in -# any case. Note that synchronize_srcu_expedited() does -not- interrupt -# other CPUs, so don't warn on uses of synchronize_srcu_expedited(). -# Of course, nothing comes for free, and srcu_read_lock() and -# srcu_read_unlock() do contain full memory barriers in payment for -# synchronize_srcu_expedited() non-interruption properties. - if ($line =~ /\b(synchronize_rcu_expedited|synchronize_sched_expedited)\(/) { - WARN("EXPEDITED_RCU_GRACE_PERIOD", - "expedited RCU grace periods should be avoided where they can degrade real-time response\n" . $herecurr); - - } - -# check of hardware specific defines - if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { - CHK("ARCH_DEFINES", - "architecture specific defines should be avoided\n" . $herecurr); - } - -# Check that the storage class is at the beginning of a declaration - if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { - WARN("STORAGE_CLASS", - "storage class should be at the beginning of the declaration\n" . $herecurr) - } - -# check the location of the inline attribute, that it is between -# storage class and type. - if ($line =~ /\b$Type\s+$Inline\b/ || - $line =~ /\b$Inline\s+$Storage\b/) { - ERROR("INLINE_LOCATION", - "inline keyword should sit between storage class and type\n" . $herecurr); - } - -# Check for __inline__ and __inline, prefer inline - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b(__inline__|__inline)\b/) { - if (WARN("INLINE", - "plain inline is preferred over $1\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; - - } - } - -# Check for __attribute__ packed, prefer __packed - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { - WARN("PREFER_PACKED", - "__packed is preferred over __attribute__((packed))\n" . $herecurr); - } - -# Check for __attribute__ aligned, prefer __aligned - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { - WARN("PREFER_ALIGNED", - "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); - } - -# Check for __attribute__ format(printf, prefer __printf - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { - if (WARN("PREFER_PRINTF", - "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; - - } - } - -# Check for __attribute__ format(scanf, prefer __scanf - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { - if (WARN("PREFER_SCANF", - "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; - } - } - -# Check for __attribute__ weak, or __weak declarations (may have link issues) - if ($^V && $^V ge 5.10.0 && - $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && - ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || - $line =~ /\b__weak\b/)) { - ERROR("WEAK_DECLARATION", - "Using weak declarations can have unintended link defects\n" . $herecurr); - } - -# check for c99 types like uint8_t used outside of uapi/ - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { - my $type = $1; - if ($type =~ /\b($typeC99Typedefs)\b/) { - $type = $1; - my $kernel_type = 'u'; - $kernel_type = 's' if ($type =~ /^_*[si]/); - $type =~ /(\d+)/; - $kernel_type .= $1; - if (CHK("PREFER_KERNEL_TYPES", - "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/; - } - } - } - -# check for cast of C90 native int or longer types constants - if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { - my $cast = $1; - my $const = $2; - if (WARN("TYPECAST_INT_CONSTANT", - "Unnecessary typecast of c90 int constant\n" . $herecurr) && - $fix) { - my $suffix = ""; - my $newconst = $const; - $newconst =~ s/${Int_type}$//; - $suffix .= 'U' if ($cast =~ /\bunsigned\b/); - if ($cast =~ /\blong\s+long\b/) { - $suffix .= 'LL'; - } elsif ($cast =~ /\blong\b/) { - $suffix .= 'L'; - } - $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; - } - } - -# check for sizeof(&) - if ($line =~ /\bsizeof\s*\(\s*\&/) { - WARN("SIZEOF_ADDRESS", - "sizeof(& should be avoided\n" . $herecurr); - } - -# check for sizeof without parenthesis - if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { - if (WARN("SIZEOF_PARENTHESIS", - "sizeof $1 should be sizeof($1)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; - } - } - -# check for struct spinlock declarations - if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { - WARN("USE_SPINLOCK_T", - "struct spinlock should be spinlock_t\n" . $herecurr); - } - -# check for seq_printf uses that could be seq_puts - if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { - my $fmt = get_quoted_string($line, $rawline); - $fmt =~ s/%%//g; - if ($fmt !~ /%/) { - if (WARN("PREFER_SEQ_PUTS", - "Prefer seq_puts to seq_printf\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; - } - } - } - -# Check for misused memsets - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { - - my $ms_addr = $2; - my $ms_val = $7; - my $ms_size = $12; - - if ($ms_size =~ /^(0x|)0$/i) { - ERROR("MEMSET", - "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); - } elsif ($ms_size =~ /^(0x|)1$/i) { - WARN("MEMSET", - "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); - } - } - -# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { - if (WARN("PREFER_ETHER_ADDR_COPY", - "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && - $fix) { - $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; - } - } - -# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { - WARN("PREFER_ETHER_ADDR_EQUAL", - "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") - } - -# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr -# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { - - my $ms_val = $7; - - if ($ms_val =~ /^(?:0x|)0+$/i) { - if (WARN("PREFER_ETH_ZERO_ADDR", - "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && - $fix) { - $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; - } - } elsif ($ms_val =~ /^(?:0xff|255)$/i) { - if (WARN("PREFER_ETH_BROADCAST_ADDR", - "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && - $fix) { - $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; - } - } - } - -# typecasts on min/max could be min_t/max_t - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { - if (defined $2 || defined $7) { - my $call = $1; - my $cast1 = deparenthesize($2); - my $arg1 = $3; - my $cast2 = deparenthesize($7); - my $arg2 = $8; - my $cast; - - if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { - $cast = "$cast1 or $cast2"; - } elsif ($cast1 ne "") { - $cast = $cast1; - } else { - $cast = $cast2; - } - WARN("MINMAX", - "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); - } - } - -# check usleep_range arguments - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { - my $min = $1; - my $max = $7; - if ($min eq $max) { - WARN("USLEEP_RANGE", - "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); - } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && - $min > $max) { - WARN("USLEEP_RANGE", - "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); - } - } - -# check for naked sscanf - if ($^V && $^V ge 5.10.0 && - defined $stat && - $line =~ /\bsscanf\b/ && - ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && - $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && - $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } - WARN("NAKED_SSCANF", - "unchecked sscanf return value\n" . "$here\n$stat_real\n"); - } - -# check for simple sscanf that should be kstrto<foo> - if ($^V && $^V ge 5.10.0 && - defined $stat && - $line =~ /\bsscanf\b/) { - my $lc = $stat =~ tr@\n@@; - $lc = $lc + $linenr; - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } - if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { - my $format = $6; - my $count = $format =~ tr@%@%@; - if ($count == 1 && - $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { - WARN("SSCANF_TO_KSTRTO", - "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); - } - } - } - -# check for new externs in .h files. - if ($realfile =~ /\.h$/ && - $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { - if (CHK("AVOID_EXTERNS", - "extern prototypes should be avoided in .h files\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; - } - } - -# check for new externs in .c files. - if ($realfile =~ /\.c$/ && defined $stat && - $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) - { - my $function_name = $1; - my $paren_space = $2; - - my $s = $stat; - if (defined $cond) { - substr($s, 0, length($cond), ''); - } - if ($s =~ /^\s*;/ && - $function_name ne 'uninitialized_var') - { - WARN("AVOID_EXTERNS", - "externs should be avoided in .c files\n" . $herecurr); - } - - if ($paren_space =~ /\n/) { - WARN("FUNCTION_ARGUMENTS", - "arguments for function declarations should follow identifier\n" . $herecurr); - } - - } elsif ($realfile =~ /\.c$/ && defined $stat && - $stat =~ /^.\s*extern\s+/) - { - WARN("AVOID_EXTERNS", - "externs should be avoided in .c files\n" . $herecurr); - } - -# checks for new __setup's - if ($rawline =~ /\b__setup\("([^"]*)"/) { - my $name = $1; - - if (!grep(/$name/, @setup_docs)) { - CHK("UNDOCUMENTED_SETUP", - "__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); - } - } - -# check for pointless casting of kmalloc return - if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { - WARN("UNNECESSARY_CASTS", - "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); - } - -# alloc style -# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { - CHK("ALLOC_SIZEOF_STRUCT", - "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); - } - -# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { - my $oldfunc = $3; - my $a1 = $4; - my $a2 = $10; - my $newfunc = "kmalloc_array"; - $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); - my $r1 = $a1; - my $r2 = $a2; - if ($a1 =~ /^sizeof\s*\S/) { - $r1 = $a2; - $r2 = $a1; - } - if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && - !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { - if (WARN("ALLOC_WITH_MULTIPLY", - "Prefer $newfunc over $oldfunc with multiply\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; - - } - } - } - -# check for krealloc arg reuse - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { - WARN("KREALLOC_ARG_REUSE", - "Reusing the krealloc arg is almost always a bug\n" . $herecurr); - } - -# check for alloc argument mismatch - if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { - WARN("ALLOC_ARRAY_ARGS", - "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); - } - -# check for multiple semicolons - if ($line =~ /;\s*;\s*$/) { - if (WARN("ONE_SEMICOLON", - "Statements terminations use 1 semicolon\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; - } - } - -# check for #defines like: 1 << <digit> that could be BIT(digit) - if ($line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { - my $ull = ""; - $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); - if (CHK("BIT_MACRO", - "Prefer using the BIT$ull macro\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; - } - } - -# check for case / default statements not preceded by break/fallthrough/switch - if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { - my $has_break = 0; - my $has_statement = 0; - my $count = 0; - my $prevline = $linenr; - while ($prevline > 1 && ($file || $count < 3) && !$has_break) { - $prevline--; - my $rline = $rawlines[$prevline - 1]; - my $fline = $lines[$prevline - 1]; - last if ($fline =~ /^\@\@/); - next if ($fline =~ /^\-/); - next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); - $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); - next if ($fline =~ /^.[\s$;]*$/); - $has_statement = 1; - $count++; - $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/); - } - if (!$has_break && $has_statement) { - WARN("MISSING_BREAK", - "Possible switch case/default not preceeded by break or fallthrough comment\n" . $herecurr); - } - } - -# check for switch/default statements without a break; - if ($^V && $^V ge 5.10.0 && - defined $stat && - $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { - my $ctx = ''; - my $herectx = $here . "\n"; - my $cnt = statement_rawlines($stat); - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } - WARN("DEFAULT_NO_BREAK", - "switch default: should use break\n" . $herectx); - } - -# check for gcc specific __FUNCTION__ - if ($line =~ /\b__FUNCTION__\b/) { - if (WARN("USE_FUNC", - "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; - } - } - -# check for uses of __DATE__, __TIME__, __TIMESTAMP__ - while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { - ERROR("DATE_TIME", - "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); - } - -# check for use of yield() - if ($line =~ /\byield\s*\(\s*\)/) { - WARN("YIELD", - "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); - } - -# check for comparisons against true and false - if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { - my $lead = $1; - my $arg = $2; - my $test = $3; - my $otype = $4; - my $trail = $5; - my $op = "!"; - - ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); - - my $type = lc($otype); - if ($type =~ /^(?:true|false)$/) { - if (("$test" eq "==" && "$type" eq "true") || - ("$test" eq "!=" && "$type" eq "false")) { - $op = ""; - } - - CHK("BOOL_COMPARISON", - "Using comparison to $otype is error prone\n" . $herecurr); - -## maybe suggesting a correct construct would better -## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); - - } - } - -# check for semaphores initialized locked - if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { - WARN("CONSIDER_COMPLETION", - "consider using a completion\n" . $herecurr); - } - -# recommend kstrto* over simple_strto* and strict_strto* - if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { - WARN("CONSIDER_KSTRTO", - "$1 is obsolete, use k$3 instead\n" . $herecurr); - } - -# check for __initcall(), use device_initcall() explicitly or more appropriate function please - if ($line =~ /^.\s*__initcall\s*\(/) { - WARN("USE_DEVICE_INITCALL", - "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); - } - -# check for various structs that are normally const (ops, kgdb, device_tree) - my $const_structs = qr{ - acpi_dock_ops| - address_space_operations| - backlight_ops| - block_device_operations| - dentry_operations| - dev_pm_ops| - dma_map_ops| - extent_io_ops| - file_lock_operations| - file_operations| - hv_ops| - ide_dma_ops| - intel_dvo_dev_ops| - item_operations| - iwl_ops| - kgdb_arch| - kgdb_io| - kset_uevent_ops| - lock_manager_operations| - microcode_ops| - mtrr_ops| - neigh_ops| - nlmsvc_binding| - of_device_id| - pci_raw_ops| - pipe_buf_operations| - platform_hibernation_ops| - platform_suspend_ops| - proto_ops| - rpc_pipe_ops| - seq_operations| - snd_ac97_build_ops| - soc_pcmcia_socket_ops| - stacktrace_ops| - sysfs_ops| - tty_operations| - uart_ops| - usb_mon_operations| - wd_ops}x; - if ($line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($const_structs)\b/) { - WARN("CONST_STRUCT", - "struct $1 should normally be const\n" . - $herecurr); - } - -# use of NR_CPUS is usually wrong -# ignore definitions of NR_CPUS and usage to define arrays as likely right - if ($line =~ /\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) - { - WARN("NR_CPUS", - "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); - } - -# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. - if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { - ERROR("DEFINE_ARCH_HAS", - "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); - } - -# likely/unlikely comparisons similar to "(likely(foo) > 0)" - if ($^V && $^V ge 5.10.0 && - $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { - WARN("LIKELY_MISUSE", - "Using $1 should generally have parentheses around the comparison\n" . $herecurr); - } - -# whine mightly about in_atomic - if ($line =~ /\bin_atomic\s*\(/) { - if ($realfile =~ m@^drivers/@) { - ERROR("IN_ATOMIC", - "do not use in_atomic in drivers\n" . $herecurr); - } elsif ($realfile !~ m@^kernel/@) { - WARN("IN_ATOMIC", - "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); - } - } - -# check for lockdep_set_novalidate_class - if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || - $line =~ /__lockdep_no_validate__\s*\)/ ) { - if ($realfile !~ m@^kernel/lockdep@ && - $realfile !~ m@^include/linux/lockdep@ && - $realfile !~ m@^drivers/base/core@) { - ERROR("LOCKDEP", - "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); - } - } - - if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || - $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { - WARN("EXPORTED_WORLD_WRITABLE", - "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); - } - -# Mode permission misuses where it seems decimal should be octal -# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop - if ($^V && $^V ge 5.10.0 && - $line =~ /$mode_perms_search/) { - foreach my $entry (@mode_permission_funcs) { - my $func = $entry->[0]; - my $arg_pos = $entry->[1]; - - my $skip_args = ""; - if ($arg_pos > 1) { - $arg_pos--; - $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; - } - my $test = "\\b$func\\s*\\(${skip_args}([\\d]+)\\s*[,\\)]"; - if ($line =~ /$test/) { - my $val = $1; - $val = $6 if ($skip_args ne ""); - - if ($val !~ /^0$/ && - (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || - length($val) ne 4)) { - ERROR("NON_OCTAL_PERMISSIONS", - "Use 4 digit octal (0777) not decimal permissions\n" . $herecurr); - } elsif ($val =~ /^$Octal$/ && (oct($val) & 02)) { - ERROR("EXPORTED_WORLD_WRITABLE", - "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); - } - } - } - } - -# validate content of MODULE_LICENSE against list from include/linux/module.h - if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { - my $extracted_string = get_quoted_string($line, $rawline); - my $valid_licenses = qr{ - GPL| - GPL\ v2| - GPL\ and\ additional\ rights| - Dual\ BSD/GPL| - Dual\ MIT/GPL| - Dual\ MPL/GPL| - Proprietary - }x; - if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { - WARN("MODULE_LICENSE", - "unknown module license " . $extracted_string . "\n" . $herecurr); - } - } - } - - # If we have no input at all, then there is nothing to report on - # so just keep quiet. - if ($#rawlines == -1) { - exit(0); - } - - # In mailback mode only produce a report in the negative, for - # things that appear to be patches. - if ($mailback && ($clean == 1 || !$is_patch)) { - exit(0); - } - - # This is not a patch, and we are are in 'no-patch' mode so - # just keep quiet. - if (!$chk_patch && !$is_patch) { - exit(0); - } - - if (!$is_patch && $file !~ /cover-letter\.patch$/) { - ERROR("NOT_UNIFIED_DIFF", - "Does not appear to be a unified-diff format patch\n"); - } - if ($is_patch && $filename ne '-' && $chk_signoff && $signoff == 0) { - ERROR("MISSING_SIGN_OFF", - "Missing Signed-off-by: line(s)\n"); - } - - print report_dump(); - if ($summary && !($clean == 1 && $quiet == 1)) { - print "$filename " if ($summary_file); - print "total: $cnt_error errors, $cnt_warn warnings, " . - (($check)? "$cnt_chk checks, " : "") . - "$cnt_lines lines checked\n"; - } - - if ($quiet == 0) { - # If there were whitespace errors which cleanpatch can fix - # then suggest that. - if ($rpt_cleaners) { - $rpt_cleaners = 0; - print << "EOM" - -NOTE: Whitespace errors detected. - You may wish to use scripts/cleanpatch or scripts/cleanfile -EOM - } - } - - if ($clean == 0 && $fix && - ("@rawlines" ne "@fixed" || - $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { - my $newfile = $filename; - $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); - my $linecount = 0; - my $f; - - @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); - - open($f, '>', $newfile) - or die "$P: Can't open $newfile for write\n"; - foreach my $fixed_line (@fixed) { - $linecount++; - if ($file) { - if ($linecount > 3) { - $fixed_line =~ s/^\+//; - print $f $fixed_line . "\n"; - } - } else { - print $f $fixed_line . "\n"; - } - } - close($f); - - if (!$quiet) { - print << "EOM"; - -Wrote EXPERIMENTAL --fix correction(s) to '$newfile' - -Do _NOT_ trust the results written to this file. -Do _NOT_ submit these changes without inspecting them for correctness. - -This EXPERIMENTAL file is simply a convenience to help rewrite patches. -No warranties, expressed or implied... -EOM - } - } - - if ($quiet == 0) { - print "\n"; - if ($clean == 1) { - print "$vname has no obvious style problems and is ready for submission.\n"; - } else { - print "$vname has style problems, please review.\n"; - } - } - return $clean; -} diff --git a/drivers/staging/greybus/scripts/spelling.txt b/drivers/staging/greybus/scripts/spelling.txt deleted file mode 100644 index 946caf3bd694..000000000000 --- a/drivers/staging/greybus/scripts/spelling.txt +++ /dev/null @@ -1,1072 +0,0 @@ -# Originally from Debian's Lintian tool. Various false positives have been -# removed, and various additions have been made as they've been discovered -# in the kernel source. -# -# License: GPLv2 -# -# The format of each line is: -# mistake||correction -# -abandonning||abandoning -abigious||ambiguous -abitrate||arbitrate -abov||above -abreviated||abbreviated -absense||absence -absolut||absolute -absoulte||absolute -acccess||access -acceleratoin||acceleration -accelleration||acceleration -accesing||accessing -accesnt||accent -accessable||accessible -accesss||access -accidentaly||accidentally -accidentually||accidentally -accoding||according -accomodate||accommodate -accomodates||accommodates -accordign||according -accoring||according -accout||account -accquire||acquire -accquired||acquired -accross||across -acessable||accessible -acess||access -achitecture||architecture -acient||ancient -acitions||actions -acitve||active -acknowldegement||acknowldegement -acknowledgement||acknowledgment -ackowledge||acknowledge -ackowledged||acknowledged -acording||according -activete||activate -acumulating||accumulating -adapater||adapter -addional||additional -additionaly||additionally -addres||address -addreses||addresses -addresss||address -aditional||additional -aditionally||additionally -aditionaly||additionally -adminstrative||administrative -adress||address -adresses||addresses -adviced||advised -afecting||affecting -agaist||against -albumns||albums -alegorical||allegorical -algorith||algorithm -algorithmical||algorithmically -algoritm||algorithm -algoritms||algorithms -algorrithm||algorithm -algorritm||algorithm -allign||align -allocatrd||allocated -allocte||allocate -allpication||application -alocate||allocate -alogirhtms||algorithms -alogrithm||algorithm -alot||a lot -alow||allow -alows||allows -altough||although -alue||value -ambigious||ambiguous -amoung||among -amout||amount -analysator||analyzer -ang||and -anniversery||anniversary -annoucement||announcement -anomolies||anomalies -anomoly||anomaly -anway||anyway -aplication||application -appearence||appearance -applicaion||application -appliction||application -applictions||applications -appplications||applications -appropiate||appropriate -appropriatly||appropriately -approriate||appropriate -approriately||appropriately -apropriate||appropriate -aquainted||acquainted -aquired||acquired -aquisition||acquisition -arbitary||arbitrary -architechture||architecture -arguement||argument -arguements||arguments -aritmetic||arithmetic -arne't||aren't -arraival||arrival -artifical||artificial -artillary||artillery -asign||assign -assertation||assertion -assiged||assigned -assigment||assignment -assigments||assignments -assistent||assistant -assocation||association -associcated||associated -assotiated||associated -assum||assume -assumtpion||assumption -asuming||assuming -asycronous||asynchronous -asynchnous||asynchronous -atomatically||automatically -atomicly||atomically -attachement||attachment -attched||attached -attemps||attempts -attruibutes||attributes -authentification||authentication -automaticaly||automatically -automaticly||automatically -automatize||automate -automatized||automated -automatizes||automates -autonymous||autonomous -auxillary||auxiliary -auxilliary||auxiliary -avaiable||available -avaible||available -availabe||available -availabled||available -availablity||availability -availale||available -availavility||availability -availble||available -availiable||available -avalable||available -avaliable||available -aysnc||async -backgroud||background -backword||backward -backwords||backwards -bahavior||behavior -bakup||backup -baloon||balloon -baloons||balloons -bandwith||bandwidth -batery||battery -beacuse||because -becasue||because -becomming||becoming -becuase||because -beeing||being -befor||before -begining||beginning -beter||better -betweeen||between -bianries||binaries -bitmast||bitmask -boardcast||broadcast -borad||board -boundry||boundary -brievely||briefly -broadcat||broadcast -cacluated||calculated -caculation||calculation -calender||calendar -calle||called -calucate||calculate -calulate||calculate -cancelation||cancellation -capabilites||capabilities -capabitilies||capabilities -capatibilities||capabilities -carefuly||carefully -cariage||carriage -catagory||category -cehck||check -challange||challenge -challanges||challenges -chanell||channel -changable||changeable -channle||channel -channnel||channel -charachter||character -charachters||characters -charactor||character -charater||character -charaters||characters -charcter||character -chcek||check -chck||check -checksuming||checksumming -childern||children -childs||children -chiled||child -chked||checked -chnage||change -chnages||changes -chnnel||channel -choosen||chosen -chouse||chose -circumvernt||circumvent -claread||cleared -clared||cleared -closeing||closing -clustred||clustered -collapsable||collapsible -colorfull||colorful -comand||command -comit||commit -commerical||commercial -comming||coming -comminucation||communication -commited||committed -commiting||committing -committ||commit -commoditiy||commodity -compability||compatibility -compaibility||compatibility -compatability||compatibility -compatable||compatible -compatibiliy||compatibility -compatibilty||compatibility -compatiblity||compatibility -competion||completion -compilant||compliant -compleatly||completely -completly||completely -complient||compliant -componnents||components -compres||compress -compresion||compression -comression||compression -comunication||communication -conbination||combination -conditionaly||conditionally -conected||connected -configuratoin||configuration -configuraton||configuration -configuretion||configuration -conider||consider -conjuction||conjunction -connectinos||connections -connnection||connection -connnections||connections -consistancy||consistency -consistant||consistent -containes||contains -containts||contains -contaisn||contains -contant||contact -contence||contents -continous||continuous -continously||continuously -continueing||continuing -contraints||constraints -controled||controlled -controler||controller -controll||control -contruction||construction -contry||country -convertion||conversion -convertor||converter -convienient||convenient -convinient||convenient -corected||corrected -correponding||corresponding -correponds||corresponds -correspoding||corresponding -cotrol||control -couter||counter -coutner||counter -cryptocraphic||cryptographic -cunter||counter -curently||currently -dafault||default -deafult||default -deamon||daemon -decompres||decompress -decription||description -defailt||default -defferred||deferred -definate||definite -definately||definitely -defintion||definition -defintions||definitions -defualt||default -defult||default -deivce||device -delared||declared -delare||declare -delares||declares -delaring||declaring -delemiter||delimiter -dependancies||dependencies -dependancy||dependency -dependant||dependent -depreacted||deprecated -depreacte||deprecate -desactivate||deactivate -desciptors||descriptors -descripton||description -descrition||description -descritptor||descriptor -desctiptor||descriptor -desriptor||descriptor -desriptors||descriptors -destory||destroy -destoryed||destroyed -destorys||destroys -destroied||destroyed -detabase||database -develope||develop -developement||development -developped||developed -developpement||development -developper||developer -developpment||development -deveolpment||development -devided||divided -deviece||device -diable||disable -dictionnary||dictionary -didnt||didn't -diferent||different -differrence||difference -difinition||definition -diplay||display -direectly||directly -disapear||disappear -disapeared||disappeared -disappared||disappeared -disconnet||disconnect -discontinous||discontinuous -dispertion||dispersion -dissapears||disappears -distiction||distinction -docuentation||documentation -documantation||documentation -documentaion||documentation -documment||document -doesnt||doesn't -dorp||drop -dosen||doesn -downlad||download -downlads||downloads -druing||during -dynmaic||dynamic -easilly||easily -ecspecially||especially -edditable||editable -editting||editing -efficently||efficiently -ehther||ether -eigth||eight -eletronic||electronic -enabledi||enabled -enchanced||enhanced -encorporating||incorporating -encrupted||encrypted -encrypiton||encryption -endianess||endianness -enhaced||enhanced -enlightnment||enlightenment -enocded||encoded -enterily||entirely -enviroiment||environment -enviroment||environment -environement||environment -environent||environment -eqivalent||equivalent -equiped||equipped -equivelant||equivalent -equivilant||equivalent -eror||error -estbalishment||establishment -etsablishment||establishment -etsbalishment||establishment -excecutable||executable -exceded||exceeded -excellant||excellent -existance||existence -existant||existent -exixt||exist -exlcude||exclude -exlcusive||exclusive -exmaple||example -expecially||especially -explicite||explicit -explicitely||explicitly -explict||explicit -explictly||explicitly -expresion||expression -exprimental||experimental -extened||extended -extensability||extensibility -extention||extension -extracter||extractor -faild||failed -faill||fail -failue||failure -failuer||failure -faireness||fairness -faliure||failure -familar||familiar -fatser||faster -feauture||feature -feautures||features -fetaure||feature -fetaures||features -fileystem||filesystem -finanize||finalize -findn||find -finilizes||finalizes -finsih||finish -flusing||flushing -folloing||following -followign||following -follwing||following -forseeable||foreseeable -forse||force -fortan||fortran -forwardig||forwarding -framwork||framework -frequncy||frequency -frome||from -fucntion||function -fuction||function -fuctions||functions -funcion||function -functionallity||functionality -functionaly||functionally -functionnality||functionality -functonality||functionality -funtion||function -funtions||functions -furthur||further -futhermore||furthermore -futrue||future -gaurenteed||guaranteed -generiously||generously -genric||generic -globel||global -grabing||grabbing -grahical||graphical -grahpical||graphical -grapic||graphic -guage||gauge -guarenteed||guaranteed -guarentee||guarantee -halfs||halves -hander||handler -handfull||handful -hanled||handled -happend||happened -harware||hardware -heirarchically||hierarchically -helpfull||helpful -hierachy||hierarchy -hierarchie||hierarchy -howver||however -hsould||should -hypter||hyper -identidier||identifier -imblance||imbalance -immeadiately||immediately -immedaite||immediate -immediatelly||immediately -immediatly||immediately -immidiate||immediate -impelentation||implementation -impementated||implemented -implemantation||implementation -implemenation||implementation -implementaiton||implementation -implementated||implemented -implemention||implementation -implemetation||implementation -implemntation||implementation -implentation||implementation -implmentation||implementation -implmenting||implementing -incomming||incoming -incompatabilities||incompatibilities -incompatable||incompatible -inconsistant||inconsistent -increas||increase -incrment||increment -indendation||indentation -indended||intended -independant||independent -independantly||independently -independed||independent -indiate||indicate -inexpect||inexpected -infomation||information -informatiom||information -informations||information -informtion||information -infromation||information -ingore||ignore -inital||initial -initalised||initialized -initalise||initialize -initalize||initialize -initation||initiation -initators||initiators -initializiation||initialization -initialzed||initialized -initilization||initialization -initilize||initialize -inofficial||unofficial -insititute||institute -instal||install -inteface||interface -integreated||integrated -integrety||integrity -integrey||integrity -intendet||intended -intented||intended -interanl||internal -interchangable||interchangeable -interferring||interfering -interger||integer -intermittant||intermittent -internel||internal -interoprability||interoperability -interrface||interface -interrrupt||interrupt -interrup||interrupt -interrups||interrupts -interruptted||interrupted -interupted||interrupted -interupt||interrupt -intial||initial -intialized||initialized -intialize||initialize -intregral||integral -intrrupt||interrupt -intuative||intuitive -invaid||invalid -invalde||invald -invalide||invalid -invididual||individual -invokation||invocation -invokations||invocations -irrelevent||irrelevant -isnt||isn't -isssue||issue -itslef||itself -jave||java -jeffies||jiffies -juse||just -jus||just -kown||known -langage||language -langauage||language -langauge||language -langugage||language -lauch||launch -layed||laid -leightweight||lightweight -lengh||length -lenght||length -lenth||length -lesstiff||lesstif -libaries||libraries -libary||library -librairies||libraries -libraris||libraries -licenceing||licencing -loggging||logging -loggin||login -logile||logfile -loosing||losing -losted||lost -machinary||machinery -maintainance||maintenance -maintainence||maintenance -maintan||maintain -makeing||making -malplaced||misplaced -malplace||misplace -managable||manageable -managment||management -mangement||management -manoeuvering||maneuvering -mappping||mapping -mathimatical||mathematical -mathimatic||mathematic -mathimatics||mathematics -maxium||maximum -mechamism||mechanism -meetign||meeting -ment||meant -mergable||mergeable -mesage||message -messags||messages -messgaes||messages -messsage||message -messsages||messages -microprocesspr||microprocessor -milliseonds||milliseconds -minumum||minimum -miscelleneous||miscellaneous -misformed||malformed -mispelled||misspelled -mispelt||misspelt -miximum||maximum -mmnemonic||mnemonic -mnay||many -modeled||modelled -modulues||modules -monochorome||monochrome -monochromo||monochrome -monocrome||monochrome -mopdule||module -mroe||more -mulitplied||multiplied -multidimensionnal||multidimensional -multple||multiple -mumber||number -muticast||multicast -mutiple||multiple -mutli||multi -nams||names -navagating||navigating -nead||need -neccecary||necessary -neccesary||necessary -neccessary||necessary -necesary||necessary -negaive||negative -negoitation||negotiation -negotation||negotiation -nerver||never -nescessary||necessary -nessessary||necessary -noticable||noticeable -notications||notifications -notifed||notified -numebr||number -numner||number -obtaion||obtain -occassionally||occasionally -occationally||occasionally -occurance||occurrence -occurances||occurrences -occured||occurred -occurence||occurrence -occure||occurred -occuring||occurring -offet||offset -omitt||omit -ommiting||omitting -ommitted||omitted -onself||oneself -ony||only -operatione||operation -opertaions||operations -optionnal||optional -optmizations||optimizations -orientatied||orientated -orientied||oriented -otherise||otherwise -ouput||output -overaall||overall -overhread||overhead -overlaping||overlapping -overriden||overridden -overun||overrun -pacakge||package -pachage||package -packacge||package -packege||package -packge||package -packtes||packets -pakage||package -pallette||palette -paln||plan -paramameters||parameters -paramater||parameter -parametes||parameters -parametised||parametrised -paramter||parameter -paramters||parameters -particuarly||particularly -particularily||particularly -pased||passed -passin||passing -pathes||paths -pecularities||peculiarities -peformance||performance -peice||piece -pendantic||pedantic -peprocessor||preprocessor -perfoming||performing -permissons||permissions -peroid||period -persistance||persistence -persistant||persistent -platfrom||platform -plattform||platform -pleaes||please -ploting||plotting -plugable||pluggable -poinnter||pointer -poiter||pointer -posible||possible -positon||position -possibilites||possibilities -powerfull||powerful -preceeded||preceded -preceeding||preceding -preceed||precede -precendence||precedence -precission||precision -preemptable||preemptible -prefered||preferred -prefferably||preferably -premption||preemption -prepaired||prepared -pressre||pressure -primative||primitive -princliple||principle -priorty||priority -privilaged||privileged -privilage||privilege -priviledge||privilege -priviledges||privileges -probaly||probably -procceed||proceed -proccesors||processors -procesed||processed -proces||process -processessing||processing -processess||processes -processpr||processor -processsed||processed -processsing||processing -procteted||protected -prodecure||procedure -progams||programs -progess||progress -programers||programmers -programm||program -programms||programs -progresss||progress -promiscous||promiscuous -promps||prompts -pronnounced||pronounced -prononciation||pronunciation -pronouce||pronounce -pronunce||pronounce -propery||property -propigate||propagate -propigation||propagation -propogate||propagate -prosess||process -protable||portable -protcol||protocol -protecion||protection -protocoll||protocol -psudo||pseudo -psuedo||pseudo -psychadelic||psychedelic -pwoer||power -quering||querying -raoming||roaming -reasearcher||researcher -reasearchers||researchers -reasearch||research -recepient||recipient -receving||receiving -recieved||received -recieve||receive -reciever||receiver -recieves||receives -recogniced||recognised -recognizeable||recognizable -recommanded||recommended -recyle||recycle -redircet||redirect -redirectrion||redirection -refcounf||refcount -refence||reference -refered||referred -referenace||reference -refering||referring -refernces||references -refernnce||reference -refrence||reference -registerd||registered -registeresd||registered -registes||registers -registraration||registration -regster||register -regualar||regular -reguator||regulator -regulamentations||regulations -reigstration||registration -releated||related -relevent||relevant -remoote||remote -remore||remote -removeable||removable -repectively||respectively -replacable||replaceable -replacments||replacements -replys||replies -reponse||response -representaion||representation -reqeust||request -requiere||require -requirment||requirement -requred||required -requried||required -requst||request -reseting||resetting -resizeable||resizable -resouces||resources -resoures||resources -responce||response -ressizes||resizes -ressource||resource -ressources||resources -retransmited||retransmitted -retreived||retrieved -retreive||retrieve -retrive||retrieve -retuned||returned -reudce||reduce -reuest||request -reuqest||request -reutnred||returned -rmeoved||removed -rmeove||remove -rmeoves||removes -rountine||routine -routins||routines -rquest||request -runing||running -runned||ran -runnning||running -runtine||runtime -sacrifying||sacrificing -safly||safely -safty||safety -savable||saveable -scaned||scanned -scaning||scanning -scarch||search -seach||search -searchs||searches -secquence||sequence -secund||second -segement||segment -senarios||scenarios -sentivite||sensitive -separatly||separately -sepcify||specify -sepc||spec -seperated||separated -seperately||separately -seperate||separate -seperatly||separately -seperator||separator -sepperate||separate -sequece||sequence -sequencial||sequential -serveral||several -setts||sets -settting||setting -shotdown||shutdown -shoud||should -shouldnt||shouldn't -shoule||should -shrinked||shrunk -siginificantly||significantly -signabl||signal -similary||similarly -similiar||similar -simlar||similar -simliar||similar -simpified||simplified -singaled||signaled -singal||signal -singed||signed -sleeped||slept -softwares||software -speach||speech -specfic||specific -speciefied||specified -specifc||specific -specifed||specified -specificatin||specification -specificaton||specification -specifing||specifying -specifiying||specifying -speficied||specified -speicify||specify -speling||spelling -spinlcok||spinlock -spinock||spinlock -splitted||split -spreaded||spread -sructure||structure -stablilization||stabilization -staically||statically -staion||station -standardss||standards -standartization||standardization -standart||standard -staticly||statically -stoped||stopped -stoppped||stopped -straming||streaming -struc||struct -structres||structures -stuct||struct -stucture||structure -sturcture||structure -subdirectoires||subdirectories -suble||subtle -substract||subtract -succesfully||successfully -succesful||successful -successfull||successful -sucessfully||successfully -sucess||success -superflous||superfluous -superseeded||superseded -suplied||supplied -suported||supported -suport||support -suppored||supported -supportin||supporting -suppoted||supported -suppported||supported -suppport||support -supress||suppress -surpresses||suppresses -susbsystem||subsystem -suspicously||suspiciously -swaping||swapping -switchs||switches -symetric||symmetric -synax||syntax -synchonized||synchronized -syncronize||synchronize -syncronizing||synchronizing -syncronus||synchronous -syste||system -sytem||system -sythesis||synthesis -taht||that -targetted||targeted -targetting||targeting -teh||the -temorary||temporary -temproarily||temporarily -thier||their -threds||threads -threshhold||threshold -throught||through -thses||these -tiggered||triggered -tipically||typically -tmis||this -torerable||tolerable -tramsmitted||transmitted -tramsmit||transmit -tranfer||transfer -transciever||transceiver -transferd||transferrd -transfered||transferred -transfering||transferring -transision||transition -transmittd||transmitted -transormed||transformed -trasmission||transmission -treshold||threshold -trigerring||triggering -trun||turn -ture||true -tyep||type -udpate||update -uesd||used -unconditionaly||unconditionally -underun||underrun -unecessary||unnecessary -unexecpted||unexpected -unexpectd||unexpected -unexpeted||unexpected -unfortunatelly||unfortunately -unifiy||unify -unintialized||uninitialized -unknonw||unknown -unknow||unknown -unkown||unknown -unneedingly||unnecessarily -unresgister||unregister -unsinged||unsigned -unstabel||unstable -unsuccessfull||unsuccessful -unsuported||unsupported -untill||until -unuseful||useless -upate||update -usefule||useful -usefull||useful -usege||usage -usera||users -usualy||usually -utilites||utilities -utillities||utilities -utilties||utilities -utiltity||utility -utitity||utility -utitlty||utility -vaid||valid -vaild||valid -valide||valid -variantions||variations -varient||variant -vaule||value -verbse||verbose -verisons||versions -verison||version -verson||version -vicefersa||vice-versa -virtal||virtual -virtaul||virtual -virtiual||virtual -visiters||visitors -vitual||virtual -wating||waiting -wether||whether -whataver||whatever -whcih||which -whenver||whenever -wheter||whether -whe||when -wierd||weird -wiil||will -wirte||write -withing||within -wnat||want -workarould||workaround -writeing||writing -writting||writing -zombe||zombie -zomebie||zombie -- cgit v1.2.3-59-g8ed1b From bc5aa3a079437159ed4685e8bb062e027d6e54a3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Mon, 19 Sep 2016 12:35:16 +0200 Subject: staging: greybus: Documentation: remove sysfs tree We do not need an example of the sysfs tree in the kernel code itself, so remove these files, as they are now pointless. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id | 1 - .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id | 1 - .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state | 0 .../Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string | 0 .../Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id | 0 .../greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id | 1 - .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces | 1 - .../greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class | 0 .../greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id | 1 - .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore | 1 - .../sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore | 1 - .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state | 0 .../Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string | 0 .../Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string | 0 .../greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id | 0 .../greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id | 1 - .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id | 0 .../staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces | 1 - drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id | 1 - .../greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class | 0 .../greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id | 1 - .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state | 0 .../Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string | 0 .../Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string | 0 .../greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id | 0 .../greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id | 0 .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id | 1 - .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id | 0 .../staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id | 1 - drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces | 1 - drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject | 0 drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id | 1 - 56 files changed, 18 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject delete mode 100644 drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore deleted file mode 100644 index f935021a8f8a..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!.gitignore diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id deleted file mode 100644 index 7ed6ff82de6b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id +++ /dev/null @@ -1 +0,0 @@ -5 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id deleted file mode 100644 index 1e8b31496214..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id +++ /dev/null @@ -1 +0,0 @@ -6 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id deleted file mode 100644 index 7ed6ff82de6b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id +++ /dev/null @@ -1 +0,0 @@ -5 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id deleted file mode 100644 index 00750edc07d6..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id +++ /dev/null @@ -1 +0,0 @@ -3 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id deleted file mode 100644 index 00750edc07d6..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id +++ /dev/null @@ -1 +0,0 @@ -3 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces deleted file mode 100644 index d00491fd7e5b..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id +++ /dev/null @@ -1 +0,0 @@ -2 -- cgit v1.2.3-59-g8ed1b From 0be6dfc296624a6cc840d246de656d7ac800b70d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 8 Sep 2016 11:58:46 +0200 Subject: staging: greybus: remove README and LICENSE We don't need yet-another-copy of the GPLv2 in the tree, and the README is now pointless, so remove both of these files. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/LICENSE | 339 ---------------------------------------- drivers/staging/greybus/README | 10 -- 2 files changed, 349 deletions(-) delete mode 100644 drivers/staging/greybus/LICENSE delete mode 100644 drivers/staging/greybus/README diff --git a/drivers/staging/greybus/LICENSE b/drivers/staging/greybus/LICENSE deleted file mode 100644 index d7f105139782..000000000000 --- a/drivers/staging/greybus/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ -GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/> - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - This program 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 of the License, or - (at your option) any later version. - - This program 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 this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - {signature of Ty Coon}, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/drivers/staging/greybus/README b/drivers/staging/greybus/README deleted file mode 100644 index b0745d37265b..000000000000 --- a/drivers/staging/greybus/README +++ /dev/null @@ -1,10 +0,0 @@ -Greybus kernel code - -To build against the running kernel (odds are you don't want this): - make - -To build against a specific kernel source tree (odds are you want this): - KERNELDIR=/home/some/random/place make - -Any questions / concerns about this code base, please email: - Greg Kroah-Hartman <greg@kroah.com> -- cgit v1.2.3-59-g8ed1b From ecfea541e66e313d743563c517b7072a79c13acc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 8 Sep 2016 15:01:23 +0200 Subject: staging: greybus: remove .gitignore file Only the tools subdirectory needs a .gitignore entry, so move it there and fix it up to only list the needed file. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/.gitignore | 15 --------------- drivers/staging/greybus/tools/.gitignore | 1 + 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 drivers/staging/greybus/.gitignore create mode 100644 drivers/staging/greybus/tools/.gitignore diff --git a/drivers/staging/greybus/.gitignore b/drivers/staging/greybus/.gitignore deleted file mode 100644 index faf45ee4dafd..000000000000 --- a/drivers/staging/greybus/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -.* -*.cmd -*.ko -*.mod.c -modules.order -Module.symvers -*.o -*.o.* -*.swp -.tmp_versions -tags -cscope.* -ncscope.* -*.patch -tools/loopback_test diff --git a/drivers/staging/greybus/tools/.gitignore b/drivers/staging/greybus/tools/.gitignore new file mode 100644 index 000000000000..023654c83068 --- /dev/null +++ b/drivers/staging/greybus/tools/.gitignore @@ -0,0 +1 @@ +loopback_test -- cgit v1.2.3-59-g8ed1b From 526dec064223d7e21b1e3c91759bddff1f7d968a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Mon, 19 Sep 2016 12:40:33 +0200 Subject: staging: greybus: remove old es1 endpoint description The Toshiba ES1 chip is no longer around, so remove the USB descriptor documentation for it as no one cares anymore. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- .../staging/greybus/Documentation/es1_ap_desc.c | 70 ---------------------- drivers/staging/greybus/devices | 11 ---- 2 files changed, 81 deletions(-) delete mode 100644 drivers/staging/greybus/Documentation/es1_ap_desc.c delete mode 100644 drivers/staging/greybus/devices diff --git a/drivers/staging/greybus/Documentation/es1_ap_desc.c b/drivers/staging/greybus/Documentation/es1_ap_desc.c deleted file mode 100644 index 1502089ec29c..000000000000 --- a/drivers/staging/greybus/Documentation/es1_ap_desc.c +++ /dev/null @@ -1,70 +0,0 @@ -/* ES1 AP Bridge Chip USB descriptor definitions */ - -static const u8 es1_dev_descriptor[] = { - 0x12, /* __u8 bLength */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x00, 0x02 /* __le16 bcdUSB v2.0 */ - 0x00, /* __u8 bDeviceClass */ - 0x00, /* __u8 bDeviceClass */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x40, /* __u8 bMaxPacketSize0; 2^64 = 512 Bytes */ - - 0xff, 0xff, /* __le16 idVendor; 0xffff made up for now */ - 0x01, 0x00, /* __le16 idProduct; 0x0001 made up for now */ - 0x01, 0x00, /* __le16 bcdDevice; ES1 */ - - 0x03, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -static const u8 es1_config_descriptor[] = { - /* one configuration */ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, 0x00, /* __le16 wTotalLength; */ - 0x01, /* __u8 bNumInterfaces; (1) */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0xc0, /* __u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* one interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x03, /* __u8 if_bNumEndpoints; */ - 0xff, /* __u8 if_bInterfaceClass; Vendor-specific */ - 0xff, /* __u8 if_bInterfaceSubClass; Vendor-specific */ - 0xff, /* __u8 if_bInterfaceProtocol; Vendor-specific */ - 0x00, /* __u8 if_iInterface; */ - - /* three endpoints */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ - 0x40, /* __u8 ep_bInterval; 64ms */ - - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x82, /* __u8 ep_bEndpointAddress; IN Endpoint 2 */ - 0x02, /* __u8 ep_bmAttributes; Bulk */ - 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ - 0x40 /* __u8 ep_bInterval; */ - - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x02, /* __u8 ep_bEndpointAddress; Out Endpoint 2 */ - 0x02, /* __u8 ep_bmAttributes; Bulk */ - 0x00, 0x04, /* __le16 ep_wMaxPacketSize; 1024 */ - 0x40 /* __u8 ep_bInterval; */ -}; diff --git a/drivers/staging/greybus/devices b/drivers/staging/greybus/devices deleted file mode 100644 index 486bba8e5de0..000000000000 --- a/drivers/staging/greybus/devices +++ /dev/null @@ -1,11 +0,0 @@ -T: Bus=01 Lev=03 Prnt=07 Port=02 Cnt=03 Dev#= 12 Spd=12 MxCh= 0 -D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 -P: Vendor=ffff ProdID=0001 Rev= 1.00 -S: Manufacturer=Greybus -S: Product=SVC Bridge -S: SerialNumber=12239 -C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr=100mA -I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=es1_ap_driver -E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=64ms -E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms -E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms -- cgit v1.2.3-59-g8ed1b From d4f56b47a8fac90b15adfae80a42a2735d6b3213 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Mon, 19 Sep 2016 15:46:40 +0200 Subject: staging: greybus: Add drivers/staging/greybus to the build This adds a proper Kconfig file for drivers/staging/greybus and fixes up the Makefile to work correctly within the kernel build system (modules depend on the .config options, etc.) Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/greybus/Kconfig | 219 +++++++++++++++++++++++++++++++++++++++ drivers/staging/greybus/Makefile | 193 +++++++++++++--------------------- 4 files changed, 293 insertions(+), 122 deletions(-) create mode 100644 drivers/staging/greybus/Kconfig diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index af9476460023..59829d8a7bd6 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -104,4 +104,6 @@ source "drivers/staging/i4l/Kconfig" source "drivers/staging/ks7010/Kconfig" +source "drivers/staging/greybus/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 9f6009dcafa8..210fcea7a5fd 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_WILC1000) += wilc1000/ obj-$(CONFIG_MOST) += most/ obj-$(CONFIG_ISDN_I4L) += i4l/ obj-$(CONFIG_KS7010) += ks7010/ +obj-$(CONFIG_GREYBUS) += greybus/ diff --git a/drivers/staging/greybus/Kconfig b/drivers/staging/greybus/Kconfig new file mode 100644 index 000000000000..c169bc3ebe5b --- /dev/null +++ b/drivers/staging/greybus/Kconfig @@ -0,0 +1,219 @@ +menuconfig GREYBUS + tristate "Greybus support" + depends on SYSFS + ---help--- + This option enables the Greybus driver core. Greybus is an + hardware protocol that was designed to provide Unipro with a + sane application layer. It was originally designed for the + ARA project, a module phone system, but has shown up in other + phones, and can be tunneled over other busses in order to + control hardware devices. + + Say Y here to enable support for these types of drivers. + + To compile this code as a module, chose M here: the module + will be called greybus.ko + +if GREYBUS + +config GREYBUS_ES2 + tristate "Greybus ES3 USB host controller" + depends on USB + ---help--- + Select this option if you have a Toshiba ES3 USB device that + acts as a Greybus "host controller". This device is a bridge + from a USB device to a Unipro network. + + To compile this code as a module, chose M here: the module + will be called gb-es2.ko + +config GREYBUS_AUDIO + tristate "Greybus Audio Class driver" + depends on SOUND + ---help--- + Select this option if you have a device that follows the + Greybus Audio Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-audio.ko + +config GREYBUS_BOOTROM + tristate "Greybus Bootrom Class driver" + ---help--- + Select this option if you have a device that follows the + Greybus Bootrom Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-bootrom.ko + +config GREYBUS_CAMERA + tristate "Greybus Camera Class driver" + depends on MEDIA && LEDS_CLASS_FLASH && BROKEN + ---help--- + Select this option if you have a device that follows the + Greybus Camera Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-camera.ko + +config GREYBUS_FIRMWARE + tristate "Greybus Firmware Download Class driver" + depends on SPI + ---help--- + Select this option if you have a device that follows the + Greybus Firmware Download Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-firmware.ko + +config GREYBUS_HID + tristate "Greybus HID Class driver" + depends on HID && INPUT + ---help--- + Select this option if you have a device that follows the + Greybus HID Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-hid.ko + +config GREYBUS_LIGHT + tristate "Greybus LED Class driver" + depends on LEDS_CLASS && BROKEN + ---help--- + Select this option if you have a device that follows the + Greybus LED Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-led.ko + +config GREYBUS_LOG + tristate "Greybus Debug Log Class driver" + ---help--- + Select this option if you have a device that follows the + Greybus Debug Log Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-log.ko + +config GREYBUS_LOOPBACK + tristate "Greybus Loopback Class driver" + ---help--- + Select this option if you have a device that follows the + Greybus Debug Log Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-log.ko + +config GREYBUS_POWER + tristate "Greybus Powersupply Class driver" + depends on POWER_SUPPLY + ---help--- + Select this option if you have a device that follows the + Greybus Powersupply Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-power-supply.ko + +config GREYBUS_RAW + tristate "Greybus Raw Class driver" + ---help--- + Select this option if you have a device that follows the + Greybus Raw Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-raw.ko + +config GREYBUS_VIBRATOR + tristate "Greybus Vibrator Motor Class driver" + ---help--- + Select this option if you have a device that follows the + Greybus Vibrator Motor Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-vibrator.ko + +menuconfig GREYBUS_BRIDGED_PHY + tristate "Greybus Bridged PHY Class drivers" + ---help--- + Select this option to pick from a variety of Greybus Bridged + PHY class drivers. These drivers emulate a number of + different "traditional" busses by tunneling them over Greybus. + Examples of this include serial, SPI, USB, and others. + + To compile this code as a module, chose M here: the module + will be called gb-phy.ko + +if GREYBUS_BRIDGED_PHY + +config GREYBUS_GPIO + tristate "Greybus GPIO Bridged PHY driver" + depends on GPIO + ---help--- + Select this option if you have a device that follows the + Greybus GPIO Bridged PHY Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-gpio.ko + +config GREYBUS_I2C + tristate "Greybus I2C Bridged PHY driver" + depends on I2C + ---help--- + Select this option if you have a device that follows the + Greybus I2C Bridged PHY Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-i2c.ko + +config GREYBUS_PWM + tristate "Greybus PWM Bridged PHY driver" + depends on PWM + ---help--- + Select this option if you have a device that follows the + Greybus PWM Bridged PHY Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-pwm.ko + +config GREYBUS_SDIO + tristate "Greybus SDIO Bridged PHY driver" + depends on MMC + ---help--- + Select this option if you have a device that follows the + Greybus SDIO Bridged PHY Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-sdio.ko + +config GREYBUS_SPI + tristate "Greybus SPI Bridged PHY driver" + depends on SPI + ---help--- + Select this option if you have a device that follows the + Greybus SPI Bridged PHY Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-spi.ko + +config GREYBUS_UART + tristate "Greybus UART Bridged PHY driver" + depends on TTY + ---help--- + Select this option if you have a device that follows the + Greybus UART Bridged PHY Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-uart.ko + +config GREYBUS_USB + tristate "Greybus USB Host Bridged PHY driver" + depends on USB + ---help--- + Select this option if you have a device that follows the + Greybus USB Host Bridged PHY Class specification. + + To compile this code as a module, chose M here: the module + will be called gb-usb.ko + +endif # GREYBUS_BRIDGED_PHY +endif # GREYBUS diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index fa5aaf363c7b..0cc769eb470a 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,3 +1,4 @@ +# Greybus core greybus-y := core.o \ debugfs.o \ hd.o \ @@ -13,135 +14,83 @@ greybus-y := core.o \ timesync.o \ timesync_platform.o -gb-gbphy-y := gbphy.o - -# Prefix all modules with gb- -gb-vibrator-y := vibrator.o -gb-power-supply-y := power_supply.o -gb-log-y := log.o -gb-loopback-y := loopback.o -gb-light-y := light.o -gb-raw-y := raw.o -gb-hid-y := hid.o -gb-es2-y := es2.o -gb-arche-y := arche-platform.o arche-apb-ctrl.o -gb-audio-module-y := audio_module.o audio_topology.o -gb-audio-codec-y := audio_codec.o -gb-audio-gb-y := audio_gb.o -gb-audio-apbridgea-y := audio_apbridgea.o -gb-audio-manager-y += audio_manager.o -gb-audio-manager-y += audio_manager_module.o -gb-bootrom-y := bootrom.o -gb-camera-y := camera.o -gb-firmware-y := fw-core.o fw-download.o fw-management.o authentication.o -gb-spilib-y := spilib.o -gb-sdio-y := sdio.o -gb-uart-y := uart.o -gb-pwm-y := pwm.o -gb-gpio-y := gpio.o -gb-i2c-y := i2c.o -gb-usb-y := usb.o -gb-spi-y := spi.o - -obj-m += greybus.o -obj-m += gb-gbphy.o -obj-m += gb-vibrator.o -obj-m += gb-power-supply.o -obj-m += gb-log.o -obj-m += gb-loopback.o -obj-m += gb-light.o -obj-m += gb-hid.o -obj-m += gb-raw.o -obj-m += gb-es2.o -ifeq ($(CONFIG_USB_HSIC_USB3613),y) - obj-m += gb-arche.o -endif -ifeq ($(CONFIG_ARCH_MSM8994),y) - obj-m += gb-audio-codec.o - obj-m += gb-audio-module.o - obj-m += gb-camera.o -endif -obj-m += gb-audio-gb.o -obj-m += gb-audio-apbridgea.o -obj-m += gb-audio-manager.o -obj-m += gb-bootrom.o -obj-m += gb-firmware.o -obj-m += gb-spilib.o -obj-m += gb-sdio.o -obj-m += gb-uart.o -obj-m += gb-pwm.o -obj-m += gb-gpio.o -obj-m += gb-i2c.o -obj-m += gb-usb.o -obj-m += gb-spi.o - -KERNELVER ?= $(shell uname -r) -KERNELDIR ?= /lib/modules/$(KERNELVER)/build -INSTALL_MOD_PATH ?= /.. -PWD := $(shell pwd) - -# kernel config option that shall be enable -CONFIG_OPTIONS_ENABLE := POWER_SUPPLY PWM SYSFS SPI USB SND_SOC MMC LEDS_CLASS INPUT - -# kernel config option that shall be disable -CONFIG_OPTIONS_DISABLE := - -# this only run in kbuild part of the makefile -ifneq ($(KERNELRELEASE),) -# This function returns the argument version if current kernel version is minor -# than the passed version, return 1 if equal or the current kernel version if it -# is greater than argument version. -kvers_cmp=$(shell [ "$(KERNELVERSION)" = "$(1)" ] && echo 1 || printf "$(1)\n$(KERNELVERSION)" | sort -V | tail -1) - -ifneq ($(call kvers_cmp,"3.19.0"),3.19.0) - CONFIG_OPTIONS_ENABLE += LEDS_CLASS_FLASH -endif - -ifneq ($(call kvers_cmp,"4.2.0"),4.2.0) - CONFIG_OPTIONS_ENABLE += V4L2_FLASH_LED_CLASS -endif - -$(foreach opt,$(CONFIG_OPTIONS_ENABLE),$(if $(CONFIG_$(opt)),, \ - $(error CONFIG_$(opt) is disabled in the kernel configuration and must be enable \ - to continue compilation))) -$(foreach opt,$(CONFIG_OPTIONS_DISABLE),$(if $(filter m y, $(CONFIG_$(opt))), \ - $(error CONFIG_$(opt) is enabled in the kernel configuration and must be disable \ - to continue compilation),)) -endif - -# add -Wall to try to catch everything we can. -ccflags-y := -Wall +obj-$(CONFIG_GREYBUS) += greybus.o # needed for trace events ccflags-y += -I$(src) -GB_AUDIO_MANAGER_SYSFS ?= true -ifeq ($(GB_AUDIO_MANAGER_SYSFS),true) -gb-audio-manager-y += audio_manager_sysfs.o -ccflags-y += -DGB_AUDIO_MANAGER_SYSFS -endif -all: module - -tools:: - $(MAKE) -C tools KERNELDIR=$(realpath $(KERNELDIR)) +# Greybus Host controller drivers +gb-es2-y := es2.o -module: - $(MAKE) -C $(KERNELDIR) M=$(PWD) +obj-$(CONFIG_GREYBUS_ES2) += gb-es2.o + +# Greybus class drivers +gb-bootrom-y := bootrom.o +gb-camera-y := camera.o +gb-firmware-y := fw-core.o fw-download.o fw-management.o authentication.o +gb-spilib-y := spilib.o +gb-hid-y := hid.o +gb-light-y := light.o +gb-log-y := log.o +gb-loopback-y := loopback.o +gb-power-supply-y := power_supply.o +gb-raw-y := raw.o +gb-vibrator-y := vibrator.o + +obj-$(CONFIG_GREYBUS_BOOTROM) += gb-bootrom.o +obj-$(CONFIG_GREYBUS_CAMERA) += gb-camera.o +obj-$(CONFIG_GREYBUS_FIRMWARE) += gb-firmware.o gb-spilib.o +obj-$(CONFIG_GREYBUS_HID) += gb-hid.o +obj-$(CONFIG_GREYBUS_LIGHT) += gb-light.o +obj-$(CONFIG_GREYBUS_LOG) += gb-log.o +obj-$(CONFIG_GREYBUS_LOOPBACK) += gb-loopback.o +obj-$(CONFIG_GREYBUS_POWER) += gb-power-supply.o +obj-$(CONFIG_GREYBUS_RAW) += gb-raw.o +obj-$(CONFIG_GREYBUS_VIBRATOR) += gb-vibrator.o + +# Greybus Audio is a bunch of modules +gb-audio-module-y := audio_module.o audio_topology.o +gb-audio-codec-y := audio_codec.o +gb-audio-gb-y := audio_gb.o +gb-audio-apbridgea-y := audio_apbridgea.o +gb-audio-manager-y := audio_manager.o audio_manager_module.o + +# Greybus Audio sysfs helpers can be useful when debugging +#GB_AUDIO_MANAGER_SYSFS ?= true +#ifeq ($(GB_AUDIO_MANAGER_SYSFS),true) +#gb-audio-manager-y += audio_manager_sysfs.o +#ccflags-y += -DGB_AUDIO_MANAGER_SYSFS +#endif + +obj-$(CONFIG_GREYBUS_AUDIO_MSM8994) += gb-audio-codec.o +obj-$(CONFIG_GREYBUS_AUDIO_MSM8994) += gb-audio-module.o +obj-$(CONFIG_GREYBUS_AUDIO) += gb-audio-gb.o +obj-$(CONFIG_GREYBUS_AUDIO) += gb-audio-apbridgea.o +obj-$(CONFIG_GREYBUS_AUDIO) += gb-audio-manager.o + + +# Greybus Bridged PHY drivers +gb-gbphy-y := gbphy.o +gb-gpio-y := gpio.o +gb-i2c-y := i2c.o +gb-pwm-y := pwm.o +gb-sdio-y := sdio.o +gb-spi-y := spi.o +gb-uart-y := uart.o +gb-usb-y := usb.o -check: - $(MAKE) -C $(KERNELDIR) M=$(PWD) C=2 CF="-D__CHECK_ENDIAN__" +obj-$(CONFIG_GREYBUS_BRIDGED_PHY) += gb-gbphy.o +obj-$(CONFIG_GREYBUS_GPIO) += gb-gpio.o +obj-$(CONFIG_GREYBUS_I2C) += gb-i2c.o +obj-$(CONFIG_GREYBUS_PWM) += gb-pwm.o +obj-$(CONFIG_GREYBUS_SDIO) += gb-sdio.o +obj-$(CONFIG_GREYBUS_SPI) += gb-spi.o +obj-$(CONFIG_GREYBUS_UART) += gb-uart.o +obj-$(CONFIG_GREYBUS_USB) += gb-usb.o -clean: - rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c - rm -f Module.markers Module.symvers modules.order - rm -rf .tmp_versions Modules.symvers - $(MAKE) -C tools clean -coccicheck: - $(MAKE) -C $(KERNELDIR) M=$(PWD) coccicheck +# Greybus Platform driver +gb-arche-y := arche-platform.o arche-apb-ctrl.o -install: module - mkdir -p $(INSTALL_MOD_PATH)/lib/modules/$(KERNELVER)/kernel/drivers/greybus/ - cp -f *.ko $(INSTALL_MOD_PATH)/lib/modules/$(KERNELVER)/kernel/drivers/greybus/ - depmod -b $(INSTALL_MOD_PATH) -a $(KERNELVER) +obj-$(CONFIG_USB_HSIC_USB3613) += gb-arche.o -- cgit v1.2.3-59-g8ed1b From 887520884b9925312b78d23f323d9cf1f7e603af Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 8 Sep 2016 11:51:33 +0200 Subject: staging: greybus: kernel_ver.h: remove lots of stuff Now that we do not care about the kernel version we are building against, we can strip out lots of backward compatibilty that was added to kernel_ver.h in order to write semi-portable driver code. To start with, remove the functions and #defines that are now in the kernel tree, no need to have duplicate copies of them all. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 284 ----------------------------------- 1 file changed, 284 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 97e7ac9498fd..eda948545917 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -22,49 +22,6 @@ #define CORE_OWNS_PSY_STRUCT #endif -#ifndef __ATTR_WO -#define __ATTR_WO(_name) { \ - .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ - .store = _name##_store, \ -} -#endif - -#ifndef __ATTR_RW -#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \ - _name##_show, _name##_store) -#endif - -#ifndef DEVICE_ATTR_RO -#define DEVICE_ATTR_RO(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_RO(_name) -#endif - -#ifndef DEVICE_ATTR_WO -#define DEVICE_ATTR_WO(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_WO(_name) -#endif - -#ifndef DEVICE_ATTR_RW -#define DEVICE_ATTR_RW(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_RW(_name) -#endif - -#ifndef U8_MAX -#define U8_MAX ((u8)~0U) -#endif /* ! U8_MAX */ - -#ifndef U16_MAX -#define U16_MAX ((u16)(~0U)) -#endif /* !U16_MAX */ - -#ifndef U32_MAX -#define U32_MAX ((u32)(~0U)) -#endif /* !U32_MAX */ - -#ifndef U64_MAX -#define U64_MAX ((u64)(~0U)) -#endif /* !U64_MAX */ - /* * The GPIO api sucks rocks in places, like removal, so work around their * explicit requirements of catching the return value for kernels older than @@ -87,54 +44,6 @@ static inline void gb_gpiochip_remove(struct gpio_chip *chip) } #endif -/* - * ATTRIBUTE_GROUPS showed up in 3.11-rc2, but we need to build on 3.10, so add - * it here. - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) -#include <linux/sysfs.h> - -#define ATTRIBUTE_GROUPS(name) \ -static const struct attribute_group name##_group = { \ - .attrs = name##_attrs, \ -}; \ -static const struct attribute_group *name##_groups[] = { \ - &name##_group, \ - NULL, \ -} - -static inline int sysfs_create_groups(struct kobject *kobj, - const struct attribute_group **groups) -{ - int error = 0; - int i; - - if (!groups) - return 0; - - for (i = 0; groups[i]; i++) { - error = sysfs_create_group(kobj, groups[i]); - if (error) { - while (--i >= 0) - sysfs_remove_group(kobj, groups[i]); - break; - } - } - return error; -} - -static inline void sysfs_remove_groups(struct kobject *kobj, - const struct attribute_group **groups) -{ - int i; - - if (!groups) - return; - for (i = 0; groups[i]; i++) - sysfs_remove_group(kobj, groups[i]); -} -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) #define MMC_HS400_SUPPORTED #define MMC_DDR52_DEFINED @@ -148,110 +57,6 @@ static inline void sysfs_remove_groups(struct kobject *kobj, #define MMC_POWER_UNDEFINED_SUPPORTED #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) -#include <linux/scatterlist.h> -static inline bool sg_miter_get_next_page(struct sg_mapping_iter *miter) -{ - if (!miter->__remaining) { - struct scatterlist *sg; - unsigned long pgoffset; - - if (!__sg_page_iter_next(&miter->piter)) - return false; - - sg = miter->piter.sg; - pgoffset = miter->piter.sg_pgoffset; - - miter->__offset = pgoffset ? 0 : sg->offset; - miter->__remaining = sg->offset + sg->length - - (pgoffset << PAGE_SHIFT) - miter->__offset; - miter->__remaining = min_t(unsigned long, miter->__remaining, - PAGE_SIZE - miter->__offset); - } - - return true; -} - -static inline bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset) -{ - sg_miter_stop(miter); - - while (offset) { - off_t consumed; - - if (!sg_miter_get_next_page(miter)) - return false; - - consumed = min_t(off_t, offset, miter->__remaining); - miter->__offset += consumed; - miter->__remaining -= consumed; - offset -= consumed; - } - - return true; -} - -static inline size_t _sg_copy_buffer(struct scatterlist *sgl, - unsigned int nents, void *buf, - size_t buflen, off_t skip, - bool to_buffer) -{ - unsigned int offset = 0; - struct sg_mapping_iter miter; - unsigned long flags; - unsigned int sg_flags = SG_MITER_ATOMIC; - - if (to_buffer) - sg_flags |= SG_MITER_FROM_SG; - else - sg_flags |= SG_MITER_TO_SG; - - sg_miter_start(&miter, sgl, nents, sg_flags); - - if (!sg_miter_skip(&miter, skip)) - return false; - - local_irq_save(flags); - - while (sg_miter_next(&miter) && offset < buflen) { - unsigned int len; - - len = min(miter.length, buflen - offset); - - if (to_buffer) - memcpy(buf + offset, miter.addr, len); - else - memcpy(miter.addr, buf + offset, len); - - offset += len; - } - - sg_miter_stop(&miter); - - local_irq_restore(flags); - return offset; -} - -static inline size_t sg_pcopy_to_buffer(struct scatterlist *sgl, - unsigned int nents, void *buf, - size_t buflen, off_t skip) -{ - return _sg_copy_buffer(sgl, nents, buf, buflen, skip, true); -} - -static inline size_t sg_pcopy_from_buffer(struct scatterlist *sgl, - unsigned int nents, void *buf, - size_t buflen, off_t skip) -{ - return _sg_copy_buffer(sgl, nents, buf, buflen, skip, false); -} -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) -#define list_last_entry(ptr, type, member) \ - list_entry((ptr)->prev, type, member) -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) /* * At this time the internal API for the set brightness was changed to the async @@ -273,19 +78,6 @@ static inline size_t sg_pcopy_from_buffer(struct scatterlist *sgl, #define LED_HAVE_SET_BLOCKING #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) -/* - * From this version upper it was introduced the possibility to disable led - * sysfs entries to handle control of the led device to v4l2, which was - * implemented later. So, before that this should return false. - */ -#include <linux/leds.h> -static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev) -{ - return false; -} -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) /* * New helper functions for registering/unregistering flash led devices as v4l2 @@ -329,82 +121,6 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev) #define SPI_CORE_SUPPORT_PM #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) -/** - * reinit_completion - reinitialize a completion structure - * @x: pointer to completion structure that is to be reinitialized - * - * This inline function should be used to reinitialize a completion structure - * so it can be reused. This is especially important after complete_all() is - * used. - */ -static inline void reinit_completion(struct completion *x) -{ - x->done = 0; -} -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) -#include <linux/pwm.h> -/* - * pwm_is_enabled() was first defined in 4.3. - * PWMF_ENABLED was first defined in 3.5-rc2, but our code is - * always newer than that. -*/ -static inline bool pwm_is_enabled(const struct pwm_device *pwm) -{ - return test_bit(PWMF_ENABLED, &pwm->flags); -} -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) -/** - * kstrtobool - convert common user inputs into boolean values - * @s: input string - * @res: result - * - * This routine returns 0 iff the first character is one of 'Yy1Nn0', or - * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value - * pointed to by res is updated upon finding a match. - */ -static inline int kstrtobool(const char *s, bool *res) -{ - if (!s) - return -EINVAL; - - switch (s[0]) { - case 'y': - case 'Y': - case '1': - *res = true; - return 0; - case 'n': - case 'N': - case '0': - *res = false; - return 0; - case 'o': - case 'O': - switch (s[1]) { - case 'n': - case 'N': - *res = true; - return 0; - case 'f': - case 'F': - *res = false; - return 0; - default: - break; - } - default: - break; - } - - return -EINVAL; -} -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) /* * After commit b2b49ccbdd54 (PM: Kconfig: Set PM_RUNTIME if PM_SLEEP is -- cgit v1.2.3-59-g8ed1b From be21106d67db9ad3c66e48c8efdcf1bc08be45a4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 8 Sep 2016 15:46:38 +0200 Subject: staging: greybus: hid: remove KERNEL_VERSION checks No need to support older kernel versions in the Greybus HID driver, so remove the checks as needed, we can now rely on all of the "new" apis being present. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/hid.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index b558c811b7a1..730d746fc4c2 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -277,23 +277,6 @@ static int gb_hid_raw_request(struct hid_device *hid, unsigned char reportnum, } } -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0) -static int gb_hid_get_raw_report(struct hid_device *hid, - unsigned char reportnum, __u8 *buf, - size_t len, unsigned char rtype) -{ - return gb_hid_raw_request(hid, reportnum, buf, len, rtype, - HID_REQ_GET_REPORT); -} - -static int gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf, - size_t len, unsigned char rtype) -{ - return gb_hid_raw_request(hid, buf[0], buf, len, rtype, - HID_REQ_SET_REPORT); -} -#endif - /* HID Callbacks */ static int gb_hid_parse(struct hid_device *hid) { @@ -422,9 +405,7 @@ static struct hid_ll_driver gb_hid_ll_driver = { .open = gb_hid_open, .close = gb_hid_close, .power = gb_hid_power, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) .raw_request = gb_hid_raw_request, -#endif }; static int gb_hid_init(struct gb_hid *ghid) @@ -444,10 +425,6 @@ static int gb_hid_init(struct gb_hid *ghid) hid->driver_data = ghid; hid->ll_driver = &gb_hid_ll_driver; hid->dev.parent = &ghid->connection->bundle->dev; -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0) - hid->hid_get_raw_report = gb_hid_get_raw_report; - hid->hid_output_raw_report = gb_hid_output_raw_report; -#endif // hid->bus = BUS_GREYBUS; /* Need a bustype for GREYBUS in <linux/input.h> */ /* Set HID device's name */ -- cgit v1.2.3-59-g8ed1b From 06000c03174988b4e8167d03ad0a36c6792686cb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 8 Sep 2016 15:47:30 +0200 Subject: staging: greybus: vibrator: remove KERNEL_VERSION checks No need to support older kernel versions in the Greybus Vibrator driver, so remove the checks as needed, we can now rely on all of the correct driver core apis being present. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/vibrator.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c index 7296a4dd0a0c..4ba0e168930f 100644 --- a/drivers/staging/greybus/vibrator.c +++ b/drivers/staging/greybus/vibrator.c @@ -108,9 +108,7 @@ ATTRIBUTE_GROUPS(vibrator); static struct class vibrator_class = { .name = "vibrator", .owner = THIS_MODULE, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) .dev_groups = vibrator_groups, -#endif }; static DEFINE_IDA(minors); @@ -169,19 +167,6 @@ static int gb_vibrator_probe(struct gb_bundle *bundle, } vib->dev = dev; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) - /* - * Newer kernels handle this in a race-free manner, by the dev_groups - * field in the struct class up above. But for older kernels, we need - * to "open code this :( - */ - retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); - if (retval) { - device_unregister(dev); - goto err_ida_remove; - } -#endif - INIT_DELAYED_WORK(&vib->delayed_work, gb_vibrator_worker); gb_pm_runtime_put_autosuspend(bundle); @@ -212,9 +197,6 @@ static void gb_vibrator_disconnect(struct gb_bundle *bundle) if (cancel_delayed_work_sync(&vib->delayed_work)) turn_off(vib); -#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) - sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); -#endif device_unregister(vib->dev); ida_simple_remove(&minors, vib->minor); gb_connection_disable(vib->connection); -- cgit v1.2.3-59-g8ed1b From b14bb976260077415047737a0032f89275622c96 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 8 Sep 2016 15:52:06 +0200 Subject: staging: greybus: gpio: remove KERNEL_VERSION checks No need to support older kernel versions in the Greybus GPIO driver, so remove the checks as needed, we can now rely on all of the correct GPIO core apis being present. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/gpio.c | 10 +--------- drivers/staging/greybus/kernel_ver.h | 22 ---------------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/drivers/staging/greybus/gpio.c b/drivers/staging/greybus/gpio.c index 294e2f52f1d4..ea8234abf185 100644 --- a/drivers/staging/greybus/gpio.c +++ b/drivers/staging/greybus/gpio.c @@ -404,11 +404,7 @@ static int gb_gpio_request_handler(struct gb_operation *op) } local_irq_disable(); -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) - generic_handle_irq_desc(irq, desc); -#else generic_handle_irq_desc(desc); -#endif local_irq_enable(); return 0; @@ -684,11 +680,7 @@ static int gb_gpio_probe(struct gbphy_device *gbphy_dev, gpio = &ggc->chip; gpio->label = "greybus_gpio"; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) gpio->parent = &gbphy_dev->dev; -#else - gpio->dev = &gbphy_dev->dev; -#endif gpio->owner = THIS_MODULE; gpio->request = gb_gpio_request; @@ -750,7 +742,7 @@ static void gb_gpio_remove(struct gbphy_device *gbphy_dev) gbphy_runtime_get_noresume(gbphy_dev); gb_connection_disable_rx(connection); - gb_gpiochip_remove(&ggc->chip); + gpiochip_remove(&ggc->chip); gb_gpio_irqchip_remove(ggc); gb_connection_disable(connection); gb_connection_destroy(connection); diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index eda948545917..0e129ff6c6d6 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -22,28 +22,6 @@ #define CORE_OWNS_PSY_STRUCT #endif -/* - * The GPIO api sucks rocks in places, like removal, so work around their - * explicit requirements of catching the return value for kernels older than - * 3.17, which they explicitly changed in the 3.17 kernel. Consistency is - * overrated. - */ -#include <linux/gpio.h> - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) -static inline void gb_gpiochip_remove(struct gpio_chip *chip) -{ - gpiochip_remove(chip); -} -#else -static inline void gb_gpiochip_remove(struct gpio_chip *chip) -{ - int ret; - - ret = gpiochip_remove(chip); -} -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) #define MMC_HS400_SUPPORTED #define MMC_DDR52_DEFINED -- cgit v1.2.3-59-g8ed1b From 3e4b5b883780ed795843f68213c9db3f8d78391e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Mon, 19 Sep 2016 16:41:54 +0200 Subject: staging: greybus: gpio: it's CONFIG_GPIOLIB, not CONFIG_GPIO The GPIO dependancy is CONFIG_GPIOLIB, not CONFIG_GPIO, no wonder it wasn't building properly... Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Kconfig b/drivers/staging/greybus/Kconfig index c169bc3ebe5b..ede91843eda5 100644 --- a/drivers/staging/greybus/Kconfig +++ b/drivers/staging/greybus/Kconfig @@ -147,7 +147,7 @@ if GREYBUS_BRIDGED_PHY config GREYBUS_GPIO tristate "Greybus GPIO Bridged PHY driver" - depends on GPIO + depends on GPIOLIB ---help--- Select this option if you have a device that follows the Greybus GPIO Bridged PHY Class specification. -- cgit v1.2.3-59-g8ed1b From 148e0b8f48a83008596876befe1d9aed256c8ea1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 8 Sep 2016 16:06:27 +0200 Subject: staging: greybus: spi: remove KERNEL_VERSION checks No need to support older kernel versions in the Greybus SPI and spilib driver, so remove the checks as needed, we can now rely on all of the correct SPI core apis being present. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/fw-core.c | 21 +-------------------- drivers/staging/greybus/kernel_ver.h | 13 ------------- drivers/staging/greybus/spi.c | 21 +-------------------- drivers/staging/greybus/spilib.c | 6 ++---- 4 files changed, 4 insertions(+), 57 deletions(-) diff --git a/drivers/staging/greybus/fw-core.c b/drivers/staging/greybus/fw-core.c index a7e4a8c24d22..454a98957ba5 100644 --- a/drivers/staging/greybus/fw-core.c +++ b/drivers/staging/greybus/fw-core.c @@ -20,26 +20,7 @@ struct gb_fw_core { struct gb_connection *cap_connection; }; -#ifndef SPI_CORE_SUPPORT_PM -static int fw_spi_prepare_transfer_hardware(struct device *dev) -{ - return gb_pm_runtime_get_sync(to_gb_bundle(dev)); -} - -static void fw_spi_unprepare_transfer_hardware(struct device *dev) -{ - gb_pm_runtime_put_autosuspend(to_gb_bundle(dev)); -} - -static struct spilib_ops __spilib_ops = { - .prepare_transfer_hardware = fw_spi_prepare_transfer_hardware, - .unprepare_transfer_hardware = fw_spi_unprepare_transfer_hardware, -}; - -static struct spilib_ops *spilib_ops = &__spilib_ops; -#else -static struct spilib_ops *spilib_ops = NULL; -#endif +static struct spilib_ops *spilib_ops; struct gb_connection *to_fw_mgmt_connection(struct device *dev) { diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 0e129ff6c6d6..59d55be1dd8a 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -86,19 +86,6 @@ #define POWER_SUPPLY_PROP_CALIBRATE -1 #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) -#define SPI_DEV_MODALIAS "spidev" -#define SPI_NOR_MODALIAS "spi-nor" -#else -#define SPI_DEV_MODALIAS "spidev" -#define SPI_NOR_MODALIAS "m25p80" -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) -/* Starting from this version, the spi core handles runtime pm automatically */ -#define SPI_CORE_SUPPORT_PM -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) /* * After commit b2b49ccbdd54 (PM: Kconfig: Set PM_RUNTIME if PM_SLEEP is diff --git a/drivers/staging/greybus/spi.c b/drivers/staging/greybus/spi.c index bb76b3c0118d..c893552b5c0b 100644 --- a/drivers/staging/greybus/spi.c +++ b/drivers/staging/greybus/spi.c @@ -13,26 +13,7 @@ #include "gbphy.h" #include "spilib.h" -#ifndef SPI_CORE_SUPPORT_PM -static int gbphy_spi_prepare_transfer_hardware(struct device *dev) -{ - return gbphy_runtime_get_sync(to_gbphy_dev(dev)); -} - -static void gbphy_spi_unprepare_transfer_hardware(struct device *dev) -{ - gbphy_runtime_put_autosuspend(to_gbphy_dev(dev)); -} - -static struct spilib_ops __spilib_ops = { - .prepare_transfer_hardware = gbphy_spi_prepare_transfer_hardware, - .unprepare_transfer_hardware = gbphy_spi_unprepare_transfer_hardware, -}; - -static struct spilib_ops *spilib_ops = &__spilib_ops; -#else -static struct spilib_ops *spilib_ops = NULL; -#endif +static struct spilib_ops *spilib_ops; static int gb_spi_probe(struct gbphy_device *gbphy_dev, const struct gbphy_device_id *id) diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c index 9427c313dd4e..e97b19148497 100644 --- a/drivers/staging/greybus/spilib.c +++ b/drivers/staging/greybus/spilib.c @@ -456,10 +456,10 @@ static int gb_spi_setup_device(struct gb_spilib *spi, u8 cs) dev_type = response.device_type; if (dev_type == GB_SPI_SPI_DEV) - strlcpy(spi_board.modalias, SPI_DEV_MODALIAS, + strlcpy(spi_board.modalias, "spidev", sizeof(spi_board.modalias)); else if (dev_type == GB_SPI_SPI_NOR) - strlcpy(spi_board.modalias, SPI_NOR_MODALIAS, + strlcpy(spi_board.modalias, "spi-nor", sizeof(spi_board.modalias)); else if (dev_type == GB_SPI_SPI_MODALIAS) memcpy(spi_board.modalias, response.name, @@ -526,9 +526,7 @@ int gb_spilib_master_init(struct gb_connection *connection, struct device *dev, gb_spi_unprepare_transfer_hardware; } -#ifdef SPI_CORE_SUPPORT_PM master->auto_runtime_pm = true; -#endif ret = spi_register_master(master); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From 7434564ca130552f3f873d0a7e87286a16890c32 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Thu, 8 Sep 2016 16:10:50 +0200 Subject: staging: greybus: sdio: fix min() type check The 0-day bot pointed out a type difference in one min() call, so fix it up by being explicit about the type being compared. Reported-by: kbuild test robot <fengguang.wu@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index a78d9e4a0321..99b997904edd 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -136,7 +136,7 @@ static int gb_sdio_get_caps(struct gb_sdio_host *host) data_max = min(data_max - sizeof(struct gb_sdio_transfer_request), data_max - sizeof(struct gb_sdio_transfer_response)); - blksz = min(le16_to_cpu(response.max_blk_size), data_max); + blksz = min_t(u16, le16_to_cpu(response.max_blk_size), data_max); blksz = max_t(u32, 512, blksz); mmc->max_blk_size = rounddown_pow_of_two(blksz); -- cgit v1.2.3-59-g8ed1b From a7af2fe6d1baa45e73531fb994444fa0f59d8f62 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Thu, 8 Sep 2016 17:17:48 +0100 Subject: staging: greybus: light: remove KERNEL_VERSION checks No need to support older kernel versions in the Greybus Light driver, so remove the checks as needed, we can now rely on all of the correct LED core apis being present. And compile only if flash and v4l2 flash is reachable. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 30 -------------- drivers/staging/greybus/light.c | 77 +++++------------------------------- 2 files changed, 10 insertions(+), 97 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 59d55be1dd8a..980263f58523 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -35,36 +35,6 @@ #define MMC_POWER_UNDEFINED_SUPPORTED #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) -/* - * At this time the internal API for the set brightness was changed to the async - * version, and one sync API was added to handle cases that need immediate - * effect. Also, the led class flash and lock for sysfs access was introduced. - */ -#define LED_HAVE_SET_SYNC -#define LED_HAVE_FLASH -#define LED_HAVE_LOCK -#include <linux/led-class-flash.h> -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) -/* - * New change in LED api, the set_sync operation was renamed to set_blocking and - * the workqueue is now handle by core. So, only one set operation is need. - */ -#undef LED_HAVE_SET_SYNC -#define LED_HAVE_SET_BLOCKING -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) -/* - * New helper functions for registering/unregistering flash led devices as v4l2 - * subdevices were added. - */ -#define V4L2_HAVE_FLASH -#include <media/v4l2-flash-led-class.h> -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) /* * Power supply get by name need to drop reference after call diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 71db077765f7..b2847feb7e86 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -9,9 +9,11 @@ #include <linux/kernel.h> #include <linux/leds.h> +#include <linux/led-class-flash.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/version.h> +#include <media/v4l2-flash-led-class.h> #include "greybus.h" #include "greybus_protocols.h" @@ -30,11 +32,8 @@ struct gb_channel { struct attribute **attrs; struct attribute_group *attr_group; const struct attribute_group **attr_groups; -#ifndef LED_HAVE_SET_BLOCKING - struct work_struct work_brightness_set; -#endif struct led_classdev *led; -#ifdef LED_HAVE_FLASH +#if IS_REACHABLE(CONFIG_LEDS_CLASS_FLASH) struct led_classdev_flash fled; struct led_flash_setting intensity_uA; struct led_flash_setting timeout_us; @@ -58,7 +57,7 @@ struct gb_light { struct gb_channel *channels; bool has_flash; bool ready; -#ifdef V4L2_HAVE_FLASH +#if IS_REACHABLE(CONFIG_V4L2_FLASH_LED_CLASS) struct v4l2_flash *v4l2_flash; #endif }; @@ -88,7 +87,7 @@ static bool is_channel_flash(struct gb_channel *channel) | GB_CHANNEL_MODE_INDICATOR)); } -#ifdef LED_HAVE_FLASH +#if IS_REACHABLE(CONFIG_LEDS_CLASS_FLASH) static struct gb_channel *get_channel_from_cdev(struct led_classdev *cdev) { struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev); @@ -157,7 +156,7 @@ static int __gb_lights_flash_brightness_set(struct gb_channel *channel) return __gb_lights_flash_intensity_set(channel, intensity); } -#else /* LED_HAVE_FLASH */ +#else static struct gb_channel *get_channel_from_cdev(struct led_classdev *cdev) { return container_of(cdev, struct gb_channel, cled); @@ -172,12 +171,11 @@ static int __gb_lights_flash_brightness_set(struct gb_channel *channel) { return 0; } -#endif /* !LED_HAVE_FLASH */ +#endif static int gb_lights_color_set(struct gb_channel *channel, u32 color); static int gb_lights_fade_set(struct gb_channel *channel); -#ifdef LED_HAVE_LOCK static void led_lock(struct led_classdev *cdev) { mutex_lock(&cdev->led_access); @@ -187,15 +185,6 @@ static void led_unlock(struct led_classdev *cdev) { mutex_unlock(&cdev->led_access); } -#else -static void led_lock(struct led_classdev *cdev) -{ -} - -static void led_unlock(struct led_classdev *cdev) -{ -} -#endif /* !LED_HAVE_LOCK */ #define gb_lights_fade_attr(__dir) \ static ssize_t fade_##__dir##_show(struct device *dev, \ @@ -444,39 +433,6 @@ static int __gb_lights_brightness_set(struct gb_channel *channel) return ret; } -#ifndef LED_HAVE_SET_BLOCKING -static void gb_brightness_set_work(struct work_struct *work) -{ - struct gb_channel *channel = container_of(work, struct gb_channel, - work_brightness_set); - - __gb_lights_brightness_set(channel); -} - -#ifdef LED_HAVE_SET_SYNC -static int gb_brightness_set_sync(struct led_classdev *cdev, - enum led_brightness value) -{ - struct gb_channel *channel = get_channel_from_cdev(cdev); - - channel->led->brightness = value; - - return __gb_lights_brightness_set(channel); -} -#endif - -static void gb_brightness_set(struct led_classdev *cdev, - enum led_brightness value) -{ - struct gb_channel *channel = get_channel_from_cdev(cdev); - - if (channel->releasing) - return; - - cdev->brightness = value; - schedule_work(&channel->work_brightness_set); -} -#else /* LED_HAVE_SET_BLOCKING */ static int gb_brightness_set(struct led_classdev *cdev, enum led_brightness value) { @@ -486,7 +442,6 @@ static int gb_brightness_set(struct led_classdev *cdev, return __gb_lights_brightness_set(channel); } -#endif static enum led_brightness gb_brightness_get(struct led_classdev *cdev) @@ -554,22 +509,13 @@ static void gb_lights_led_operations_set(struct gb_channel *channel, struct led_classdev *cdev) { cdev->brightness_get = gb_brightness_get; -#ifdef LED_HAVE_SET_SYNC - cdev->brightness_set_sync = gb_brightness_set_sync; -#endif -#ifdef LED_HAVE_SET_BLOCKING cdev->brightness_set_blocking = gb_brightness_set; -#endif -#ifndef LED_HAVE_SET_BLOCKING - cdev->brightness_set = gb_brightness_set; - INIT_WORK(&channel->work_brightness_set, gb_brightness_set_work); -#endif if (channel->flags & GB_LIGHT_CHANNEL_BLINK) cdev->blink_set = gb_blink_set; } -#ifdef V4L2_HAVE_FLASH +#if IS_REACHABLE(CONFIG_V4L2_FLASH_LED_CLASS) /* V4L2 specific helpers */ static const struct v4l2_flash_ops v4l2_flash_ops; @@ -655,7 +601,7 @@ static void gb_lights_light_v4l2_unregister(struct gb_light *light) } #endif -#ifdef LED_HAVE_FLASH +#if IS_REACHABLE(CONFIG_LEDS_CLASS_FLASH) /* Flash specific operations */ static int gb_lights_flash_intensity_set(struct led_classdev_flash *fcdev, u32 brightness) @@ -936,7 +882,7 @@ static void __gb_lights_flash_led_unregister(struct gb_channel *channel) { } -#endif /* LED_HAVE_FLASH */ +#endif static int __gb_lights_led_register(struct gb_channel *channel) { @@ -1132,9 +1078,6 @@ static int gb_lights_light_register(struct gb_light *light) static void gb_lights_channel_free(struct gb_channel *channel) { -#ifndef LED_HAVE_SET_BLOCKING - flush_work(&channel->work_brightness_set); -#endif kfree(channel->attrs); kfree(channel->attr_group); kfree(channel->attr_groups); -- cgit v1.2.3-59-g8ed1b From 2064ae5741f49a3e8c178680390eb296592562c1 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Thu, 8 Sep 2016 17:17:49 +0100 Subject: staging: greybus: sdio: remove KERNEL_VERSION checks No need to support older kernel versions in the Greybus SDIO driver, so remove the checks as needed, we can now rely on all of the correct SDIO core apis being present. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 13 ------------- drivers/staging/greybus/sdio.c | 12 ++++-------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 980263f58523..1d93fb020961 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -22,19 +22,6 @@ #define CORE_OWNS_PSY_STRUCT #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) -#define MMC_HS400_SUPPORTED -#define MMC_DDR52_DEFINED -#endif - -#ifndef MMC_CAP2_CORE_RUNTIME_PM -#define MMC_CAP2_CORE_RUNTIME_PM 0 -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) -#define MMC_POWER_UNDEFINED_SUPPORTED -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) /* * Power supply get by name need to drop reference after call diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 99b997904edd..c7133b1c7fd8 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -46,6 +46,10 @@ struct gb_sdio_host { /* kernel vdd starts at 0x80 and we need to translate to greybus ones 0x01 */ #define GB_SDIO_VDD_SHIFT 8 +#ifndef MMC_CAP2_CORE_RUNTIME_PM +#define MMC_CAP2_CORE_RUNTIME_PM 0 +#endif + static inline bool single_op(struct mmc_command *cmd) { uint32_t opcode = cmd->opcode; @@ -78,10 +82,8 @@ static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) ((r & GB_SDIO_CAP_DRIVER_TYPE_D) ? MMC_CAP_DRIVER_TYPE_D : 0); caps2 = ((r & GB_SDIO_CAP_HS200_1_2V) ? MMC_CAP2_HS200_1_2V_SDR : 0) | -#ifdef MMC_HS400_SUPPORTED ((r & GB_SDIO_CAP_HS400_1_2V) ? MMC_CAP2_HS400_1_2V : 0) | ((r & GB_SDIO_CAP_HS400_1_8V) ? MMC_CAP2_HS400_1_8V : 0) | -#endif ((r & GB_SDIO_CAP_HS200_1_8V) ? MMC_CAP2_HS200_1_8V_SDR : 0); host->mmc->caps = caps; @@ -617,11 +619,9 @@ static void gb_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_POWER_ON: power_mode = GB_SDIO_POWER_ON; break; -#ifdef MMC_POWER_UNDEFINED_SUPPORTED case MMC_POWER_UNDEFINED: power_mode = GB_SDIO_POWER_UNDEFINED; break; -#endif } request.power_mode = power_mode; @@ -665,19 +665,15 @@ static void gb_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_TIMING_UHS_DDR50: timing = GB_SDIO_TIMING_UHS_DDR50; break; -#ifdef MMC_DDR52_DEFINED case MMC_TIMING_MMC_DDR52: timing = GB_SDIO_TIMING_MMC_DDR52; break; -#endif case MMC_TIMING_MMC_HS200: timing = GB_SDIO_TIMING_MMC_HS200; break; -#ifdef MMC_HS400_SUPPORTED case MMC_TIMING_MMC_HS400: timing = GB_SDIO_TIMING_MMC_HS400; break; -#endif } request.timing = timing; -- cgit v1.2.3-59-g8ed1b From 722a133a6a6687bba21cfdf33bde5767109d0dea Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Thu, 8 Sep 2016 17:17:50 +0100 Subject: staging: greybus: power_supply: remove KERNEL_VERSION checks No need to support older kernel versions in the Greybus Power Supply driver, so remove the checks as needed, we can now rely on all of the correct Power Supply core apis being present. Also move some properties definitions to the power supply greybus code. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/kernel_ver.h | 26 ---------------- drivers/staging/greybus/power_supply.c | 56 +++++++++++----------------------- 2 files changed, 18 insertions(+), 64 deletions(-) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 1d93fb020961..cfd7bc7d4076 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -17,32 +17,6 @@ #include <linux/kernel.h> #include <linux/version.h> -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) -/* Commit: 297d716 power_supply: Change ownership from driver to core */ -#define CORE_OWNS_PSY_STRUCT -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) -/* - * Power supply get by name need to drop reference after call - */ -#define PSY_HAVE_PUT -#endif - -/* - * General power supply properties that could be absent from various reasons, - * like kernel versions or vendor specific versions - */ -#ifndef POWER_SUPPLY_PROP_VOLTAGE_BOOT - #define POWER_SUPPLY_PROP_VOLTAGE_BOOT -1 -#endif -#ifndef POWER_SUPPLY_PROP_CURRENT_BOOT - #define POWER_SUPPLY_PROP_CURRENT_BOOT -1 -#endif -#ifndef POWER_SUPPLY_PROP_CALIBRATE - #define POWER_SUPPLY_PROP_CALIBRATE -1 -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) /* * After commit b2b49ccbdd54 (PM: Kconfig: Set PM_RUNTIME if PM_SLEEP is diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c index 68dd3d2f7585..e85c988b7034 100644 --- a/drivers/staging/greybus/power_supply.c +++ b/drivers/staging/greybus/power_supply.c @@ -27,14 +27,8 @@ struct gb_power_supply_prop { struct gb_power_supply { u8 id; bool registered; -#ifndef CORE_OWNS_PSY_STRUCT - struct power_supply psy; -#define to_gb_power_supply(x) container_of(x, struct gb_power_supply, psy) -#else struct power_supply *psy; struct power_supply_desc desc; -#define to_gb_power_supply(x) power_supply_get_drvdata(x) -#endif char name[64]; struct gb_power_supplies *supplies; struct delayed_work work; @@ -61,6 +55,22 @@ struct gb_power_supplies { struct mutex supplies_lock; }; +#define to_gb_power_supply(x) power_supply_get_drvdata(x) + +/* + * General power supply properties that could be absent from various reasons, + * like kernel versions or vendor specific versions + */ +#ifndef POWER_SUPPLY_PROP_VOLTAGE_BOOT + #define POWER_SUPPLY_PROP_VOLTAGE_BOOT -1 +#endif +#ifndef POWER_SUPPLY_PROP_CURRENT_BOOT + #define POWER_SUPPLY_PROP_CURRENT_BOOT -1 +#endif +#ifndef POWER_SUPPLY_PROP_CALIBRATE + #define POWER_SUPPLY_PROP_CALIBRATE -1 +#endif + /* cache time in milliseconds, if cache_time is set to 0 cache is disable */ static unsigned int cache_time = 1000; /* @@ -342,17 +352,10 @@ static void next_interval(struct gb_power_supply *gbpsy) gbpsy->update_interval = update_interval_max; } -#ifndef CORE_OWNS_PSY_STRUCT -static void __gb_power_supply_changed(struct gb_power_supply *gbpsy) -{ - power_supply_changed(&gbpsy->psy); -} -#else static void __gb_power_supply_changed(struct gb_power_supply *gbpsy) { power_supply_changed(gbpsy->psy); } -#endif static void gb_power_supply_state_change(struct gb_power_supply *gbpsy, struct gb_power_supply_prop *prop) @@ -451,9 +454,8 @@ static int __gb_power_supply_set_name(char *init_name, char *name, size_t len) strlcpy(name, init_name, len); while ((ret < len) && (psy = power_supply_get_by_name(name))) { -#ifdef PSY_HAVE_PUT power_supply_put(psy); -#endif + ret = snprintf(name, len, "%s_%u", init_name, ++i); } if (ret >= len) @@ -814,23 +816,6 @@ static int property_is_writeable(struct power_supply *b, return is_psy_prop_writeable(gbpsy, psp); } -#ifndef CORE_OWNS_PSY_STRUCT -static int gb_power_supply_register(struct gb_power_supply *gbpsy) -{ - struct gb_connection *connection = get_conn_from_psy(gbpsy); - - gbpsy->psy.name = gbpsy->name; - gbpsy->psy.type = gbpsy->type; - gbpsy->psy.properties = gbpsy->props_raw; - gbpsy->psy.num_properties = total_props(gbpsy); - gbpsy->psy.get_property = get_property; - gbpsy->psy.set_property = set_property; - gbpsy->psy.property_is_writeable = property_is_writeable; - - return power_supply_register(&connection->bundle->dev, - &gbpsy->psy); -} -#else static int gb_power_supply_register(struct gb_power_supply *gbpsy) { struct gb_connection *connection = get_conn_from_psy(gbpsy); @@ -850,7 +835,6 @@ static int gb_power_supply_register(struct gb_power_supply *gbpsy) &gbpsy->desc, &cfg); return PTR_ERR_OR_ZERO(gbpsy->psy); } -#endif static void _gb_power_supply_free(struct gb_power_supply *gbpsy) { @@ -866,13 +850,9 @@ static void _gb_power_supply_release(struct gb_power_supply *gbpsy) gbpsy->update_interval = 0; cancel_delayed_work_sync(&gbpsy->work); -#ifndef CORE_OWNS_PSY_STRUCT - if (gbpsy->registered) - power_supply_unregister(&gbpsy->psy); -#else + if (gbpsy->registered) power_supply_unregister(gbpsy->psy); -#endif _gb_power_supply_free(gbpsy); } -- cgit v1.2.3-59-g8ed1b From b04f56c6e7ccdc7d09ccd20f28bb4d34a1603ebb Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Thu, 8 Sep 2016 17:17:51 +0100 Subject: staging: greybus: makefile: fix dependency of spi to spilib Greybus SPI driver depends on gb-spilib and we need to state that at makefile to make it link correctly. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 0cc769eb470a..f337b7b70782 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -85,7 +85,7 @@ obj-$(CONFIG_GREYBUS_GPIO) += gb-gpio.o obj-$(CONFIG_GREYBUS_I2C) += gb-i2c.o obj-$(CONFIG_GREYBUS_PWM) += gb-pwm.o obj-$(CONFIG_GREYBUS_SDIO) += gb-sdio.o -obj-$(CONFIG_GREYBUS_SPI) += gb-spi.o +obj-$(CONFIG_GREYBUS_SPI) += gb-spi.o gb-spilib.o obj-$(CONFIG_GREYBUS_UART) += gb-uart.o obj-$(CONFIG_GREYBUS_USB) += gb-usb.o -- cgit v1.2.3-59-g8ed1b From a0cf5951def318f40eec031c0898ad0140663824 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rui.silva@linaro.org> Date: Thu, 8 Sep 2016 17:17:52 +0100 Subject: staging: greybus: Kconfig: enable possibility to select light driver Remove BROKEN keyword to allow the light driver to be select now that we fixed the kernel version dependencies. Also fix the module name in the help section. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/greybus/Kconfig b/drivers/staging/greybus/Kconfig index ede91843eda5..89c49767d247 100644 --- a/drivers/staging/greybus/Kconfig +++ b/drivers/staging/greybus/Kconfig @@ -78,13 +78,13 @@ config GREYBUS_HID config GREYBUS_LIGHT tristate "Greybus LED Class driver" - depends on LEDS_CLASS && BROKEN + depends on LEDS_CLASS ---help--- Select this option if you have a device that follows the Greybus LED Class specification. To compile this code as a module, chose M here: the module - will be called gb-led.ko + will be called gb-light.ko config GREYBUS_LOG tristate "Greybus Debug Log Class driver" -- cgit v1.2.3-59-g8ed1b From 948c6227e76ef2443b327a409dc8eced92b32bda Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@google.com> Date: Fri, 9 Sep 2016 09:47:01 +0200 Subject: staging: greybus: remove CONFIG_PM_RUNTIME from kernel_ver.h The last thing remaining in kernel_ver.h was the setting of CONFIG_PM_RUNTIME, which isn't needed in a in-tree implementation. So remove the setting of this value, and the .h file entirely as that was the last thing left in it. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/audio_module.c | 2 +- drivers/staging/greybus/bundle.c | 3 +-- drivers/staging/greybus/bundle.h | 2 +- drivers/staging/greybus/camera.c | 2 +- drivers/staging/greybus/es2.c | 1 - drivers/staging/greybus/gbphy.c | 2 +- drivers/staging/greybus/gbphy.h | 2 +- drivers/staging/greybus/greybus.h | 1 - drivers/staging/greybus/interface.c | 2 +- drivers/staging/greybus/kernel_ver.h | 33 --------------------------------- 10 files changed, 7 insertions(+), 43 deletions(-) delete mode 100644 drivers/staging/greybus/kernel_ver.h diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index 411735df2bb4..ae1c0fa85752 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -421,7 +421,7 @@ static const struct greybus_bundle_id gb_audio_id_table[] = { }; MODULE_DEVICE_TABLE(greybus, gb_audio_id_table); -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int gb_audio_suspend(struct device *dev) { struct gb_bundle *bundle = to_gb_bundle(dev); diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 5bd7731237f5..d2ef57d090be 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -89,8 +89,7 @@ static void gb_bundle_release(struct device *dev) kfree(bundle); } -#ifdef CONFIG_PM_RUNTIME - +#ifdef CONFIG_PM static void gb_bundle_disable_all_connections(struct gb_bundle *bundle) { struct gb_connection *connection; diff --git a/drivers/staging/greybus/bundle.h b/drivers/staging/greybus/bundle.h index 349845ee893c..0c3491def96c 100644 --- a/drivers/staging/greybus/bundle.h +++ b/drivers/staging/greybus/bundle.h @@ -41,7 +41,7 @@ int gb_bundle_add(struct gb_bundle *bundle); void gb_bundle_destroy(struct gb_bundle *bundle); /* Bundle Runtime PM wrappers */ -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static inline int gb_pm_runtime_get_sync(struct gb_bundle *bundle) { int retval; diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index e1f3046105c1..46d2e8a9e490 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -1344,7 +1344,7 @@ static const struct greybus_bundle_id gb_camera_id_table[] = { { }, }; -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int gb_camera_suspend(struct device *dev) { struct gb_bundle *bundle = to_gb_bundle(dev); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 6cb13fcf62d4..071bb1cfd3ae 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -17,7 +17,6 @@ #include "arpc.h" #include "greybus.h" #include "greybus_trace.h" -#include "kernel_ver.h" #include "connection.h" diff --git a/drivers/staging/greybus/gbphy.c b/drivers/staging/greybus/gbphy.c index 478c162d8ff0..bcde7c9a0f17 100644 --- a/drivers/staging/greybus/gbphy.c +++ b/drivers/staging/greybus/gbphy.c @@ -52,7 +52,7 @@ static void gbphy_dev_release(struct device *dev) kfree(gbphy_dev); } -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int gb_gbphy_idle(struct device *dev) { pm_runtime_mark_last_busy(dev); diff --git a/drivers/staging/greybus/gbphy.h b/drivers/staging/greybus/gbphy.h index e251186d5cbb..8ee68055ccc4 100644 --- a/drivers/staging/greybus/gbphy.h +++ b/drivers/staging/greybus/gbphy.h @@ -66,7 +66,7 @@ void gb_gbphy_deregister_driver(struct gbphy_driver *driver); #define module_gbphy_driver(__gbphy_driver) \ module_driver(__gbphy_driver, gb_gbphy_register, gb_gbphy_deregister) -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static inline int gbphy_runtime_get_sync(struct gbphy_device *gbphy_dev) { struct device *dev = &gbphy_dev->dev; diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 3e32028832c1..12526887ae2e 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -21,7 +21,6 @@ #include <linux/pm_runtime.h> #include <linux/idr.h> -#include "kernel_ver.h" #include "greybus_id.h" #include "greybus_manifest.h" #include "greybus_protocols.h" diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index c3ed3d7dee99..546b090e2d51 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -698,7 +698,7 @@ static void gb_interface_release(struct device *dev) kfree(intf); } -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int gb_interface_suspend(struct device *dev) { struct gb_interface *intf = to_gb_interface(dev); diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h deleted file mode 100644 index cfd7bc7d4076..000000000000 --- a/drivers/staging/greybus/kernel_ver.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Greybus kernel "version" glue logic. - * - * Copyright 2014 Google Inc. - * Copyright 2014 Linaro Ltd. - * - * Released under the GPLv2 only. - * - * Backports of newer kernel apis to allow the code to build properly on older - * kernel versions. Remove this file when merging to upstream, it should not be - * needed at all - */ - -#ifndef __GREYBUS_KERNEL_VER_H -#define __GREYBUS_KERNEL_VER_H - -#include <linux/kernel.h> -#include <linux/version.h> - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) -/* - * After commit b2b49ccbdd54 (PM: Kconfig: Set PM_RUNTIME if PM_SLEEP is - * selected) PM_RUNTIME is always set if PM is set, so files that are build - * conditionally if CONFIG_PM_RUNTIME is set may now be build if CONFIG_PM is - * set. - */ - -#ifdef CONFIG_PM -#define CONFIG_PM_RUNTIME -#endif /* CONFIG_PM */ -#endif - -#endif /* __GREYBUS_KERNEL_VER_H */ -- cgit v1.2.3-59-g8ed1b From 629c1fb53717436ea0575daa1ae889e06bb13f71 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Date: Mon, 19 Sep 2016 18:23:19 +0200 Subject: staging: greybus: add some MAINTAINERS Johan and Alex and I are going to maintain the greybus code, so add it to MAINTAINERS so we get cc:ed on patches. Acked-by: Johan Hovold <johan@kernel.org> Acked-by: Alex Elder <elder@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6781a3febd59..d369b0a0f5e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5283,6 +5283,13 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/aeroflex/ +GREYBUS SUBSYSTEM +M: Johan Hovold <johan@kernel.org> +M: Alex Elder <elder@kernel.org> +M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +S: Maintained +F: drivers/staging/greybus/ + GSPCA FINEPIX SUBDRIVER M: Frank Zago <frank@zago.net> L: linux-media@vger.kernel.org -- cgit v1.2.3-59-g8ed1b From 2bbadafbe4eacab57aa7bc8e50287c1366303807 Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva <rmfrfs@gmail.com> Date: Mon, 19 Sep 2016 17:07:59 +0100 Subject: staging: greybus: MAINTAINERS: add greybus protocol drivers maintainers Add me to some Greybus protocol drivers maintainers, spi, sdio, power supply, light and gpio. Signed-off-by: Rui Miguel Silva <rmfrfs@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d369b0a0f5e1..fb55334d602e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5290,6 +5290,16 @@ M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> S: Maintained F: drivers/staging/greybus/ +GREYBUS PROTOCOLS DRIVERS +M: Rui Miguel Silva <rmfrfs@gmail.com> +S: Maintained +F: drivers/staging/greybus/sdio.c +F: drivers/staging/greybus/light.c +F: drivers/staging/greybus/gpio.c +F: drivers/staging/greybus/power_supply.c +F: drivers/staging/greybus/spi.c +F: drivers/staging/greybus/spilib.c + GSPCA FINEPIX SUBDRIVER M: Frank Zago <frank@zago.net> L: linux-media@vger.kernel.org -- cgit v1.2.3-59-g8ed1b